Skip to main content

PolicyParser

A class that wraps a valid Policy object to provide convenient query methods.

PolicyParser provides a high-level interface for querying and analyzing policies. It validates policies upon instantiation and offers methods to check permissions, retrieve rules, constraints, and duties. This class simplifies policy evaluation for applications that need to make access control decisions.

Common Usage Patterns

Examples

// 1. Basic Permission Checking
const policy: Policy = {
policyURI: 'ipfs://QmPolicy',
profile: 'PD-REL-v1.0',
rules: [
{ action: 'USE', effect: 'permit' },
{ action: 'COMMERCIALIZE', effect: 'prohibit' }
]
};

const parser = new PolicyParser(policy);
console.log(parser.isPermitted('USE')); // true
console.log(parser.isProhibited('COMMERCIALIZE')); // true

// 2. Evaluating Token-Gated Access
const nftPolicy: Policy = {
policyURI: 'ipfs://QmNFTPolicy',
profile: 'PD-REL-v1.0',
rules: [{
action: 'MODIFY',
effect: 'permit',
constraints: [{
type: 'TOKEN_GATED',
value: { chainId: 1, contractAddress: '0xNFT...', minBalance: 1 }
}]
}]
};

const nftParser = new PolicyParser(nftPolicy);
const constraints = nftParser.getConstraintsForAction('MODIFY');

// Check if user meets token requirements
async function canUserModify(userAddress: string): Promise<boolean> {
if (!nftParser.isPermitted('MODIFY')) return false;

for (const constraint of constraints) {
if (constraint.type === 'TOKEN_GATED') {
const balance = await checkTokenBalance(userAddress, constraint.value);
if (balance < constraint.value.minBalance) return false;
}
}
return true;
}

// 3. Processing Payment Duties
const paymentPolicy: Policy = {
policyURI: 'ar://PaymentPolicy',
profile: 'PD-REL-v1.0',
rules: [{
action: 'USE',
effect: 'permit',
duties: [{
type: 'PAYMENT',
value: { amount: '10.00', currency: 'USDC', recipient: '0xCreator' }
}]
}]
};

const paymentParser = new PolicyParser(paymentPolicy);
const useDuties = paymentParser.getDutiesForAction('USE');

// Process required payments
async function processAccess(): Promise<void> {
for (const duty of useDuties) {
if (duty.type === 'PAYMENT') {
await processPayment(duty.value);
}
}
}

// 4. Complex Multi-Rule Analysis (OR Logic)
const complexPolicy: Policy = {
policyURI: 'ipfs://QmComplex',
profile: 'PD-REL-v1.0',
logic: 'OR', // User can satisfy ANY rule
rules: [
{
action: 'TRANSFER',
effect: 'permit',
description: 'Free for NFT holders',
constraints: [{
type: 'TOKEN_GATED',
value: { chainId: 1, contractAddress: '0xNFT', minBalance: 1 }
}]
},
{
action: 'TRANSFER',
effect: 'permit',
description: 'Or pay a fee',
duties: [{
type: 'PAYMENT',
value: { amount: '50.00', currency: 'USD', recipient: '0xTreasury' }
}]
}
]
};

const complexParser = new PolicyParser(complexPolicy);
const transferRules = complexParser.getRulesForAction('TRANSFER');

// Present options to user
for (const rule of transferRules) {
console.log(`Option: ${rule.description}`);
if (rule.constraints) {
console.log('Requirements:', rule.constraints);
}
if (rule.duties) {
console.log('Obligations:', rule.duties);
}
}

// 5. Time-Based Access Control
const timedPolicy: Policy = {
policyURI: 'ipfs://QmTimed',
profile: 'PD-REL-v1.0',
rules: [{
action: 'DISPLAY',
effect: 'permit',
constraints: [{
type: 'TIME_PERIOD',
value: { start: 1704067200, end: 1735689599 } // 2024 only
}]
}]
};

const timedParser = new PolicyParser(timedPolicy);
const displayConstraints = timedParser.getConstraintsForAction('DISPLAY');

// Check if content is currently accessible
function isContentAvailable(): boolean {
const now = Math.floor(Date.now() / 1000);
for (const constraint of displayConstraints) {
if (constraint.type === 'TIME_PERIOD') {
if (now < constraint.value.start || now > constraint.value.end) {
return false;
}
}
}
return timedParser.isPermitted('DISPLAY');
}

// Error handling for invalid policies
try {
const invalidPolicy = { policyURI: '', profile: 'wrong', rules: [] };
new PolicyParser(invalidPolicy as Policy); // Throws validation error
} catch (error) {
console.error('Policy validation failed:', error.message);
}

Definition

