Files
metacrypt/internal/policy/policy_test.go
Kyle Isom 4ddd32b117 Implement Phase 1: core framework, operational tooling, and runbook
Core packages: crypto (Argon2id/AES-256-GCM), config (TOML/viper),
db (SQLite/migrations), barrier (encrypted storage), seal (state machine
with rate-limited unseal), auth (MCIAS integration with token cache),
policy (priority-based ACL engine), engine (interface + registry).

Server: HTTPS with TLS 1.2+, REST API, auth/admin middleware, htmx web UI
(init, unseal, login, dashboard pages).

CLI: cobra/viper subcommands (server, init, status, snapshot) with env
var override support (METACRYPT_ prefix).

Operational tooling: Dockerfile (multi-stage, non-root), docker-compose,
hardened systemd units (service + daily backup timer), install script,
backup script with retention pruning, production config examples.

Runbook covering installation, configuration, daily operations,
backup/restore, monitoring, troubleshooting, and security procedures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 20:43:11 -07:00

178 lines
4.0 KiB
Go

package policy
import (
"context"
"path/filepath"
"testing"
"git.wntrmute.dev/kyle/metacrypt/internal/barrier"
"git.wntrmute.dev/kyle/metacrypt/internal/crypto"
"git.wntrmute.dev/kyle/metacrypt/internal/db"
)
func setupPolicy(t *testing.T) (*Engine, func()) {
t.Helper()
dir := t.TempDir()
database, err := db.Open(filepath.Join(dir, "test.db"))
if err != nil {
t.Fatalf("open db: %v", err)
}
if err := db.Migrate(database); err != nil {
t.Fatalf("migrate: %v", err)
}
b := barrier.NewAESGCMBarrier(database)
mek, _ := crypto.GenerateKey()
b.Unseal(mek)
e := NewEngine(b)
return e, func() { database.Close() }
}
func TestAdminBypass(t *testing.T) {
e, cleanup := setupPolicy(t)
defer cleanup()
effect, err := e.Evaluate(context.Background(), &Request{
Username: "admin-user",
Roles: []string{"admin"},
Resource: "engine/transit/default/encrypt",
Action: "write",
})
if err != nil {
t.Fatalf("Evaluate: %v", err)
}
if effect != EffectAllow {
t.Fatalf("admin should always be allowed, got: %s", effect)
}
}
func TestDefaultDeny(t *testing.T) {
e, cleanup := setupPolicy(t)
defer cleanup()
effect, err := e.Evaluate(context.Background(), &Request{
Username: "user1",
Roles: []string{"viewer"},
Resource: "engine/transit/default/encrypt",
Action: "write",
})
if err != nil {
t.Fatalf("Evaluate: %v", err)
}
if effect != EffectDeny {
t.Fatalf("default should deny, got: %s", effect)
}
}
func TestPolicyRuleCRUD(t *testing.T) {
e, cleanup := setupPolicy(t)
defer cleanup()
ctx := context.Background()
rule := &Rule{
ID: "test-rule",
Priority: 100,
Effect: EffectAllow,
Roles: []string{"operator"},
Resources: []string{"engine/transit/*"},
Actions: []string{"read", "write"},
}
if err := e.CreateRule(ctx, rule); err != nil {
t.Fatalf("CreateRule: %v", err)
}
got, err := e.GetRule(ctx, "test-rule")
if err != nil {
t.Fatalf("GetRule: %v", err)
}
if got.Priority != 100 {
t.Errorf("priority: got %d, want 100", got.Priority)
}
rules, err := e.ListRules(ctx)
if err != nil {
t.Fatalf("ListRules: %v", err)
}
if len(rules) != 1 {
t.Fatalf("ListRules: got %d rules, want 1", len(rules))
}
if err := e.DeleteRule(ctx, "test-rule"); err != nil {
t.Fatalf("DeleteRule: %v", err)
}
rules, _ = e.ListRules(ctx)
if len(rules) != 0 {
t.Fatalf("after delete: got %d rules, want 0", len(rules))
}
}
func TestPolicyPriorityOrder(t *testing.T) {
e, cleanup := setupPolicy(t)
defer cleanup()
ctx := context.Background()
// Lower priority number = higher priority. Deny should win.
e.CreateRule(ctx, &Rule{
ID: "allow-rule",
Priority: 200,
Effect: EffectAllow,
Roles: []string{"operator"},
Resources: []string{"engine/transit/*"},
Actions: []string{"write"},
})
e.CreateRule(ctx, &Rule{
ID: "deny-rule",
Priority: 100,
Effect: EffectDeny,
Roles: []string{"operator"},
Resources: []string{"engine/transit/*"},
Actions: []string{"write"},
})
effect, _ := e.Evaluate(ctx, &Request{
Username: "user1",
Roles: []string{"operator"},
Resource: "engine/transit/default",
Action: "write",
})
if effect != EffectDeny {
t.Fatalf("higher priority deny should win, got: %s", effect)
}
}
func TestPolicyUsernameMatch(t *testing.T) {
e, cleanup := setupPolicy(t)
defer cleanup()
ctx := context.Background()
e.CreateRule(ctx, &Rule{
ID: "user-specific",
Priority: 100,
Effect: EffectAllow,
Usernames: []string{"alice"},
Resources: []string{"engine/*"},
Actions: []string{"read"},
})
effect, _ := e.Evaluate(ctx, &Request{
Username: "alice",
Roles: []string{"user"},
Resource: "engine/ca",
Action: "read",
})
if effect != EffectAllow {
t.Fatalf("alice should be allowed, got: %s", effect)
}
effect, _ = e.Evaluate(ctx, &Request{
Username: "bob",
Roles: []string{"user"},
Resource: "engine/ca",
Action: "read",
})
if effect != EffectDeny {
t.Fatalf("bob should be denied, got: %s", effect)
}
}