Schema Targeting and Selection
How policies target specific types and conditions
Schema targeting is one of PolicyFlow's most powerful features, allowing policies to precisely define when they should be evaluated. This enables efficient policy execution and clear separation of concerns.
Basic Schema Declaration
The schemas
block defines which types a policy operates on:
policy UserDataAccess {
schemas {
User from Auth.CorporateUser
Resource from Data.UserProfile
Context from Web.RequestContext
}
// This policy only runs when:
// - The user is a CorporateUser
// - The resource is a UserProfile
// - The context is a WebRequest
}
Conditional Schemas
Add where
clauses to create more specific conditions:
policy EuropeanDataProtection {
schemas {
// Only EU users
User from Auth.User where user.region == "EU"
// Only resources containing PII
Resource from Data.Document where resource.containsPII == true
// Only production environment
Context from Web.Context where context.environment == "production"
}
// This policy only runs for EU users accessing PII in production
}
Schema Filtering Patterns
Pattern 1: Environment-Specific Policies
Create policies that only apply in certain environments:
policy ProductionSafeguards {
schemas {
Context from Web.RequestContext where context.environment == "production"
}
rules {
rule NoDeletionsInProd {
when action == "delete" AND !("prod-admin" in user.roles)
then DENY
priority: 1000
reason: "Deletions require prod-admin role in production"
}
}
}
Pattern 2: User Attribute Filtering
Target specific user groups:
policy ContractorLimitations {
schemas {
User from Auth.User where user.employmentType == "contractor"
Resource from Data.InternalResource
}
rules {
rule LimitContractorAccess {
when resource.classification == "Confidential"
then DENY
reason: "Contractors cannot access confidential resources"
}
}
}
Pattern 3: Resource-Based Filtering
Apply policies only to specific resource types:
policy FinancialDocumentControl {
schemas {
Resource from Docs.Document where resource.category == "financial"
}
rules {
rule RequireFinanceRole {
when !("finance" in user.roles) AND action != "read"
then DENY
reason: "Only finance team can modify financial documents"
}
}
}
Universal vs Partial Policies
Universal Policies
Policies without a schemas
block run for ALL requests. They are ideal for enforcing global security checks that must apply everywhere.
policy GlobalDenyChecks {
// No schemas block - runs for every request
rules {
rule RequireAuthentication {
when user == null
then DENY
priority: 0 // Evaluate first
reason: "Authentication required"
}
rule SystemMaintenance {
when env["MAINTENANCE_MODE"] == "true"
then DENY
priority: 0 // Evaluate first
reason: "System under maintenance"
}
}
}
If no rules in a universal policy match, it abstains from making a decision, allowing other, more specific policies to grant access. However, if any of its DENY
rules match, the request is immediately denied.
Partial Policies
Specify constraints on only some types:
policy HighValueTransactions {
schemas {
// Only constraint is on the resource
Resource from Finance.Transaction where resource.amount > 10000
}
// Applies to ANY user and ANY context accessing high-value transactions
rules {
rule RequireApproval {
when !resource.isApproved
then DENY
reason: "High-value transactions require approval"
}
}
}
Advanced Filtering Techniques
Complex Where Conditions
Use complex boolean logic in where clauses:
policy AdvancedFiltering {
schemas {
User from Auth.User where
user.isActive AND
user.emailVerified AND
(user.department in ["Engineering", "IT"] OR "admin" in user.roles)
Resource from Data.Resource where
resource.createdAt > DateTime.Parse("2024-01-01") AND
resource.size < 1000000 AND
resource.owner != null
}
}
Using Functions in Where Clauses
Reference properties and use methods:
policy TimeBasedAccess {
schemas {
// Only old documents
Resource from Docs.Document where
(DateTime.Now() - resource.createdAt).TotalDays() > 365
// Only users from company domain
User from Auth.User where
user.email.EndsWith("@company.com")
}
}
Relationship-Based Filtering
Filter based on relationships:
policy TeamAccess {
schemas {
User from Auth.User
Resource from Project.Task where
Relationships.Has(user, "member_of", resource.project)
}
// Policy only applies when user is a member of the task's project
}
Note: When evaluating where
clauses for policy selection, all input principals (user
, resource
, context
) are available for use in the conditions of any other principal. This means the user
and resource
are already bound and accessible when evaluating the relationship check in the example above.
Performance Considerations
Index Your Where Clause Attributes
Attributes used in where clauses should be indexed for performance:
// These attributes should be indexed in your data store:
// - user.isActive
// - user.region
// - resource.classification
// - context.environment
policy OptimizedPolicy {
schemas {
User from Auth.User where user.isActive == true
Resource from Data.Resource where resource.classification == "Public"
Context from Web.Context where context.environment == "production"
}
}
Keep Where Clauses Simple
Complex computations in where clauses can impact performance:
// Good: Simple attribute checks
schemas {
User from Auth.User where user.accountType == "premium"
}
// Avoid: Complex computations
schemas {
User from Auth.User where
user.transactions.Filter(t => t.amount > 1000).Count() > 10
}
Schema Selection Strategy
The PolicyFlow engine uses this strategy to select policies:
- Type Matching: Check if incoming types match the policy's declared types
- Where Evaluation: Evaluate where conditions using the actual data
- Partial Matching: For partial policies, only check specified constraints
Selection Example
Given these policies:
// Policy A: Specific
policy A {
schemas {
User from Auth.Employee where user.department == "Finance"
Resource from Docs.FinancialReport
}
}
// Policy B: General
policy B {
schemas {
User from Auth.Employee
Resource from Docs.Document
}
}
// Policy C: Universal
policy C {
// No schemas - runs always
}
For a request with:
- User: Employee in Finance department
- Resource: FinancialReport
The engine would run: Policy A (exact match), Policy B (FinancialReport is a Document), and Policy C (universal).
Best Practices
1. Be Specific
More specific schemas improve performance and clarity:
// Good: Specific targeting
schemas {
User from Auth.AdminUser where user.isActive
Resource from Sensitive.CustomerData
Context from Web.InternalRequest
}
// Too broad
schemas {
User from Auth.User
Resource from Data.Resource
}
2. Use Meaningful Conditions
Where clauses should express business requirements:
// Clear business rule
schemas {
User from Auth.User where user.accountStatus == "suspended"
}
// Unclear magic number
schemas {
User from Auth.User where user.statusCode == 3
}
3. Consider Policy Composition
Use multiple focused policies instead of one complex policy:
// Good: Separate policies for different concerns
policy GDPRCompliance {
schemas {
User from Auth.User where user.region == "EU"
}
}
policy SOXCompliance {
schemas {
Resource from Finance.FinancialRecord
}
}
// Avoid: One policy trying to do everything
policy EverythingPolicy {
schemas {
User from Auth.User where user.region == "EU" OR user.department == "Finance"
Resource from Data.Resource where resource.type == "financial" OR resource.containsPII
}
}
Schema Definition Examples
Here's how to define schemas with the new syntax:
schema UserSchema {
User type StandardUser {
id: UUID
email: Email
isActive: Boolean = true
createdAt: DateTime = DateTime.Now()
clearanceLevel: Number range(0..10) = 0
}
User type AdminUser : StandardUser {
adminSince: DateTime
superAdmin: Boolean = false
}
}
schema ResourceSchema {
Resource type Document {
id: UUID
title: String
ownerId: UUID
classification: String = "Internal"
size: Number range(0..10485760) // Max 10MB
}
Resource type SensitiveDocument : Document {
encryptionRequired: Boolean = true
auditLevel: String = "Enhanced"
}
}