Policies Overview
Understanding the structure and components of PolicyFlow policies
Policies are the core logic components of PolicyFlow. They define the authorization rules that determine whether a user can perform an action on a resource in a given context.
Policy Declaration
A policy begins with a declaration that includes its unique name and optional metadata:
/**
* This policy controls access to financial reports.
* It is a critical component of our SOX compliance.
*/
policy ReportingAccess {
// A human-readable summary of the policy's purpose
description: "Controls read and export access for quarterly financial reports."
// Version tag for tracking changes (does not affect evaluation)
version: "1.2.0"
// Tags for organization, filtering, and discovery
tags: ["finance", "sox", "reporting", "pii"]
// ... policy body ...
}
Policy Structure
A complete policy consists of several key sections:
1. Imports
Import the schemas and types your policy will use:
import * as Auth from "@/schemas/auth.pfs:AuthSchema";
import * as Finance from "@/schemas/finance.pfs:FinanceSchema";
2. Metadata (Optional)
Provide information about the policy. Note that the version
field is purely informational and does not affect policy evaluation or deployment:
policy FinancialReporting {
description: "Manages access to financial reports"
version: "2.1.0" // This is just a tag for human reference
tags: ["finance", "compliance"]
// ... rest of policy ...
}
The version field can be used to:
- Track changes in your version control system
- Document policy evolution
- Help with change management processes
- Provide context in audit logs
However, PolicyFlow does not use the version for:
- Policy selection or routing
- Backward compatibility
- Version-based evaluation
3. Action Filtering (Optional)
Specify which actions this policy handles:
policy DocumentPolicy {
actions: [
"documents:read",
"documents:write",
"documents:delete",
"documents:**" // All document actions
]
// ... rest of policy ...
}
4. Schema Declaration
Define the types of User, Resource, and Context this policy applies to:
policy DocumentAccess {
schemas {
User from Auth.CorporateUser
Resource from Docs.Document
Context from Web.RequestContext
}
// ... rest of policy ...
}
5. Rules
The heart of the policy - the authorization logic:
policy DocumentAccess {
rules {
rule OwnerAccess {
when user.id == resource.ownerId
then ALLOW
reason: "Document owner has full access"
}
rule AdminOverride {
when "admin" in user.roles
then ALLOW
priority: 1000
reason: "Administrators have full access"
}
}
}
6. Default Decision
Policies in PolicyFlow are secure by default. If a request is evaluated against a policy and none of its rules produce an ALLOW
decision, the policy's outcome is implicitly DENY
. You do not need to specify a default decision; the system is designed to "fail closed" for maximum security.
Complete Policy Example
Here's a comprehensive example that demonstrates all the concepts:
import * as Auth from "@/schemas/auth.pfs:AuthSchema";
import * as Docs from "@/schemas/documents.pfs:DocumentSchema";
import * as Context from "@/schemas/context.pfs:ContextSchema";
/**
* Controls access to corporate documents based on classification,
* ownership, and user attributes.
*/
policy CorporateDocumentAccess {
description: "Comprehensive document access control policy"
version: "3.0.0" // Just a tag - doesn't affect evaluation
tags: ["documents", "security", "compliance"]
// Only handle document-related actions
actions: ["documents:**"]
// Define the types this policy works with
schemas {
User from Auth.CorporateUser where user.isActive
Resource from Docs.CorporateDocument
Context from Context.WebRequest
}
// Helper function for checking business hours
function isBusinessHours(): Boolean {
const hour = DateTime.Now().ToUnit(TimeUnit.Hours);
const day = DateTime.Now().DayOfWeek(); // 0=Sun, 6=Sat
return day >= 1 AND day <= 5 AND hour >= 9 AND hour < 17;
}
rules {
// Rule 1: Deny access outside of business hours for non-admins
rule DenyOutsideBusinessHours {
when !isBusinessHours() AND !("admin" in user.roles)
then DENY
priority: 1000
reason: "Access is restricted to business hours (9am-5pm, Mon-Fri)"
}
// Rule 2: Owners always have access
rule OwnerAccess {
when user.id == resource.ownerId
then ALLOW
reason: "Resource owner"
}
// Rule 3: Admins have full access
rule AdministratorAccess {
when "admin" in user.roles
then ALLOW
priority: 1000
reason: "Administrator privilege"
}
// Rule 4: Department members can access internal docs
rule DepartmentAccess {
when user.department == resource.department
AND resource.classification == "Internal"
AND action in ["documents:read", "documents:write"]
then ALLOW
reason: "Same department access"
}
// Rule 5: Everyone can read public documents
rule PublicReadAccess {
when resource.classification == "Public"
AND action == "documents:read"
then ALLOW
reason: "Public document read access"
}
// Rule 6: Deny access to archived documents
rule DenyArchivedAccess {
when resource.status == "Archived"
AND !("archivist" in user.roles)
then DENY
priority: 1000
reason: "Document is archived"
}
// Rule 7: Require strong authentication for confidential docs
rule ConfidentialRequiresMFA {
when resource.classification == "Confidential"
AND context.authMethod != "mfa"
then DENY
priority: 2000
reason: "Confidential documents require MFA"
}
}
}
Key Concepts
Rule Evaluation Order
Rules are evaluated in order, but the final decision depends on priorities and conflict resolution:
- Rules are evaluated sequentially
- Each rule produces
ALLOW
,DENY
, or no decision (if condition is false) - Priorities (0-10000, lower numbers first) affect evaluation order
- The engine uses "Deny-Overrides" strategy
Conditional Schemas
You can add conditions to schema declarations:
schemas {
// Only active users from EU
User from Auth.User where user.isActive AND user.region == "EU"
// Only production databases
Resource from DB.Database where resource.environment == "prod"
// Only requests from internal network
Context from Web.Request where context.ipAddress.Matches("10.0.0.0/8")
}
Policy Functions
Define reusable logic within a policy:
policy DataAccess {
function hasValidSubscription(user: User): Boolean {
return user.subscriptionStatus == "active"
AND user.subscriptionExpiry > DateTime.Now();
}
function isHighRiskAction(action: String): Boolean {
return action in ["delete", "export", "share-external"];
}
rules {
rule RequireSubscription {
when !hasValidSubscription(user) AND isHighRiskAction(action)
then DENY
}
}
}