Skip to content

Commit

Permalink
Merge branch 'encrypted-object-casts' into 8.x
Browse files Browse the repository at this point in the history
  • Loading branch information
taylorotwell committed Oct 26, 2020
2 parents 3f0645c + 378b71b commit 55044ab
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 7 deletions.
19 changes: 15 additions & 4 deletions src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ trait HasAttributes
'decimal',
'double',
'encrypted',
'encrypted:array',
'encrypted:collection',
'encrypted:json',
'encrypted:object',
'float',
'int',
'integer',
Expand Down Expand Up @@ -525,6 +529,15 @@ protected function castAttribute($key, $value)
return $value;
}

// If the key is one of the encrypted castable types, we'll first decrypt
// the value and update the cast type so we may leverage the following
// logic for casting this value to any additionally specified types.
if ($this->isEncryptedCastable($key)) {
$value = $this->fromEncryptedString($value);

$castType = Str::after($castType, 'encrypted:');
}

switch ($castType) {
case 'int':
case 'integer':
Expand Down Expand Up @@ -554,8 +567,6 @@ protected function castAttribute($key, $value)
return $this->asDateTime($value);
case 'timestamp':
return $this->asTimestamp($value);
case 'encrypted':
return $this->fromEncryptedString($value);
}

if ($this->isClassCastable($key)) {
Expand Down Expand Up @@ -1112,7 +1123,7 @@ protected function isDateCastable($key)
*/
protected function isJsonCastable($key)
{
return $this->hasCast($key, ['array', 'json', 'object', 'collection']);
return $this->hasCast($key, ['array', 'json', 'object', 'collection', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
}

/**
Expand All @@ -1123,7 +1134,7 @@ protected function isJsonCastable($key)
*/
protected function isEncryptedCastable($key)
{
return $this->hasCast($key, ['encrypted']);
return $this->hasCast($key, ['encrypted', 'encrypted:array', 'encrypted:collection', 'encrypted:json', 'encrypted:object']);
}

/**
Expand Down
110 changes: 107 additions & 3 deletions tests/Integration/Database/EloquentModelEncryptedCastingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Schema;

Expand All @@ -25,6 +26,10 @@ protected function setUp(): void
Schema::create('encrypted_casts', function (Blueprint $table) {
$table->increments('id');
$table->string('secret', 1000)->nullable();
$table->text('secret_array')->nullable();
$table->text('secret_json')->nullable();
$table->text('secret_object')->nullable();
$table->text('secret_collection')->nullable();
});
}

Expand All @@ -37,21 +42,116 @@ public function testStringsAreCastable()
->with('encrypted-secret-string')
->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',
]);
}

public function testArraysAreCastable()
{
$this->encrypter->expects('encryptString')
->with('{"key1":"value1"}')
->andReturn('encrypted-secret-array-string');
$this->encrypter->expects('decryptString')
->with('encrypted-secret-array-string')
->andReturn('{"key1":"value1"}');

/** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */
$subject = EncryptedCast::create([
'secret_array' => ['key1' => 'value1'],
]);

$this->assertSame(['key1' => 'value1'], $subject->secret_array);
$this->assertDatabaseHas('encrypted_casts', [
'id' => $subject->id,
'secret_array' => 'encrypted-secret-array-string',
]);
}

public function testJsonIsCastable()
{
$this->encrypter->expects('encryptString')
->with('{"key1":"value1"}')
->andReturn('encrypted-secret-json-string');
$this->encrypter->expects('decryptString')
->with('encrypted-secret-json-string')
->andReturn('{"key1":"value1"}');

/** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */
$subject = EncryptedCast::create([
'secret_json' => ['key1' => 'value1'],
]);

$this->assertSame(['key1' => 'value1'], $subject->secret_json);
$this->assertDatabaseHas('encrypted_casts', [
'id' => $subject->id,
'secret_json' => 'encrypted-secret-json-string',
]);
}

public function testObjectIsCastable()
{
$object = new \stdClass();
$object->key1 = 'value1';

$this->encrypter->expects('encryptString')
->with('{"key1":"value1"}')
->andReturn('encrypted-secret-object-string');
$this->encrypter->expects('decryptString')
->twice()
->with('encrypted-secret-object-string')
->andReturn('{"key1":"value1"}');

/** @var \Illuminate\Tests\Integration\Database\EncryptedCast $object */
$object = EncryptedCast::create([
'secret' => 'this is a secret string',
'secret_object' => $object,
]);

$this->assertSame('this is a secret string', $object->secret);
$this->assertInstanceOf(\stdClass::class, $object->secret_object);
$this->assertSame('value1', $object->secret_object->key1);
$this->assertDatabaseHas('encrypted_casts', [
'id' => $object->id,
'secret' => 'encrypted-secret-string',
'secret_object' => 'encrypted-secret-object-string',
]);
}

public function testCollectionIsCastable()
{
$this->encrypter->expects('encryptString')
->with('{"key1":"value1"}')
->andReturn('encrypted-secret-collection-string');
$this->encrypter->expects('decryptString')
->twice()
->with('encrypted-secret-collection-string')
->andReturn('{"key1":"value1"}');

/** @var \Illuminate\Tests\Integration\Database\EncryptedCast $subject */
$subject = EncryptedCast::create([
'secret_collection' => new Collection(['key1' => 'value1']),
]);

$this->assertInstanceOf(Collection::class, $subject->secret_collection);
$this->assertSame('value1', $subject->secret_collection->get('key1'));
$this->assertDatabaseHas('encrypted_casts', [
'id' => $subject->id,
'secret_collection' => 'encrypted-secret-collection-string',
]);
}
}

/**
* @property $secret
* @property $secret_array
* @property $secret_json
* @property $secret_object
* @property $secret_collection
*/
class EncryptedCast extends Model
{
Expand All @@ -60,5 +160,9 @@ class EncryptedCast extends Model

public $casts = [
'secret' => 'encrypted',
'secret_array' => 'encrypted:array',
'secret_json' => 'encrypted:json',
'secret_object' => 'encrypted:object',
'secret_collection' => 'encrypted:collection',
];
}

0 comments on commit 55044ab

Please sign in to comment.