Authentication & Authorization
This guide provides a comprehensive reference for SPOG's security model, covering both authentication (verifying identity) and authorization (granting permissions). You will learn how JWT tokens carry user identity, how REGO policies make access decisions, and how to write custom policies that match your organizational structure.
What You'll Learn
- How SPOG's security model separates authentication from authorization
- JWT token structure and how claims become available in policies
- REGO syntax fundamentals: OR logic, AND logic, and label checking
- How to build helper modules that create reusable policy abstractions
- Access control patterns: RBAC, ABAC, scopes, and dynamic scopes
- How to use the debug interfaces for policy development and testing
- Best practices for writing secure, maintainable authorization policies
Prerequisites
This guide assumes familiarity with the Architecture Overview, which introduces the three-layer permission model. For background on cluster labels and how they work, see the Labels & Filter Queries guide. All REGO examples use label patterns from the quickstart deployment.
Security Model Overview
SPOG implements a defense-in-depth security model with clear separation between authentication and authorization.
Authentication vs Authorization
Authentication answers "Who are you?" - It verifies user identity through credentials, tokens, or identity providers. SPOG supports multiple authentication methods: static users for development/testing and OIDC for production (including LDAP via OIDC providers like Keycloak).
Authorization answers "What can you do?" - After authentication establishes identity, the REGO policy engine evaluates what actions that identity can perform on which resources. Authorization decisions consider both user attributes (roles, groups) and resource attributes (cluster labels).
The Authorization Flow
When a user attempts an operation, SPOG follows this flow:
- Token Validation: The auth service validates the JWT token
- Claims Extraction: User attributes are extracted from the token
- Context Assembly: User claims are combined with cluster labels to form the policy input
- Policy Evaluation: The REGO engine evaluates policies against the assembled input
- Decision: Access is granted or denied based on policy results
| Text Only | |
|---|---|
Well-Known Permissions
SPOG's policy engine evaluates these well-known permissions defined in pdns_permissions.rego:
| Permission | Purpose | Example Use |
|---|---|---|
connect |
Basic connection (always true for authenticated users) | Establish session |
read |
View cluster in UI | See cluster in dashboard |
read_logs |
View detailed information | Read container logs |
clear_cache |
Clear DNS cache | Invalidate cached records |
restart_instance_set |
Restart pods | Rolling restart of instances |
delete_pod |
Delete individual pods | Remove specific container |
dns_check |
Execute DNS queries | Test DNS resolution |
These permissions use helper rules from user.rego (like can_see_cluster, can_observe_cluster) to make decisions. The helpers are not permissions themselves - they're reusable building blocks.
Write Without Read
Write permissions (clear_cache, restart_instance_set) require cluster visibility but not read access. A user can have permission to clear cache without having permission to view logs.
Authentication Methods
SPOG supports multiple authentication methods to accommodate different deployment scenarios.
Static Users (Development and POC)
Static users provide a simple authentication mechanism for development, testing, and proof-of-concept deployments. Users are defined directly in the Helm configuration without requiring external identity infrastructure.
Configuration Structure:
Static User Fields:
Static user fields are arbitrary - whatever you define in the YAML becomes available as claims in REGO. The only special field is password, which is used for authentication and then removed from the claims. Common conventions include:
| Field | Convention | Available in REGO |
|---|---|---|
password |
Authentication credential | Not available (removed after auth) |
id |
Unique identifier for the user | input.user.id |
sub |
Subject claim (typically matches id) | input.user.sub |
name |
Display name shown in the UI | input.user.name |
roles |
Array of role strings for authorization | input.user.roles |
groups |
Array of group memberships | input.user.groups |
| any field | Custom claims you define | input.user.<field> |
You can add any custom fields your policies need:
| YAML | |
|---|---|
Static Users Are Not for Production
Static users store credentials in Kubernetes ConfigMaps. Use static users only for development, testing, or demos. For production deployments, configure OIDC integration with your enterprise identity provider.
OIDC Integration (Production)
For production environments, SPOG integrates with OIDC (OpenID Connect) identity providers such as Keycloak, Okta, Azure AD, or Auth0. OIDC provides enterprise-grade authentication with features like single sign-on, multi-factor authentication, and centralized user management.
Configuration Overview:
When OIDC is enabled, SPOG:
- Redirects unauthenticated users to the identity provider
- Receives an ID token after successful authentication
- Extracts claims from the token for authorization decisions
- Maps OIDC claims to REGO input fields
Common OIDC Claims Mapping:
| OIDC Claim | REGO Path | Description |
|---|---|---|
sub |
input.user.sub |
Unique user identifier |
name |
input.user.name |
Display name |
email |
input.user.email |
Email address |
groups |
input.user.groups |
Group memberships |
roles |
input.user.roles |
Role assignments |
| Custom claims | input.user.<claim> |
Any custom claims |
Claims Are Case-Normalized
SPOG normalizes all claim keys to lowercase before making them available in REGO policies. If your OIDC provider sends Groups or ROLES, they will be accessible as input.user.groups and input.user.roles.
Playlist Tokens (NOC Displays)
Playlist tokens provide bearer token authentication for NOC displays, kiosk screens, and dashboards that need to show playlists without interactive user login. Unlike user tokens (OIDC or static), playlist tokens are simple bearer strings with claims defined in the Helm configuration.
Key Characteristics:
- Prefix:
PLT_(e.g.,PLT_abc123def456) - Token type:
playlist(routed toinput.playlistin REGO) - Read-only access: Can only view playlists, not modify data
- No interactive login required
Configuration Structure:
| YAML | |
|---|---|
Token Source Modes:
| Mode | Configuration | Use Case |
|---|---|---|
| Direct | token: "PLT_..." |
Simple setup, token visible in config |
| From Secret | fromSecret: {name, key} |
Reference pre-existing K8s secret |
| Generate | generate: {secretName, key, length} |
Auto-generate secure token |
Claims Available in REGO:
Playlist token claims are available at input.playlist.<claim>:
| Claim | Example | REGO Path |
|---|---|---|
groups |
["noc-viewers"] |
input.playlist.groups |
playlists |
["noc-24x7"] |
input.playlist.playlists |
| Custom claims | Any value you define | input.playlist.<claim> |
Using Playlist Tokens:
Access playlists by including the token in the URL:
| Text Only | |
|---|---|
REGO Policy for Playlist Access:
Retrieving Auto-Generated Tokens:
| Bash | |
|---|---|
Playlist Tokens Are Read-Only
Playlist tokens only grant access to view playlists and dashboard data. They cannot perform write operations, clear caches, restart instances, or access cluster management features.
Understanding JWT Tokens
JWT (JSON Web Token) tokens are the carrier of identity information in SPOG. Understanding their structure helps you write effective authorization policies.
Token Structure
A JWT consists of three parts separated by dots: header.payload.signature
The payload contains claims - key-value pairs that describe the token subject and metadata:
| JSON | |
|---|---|
Standard vs Custom Claims
Standard Claims (defined by JWT specification):
| Claim | Description |
|---|---|
sub |
Subject - unique identifier for the user |
iat |
Issued At - timestamp when token was created |
exp |
Expiration - timestamp when token expires |
iss |
Issuer - who created the token |
Custom Claims (added by SPOG or your OIDC provider):
| Claim | Description |
|---|---|
roles |
Array of role assignments |
groups |
Array of group memberships |
token_type |
Type of token (typically user) |
name |
Human-readable display name |
How Claims Become REGO Input
The policy service transforms JWT claims into the input object available in REGO policies:
View Your Claims in the Debug Interface
Navigate to /debug/user to see your actual JWT claims as they appear to the policy service. This is invaluable when writing policies that reference specific claim fields.
REGO Authorization System
REGO is a declarative policy language created by the Open Policy Agent (OPA) project. SPOG uses REGO because it provides powerful, flexible authorization logic that can express complex access control requirements.
What is REGO?
REGO policies are collections of rules that evaluate to true or false. Unlike imperative code that describes how to compute something, REGO describes what conditions must be true for a result.
Key characteristics of REGO:
- Declarative: Describe conditions, not procedures
- Data-driven: Policies operate on structured input data
- Composable: Build complex rules from simpler rules
- Safe: No side effects, guaranteed termination
Policy Structure in SPOG
SPOG organizes REGO policies into packages with specific purposes:
| Package | Purpose | File |
|---|---|---|
user |
Helper rules for user attribute matching | user.rego |
pdns_permissions |
Well-known permissions for cluster operations | pdns_permissions.rego |
pdns_global_flags |
Custom flags for navigation and dashboard access | pdns_global_flags.rego |
Required Policy File Structure
Every REGO policy file must begin with a package declaration:
Package Declaration (package name)
The package statement declares which namespace this file's rules belong to. This is mandatory - OPA will reject files without a package declaration. The package name determines how other policies reference these rules:
| Package Declaration | How to Reference Rules |
|---|---|
package user |
user.can_see_cluster, user.admin |
package pdns_permissions |
data.pdns_permissions.read |
package pdns_global_flags |
data.pdns_global_flags.dashboard_noc |
When importing and using another package's rules within a policy file, use the import statement:
| Rego | |
|---|---|
Always Include Package Declaration
Every policy file must start with package <name>. Missing this will cause policy evaluation errors.
pdns_permissions vs pdns_global_flags
Understanding the difference between these two packages is essential for writing effective policies:
pdns_permissions defines well-known permission names that SPOG's frontend and backend explicitly check. These permission names (like read, clear_cache, restart_instance_set) are hard-coded into the system - the UI checks them to show/hide buttons, and the backend checks them to allow/deny operations. You cannot invent new permission names here; you can only define the rules that grant the existing permissions.
| Rego | |
|---|---|
pdns_global_flags defines arbitrary flag names that you invent for use with requires: in navigation menus and dashboards. The flag names are entirely up to you - SPOG doesn't know or care what they're called. You create a flag, then reference it in your navigation or dashboard configuration.
| Rego | |
|---|---|
These flags are then referenced in navigation and dashboard configuration:
| YAML | |
|---|---|
| Aspect | pdns_permissions | pdns_global_flags |
|---|---|---|
| Names | Well-known, hard-coded in SPOG | Arbitrary, you invent them |
| Purpose | Cluster operations (read, write, execute) | UI visibility (navigation, dashboards) |
| Checked by | Frontend + Backend | Frontend only (via requires:) |
| Creating new | Cannot add new permission names | Create any flag names you need |
Naming Convention
While pdns_global_flags names are arbitrary, using descriptive prefixes helps organization:
dashboard_*for dashboard access flagsplaylist_*for playlist access flagsnavigation_*for navigation item visibilityfeature_*for feature flags
Policy File Locations:
- Default Policies: Built into the policy service image at
/etc/defaultPolicies/ - User Policies: Mounted from ConfigMaps at
/etc/policies/
User policies override default policies when they define the same package. This allows you to customize authorization without modifying the base image.
REGO Syntax Essentials
Understanding three fundamental REGO patterns will enable you to write most authorization policies. These patterns are demonstrated throughout the complete examples in the Writing REGO Policies section below.
OR Logic: Multiple Rules with the Same Name
When you define multiple rules with the same name, REGO treats them as alternatives - if any rule evaluates to true, the result is true. This implements OR logic.
Example: Regional Access
This defines four separate rules named has_matching_region. A user matches if:
- They have the
americasrole AND the cluster is inus-eastorus-west, OR - They have the
europerole AND the cluster is ineu-eastoreu-west, OR - They have the
apacrole AND the cluster is inap-northorap-south, OR - They have the
globalrole (regardless of cluster region)
Another Example: Dashboard Access
OR Logic Summary
Multiple rules with the same name implement OR logic. The rule is true if any of the definitions evaluates to true.
AND Logic: Multiple Conditions in a Rule
Within a single rule definition, multiple conditions on separate lines implement AND logic - all conditions must be true for the rule to be true.
Example: Cluster Visibility
| Rego | |
|---|---|
This rule requires all three helper rules to be true. A user with americas role (matching region) but missing the production role (environment mismatch) cannot see production clusters.
Example: Non-Production Operator
| Rego | |
|---|---|
All three conditions must be true:
1. User can see the cluster (via region/role/environment matching)
2. User has the content-manager-non-prod role
3. The cluster is NOT in the production environment
Combining OR and AND:
This defines three ways to get DNS content management permission:
1. Have the content-manager role (full access), OR
2. Have content-manager-non-prod AND be accessing a non-production cluster, OR
3. Be an admin
AND Logic Summary
Multiple lines within a single rule block implement AND logic. All conditions must be true for that rule definition to be true.
Checking Labels and Values
REGO provides powerful operators for checking membership and matching values. Understanding how in works is essential for SPOG policies.
Using in for Membership:
The in operator checks if a value is a member of a collection:
Labels Are Always Arrays
Cluster labels are always arrays, even for single values (e.g., region: ["us-east"]). This means:
- Never use
==for label comparisons:input.cluster.labels.region == "us-east"will fail - Use
into check if a value exists in a label:"us-east" in input.cluster.labels.region - Use
someto iterate when matching against user attributes
Using some for Iteration:
The some keyword introduces a local variable that iterates over a collection:
| Rego | |
|---|---|
This iterates through all user roles and checks if any of them is in the cluster's role label. Use some when you need to match ANY element from one collection against another.
Checking Label Existence:
| Rego | |
|---|---|
Combining Iteration for Multi-Valued Labels:
When clusters can have multiple values for a label (e.g., multiple teams managing one cluster), use some to check both sides:
| Rego | |
|---|---|
Single vs Multi-Valued Labels
Most labels are single-valued strings in the input (e.g., region: "us-east"). However, some labels can be arrays for shared ownership (e.g., team: ["platform", "security"]). The in operator works for both - it checks membership naturally whether the value is a string or an element in an array.
Building Helper Modules
Helper modules provide reusable abstractions that simplify permission policies. The user.rego file demonstrates this pattern.
The user.rego Pattern
The user package defines convenience rules that other policies import and use:
Importing Helper Modules
Other policy packages import the user module to leverage its rules:
Benefits of Helper Modules
- Abstraction: Permission rules read naturally -
user.can_see_clusteris clearer than inline conditions - Reusability: Define matching logic once, use it in multiple permission rules
- Maintainability: Update region mappings in one place when adding new regions
- Layered Security: Build complex permissions from well-tested building blocks
Creating Custom Helper Modules
You can create additional helper modules for your organization's needs:
Then import and use in permissions:
| Rego | |
|---|---|
Access Control Patterns
SPOG supports multiple access control patterns that can be combined to match your organizational requirements.
Role-Based Access Control (RBAC)
RBAC assigns permissions based on roles. Users receive roles, and roles determine what actions are permitted.
Role Hierarchy Example:
Using Role Hierarchy in Permissions:
Pattern 1: Role-Based Dashboard Access
Attribute-Based Access Control (ABAC)
ABAC makes decisions based on arbitrary attributes of the user, resource, and environment - not just role membership. This enables fine-grained control using any claim from your identity provider or static user configuration.
Recall that static user fields are arbitrary - you can define any attributes your policies need. ABAC leverages this flexibility.
Pattern 2: Department and Cost Center Access
Static user configuration for department-based access:
| YAML | |
|---|---|
Pattern 3: Allowed Regions List
Instead of using roles for regions, store allowed regions directly as a user attribute:
Static user with explicit region list:
Pattern 4: Clearance Level Hierarchy
Pattern 5: Multi-Tenant Customer Isolation
Pattern 6: Team Membership Attribute
| Rego | |
|---|---|
Static user with team membership:
| YAML | |
|---|---|
ABAC vs RBAC
The key difference: RBAC checks "some-role" in input.user.roles, while ABAC uses arbitrary attributes like input.user.department, input.user.allowed_regions, or input.user.clearance_level. ABAC is more flexible but requires your identity provider to include these attributes in tokens.
Scopes and Permission Boundaries
Scopes define permission boundaries, often used with OIDC tokens to limit what an authenticated session can do.
Pattern 6: Scope-Based Permissions
Using Scopes in Permissions:
| Rego | |
|---|---|
Dynamic Scopes
Dynamic scopes encode variable information in the scope string itself, allowing policies to extract and match against cluster attributes.
Pattern 7: Region-Scoped Access
Pattern 8: Customer-Scoped Access
Pattern 9: Environment-Scoped Write Access
Dynamic Permission Arguments
Dynamic permission arguments allow dashboard and navigation configurations to pass runtime values to REGO policies. This enables truly dynamic access control without per-resource REGO rules.
How It Works:
- Dashboard configuration specifies
requireswith arguments - UI extracts route parameters and passes them to the policy engine
- REGO receives values in
input.argumentsobject - Policy evaluates using these dynamic values
Configuration Format:
| YAML | |
|---|---|
Argument Value Syntax:
| Syntax | Description | Example |
|---|---|---|
:param |
Extract from URL route parameter | :team from /team/:team |
{{ param }} |
Template expression | {{ team }}_suffix |
"value" |
Static string | "production" |
REGO Policy Pattern:
Input Structure:
When arguments are provided, the policy engine receives:
| JSON | |
|---|---|
Benefits:
- One rule for all teams:
see_team_dashboardworks for any team value - No per-resource REGO: Add new teams without policy changes
- Runtime flexibility: Access control adapts to URL parameters
- Reduced maintenance: Fewer rules to manage
When to Use Dynamic Arguments
Use dynamic arguments when you have parameterized routes (like /team/:team) and want access control to follow the parameter. For static routes, use simple string format in requires.
Writing REGO Policies
This section provides complete, ready-to-use policy examples covering common authorization scenarios. Each pattern is self-contained and can be adapted for your organization.
Pattern 10: Complete Regional Operations Policy
Pattern 11: Tiered Service Access
Permission Inheritance Design
This pattern implements implicit permission inheritance - users with higher-tier access automatically get lower-tier access. Design these hierarchies carefully to ensure this behavior matches your security requirements. Always document which permissions imply which other permissions.
Testing & Debugging Policies
SPOG provides two debug interfaces for developing and testing authorization policies.
Using the User Debug Interface
The User Debug interface at /debug/user displays your JWT claims and generates REGO code snippets based on your actual token structure.
Navigating to User Debug:
- Log into the Glass UI
- Navigate to
/debug/user(or access via Dev Mode menu)
Understanding the Interface:
The page displays three sections:
1. User Profile Section
Shows basic identity information: - Name: Display name from claims - Email: Email address (if available) - Type: Token type (User Token or Machine Token)
2. JWT Claims Section
Displays the complete claims object as an expandable JSON tree. This is exactly what your REGO policies see via input.user. Common fields include:
| Text Only | |
|---|---|
3. REGO Policy Examples Section
The interface analyzes your claims structure and generates relevant REGO code snippets. These snippets demonstrate proper syntax using your actual field names and values.
Understanding the Generated Code Snippets:
The User Debug page generates six types of examples based on your claims:
Snippet 1: Check if array contains a value
Use this pattern when checking if a user has a specific role, group, or other array membership.
Snippet 2: Iterate over array with some
| Rego | |
|---|---|
Use this pattern when you need to find any match between user attributes and cluster labels.
Snippet 3: Match string exactly
Use this pattern for exact string comparisons on scalar claims.
Snippet 4: Check value in allowed list
Use this pattern to verify a claim value is within an acceptable set.
Snippet 5: Combine multiple conditions
| Rego | |
|---|---|
Use this pattern to create rules with AND logic - all conditions must be true.
Snippet 6: Define reusable rules
| Rego | |
|---|---|
Use this pattern to create helper rules in user.rego that simplify permission definitions.
Copy and Adapt
Click the "Copy" button on any example to copy it to your clipboard. These snippets use your actual claim field names, so they're ready to adapt for your policies.
Using the Policy Debug Interface
The Policy Debug interface at /debug/policy provides an interactive environment for testing REGO policies against simulated inputs.
Navigating to Policy Debug:
- Log into the Glass UI
- Navigate to
/debug/policy(or access via Dev Mode menu)
Interface Components:
1. File Selector (Top Bar)
A dropdown showing available policy files organized into two groups: - User Policies: Custom policies mounted from ConfigMaps - Default Policies: Built-in policies from the service image
Select a file to view and edit its contents. Modified files show a "modified" badge.
2. Query Input
Enter REGO query expressions to evaluate:
| Text Only | |
|---|---|
Press Enter or click "Evaluate" to run the query.
3. Input Data Editor
A JSON/YAML editor for defining the policy input. This simulates the context that would be assembled for a real authorization check:
4. Policy File Editor
View and modify policy file contents. Changes are temporary (session-only) and don't affect the actual deployed policies. This allows safe experimentation.
5. Results Panel
Displays: - Result: The evaluation result (true, false, or a value) - Trace: Step-by-step evaluation trace for debugging
Step-by-Step Debugging Workflow:
- Select a policy file to understand its structure
- Set up input data that simulates your test scenario
- Enter a query for the rule you want to test
- Click Evaluate to see the result
- Review the trace if the result is unexpected
- Modify policies or input and re-evaluate to iterate
Example Debugging Session:
Testing why a user cannot see a cluster:
| Text Only | |
|---|---|
The result is false. Test the component rules:
| Text Only | |
|---|---|
The user lacks the authoritative role. Add it to the input and re-evaluate:
| JSON | |
|---|---|
Changes Are Session-Only
Policy modifications in the debug interface are temporary. To deploy policy changes, update the Helm configuration and redeploy.
Best Practices
Follow these practices to create secure, maintainable authorization policies.
Security Best Practices
Principle of Least Privilege
Grant the minimum permissions required for each role. Start with deny-all and explicitly allow specific actions:
| Rego | |
|---|---|
Explicit Admin Checks
Always verify admin status explicitly rather than assuming admin access:
| Rego | |
|---|---|
Separate Production Permissions
Require explicit production access rather than granting it implicitly:
| Rego | |
|---|---|
Audit-Friendly Rules
Write rules that produce clear audit trails:
| Rego | |
|---|---|
Maintainability Best Practices
Organize Helper Rules in user.rego
Keep all user-related helper logic in a single file:
| Rego | |
|---|---|
Use Descriptive Rule Names
Choose names that clearly indicate what the rule checks:
| Rego | |
|---|---|
Document Complex Logic
Add comments explaining non-obvious policy decisions:
Version Your Policies
Include version comments and change history:
| Rego | |
|---|---|
Quick Reference
REGO Syntax Summary
| Pattern | Syntax | Meaning |
|---|---|---|
| OR (multiple rules) | rule if {...} rule if {...} |
Any rule true = result true |
| AND (multiple conditions) | rule if { cond1; cond2 } |
All conditions must be true |
| Array membership | "value" in array |
Check if value is in array |
| Iteration | some x in array |
Iterate over array elements |
| String prefix | startswith(s, "prefix:") |
Check string starts with prefix |
| String extraction | trim_prefix(s, "prefix:") |
Remove prefix from string |
| Negation | not condition |
True if condition is false |
| Assignment | x := expression |
Assign value to variable |
| Comparison | ==, !=, <, > |
Compare values |
Input Structure Reference
Well-Known Permissions
These are the permissions defined in pdns_permissions.rego that SPOG evaluates:
| Permission | Helper Rule Used | Use Case |
|---|---|---|
connect |
Always true | Basic connection |
read |
user.can_see_cluster |
View cluster in UI |
read_logs |
user.can_observe_cluster |
View container logs |
clear_cache |
user.can_manage_dns_content |
Clear DNS cache |
restart_instance_set |
user.can_manage_instances |
Restart pods |
delete_pod |
user.can_manage_instances |
Delete individual pods |
dns_check |
user.can_observe_cluster |
Execute DNS queries |
The helper rules (can_see_cluster, can_observe_cluster, etc.) are defined in user.rego and can be customized.
What's Next?
Now that you understand SPOG's authorization system, explore these related topics:
Related Concepts
- Labels & Filter Queries: Deep dive into cluster labels used in authorization
- Dashboards and Playlists: Configure permission-gated dashboards
- Navigation Configuration: Set up role-based navigation menus
Integration Guides
- Team-Based Deployment: Complete example showing labels, dashboards, navigation, and REGO integration
- Multi-Region Operations: Advanced example with regional access control
External Resources
- OPA Documentation: Open Policy Agent project documentation
- REGO Language Reference: Complete REGO syntax and built-in functions
Summary
SPOG's authentication and authorization system provides flexible, policy-driven access control through:
- Multiple Authentication Methods: Static users for development, OIDC for production
- JWT-Based Identity: Tokens carry user attributes that become policy input
- REGO Policy Engine: Declarative policies express complex authorization logic
- Helper Module Pattern: Reusable rules in
user.regosimplify permission definitions - Multiple Access Patterns: RBAC, ABAC, and scope-based access control
- Debug Interfaces: Interactive tools for policy development and testing
The key REGO concepts to remember:
- Multiple rules with the same name implement OR logic - any rule being true makes the result true
- Multiple conditions within a rule implement AND logic - all conditions must be true
- Use
into check array membership andsometo iterate over arrays - Build helper modules to create reusable abstractions that simplify permission rules
Use the debug interfaces at /debug/user and /debug/policy to develop and test policies before deployment.