Files
opencloud/services/graph/pkg/validate/libregraph.go
2025-05-15 14:11:35 +02:00

140 lines
4.1 KiB
Go

package validate
import (
"context"
"slices"
"github.com/go-playground/validator/v10"
libregraph "github.com/opencloud-eu/libre-graph-api-go"
"github.com/opencloud-eu/opencloud/services/graph/pkg/unifiedrole"
)
type contextKey int
const (
ContextKeyRoleIDsValueKey contextKey = iota
)
// initLibregraph initializes libregraph validation
func initLibregraph(v *validator.Validate) {
for _, f := range []func(*validator.Validate){
libregraphDriveItemInvite,
libregraphDriveRecipient,
libregraphPermission,
} {
f(v)
}
}
// libregraphDriveItemInvite validates libregraph.DriveItemInvite
func libregraphDriveItemInvite(v *validator.Validate) {
s := libregraph.DriveItemInvite{}
v.RegisterStructValidationMapRules(map[string]string{
"Recipients": "len=1,dive",
"Roles": "max=1",
"ExpirationDateTime": "omitnil,gt",
}, s)
v.RegisterStructValidationCtx(func(ctx context.Context, sl validator.StructLevel) {
driveItemInvite := sl.Current().Interface().(libregraph.DriveItemInvite)
rolesAndActions(ctx, sl, driveItemInvite.Roles, driveItemInvite.LibreGraphPermissionsActions, false)
}, s)
}
// libregraphDriveRecipient validates libregraph.DriveRecipient
func libregraphDriveRecipient(v *validator.Validate) {
v.RegisterStructValidationMapRules(map[string]string{
"ObjectId": "ne=",
"LibreGraphRecipientType": "oneof=user group",
}, libregraph.DriveRecipient{})
}
// libregraphPermission validates libregraph.Permission
func libregraphPermission(v *validator.Validate) {
s := libregraph.Permission{}
v.RegisterStructValidationMapRules(map[string]string{
"Roles": "max=1",
}, s)
v.RegisterStructValidationCtx(func(ctx context.Context, sl validator.StructLevel) {
permission := sl.Current().Interface().(libregraph.Permission)
if _, ok := permission.GetIdOk(); ok {
sl.ReportError(permission.Id, "Id", "Id", "readonly", "")
}
if _, ok := permission.GetHasPasswordOk(); ok {
sl.ReportError(permission.HasPassword, "hasPassword", "HasPassword", "readonly", "")
}
rolesAndActions(ctx, sl, permission.Roles, permission.LibreGraphPermissionsActions, true)
}, s)
}
func rolesAndActions(ctx context.Context, sl validator.StructLevel, roles, actions []string, allowEmpty bool) {
totalRoles := len(roles)
totalActions := len(actions)
switch {
case allowEmpty && totalRoles == 0 && totalActions == 0:
break
case totalRoles != 0 && totalActions != 0:
fallthrough
case totalRoles == totalActions:
sl.ReportError(roles, "Roles", "Roles", "one_or_another", "")
sl.ReportError(actions, "LibreGraphPermissionsActions", "LibreGraphPermissionsActions", "one_or_another", "")
}
var availableRoles []string
var availableActions []string
var definitions []*libregraph.UnifiedRoleDefinition
switch roles, ok := ctx.Value(ContextKeyRoleIDsValueKey).([]string); {
case ok:
definitions = unifiedrole.GetRoles(unifiedrole.RoleFilterIDs(roles...))
default:
// it the ctx does not contain the allowed role IDs, we need to fall back to all roles
definitions = unifiedrole.GetRoles(unifiedrole.RoleFilterAll())
}
for _, definition := range definitions {
if slices.Contains(availableRoles, definition.GetId()) {
continue
}
availableRoles = append(availableRoles, definition.GetId())
for _, permission := range definition.GetRolePermissions() {
for _, action := range permission.GetAllowedResourceActions() {
if slices.Contains(availableActions, action) {
continue
}
availableActions = append(availableActions, action)
}
}
}
for _, role := range roles {
if slices.Contains(availableRoles, role) {
continue
}
sl.ReportError(roles, "Roles", "Roles", "available_role", "")
}
for _, action := range actions {
if slices.Contains(availableActions, action) {
continue
}
sl.ReportError(actions, "LibreGraphPermissionsActions", "LibreGraphPermissionsActions", "available_action", "")
}
}
// ContextWithAllowedRoleIDs returns a new context which includes the allowed role IDs.
func ContextWithAllowedRoleIDs(ctx context.Context, rolesIds []string) context.Context {
return context.WithValue(ctx, ContextKeyRoleIDsValueKey, rolesIds)
}