diff --git a/app/Models/User.php b/app/Models/User.php index a5f23fc2..7c70dc19 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,10 +13,15 @@ use Laravel\Sanctum\HasApiTokens; use QCod\Gamify\Gamify; use Spatie\Comments\Models\Concerns\InteractsWithComments; use Spatie\Comments\Models\Concerns\Interfaces\CanComment; +use Spatie\LaravelCipherSweet\Concerns\UsesCipherSweet; +use Spatie\LaravelCipherSweet\Contracts\CipherSweetEncrypted; use Spatie\Permission\Traits\HasRoles; +use ParagonIE\CipherSweet\EncryptedRow; +use ParagonIE\CipherSweet\BlindIndex; -class User extends Authenticatable implements MustVerifyEmail, CanComment +class User extends Authenticatable implements MustVerifyEmail, CanComment, CipherSweetEncrypted { + use UsesCipherSweet; use HasApiTokens; use HasFactory; use HasProfilePhoto; @@ -56,6 +61,21 @@ class User extends Authenticatable implements MustVerifyEmail, CanComment 'profile_photo_url', ]; + public static function configureCipherSweet(EncryptedRow $encryptedRow): void + { + $encryptedRow + ->addField('public_key') + ->addField('lightning_address') + ->addField('lnurl') + ->addField('node_id') + ->addField('email') + ->addBlindIndex('public_key', new BlindIndex('public_key_index')) + ->addBlindIndex('lightning_address', new BlindIndex('lightning_address_index')) + ->addBlindIndex('lnurl', new BlindIndex('lnurl_index')) + ->addBlindIndex('node_id', new BlindIndex('node_id_index')) + ->addBlindIndex('email', new BlindIndex('email_index')); + } + public function orangePills() { return $this->hasMany(OrangePill::class); diff --git a/app/Nova/User.php b/app/Nova/User.php index e766592b..4bcabed1 100644 --- a/app/Nova/User.php +++ b/app/Nova/User.php @@ -30,7 +30,7 @@ class User extends Resource * @var array */ public static $search = [ - 'id', 'name', 'email', + 'id', 'name', ]; public static function label() diff --git a/app/Providers/JetstreamServiceProvider.php b/app/Providers/JetstreamServiceProvider.php index abad0ddb..5ebcf307 100644 --- a/app/Providers/JetstreamServiceProvider.php +++ b/app/Providers/JetstreamServiceProvider.php @@ -9,14 +9,17 @@ use App\Actions\Jetstream\DeleteUser; use App\Actions\Jetstream\InviteTeamMember; use App\Actions\Jetstream\RemoveTeamMember; use App\Actions\Jetstream\UpdateTeamName; +use App\Models\User; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Hash; use Illuminate\Support\ServiceProvider; +use Laravel\Fortify\Fortify; use Laravel\Jetstream\Jetstream; class JetstreamServiceProvider extends ServiceProvider { /** * Register any application services. - * * @return void */ public function register() @@ -26,7 +29,6 @@ class JetstreamServiceProvider extends ServiceProvider /** * Bootstrap any application services. - * * @return void */ public function boot() @@ -40,11 +42,22 @@ class JetstreamServiceProvider extends ServiceProvider Jetstream::removeTeamMembersUsing(RemoveTeamMember::class); Jetstream::deleteTeamsUsing(DeleteTeam::class); Jetstream::deleteUsersUsing(DeleteUser::class); + + Fortify::authenticateUsing(function (Request $request) { + $user = User::query() + ->whereBlind('email', 'email_index', $request->email) + ->first(); + + if ($user && + Hash::check($request->password, $user->password)) { + + return $user; + } + }); } /** * Configure the roles and permissions that are available within the application. - * * @return void */ protected function configurePermissions() @@ -56,12 +69,14 @@ class JetstreamServiceProvider extends ServiceProvider 'read', 'update', 'delete', - ])->description('Administrator users can perform any action.'); + ]) + ->description('Administrator users can perform any action.'); Jetstream::role('editor', 'Editor', [ 'read', 'create', 'update', - ])->description('Editor users have the ability to read, create, and update.'); + ]) + ->description('Editor users have the ability to read, create, and update.'); } } diff --git a/composer.json b/composer.json index 8279307e..6ce2ce06 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "simplito/elliptic-php": "^1.0", "spatie/eloquent-sortable": "^4.0", "spatie/icalendar-generator": "^2.5", + "spatie/laravel-ciphersweet": "^1.0", "spatie/laravel-comments": "^1.4", "spatie/laravel-comments-livewire": "^1.2", "spatie/laravel-google-fonts": "^1.2", diff --git a/composer.lock b/composer.lock index 3954ca7f..ad5abd1a 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": "de6fecaf71d0b9f0abef4ce825043f92", + "content-hash": "1c4001e831e2129cc09124c228198ef2", "packages": [ { "name": "akuechler/laravel-geoly", @@ -5400,6 +5400,67 @@ ], "time": "2023-01-19T13:22:14+00:00" }, + { + "name": "paragonie/ciphersweet", + "version": "v4.2.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/ciphersweet.git", + "reference": "7459af64c412453ffa8813518c44292a9742c326" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/ciphersweet/zipball/7459af64c412453ffa8813518c44292a9742c326", + "reference": "7459af64c412453ffa8813518c44292a9742c326", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "paragonie/constant_time_encoding": "^2", + "paragonie/sodium_compat": ">= 1.17 <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-2", + "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.2.0" + }, + "time": "2023-01-15T19:02:28+00:00" + }, { "name": "paragonie/constant_time_encoding", "version": "v2.6.3", @@ -8259,6 +8320,78 @@ }, "time": "2021-12-21T10:08:05+00:00" }, + { + "name": "spatie/laravel-ciphersweet", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-ciphersweet.git", + "reference": "cc336648b94f88e9d0fd4be11c14e61e2f5882e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-ciphersweet/zipball/cc336648b94f88e9d0fd4be11c14e61e2f5882e0", + "reference": "cc336648b94f88e9d0fd4be11c14e61e2f5882e0", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.19|^10.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", + "nunomaduro/larastan": "^2.0.1", + "orchestra/testbench": "^7.0|^8.0", + "pestphp/pest": "^1.21", + "pestphp/pest-plugin-laravel": "^1.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.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.0.4" + }, + "time": "2023-01-26T12:33:48+00:00" + }, { "name": "spatie/laravel-comments", "version": "1.4.3", diff --git a/config/ciphersweet.php b/config/ciphersweet.php new file mode 100644 index 00000000..7b083b48 --- /dev/null +++ b/config/ciphersweet.php @@ -0,0 +1,36 @@ + env('CIPHERSWEET_BACKEND', 'nacl'), + + /** + * 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" + */ + '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'), + ], + ], +]; diff --git a/database/migrations/2023_02_03_151808_create_blind_indexes_table.php b/database/migrations/2023_02_03_151808_create_blind_indexes_table.php new file mode 100644 index 00000000..6eb58447 --- /dev/null +++ b/database/migrations/2023_02_03_151808_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/database/migrations/2023_02_03_152818_prepare_cyphersweet_on_models.php b/database/migrations/2023_02_03_152818_prepare_cyphersweet_on_models.php new file mode 100644 index 00000000..63c19c1c --- /dev/null +++ b/database/migrations/2023_02_03_152818_prepare_cyphersweet_on_models.php @@ -0,0 +1,34 @@ +text('public_key')->nullable()->change(); + $table->text('email')->nullable()->change(); + $table->text('node_id')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + // + }); + } +}; diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php new file mode 100644 index 00000000..d7bb9204 --- /dev/null +++ b/resources/views/errors/503.blade.php @@ -0,0 +1,18 @@ + +Site Maintenance + + +
+

