ops design and app
add more comments feature, more tags feature, and other frontend updates
This commit is contained in:
@@ -409,6 +409,96 @@ func (s *Service) UpdateUserAccess(ctx context.Context, actor Principal, updates
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (s *Service) ListResourcePermissions(ctx context.Context, actor Principal, resourceType ResourceType, resourceID string) ([]ResourcePermission, error) {
|
||||
if actor.UserID == "" {
|
||||
return nil, fmt.Errorf("authentication required")
|
||||
}
|
||||
if actor.Role != RoleAdmin {
|
||||
permission, err := s.repo.EffectiveResourcePermission(ctx, actor, resourceType, resourceID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !PermissionAllows(permission, PermissionAdmin) {
|
||||
return nil, fmt.Errorf("admin permission required")
|
||||
}
|
||||
}
|
||||
return s.repo.ListResourcePermissions(ctx, resourceType, resourceID)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateResourcePermissions(ctx context.Context, actor Principal, resourceType ResourceType, resourceID string, updates []ResourcePermissionUpdate) ([]ResourcePermission, error) {
|
||||
if actor.UserID == "" {
|
||||
return nil, fmt.Errorf("authentication required")
|
||||
}
|
||||
if actor.Role != RoleAdmin {
|
||||
permission, err := s.repo.EffectiveResourcePermission(ctx, actor, resourceType, resourceID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !PermissionAllows(permission, PermissionAdmin) {
|
||||
return nil, fmt.Errorf("admin permission required")
|
||||
}
|
||||
}
|
||||
normalized := make([]ResourcePermissionUpdate, 0, len(updates))
|
||||
seen := make(map[string]struct{}, len(updates))
|
||||
for _, update := range updates {
|
||||
update.UserID = strings.TrimSpace(update.UserID)
|
||||
if update.UserID == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[update.UserID]; ok {
|
||||
return nil, fmt.Errorf("duplicate user permission")
|
||||
}
|
||||
seen[update.UserID] = struct{}{}
|
||||
update.Permission = normalizePermission(update.Permission)
|
||||
if !validPermission(update.Permission) {
|
||||
return nil, fmt.Errorf("invalid permission")
|
||||
}
|
||||
normalized = append(normalized, update)
|
||||
}
|
||||
if err := s.repo.ReplaceResourcePermissions(ctx, resourceType, resourceID, actor.UserID, normalized); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.repo.Audit(ctx, actor.UserID, "auth.resource.permissions.update", string(resourceType), resourceID, "", "", map[string]any{
|
||||
"updates": len(normalized),
|
||||
})
|
||||
return s.repo.ListResourcePermissions(ctx, resourceType, resourceID)
|
||||
}
|
||||
|
||||
func (s *Service) EnsureResourcePermission(ctx context.Context, actor Principal, resourceType ResourceType, resourceID string, update ResourcePermissionUpdate) error {
|
||||
if actor.UserID == "" {
|
||||
return fmt.Errorf("authentication required")
|
||||
}
|
||||
update.UserID = strings.TrimSpace(update.UserID)
|
||||
if update.UserID == "" {
|
||||
return fmt.Errorf("user id is required")
|
||||
}
|
||||
update.Permission = normalizePermission(update.Permission)
|
||||
if !validPermission(update.Permission) || update.Permission == PermissionNone {
|
||||
return fmt.Errorf("invalid permission")
|
||||
}
|
||||
if update.UserID != actor.UserID && actor.Role != RoleAdmin {
|
||||
permission, err := s.repo.EffectiveResourcePermission(ctx, actor, resourceType, resourceID, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !PermissionAllows(permission, PermissionAdmin) {
|
||||
return fmt.Errorf("admin permission required")
|
||||
}
|
||||
}
|
||||
if err := s.repo.EnsureResourcePermission(ctx, resourceType, resourceID, actor.UserID, update); err != nil {
|
||||
return err
|
||||
}
|
||||
s.repo.Audit(ctx, actor.UserID, "auth.resource.permissions.ensure", string(resourceType), resourceID, "", "", map[string]any{
|
||||
"userID": update.UserID,
|
||||
"permission": update.Permission,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) EffectiveResourcePermission(ctx context.Context, principal Principal, resourceType ResourceType, resourceID string, collectionIDs []string) (Permission, error) {
|
||||
return s.repo.EffectiveResourcePermission(ctx, principal, resourceType, resourceID, collectionIDs)
|
||||
}
|
||||
|
||||
func (s *Service) SendPasswordReset(ctx context.Context, actor Principal, userID string) error {
|
||||
if !Allows(actor, ScopeAdmin) {
|
||||
return fmt.Errorf("admin role required")
|
||||
@@ -715,6 +805,23 @@ func RoleAllows(role Role, required Scope) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func PermissionAllows(actual Permission, required Permission) bool {
|
||||
return permissionRank(actual) >= permissionRank(required)
|
||||
}
|
||||
|
||||
func normalizePermission(permission Permission) Permission {
|
||||
switch Permission(strings.ToLower(strings.TrimSpace(string(permission)))) {
|
||||
case "view":
|
||||
return PermissionRead
|
||||
case "edit":
|
||||
return PermissionWrite
|
||||
case PermissionRead, PermissionWrite, PermissionAdmin, PermissionNone:
|
||||
return Permission(strings.ToLower(strings.TrimSpace(string(permission))))
|
||||
default:
|
||||
return Permission(strings.ToLower(strings.TrimSpace(string(permission))))
|
||||
}
|
||||
}
|
||||
|
||||
func Allows(principal Principal, required Scope) bool {
|
||||
if principal.UserID == "" {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user