Skip to content

Commit

Permalink
Merge pull request #52 from tonysm/tm/encrypt-as-string
Browse files Browse the repository at this point in the history
Allow configuring the encryption handlers
  • Loading branch information
tonysm authored Mar 16, 2024
2 parents 9a89b33 + 8036b37 commit 15a0e37
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 4 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,25 @@ class Post extends Model
}
```

This uses [Laravel's Encryption](https://laravel.com/docs/10.x/encryption#introduction) feature.
This uses [Laravel's Encryption](https://laravel.com/docs/encryption#introduction) feature. By default, it will encrypt using the default encryption handler in Laravel, which serializes the value before encrypting it. However you can opt-in skip the serialization step and only encrypt as string by calling the following method in the `AppServiceProvider`:

```php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Tonysm\RichTextLaravel\RichTextLaravel;

class AppServiceProvider extends ServiceProvider
{
// ...
public function boot(): void
{
RichTextLaravel::encryptAsString();
}
}
```

In the next major version of the package, this will be the default, so it's recommended that you do that.

#### Key Rotation

Expand Down
5 changes: 3 additions & 2 deletions src/Casts/AsEncryptedRichTextContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Tonysm\RichTextLaravel\Content;
use Tonysm\RichTextLaravel\RichTextLaravel;

class AsEncryptedRichTextContent implements CastsAttributes
{
Expand All @@ -18,7 +19,7 @@ class AsEncryptedRichTextContent implements CastsAttributes
*/
public function set($model, $key, $value, $attributes)
{
return encrypt(Content::toStorage($value));
return RichTextLaravel::encrypt(Content::toStorage($value), $model, $key);
}

/**
Expand All @@ -32,6 +33,6 @@ public function set($model, $key, $value, $attributes)
*/
public function get($model, $key, $value, $attributes)
{
return Content::fromStorage($value ? decrypt($value) : null);
return Content::fromStorage(RichTextLaravel::decrypt($value, $model, $key));
}
}
61 changes: 61 additions & 0 deletions src/RichTextLaravel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Tonysm\RichTextLaravel;

use Illuminate\Support\Facades\Crypt;

class RichTextLaravel
{
/**
* The handler responsible for encrypting the rich text attributes.
*
* @var callable
*/
protected static $encryptHandler;

/**
* The handle responsible for decrypting the rich text attributes.
*
* @var callable
*/
protected static $decryptHandler;

/**
* Override the way the package handles encryption.
*/
public static function encryptUsing($encryption, $decryption): void
{
static::$encryptHandler = $encryption;
static::$decryptHandler = $decryption;
}

/**
* Configures the Rich Text Laravel package to store encrypted data as string,
* instead of using Laravel's default encryption mode, which serializes the
* content before encrypting it.
*/
public static function encryptAsString(): void
{
static::$encryptHandler = fn ($value) => Crypt::encryptString($value);
static::$decryptHandler = fn ($value) => ($value ? Crypt::decryptString($value) : $value);
}

public static function clearEncryptionHandlers(): void
{
static::encryptUsing(null, null);
}

public static function encrypt($value, $model, $key): string
{
$encrypt = static::$encryptHandler ??= fn ($value) => Crypt::encrypt($value);

return call_user_func($encrypt, $value, $model, $key);
}

public static function decrypt($value, $model, $key): ?string
{
$decrypt = static::$decryptHandler ??= fn ($value) => Crypt::decrypt($value);

return $value ? call_user_func($decrypt, $value, $model, $key) : $value;
}
}
12 changes: 11 additions & 1 deletion tests/EncryptedModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Tonysm\RichTextLaravel\Tests;

use Illuminate\Support\Facades\DB;
use Tonysm\RichTextLaravel\RichTextLaravel;
use Workbench\App\Models\EncryptedMessage;
use Workbench\App\Models\Message;

Expand All @@ -18,10 +19,19 @@ public function encrypt_content_based_on_encrypted_option_at_declaration_time()
$this->assertNotEncryptedRichTextAttribute($clearMessage, 'content', 'Hello World');
}

/** @test */
public function encrypts_as_string()
{
RichTextLaravel::encryptAsString();

$encryptedMessage = EncryptedMessage::create(['content' => 'Hello World']);
$this->assertEncryptedRichTextAttribute($encryptedMessage, 'content', 'Hello World');
}

private function assertEncryptedRichTextAttribute($model, $field, $expectedValue)
{
$this->assertStringNotContainsString($expectedValue, $encrypted = DB::table('rich_texts')->where('record_id', $model->id)->value('body'));
$this->assertEquals($expectedValue, decrypt($encrypted));
$this->assertEquals($expectedValue, RichTextLaravel::decrypt($encrypted, $model, $field));
$this->assertStringContainsString($expectedValue, $model->refresh()->{$field}->body->toHtml());
}

Expand Down
3 changes: 3 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Foundation\Testing\RefreshDatabase;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Orchestra\Testbench\TestCase as Orchestra;
use Tonysm\RichTextLaravel\RichTextLaravel;

class TestCase extends Orchestra
{
Expand All @@ -19,6 +20,8 @@ public function setUp(): void
Factory::guessFactoryNamesUsing(
fn (string $modelName) => 'Tonysm\\RichTextLaravel\\Database\\Factories\\'.class_basename($modelName).'Factory'
);

RichTextLaravel::clearEncryptionHandlers();
}

public function getEnvironmentSetUp($app)
Expand Down

0 comments on commit 15a0e37

Please sign in to comment.