internal/rbac: drei Grundrollen (superadmin, tenant_admin, user) mit Hierarchie ueber eine einfache Eltern-Map (tenant_admin erbt von user, superadmin erbt von tenant_admin) — EffectivePermissions loest die volle vererbte Rechtemenge auf (Akzeptanzkriterium 2). Policy-Modell bewusst als reine Go-Datenstruktur getrennt von der Durchsetzung (RBAC-02), nach Casbin-Prinzip. Store verwaltet Rollenzuweisungen innerhalb EINER Tenant-Datenbank (Modell C, analog internal/user.TenantUserStore) — nur 'user' und 'tenant_admin' sind hier zuweisbar (assignableRoles-Matrix). Ein Zuweisungsversuch fuer 'superadmin' wird abgewiesen, da diese Rolle mandantenuebergreifend ist und bereits durch die Existenz eines Kontos in IAM-01s SuperadminStore repraesentiert wird — keine doppelte Modellierung. Jede Zuweisung schreibt zusaetzlich einen Historieneintrag (role_assignment_history) mit grantedBy/grantedAt, atomar in derselben Transaktion (Akzeptanzkriterium 3). Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS): 1. Zuweisung ausserhalb der erlaubten Matrix abgewiesen — TestStore_RejectsSuperadminOutsideAllowedMatrix und TestStore_RejectsUnknownRole: beide ErrRoleNotAssignableInTenantScope, kein Datensatz hinterlassen. PASS. 2. Rollenhierarchie liefert erwartete effektive Rechtemenge — TestEffectivePermissions_Inheritance: tenant_admin hat geerbte user-Rechte + eigene, aber nicht platform.manage_tenants; superadmin hat die volle Kette. PASS. 3. Datenmodell von zweiter Person gegen Dokumentation geprueft — NICHT durchgefuehrt (keine zweite Person in dieser Session verfuegbar). Offen. Zusaetzlich automatisiert getestet (Akzeptanzkriterium 3): TestStore_HistoryTracksWhoAndWhen — zwei aufeinanderfolgende Zuweisungen, Historie liefert beide mit korrektem grantedBy in chronologischer Reihenfolge. PASS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
72 lines
2.2 KiB
Go
72 lines
2.2 KiB
Go
// Package rbac implementiert Core RBAC-01: das Rollenmodell mit
|
|
// Grundrollen/-rechten, Hierarchie und Zuweisung. Die eigentliche
|
|
// Durchsetzung (erlaubt/verboten je Request) ist RBAC-02, dieses Paket
|
|
// liefert nur Modell + Zuweisung (Casbin-Prinzip: Policy-Modell getrennt
|
|
// von der Entscheidungsfunktion).
|
|
package rbac
|
|
|
|
// Role ist eine der drei Grundrollen. Superadmin lebt mandantenuebergreifend
|
|
// (siehe internal/user.SuperadminStore aus IAM-01) und wird deshalb NICHT
|
|
// ueber Store (tenant-gescoped) zugewiesen — die Existenz eines Superadmin-
|
|
// Kontos IST die Rollenzuweisung.
|
|
type Role string
|
|
|
|
const (
|
|
RoleSuperadmin Role = "superadmin"
|
|
RoleTenantAdmin Role = "tenant_admin"
|
|
RoleUser Role = "user"
|
|
)
|
|
|
|
type Permission string
|
|
|
|
const (
|
|
PermSelfRead Permission = "self.read"
|
|
PermSelfUpdate Permission = "self.update"
|
|
PermManageUsers Permission = "tenant.manage_users"
|
|
PermManageSettings Permission = "tenant.manage_settings"
|
|
PermManageTenants Permission = "platform.manage_tenants"
|
|
)
|
|
|
|
// parent bildet die Rollenhierarchie ab (Akzeptanzkriterium 2): eine Rolle
|
|
// erbt alle Rechte ihrer Elternrolle. tenant_admin erbt von user,
|
|
// superadmin erbt von tenant_admin.
|
|
var parent = map[Role]Role{
|
|
RoleTenantAdmin: RoleUser,
|
|
RoleSuperadmin: RoleTenantAdmin,
|
|
}
|
|
|
|
// direct sind die einer Rolle direkt (ohne Vererbung) zugeordneten Rechte.
|
|
var direct = map[Role][]Permission{
|
|
RoleUser: {PermSelfRead, PermSelfUpdate},
|
|
RoleTenantAdmin: {PermManageUsers, PermManageSettings},
|
|
RoleSuperadmin: {PermManageTenants},
|
|
}
|
|
|
|
// EffectivePermissions liefert die vollstaendige, ueber die Hierarchie
|
|
// aufgeloeste Rechtemenge einer Rolle (Akzeptanzkriterium 2 / Pruefung 2).
|
|
func EffectivePermissions(role Role) []Permission {
|
|
seen := make(map[Permission]bool)
|
|
var out []Permission
|
|
|
|
for r, ok := role, true; ok; r, ok = parent[r] {
|
|
for _, p := range direct[r] {
|
|
if !seen[p] {
|
|
seen[p] = true
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// HasPermission prueft, ob eine Rolle (inklusive geerbter Rechte) ein
|
|
// bestimmtes Recht besitzt.
|
|
func HasPermission(role Role, perm Permission) bool {
|
|
for _, p := range EffectivePermissions(role) {
|
|
if p == perm {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|