PDP (Policy Decision Point)

Authorization Security Notes Jan 6, 2025 JAVASCRIPT

Definition

Every time you try to access something protected - a file, an API endpoint, a database record - someone needs to answer the question: “Should this user be allowed to do this action on this resource right now?” That decision-maker is the Policy Decision Point (PDP). It’s the brain of your authorization system, the component that actually thinks through the rules and says yes or no.

The PDP receives all the context it needs to make a decision: who’s asking (the user and their attributes like role, department, clearance level), what they’re trying to do (read, write, delete), what they’re trying to access (and its attributes like sensitivity, owner, classification), and environmental factors (time of day, IP address, device type). It then consults policies - the rules your organization has defined - and determines whether this specific request should be allowed.

What makes PDPs powerful is that they centralize authorization logic. Instead of scattering permission checks throughout your code (“if user.role == ‘admin’…” in a hundred places), you have one component that embodies all your access rules. This means changing a policy updates enforcement everywhere instantly. It also means your policies can be audited, tested, and evolved independently from your application code. Modern PDPs support sophisticated rules like “engineers can read documents from their own team during business hours” or “medical staff can access patient records only when they have an active care relationship.”

Example

PDPs are the invisible decision-makers behind sophisticated access control:

Healthcare Systems: When a doctor tries to view a patient’s records, the PDP evaluates: Is this doctor currently treating this patient? Is the doctor in the appropriate department? Is it during normal care hours or an emergency? Are there any patient-requested restrictions? The PDP might allow access to recent test results but block access to psychiatric notes based on different policy rules.

Financial Trading Platforms: A trader tries to execute a large order. The PDP checks: Is this within the trader’s trading limits? Is this security on their approved list? Has the compliance team flagged any issues? Is the market currently open? What’s their risk exposure? A single decision involves dozens of policy rules evaluated in milliseconds.

Cloud Infrastructure (AWS IAM): When your code tries to read from an S3 bucket, AWS’s PDP evaluates your IAM policies, resource policies, organization policies, and session policies. It decides whether your specific principal, performing this specific action, on this specific resource, with these specific conditions, should be allowed.

Document Management Systems: An employee tries to download a document. The PDP checks: Does their role allow downloads? Is the document classified appropriately for their clearance? Are they on a managed device? Is their session recent or should they re-authenticate? Are there any legal holds preventing download?

API Gateways: Every API call hits the gateway’s PDP: Is this API key valid for this endpoint? Has the caller exceeded their rate limit? Does their subscription tier include this feature? Are they calling from an allowed IP range?

Analogy

The Judge in a Courtroom: When a case comes to court, the judge (PDP) doesn’t make arrests or guard the exits - that’s law enforcement (PEP). The judge examines the evidence (attributes), consults the law (policies), considers precedent, and renders a verdict (permit/deny). The judge’s role is purely decision-making, based on rules they didn’t create but are expert at interpreting.

The Air Traffic Controller: When a plane requests to land, the controller (PDP) doesn’t physically guide the plane - they make decisions. They consider: runway availability, weather, other aircraft, fuel situations, emergencies. Based on all these factors and FAA rules, they decide: cleared to land, hold pattern, or divert. The decision is separate from enforcement.

The Loan Officer: When you apply for a mortgage, the loan officer (PDP) gathers your information (income, credit score, debt ratio, employment history) and evaluates it against the bank’s lending policies. They don’t write the policies or disburse the funds - they make the approval decision based on rules and data.

The Immigration Officer with the Rulebook: At border control, the officer consults regulations about visa requirements, travel restrictions, and entry conditions. They examine your passport, visa, purpose, duration, and destination, then make a decision based on immigration law. The computer systems helping them are essentially PDPs - gathering attributes and checking policies.

Code Example


// Policy Decision Point implementation
class PolicyDecisionPoint {
  constructor(policyStore) {
    this.policyStore = policyStore
    this.cache = new Map()
  }

  // Main authorization decision method
  async authorize(request) {
    const { subject, resource, action, environment } = request

    // Check cache first (with TTL)
    const cacheKey = this.getCacheKey(request)
    const cached = this.cache.get(cacheKey)
    if (cached && cached.expiry > Date.now()) {
      return cached.decision
    }

    try {
      // Retrieve applicable policies
      const policies = await this.policyStore.getPolicies({
        resourceType: resource.type,
        action: action
      })

      // Evaluate policies in order of priority
      const decision = this.evaluatePolicies(
        policies,
        subject,
        resource,
        action,
        environment
      )

      // Cache decision (short TTL for security)
      this.cache.set(cacheKey, {
        decision,
        expiry: Date.now() + 60000 // 1 minute
      })

      // Audit the decision
      await this.auditDecision(request, decision)

      return decision
    } catch (error) {
      // Fail closed - deny on error
      return {
        decision: 'DENY',
        reason: 'Policy evaluation error',
        error: error.message
      }
    }
  }

