PolicyFlow

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:

  1. Type Matching: Check if incoming types match the policy's declared types
  2. Where Evaluation: Evaluate where conditions using the actual data
  3. 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"
    }
}

Next Steps