Add SSH-managed node registry with connection testing and reauth.
Register hosts under Containers/VMs/Docker with encrypted key storage, and require re-authentication for sensitive account changes.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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")
|
||||
|
||||
// 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 (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) 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
|
||||
}
|
||||
Reference in New Issue
Block a user