auth/security: API-Firmenzuordnung nur für Mitglieder oder Allowlist, Google-Vorabübernahme geschlossen, gemeinsamer Login-Abschluss (Umsetzungsaudit 29.09.: R-01, K-04, K-07, K-08, B-10)
- R-01: ApiNewsroomAssignment hängt an eine bestehende Firma nur noch Owner, Mitglieder oder Nutzer aus NEWSROOM_API_ASSIGNMENT_USER_IDS; Pivot mit granted_via = api; Befehl company-memberships:api-assignments (Liste, --remove mit Vorschau) - K-04: OAuth-Verifikation eines offenen Kontos verwirft Passwort, remember_token und Sanctum-Tokens; SessionRevocation + Middleware EnsureSessionIsNotRevoked widerruft alte Sessions (Redis-tauglich) - K-08: LoginCompletion für Passwort, Magic Link und Google — last_login_at und Anmeldemethode auch nach 2FA, Landeziel über die Challenge, Google ohne erzwungenes remember, magic-login ohne guest für eigene Links - B-10: eigener Magic-Link-Zweck image_confirmation (Migration) - K-07: legalAcceptanceVersions #[Locked], Versionswechsel beim Absenden wird abgelehnt, Gate-Abfragen je Request zwischengespeichert Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3dbc3fa673
commit
1ccb4a8479
29 changed files with 1287 additions and 73 deletions
|
|
@ -6,6 +6,7 @@
|
|||
'quotaTotal' => null,
|
||||
'quotaRemaining' => null,
|
||||
'hasTitleImage' => true,
|
||||
'legalDocuments' => null,
|
||||
])
|
||||
|
||||
{{--
|
||||
|
|
@ -35,8 +36,23 @@
|
|||
$bookingRequired = ! (auth()->user()?->hasActiveBooking() ?? true);
|
||||
// Zustimmungsgate (Bildkennzeichnung Block G): offene Fassungen von AGB
|
||||
// und Richtlinien, die vor dem Einreichen aktiv angenommen werden müssen.
|
||||
$legalGate = app(\App\Services\Legal\LegalAcceptanceGate::class);
|
||||
$legalDocumentsToAccept = $legalGate->requiredFor(auth()->user())->map(fn ($document) => $legalGate->describe($document))->values();
|
||||
// Die Seiten reichen das je Request einmal ermittelte Ergebnis durch
|
||||
// (InteractsWithAiDeclaration::legalDocumentsForSubmit(), OPT-7) — die
|
||||
// Liste hätte sonst je Zeile eigene Abfragen. Ohne Übergabe: selbst
|
||||
// ermitteln, bei fehlendem Pflichtdokument aber nicht beim Rendern
|
||||
// scheitern (K-07); das Absenden prüft verbindlich.
|
||||
if ($legalDocuments === null) {
|
||||
$legalGate = app(\App\Services\Legal\LegalAcceptanceGate::class);
|
||||
|
||||
try {
|
||||
$legalDocuments = $legalGate->requiredFor(auth()->user())->map(fn ($document) => $legalGate->describe($document))->values();
|
||||
} catch (\RuntimeException $exception) {
|
||||
report($exception);
|
||||
$legalDocuments = collect();
|
||||
}
|
||||
}
|
||||
|
||||
$legalDocumentsToAccept = collect($legalDocuments);
|
||||
$needsLegalAcceptance = $legalDocumentsToAccept->isNotEmpty();
|
||||
@endphp
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\Auth\LoginCompletion;
|
||||
use App\Support\LoginRedirect;
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
|
@ -49,10 +50,7 @@ new #[Layout('components.layouts.auth.pressekonto', ['heading' => 'Willkommen zu
|
|||
// 2FA aktiv: nicht einloggen, sondern in Fortifys Challenge übergeben
|
||||
// (Session-Vertrag login.id/login.remember wie RedirectIfTwoFactorAuthenticatable).
|
||||
if ($user->hasEnabledTwoFactorAuthentication()) {
|
||||
Session::put([
|
||||
'login.id' => $user->getKey(),
|
||||
'login.remember' => $this->remember,
|
||||
]);
|
||||
app(LoginCompletion::class)->handOffToTwoFactor(request(), $user, 'password', $this->remember);
|
||||
|
||||
$this->redirect(route('two-factor.challenge'));
|
||||
|
||||
|
|
@ -78,10 +76,7 @@ new #[Layout('components.layouts.auth.pressekonto', ['heading' => 'Willkommen zu
|
|||
]);
|
||||
}
|
||||
|
||||
$user->update([
|
||||
'last_login_at' => now(),
|
||||
'last_login_ip' => request()->ip(),
|
||||
]);
|
||||
app(LoginCompletion::class)->recordLogin($user, request());
|
||||
|
||||
Session::regenerate();
|
||||
|
||||
|
|
|
|||
|
|
@ -1095,6 +1095,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex
|
|||
:confirm-label="__('Zur Prüfung freigeben')"
|
||||
:quota-total="$quotaTotal"
|
||||
:quota-remaining="$quotaRemaining"
|
||||
:has-title-image="false" />
|
||||
:has-title-image="false"
|
||||
:legal-documents="$this->legalDocumentsForSubmit()" />
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1064,5 +1064,6 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl
|
|||
:confirm-label="$currentStatus === 'rejected' ? __('Speichern & erneut zur Prüfung freigeben') : __('Speichern & zur Prüfung freigeben')"
|
||||
:quota-total="$quotaTotal"
|
||||
:quota-remaining="$quotaRemaining"
|
||||
:has-title-image="! $coverIsPlaceholder" />
|
||||
:has-title-image="! $coverIsPlaceholder"
|
||||
:legal-documents="$this->legalDocumentsForSubmit()" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -494,7 +494,8 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class
|
|||
</flux:modal.trigger>
|
||||
<x-press-release-submit-modal :name="'confirm-submit-' . $pr->id"
|
||||
:action="'submitForReview(' . $pr->id . ')'"
|
||||
:has-title-image="$pr->images_count > 0" />
|
||||
:has-title-image="$pr->images_count > 0"
|
||||
:legal-documents="$this->legalDocumentsForSubmit()" />
|
||||
@endif
|
||||
</div>
|
||||
@if ($boostEndsAt = $boostEndsById->get($pr->id))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue