Permission Service Integration

Overview

The PermissionService is a centralized authorization service that:
  1. Queries user roles from Better Auth PostgreSQL tables
  2. Maps roles to permissions (e.g., owner["org:manage", "data:write", "leave:approve", ...])
  3. Caches permission lookups for 5 minutes
  4. Provides methods to check authorization in your use cases
Use it whenever you need to verify if a user is allowed to perform an action.

Interface

The PermissionService implements the port.PermissionService interface:

Usage Patterns

Pattern 1: Check Specific Permission

When: You need to verify a single permission like “data:write” or “leave:approve”

Pattern 2: Check Role

When: You need to know what role the user has (for role-specific logic or UI rendering)

Pattern 3: Check Multiple Permissions (Any)

When: User needs ANY of several permissions to proceed

Pattern 4: Leave Approval (Pre-Built Method)

When: Checking if user can approve leave for an employee

Integration with Dependency Injection

In Module Initialization

In Your Use Case


Caching Behavior

Cache Details

  • Key: perm:<userId>:<organizationId>
  • TTL: 5 minutes
  • Stored: User’s org role + resolved permissions array

How It Works

First request:
Second request (within 5 minutes):
After 5 minutes:

Performance Impact

  • Cached: ~1-2 µs (map lookup)
  • Uncached: ~10-50 ms (DB query + resolution)
This is why caching is important — a single approval operation might check permissions 3-4 times.

Error Handling

Common Errors

Defensive Coding

Always treat missing users as “not authorized”:

Permission Reference

Available Permissions

Role-to-Permission Mapping


Middleware Integration

Auto-Extracting Organization from JWT

The request context contains organization_id from the JWT. Use it:

Testing

Unit Test Example


FAQ

Q: Can I change permission mappings?

A: The mappings are hardcoded in permission_service.go (the defaultRolePermissions map). To change them, edit this file and redeploy. This is intentional — permissions are part of system design, not runtime configuration.

Q: What if I need real-time permission updates (< 5 sec)?

A: Options:
  1. Use shorter TTL: Change cache TTL from 5 min to 1 min in NewPermissionService()
  2. Subscribe to events: Have Bun publish a NATS event when role changes, Go listens and invalidates cache
  3. Disable cache: Return false from cache.get() to force DB queries (not recommended for performance)

Q: Can I add new permissions?

A: Yes:
  1. Add the permission string to the appropriate role in defaultRolePermissions
  2. Use it in your service: HasPermission(ctx, user, org, "my:new:permission")
  3. Test it

Q: What about superadmins?

A: Platform superadmins (from auth.role = "superadmin") can do anything. Check with IsUserSuperAdmin(ctx, userID) if needed.