refactor(encryption): drop client-side encryption setup endpoint

Transactions now live unencrypted in the backend; new data is never
encrypted client-side again. Remove the /api/encryption/setup endpoint,
its form request, and the dead encrypted=false transaction filter.
The decrypt-migration path (encryption/message, encrypted=true) stays.
This commit is contained in:
Víctor Falcón 2026-06-09 15:03:59 +02:00
parent b76a0de074
commit 418f38fe92
6 changed files with 1 additions and 124 deletions

View File

@ -21,8 +21,6 @@ class TransactionController extends Controller
if ($request->query('encrypted') === 'true') {
$query->where(fn ($q) => $q->whereNotNull('description_iv')->orWhereNotNull('notes_iv'));
} elseif ($request->query('encrypted') === 'false') {
$query->whereNull('description_iv')->whereNull('notes_iv');
}
$transactions = $query->simplePaginate(100);

View File

@ -2,34 +2,11 @@
namespace App\Http\Controllers;
use App\Http\Requests\SetupEncryptionRequest;
use App\Models\EncryptedMessage;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class EncryptionController extends Controller
{
public function setup(SetupEncryptionRequest $request): JsonResponse
{
$user = $request->user();
$user->update([
'encryption_salt' => $request->validated('salt'),
]);
EncryptedMessage::query()->updateOrCreate(
['user_id' => $user->id],
[
'encrypted_content' => $request->validated('encrypted_content'),
'iv' => $request->validated('iv'),
]
);
return response()->json([
'message' => 'Encryption setup completed successfully',
]);
}
public function getMessage(Request $request): JsonResponse
{
$user = $request->user();

View File

@ -1,31 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class SetupEncryptionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'salt' => ['required', 'string', 'size:24'],
'encrypted_content' => ['required', 'string'],
'iv' => ['required', 'string', 'size:16'],
];
}
}

View File

@ -14,8 +14,7 @@ use App\Http\Controllers\Sync\TransactionSyncController;
use Illuminate\Support\Facades\Route;
Route::middleware(['web', 'auth'])->group(function () {
// Encryption
Route::post('encryption/setup', [EncryptionController::class, 'setup']);
// Encryption (legacy decrypt-migration support only)
Route::get('encryption/message', [EncryptionController::class, 'getMessage']);
// Import Data (for import drawers)

View File

@ -27,23 +27,6 @@ test('encrypted transactions endpoint returns only encrypted transactions', func
expect($data[0]['id'])->toBe($encrypted->id);
});
test('plaintext filter returns only plaintext transactions', function () {
Transaction::factory()->create([
'user_id' => $this->user->id,
'description_iv' => 'some-iv',
]);
$plaintext = Transaction::factory()->plaintext()->create([
'user_id' => $this->user->id,
]);
$response = $this->getJson('/api/transactions?encrypted=false');
$response->assertOk();
$data = $response->json('data');
expect($data)->toHaveCount(1);
expect($data[0]['id'])->toBe($plaintext->id);
});
test('encrypted transactions endpoint paginates correctly', function () {
$account = Account::factory()->create(['user_id' => $this->user->id]);
$category = Category::factory()->create(['user_id' => $this->user->id]);

View File

@ -4,7 +4,6 @@ use App\Models\EncryptedMessage;
use App\Models\User;
use function Pest\Laravel\actingAs;
use function Pest\Laravel\assertDatabaseHas;
test('authenticated user without encryption salt can access setup page', function () {
$user = User::factory()->create(['encryption_salt' => null]);
@ -14,54 +13,6 @@ test('authenticated user without encryption salt can access setup page', functio
$response->assertSuccessful();
});
test('user can setup encryption', function () {
$user = User::factory()->create(['encryption_salt' => null]);
$response = actingAs($user)->postJson('/api/encryption/setup', [
'salt' => str_repeat('a', 24),
'encrypted_content' => 'encrypted_test_content',
'iv' => str_repeat('b', 16),
]);
$response->assertSuccessful();
$user->refresh();
expect($user->encryption_salt)->toBe(str_repeat('a', 24));
assertDatabaseHas('encrypted_messages', [
'user_id' => $user->id,
'encrypted_content' => 'encrypted_test_content',
'iv' => str_repeat('b', 16),
]);
});
test('encryption setup requires valid salt', function () {
$user = User::factory()->create(['encryption_salt' => null]);
$response = actingAs($user)->postJson('/api/encryption/setup', [
'salt' => 'invalid',
'encrypted_content' => 'encrypted_test_content',
'iv' => str_repeat('b', 16),
]);
$response->assertUnprocessable();
$response->assertJsonValidationErrors(['salt']);
});
test('encryption setup requires valid iv', function () {
$user = User::factory()->create(['encryption_salt' => null]);
$response = actingAs($user)->postJson('/api/encryption/setup', [
'salt' => str_repeat('a', 24),
'encrypted_content' => 'encrypted_test_content',
'iv' => 'invalid',
]);
$response->assertUnprocessable();
$response->assertJsonValidationErrors(['iv']);
});
test('user can retrieve encrypted message', function () {
$user = User::factory()->create([
'encryption_salt' => str_repeat('a', 24),