package data import ( "database/sql" "fmt" "github.com/oklog/ulid/v2" ) const ( // Constants for error messages errScanPermission = "failed to scan permission: %w" errIteratePermissions = "error iterating permissions: %w" // Constants for comparison zeroCount = 0 ) // Permission represents a system permission. type Permission struct { ID string Resource string Action string Description string } // AuthorizationService provides methods for checking user permissions. type AuthorizationService struct { db *sql.DB } // NewAuthorizationService creates a new authorization service. func NewAuthorizationService(db *sql.DB) *AuthorizationService { return &AuthorizationService{db: db} } // UserHasPermission checks if a user has a specific permission for a resource and action func (a *AuthorizationService) UserHasPermission(userID, resource, action string) (bool, error) { query := ` SELECT COUNT(*) FROM permissions p JOIN role_permissions rp ON p.id = rp.pid JOIN user_roles ur ON rp.rid = ur.rid WHERE ur.uid = ? AND p.resource = ? AND p.action = ? ` var count int err := a.db.QueryRow(query, userID, resource, action).Scan(&count) if err != nil { return false, fmt.Errorf("failed to check user permission: %w", err) } return count > zeroCount, nil } // GetUserPermissions returns all permissions for a user based on their roles. func (a *AuthorizationService) GetUserPermissions(userID string) ([]Permission, error) { query := ` SELECT DISTINCT p.id, p.resource, p.action, p.description FROM permissions p JOIN role_permissions rp ON p.id = rp.pid JOIN user_roles ur ON rp.rid = ur.rid WHERE ur.uid = ? ` rows, err := a.db.Query(query, userID) if err != nil { return nil, fmt.Errorf("failed to get user permissions: %w", err) } defer rows.Close() var permissions []Permission for rows.Next() { var perm Permission if scanErr := rows.Scan(&perm.ID, &perm.Resource, &perm.Action, &perm.Description); scanErr != nil { return nil, fmt.Errorf(errScanPermission, scanErr) } permissions = append(permissions, perm) } if rowErr := rows.Err(); rowErr != nil { return nil, fmt.Errorf(errIteratePermissions, rowErr) } return permissions, nil } // GetRolePermissions returns all permissions for a specific role. func (a *AuthorizationService) GetRolePermissions(roleID string) ([]Permission, error) { query := ` SELECT p.id, p.resource, p.action, p.description FROM permissions p JOIN role_permissions rp ON p.id = rp.pid WHERE rp.rid = ? ` rows, err := a.db.Query(query, roleID) if err != nil { return nil, fmt.Errorf("failed to get role permissions: %w", err) } defer rows.Close() var permissions []Permission for rows.Next() { var perm Permission if scanErr := rows.Scan(&perm.ID, &perm.Resource, &perm.Action, &perm.Description); scanErr != nil { return nil, fmt.Errorf(errScanPermission, scanErr) } permissions = append(permissions, perm) } if rowErr := rows.Err(); rowErr != nil { return nil, fmt.Errorf(errIteratePermissions, rowErr) } return permissions, nil } // GrantPermissionToRole grants a permission to a role func (a *AuthorizationService) GrantPermissionToRole(roleID, permissionID string) error { // Check if the role-permission relationship already exists checkQuery := `SELECT COUNT(*) FROM role_permissions WHERE rid = ? AND pid = ?` var count int err := a.db.QueryRow(checkQuery, roleID, permissionID).Scan(&count) if err != nil { return fmt.Errorf("failed to check role permission: %w", err) } if count > 0 { return nil // Permission already granted } // Generate a new ID for the role-permission relationship id := GenerateID() // Insert the new role-permission relationship insertQuery := `INSERT INTO role_permissions (id, rid, pid) VALUES (?, ?, ?)` _, err = a.db.Exec(insertQuery, id, roleID, permissionID) if err != nil { return fmt.Errorf("failed to grant permission to role: %w", err) } return nil } // RevokePermissionFromRole revokes a permission from a role func (a *AuthorizationService) RevokePermissionFromRole(roleID, permissionID string) error { query := `DELETE FROM role_permissions WHERE rid = ? AND pid = ?` _, err := a.db.Exec(query, roleID, permissionID) if err != nil { return fmt.Errorf("failed to revoke permission from role: %w", err) } return nil } // GetAllPermissions returns all permissions in the system. func (a *AuthorizationService) GetAllPermissions() ([]Permission, error) { query := `SELECT id, resource, action, description FROM permissions` rows, err := a.db.Query(query) if err != nil { return nil, fmt.Errorf("failed to get permissions: %w", err) } defer rows.Close() var permissions []Permission for rows.Next() { var perm Permission if scanErr := rows.Scan(&perm.ID, &perm.Resource, &perm.Action, &perm.Description); scanErr != nil { return nil, fmt.Errorf(errScanPermission, scanErr) } permissions = append(permissions, perm) } if rowErr := rows.Err(); rowErr != nil { return nil, fmt.Errorf(errIteratePermissions, rowErr) } return permissions, nil } // GenerateID generates a unique ID for database records func GenerateID() string { return ulid.Make().String() }