Skip to content

Commit

Permalink
[8.x] Model::encryptUsing() (#35080)
Browse files Browse the repository at this point in the history
* implementation

* tests
  • Loading branch information
hivokas authored Nov 3, 2020
1 parent f802e81 commit 71dbf22
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 24 deletions.
25 changes: 21 additions & 4 deletions src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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;
}

/**
Expand Down
75 changes: 55 additions & 20 deletions tests/Integration/Database/EloquentModelEncryptedCastingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 */
Expand All @@ -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 */
Expand All @@ -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 */
Expand All @@ -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 */
Expand All @@ -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',
]);
}
}

/**
Expand Down

0 comments on commit 71dbf22

Please sign in to comment.