readysite / website / internal / access / access_test.go
6.2 KB
access_test.go
package access_test

import (
	"testing"

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

func init() {
	// Use in-memory database for tests
	models.DB = engines.NewMemory()
	models.Users = database.Manage(models.DB, new(models.User),
		database.WithUniqueIndex[models.User]("Email"),
	)
	models.ACLRules = database.Manage(models.DB, new(models.ACLRule),
		database.WithIndex[models.ACLRule]("SubjectType", "SubjectID"),
		database.WithIndex[models.ACLRule]("ResourceType", "ResourceID"),
	)
}

func TestIsAdmin(t *testing.T) {
	adminUser := &models.User{Role: access.RoleAdmin}
	normalUser := &models.User{Role: access.RoleUser}
	viewerUser := &models.User{Role: access.RoleViewer}

	if !access.IsAdmin(adminUser) {
		t.Error("expected admin user to be admin")
	}
	if access.IsAdmin(normalUser) {
		t.Error("expected normal user not to be admin")
	}
	if access.IsAdmin(viewerUser) {
		t.Error("expected viewer user not to be admin")
	}
}

func TestGrants(t *testing.T) {
	tests := []struct {
		have, want string
		expected   bool
	}{
		// Admin grants everything
		{access.PermAdmin, access.PermAdmin, true},
		{access.PermAdmin, access.PermDelete, true},
		{access.PermAdmin, access.PermWrite, true},
		{access.PermAdmin, access.PermRead, true},

		// Delete grants write and read
		{access.PermDelete, access.PermDelete, true},
		{access.PermDelete, access.PermWrite, true},
		{access.PermDelete, access.PermRead, true},
		{access.PermDelete, access.PermAdmin, false},

		// Write grants read only
		{access.PermWrite, access.PermWrite, true},
		{access.PermWrite, access.PermRead, true},
		{access.PermWrite, access.PermDelete, false},
		{access.PermWrite, access.PermAdmin, false},

		// Read grants only read
		{access.PermRead, access.PermRead, true},
		{access.PermRead, access.PermWrite, false},
		{access.PermRead, access.PermDelete, false},
		{access.PermRead, access.PermAdmin, false},
	}

	for _, tt := range tests {
		result := access.Grants(tt.have, tt.want)
		if result != tt.expected {
			t.Errorf("Grants(%q, %q) = %v, want %v", tt.have, tt.want, result, tt.expected)
		}
	}
}

func TestCheckAccessAdmin(t *testing.T) {
	adminUser := &models.User{Role: access.RoleAdmin}

	// Admins should have access to everything
	if !access.CheckAccess(adminUser, access.ResourcePage, "any-page", access.PermRead) {
		t.Error("admin should have read access to pages")
	}
	if !access.CheckAccess(adminUser, access.ResourcePage, "any-page", access.PermAdmin) {
		t.Error("admin should have admin access to pages")
	}
}

func TestCheckAccessPublicRule(t *testing.T) {
	// Create a public rule for a page
	rule := &models.ACLRule{
		SubjectType:  access.SubjectPublic,
		SubjectID:    "",
		ResourceType: access.ResourcePage,
		ResourceID:   "public-page",
		Permission:   access.PermRead,
	}
	models.ACLRules.Insert(rule)
	defer models.ACLRules.Delete(rule)

	// Anonymous user should have read access
	if !access.CheckAccess(nil, access.ResourcePage, "public-page", access.PermRead) {
		t.Error("anonymous user should have read access to public page")
	}

	// Anonymous user should NOT have write access
	if access.CheckAccess(nil, access.ResourcePage, "public-page", access.PermWrite) {
		t.Error("anonymous user should not have write access to public page")
	}
}

func TestCheckAccessUserRule(t *testing.T) {
	// Create a user
	user := &models.User{Role: access.RoleUser}
	user.ID = "test-user-acl"
	models.Users.Insert(user)
	defer models.Users.Delete(user)

	// Create a user-specific rule
	rule := &models.ACLRule{
		SubjectType:  access.SubjectUser,
		SubjectID:    user.ID,
		ResourceType: access.ResourcePage,
		ResourceID:   "user-page",
		Permission:   access.PermWrite,
	}
	models.ACLRules.Insert(rule)
	defer models.ACLRules.Delete(rule)

	// User should have write access
	if !access.CheckAccess(user, access.ResourcePage, "user-page", access.PermWrite) {
		t.Error("user should have write access to their page")
	}

	// User should have read access (implied by write)
	if !access.CheckAccess(user, access.ResourcePage, "user-page", access.PermRead) {
		t.Error("user should have read access to their page")
	}

	// Other users should NOT have access
	otherUser := &models.User{Role: access.RoleUser}
	otherUser.ID = "other-user"
	if access.CheckAccess(otherUser, access.ResourcePage, "user-page", access.PermWrite) {
		t.Error("other user should not have access")
	}
}

func TestCheckAccessRoleRule(t *testing.T) {
	// Create a role-based rule
	rule := &models.ACLRule{
		SubjectType:  access.SubjectRole,
		SubjectID:    access.RoleUser,
		ResourceType: access.ResourceCollection,
		ResourceID:   "", // All collections
		Permission:   access.PermRead,
	}
	models.ACLRules.Insert(rule)
	defer models.ACLRules.Delete(rule)

	// User with 'user' role should have read access
	user := &models.User{Role: access.RoleUser}
	user.ID = "role-test-user"
	if !access.CheckAccess(user, access.ResourceCollection, "any-collection", access.PermRead) {
		t.Error("user should have read access via role")
	}

	// Viewer should NOT have access
	viewer := &models.User{Role: access.RoleViewer}
	viewer.ID = "role-test-viewer"
	if access.CheckAccess(viewer, access.ResourceCollection, "any-collection", access.PermRead) {
		t.Error("viewer should not have access via user role")
	}
}

func TestGrantAndRevokeAccess(t *testing.T) {
	// Grant access
	err := access.GrantAccess(access.SubjectUser, "grant-test-user", access.ResourcePage, "grant-page", access.PermWrite)
	if err != nil {
		t.Fatalf("failed to grant access: %v", err)
	}

	// Verify rule was created
	rules, err := access.GetRules(access.ResourcePage, "grant-page")
	if err != nil {
		t.Fatalf("failed to get rules: %v", err)
	}
	if len(rules) != 1 {
		t.Errorf("expected 1 rule, got %d", len(rules))
	}

	// Revoke access
	err = access.RevokeAccess(access.SubjectUser, "grant-test-user", access.ResourcePage, "grant-page")
	if err != nil {
		t.Fatalf("failed to revoke access: %v", err)
	}

	// Verify rule was deleted
	rules, _ = access.GetRules(access.ResourcePage, "grant-page")
	if len(rules) != 0 {
		t.Errorf("expected 0 rules after revoke, got %d", len(rules))
	}
}
← Back