diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index 095edacb855b..2a3ec0ee477b 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -123,6 +123,13 @@ trait HasAttributes */ protected static $mutatorCache = []; + /** + * The encrypter instance that is used to encrypt attributes. + * + * @var \Illuminate\Contracts\Encryption\Encrypter + */ + public static $encrypter; + /** * Convert the model's attributes to an array. * @@ -883,15 +890,14 @@ public function fromJson($value, $asObject = false) } /** - * Decode the given encrypted string. + * Decrypt the given encrypted string. * * @param string $value - * @param bool $asObject * @return mixed */ public function fromEncryptedString($value) { - return Crypt::decryptString($value); + return (static::$encrypter ?? Crypt::getFacadeRoot())->decrypt($value, false); } /** @@ -903,7 +909,18 @@ public function fromEncryptedString($value) */ protected function castAttributeAsEncryptedString($key, $value) { - return Crypt::encryptString($value); + return (static::$encrypter ?? Crypt::getFacadeRoot())->encrypt($value, false); + } + + /** + * Set the encrypter instance that will be used to encrypt attributes. + * + * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter + * @return void + */ + public static function encryptUsing($encrypter) + { + static::$encrypter = $encrypter; } /** diff --git a/tests/Integration/Database/EloquentModelEncryptedCastingTest.php b/tests/Integration/Database/EloquentModelEncryptedCastingTest.php index aea6830d728d..6eca8078028c 100644 --- a/tests/Integration/Database/EloquentModelEncryptedCastingTest.php +++ b/tests/Integration/Database/EloquentModelEncryptedCastingTest.php @@ -31,15 +31,17 @@ protected function setUp(): void $table->text('secret_object')->nullable(); $table->text('secret_collection')->nullable(); }); + + Model::$encrypter = null; } public function testStringsAreCastable() { - $this->encrypter->expects('encryptString') - ->with('this is a secret string') + $this->encrypter->expects('encrypt') + ->with('this is a secret string', false) ->andReturn('encrypted-secret-string'); - $this->encrypter->expects('decryptString') - ->with('encrypted-secret-string') + $this->encrypter->expects('decrypt') + ->with('encrypted-secret-string', false) ->andReturn('this is a secret string'); /** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */ @@ -56,11 +58,11 @@ public function testStringsAreCastable() public function testArraysAreCastable() { - $this->encrypter->expects('encryptString') - ->with('{"key1":"value1"}') + $this->encrypter->expects('encrypt') + ->with('{"key1":"value1"}', false) ->andReturn('encrypted-secret-array-string'); - $this->encrypter->expects('decryptString') - ->with('encrypted-secret-array-string') + $this->encrypter->expects('decrypt') + ->with('encrypted-secret-array-string', false) ->andReturn('{"key1":"value1"}'); /** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */ @@ -77,11 +79,11 @@ public function testArraysAreCastable() public function testJsonIsCastable() { - $this->encrypter->expects('encryptString') - ->with('{"key1":"value1"}') + $this->encrypter->expects('encrypt') + ->with('{"key1":"value1"}', false) ->andReturn('encrypted-secret-json-string'); - $this->encrypter->expects('decryptString') - ->with('encrypted-secret-json-string') + $this->encrypter->expects('decrypt') + ->with('encrypted-secret-json-string', false) ->andReturn('{"key1":"value1"}'); /** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */ @@ -101,12 +103,12 @@ public function testObjectIsCastable() $object = new \stdClass(); $object->key1 = 'value1'; - $this->encrypter->expects('encryptString') - ->with('{"key1":"value1"}') + $this->encrypter->expects('encrypt') + ->with('{"key1":"value1"}', false) ->andReturn('encrypted-secret-object-string'); - $this->encrypter->expects('decryptString') + $this->encrypter->expects('decrypt') ->twice() - ->with('encrypted-secret-object-string') + ->with('encrypted-secret-object-string', false) ->andReturn('{"key1":"value1"}'); /** @var \Illuminate\Tests\Integration\Database\EncryptedCast $object */ @@ -124,12 +126,12 @@ public function testObjectIsCastable() public function testCollectionIsCastable() { - $this->encrypter->expects('encryptString') - ->with('{"key1":"value1"}') + $this->encrypter->expects('encrypt') + ->with('{"key1":"value1"}', false) ->andReturn('encrypted-secret-collection-string'); - $this->encrypter->expects('decryptString') + $this->encrypter->expects('decrypt') ->twice() - ->with('encrypted-secret-collection-string') + ->with('encrypted-secret-collection-string', false) ->andReturn('{"key1":"value1"}'); /** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */ @@ -144,6 +146,39 @@ public function testCollectionIsCastable() 'secret_collection' => 'encrypted-secret-collection-string', ]); } + + public function testCustomEncrypterCanBeSpecified() + { + $customEncrypter = $this->mock(Encrypter::class); + + $this->assertNull(Model::$encrypter); + + Model::encryptUsing($customEncrypter); + + $this->assertSame($customEncrypter, Model::$encrypter); + + $this->encrypter->expects('encrypt') + ->never(); + $this->encrypter->expects('decrypt') + ->never(); + $customEncrypter->expects('encrypt') + ->with('this is a secret string', false) + ->andReturn('encrypted-secret-string'); + $customEncrypter->expects('decrypt') + ->with('encrypted-secret-string', false) + ->andReturn('this is a secret string'); + + /** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */ + $subject = EncryptedCast::create([ + 'secret' => 'this is a secret string', + ]); + + $this->assertSame('this is a secret string', $subject->secret); + $this->assertDatabaseHas('encrypted_casts', [ + 'id' => $subject->id, + 'secret' => 'encrypted-secret-string', + ]); + } } /**