  evaluatePolicies(policies, subject, resource, action, environment) {
    let finalDecision = 'DENY' // Default deny
    let obligations = []
    let advice = []

    for (const policy of policies) {
      const result = this.evaluatePolicy(
        policy,
        subject,
        resource,
        action,
        environment
      )

      if (result.effect === 'DENY') {
        // Any deny = final deny (deny overrides)
        return {
          decision: 'DENY',
          reason: result.reason,
          matchedPolicy: policy.id
        }
      }

      if (result.effect === 'PERMIT') {
        finalDecision = 'PERMIT'
        obligations = obligations.concat(result.obligations || [])
        advice = advice.concat(result.advice || [])
      }
    }

    return {
      decision: finalDecision,
      obligations, // Must be enforced (e.g., log access)
      advice,      // Should be enforced (e.g., notify owner)
      evaluatedPolicies: policies.map(p => p.id)
    }
  }

  evaluatePolicy(policy, subject, resource, action, environment) {
    // Check if policy target matches
    if (!this.matchesTarget(policy.target, subject, resource, action)) {
      return { effect: 'NOT_APPLICABLE' }
    }

    // Evaluate condition
    const conditionMet = this.evaluateCondition(
      policy.condition,
      subject,
      resource,
      action,
      environment
    )

    if (!conditionMet) {
      return { effect: 'NOT_APPLICABLE' }
    }

    return {
      effect: policy.effect, // PERMIT or DENY
      reason: policy.description,
      obligations: policy.obligations,
      advice: policy.advice
    }
  }

  evaluateCondition(condition, subject, resource, action, environment) {
    // Example: evaluate complex condition expression
    // "subject.department === resource.department AND environment.time.hour < 17"

    try {
      // Use safe expression evaluator (NOT eval()!)
      const context = {
        subject: subject.attributes,
        resource: resource.attributes,
        action: action,
        environment: environment
      }

      return this.safeEvaluate(condition, context)
    } catch (error) {
      // Fail closed on evaluation error
      return false
    }
  }

  getCacheKey(request) {
    return JSON.stringify({
      subject: request.subject.id,
      resource: request.resource.id,
      action: request.action
    })
  }

  async auditDecision(request, decision) {
    await auditLog.log({
      timestamp: new Date().toISOString(),
      type: 'authorization_decision',
      subject: request.subject.id,
      resource: request.resource.id,
      action: request.action,
      decision: decision.decision,
      reason: decision.reason,
      policies: decision.evaluatedPolicies
    })
  }
}

// Using PDP in API
const pdp = new PolicyDecisionPoint(policyStore)

app.delete('/api/documents/:id', async (req, res) => {
  const decision = await pdp.authorize({
    subject: {
      id: req.user.id,
      attributes: req.user
    },
    resource: {
      id: req.params.id,
      type: 'document',
      attributes: await getDocumentAttributes(req.params.id)
    },
    action: 'DELETE',
    environment: {
      time: new Date(),
      ipAddress: req.ip,
      userAgent: req.headers['user-agent']
    }
  })

  if (decision.decision === 'DENY') {
    return res.status(403).json({
      error: 'Forbidden',
      reason: decision.reason
    })
  }

  // Enforce obligations
  for (const obligation of decision.obligations || []) {
    await enforceObligation(obligation)
  }

  // Proceed with delete
  await deleteDocument(req.params.id)
  res.status(204).send()
})

Security Notes

SECURITY NOTES

CRITICAL: Policy Decision Point (PDP) evaluates authorization policies. Core of fine-grained authorization.

PDP Responsibilities:

  • Policy evaluation: Evaluates if request allowed
  • Context gathering: Collects context for evaluation
  • Rule matching: Matches request against rules
  • Decision: Allows/denies request

Integration:

  • PEP calls PDP: Policy Enforcement Point requests decision
  • Policy storage: PDPs access policy repository
  • Caching: Cache decisions for performance
  • Monitoring: Monitor policy decisions

Best Practices:

  • Centralize PDPs: Single decision point for consistency
  • Performance: Implement caching to improve performance
  • Audit: Log all policy decisions
  • Versioning: Version policies for rollback
  • Testing: Comprehensive policy testing

Standards & RFCs

Standards & RFCs
3)- Open Policy Agent (OPA) - Modern policy engine specification