call_cedar_macros_composed
Two Cedar macros composed with && in one when { … } clause:
is_eligible(context.input.shares, context.input.stock) && is_not_blocked(context.input.stock).
Both macros are defined in the macro library (macros.dw) and loaded via
--macros; each is called with a different argument shape (a Long and a
String), showing that ?p literal-splicing carries call-site types through.
Validate with:
dogwood validate policy.dw --policy-schema schema.cedarschema --macros macros.dw
Referenced by guide/09-calling-macros.md.
Policy
// Two `def cedar` macros composed with `&&` in one `when` clause.
// Both are defined in the macro library (macros.dw); each is called
// with a different argument shape (Long, String).
@id("sell_eligible_unblocked")
permit (principal, action == Drupe::Action::"SellShares", resource)
when {
is_eligible(context.input.shares, context.input.stock)
&& is_not_blocked(context.input.stock)
};
Schema
namespace Drupe {
type ApproveSaleInput = {
shares: Long,
stock: String
};
type ApproveSaleOutput = {
approved: Bool
};
type ContentFilterFinding = {
severityScore: decimal
};
type GetStockInfoInput = {
stock: String
};
type GetStockInfoOutput = {
info: String
};
type PromptAttackFinding = {
severityScore: decimal
};
type SellSharesInput = {
shares: Long,
stock: String
};
type SellSharesOutput = {
proceeds: decimal
};
type SensitiveInfoFinding = {
confidenceScore: decimal
};
type SystemContext = {
now: datetime
};
entity Gateway;
entity IamEntity = {
id: String
};
entity OAuthUser = {
id: String
} tags String;
entity UnauthenticatedUser;
action "ApproveSale" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: ApproveSaleInput,
output?: ApproveSaleOutput,
system: SystemContext
}
};
action "CallTool" in [Action::"Mcp"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "GetStockInfo" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: GetStockInfoInput,
output?: GetStockInfoOutput,
system: SystemContext
}
};
action "Http" appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "InvokeAgent" in [Action::"Http"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
input?: { },
system: SystemContext
}
};
action "InvokeLLM" in [Action::"Http"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
input?: { },
system: SystemContext
}
};
action "Mcp" appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
action "SellShares" in [Action::"CallTool"] appliesTo {
principal: [IamEntity, OAuthUser, UnauthenticatedUser],
resource: [Gateway],
context: {
input: SellSharesInput,
output?: SellSharesOutput,
system: SystemContext
}
};
action "UnknownTool" in [Action::"CallTool"] appliesTo {
principal: [OAuthUser, IamEntity, UnauthenticatedUser],
resource: [Gateway],
context: {
system: SystemContext
}
};
}
Macros
// Macro library: two reusable `def cedar` fragments composed by policy.dw.
//
// `is_eligible` takes a Long (?shares) and a String (?stock); `is_not_blocked`
// takes a String (?stock). Each is called with a different argument shape,
// proving ?p literal-splicing carries call-site types through.
def cedar is_eligible(?shares, ?stock) {
?shares < 100 || ?stock == "FOO"
};
def cedar is_not_blocked(?stock) {
!(?stock == "BLOCKED")
};