694 lines
19 KiB
Go
694 lines
19 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/kms/api-key-service/internal/domain"
|
|
"github.com/kms/api-key-service/internal/repository"
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
// PermissionRepository implements the PermissionRepository interface for PostgreSQL
|
|
type PermissionRepository struct {
|
|
db repository.DatabaseProvider
|
|
}
|
|
|
|
// NewPermissionRepository creates a new PostgreSQL permission repository
|
|
func NewPermissionRepository(db repository.DatabaseProvider) repository.PermissionRepository {
|
|
return &PermissionRepository{db: db}
|
|
}
|
|
|
|
// CreateAvailablePermission creates a new available permission
|
|
func (r *PermissionRepository) CreateAvailablePermission(ctx context.Context, permission *domain.AvailablePermission) error {
|
|
query := `
|
|
INSERT INTO available_permissions (
|
|
id, scope, name, description, category, parent_scope,
|
|
is_system, created_by, updated_by, created_at, updated_at
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
now := time.Now()
|
|
|
|
if permission.ID == uuid.Nil {
|
|
permission.ID = uuid.New()
|
|
}
|
|
|
|
_, err := db.ExecContext(ctx, query,
|
|
permission.ID,
|
|
permission.Scope,
|
|
permission.Name,
|
|
permission.Description,
|
|
permission.Category,
|
|
permission.ParentScope,
|
|
permission.IsSystem,
|
|
permission.CreatedBy,
|
|
permission.UpdatedBy,
|
|
now,
|
|
now,
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create available permission: %w", err)
|
|
}
|
|
|
|
permission.CreatedAt = now
|
|
permission.UpdatedAt = now
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetAvailablePermission retrieves an available permission by ID
|
|
func (r *PermissionRepository) GetAvailablePermission(ctx context.Context, permissionID uuid.UUID) (*domain.AvailablePermission, error) {
|
|
query := `
|
|
SELECT id, scope, name, description, category, parent_scope,
|
|
is_system, created_at, created_by, updated_at, updated_by
|
|
FROM available_permissions
|
|
WHERE id = $1
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
row := db.QueryRowContext(ctx, query, permissionID)
|
|
|
|
permission := &domain.AvailablePermission{}
|
|
err := row.Scan(
|
|
&permission.ID,
|
|
&permission.Scope,
|
|
&permission.Name,
|
|
&permission.Description,
|
|
&permission.Category,
|
|
&permission.ParentScope,
|
|
&permission.IsSystem,
|
|
&permission.CreatedAt,
|
|
&permission.CreatedBy,
|
|
&permission.UpdatedAt,
|
|
&permission.UpdatedBy,
|
|
)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("permission with ID '%s' not found", permissionID)
|
|
}
|
|
return nil, fmt.Errorf("failed to get available permission: %w", err)
|
|
}
|
|
|
|
return permission, nil
|
|
}
|
|
|
|
// GetAvailablePermissionByScope retrieves an available permission by scope
|
|
func (r *PermissionRepository) GetAvailablePermissionByScope(ctx context.Context, scope string) (*domain.AvailablePermission, error) {
|
|
query := `
|
|
SELECT id, scope, name, description, category, parent_scope,
|
|
is_system, created_at, created_by, updated_at, updated_by
|
|
FROM available_permissions
|
|
WHERE scope = $1
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
row := db.QueryRowContext(ctx, query, scope)
|
|
|
|
permission := &domain.AvailablePermission{}
|
|
err := row.Scan(
|
|
&permission.ID,
|
|
&permission.Scope,
|
|
&permission.Name,
|
|
&permission.Description,
|
|
&permission.Category,
|
|
&permission.ParentScope,
|
|
&permission.IsSystem,
|
|
&permission.CreatedAt,
|
|
&permission.CreatedBy,
|
|
&permission.UpdatedAt,
|
|
&permission.UpdatedBy,
|
|
)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("permission with scope '%s' not found", scope)
|
|
}
|
|
return nil, fmt.Errorf("failed to get available permission by scope: %w", err)
|
|
}
|
|
|
|
return permission, nil
|
|
}
|
|
|
|
// ListAvailablePermissions retrieves available permissions with pagination and filtering
|
|
func (r *PermissionRepository) ListAvailablePermissions(ctx context.Context, category string, includeSystem bool, limit, offset int) ([]*domain.AvailablePermission, error) {
|
|
var args []interface{}
|
|
var whereClauses []string
|
|
argIndex := 1
|
|
|
|
// Build WHERE clause based on filters
|
|
if category != "" {
|
|
whereClauses = append(whereClauses, fmt.Sprintf("category = $%d", argIndex))
|
|
args = append(args, category)
|
|
argIndex++
|
|
}
|
|
|
|
if !includeSystem {
|
|
whereClauses = append(whereClauses, fmt.Sprintf("is_system = $%d", argIndex))
|
|
args = append(args, false)
|
|
argIndex++
|
|
}
|
|
|
|
whereClause := ""
|
|
if len(whereClauses) > 0 {
|
|
whereClause = "WHERE " + fmt.Sprintf("%s", whereClauses[0])
|
|
for i := 1; i < len(whereClauses); i++ {
|
|
whereClause += " AND " + whereClauses[i]
|
|
}
|
|
}
|
|
|
|
query := fmt.Sprintf(`
|
|
SELECT id, scope, name, description, category, parent_scope,
|
|
is_system, created_at, created_by, updated_at, updated_by
|
|
FROM available_permissions
|
|
%s
|
|
ORDER BY category, scope
|
|
LIMIT $%d OFFSET $%d
|
|
`, whereClause, argIndex, argIndex+1)
|
|
|
|
args = append(args, limit, offset)
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
rows, err := db.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list available permissions: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var permissions []*domain.AvailablePermission
|
|
for rows.Next() {
|
|
permission := &domain.AvailablePermission{}
|
|
err := rows.Scan(
|
|
&permission.ID,
|
|
&permission.Scope,
|
|
&permission.Name,
|
|
&permission.Description,
|
|
&permission.Category,
|
|
&permission.ParentScope,
|
|
&permission.IsSystem,
|
|
&permission.CreatedAt,
|
|
&permission.CreatedBy,
|
|
&permission.UpdatedAt,
|
|
&permission.UpdatedBy,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan available permission: %w", err)
|
|
}
|
|
permissions = append(permissions, permission)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("failed to iterate available permissions: %w", err)
|
|
}
|
|
|
|
return permissions, nil
|
|
}
|
|
|
|
// UpdateAvailablePermission updates an available permission
|
|
func (r *PermissionRepository) UpdateAvailablePermission(ctx context.Context, permissionID uuid.UUID, permission *domain.AvailablePermission) error {
|
|
query := `
|
|
UPDATE available_permissions
|
|
SET scope = $2, name = $3, description = $4, category = $5,
|
|
parent_scope = $6, is_system = $7, updated_by = $8, updated_at = $9
|
|
WHERE id = $1
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
now := time.Now()
|
|
|
|
result, err := db.ExecContext(ctx, query,
|
|
permissionID,
|
|
permission.Scope,
|
|
permission.Name,
|
|
permission.Description,
|
|
permission.Category,
|
|
permission.ParentScope,
|
|
permission.IsSystem,
|
|
permission.UpdatedBy,
|
|
now,
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update available permission: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("permission with ID %s not found", permissionID)
|
|
}
|
|
|
|
permission.UpdatedAt = now
|
|
return nil
|
|
}
|
|
|
|
// DeleteAvailablePermission deletes an available permission
|
|
func (r *PermissionRepository) DeleteAvailablePermission(ctx context.Context, permissionID uuid.UUID) error {
|
|
// First check if the permission has any child permissions
|
|
checkChildrenQuery := `
|
|
SELECT COUNT(*) FROM available_permissions
|
|
WHERE parent_scope = (SELECT scope FROM available_permissions WHERE id = $1)
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
var childCount int
|
|
err := db.QueryRowContext(ctx, checkChildrenQuery, permissionID).Scan(&childCount)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to check for child permissions: %w", err)
|
|
}
|
|
|
|
if childCount > 0 {
|
|
return fmt.Errorf("cannot delete permission: it has %d child permissions", childCount)
|
|
}
|
|
|
|
// Check if the permission is granted to any tokens
|
|
checkGrantsQuery := `
|
|
SELECT COUNT(*) FROM granted_permissions
|
|
WHERE permission_id = $1 AND revoked = false
|
|
`
|
|
|
|
var grantCount int
|
|
err = db.QueryRowContext(ctx, checkGrantsQuery, permissionID).Scan(&grantCount)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to check for active grants: %w", err)
|
|
}
|
|
|
|
if grantCount > 0 {
|
|
return fmt.Errorf("cannot delete permission: it is currently granted to %d tokens", grantCount)
|
|
}
|
|
|
|
// Delete the permission
|
|
deleteQuery := `DELETE FROM available_permissions WHERE id = $1`
|
|
result, err := db.ExecContext(ctx, deleteQuery, permissionID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete available permission: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("permission with ID %s not found", permissionID)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidatePermissionScopes checks if all given scopes exist and are valid
|
|
func (r *PermissionRepository) ValidatePermissionScopes(ctx context.Context, scopes []string) ([]string, error) {
|
|
if len(scopes) == 0 {
|
|
return []string{}, nil
|
|
}
|
|
|
|
query := `
|
|
SELECT scope
|
|
FROM available_permissions
|
|
WHERE scope = ANY($1)
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
rows, err := db.QueryContext(ctx, query, pq.Array(scopes))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to validate permission scopes: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
validScopes := make(map[string]bool)
|
|
for rows.Next() {
|
|
var scope string
|
|
if err := rows.Scan(&scope); err != nil {
|
|
return nil, fmt.Errorf("failed to scan scope: %w", err)
|
|
}
|
|
validScopes[scope] = true
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating scopes: %w", err)
|
|
}
|
|
|
|
var result []string
|
|
for _, scope := range scopes {
|
|
if validScopes[scope] {
|
|
result = append(result, scope)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// GetPermissionHierarchy returns all parent and child permissions for given scopes
|
|
func (r *PermissionRepository) GetPermissionHierarchy(ctx context.Context, scopes []string) ([]*domain.AvailablePermission, error) {
|
|
if len(scopes) == 0 {
|
|
return []*domain.AvailablePermission{}, nil
|
|
}
|
|
|
|
// Use recursive CTE to get full hierarchy
|
|
query := `
|
|
WITH RECURSIVE permission_hierarchy AS (
|
|
-- Base case: get permissions matching the input scopes
|
|
SELECT id, scope, name, description, category, parent_scope,
|
|
is_system, created_at, created_by, updated_at, updated_by, 0 as level
|
|
FROM available_permissions
|
|
WHERE scope = ANY($1)
|
|
|
|
UNION ALL
|
|
|
|
-- Recursive case: get all parents and children
|
|
SELECT ap.id, ap.scope, ap.name, ap.description, ap.category, ap.parent_scope,
|
|
ap.is_system, ap.created_at, ap.created_by, ap.updated_at, ap.updated_by,
|
|
ph.level + 1 as level
|
|
FROM available_permissions ap
|
|
JOIN permission_hierarchy ph ON (
|
|
-- Get parents (where ap.scope = ph.parent_scope)
|
|
ap.scope = ph.parent_scope
|
|
OR
|
|
-- Get children (where ap.parent_scope = ph.scope)
|
|
ap.parent_scope = ph.scope
|
|
)
|
|
WHERE ph.level < 5 -- Prevent infinite recursion
|
|
)
|
|
SELECT DISTINCT id, scope, name, description, category, parent_scope,
|
|
is_system, created_at, created_by, updated_at, updated_by
|
|
FROM permission_hierarchy
|
|
ORDER BY scope
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
rows, err := db.QueryContext(ctx, query, pq.Array(scopes))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get permission hierarchy: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var permissions []*domain.AvailablePermission
|
|
for rows.Next() {
|
|
permission := &domain.AvailablePermission{}
|
|
err := rows.Scan(
|
|
&permission.ID,
|
|
&permission.Scope,
|
|
&permission.Name,
|
|
&permission.Description,
|
|
&permission.Category,
|
|
&permission.ParentScope,
|
|
&permission.IsSystem,
|
|
&permission.CreatedAt,
|
|
&permission.CreatedBy,
|
|
&permission.UpdatedAt,
|
|
&permission.UpdatedBy,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan permission hierarchy: %w", err)
|
|
}
|
|
permissions = append(permissions, permission)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("failed to iterate permission hierarchy: %w", err)
|
|
}
|
|
|
|
return permissions, nil
|
|
}
|
|
|
|
// GrantedPermissionRepository implements the GrantedPermissionRepository interface for PostgreSQL
|
|
type GrantedPermissionRepository struct {
|
|
db repository.DatabaseProvider
|
|
}
|
|
|
|
// NewGrantedPermissionRepository creates a new PostgreSQL granted permission repository
|
|
func NewGrantedPermissionRepository(db repository.DatabaseProvider) repository.GrantedPermissionRepository {
|
|
return &GrantedPermissionRepository{db: db}
|
|
}
|
|
|
|
// GrantPermissions grants multiple permissions to a token
|
|
func (r *GrantedPermissionRepository) GrantPermissions(ctx context.Context, grants []*domain.GrantedPermission) error {
|
|
if len(grants) == 0 {
|
|
return nil
|
|
}
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
tx, err := db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to begin transaction: %w", err)
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
query := `
|
|
INSERT INTO granted_permissions (
|
|
id, token_type, token_id, permission_id, scope, created_by, created_at
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
ON CONFLICT (token_type, token_id, permission_id) DO NOTHING
|
|
`
|
|
|
|
stmt, err := tx.PrepareContext(ctx, query)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to prepare statement: %w", err)
|
|
}
|
|
defer stmt.Close()
|
|
|
|
now := time.Now()
|
|
for _, grant := range grants {
|
|
if grant.ID == uuid.Nil {
|
|
grant.ID = uuid.New()
|
|
}
|
|
|
|
_, err = stmt.ExecContext(ctx,
|
|
grant.ID,
|
|
string(grant.TokenType),
|
|
grant.TokenID,
|
|
grant.PermissionID,
|
|
grant.Scope,
|
|
grant.CreatedBy,
|
|
now,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to grant permission: %w", err)
|
|
}
|
|
|
|
grant.CreatedAt = now
|
|
}
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
return fmt.Errorf("failed to commit transaction: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetGrantedPermissions retrieves all granted permissions for a token
|
|
func (r *GrantedPermissionRepository) GetGrantedPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]*domain.GrantedPermission, error) {
|
|
query := `
|
|
SELECT id, token_type, token_id, permission_id, scope, created_at, created_by, revoked
|
|
FROM granted_permissions
|
|
WHERE token_type = $1 AND token_id = $2 AND revoked = false
|
|
ORDER BY created_at ASC
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
rows, err := db.QueryContext(ctx, query, string(tokenType), tokenID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to query granted permissions: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var permissions []*domain.GrantedPermission
|
|
for rows.Next() {
|
|
perm := &domain.GrantedPermission{}
|
|
var tokenTypeStr string
|
|
|
|
err := rows.Scan(
|
|
&perm.ID,
|
|
&tokenTypeStr,
|
|
&perm.TokenID,
|
|
&perm.PermissionID,
|
|
&perm.Scope,
|
|
&perm.CreatedAt,
|
|
&perm.CreatedBy,
|
|
&perm.Revoked,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan granted permission: %w", err)
|
|
}
|
|
|
|
perm.TokenType = domain.TokenType(tokenTypeStr)
|
|
permissions = append(permissions, perm)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating granted permissions: %w", err)
|
|
}
|
|
|
|
return permissions, nil
|
|
}
|
|
|
|
// GetGrantedPermissionScopes retrieves only the scopes for a token (more efficient)
|
|
func (r *GrantedPermissionRepository) GetGrantedPermissionScopes(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]string, error) {
|
|
query := `
|
|
SELECT scope
|
|
FROM granted_permissions
|
|
WHERE token_type = $1 AND token_id = $2 AND revoked = false
|
|
ORDER BY scope ASC
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
rows, err := db.QueryContext(ctx, query, string(tokenType), tokenID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to query granted permission scopes: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var scopes []string
|
|
for rows.Next() {
|
|
var scope string
|
|
if err := rows.Scan(&scope); err != nil {
|
|
return nil, fmt.Errorf("failed to scan permission scope: %w", err)
|
|
}
|
|
scopes = append(scopes, scope)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating permission scopes: %w", err)
|
|
}
|
|
|
|
return scopes, nil
|
|
}
|
|
|
|
// RevokePermission revokes a specific permission from a token
|
|
func (r *GrantedPermissionRepository) RevokePermission(ctx context.Context, grantID uuid.UUID, revokedBy string) error {
|
|
query := `
|
|
UPDATE granted_permissions
|
|
SET revoked = true, revoked_by = $2, revoked_at = $3
|
|
WHERE id = $1 AND revoked = false
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
now := time.Now()
|
|
|
|
result, err := db.ExecContext(ctx, query, grantID, revokedBy, now)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to revoke permission: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("permission grant with ID %s not found or already revoked", grantID)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RevokeAllPermissions revokes all permissions from a token
|
|
func (r *GrantedPermissionRepository) RevokeAllPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, revokedBy string) error {
|
|
query := `
|
|
UPDATE granted_permissions
|
|
SET revoked = true, revoked_by = $3, revoked_at = $4
|
|
WHERE token_type = $1 AND token_id = $2 AND revoked = false
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
now := time.Now()
|
|
|
|
result, err := db.ExecContext(ctx, query, tokenType, tokenID, revokedBy, now)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to revoke all permissions: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
// Note: rowsAffected being 0 is not necessarily an error here -
|
|
// the token might not have had any active permissions
|
|
_ = rowsAffected
|
|
|
|
return nil
|
|
}
|
|
|
|
// HasPermission checks if a token has a specific permission
|
|
func (r *GrantedPermissionRepository) HasPermission(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, scope string) (bool, error) {
|
|
query := `
|
|
SELECT 1
|
|
FROM granted_permissions gp
|
|
JOIN available_permissions ap ON gp.permission_id = ap.id
|
|
WHERE gp.token_type = $1
|
|
AND gp.token_id = $2
|
|
AND gp.scope = $3
|
|
AND gp.revoked = false
|
|
LIMIT 1
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
var exists int
|
|
err := db.QueryRowContext(ctx, query, string(tokenType), tokenID, scope).Scan(&exists)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return false, nil
|
|
}
|
|
return false, fmt.Errorf("failed to check permission: %w", err)
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// HasAnyPermission checks if a token has any of the specified permissions
|
|
func (r *GrantedPermissionRepository) HasAnyPermission(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, scopes []string) (map[string]bool, error) {
|
|
if len(scopes) == 0 {
|
|
return make(map[string]bool), nil
|
|
}
|
|
|
|
query := `
|
|
SELECT gp.scope
|
|
FROM granted_permissions gp
|
|
JOIN available_permissions ap ON gp.permission_id = ap.id
|
|
WHERE gp.token_type = $1
|
|
AND gp.token_id = $2
|
|
AND gp.scope = ANY($3)
|
|
AND gp.revoked = false
|
|
`
|
|
|
|
db := r.db.GetDB().(*sql.DB)
|
|
rows, err := db.QueryContext(ctx, query, string(tokenType), tokenID, pq.Array(scopes))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to check permissions: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
result := make(map[string]bool)
|
|
// Initialize all scopes as false
|
|
for _, scope := range scopes {
|
|
result[scope] = false
|
|
}
|
|
|
|
// Mark found permissions as true
|
|
for rows.Next() {
|
|
var scope string
|
|
if err := rows.Scan(&scope); err != nil {
|
|
return nil, fmt.Errorf("failed to scan permission scope: %w", err)
|
|
}
|
|
result[scope] = true
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating permission results: %w", err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|