package api import ( "errors" "net/http" "codeberg.org/SquidSE/ClusterCanvas/service/internal/settings" ) var errUsersManageRequired = errors.New("permission users.manage required") var errNodesReadRequired = errors.New("permission nodes.read required") var errNodesExecRequired = errors.New("permission nodes.exec required") var errNodesDeleteRequired = errors.New("permission nodes.delete required") var errActionsCreateRequired = errors.New("permission actions.create required") var errActionsUpdateRequired = errors.New("permission actions.update required") var errActionsDeleteRequired = errors.New("permission actions.delete required") var errActionsExecuteRequired = errors.New("permission actions.execute required") var errJobsRunRequired = errors.New("permission jobs.run required") var errLogsReadRequired = errors.New("permission logs.read required") // permissionsForUser returns the union of permissions from the user's groups. func permissionsForUser(user settings.UserCredential, groups []settings.Group) map[string]struct{} { groupByName := make(map[string]settings.Group, len(groups)) for _, group := range groups { groupByName[group.Name] = group } result := make(map[string]struct{}) for _, groupName := range user.GroupNames { group, ok := groupByName[groupName] if !ok { continue } for _, permission := range group.Permissions { result[permission] = struct{}{} } } return result } func userHasPermission(user settings.UserCredential, groups []settings.Group, permission string) bool { _, ok := permissionsForUser(user, groups)[permission] return ok } func permissionList(user settings.UserCredential, groups []settings.Group) []string { set := permissionsForUser(user, groups) ordered := allPermissionList() result := make([]string, 0, len(set)) for _, permission := range ordered { if _, ok := set[permission]; ok { result = append(result, permission) } } return result } func authorizeNamedPermission(request *http.Request, configDir string, permission string, missing error) error { user, ok := UserFromContext(request.Context()) if !ok { return errors.New("authentication required") } payload, err := loadSettingsOrDefault(configDir) if err != nil { return err } if !userHasPermission(user, payload.Groups, permission) { return missing } return nil } func (app *App) authorizeUsersWrite(request *http.Request) error { user, ok := UserFromContext(request.Context()) if !ok { return errors.New("authentication required") } payload, err := loadSettingsOrDefault(app.ConfigDir) if err != nil { return err } if !userHasPermission(user, payload.Groups, "users.manage") { return errUsersManageRequired } return nil } func (app *App) authorizeNodesRead(request *http.Request) error { user, ok := UserFromContext(request.Context()) if !ok { return errors.New("authentication required") } payload, err := loadSettingsOrDefault(app.ConfigDir) if err != nil { return err } if !userHasPermission(user, payload.Groups, "nodes.read") { return errNodesReadRequired } return nil } func (app *App) authorizeLogsRead(request *http.Request) error { user, ok := UserFromContext(request.Context()) if !ok { return errors.New("authentication required") } payload, err := loadSettingsOrDefault(app.ConfigDir) if err != nil { return err } if !userHasPermission(user, payload.Groups, "logs.read") { return errLogsReadRequired } return nil } func (app *App) authorizeNodesExec(request *http.Request) error { user, ok := UserFromContext(request.Context()) if !ok { return errors.New("authentication required") } payload, err := loadSettingsOrDefault(app.ConfigDir) if err != nil { return err } if !userHasPermission(user, payload.Groups, "nodes.exec") { return errNodesExecRequired } return nil } func (app *App) authorizeNodesDelete(request *http.Request) error { user, ok := UserFromContext(request.Context()) if !ok { return errors.New("authentication required") } payload, err := loadSettingsOrDefault(app.ConfigDir) if err != nil { return err } if !userHasPermission(user, payload.Groups, "nodes.delete") { return errNodesDeleteRequired } return nil } func authorizeActionsCreate(request *http.Request, configDir string) error { return authorizeNamedPermission(request, configDir, "actions.create", errActionsCreateRequired) } func authorizeActionsUpdate(request *http.Request, configDir string) error { return authorizeNamedPermission(request, configDir, "actions.update", errActionsUpdateRequired) } func authorizeActionsDelete(request *http.Request, configDir string) error { return authorizeNamedPermission(request, configDir, "actions.delete", errActionsDeleteRequired) } func authorizeActionsExecute(request *http.Request, configDir string) error { return authorizeNamedPermission(request, configDir, "actions.execute", errActionsExecuteRequired) } func (app *App) authorizeJobsRun(request *http.Request) error { user, ok := UserFromContext(request.Context()) if !ok { return errors.New("authentication required") } payload, err := loadSettingsOrDefault(app.ConfigDir) if err != nil { return err } if !userHasPermission(user, payload.Groups, "jobs.run") { return errJobsRunRequired } return nil }