auth: automatische Firmenrechte mit Herkunft und Entzug, Nachweis vor Sicherheitsaktionen (Audit Block 1, Deploy C)
F10: company_user.granted_via unterscheidet automatisch vergebene Rechte (E-Mail-Sync) von manuellen und aus dem Altbestand. Beim Login senkt der Sync automatische responsible-Rechte ohne aktuellen E-Mail- Treffer auf member (E2) — Mitarbeiterwechsel nehmen den Schreibzugriff mit, Lesezugriff bleibt. Das Admin-Formular markiert nur neue oder geänderte Zeilen als manual, unveränderte behalten ihre Herkunft. Bestandsprüfung: php artisan company-memberships:audit listet responsible-Zuordnungen ohne E-Mail-Grundlage (Tabelle ab Go-Live, CSV vollständig) und markiert mit --classify die erklärbaren als auto. Auf Live nach der Migration einmal mit --classify laufen lassen; nicht erklärbare Zeilen bleiben manual und damit geschützt. F03 (E3): Zwei-Faktor deaktivieren, Wiederherstellungs-Codes erneuern und die E-Mail-Änderung verlangen einen aktuellen App-Code oder einen Wiederherstellungs-Code (der dabei verbraucht wird); ohne 2FA das Passwort. Reine Magic-Link-Konten ohne Passwort (der Altbestand) können die Adresse weiter ändern — die neue Adresse muss ihren Bestätigungslink ohnehin einlösen. Fünf Fehlversuche pro Minute. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
a7f991f01e
commit
62202a4b13
10 changed files with 596 additions and 24 deletions
|
|
@ -15,6 +15,7 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
|
|||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Cashier\Billable;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
|
|
@ -373,7 +374,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
public function companies(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Company::class)
|
||||
->withPivot('role')
|
||||
->withPivot('role', 'granted_via')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
|
|
@ -601,10 +602,24 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
*/
|
||||
public function syncCompaniesWithRoles(array $companyIds, array $roles): void
|
||||
{
|
||||
// Herkunft (Audit F10): Was der Admin neu anlegt oder in der Rolle
|
||||
// ändert, ist `manual` und bleibt vom automatischen Entzug unberührt.
|
||||
// Unveränderte Zeilen behalten ihre Herkunft – sonst würde jedes
|
||||
// Speichern des Formulars alle automatischen Rechte zementieren.
|
||||
$current = DB::table('company_user')
|
||||
->where('user_id', $this->getKey())
|
||||
->pluck('role', 'company_id')
|
||||
->mapWithKeys(fn (string $role, int|string $companyId): array => [(int) $companyId => $role]);
|
||||
|
||||
$payload = [];
|
||||
|
||||
foreach ($companyIds as $companyId) {
|
||||
$payload[$companyId] = ['role' => $roles[$companyId] ?? 'member'];
|
||||
$role = $roles[$companyId] ?? 'member';
|
||||
$payload[$companyId] = ['role' => $role];
|
||||
|
||||
if (! isset($current[(int) $companyId]) || $current[(int) $companyId] !== $role) {
|
||||
$payload[$companyId]['granted_via'] = 'manual';
|
||||
}
|
||||
}
|
||||
|
||||
$this->companies()->sync($payload);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue