Warden

Full Example

A complete example combining RBAC, ABAC, and ReBAC in a single application.

This example demonstrates all three authorization models working together.

Setup

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/xraph/warden"
    "github.com/xraph/warden/assignment"
    "github.com/xraph/warden/id"
    "github.com/xraph/warden/permission"
    "github.com/xraph/warden/policy"
    "github.com/xraph/warden/relation"
    "github.com/xraph/warden/role"
    "github.com/xraph/warden/store/memory"
)

func main() {
    ctx := context.Background()
    st := memory.New()

    eng, err := warden.NewEngine(warden.WithStore(st))
    if err != nil {
        log.Fatal(err)
    }

Step 1: RBAC — Roles and Permissions

    // IDs and CreatedAt/UpdatedAt timestamps are auto-assigned by the
    // store on Create — no boilerplate. The pointer is mutated in
    // place, so role.ID is populated by the time the call returns.

    // Create permissions
    readPerm := &permission.Permission{
        Name: "doc:read", Resource: "document", Action: "read",
    }
    writePerm := &permission.Permission{
        Name: "doc:write", Resource: "document", Action: "write",
    }
    _ = st.CreatePermission(ctx, readPerm)
    _ = st.CreatePermission(ctx, writePerm)

    // Create roles
    viewerRole := &role.Role{Name: "Viewer", Slug: "viewer"}
    editorRole := &role.Role{Name: "Editor", Slug: "editor"}
    _ = st.CreateRole(ctx, viewerRole)
    _ = st.CreateRole(ctx, editorRole)

    // Attach permissions by name (not typeid).
    _ = st.AttachPermission(ctx, viewerRole.ID, permission.Ref{Name: "doc:read"})
    _ = st.AttachPermission(ctx, editorRole.ID, permission.Ref{Name: "doc:read"})
    _ = st.AttachPermission(ctx, editorRole.ID, permission.Ref{Name: "doc:write"})

    // Assign viewer role to user
    _ = st.CreateAssignment(ctx, &assignment.Assignment{
        RoleID:      viewerRole.ID,
        SubjectKind: "user",
        SubjectID:   "alice",
    })

Step 2: ABAC — Policy Guardrails

    // Deny writes after business hours
    _ = st.CreatePolicy(ctx, &policy.Policy{
        Name:      "Business Hours Only",
        Effect:    policy.EffectDeny,
        Priority:  10,
        IsActive:  true,
        Version:   1,
        Subjects:  []string{"*"},
        Actions:   []string{"write", "delete"},
        Resources: []string{"*"},
        Conditions: []policy.Condition{{
            Field:    "time",
            Operator: policy.OpTimeAfter,
            Value:    "18:00",
        }},
    })

Step 3: ReBAC — Relationship-Based Sharing

    // Bob can view doc-456 via direct relation
    _ = st.CreateRelation(ctx, &relation.Tuple{
        ObjectType:  "document",
        ObjectID:    "doc-456",
        Relation:    "viewer",
        SubjectType: "user",
        SubjectID:   "bob",
    })

Step 4: Run Checks

    checks := []struct {
        name string
        req  *warden.CheckRequest
    }{
        {
            name: "Alice reads document (RBAC)",
            req: &warden.CheckRequest{
                Subject:      warden.Subject{Kind: "user", ID: "alice"},
                Action:       "read",
                ResourceType: "document",
                ResourceID:   "doc-123",
            },
        },
        {
            name: "Alice writes document (RBAC - should fail, viewer only)",
            req: &warden.CheckRequest{
                Subject:      warden.Subject{Kind: "user", ID: "alice"},
                Action:       "write",
                ResourceType: "document",
                ResourceID:   "doc-123",
            },
        },
        {
            name: "Bob views doc-456 (ReBAC)",
            req: &warden.CheckRequest{
                Subject:      warden.Subject{Kind: "user", ID: "bob"},
                Action:       "read",
                ResourceType: "document",
                ResourceID:   "doc-456",
            },
        },
    }

    for _, c := range checks {
        result, err := eng.Check(ctx, c.req)
        if err != nil {
            fmt.Printf("%-50s ERROR: %v\n", c.name, err)
            continue
        }
        fmt.Printf("%-50s allowed=%v reason=%q\n", c.name, result.Allowed, result.Reason)
    }
}

Expected Output

Alice reads document (RBAC)                        allowed=true  reason="rbac: granted"
Alice writes document (RBAC - should fail)         allowed=false reason="default deny"
Bob views doc-456 (ReBAC)                          allowed=true  reason="rebac: granted"

On this page