Files
ClusterCanvas/service/internal/api/authz.go
T
Squid 463aa9a7a3 Add activity logs, sidebar badges, and logout with working alert colors.
Record auth audits and surface them in Activity, poll sidebar counts without resetting idle, and add a profile logout path plus theme red/green/blue tokens so failure badges render.
2026-07-18 21:43:44 +02:00

163 lines
4.5 KiB
Go

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 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)
}