class PolicyParser {
constructor(policy: Policy);
isPermitted(action: string): boolean;
isProhibited(action: string): boolean;
getRulesForAction(action: string): Rule[];
getDutiesForAction(action: string): Duty[];
getConstraintsForAction(action: string): Constraint[];
}

Constructor

Creates a new PolicyParser instance and validates the policy.

The constructor performs structural validation to ensure the policy conforms to the PD-REL specification. If validation fails, an error is thrown with details about the validation issues.

Parameters:

  • policy (Policy) - A PD-REL policy object to parse and query

Examples

// Valid policy
const validPolicy: Policy = {
policyURI: 'ipfs://QmValid',
profile: 'PD-REL-v1.0',
rules: [{ action: 'USE', effect: 'permit' }]
};
const parser = new PolicyParser(validPolicy); // Success

// Invalid policy - missing required fields
const invalidPolicy = {
policyURI: '',
profile: 'PD-REL-v1.0',
rules: []
};
try {
new PolicyParser(invalidPolicy as Policy);
} catch (error) {
console.error(error.message);
// "Invalid policy: policyURI must be a non-empty string., rules must be a non-empty array."
}

Methods

isPermitted

Checks if a specific action is permitted by the policy rules.

This method provides a simplified permission check that looks for the presence of permit rules and absence of prohibit rules. It does NOT evaluate constraints or check if duties have been fulfilled.

isPermitted(action: string): boolean

Parameters:

  • action (string) - The action to check (e.g., 'TRANSFER', 'USE', or custom URI)

Returns: boolean

True if at least one 'permit' rule exists AND no 'prohibit' rule exists for the action

Examples

const policy: Policy = {
policyURI: 'ipfs://Qm...',
profile: 'PD-REL-v1.0',
rules: [
{ action: 'USE', effect: 'permit' },
{ action: 'TRANSFER', effect: 'permit', constraints: [...] },
{ action: 'COMMERCIALIZE', effect: 'prohibit' }
]
};

const parser = new PolicyParser(policy);

// Basic permission checks
console.log(parser.isPermitted('USE')); // true (has permit)
console.log(parser.isPermitted('TRANSFER')); // true (has permit, constraints not evaluated)
console.log(parser.isPermitted('COMMERCIALIZE')); // false (has prohibit)
console.log(parser.isPermitted('MODIFY')); // false (no rules)

// With conflicting rules
const conflictPolicy: Policy = {
policyURI: 'ipfs://QmConflict',
profile: 'PD-REL-v1.0',
rules: [
{ action: 'DISPLAY', effect: 'permit' },
{ action: 'DISPLAY', effect: 'prohibit' } // Prohibition takes precedence
]
};
const conflictParser = new PolicyParser(conflictPolicy);
console.log(conflictParser.isPermitted('DISPLAY')); // false

isProhibited

Checks if a specific action is explicitly prohibited.

Returns true if any 'prohibit' rule exists for the specified action, regardless of any 'permit' rules that may also exist.

isProhibited(action: string): boolean

Parameters:

  • action (string) - The action to check (e.g., 'COMMERCIALIZE', 'TRANSFER', or custom URI)

Returns: boolean

True if at least one 'prohibit' rule exists for the action

Examples

const policy: Policy = {
policyURI: 'ipfs://Qm...',
profile: 'PD-REL-v1.0',
rules: [
{ action: 'USE', effect: 'permit' },
{ action: 'COMMERCIALIZE', effect: 'prohibit' },
{ action: 'TRANSFER', effect: 'permit' },
{ action: 'TRANSFER', effect: 'prohibit', target: 'specific-asset' }
]
};

const parser = new PolicyParser(policy);

console.log(parser.isProhibited('USE')); // false
console.log(parser.isProhibited('COMMERCIALIZE')); // true
console.log(parser.isProhibited('TRANSFER')); // true (has at least one prohibit)
console.log(parser.isProhibited('MODIFY')); // false (no rules)

getRulesForAction

Retrieves all rules associated with a specific action.

Returns all rules (both permit and prohibit) that match the specified action. This is useful for detailed analysis of how an action is regulated.

getRulesForAction(action: string): Rule[]

Parameters:

  • action (string) - The action to retrieve rules for (e.g., 'USE', 'TRANSFER', or custom URI)

Returns: Rule[]

An array of Rule objects that match the action, may be empty

Examples

