Merge pull request #5 from bjornvoesten/development

Added nullable attributes
This commit is contained in:
Bjorn Voesten 2021-01-22 00:23:16 +01:00 committed by GitHub
commit 5fdadf1b46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 53 deletions

View File

@ -13,10 +13,10 @@ class Encrypted implements CastsAttributes
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
* @param array $attributes * @param array $attributes
* @return string * @return string|null
* @throws \Exception * @throws \Exception
*/ */
public function get($model, string $key, $value, array $attributes) public function get($model, string $key, $value, array $attributes): ?string
{ {
return $model->decrypt($key); return $model->decrypt($key);
} }

View File

@ -108,23 +108,23 @@ class CipherSweetService
$field = new EncryptedField( $field = new EncryptedField(
$this->engine, $this->engine,
$table = $model->getTable(), $table = $model->getTable(),
$attribute->column, $attribute->column
); );
// Map and add the indexes to the encrypted // Map and add the indexes to the encrypted
// field instance. // field instance.
collect($attribute->indexes) collect($attribute->indexes)
->map(function (Index $index) { ->map(static function (Index $index) {
return $index = new BlindIndex( return new BlindIndex(
$index->column, $index->column,
$index->transformers, $index->transformers,
$index->bits, $index->bits,
$index->fast, $index->fast
); );
}) })
->each( ->each(static function ($index) use ($field) {
fn($index) => $field->addBlindIndex($index) return $field->addBlindIndex($index);
); });
return $field; return $field;
} }
@ -134,18 +134,24 @@ class CipherSweetService
* *
* @param \Illuminate\Database\Eloquent\Model|\BjornVoesten\CipherSweet\Concerns\WithAttributeEncryption $model * @param \Illuminate\Database\Eloquent\Model|\BjornVoesten\CipherSweet\Concerns\WithAttributeEncryption $model
* @param string $attribute * @param string $attribute
* @param string|int|boolean $value * @param string|int|boolean|null $value
* @return array * @return array
* @throws \ParagonIE\CipherSweet\Exception\BlindIndexNameCollisionException * @throws \ParagonIE\CipherSweet\Exception\BlindIndexNameCollisionException
* @throws \ParagonIE\CipherSweet\Exception\BlindIndexNotFoundException * @throws \ParagonIE\CipherSweet\Exception\BlindIndexNotFoundException
* @throws \ParagonIE\CipherSweet\Exception\CryptoOperationException * @throws \ParagonIE\CipherSweet\Exception\CryptoOperationException
* @throws \SodiumException * @throws \SodiumException
*/ */
public function encrypt(Model $model, string $attribute, $value) public function encrypt(Model $model, string $attribute, $value): array
{ {
return $this $field = $this->field($model, $attribute);
->field($model, $attribute)
->prepareForStorage($value); if (is_null($value)) {
return [null, array_map(static function () {
return null;
}, $field->getAllBlindIndexes(''))];
}
return $field->prepareForStorage($value);
} }
/** /**
@ -158,8 +164,12 @@ class CipherSweetService
* @throws \ParagonIE\CipherSweet\Exception\BlindIndexNameCollisionException * @throws \ParagonIE\CipherSweet\Exception\BlindIndexNameCollisionException
* @throws \ParagonIE\CipherSweet\Exception\CryptoOperationException * @throws \ParagonIE\CipherSweet\Exception\CryptoOperationException
*/ */
public function decrypt(Model $model, string $attribute, $value) public function decrypt(Model $model, string $attribute, $value): ?string
{ {
if (is_null($value)) {
return null;
}
return $this return $this
->field($model, $attribute) ->field($model, $attribute)
->decryptValue($value); ->decryptValue($value);

View File

@ -35,12 +35,12 @@ trait WithAttributeEncryption
* Encrypt the attribute. * Encrypt the attribute.
* *
* @param string $attribute * @param string $attribute
* @return string * @return string|null
*/ */
public function decrypt(string $attribute): string public function decrypt(string $attribute): ?string
{ {
return app('ciphersweet')->decrypt( return app('ciphersweet')->decrypt(
$this, $attribute, $this->attributes[$attribute] $this, $attribute, $this->attributes[$attribute] ?? null
); );
} }

View File

@ -10,8 +10,8 @@ trait CreateUsersTable
protected function createUsersTable(): void protected function createUsersTable(): void
{ {
Schema::create('users', function (Blueprint $table) { Schema::create('users', function (Blueprint $table) {
$table->id('id'); $table->id();
$table->string('social_security_number'); $table->string('social_security_number')->nullable();
$table->string('social_security_number_index')->nullable(); $table->string('social_security_number_index')->nullable();
$table->string('custom_index')->nullable(); $table->string('custom_index')->nullable();
$table->timestamps(); $table->timestamps();

View File

@ -9,10 +9,10 @@ trait CreatesUsers
/** /**
* Create a new user instance. * Create a new user instance.
* *
* @param string $socialSecurityNumber * @param string|null $socialSecurityNumber
* @return \Tests\Mocks\User|\Illuminate\Database\Eloquent\Model * @return \Tests\Mocks\User|\Illuminate\Database\Eloquent\Model
*/ */
protected function user(string $socialSecurityNumber): User protected function user(?string $socialSecurityNumber): User
{ {
return User::query()->create([ return User::query()->create([
'social_security_number' => $socialSecurityNumber, 'social_security_number' => $socialSecurityNumber,

View File

@ -27,12 +27,12 @@ class EncryptionTest extends TestCase
'social_security_number' => '123-456-789', 'social_security_number' => '123-456-789',
]); ]);
$this->assertSame( static::assertSame(
'123-456-789', '123-456-789',
$user->social_security_number $user->social_security_number
); );
$this->assertNotEmpty( static::assertNotEmpty(
$user->social_security_number_index $user->social_security_number_index
); );
} }
@ -41,12 +41,12 @@ class EncryptionTest extends TestCase
{ {
$user = $this->user('123-456-789'); $user = $this->user('123-456-789');
$this->assertNotSame( static::assertNotSame(
'123-456-789', '123-456-789',
$user->getRawOriginal('social_security_number') $user->getRawOriginal('social_security_number')
); );
$this->assertNotEmpty( static::assertNotEmpty(
$user->social_security_number_index $user->social_security_number_index
); );
@ -78,12 +78,12 @@ class EncryptionTest extends TestCase
]) ])
->save(); ->save();
$this->assertNotSame( static::assertNotSame(
'123-456-789', '123-456-789',
$user->getRawOriginal('social_security_number') $user->getRawOriginal('social_security_number')
); );
$this->assertNotEmpty( static::assertNotEmpty(
$user->getAttribute('custom_index') $user->getAttribute('custom_index')
); );
@ -100,9 +100,29 @@ class EncryptionTest extends TestCase
{ {
$user = $this->user('123-456-789'); $user = $this->user('123-456-789');
$this->assertSame( static::assertSame(
'123-456-789', '123-456-789',
$user->getAttribute('social_security_number') $user->getAttribute('social_security_number')
); );
} }
public function testAttributesCanBeMadeNull(): void
{
$user = $this->user('123-456-789');
static::assertSame(
'123-456-789',
$user->social_security_number
);
$user->social_security_number = null;
static::assertNull(
$user->social_security_number
);
static::assertNull(
$user->social_security_number_index
);
}
} }

View File

@ -32,8 +32,8 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertNotContains($userTwo->id, $keys); static::assertNotContains($userTwo->id, $keys);
// Assert success using provided index. // Assert success using provided index.
/** @var \Illuminate\Database\Eloquent\Collection $keys */ /** @var \Illuminate\Database\Eloquent\Collection $keys */
@ -44,8 +44,8 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertNotContains($userTwo->id, $keys); static::assertNotContains($userTwo->id, $keys);
// Assert undefined index exception. // Assert undefined index exception.
$this->expectException(Exception::class); $this->expectException(Exception::class);
@ -57,6 +57,22 @@ class QueryTest extends TestCase
->get(); ->get();
} }
public function testCanQueryNullableAttributes(): void
{
$userOne = $this->user('123-456-789');
$userTwo = $this->user(null);
// Assert success.
/** @var \Illuminate\Database\Eloquent\Collection $keys */
$keys = User::query()
->whereEncrypted('social_security_number', '=', '123-456-789')
->get()
->modelKeys();
static::assertContains($userOne->id, $keys);
static::assertNotContains($userTwo->id, $keys);
}
public function testCanQueryEncryptedAttributeWithOrWhereClause(): void public function testCanQueryEncryptedAttributeWithOrWhereClause(): void
{ {
$userOne = $this->user('123-456-789'); $userOne = $this->user('123-456-789');
@ -71,9 +87,9 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertNotContains($userTwo->id, $keys); static::assertNotContains($userTwo->id, $keys);
$this->assertContains($userThree->id, $keys); static::assertContains($userThree->id, $keys);
// Assert success using provided index. // Assert success using provided index.
/** @var \Illuminate\Database\Eloquent\Collection $keys */ /** @var \Illuminate\Database\Eloquent\Collection $keys */
@ -85,9 +101,9 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertNotContains($userTwo->id, $keys); static::assertNotContains($userTwo->id, $keys);
$this->assertContains($userThree->id, $keys); static::assertContains($userThree->id, $keys);
// Assert undefined index exception. // Assert undefined index exception.
$this->expectException(Exception::class); $this->expectException(Exception::class);
@ -118,9 +134,9 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertContains($userTwo->id, $keys); static::assertContains($userTwo->id, $keys);
$this->assertNotContains($userThree->id, $keys); static::assertNotContains($userThree->id, $keys);
// Assert success using provided index. // Assert success using provided index.
/** @var \Illuminate\Database\Eloquent\Collection $keys */ /** @var \Illuminate\Database\Eloquent\Collection $keys */
@ -136,9 +152,9 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertContains($userTwo->id, $keys); static::assertContains($userTwo->id, $keys);
$this->assertNotContains($userThree->id, $keys); static::assertNotContains($userThree->id, $keys);
$keys = User::query() $keys = User::query()
->whereInEncrypted( ->whereInEncrypted(
@ -149,7 +165,7 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertEmpty($keys); static::assertEmpty($keys);
} }
public function testCanQueryEncryptedAttributeWithOrWhereInClause(): void public function testCanQueryEncryptedAttributeWithOrWhereInClause(): void
@ -172,9 +188,9 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertContains($userTwo->id, $keys); static::assertContains($userTwo->id, $keys);
$this->assertNotContains($userThree->id, $keys); static::assertNotContains($userThree->id, $keys);
// Assert success using provided index. // Assert success using provided index.
/** @var \Illuminate\Database\Eloquent\Collection $keys */ /** @var \Illuminate\Database\Eloquent\Collection $keys */
@ -192,9 +208,9 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertContains($userOne->id, $keys); static::assertContains($userOne->id, $keys);
$this->assertContains($userTwo->id, $keys); static::assertContains($userTwo->id, $keys);
$this->assertNotContains($userThree->id, $keys); static::assertNotContains($userThree->id, $keys);
$keys = User::query() $keys = User::query()
->whereInEncrypted( ->whereInEncrypted(
@ -210,6 +226,6 @@ class QueryTest extends TestCase
->get() ->get()
->modelKeys(); ->modelKeys();
$this->assertEmpty($keys); static::assertEmpty($keys);
} }
} }