mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2025-12-13 05:26:47 +00:00
🚀 feat(dependencies): add spatie/laravel-ciphersweet package for encryption support
✨ 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
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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",
|
||||
|
||||
203
composer.lock
generated
203
composer.lock
generated
@@ -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",
|
||||
|
||||
54
config/ciphersweet.php
Normal file
54
config/ciphersweet.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/**
|
||||
* This controls which cryptographic backend will be used by CipherSweet.
|
||||
* Unless you have specific compliance requirements, you should choose
|
||||
* "nacl".
|
||||
*
|
||||
* Supported: "boring", "fips", "nacl", "custom"
|
||||
*/
|
||||
'backend' => 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)
|
||||
];
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('einundzwanzig_plebs', function (Blueprint $table) {
|
||||
$table->string('email')->unique()->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('einundzwanzig_plebs', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('blind_indexes', function (Blueprint $table) {
|
||||
$table->morphs('indexable');
|
||||
$table->string('name');
|
||||
$table->string('value');
|
||||
|
||||
$table->index(['name', 'value']);
|
||||
$table->unique(['indexable_type', 'indexable_id', 'name']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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 () {
|
||||
|
||||
<section>
|
||||
@if($currentPleb && $currentPleb->association_status->value > 1)
|
||||
<div
|
||||
class="inline-flex flex-col w-full max-w-lg px-4 py-2 rounded-lg text-sm bg-white dark:bg-gray-800 shadow-sm border border-gray-200 dark:border-gray-700/60 text-gray-600 dark:text-gray-400">
|
||||
<div class="flex w-full justify-between items-start">
|
||||
<div class="flex">
|
||||
<svg class="shrink-0 fill-current text-yellow-500 mt-[3px] mr-3" width="16"
|
||||
height="16" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z"></path>
|
||||
</svg>
|
||||
<div>
|
||||
<div class="font-medium text-gray-800 dark:text-gray-100 mb-1">
|
||||
Du bist derzeit ein Mitglied des Vereins.
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div
|
||||
class="inline-flex flex-col w-full max-w-lg px-4 py-2 rounded-lg text-sm bg-white dark:bg-gray-800 shadow-sm border border-gray-200 dark:border-gray-700/60 text-gray-600 dark:text-gray-400">
|
||||
<div class="flex w-full justify-between items-start">
|
||||
<div class="flex">
|
||||
<svg class="shrink-0 fill-current text-yellow-500 mt-[3px] mr-3"
|
||||
width="16"
|
||||
height="16" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z"></path>
|
||||
</svg>
|
||||
<div>
|
||||
<div class="font-medium text-gray-800 dark:text-gray-100 mb-1">
|
||||
Du bist derzeit ein Mitglied des Vereins.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="inline-flex flex-col w-full px-4 py-2 rounded-lg text-sm bg-white dark:bg-gray-800 shadow-sm border border-gray-200 dark:border-gray-700/60 text-gray-600 dark:text-gray-400">
|
||||
<div class="flex w-full justify-between items-start">
|
||||
<div class="flex w-full">
|
||||
<svg class="shrink-0 fill-current text-yellow-500 mt-[3px] mr-3"
|
||||
width="16"
|
||||
height="16" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z"></path>
|
||||
</svg>
|
||||
<div class="w-full">
|
||||
<div
|
||||
class="w-full font-medium text-gray-800 dark:text-gray-100 mb-1">
|
||||
Falls du möchtest, kannst du hier eine E-Mail Adresse
|
||||
hinterlegen,
|
||||
damit der Verein dich darüber informieren kann, wenn es
|
||||
Neuigkeiten
|
||||
gibt.<br><br>
|
||||
Am besten eine anynomisierte E-Mail Adresse verwenden. Wir
|
||||
sichern
|
||||
diese Adresse AES-256 verschlüsselt in der Datenbank ab.
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<x-input wire:model.live.debounce="fax" label="Fax-Nummer"/>
|
||||
<x-input wire:model.live.debounce="email"
|
||||
label="E-Mail Adresse"/>
|
||||
</div>
|
||||
<div class="flex space-x-2 mt-2">
|
||||
<x-button wire:click="saveEmail" label="Speichern"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user