231 lines
7.2 KiB
Go
231 lines
7.2 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/oklog/ulid/v2"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
var (
|
|
permissionRole string
|
|
permissionResource string
|
|
permissionAction string
|
|
)
|
|
|
|
var permissionCmd = &cobra.Command{
|
|
Use: "permission",
|
|
Short: "Manage permissions",
|
|
Long: `Commands for managing permissions in the MCIAS system.`,
|
|
}
|
|
|
|
var listPermissionsCmd = &cobra.Command{
|
|
Use: "list",
|
|
Short: "List all permissions",
|
|
Long: `List all permissions in the MCIAS system.`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
listPermissions()
|
|
},
|
|
}
|
|
|
|
var grantPermissionCmd = &cobra.Command{
|
|
Use: "grant",
|
|
Short: "Grant a permission to a role",
|
|
Long: `Grant a permission to a role in the MCIAS system.`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
grantPermission()
|
|
},
|
|
}
|
|
|
|
var revokePermissionCmd = &cobra.Command{
|
|
Use: "revoke",
|
|
Short: "Revoke a permission from a role",
|
|
Long: `Revoke a permission from a role in the MCIAS system.`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
revokePermission()
|
|
},
|
|
}
|
|
|
|
// nolint:gochecknoinits // This is a standard pattern in Cobra applications
|
|
func init() {
|
|
rootCmd.AddCommand(permissionCmd)
|
|
permissionCmd.AddCommand(listPermissionsCmd)
|
|
permissionCmd.AddCommand(grantPermissionCmd)
|
|
permissionCmd.AddCommand(revokePermissionCmd)
|
|
|
|
grantPermissionCmd.Flags().StringVar(&permissionRole, "role", "", "Name of the role to grant the permission to")
|
|
grantPermissionCmd.Flags().StringVar(&permissionResource, "resource", "", "Resource for the permission")
|
|
grantPermissionCmd.Flags().StringVar(&permissionAction, "action", "", "Action for the permission")
|
|
if err := grantPermissionCmd.MarkFlagRequired("role"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error marking role flag as required: %v\n", err)
|
|
}
|
|
if err := grantPermissionCmd.MarkFlagRequired("resource"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error marking resource flag as required: %v\n", err)
|
|
}
|
|
if err := grantPermissionCmd.MarkFlagRequired("action"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error marking action flag as required: %v\n", err)
|
|
}
|
|
|
|
revokePermissionCmd.Flags().StringVar(&permissionRole, "role", "", "Name of the role to revoke the permission from")
|
|
revokePermissionCmd.Flags().StringVar(&permissionResource, "resource", "", "Resource for the permission")
|
|
revokePermissionCmd.Flags().StringVar(&permissionAction, "action", "", "Action for the permission")
|
|
if err := revokePermissionCmd.MarkFlagRequired("role"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error marking role flag as required: %v\n", err)
|
|
}
|
|
if err := revokePermissionCmd.MarkFlagRequired("resource"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error marking resource flag as required: %v\n", err)
|
|
}
|
|
if err := revokePermissionCmd.MarkFlagRequired("action"); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error marking action flag as required: %v\n", err)
|
|
}
|
|
}
|
|
|
|
func listPermissions() {
|
|
dbPath := viper.GetString("db")
|
|
logger := log.New(os.Stdout, "MCIAS: ", log.LstdFlags)
|
|
|
|
db, err := sql.Open("sqlite3", dbPath)
|
|
if err != nil {
|
|
logger.Fatalf("Failed to open database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
rows, err := db.Query("SELECT id, resource, action, description FROM permissions ORDER BY resource, action")
|
|
if err != nil {
|
|
logger.Fatalf("Failed to query permissions: %v", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
fmt.Printf("%-24s %-20s %-15s %-30s\n", "ID", "RESOURCE", "ACTION", "DESCRIPTION")
|
|
fmt.Println(strings.Repeat("-", 90))
|
|
for rows.Next() {
|
|
var id, resource, action, description string
|
|
if scanErr := rows.Scan(&id, &resource, &action, &description); scanErr != nil {
|
|
logger.Fatalf("Failed to scan permission row: %v", scanErr)
|
|
}
|
|
fmt.Printf("%-24s %-20s %-15s %-30s\n", id, resource, action, description)
|
|
}
|
|
|
|
if rowErr := rows.Err(); rowErr != nil {
|
|
logger.Fatalf("Error iterating permission rows: %v", rowErr)
|
|
}
|
|
}
|
|
|
|
func grantPermission() {
|
|
dbPath := viper.GetString("db")
|
|
logger := log.New(os.Stdout, "MCIAS: ", log.LstdFlags)
|
|
|
|
db, err := sql.Open("sqlite3", dbPath)
|
|
if err != nil {
|
|
logger.Fatalf("Failed to open database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Get role ID
|
|
var roleID string
|
|
err = db.QueryRow("SELECT id FROM roles WHERE role = ?", permissionRole).Scan(&roleID)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
logger.Fatalf("Role %s not found", permissionRole)
|
|
}
|
|
logger.Fatalf("Failed to get role ID: %v", err)
|
|
}
|
|
|
|
// Get permission ID
|
|
var permissionID string
|
|
err = db.QueryRow("SELECT id FROM permissions WHERE resource = ? AND action = ?",
|
|
permissionResource, permissionAction).Scan(&permissionID)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
logger.Fatalf("Permission with resource '%s' and action '%s' not found",
|
|
permissionResource, permissionAction)
|
|
}
|
|
logger.Fatalf("Failed to get permission ID: %v", err)
|
|
}
|
|
|
|
// Check if role already has this permission
|
|
var count int
|
|
err = db.QueryRow("SELECT COUNT(*) FROM role_permissions WHERE rid = ? AND pid = ?",
|
|
roleID, permissionID).Scan(&count)
|
|
if err != nil {
|
|
logger.Fatalf("Failed to check if role has permission: %v", err)
|
|
}
|
|
if count > 0 {
|
|
logger.Fatalf("Role %s already has permission %s:%s",
|
|
permissionRole, permissionResource, permissionAction)
|
|
}
|
|
|
|
// Generate a new ID for the role-permission relationship
|
|
id := ulid.Make().String()
|
|
|
|
// Grant permission to role
|
|
_, err = db.Exec("INSERT INTO role_permissions (id, rid, pid) VALUES (?, ?, ?)",
|
|
id, roleID, permissionID)
|
|
if err != nil {
|
|
logger.Fatalf("Failed to grant permission: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Permission %s:%s granted to role %s successfully\n",
|
|
permissionResource, permissionAction, permissionRole)
|
|
}
|
|
|
|
func revokePermission() {
|
|
dbPath := viper.GetString("db")
|
|
logger := log.New(os.Stdout, "MCIAS: ", log.LstdFlags)
|
|
|
|
db, err := sql.Open("sqlite3", dbPath)
|
|
if err != nil {
|
|
logger.Fatalf("Failed to open database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Get role ID
|
|
var roleID string
|
|
err = db.QueryRow("SELECT id FROM roles WHERE role = ?", permissionRole).Scan(&roleID)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
logger.Fatalf("Role %s not found", permissionRole)
|
|
}
|
|
logger.Fatalf("Failed to get role ID: %v", err)
|
|
}
|
|
|
|
// Get permission ID
|
|
var permissionID string
|
|
err = db.QueryRow("SELECT id FROM permissions WHERE resource = ? AND action = ?",
|
|
permissionResource, permissionAction).Scan(&permissionID)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
logger.Fatalf("Permission with resource '%s' and action '%s' not found",
|
|
permissionResource, permissionAction)
|
|
}
|
|
logger.Fatalf("Failed to get permission ID: %v", err)
|
|
}
|
|
|
|
// Check if role has this permission
|
|
var count int
|
|
err = db.QueryRow("SELECT COUNT(*) FROM role_permissions WHERE rid = ? AND pid = ?",
|
|
roleID, permissionID).Scan(&count)
|
|
if err != nil {
|
|
logger.Fatalf("Failed to check if role has permission: %v", err)
|
|
}
|
|
if count == 0 {
|
|
logger.Fatalf("Role %s does not have permission %s:%s",
|
|
permissionRole, permissionResource, permissionAction)
|
|
}
|
|
|
|
// Revoke permission from role
|
|
_, err = db.Exec("DELETE FROM role_permissions WHERE rid = ? AND pid = ?", roleID, permissionID)
|
|
if err != nil {
|
|
logger.Fatalf("Failed to revoke permission: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Permission %s:%s revoked from role %s successfully\n",
|
|
permissionResource, permissionAction, permissionRole)
|
|
}
|