From f600c7983c1c294d6816f3b9d9240611356c5a89 Mon Sep 17 00:00:00 2001 From: fsociety Date: Fri, 25 Oct 2024 16:15:28 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20feat(dependencies):=20add=20spat?= =?UTF-8?q?ie/laravel-ciphersweet=20package=20for=20encryption=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ feat(profile): integrate email and fax fields in association profile 🆕 feat(migrations): create blind_indexes table for encrypted data 🔧 feat(model): implement CipherSweet in EinundzwanzigPleb for email encryption 🔧 config: add ciphersweet configuration file for encryption settings 🗄️ migration: add email field to einundzwanzig_plebs table for user data --- app/Models/EinundzwanzigPleb.php | 14 +- composer.json | 1 + composer.lock | 203 +++++++++++++++++- config/ciphersweet.php | 54 +++++ ...ail_field_to_einundzwanzig_plebs_table.php | 27 +++ ...0_25_140555_create_blind_indexes_table.php | 20 ++ .../views/pages/association/profile.blade.php | 97 +++++++-- 7 files changed, 395 insertions(+), 21 deletions(-) create mode 100644 config/ciphersweet.php create mode 100644 database/migrations/2024_10_25_140418_add_email_field_to_einundzwanzig_plebs_table.php create mode 100644 database/migrations/2024_10_25_140555_create_blind_indexes_table.php diff --git a/app/Models/EinundzwanzigPleb.php b/app/Models/EinundzwanzigPleb.php index d475a6b..62dbaed 100644 --- a/app/Models/EinundzwanzigPleb.php +++ b/app/Models/EinundzwanzigPleb.php @@ -4,9 +4,14 @@ namespace App\Models; use App\Enums\AssociationStatus; use Illuminate\Database\Eloquent\Model; +use ParagonIE\CipherSweet\BlindIndex; +use ParagonIE\CipherSweet\EncryptedRow; +use Spatie\LaravelCipherSweet\Concerns\UsesCipherSweet; +use Spatie\LaravelCipherSweet\Contracts\CipherSweetEncrypted; -class EinundzwanzigPleb extends Model +class EinundzwanzigPleb extends Model implements CipherSweetEncrypted { + use UsesCipherSweet; protected $guarded = []; @@ -17,6 +22,13 @@ class EinundzwanzigPleb extends Model ]; } + public static function configureCipherSweet(EncryptedRow $encryptedRow): void + { + $encryptedRow + ->addOptionalTextField('email') + ->addBlindIndex('email', new BlindIndex('email_index')); + } + public function profile() { return $this->hasOne(Profile::class, 'pubkey', 'pubkey'); diff --git a/composer.json b/composer.json index 477032c..b7cf325 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "sentry/sentry-laravel": "^4.9", "simplesoftwareio/simple-qrcode": "^4.2", "spatie/image": "^3.7", + "spatie/laravel-ciphersweet": "^1.6", "spatie/laravel-google-fonts": "^1.4", "spatie/laravel-markdown": "^2.5", "spatie/laravel-medialibrary": "^11.9", diff --git a/composer.lock b/composer.lock index 6e11251..8caa473 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bda4dccb94a2861dc6ff18410d22a5ec", + "content-hash": "e21a704ce71a5cc77ae5a1d48a8034bd", "packages": [ { "name": "akuechler/laravel-geoly", @@ -3846,6 +3846,135 @@ ], "time": "2024-09-24T14:04:43+00:00" }, + { + "name": "paragonie/ciphersweet", + "version": "v4.7.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/ciphersweet.git", + "reference": "d7013e61f565c63213251222361ecbe060ec22de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/ciphersweet/zipball/d7013e61f565c63213251222361ecbe060ec22de", + "reference": "d7013e61f565c63213251222361ecbe060ec22de", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "paragonie/constant_time_encoding": "^2|^3", + "paragonie/sodium_compat": "^1|^2", + "php": "^8.1" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\CipherSweet\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" + } + ], + "description": "Searchable field-level encryption library for relational databases", + "keywords": [ + "FIPS 140-3", + "NIST cryptography", + "SQL encryption", + "crm", + "cryptography", + "database encryption", + "encrypt", + "encryption", + "field-level encryption", + "libsodium", + "queryable encryption", + "searchable encryption" + ], + "support": { + "issues": "https://github.com/paragonie/ciphersweet/issues", + "source": "https://github.com/paragonie/ciphersweet/tree/v4.7.0" + }, + "time": "2024-05-11T06:44:22+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2024-05-08T12:36:18+00:00" + }, { "name": "paragonie/random_compat", "version": "v9.99.100", @@ -6540,6 +6669,78 @@ }, "time": "2024-05-16T08:48:33+00:00" }, + { + "name": "spatie/laravel-ciphersweet", + "version": "1.6.2", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-ciphersweet.git", + "reference": "77b5cd8066858529ca611edf99288366efa62b61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-ciphersweet/zipball/77b5cd8066858529ca611edf99288366efa62b61", + "reference": "77b5cd8066858529ca611edf99288366efa62b61", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.19|^10.0|^11.0", + "paragonie/ciphersweet": "^4.0.1", + "php": "^8.1", + "spatie/laravel-package-tools": "^1.12.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.8", + "nunomaduro/collision": "^6.0|^8.0", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^7.0|^8.0|^9.0", + "pestphp/pest": "^1.21|^2.34", + "pestphp/pest-plugin-laravel": "^1.1|^2.3", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5|^10.5", + "spatie/laravel-ray": "^1.26" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\LaravelCipherSweet\\LaravelCipherSweetServiceProvider" + ], + "aliases": { + "LaravelCipherSweet": "Spatie\\LaravelCipherSweet\\Facades\\LaravelCipherSweet" + } + } + }, + "autoload": { + "psr-4": { + "Spatie\\LaravelCipherSweet\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rias Van der Veken", + "email": "rias@spatie.be", + "role": "Developer" + } + ], + "description": "Use ciphersweet in your Laravel project", + "homepage": "https://github.com/spatie/laravel-ciphersweet", + "keywords": [ + "laravel", + "laravel-ciphersweet", + "spatie" + ], + "support": { + "source": "https://github.com/spatie/laravel-ciphersweet/tree/1.6.2" + }, + "time": "2024-07-18T13:03:10+00:00" + }, { "name": "spatie/laravel-google-fonts", "version": "1.4.1", diff --git a/config/ciphersweet.php b/config/ciphersweet.php new file mode 100644 index 0000000..188019c --- /dev/null +++ b/config/ciphersweet.php @@ -0,0 +1,54 @@ + env('CIPHERSWEET_BACKEND', 'nacl'), + + /** + * Set backend-specific options here. "custom" points to a factory class that returns a + * backend from its `__invoke` method. Please see the docs for more details. + */ + 'backends' => [ + // 'custom' => CustomBackendFactory::class, + ], + + /** + * Select which key provider your application will use. The default option + * is to read a string literal out of .env, but it's also possible to + * provide the key in a file or use random keys for testing. + * + * Supported: "file", "random", "string", "custom" + */ + 'provider' => env('CIPHERSWEET_PROVIDER', 'string'), + + /** + * Set provider-specific options here. "string" will read the key directly + * from your .env file. "file" will read the contents of the specified file + * to use as your key. "custom" points to a factory class that returns a + * provider from its `__invoke` method. Please see the docs for more details. + */ + 'providers' => [ + 'file' => [ + 'path' => env('CIPHERSWEET_FILE_PATH'), + ], + 'string' => [ + 'key' => env('CIPHERSWEET_KEY'), + ], + // 'custom' => CustomKeyProviderFactory::class, + ], + + /* + * The provided code snippet checks whether the $permitEmpty property is set to false + * for a given field. If it is not set to false, it throws an EmptyFieldException indicating + * that the field is not defined in the row. This ensures that the code enforces the requirement for + * the field to have a value and alerts the user if it is empty or undefined. + * Supported: "true", "false" + */ + 'permit_empty' => env('CIPHERSWEET_PERMIT_EMPTY', FALSE) +]; diff --git a/database/migrations/2024_10_25_140418_add_email_field_to_einundzwanzig_plebs_table.php b/database/migrations/2024_10_25_140418_add_email_field_to_einundzwanzig_plebs_table.php new file mode 100644 index 0000000..19b8a9d --- /dev/null +++ b/database/migrations/2024_10_25_140418_add_email_field_to_einundzwanzig_plebs_table.php @@ -0,0 +1,27 @@ +string('email')->unique()->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('einundzwanzig_plebs', function (Blueprint $table) { + // + }); + } +}; diff --git a/database/migrations/2024_10_25_140555_create_blind_indexes_table.php b/database/migrations/2024_10_25_140555_create_blind_indexes_table.php new file mode 100644 index 0000000..6eb5844 --- /dev/null +++ b/database/migrations/2024_10_25_140555_create_blind_indexes_table.php @@ -0,0 +1,20 @@ +morphs('indexable'); + $table->string('name'); + $table->string('value'); + + $table->index(['name', 'value']); + $table->unique(['indexable_type', 'indexable_id', 'name']); + }); + } +}; diff --git a/resources/views/pages/association/profile.blade.php b/resources/views/pages/association/profile.blade.php index 3112524..cf4ebd2 100644 --- a/resources/views/pages/association/profile.blade.php +++ b/resources/views/pages/association/profile.blade.php @@ -19,13 +19,17 @@ use function Laravel\Folio\{middleware, name}; name('association.profile'); -state(['yearsPaid' => []]); -state(['events' => []]); -state(['payments' => []]); -state(['amountToPay' => config('app.env') === 'production' ? 21000 : 1]); -state(['currentYearIsPaid' => false]); -state(['currentPubkey' => null]); -state(['currentPleb' => null]); +state([ + 'fax' => '', + 'email' => '', + 'yearsPaid' => [], + 'events' => [], + 'payments' => [], + 'amountToPay' => config('app.env') === 'production' ? 21000 : 1, + 'currentYearIsPaid' => false, + 'currentPubkey' => null, + 'currentPleb' => null, +]); form(\App\Livewire\Forms\ApplicationForm::class); @@ -38,6 +42,7 @@ on([ => $query->where('year', date('Y')), ]) ->where('pubkey', $pubkey)->first(); + $this->email = $this->currentPleb->email; if ($this->currentPleb->association_status === \App\Enums\AssociationStatus::ACTIVE) { $this->amountToPay = config('app.env') === 'production' ? 21000 : 1; } @@ -60,6 +65,23 @@ on([ }, ]); +updated([ + 'fax' => function () { + $this->js('alert("Markus Turm wird sich per Fax melden!")'); + }, +]); + +$saveEmail = function () { + $this->validate([ + 'email' => 'required|email', + ]); + $this->currentPleb->update([ + 'email' => $this->email, + ]); + $notification = new Notification($this); + $notification->success('E-Mail Adresse gespeichert.'); +}; + $pay = function ($comment) { $paymentEvent = $this->currentPleb ->paymentEvents() @@ -413,18 +435,55 @@ $loadEvents = function () {
@if($currentPleb && $currentPleb->association_status->value > 1) -
-
-
- - - -
-
- Du bist derzeit ein Mitglied des Vereins. +
+
+
+
+ + + +
+
+ Du bist derzeit ein Mitglied des Vereins. +
+
+
+
+
+
+
+
+ + + +
+
+ Falls du möchtest, kannst du hier eine E-Mail Adresse + hinterlegen, + damit der Verein dich darüber informieren kann, wenn es + Neuigkeiten + gibt.

+ Am besten eine anynomisierte E-Mail Adresse verwenden. Wir + sichern + diese Adresse AES-256 verschlüsselt in der Datenbank ab. +
+
+ + +
+
+ +