const policy: Policy = {
policyURI: 'ipfs://Qm...',
profile: 'PD-REL-v1.0',
logic: 'OR',
rules: [
{
action: 'TRANSFER',
effect: 'permit',
description: 'Token holders can transfer',
constraints: [{ type: 'TOKEN_GATED', value: {...} }]
},
{
action: 'TRANSFER',
effect: 'permit',
description: 'Or pay to transfer',
duties: [{ type: 'PAYMENT', value: {...} }]
},
{
action: 'TRANSFER',
effect: 'prohibit',
description: 'But not to blacklisted addresses',
target: 'blacklist://addresses'
}
]
};

const parser = new PolicyParser(policy);
const transferRules = parser.getRulesForAction('TRANSFER');

console.log(`Found ${transferRules.length} TRANSFER rules:`);
transferRules.forEach((rule, i) => {
console.log(`${i + 1}. ${rule.effect}: ${rule.description}`);
console.log(` Constraints: ${rule.constraints?.length || 0}`);
console.log(` Duties: ${rule.duties?.length || 0}`);
});

// Check for non-existent action
const modifyRules = parser.getRulesForAction('MODIFY');
console.log(modifyRules.length); // 0

getDutiesForAction

Retrieves all duties from the first 'permit' rule for a given action.

This method finds the first 'permit' rule for the specified action and returns its duties. If multiple permit rules exist for the same action, only the duties from the first one are returned.

getDutiesForAction(action: string): Duty[]

Parameters:

  • action (string) - The action to retrieve duties for (e.g., 'MODIFY', 'USE', or custom URI)

Returns: Duty[]

An array of Duty objects from the first permit rule, or empty array if none exist

Examples

const policy: Policy = {
policyURI: 'ipfs://Qm...',
profile: 'PD-REL-v1.0',
rules: [
{
action: 'MODIFY',
effect: 'permit',
duties: [
{ type: 'ATTRIBUTION', value: 'Based on original work by Artist' },
{ type: 'SHARE_ALIKE', value: 'ipfs://QmOriginalPolicy' }
]
},
{
action: 'REPRODUCE',
effect: 'permit',
duties: [
{ type: 'PAYMENT', value: { amount: '10.00', currency: 'USD', recipient: '0xArtist' } }
]
},
{
action: 'USE',
effect: 'permit'
// No duties
}
]
};

const parser = new PolicyParser(policy);

// Get duties for different actions
const modifyDuties = parser.getDutiesForAction('MODIFY');
console.log(modifyDuties.length); // 2
console.log(modifyDuties[0].type); // 'ATTRIBUTION'

const reproduceDuties = parser.getDutiesForAction('REPRODUCE');
console.log(reproduceDuties[0].type); // 'PAYMENT'

const useDuties = parser.getDutiesForAction('USE');
console.log(useDuties.length); // 0 (no duties)

const transferDuties = parser.getDutiesForAction('TRANSFER');
console.log(transferDuties.length); // 0 (no TRANSFER rules)

getConstraintsForAction

Retrieves all constraints from all rules for a given action.

Aggregates constraints from all rules (both permit and prohibit) that match the specified action. This provides a complete view of all conditions that might apply to an action.

getConstraintsForAction(action: string): Constraint[]

Parameters:

  • action (string) - The action to retrieve constraints for (e.g., 'TRANSFER', 'USE', or custom URI)

Returns: Constraint[]

An array of all Constraint objects from all matching rules, may contain duplicates

Examples

const policy: Policy = {
policyURI: 'ipfs://Qm...',
profile: 'PD-REL-v1.0',
logic: 'OR',
rules: [
{
action: 'TRANSFER',
effect: 'permit',
constraints: [
{ type: 'TOKEN_GATED', value: { chainId: 1, contractAddress: '0xNFT1', minBalance: 1 } }
]
},
{
action: 'TRANSFER',
effect: 'permit',
constraints: [
{ type: 'TIME_PERIOD', value: { start: 1704067200, end: 1735689599 } },
{ type: 'USAGE_COUNT', value: 100 }
]
},
{
action: 'TRANSFER',
effect: 'prohibit',
constraints: [
{ type: 'SPATIAL', value: 'blocked-countries' }
]
}
]
};

const parser = new PolicyParser(policy);
const constraints = parser.getConstraintsForAction('TRANSFER');

console.log(`Total constraints: ${constraints.length}`); // 4

// Group constraints by type
const constraintTypes = constraints.reduce((acc, c) => {
acc[c.type] = (acc[c.type] || 0) + 1;
return acc;
}, {} as Record<string, number>);

console.log('Constraint types:', constraintTypes);
// { TOKEN_GATED: 1, TIME_PERIOD: 1, USAGE_COUNT: 1, SPATIAL: 1 }

// Check if any time constraints exist
const hasTimeConstraint = constraints.some(c => c.type === 'TIME_PERIOD');
console.log('Has time constraint:', hasTimeConstraint); // true

See Also

Since

Version 1.0.0