Accept the risk
An error that an authorised user may accept, with a reason.
Some errors may be accepted by the right person. acceptance on an error rule says who may accept
it (roles), whether a reason is needed (justification) and how long the acceptance lasts
(expiresAfter). The request carries an accept-risk resolution. When the actor holds one of the
roles and gives a reason, the finding becomes accepted and stops blocking.
Syntax
- id: <rule id>
kind: validation
severity: error
acceptance:
allowed: true
roles: [risk-officer] # empty or left out: any actor
justification: required # or none
expiresAfter: P30D # ISO 8601 duration, for the host's audit trail"actor": { "id": "u-7", "roles": ["risk-officer"] },
"resolutions": [ { "rule": "<rule id>", "type": "accept-risk", "justification": "<why>" } ]Example
A credit limit of 8000 is above 5000. A risk officer accepts the risk and gives a reason, so the
request is allowed. The ruleset's other golden tests show that a clerk, or an acceptance without a
reason, still gets deny.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.accept-risk, version: 1.0.0, title: "Accept the risk" }
scope:
- { level: organization, id: learn }
entities:
Customer:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Customer" }
rules:
- id: customer.credit.limit
kind: validation
target: { entity: Customer, field: /creditLimit }
operations: [create, update]
assert: { op: lte, args: [{ var: data.creditLimit }, 5000] }
severity: error
acceptance:
allowed: true
roles: [risk-officer]
justification: required
expiresAfter: P30D
finding: { code: LRN-RSK-001, message: customer.creditTooHigh }
messages:
en:
customer.creditTooHigh: "A credit limit above 5000 needs a risk acceptance."
tests:
- name: a risk officer accepts the risk with a reason
entity: Customer
operation: create
given:
data: { creditLimit: 8000 }
actor: { id: u-7, roles: [risk-officer] }
resolutions:
- { rule: customer.credit.limit, type: accept-risk, justification: "long-standing customer" }
expect:
decision: allow
findings:
- { rule: customer.credit.limit, status: accepted, blocking: false }
- name: a clerk cannot accept the risk
entity: Customer
operation: create
given:
data: { creditLimit: 8000 }
actor: { id: u-2, roles: [clerk] }
resolutions:
- { rule: customer.credit.limit, type: accept-risk, justification: "long-standing customer" }
expect:
decision: deny
findings:
- { rule: customer.credit.limit, status: open, blocking: true }
- name: an acceptance without a justification does not count
entity: Customer
operation: create
given:
data: { creditLimit: 8000 }
actor: { id: u-7, roles: [risk-officer] }
resolutions:
- { rule: customer.credit.limit, type: accept-risk }
expect:
decision: deny
findings:
- { rule: customer.credit.limit, status: open }{
"entity": "Customer",
"operation": "create",
"data": {
"creditLimit": 8000
},
"actor": {
"id": "u-7",
"roles": [
"risk-officer"
]
},
"resolutions": [
{
"rule": "customer.credit.limit",
"type": "accept-risk",
"justification": "long-standing customer"
}
]
}Result, from the engine
Decisionallow1 finding, server channel
LRN-RSK-001errornot blocking, acceptedA credit limit above 5000 needs a risk acceptance./creditLimit
Common mistakes
acceptanceon a warning. Only anerrorcan be accepted.- Taking
actor.rolesfrom the request body. The host must set the actor from its own authentication, or anyone could claim to be a risk officer.
Exercise
Let a manager accept the risk too, and stop asking for a reason. Write a test where a manager accepts without a justification, and one where a clerk is still denied.
Hint
Add manager to roles and set justification: none.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.accept-risk, version: 1.0.0, title: "Accept the risk" }
scope:
- { level: organization, id: learn }
entities:
Customer:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Customer" }
rules:
- id: customer.credit.limit
kind: validation
target: { entity: Customer, field: /creditLimit }
operations: [create, update]
assert: { op: lte, args: [{ var: data.creditLimit }, 5000] }
severity: error
acceptance:
allowed: true
roles: [risk-officer, manager]
justification: none
expiresAfter: P30D
finding: { code: LRN-RSK-001, message: customer.creditTooHigh }
messages:
en:
customer.creditTooHigh: "A credit limit above 5000 needs a risk acceptance."
tests:
- name: a manager accepts the risk without a reason
entity: Customer
operation: create
given:
data: { creditLimit: 8000 }
actor: { id: u-9, roles: [manager] }
resolutions:
- { rule: customer.credit.limit, type: accept-risk }
expect:
decision: allow
findings:
- { rule: customer.credit.limit, status: accepted }
- name: a clerk still cannot accept the risk
entity: Customer
operation: create
given:
data: { creditLimit: 8000 }
actor: { id: u-2, roles: [clerk] }
resolutions:
- { rule: customer.credit.limit, type: accept-risk }
expect:
decision: deny
findings:
- { rule: customer.credit.limit, status: open }{
"entity": "Customer",
"operation": "create",
"data": {
"creditLimit": 8000
},
"actor": {
"id": "u-9",
"roles": [
"manager"
]
},
"resolutions": [
{
"rule": "customer.credit.limit",
"type": "accept-risk"
}
]
}Result, from the engine
Decisionallow1 finding, server channel
LRN-RSK-001errornot blocking, acceptedA credit limit above 5000 needs a risk acceptance./creditLimit