3.9 KB
acl.go
package controllers

import (
	"fmt"
	"net/http"

	"github.com/readysite/readysite/pkg/application"
	"github.com/readysite/readysite/website/internal/access"
	"github.com/readysite/readysite/website/models"
)

// ACL returns the acl controller.
func ACL() (string, *ACLController) {
	return "acl", &ACLController{}
}

// ACLController handles ACL management.
type ACLController struct {
	application.BaseController
}

// Setup registers routes.
func (c *ACLController) Setup(app *application.App) {
	c.BaseController.Setup(app)
}

// Handle implements Controller interface with value receiver for request isolation.
func (c ACLController) Handle(r *http.Request) application.Controller {
	c.Request = r
	return &c
}

// ResourceType returns the current resource type from the path.
func (c *ACLController) ResourceType() string {
	if c.Request == nil {
		return ""
	}
	return c.PathValue("type")
}

// ResourceID returns the current resource ID from the path.
func (c *ACLController) ResourceID() string {
	if c.Request == nil {
		return ""
	}
	return c.PathValue("id")
}

// Rules returns the ACL rules for the current resource.
func (c *ACLController) Rules() []*models.ACLRule {
	rules, _ := access.GetRules(c.ResourceType(), c.ResourceID())
	return rules
}

// SubjectTypes returns available subject types.
func (c *ACLController) SubjectTypes() []string {
	return []string{access.SubjectUser, access.SubjectRole, access.SubjectPublic}
}

// Permissions returns available permission levels.
func (c *ACLController) Permissions() []string {
	return []string{access.PermRead, access.PermWrite, access.PermDelete, access.PermAdmin}
}

// Users returns all users for user selection.
func (c *ACLController) Users() []*models.User {
	users, _ := models.Users.Search("ORDER BY Email")
	return users
}

// Roles returns available roles.
func (c *ACLController) Roles() []string {
	return []string{access.RoleAdmin, access.RoleUser, access.RoleViewer}
}

// Create creates a new ACL rule.
func (c *ACLController) Create(w http.ResponseWriter, r *http.Request) {
	resourceType := r.PathValue("type")
	resourceID := r.PathValue("id")
	subjectType := r.FormValue("subject_type")
	subjectID := r.FormValue("subject_id")
	permission := r.FormValue("permission")

	if subjectType == "" || permission == "" {
		c.RenderError(w, r, fmt.Errorf("Subject type and permission are required"))
		return
	}

	// Public rules don't need a subject ID
	if subjectType == access.SubjectPublic {
		subjectID = ""
	}

	if err := access.GrantAccess(subjectType, subjectID, resourceType, resourceID, permission); err != nil {
		c.RenderError(w, r, fmt.Errorf("Failed to create rule: %w", err))
		return
	}

	c.Refresh(w, r)
}

// Delete deletes an ACL rule.
func (c *ACLController) Delete(w http.ResponseWriter, r *http.Request) {
	ruleID := r.PathValue("ruleId")

	rule, err := models.ACLRules.Get(ruleID)
	if err != nil {
		c.RenderError(w, r, fmt.Errorf("Rule not found"))
		return
	}

	if err := models.ACLRules.Delete(rule); err != nil {
		c.RenderError(w, r, fmt.Errorf("Failed to delete rule: %w", err))
		return
	}

	c.Refresh(w, r)
}

// SubjectLabel returns a human-readable label for a subject.
func (c *ACLController) SubjectLabel(rule *models.ACLRule) string {
	switch rule.SubjectType {
	case access.SubjectPublic:
		return "Public"
	case access.SubjectRole:
		return "Role: " + rule.SubjectID
	case access.SubjectUser:
		user, err := models.Users.Get(rule.SubjectID)
		if err == nil && user != nil {
			return "User: " + user.Email
		}
		return "User: " + rule.SubjectID
	default:
		return rule.SubjectType + ": " + rule.SubjectID
	}
}

// PermissionLabel returns a human-readable label for a permission.
func (c *ACLController) PermissionLabel(perm string) string {
	switch perm {
	case access.PermRead:
		return "Read"
	case access.PermWrite:
		return "Write"
	case access.PermDelete:
		return "Delete"
	case access.PermAdmin:
		return "Admin"
	default:
		return perm
	}
}
← Back