mcias/data/auth.go

184 lines
5.1 KiB
Go

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()
}