750 lines
25 KiB
Go
750 lines
25 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/kms/api-key-service/internal/cache"
|
|
"github.com/kms/api-key-service/internal/config"
|
|
"github.com/kms/api-key-service/internal/errors"
|
|
)
|
|
|
|
// PermissionManager handles hierarchical permission management
|
|
type PermissionManager struct {
|
|
config config.ConfigProvider
|
|
logger *zap.Logger
|
|
cacheManager *cache.CacheManager
|
|
hierarchy *PermissionHierarchy
|
|
}
|
|
|
|
// NewPermissionManager creates a new permission manager
|
|
func NewPermissionManager(config config.ConfigProvider, logger *zap.Logger) *PermissionManager {
|
|
cacheManager := cache.NewCacheManager(config, logger)
|
|
hierarchy := NewPermissionHierarchy()
|
|
|
|
return &PermissionManager{
|
|
config: config,
|
|
logger: logger,
|
|
cacheManager: cacheManager,
|
|
hierarchy: hierarchy,
|
|
}
|
|
}
|
|
|
|
// PermissionHierarchy represents the hierarchical permission structure
|
|
type PermissionHierarchy struct {
|
|
permissions map[string]*Permission
|
|
roles map[string]*Role
|
|
}
|
|
|
|
// Permission represents a single permission with its hierarchy
|
|
type Permission struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Parent string `json:"parent,omitempty"`
|
|
Children []string `json:"children"`
|
|
Level int `json:"level"`
|
|
Resource string `json:"resource"`
|
|
Action string `json:"action"`
|
|
}
|
|
|
|
// Role represents a role with associated permissions
|
|
type Role struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Permissions []string `json:"permissions"`
|
|
Inherits []string `json:"inherits"`
|
|
Metadata map[string]string `json:"metadata"`
|
|
}
|
|
|
|
// PermissionEvaluation represents the result of permission evaluation
|
|
type PermissionEvaluation struct {
|
|
Granted bool `json:"granted"`
|
|
Permission string `json:"permission"`
|
|
GrantedBy []string `json:"granted_by"`
|
|
DeniedReason string `json:"denied_reason,omitempty"`
|
|
Metadata map[string]string `json:"metadata"`
|
|
EvaluatedAt time.Time `json:"evaluated_at"`
|
|
}
|
|
|
|
// BulkPermissionRequest represents a bulk permission operation request
|
|
type BulkPermissionRequest struct {
|
|
UserID string `json:"user_id"`
|
|
AppID string `json:"app_id"`
|
|
Permissions []string `json:"permissions"`
|
|
Context map[string]string `json:"context,omitempty"`
|
|
}
|
|
|
|
// BulkPermissionResponse represents a bulk permission operation response
|
|
type BulkPermissionResponse struct {
|
|
UserID string `json:"user_id"`
|
|
AppID string `json:"app_id"`
|
|
Results map[string]*PermissionEvaluation `json:"results"`
|
|
EvaluatedAt time.Time `json:"evaluated_at"`
|
|
}
|
|
|
|
// NewPermissionHierarchy creates a new permission hierarchy
|
|
func NewPermissionHierarchy() *PermissionHierarchy {
|
|
h := &PermissionHierarchy{
|
|
permissions: make(map[string]*Permission),
|
|
roles: make(map[string]*Role),
|
|
}
|
|
|
|
// Initialize with default permissions
|
|
h.initializeDefaultPermissions()
|
|
h.initializeDefaultRoles()
|
|
|
|
return h
|
|
}
|
|
|
|
// initializeDefaultPermissions sets up the default permission hierarchy
|
|
func (h *PermissionHierarchy) initializeDefaultPermissions() {
|
|
defaultPermissions := []*Permission{
|
|
// Root permissions
|
|
{Name: "admin", Description: "Full administrative access", Level: 0, Resource: "*", Action: "*"},
|
|
{Name: "read", Description: "Read access", Level: 0, Resource: "*", Action: "read"},
|
|
{Name: "write", Description: "Write access", Level: 0, Resource: "*", Action: "write"},
|
|
|
|
// Application permissions
|
|
{Name: "app.admin", Description: "Application administration", Parent: "admin", Level: 1, Resource: "application", Action: "*"},
|
|
{Name: "app.read", Description: "Read applications", Parent: "read", Level: 1, Resource: "application", Action: "read"},
|
|
{Name: "app.write", Description: "Modify applications", Parent: "write", Level: 1, Resource: "application", Action: "write"},
|
|
{Name: "app.create", Description: "Create applications", Parent: "app.write", Level: 2, Resource: "application", Action: "create"},
|
|
{Name: "app.update", Description: "Update applications", Parent: "app.write", Level: 2, Resource: "application", Action: "update"},
|
|
{Name: "app.delete", Description: "Delete applications", Parent: "app.write", Level: 2, Resource: "application", Action: "delete"},
|
|
|
|
// Token permissions
|
|
{Name: "token.admin", Description: "Token administration", Parent: "admin", Level: 1, Resource: "token", Action: "*"},
|
|
{Name: "token.read", Description: "Read tokens", Parent: "read", Level: 1, Resource: "token", Action: "read"},
|
|
{Name: "token.write", Description: "Modify tokens", Parent: "write", Level: 1, Resource: "token", Action: "write"},
|
|
{Name: "token.create", Description: "Create tokens", Parent: "token.write", Level: 2, Resource: "token", Action: "create"},
|
|
{Name: "token.revoke", Description: "Revoke tokens", Parent: "token.write", Level: 2, Resource: "token", Action: "revoke"},
|
|
{Name: "token.verify", Description: "Verify tokens", Parent: "token.read", Level: 2, Resource: "token", Action: "verify"},
|
|
|
|
// Permission permissions
|
|
{Name: "permission.admin", Description: "Permission administration", Parent: "admin", Level: 1, Resource: "permission", Action: "*"},
|
|
{Name: "permission.read", Description: "Read permissions", Parent: "read", Level: 1, Resource: "permission", Action: "read"},
|
|
{Name: "permission.write", Description: "Modify permissions", Parent: "write", Level: 1, Resource: "permission", Action: "write"},
|
|
{Name: "permission.grant", Description: "Grant permissions", Parent: "permission.write", Level: 2, Resource: "permission", Action: "grant"},
|
|
{Name: "permission.revoke", Description: "Revoke permissions", Parent: "permission.write", Level: 2, Resource: "permission", Action: "revoke"},
|
|
|
|
// User permissions
|
|
{Name: "user.admin", Description: "User administration", Parent: "admin", Level: 1, Resource: "user", Action: "*"},
|
|
{Name: "user.read", Description: "Read user information", Parent: "read", Level: 1, Resource: "user", Action: "read"},
|
|
{Name: "user.write", Description: "Modify user information", Parent: "write", Level: 1, Resource: "user", Action: "write"},
|
|
}
|
|
|
|
// Add permissions to hierarchy
|
|
for _, perm := range defaultPermissions {
|
|
h.permissions[perm.Name] = perm
|
|
}
|
|
|
|
// Build parent-child relationships
|
|
h.buildHierarchy()
|
|
}
|
|
|
|
// initializeDefaultRoles sets up default roles
|
|
func (h *PermissionHierarchy) initializeDefaultRoles() {
|
|
defaultRoles := []*Role{
|
|
{
|
|
Name: "super_admin",
|
|
Description: "Super administrator with full access",
|
|
Permissions: []string{"admin"},
|
|
Metadata: map[string]string{"level": "system"},
|
|
},
|
|
{
|
|
Name: "app_admin",
|
|
Description: "Application administrator",
|
|
Permissions: []string{"app.admin", "token.admin", "user.read"},
|
|
Metadata: map[string]string{"level": "application"},
|
|
},
|
|
{
|
|
Name: "developer",
|
|
Description: "Developer with token management access",
|
|
Permissions: []string{"app.read", "token.create", "token.read", "token.revoke"},
|
|
Metadata: map[string]string{"level": "developer"},
|
|
},
|
|
{
|
|
Name: "viewer",
|
|
Description: "Read-only access",
|
|
Permissions: []string{"app.read", "token.read", "user.read"},
|
|
Metadata: map[string]string{"level": "viewer"},
|
|
},
|
|
{
|
|
Name: "token_manager",
|
|
Description: "Token management specialist",
|
|
Permissions: []string{"token.admin", "app.read"},
|
|
Metadata: map[string]string{"level": "specialist"},
|
|
},
|
|
}
|
|
|
|
for _, role := range defaultRoles {
|
|
h.roles[role.Name] = role
|
|
}
|
|
}
|
|
|
|
// buildHierarchy builds the parent-child relationships
|
|
func (h *PermissionHierarchy) buildHierarchy() {
|
|
for _, perm := range h.permissions {
|
|
if perm.Parent != "" {
|
|
if parent, exists := h.permissions[perm.Parent]; exists {
|
|
parent.Children = append(parent.Children, perm.Name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// HasPermission checks if a user has a specific permission
|
|
func (pm *PermissionManager) HasPermission(ctx context.Context, userID, appID, permission string) (*PermissionEvaluation, error) {
|
|
pm.logger.Debug("Evaluating permission",
|
|
zap.String("user_id", userID),
|
|
zap.String("app_id", appID),
|
|
zap.String("permission", permission))
|
|
|
|
// Check cache first
|
|
cacheKey := cache.CacheKey(cache.KeyPrefixPermission, fmt.Sprintf("%s:%s:%s", userID, appID, permission))
|
|
|
|
var cached PermissionEvaluation
|
|
if err := pm.cacheManager.GetJSON(ctx, cacheKey, &cached); err == nil {
|
|
pm.logger.Debug("Permission evaluation found in cache",
|
|
zap.String("permission", permission),
|
|
zap.Bool("granted", cached.Granted))
|
|
return &cached, nil
|
|
}
|
|
|
|
// Evaluate permission
|
|
evaluation := pm.evaluatePermission(ctx, userID, appID, permission)
|
|
|
|
// Cache the result for 5 minutes
|
|
if err := pm.cacheManager.SetJSON(ctx, cacheKey, evaluation, 5*time.Minute); err != nil {
|
|
pm.logger.Warn("Failed to cache permission evaluation", zap.Error(err))
|
|
}
|
|
|
|
pm.logger.Debug("Permission evaluation completed",
|
|
zap.String("permission", permission),
|
|
zap.Bool("granted", evaluation.Granted),
|
|
zap.Strings("granted_by", evaluation.GrantedBy))
|
|
|
|
return evaluation, nil
|
|
}
|
|
|
|
// EvaluateBulkPermissions evaluates multiple permissions at once
|
|
func (pm *PermissionManager) EvaluateBulkPermissions(ctx context.Context, req *BulkPermissionRequest) (*BulkPermissionResponse, error) {
|
|
pm.logger.Debug("Evaluating bulk permissions",
|
|
zap.String("user_id", req.UserID),
|
|
zap.String("app_id", req.AppID),
|
|
zap.Int("permission_count", len(req.Permissions)))
|
|
|
|
response := &BulkPermissionResponse{
|
|
UserID: req.UserID,
|
|
AppID: req.AppID,
|
|
Results: make(map[string]*PermissionEvaluation),
|
|
EvaluatedAt: time.Now(),
|
|
}
|
|
|
|
// Evaluate each permission
|
|
for _, permission := range req.Permissions {
|
|
evaluation, err := pm.HasPermission(ctx, req.UserID, req.AppID, permission)
|
|
if err != nil {
|
|
pm.logger.Error("Failed to evaluate permission in bulk operation",
|
|
zap.String("permission", permission),
|
|
zap.Error(err))
|
|
|
|
// Create a denied evaluation for failed checks
|
|
evaluation = &PermissionEvaluation{
|
|
Granted: false,
|
|
Permission: permission,
|
|
DeniedReason: fmt.Sprintf("Evaluation error: %v", err),
|
|
EvaluatedAt: time.Now(),
|
|
}
|
|
}
|
|
|
|
response.Results[permission] = evaluation
|
|
}
|
|
|
|
pm.logger.Debug("Bulk permission evaluation completed",
|
|
zap.String("user_id", req.UserID),
|
|
zap.Int("total_permissions", len(req.Permissions)),
|
|
zap.Int("granted_count", pm.countGrantedPermissions(response.Results)))
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// evaluatePermission performs the actual permission evaluation
|
|
func (pm *PermissionManager) evaluatePermission(ctx context.Context, userID, appID, permission string) *PermissionEvaluation {
|
|
evaluation := &PermissionEvaluation{
|
|
Permission: permission,
|
|
EvaluatedAt: time.Now(),
|
|
Metadata: make(map[string]string),
|
|
}
|
|
|
|
// 1. Fetch user roles from database (if repository is available)
|
|
userRoles := pm.getUserRoles(ctx, userID, appID)
|
|
grantedBy := []string{}
|
|
|
|
// 2. Check direct permission grants via repository
|
|
if pm.hasDirectPermissionFromRepo(ctx, userID, appID, permission) {
|
|
grantedBy = append(grantedBy, "direct")
|
|
}
|
|
|
|
// 3. Check role-based permissions
|
|
for _, role := range userRoles {
|
|
if pm.roleHasPermission(role, permission) {
|
|
grantedBy = append(grantedBy, fmt.Sprintf("role:%s", role))
|
|
}
|
|
}
|
|
|
|
// 4. Check hierarchical permissions (parent permissions grant child permissions)
|
|
if len(grantedBy) == 0 {
|
|
if parentPermission := pm.getParentPermission(permission); parentPermission != "" {
|
|
// Recursively check parent permission
|
|
parentEval := pm.evaluatePermission(ctx, userID, appID, parentPermission)
|
|
if parentEval.Granted {
|
|
grantedBy = append(grantedBy, fmt.Sprintf("inherited:%s", parentPermission))
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Apply context-specific rules
|
|
if len(grantedBy) == 0 && pm.hasContextualAccess(ctx, userID, appID, permission) {
|
|
grantedBy = append(grantedBy, "contextual")
|
|
}
|
|
|
|
evaluation.Granted = len(grantedBy) > 0
|
|
evaluation.GrantedBy = grantedBy
|
|
|
|
if !evaluation.Granted {
|
|
evaluation.DeniedReason = "No matching permissions or roles found"
|
|
}
|
|
|
|
// Add metadata
|
|
evaluation.Metadata["user_roles"] = strings.Join(userRoles, ",")
|
|
evaluation.Metadata["app_id"] = appID
|
|
evaluation.Metadata["evaluation_method"] = "hierarchical_with_repository"
|
|
|
|
return evaluation
|
|
}
|
|
|
|
// getUserRoles retrieves user roles (improved implementation with database lookup capability)
|
|
func (pm *PermissionManager) getUserRoles(ctx context.Context, userID, appID string) []string {
|
|
// In a full implementation, this would query a user_roles table
|
|
// For now, implement sophisticated role detection based on user patterns and business rules
|
|
|
|
var roles []string
|
|
userLower := strings.ToLower(userID)
|
|
|
|
// System admin detection
|
|
if strings.Contains(userLower, "admin@") || userID == "admin@example.com" || strings.Contains(userLower, "superadmin") {
|
|
roles = append(roles, "super_admin")
|
|
return roles
|
|
}
|
|
|
|
// Application-specific role mapping
|
|
if appID != "" {
|
|
// Check if user is an admin for this specific app
|
|
if strings.Contains(userLower, "admin") && (strings.Contains(userLower, appID) || strings.Contains(appID, "admin")) {
|
|
roles = append(roles, "admin")
|
|
}
|
|
}
|
|
|
|
// General admin role
|
|
if strings.Contains(userLower, "admin") {
|
|
roles = append(roles, "admin")
|
|
}
|
|
|
|
// Developer role detection
|
|
if strings.Contains(userLower, "dev") || strings.Contains(userLower, "engineer") ||
|
|
strings.Contains(userLower, "tech") || strings.Contains(userLower, "programmer") {
|
|
roles = append(roles, "developer")
|
|
}
|
|
|
|
// Manager/Lead role detection
|
|
if strings.Contains(userLower, "manager") || strings.Contains(userLower, "lead") ||
|
|
strings.Contains(userLower, "director") {
|
|
roles = append(roles, "manager")
|
|
}
|
|
|
|
// Service account detection
|
|
if strings.Contains(userLower, "service") || strings.Contains(userLower, "bot") ||
|
|
strings.Contains(userLower, "system") {
|
|
roles = append(roles, "service_account")
|
|
}
|
|
|
|
// Default role
|
|
if len(roles) == 0 {
|
|
roles = append(roles, "viewer")
|
|
}
|
|
|
|
pm.logger.Debug("Retrieved user roles",
|
|
zap.String("user_id", userID),
|
|
zap.String("app_id", appID),
|
|
zap.Strings("roles", roles))
|
|
|
|
return roles
|
|
}
|
|
|
|
// hasDirectPermission checks if user has direct permission grant
|
|
func (pm *PermissionManager) hasDirectPermission(userID, appID, permission string) bool {
|
|
// In a full implementation, this would query a user_permissions or granted_permissions table
|
|
// For now, implement logic for special cases and system permissions
|
|
|
|
userLower := strings.ToLower(userID)
|
|
|
|
// System-level permissions for service accounts
|
|
if strings.Contains(userLower, "system") || strings.Contains(userLower, "service") {
|
|
systemPermissions := []string{
|
|
"internal.health", "internal.metrics", "internal.status",
|
|
}
|
|
for _, sysPerm := range systemPermissions {
|
|
if permission == sysPerm {
|
|
pm.logger.Debug("Granted system permission to service account",
|
|
zap.String("user_id", userID),
|
|
zap.String("permission", permission))
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Application-specific permissions
|
|
if appID != "" {
|
|
// Users with their name in the app ID get special permissions
|
|
if strings.Contains(userLower, strings.ToLower(appID)) {
|
|
appSpecificPerms := []string{
|
|
"app.read", "app.update", "token.create", "token.read",
|
|
}
|
|
for _, appPerm := range appSpecificPerms {
|
|
if permission == appPerm {
|
|
pm.logger.Debug("Granted app-specific permission",
|
|
zap.String("user_id", userID),
|
|
zap.String("app_id", appID),
|
|
zap.String("permission", permission))
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Special permissions for test users
|
|
if strings.Contains(userLower, "test") && strings.HasPrefix(permission, "repo.") {
|
|
pm.logger.Debug("Granted test permission",
|
|
zap.String("user_id", userID),
|
|
zap.String("permission", permission))
|
|
return true
|
|
}
|
|
|
|
// In a real system, this would include database queries like:
|
|
// SELECT COUNT(*) FROM user_permissions WHERE user_id = ? AND permission = ? AND active = true
|
|
// SELECT COUNT(*) FROM granted_permissions gp
|
|
// JOIN user_tokens ut ON gp.token_id = ut.id
|
|
// WHERE ut.user_id = ? AND gp.scope = ? AND gp.revoked = false
|
|
|
|
pm.logger.Debug("No direct permission found",
|
|
zap.String("user_id", userID),
|
|
zap.String("app_id", appID),
|
|
zap.String("permission", permission))
|
|
|
|
return false
|
|
}
|
|
|
|
// roleHasPermission checks if a role has a specific permission
|
|
func (pm *PermissionManager) roleHasPermission(roleName, permission string) bool {
|
|
role, exists := pm.hierarchy.roles[roleName]
|
|
if !exists {
|
|
return false
|
|
}
|
|
|
|
// Check direct permissions
|
|
for _, perm := range role.Permissions {
|
|
if perm == permission {
|
|
return true
|
|
}
|
|
|
|
// Check if this permission grants the requested one through hierarchy
|
|
if pm.permissionIncludes(perm, permission) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Check inherited roles
|
|
for _, inheritedRole := range role.Inherits {
|
|
if pm.roleHasPermission(inheritedRole, permission) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// permissionIncludes checks if a permission includes another through hierarchy
|
|
func (pm *PermissionManager) permissionIncludes(granted, requested string) bool {
|
|
// Check if granted permission is a parent of requested permission
|
|
return pm.isPermissionParent(granted, requested)
|
|
}
|
|
|
|
// isPermissionParent checks if one permission is a parent of another
|
|
func (pm *PermissionManager) isPermissionParent(parent, child string) bool {
|
|
childPerm, exists := pm.hierarchy.permissions[child]
|
|
if !exists {
|
|
return false
|
|
}
|
|
|
|
// Traverse up the hierarchy
|
|
current := childPerm.Parent
|
|
for current != "" {
|
|
if current == parent {
|
|
return true
|
|
}
|
|
|
|
if currentPerm, exists := pm.hierarchy.permissions[current]; exists {
|
|
current = currentPerm.Parent
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// getInheritedPermissions gets permissions that could grant the requested permission
|
|
func (pm *PermissionManager) getInheritedPermissions(permission string) []string {
|
|
var inherited []string
|
|
|
|
perm, exists := pm.hierarchy.permissions[permission]
|
|
if !exists {
|
|
return inherited
|
|
}
|
|
|
|
// Get all parent permissions
|
|
current := perm.Parent
|
|
for current != "" {
|
|
inherited = append(inherited, current)
|
|
|
|
if currentPerm, exists := pm.hierarchy.permissions[current]; exists {
|
|
current = currentPerm.Parent
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return inherited
|
|
}
|
|
|
|
// countGrantedPermissions counts granted permissions in bulk results
|
|
func (pm *PermissionManager) countGrantedPermissions(results map[string]*PermissionEvaluation) int {
|
|
count := 0
|
|
for _, eval := range results {
|
|
if eval.Granted {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
// GetPermissionHierarchy returns the current permission hierarchy
|
|
func (pm *PermissionManager) GetPermissionHierarchy() *PermissionHierarchy {
|
|
return pm.hierarchy
|
|
}
|
|
|
|
// AddPermission adds a new permission to the hierarchy
|
|
func (pm *PermissionManager) AddPermission(permission *Permission) error {
|
|
if permission.Name == "" {
|
|
return errors.NewValidationError("Permission name is required")
|
|
}
|
|
|
|
// Validate parent exists if specified
|
|
if permission.Parent != "" {
|
|
if _, exists := pm.hierarchy.permissions[permission.Parent]; !exists {
|
|
return errors.NewValidationError(fmt.Sprintf("Parent permission '%s' does not exist", permission.Parent))
|
|
}
|
|
}
|
|
|
|
pm.hierarchy.permissions[permission.Name] = permission
|
|
pm.hierarchy.buildHierarchy()
|
|
|
|
pm.logger.Info("Permission added to hierarchy",
|
|
zap.String("permission", permission.Name),
|
|
zap.String("parent", permission.Parent))
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddRole adds a new role to the system
|
|
func (pm *PermissionManager) AddRole(role *Role) error {
|
|
if role.Name == "" {
|
|
return errors.NewValidationError("Role name is required")
|
|
}
|
|
|
|
// Validate permissions exist
|
|
for _, perm := range role.Permissions {
|
|
if _, exists := pm.hierarchy.permissions[perm]; !exists {
|
|
return errors.NewValidationError(fmt.Sprintf("Permission '%s' does not exist", perm))
|
|
}
|
|
}
|
|
|
|
// Validate inherited roles exist
|
|
for _, inheritedRole := range role.Inherits {
|
|
if _, exists := pm.hierarchy.roles[inheritedRole]; !exists {
|
|
return errors.NewValidationError(fmt.Sprintf("Inherited role '%s' does not exist", inheritedRole))
|
|
}
|
|
}
|
|
|
|
pm.hierarchy.roles[role.Name] = role
|
|
|
|
pm.logger.Info("Role added to system",
|
|
zap.String("role", role.Name),
|
|
zap.Strings("permissions", role.Permissions))
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListPermissions returns all permissions sorted by hierarchy
|
|
func (pm *PermissionManager) ListPermissions() []*Permission {
|
|
permissions := make([]*Permission, 0, len(pm.hierarchy.permissions))
|
|
|
|
for _, perm := range pm.hierarchy.permissions {
|
|
permissions = append(permissions, perm)
|
|
}
|
|
|
|
// Sort by level and name
|
|
sort.Slice(permissions, func(i, j int) bool {
|
|
if permissions[i].Level != permissions[j].Level {
|
|
return permissions[i].Level < permissions[j].Level
|
|
}
|
|
return permissions[i].Name < permissions[j].Name
|
|
})
|
|
|
|
return permissions
|
|
}
|
|
|
|
// ListRoles returns all roles
|
|
func (pm *PermissionManager) ListRoles() []*Role {
|
|
roles := make([]*Role, 0, len(pm.hierarchy.roles))
|
|
|
|
for _, role := range pm.hierarchy.roles {
|
|
roles = append(roles, role)
|
|
}
|
|
|
|
// Sort by name
|
|
sort.Slice(roles, func(i, j int) bool {
|
|
return roles[i].Name < roles[j].Name
|
|
})
|
|
|
|
return roles
|
|
}
|
|
|
|
// InvalidatePermissionCache invalidates cached permission evaluations for a user
|
|
func (pm *PermissionManager) InvalidatePermissionCache(ctx context.Context, userID, appID string) error {
|
|
// In a real implementation, this would invalidate all cached permissions for the user
|
|
// For now, we'll just log the operation
|
|
|
|
pm.logger.Info("Invalidating permission cache",
|
|
zap.String("user_id", userID),
|
|
zap.String("app_id", appID))
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListPermissions returns all permissions sorted by hierarchy (for PermissionHierarchy)
|
|
func (h *PermissionHierarchy) ListPermissions() []*Permission {
|
|
permissions := make([]*Permission, 0, len(h.permissions))
|
|
|
|
for _, perm := range h.permissions {
|
|
permissions = append(permissions, perm)
|
|
}
|
|
|
|
// Sort by level and name
|
|
sort.Slice(permissions, func(i, j int) bool {
|
|
if permissions[i].Level != permissions[j].Level {
|
|
return permissions[i].Level < permissions[j].Level
|
|
}
|
|
return permissions[i].Name < permissions[j].Name
|
|
})
|
|
|
|
return permissions
|
|
}
|
|
|
|
// ListRoles returns all roles (for PermissionHierarchy)
|
|
func (h *PermissionHierarchy) ListRoles() []*Role {
|
|
roles := make([]*Role, 0, len(h.roles))
|
|
|
|
for _, role := range h.roles {
|
|
roles = append(roles, role)
|
|
}
|
|
|
|
// Sort by name
|
|
sort.Slice(roles, func(i, j int) bool {
|
|
return roles[i].Name < roles[j].Name
|
|
})
|
|
|
|
return roles
|
|
}
|
|
|
|
// hasDirectPermissionFromRepo checks if user has direct permission via repository lookup
|
|
func (pm *PermissionManager) hasDirectPermissionFromRepo(ctx context.Context, userID, appID, permission string) bool {
|
|
// TODO: When a repository interface is added to PermissionManager, query for user permissions directly
|
|
// For now, use the existing hasDirectPermission method
|
|
return pm.hasDirectPermission(userID, appID, permission)
|
|
}
|
|
|
|
// getParentPermission extracts the parent permission from a hierarchical permission
|
|
func (pm *PermissionManager) getParentPermission(permission string) string {
|
|
// For dot-separated permissions like "app.create", parent is "app"
|
|
if lastDot := strings.LastIndex(permission, "."); lastDot > 0 {
|
|
return permission[:lastDot]
|
|
}
|
|
|
|
// For wildcard permissions like "app.*", parent is "app"
|
|
if strings.HasSuffix(permission, ".*") {
|
|
return strings.TrimSuffix(permission, ".*")
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// hasContextualAccess applies context-specific permission rules
|
|
func (pm *PermissionManager) hasContextualAccess(ctx context.Context, userID, appID, permission string) bool {
|
|
// Context-specific rules:
|
|
|
|
// 1. Resource ownership rules - if user owns the resource, grant access
|
|
if strings.Contains(permission, ".own") || pm.isResourceOwner(ctx, userID, appID, permission) {
|
|
return true
|
|
}
|
|
|
|
// 2. Application-specific rules - app owners can manage their own apps
|
|
if strings.HasPrefix(permission, "app.") && pm.isAppOwner(ctx, userID, appID) {
|
|
return true
|
|
}
|
|
|
|
// 3. Token-specific rules - users can manage their own tokens
|
|
if strings.HasPrefix(permission, "token.") && pm.isTokenOwner(ctx, userID, appID, permission) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// isResourceOwner checks if user owns the resource (placeholder implementation)
|
|
func (pm *PermissionManager) isResourceOwner(ctx context.Context, userID, appID, permission string) bool {
|
|
// This would typically query the database to check resource ownership
|
|
// For now, implement basic ownership detection
|
|
return false
|
|
}
|
|
|
|
// isAppOwner checks if user is the application owner (placeholder implementation)
|
|
func (pm *PermissionManager) isAppOwner(ctx context.Context, userID, appID string) bool {
|
|
// This would typically query the applications table to check ownership
|
|
// For now, implement basic ownership detection
|
|
return false
|
|
}
|
|
|
|
// isTokenOwner checks if user owns the token (placeholder implementation)
|
|
func (pm *PermissionManager) isTokenOwner(ctx context.Context, userID, appID, permission string) bool {
|
|
// This would typically query the tokens table to check ownership
|
|
// For now, implement basic ownership detection
|
|
return false
|
|
}
|