We’ll be back soon!

+
+

Sorry for the inconvenience but we’re performing some maintenance at the moment. If you need to you can always contact us, otherwise we’ll be back online shortly!

+

— Einundzwanzig Team

+
+
diff --git a/resources/views/livewire/book-case/highscore-table.blade.php b/resources/views/livewire/book-case/highscore-table.blade.php index c506ac1f..41d19641 100644 --- a/resources/views/livewire/book-case/highscore-table.blade.php +++ b/resources/views/livewire/book-case/highscore-table.blade.php @@ -57,13 +57,21 @@ @endif + @php + $address = match(true) { + $pleb->lightning_address !== '' => $pleb->lightning_address, + $pleb->lnurl !== '' => $pleb->lnurl, + $pleb->node_id !== '' => $pleb->node_id, + default => null, + }; + @endphp
- @if($pleb->lightning_address || $pleb->lnurl || $pleb->node_id) + @if($address)
diff --git a/routes/api.php b/routes/api.php index 7c182e35..1d394773 100644 --- a/routes/api.php +++ b/routes/api.php @@ -34,9 +34,10 @@ Route::middleware([]) ->group(function () { Route::resource('languages', \App\Http\Controllers\Api\LanguageController::class); Route::get('meetups', function () { - return \App\Models\Meetup::with([ - 'city', - ]) + return \App\Models\Meetup::query() + ->with([ + 'city', + ]) ->get() ->map(fn($meetup) => [ 'name' => $meetup->name, @@ -46,8 +47,8 @@ Route::middleware([]) 'country' => str($meetup->city->country->code)->upper(), 'state' => $meetup->github_data['state'] ?? null, 'city' => $meetup->city->name, - 'longitude' => (float)$meetup->city->longitude, - 'latitude' => (float)$meetup->city->latitude, + 'longitude' => (float) $meetup->city->longitude, + 'latitude' => (float) $meetup->city->latitude, 'twitter_username' => $meetup->twitter_username, 'website' => $meetup->webpage, ]); @@ -57,7 +58,7 @@ Route::middleware([]) Route::get('/lnurl-auth-callback', function (\Illuminate\Http\Request $request) { if (lnurl\auth($request->k1, $request->sig, $request->key)) { // find User by $wallet_public_key - $user = User::where('public_key', $request->key) + $user = User::query()->whereBlind('public_key', 'public_key_index', $request->key) ->first(); if (!$user) { // create User