角色权限、RequirePermission、/me permissions 与 migration 000015; Reviewer Approve → Closed。Next:ECR-013B Contract Definition。 Co-authored-by: Cursor <cursoragent@cursor.com>
132 lines
3.4 KiB
Go
132 lines
3.4 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// AdminRole is an ops RBAC role.
|
|
type AdminRole struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Name string `json:"name"`
|
|
System bool `json:"system"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// ListAdminRoles returns all roles.
|
|
func (r *AdminRepo) ListAdminRoles(ctx context.Context) ([]AdminRole, error) {
|
|
rows, err := r.Pool.Query(ctx, `
|
|
SELECT id, name, system, created_at FROM admin_roles ORDER BY name`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []AdminRole
|
|
for rows.Next() {
|
|
var a AdminRole
|
|
if err := rows.Scan(&a.ID, &a.Name, &a.System, &a.CreatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, a)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// GetAdminRole loads one role.
|
|
func (r *AdminRepo) GetAdminRole(ctx context.Context, id uuid.UUID) (*AdminRole, error) {
|
|
var a AdminRole
|
|
err := r.Pool.QueryRow(ctx, `
|
|
SELECT id, name, system, created_at FROM admin_roles WHERE id=$1`, id,
|
|
).Scan(&a.ID, &a.Name, &a.System, &a.CreatedAt)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &a, nil
|
|
}
|
|
|
|
// ListRolePermissions returns permission codes for a role.
|
|
func (r *AdminRepo) ListRolePermissions(ctx context.Context, roleID uuid.UUID) ([]string, error) {
|
|
rows, err := r.Pool.Query(ctx, `
|
|
SELECT code FROM admin_role_permissions WHERE role_id=$1 ORDER BY code`, roleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []string
|
|
for rows.Next() {
|
|
var c string
|
|
if err := rows.Scan(&c); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, c)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// ReplaceRolePermissions replaces the full permission set for a role.
|
|
func (r *AdminRepo) ReplaceRolePermissions(ctx context.Context, roleID uuid.UUID, codes []string) error {
|
|
tx, err := r.Pool.Begin(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
if _, err := tx.Exec(ctx, `DELETE FROM admin_role_permissions WHERE role_id=$1`, roleID); err != nil {
|
|
return err
|
|
}
|
|
for _, code := range codes {
|
|
if _, err := tx.Exec(ctx, `
|
|
INSERT INTO admin_role_permissions(role_id, code) VALUES ($1,$2)`, roleID, code); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return tx.Commit(ctx)
|
|
}
|
|
|
|
// ListPermissionsForAdmin returns permission codes for an admin account.
|
|
func (r *AdminRepo) ListPermissionsForAdmin(ctx context.Context, adminID uuid.UUID) ([]string, error) {
|
|
rows, err := r.Pool.Query(ctx, `
|
|
SELECT p.code
|
|
FROM admin_accounts a
|
|
JOIN admin_role_permissions p ON p.role_id = a.role_id
|
|
WHERE a.id=$1 AND a.deleted_at IS NULL AND a.role_id IS NOT NULL
|
|
ORDER BY p.code`, adminID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []string
|
|
for rows.Next() {
|
|
var c string
|
|
if err := rows.Scan(&c); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, c)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// GetAdminRoleMeta returns role id/name for an account.
|
|
func (r *AdminRepo) GetAdminRoleMeta(ctx context.Context, adminID uuid.UUID) (roleID *uuid.UUID, name string, err error) {
|
|
var id uuid.UUID
|
|
err = r.Pool.QueryRow(ctx, `
|
|
SELECT r.id, r.name
|
|
FROM admin_accounts a
|
|
JOIN admin_roles r ON r.id = a.role_id
|
|
WHERE a.id=$1 AND a.deleted_at IS NULL`, adminID,
|
|
).Scan(&id, &name)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, "", nil
|
|
}
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
return &id, name, nil
|
|
}
|