feat(09-11): add permissioned admin metadata and settings

This commit is contained in:
Jakub Zych
2026-09-26 23:06:22 +02:00
parent 8b02fff028
commit 10ca7a02e3
12 changed files with 947 additions and 7 deletions

View File

@@ -71,3 +71,185 @@ func compileRegistry(items []controllerRef) (*Registry, error) {
}
return &Registry{byID: byID}, nil
}
func compileContributions(reg *Registry, plugins []party.Plugin) error {
if reg == nil {
return fmt.Errorf("cabana: registry is nil")
}
reg.permissions = map[string]pact.Permission{}
reg.roleGrants = map[string]map[string]bool{}
reg.settings = map[string]*CompiledSetting{}
seenNavigation := map[string]struct{}{}
for _, plugin := range plugins {
if plugin == nil {
continue
}
if src, ok := plugin.(pact.HasPermissions); ok && src != nil {
for _, permission := range src.Permissions() {
if !permissionCode(permission.Code, false) {
return fmt.Errorf("cabana: plugin %s registered invalid permission %q", plugin.ID(), permission.Code)
}
if _, exists := reg.permissions[permission.Code]; exists {
return fmt.Errorf("cabana: duplicate permission %s", permission.Code)
}
reg.permissions[permission.Code] = permission
for _, role := range permission.Roles {
role = strings.TrimSpace(role)
if role == "" {
return fmt.Errorf("cabana: permission %s has an empty role", permission.Code)
}
if reg.roleGrants[role] == nil {
reg.roleGrants[role] = map[string]bool{}
}
reg.roleGrants[role][permission.Code] = true
}
}
}
if src, ok := plugin.(pact.HasSettings); ok && src != nil {
assets, hasAssets := plugin.(pact.AdminAssets)
for _, item := range src.Settings() {
if !identifier(item.Code) {
return fmt.Errorf("cabana: plugin %s registered invalid setting %q", plugin.ID(), item.Code)
}
if _, exists := reg.settings[item.Code]; exists {
return fmt.Errorf("cabana: duplicate setting %s", item.Code)
}
if !hasAssets || assets == nil || assets.AdminFS() == nil {
return fmt.Errorf("cabana: plugin %s has settings but no AdminFS", plugin.ID())
}
compiled, err := compileSetting(plugin.ID(), item, assets.AdminFS())
if err != nil {
return err
}
reg.settings[item.Code] = compiled
}
}
if src, ok := plugin.(pact.HasNavigation); ok && src != nil {
for _, item := range src.Navigation() {
if err := validateNavigationShape(plugin.ID(), item, seenNavigation); err != nil {
return err
}
reg.navigation = append(reg.navigation, item)
}
}
}
for id, controller := range reg.byID {
if err := reg.validatePermissions("controller "+id, requiredOf(controller.Controller)); err != nil {
return err
}
for name, relation := range controller.Relations {
if err := reg.validatePermissions("relation "+id+"."+name, relation.RequiredPermissions); err != nil {
return err
}
}
}
for _, item := range reg.navigation {
if err := reg.validateNavigation(item); err != nil {
return err
}
}
for code, setting := range reg.settings {
if err := reg.validatePermissions("setting "+code, setting.Item.Permissions); err != nil {
return err
}
}
return nil
}
func permissionCode(code string, wildcard bool) bool {
parts := strings.Split(code, ".")
if len(parts) < 2 {
return false
}
for i, part := range parts {
if wildcard && i == len(parts)-1 && part == "*" {
return true
}
if !identifier(part) {
return false
}
}
return true
}
func (r *Registry) validatePermissions(owner string, permissions []string) error {
for _, code := range permissions {
if !permissionCode(code, true) || !r.permissionExists(code) {
return fmt.Errorf("cabana: %s references unknown permission %s", owner, code)
}
}
return nil
}
func (r *Registry) permissionExists(code string) bool {
if r == nil {
return false
}
if _, ok := r.permissions[code]; ok {
return true
}
if !strings.HasSuffix(code, ".*") {
return false
}
prefix := strings.TrimSuffix(code, "*")
for candidate := range r.permissions {
if strings.HasPrefix(candidate, prefix) {
return true
}
}
return false
}
func validateNavigationShape(pluginID string, item pact.NavigationItem, seen map[string]struct{}) error {
if !identifier(item.Code) {
return fmt.Errorf("cabana: plugin %s registered invalid navigation code %q", pluginID, item.Code)
}
if _, exists := seen[item.Code]; exists {
return fmt.Errorf("cabana: duplicate navigation code %s", item.Code)
}
seen[item.Code] = struct{}{}
childSeen := map[string]struct{}{}
for _, child := range item.SideMenu {
if !identifier(child.Code) {
return fmt.Errorf("cabana: navigation %s has invalid child code %q", item.Code, child.Code)
}
if _, exists := childSeen[child.Code]; exists {
return fmt.Errorf("cabana: navigation %s has duplicate child %s", item.Code, child.Code)
}
childSeen[child.Code] = struct{}{}
}
return nil
}
func (r *Registry) validateNavigation(item pact.NavigationItem) error {
if _, ok := r.byID[item.Controller]; !ok {
return fmt.Errorf("cabana: navigation %s references unknown controller %s", item.Code, item.Controller)
}
if err := r.validatePermissions("navigation "+item.Code, item.Permissions); err != nil {
return err
}
for _, child := range item.SideMenu {
if _, ok := r.byID[child.Controller]; !ok {
return fmt.Errorf("cabana: navigation %s.%s references unknown controller %s", item.Code, child.Code, child.Controller)
}
if err := r.validatePermissions("navigation "+item.Code+"."+child.Code, child.Permissions); err != nil {
return err
}
}
return nil
}
func (r *Registry) rolePermissions(role string) map[string]bool {
out := map[string]bool{}
if r == nil {
return out
}
for code, allowed := range r.roleGrants[role] {
if allowed {
out[code] = true
}
}
return out
}