Adding ABAC rules ideas

This commit is contained in:
simonredfern 2025-12-30 11:46:58 +01:00
parent 220007ee61
commit 45f55f1ac1
2 changed files with 555 additions and 176 deletions

View File

@ -4869,8 +4869,18 @@ trait APIMethods600 {
)
),
examples = List(
"authenticatedUser.userId == user.userId",
"bankOpt.isDefined && bankOpt.get.bankId.value == \"gh.29.uk\""
AbacRuleExampleJsonV600(
category = "User Access",
title = "Check User Identity",
code = "authenticatedUser.userId == user.userId",
description = "Verify that the authenticated user matches the target user"
),
AbacRuleExampleJsonV600(
category = "Bank Access",
title = "Check Specific Bank",
code = "bankOpt.isDefined && bankOpt.get.bankId.value == \"gh.29.uk\"",
description = "Verify that the bank context is defined and matches a specific bank ID"
)
),
available_operators = List("==", "!=", "&&", "||", "!", ">", "<", ">=", "<=", "contains", "isDefined"),
notes = List(
@ -5018,179 +5028,541 @@ trait APIMethods600 {
AbacObjectPropertyJsonV600("endTime", "Option[Date]", "Request end time")
))
),
scala_code_examples = List(
"// === authenticatedUser (User) - Always Available ===",
"authenticatedUser.emailAddress.contains(\"@example.com\")",
"authenticatedUser.provider == \"obp\"",
"authenticatedUser.userId == userOpt.get.userId",
"!authenticatedUser.isDeleted.getOrElse(false)",
"// === authenticatedUserAttributes (List[UserAttributeTrait]) ===",
"authenticatedUserAttributes.exists(attr => attr.name == \"role\" && attr.value == \"admin\")",
"authenticatedUserAttributes.find(_.name == \"department\").exists(_.value == \"finance\")",
"authenticatedUserAttributes.exists(attr => attr.name == \"role\" && List(\"admin\", \"manager\").contains(attr.value))",
"// === authenticatedUserAuthContext (List[UserAuthContext]) ===",
"authenticatedUserAuthContext.exists(_.key == \"session_type\" && _.value == \"secure\")",
"authenticatedUserAuthContext.exists(_.key == \"auth_method\" && _.value == \"certificate\")",
"// === onBehalfOfUserOpt (Option[User]) - Delegation ===",
"onBehalfOfUserOpt.exists(_.emailAddress.endsWith(\"@company.com\"))",
"onBehalfOfUserOpt.isEmpty || onBehalfOfUserOpt.get.userId == authenticatedUser.userId",
"onBehalfOfUserOpt.forall(_.userId != authenticatedUser.userId)",
"// === onBehalfOfUserAttributes (List[UserAttributeTrait]) ===",
"onBehalfOfUserAttributes.exists(attr => attr.name == \"delegation_level\" && attr.value == \"full\")",
"onBehalfOfUserAttributes.isEmpty || onBehalfOfUserAttributes.exists(_.name == \"authorized\")",
"// === userOpt (Option[User]) - Target User ===",
"userOpt.isDefined && userOpt.get.userId == authenticatedUser.userId",
"userOpt.exists(_.provider == \"obp\")",
"userOpt.exists(_.emailAddress.endsWith(\"@trusted.com\"))",
"userOpt.forall(!_.isDeleted.getOrElse(false))",
"// === userAttributes (List[UserAttributeTrait]) ===",
"userAttributes.exists(attr => attr.name == \"account_type\" && attr.value == \"premium\")",
"userAttributes.exists(attr => attr.name == \"kyc_status\" && attr.value == \"verified\")",
"userAttributes.find(_.name == \"tier\").exists(_.value.toInt >= 2)",
"// === bankOpt (Option[Bank]) ===",
"bankOpt.isDefined && bankOpt.get.bankId.value == \"gh.29.uk\"",
"bankOpt.exists(_.fullName.contains(\"Community\"))",
"bankOpt.exists(_.websiteUrl.contains(\"https://\"))",
"// === bankAttributes (List[BankAttributeTrait]) ===",
"bankAttributes.exists(attr => attr.name == \"region\" && attr.value == \"EU\")",
"bankAttributes.exists(attr => attr.name == \"certified\" && attr.value == \"true\")",
"// === accountOpt (Option[BankAccount]) ===",
"accountOpt.isDefined && accountOpt.get.balance > 1000",
"accountOpt.exists(acc => acc.currency == \"USD\" && acc.balance > 5000)",
"accountOpt.exists(_.accountType == \"SAVINGS\")",
"accountOpt.exists(_.number.length >= 10)",
"// === accountAttributes (List[AccountAttribute]) ===",
"accountAttributes.exists(attr => attr.name == \"status\" && attr.value == \"active\")",
"accountAttributes.exists(attr => attr.name == \"account_tier\" && attr.value == \"gold\")",
"// === transactionOpt (Option[Transaction]) ===",
"transactionOpt.isDefined && transactionOpt.get.amount < 10000",
"transactionOpt.exists(_.transactionType.contains(\"TRANSFER\"))",
"transactionOpt.exists(t => t.currency == \"EUR\" && t.amount > 100)",
"transactionOpt.exists(_.balance > 0)",
"// === transactionAttributes (List[TransactionAttribute]) ===",
"transactionAttributes.exists(attr => attr.name == \"category\" && attr.value == \"business\")",
"!transactionAttributes.exists(attr => attr.name == \"flagged\" && attr.value == \"true\")",
"// === transactionRequestOpt (Option[TransactionRequest]) ===",
"transactionRequestOpt.exists(_.status == \"PENDING\")",
"transactionRequestOpt.exists(_.type == \"SEPA\")",
"transactionRequestOpt.exists(_.this_bank_id.value == bankOpt.get.bankId.value)",
"// === transactionRequestAttributes (List[TransactionRequestAttributeTrait]) ===",
"transactionRequestAttributes.exists(attr => attr.name == \"priority\" && attr.value == \"high\")",
"transactionRequestAttributes.exists(attr => attr.name == \"source\" && attr.value == \"mobile_app\")",
"// === customerOpt (Option[Customer]) ===",
"customerOpt.exists(_.legalName.contains(\"Corp\"))",
"customerOpt.isDefined && customerOpt.get.email == authenticatedUser.emailAddress",
"customerOpt.exists(_.relationshipStatus == \"ACTIVE\")",
"customerOpt.exists(_.mobileNumber.nonEmpty)",
"// === customerAttributes (List[CustomerAttribute]) ===",
"customerAttributes.exists(attr => attr.name == \"risk_level\" && attr.value == \"low\")",
"customerAttributes.exists(attr => attr.name == \"vip_status\" && attr.value == \"true\")",
"// === callContext (Option[CallContext]) ===",
"callContext.exists(_.ipAddress.exists(_.startsWith(\"192.168\")))",
"callContext.exists(_.verb.exists(_ == \"GET\"))",
"callContext.exists(_.url.exists(_.contains(\"/accounts/\")))",
"// === OBJECT-TO-OBJECT COMPARISONS ===",
"// User Comparisons - Self Access",
"userOpt.exists(_.userId == authenticatedUser.userId)",
"userOpt.exists(_.emailAddress == authenticatedUser.emailAddress)",
"userOpt.exists(u => authenticatedUser.emailAddress.split(\"@\")(1) == u.emailAddress.split(\"@\")(1))",
"// User Comparisons - Delegation",
"onBehalfOfUserOpt.isDefined && userOpt.isDefined && onBehalfOfUserOpt.get.userId == userOpt.get.userId",
"userOpt.exists(_.userId != authenticatedUser.userId)",
"// Customer-User Comparisons",
"customerOpt.exists(_.email == authenticatedUser.emailAddress)",
"customerOpt.isDefined && userOpt.isDefined && customerOpt.get.email == userOpt.get.emailAddress",
"customerOpt.exists(c => userOpt.exists(u => c.legalName.contains(u.name)))",
"// Account-Transaction Comparisons",
"transactionOpt.isDefined && accountOpt.isDefined && transactionOpt.get.amount < accountOpt.get.balance",
"transactionOpt.exists(t => accountOpt.exists(a => t.amount <= a.balance * 0.5))",
"transactionOpt.exists(t => accountOpt.exists(a => t.currency == a.currency))",
"transactionOpt.exists(t => accountOpt.exists(a => a.balance - t.amount >= 0))",
"transactionOpt.exists(t => accountOpt.exists(a => (a.accountType == \"CHECKING\" && t.transactionType.exists(_.contains(\"DEBIT\")))))",
"// Bank-Account Comparisons",
"accountOpt.isDefined && bankOpt.isDefined && accountOpt.get.bankId == bankOpt.get.bankId.value",
"accountOpt.exists(a => bankAttributes.exists(attr => attr.name == \"primary_currency\" && attr.value == a.currency))",
"// Transaction Request Comparisons",
"transactionRequestOpt.exists(tr => accountOpt.exists(a => tr.this_account_id.value == a.accountId.value))",
"transactionRequestOpt.exists(tr => bankOpt.exists(b => tr.this_bank_id.value == b.bankId.value))",
"transactionOpt.isDefined && transactionRequestOpt.isDefined && transactionOpt.get.amount == transactionRequestOpt.get.charge.value.toDouble",
"// Attribute Cross-Comparisons",
"userAttributes.exists(ua => ua.name == \"tier\" && accountAttributes.exists(aa => aa.name == \"tier\" && ua.value == aa.value))",
"customerAttributes.exists(ca => ca.name == \"segment\" && accountAttributes.exists(aa => aa.name == \"segment\" && ca.value == aa.value))",
"authenticatedUserAttributes.exists(ua => ua.name == \"department\" && accountAttributes.exists(aa => aa.name == \"department\" && ua.value == aa.value))",
"transactionAttributes.exists(ta => ta.name == \"risk_score\" && userAttributes.exists(ua => ua.name == \"risk_tolerance\" && ta.value.toInt <= ua.value.toInt))",
"bankAttributes.exists(ba => ba.name == \"region\" && customerAttributes.exists(ca => ca.name == \"region\" && ba.value == ca.value))",
"// === COMPLEX MULTI-OBJECT EXAMPLES ===",
"authenticatedUser.emailAddress.endsWith(\"@bank.com\") && accountOpt.exists(_.balance > 0) && bankOpt.exists(_.bankId.value == \"gh.29.uk\")",
"authenticatedUserAttributes.exists(_.name == \"role\" && _.value == \"manager\") && userOpt.exists(_.userId != authenticatedUser.userId)",
"(onBehalfOfUserOpt.isEmpty || onBehalfOfUserOpt.exists(_.userId == authenticatedUser.userId)) && accountOpt.exists(_.balance > 1000)",
"userAttributes.exists(_.name == \"kyc_status\" && _.value == \"verified\") && (onBehalfOfUserOpt.isEmpty || onBehalfOfUserAttributes.exists(_.name == \"authorized\"))",
"customerAttributes.exists(_.name == \"vip_status\" && _.value == \"true\") && accountAttributes.exists(_.name == \"account_tier\" && _.value == \"premium\")",
"// Chained Object Validation",
"userOpt.exists(u => customerOpt.exists(c => c.email == u.emailAddress && accountOpt.exists(a => transactionOpt.exists(t => t.accountId.value == a.accountId.value))))",
"bankOpt.exists(b => accountOpt.exists(a => a.bankId == b.bankId.value && transactionRequestOpt.exists(tr => tr.this_account_id.value == a.accountId.value)))",
"// Aggregation Examples",
"authenticatedUserAttributes.exists(aua => userAttributes.exists(ua => aua.name == ua.name && aua.value == ua.value))",
"transactionAttributes.forall(ta => accountAttributes.exists(aa => aa.name == \"allowed_transaction_\" + ta.name))",
"// === REAL-WORLD BUSINESS LOGIC ===",
"// Loan Approval",
"customerAttributes.exists(ca => ca.name == \"credit_score\" && ca.value.toInt > 650) && accountOpt.exists(_.balance > 5000)",
"// Wire Transfer Authorization",
"transactionOpt.exists(t => t.amount < 100000 && t.transactionType.exists(_.contains(\"WIRE\"))) && authenticatedUserAttributes.exists(_.name == \"wire_authorized\")",
"// Self-Service Account Closure",
"accountOpt.exists(a => (a.balance == 0 && userOpt.exists(_.userId == authenticatedUser.userId)) || authenticatedUserAttributes.exists(_.name == \"role\" && _.value == \"manager\"))",
"// VIP Priority Processing",
"(customerAttributes.exists(_.name == \"vip_status\" && _.value == \"true\") || accountAttributes.exists(_.name == \"account_tier\" && _.value == \"platinum\"))",
"// Joint Account Access",
"accountOpt.exists(a => a.accountHolders.exists(h => h.userId == authenticatedUser.userId || h.emailAddress == authenticatedUser.emailAddress))",
"// === SAFE OPTION HANDLING PATTERNS ===",
"userOpt match { case Some(u) => u.userId == authenticatedUser.userId case None => false }",
"accountOpt.exists(_.balance > 0)",
"userOpt.forall(!_.isDeleted.getOrElse(false))",
"accountOpt.map(_.balance).getOrElse(0) > 100",
"// === ERROR PREVENTION EXAMPLES ===",
"// WRONG: accountOpt.get.balance > 1000 (unsafe!)",
"// RIGHT: accountOpt.exists(_.balance > 1000)",
"// WRONG: userOpt.get.userId == authenticatedUser.userId",
"// RIGHT: userOpt.exists(_.userId == authenticatedUser.userId)",
"// IMPORTANT: Use camelCase (userId NOT user_id, emailAddress NOT email_address)",
"// IMPORTANT: Parameters use Opt suffix for Optional types (userOpt, accountOpt, bankOpt)",
"// IMPORTANT: Always check isDefined before using .get, or use safe methods like exists(), forall(), map()"
examples = List(
AbacRuleExampleJsonV600(
category = "User - Authenticated User",
title = "Check Email Domain",
code = "authenticatedUser.emailAddress.contains(\"@example.com\")",
description = "Verify that the authenticated user's email belongs to a specific domain"
),
AbacRuleExampleJsonV600(
category = "User - Authenticated User",
title = "Check Authentication Provider",
code = "authenticatedUser.provider == \"obp\"",
description = "Verify the authentication provider is OBP"
),
AbacRuleExampleJsonV600(
category = "User - Authenticated User",
title = "Compare Authenticated to Target User",
code = "authenticatedUser.userId == userOpt.get.userId",
description = "Check if authenticated user matches the target user (unsafe - use exists instead)"
),
AbacRuleExampleJsonV600(
category = "User - Authenticated User",
title = "Check User Not Deleted",
code = "!authenticatedUser.isDeleted.getOrElse(false)",
description = "Verify the authenticated user is not marked as deleted"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Authenticated User",
title = "Check Admin Role",
code = "authenticatedUserAttributes.exists(attr => attr.name == \"role\" && attr.value == \"admin\")",
description = "Check if authenticated user has admin role attribute"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Authenticated User",
title = "Check Department",
code = "authenticatedUserAttributes.find(_.name == \"department\").exists(_.value == \"finance\")",
description = "Check if authenticated user belongs to finance department"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Authenticated User",
title = "Check Multiple Roles",
code = "authenticatedUserAttributes.exists(attr => attr.name == \"role\" && List(\"admin\", \"manager\").contains(attr.value))",
description = "Check if authenticated user has admin or manager role"
),
AbacRuleExampleJsonV600(
category = "User Auth Context",
title = "Check Session Type",
code = "authenticatedUserAuthContext.exists(_.key == \"session_type\" && _.value == \"secure\")",
description = "Verify the session type is secure"
),
AbacRuleExampleJsonV600(
category = "User Auth Context",
title = "Check Auth Method",
code = "authenticatedUserAuthContext.exists(_.key == \"auth_method\" && _.value == \"certificate\")",
description = "Verify authentication was done via certificate"
),
AbacRuleExampleJsonV600(
category = "User - Delegation",
title = "Check Delegated User Email Domain",
code = "onBehalfOfUserOpt.exists(_.emailAddress.endsWith(\"@company.com\"))",
description = "Check if delegation user belongs to specific company domain"
),
AbacRuleExampleJsonV600(
category = "User - Delegation",
title = "Check No Delegation or Self Delegation",
code = "onBehalfOfUserOpt.isEmpty || onBehalfOfUserOpt.get.userId == authenticatedUser.userId",
description = "Allow if no delegation or user delegating to themselves"
),
AbacRuleExampleJsonV600(
category = "User - Delegation",
title = "Check Different User Delegation",
code = "onBehalfOfUserOpt.forall(_.userId != authenticatedUser.userId)",
description = "Check that delegation is to a different user (if present)"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Delegation",
title = "Check Delegation Level",
code = "onBehalfOfUserAttributes.exists(attr => attr.name == \"delegation_level\" && attr.value == \"full\")",
description = "Check if delegation has full permission level"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Delegation",
title = "Check Authorized Delegation",
code = "onBehalfOfUserAttributes.isEmpty || onBehalfOfUserAttributes.exists(_.name == \"authorized\")",
description = "Allow if no delegation attributes or has authorized attribute"
),
AbacRuleExampleJsonV600(
category = "User - Target User",
title = "Check Self Access",
code = "userOpt.isDefined && userOpt.get.userId == authenticatedUser.userId",
description = "Check if target user is the authenticated user (self-access)"
),
AbacRuleExampleJsonV600(
category = "User - Target User",
title = "Check Target User Provider",
code = "userOpt.exists(_.provider == \"obp\")",
description = "Check if target user is authenticated via OBP provider"
),
AbacRuleExampleJsonV600(
category = "User - Target User",
title = "Check Target User Email Domain",
code = "userOpt.exists(_.emailAddress.endsWith(\"@trusted.com\"))",
description = "Check if target user belongs to trusted domain"
),
AbacRuleExampleJsonV600(
category = "User - Target User",
title = "Check Target User Active",
code = "userOpt.forall(!_.isDeleted.getOrElse(false))",
description = "Ensure target user is not deleted (if present)"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Target User",
title = "Check Premium Account",
code = "userAttributes.exists(attr => attr.name == \"account_type\" && attr.value == \"premium\")",
description = "Check if target user has premium account type"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Target User",
title = "Check KYC Status",
code = "userAttributes.exists(attr => attr.name == \"kyc_status\" && attr.value == \"verified\")",
description = "Check if target user has verified KYC status"
),
AbacRuleExampleJsonV600(
category = "User Attributes - Target User",
title = "Check User Tier Level",
code = "userAttributes.find(_.name == \"tier\").exists(_.value.toInt >= 2)",
description = "Check if user tier is 2 or higher"
),
AbacRuleExampleJsonV600(
category = "Bank",
title = "Check Specific Bank ID",
code = "bankOpt.isDefined && bankOpt.get.bankId.value == \"gh.29.uk\"",
description = "Check if bank context is defined and matches specific bank ID"
),
AbacRuleExampleJsonV600(
category = "Bank",
title = "Check Bank Name Contains Text",
code = "bankOpt.exists(_.fullName.contains(\"Community\"))",
description = "Check if bank full name contains specific text"
),
AbacRuleExampleJsonV600(
category = "Bank",
title = "Check Bank Has HTTPS Website",
code = "bankOpt.exists(_.websiteUrl.contains(\"https://\"))",
description = "Check if bank website uses HTTPS"
),
AbacRuleExampleJsonV600(
category = "Bank Attributes",
title = "Check Bank Region",
code = "bankAttributes.exists(attr => attr.name == \"region\" && attr.value == \"EU\")",
description = "Check if bank is in EU region"
),
AbacRuleExampleJsonV600(
category = "Bank Attributes",
title = "Check Bank Certification",
code = "bankAttributes.exists(attr => attr.name == \"certified\" && attr.value == \"true\")",
description = "Check if bank has certification attribute"
),
AbacRuleExampleJsonV600(
category = "Account",
title = "Check Minimum Balance",
code = "accountOpt.isDefined && accountOpt.get.balance > 1000",
description = "Check if account balance is above threshold"
),
AbacRuleExampleJsonV600(
category = "Account",
title = "Check USD Account Balance",
code = "accountOpt.exists(acc => acc.currency == \"USD\" && acc.balance > 5000)",
description = "Check if USD account has balance above $5000"
),
AbacRuleExampleJsonV600(
category = "Account",
title = "Check Account Type",
code = "accountOpt.exists(_.accountType == \"SAVINGS\")",
description = "Check if account is a savings account"
),
AbacRuleExampleJsonV600(
category = "Account",
title = "Check Account Number Length",
code = "accountOpt.exists(_.number.length >= 10)",
description = "Check if account number has minimum length"
),
AbacRuleExampleJsonV600(
category = "Account Attributes",
title = "Check Account Status",
code = "accountAttributes.exists(attr => attr.name == \"status\" && attr.value == \"active\")",
description = "Check if account has active status"
),
AbacRuleExampleJsonV600(
category = "Account Attributes",
title = "Check Account Tier",
code = "accountAttributes.exists(attr => attr.name == \"account_tier\" && attr.value == \"gold\")",
description = "Check if account has gold tier"
),
AbacRuleExampleJsonV600(
category = "Transaction",
title = "Check Transaction Amount Limit",
code = "transactionOpt.isDefined && transactionOpt.get.amount < 10000",
description = "Check if transaction amount is below limit"
),
AbacRuleExampleJsonV600(
category = "Transaction",
title = "Check Transaction Type",
code = "transactionOpt.exists(_.transactionType.contains(\"TRANSFER\"))",
description = "Check if transaction is a transfer type"
),
AbacRuleExampleJsonV600(
category = "Transaction",
title = "Check EUR Transaction Amount",
code = "transactionOpt.exists(t => t.currency == \"EUR\" && t.amount > 100)",
description = "Check if EUR transaction exceeds €100"
),
AbacRuleExampleJsonV600(
category = "Transaction",
title = "Check Positive Balance After Transaction",
code = "transactionOpt.exists(_.balance > 0)",
description = "Check if balance remains positive after transaction"
),
AbacRuleExampleJsonV600(
category = "Transaction Attributes",
title = "Check Transaction Category",
code = "transactionAttributes.exists(attr => attr.name == \"category\" && attr.value == \"business\")",
description = "Check if transaction is categorized as business"
),
AbacRuleExampleJsonV600(
category = "Transaction Attributes",
title = "Check Transaction Not Flagged",
code = "!transactionAttributes.exists(attr => attr.name == \"flagged\" && attr.value == \"true\")",
description = "Check that transaction is not flagged"
),
AbacRuleExampleJsonV600(
category = "Transaction Request",
title = "Check Pending Status",
code = "transactionRequestOpt.exists(_.status == \"PENDING\")",
description = "Check if transaction request is pending"
),
AbacRuleExampleJsonV600(
category = "Transaction Request",
title = "Check SEPA Type",
code = "transactionRequestOpt.exists(_.type == \"SEPA\")",
description = "Check if transaction request is SEPA type"
),
AbacRuleExampleJsonV600(
category = "Transaction Request",
title = "Check Same Bank",
code = "transactionRequestOpt.exists(_.this_bank_id.value == bankOpt.get.bankId.value)",
description = "Check if transaction request is for the same bank (unsafe - use exists)"
),
AbacRuleExampleJsonV600(
category = "Transaction Request Attributes",
title = "Check High Priority",
code = "transactionRequestAttributes.exists(attr => attr.name == \"priority\" && attr.value == \"high\")",
description = "Check if transaction request has high priority"
),
AbacRuleExampleJsonV600(
category = "Transaction Request Attributes",
title = "Check Mobile App Source",
code = "transactionRequestAttributes.exists(attr => attr.name == \"source\" && attr.value == \"mobile_app\")",
description = "Check if transaction request originated from mobile app"
),
AbacRuleExampleJsonV600(
category = "Customer",
title = "Check Corporate Customer",
code = "customerOpt.exists(_.legalName.contains(\"Corp\"))",
description = "Check if customer legal name contains Corp"
),
AbacRuleExampleJsonV600(
category = "Customer",
title = "Check Customer Email Matches User",
code = "customerOpt.isDefined && customerOpt.get.email == authenticatedUser.emailAddress",
description = "Check if customer email matches authenticated user"
),
AbacRuleExampleJsonV600(
category = "Customer",
title = "Check Active Customer Relationship",
code = "customerOpt.exists(_.relationshipStatus == \"ACTIVE\")",
description = "Check if customer relationship is active"
),
AbacRuleExampleJsonV600(
category = "Customer",
title = "Check Customer Has Mobile",
code = "customerOpt.exists(_.mobileNumber.nonEmpty)",
description = "Check if customer has mobile number on file"
),
AbacRuleExampleJsonV600(
category = "Customer Attributes",
title = "Check Low Risk Customer",
code = "customerAttributes.exists(attr => attr.name == \"risk_level\" && attr.value == \"low\")",
description = "Check if customer has low risk level"
),
AbacRuleExampleJsonV600(
category = "Customer Attributes",
title = "Check VIP Status",
code = "customerAttributes.exists(attr => attr.name == \"vip_status\" && attr.value == \"true\")",
description = "Check if customer has VIP status"
),
AbacRuleExampleJsonV600(
category = "Call Context",
title = "Check Internal Network",
code = "callContext.exists(_.ipAddress.exists(_.startsWith(\"192.168\")))",
description = "Check if request comes from internal network"
),
AbacRuleExampleJsonV600(
category = "Call Context",
title = "Check GET Request",
code = "callContext.exists(_.verb.exists(_ == \"GET\"))",
description = "Check if request is GET method"
),
AbacRuleExampleJsonV600(
category = "Call Context",
title = "Check URL Contains Pattern",
code = "callContext.exists(_.url.exists(_.contains(\"/accounts/\")))",
description = "Check if request URL contains accounts path"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - User Comparisons",
title = "Self Access Check",
code = "userOpt.exists(_.userId == authenticatedUser.userId)",
description = "Check if target user is the authenticated user"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - User Comparisons",
title = "Same Email Check",
code = "userOpt.exists(_.emailAddress == authenticatedUser.emailAddress)",
description = "Check if target user has same email as authenticated user"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - User Comparisons",
title = "Same Email Domain",
code = "userOpt.exists(u => authenticatedUser.emailAddress.split(\"@\")(1) == u.emailAddress.split(\"@\")(1))",
description = "Check if both users belong to same email domain"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - User Comparisons",
title = "Delegation Match",
code = "onBehalfOfUserOpt.isDefined && userOpt.isDefined && onBehalfOfUserOpt.get.userId == userOpt.get.userId",
description = "Check if delegation user matches target user"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - User Comparisons",
title = "Different User Access",
code = "userOpt.exists(_.userId != authenticatedUser.userId)",
description = "Check if accessing a different user's data"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Customer Comparisons",
title = "Customer Email Matches Auth User",
code = "customerOpt.exists(_.email == authenticatedUser.emailAddress)",
description = "Check if customer email matches authenticated user"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Customer Comparisons",
title = "Customer Email Matches Target User",
code = "customerOpt.isDefined && userOpt.isDefined && customerOpt.get.email == userOpt.get.emailAddress",
description = "Check if customer email matches target user email"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Customer Comparisons",
title = "Customer Name Contains User Name",
code = "customerOpt.exists(c => userOpt.exists(u => c.legalName.contains(u.name)))",
description = "Check if customer legal name contains user name"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Account/Transaction",
title = "Transaction Within Balance",
code = "transactionOpt.isDefined && accountOpt.isDefined && transactionOpt.get.amount < accountOpt.get.balance",
description = "Check if transaction amount is less than account balance"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Account/Transaction",
title = "Transaction Within 50% Balance",
code = "transactionOpt.exists(t => accountOpt.exists(a => t.amount <= a.balance * 0.5))",
description = "Check if transaction is within 50% of account balance"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Account/Transaction",
title = "Same Currency Check",
code = "transactionOpt.exists(t => accountOpt.exists(a => t.currency == a.currency))",
description = "Check if transaction and account have same currency"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Account/Transaction",
title = "Sufficient Funds After Transaction",
code = "transactionOpt.exists(t => accountOpt.exists(a => a.balance - t.amount >= 0))",
description = "Check if account will have sufficient funds after transaction"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Account/Transaction",
title = "Debit from Checking",
code = "transactionOpt.exists(t => accountOpt.exists(a => (a.accountType == \"CHECKING\" && t.transactionType.exists(_.contains(\"DEBIT\")))))",
description = "Check if debit transaction from checking account"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Bank/Account",
title = "Account Belongs to Bank",
code = "accountOpt.isDefined && bankOpt.isDefined && accountOpt.get.bankId == bankOpt.get.bankId.value",
description = "Check if account belongs to the bank"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Bank/Account",
title = "Account Currency Matches Bank Currency",
code = "accountOpt.exists(a => bankAttributes.exists(attr => attr.name == \"primary_currency\" && attr.value == a.currency))",
description = "Check if account currency matches bank's primary currency"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Transaction Request",
title = "Transaction Request for Account",
code = "transactionRequestOpt.exists(tr => accountOpt.exists(a => tr.this_account_id.value == a.accountId.value))",
description = "Check if transaction request is for this account"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Transaction Request",
title = "Transaction Request for Bank",
code = "transactionRequestOpt.exists(tr => bankOpt.exists(b => tr.this_bank_id.value == b.bankId.value))",
description = "Check if transaction request is for this bank"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Transaction Request",
title = "Transaction Amount Matches Charge",
code = "transactionOpt.isDefined && transactionRequestOpt.isDefined && transactionOpt.get.amount == transactionRequestOpt.get.charge.value.toDouble",
description = "Check if transaction amount matches request charge"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Attribute Comparisons",
title = "User and Account Same Tier",
code = "userAttributes.exists(ua => ua.name == \"tier\" && accountAttributes.exists(aa => aa.name == \"tier\" && ua.value == aa.value))",
description = "Check if user tier matches account tier"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Attribute Comparisons",
title = "Customer and Account Same Segment",
code = "customerAttributes.exists(ca => ca.name == \"segment\" && accountAttributes.exists(aa => aa.name == \"segment\" && ca.value == aa.value))",
description = "Check if customer segment matches account segment"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Attribute Comparisons",
title = "Auth User and Account Same Department",
code = "authenticatedUserAttributes.exists(ua => ua.name == \"department\" && accountAttributes.exists(aa => aa.name == \"department\" && ua.value == aa.value))",
description = "Check if authenticated user department matches account department"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Attribute Comparisons",
title = "Transaction Risk Within User Tolerance",
code = "transactionAttributes.exists(ta => ta.name == \"risk_score\" && userAttributes.exists(ua => ua.name == \"risk_tolerance\" && ta.value.toInt <= ua.value.toInt))",
description = "Check if transaction risk is within user's tolerance"
),
AbacRuleExampleJsonV600(
category = "Cross-Object - Attribute Comparisons",
title = "Bank and Customer Same Region",
code = "bankAttributes.exists(ba => ba.name == \"region\" && customerAttributes.exists(ca => ca.name == \"region\" && ba.value == ca.value))",
description = "Check if bank and customer are in same region"
),
AbacRuleExampleJsonV600(
category = "Complex - Multi-Object",
title = "Bank Employee with Active Account",
code = "authenticatedUser.emailAddress.endsWith(\"@bank.com\") && accountOpt.exists(_.balance > 0) && bankOpt.exists(_.bankId.value == \"gh.29.uk\")",
description = "Check if bank employee accessing active account at specific bank"
),
AbacRuleExampleJsonV600(
category = "Complex - Multi-Object",
title = "Manager Accessing Other User",
code = "authenticatedUserAttributes.exists(_.name == \"role\" && _.value == \"manager\") && userOpt.exists(_.userId != authenticatedUser.userId)",
description = "Check if manager is accessing another user's data"
),
AbacRuleExampleJsonV600(
category = "Complex - Multi-Object",
title = "Self or Authorized Delegation with Balance",
code = "(onBehalfOfUserOpt.isEmpty || onBehalfOfUserOpt.get.userId == authenticatedUser.userId) && accountOpt.exists(_.balance > 1000)",
description = "Check if self-access or authorized delegation with minimum balance"
),
AbacRuleExampleJsonV600(
category = "Complex - Multi-Object",
title = "Verified User with Optional Delegation",
code = "userAttributes.exists(_.name == \"kyc_status\" && _.value == \"verified\") && (onBehalfOfUserOpt.isEmpty || onBehalfOfUserAttributes.exists(_.name == \"authorized\"))",
description = "Check if user is KYC verified and delegation is authorized (if present)"
),
AbacRuleExampleJsonV600(
category = "Complex - Multi-Object",
title = "VIP Customer with Premium Account",
code = "customerAttributes.exists(_.name == \"vip_status\" && _.value == \"true\") && accountAttributes.exists(_.name == \"account_tier\" && _.value == \"premium\")",
description = "Check if VIP customer has premium account"
),
AbacRuleExampleJsonV600(
category = "Complex - Chained Validation",
title = "User-Customer-Account-Transaction Chain",
code = "userOpt.exists(u => customerOpt.exists(c => c.email == u.emailAddress && accountOpt.exists(a => transactionOpt.exists(t => t.accountId.value == a.accountId.value))))",
description = "Validate complete chain from user to customer to account to transaction"
),
AbacRuleExampleJsonV600(
category = "Complex - Chained Validation",
title = "Bank-Account-Transaction Request Chain",
code = "bankOpt.exists(b => accountOpt.exists(a => a.bankId == b.bankId.value && transactionRequestOpt.exists(tr => tr.this_account_id.value == a.accountId.value)))",
description = "Validate bank owns account and transaction request is for that account"
),
AbacRuleExampleJsonV600(
category = "Complex - Aggregation",
title = "Matching Attributes",
code = "authenticatedUserAttributes.exists(aua => userAttributes.exists(ua => aua.name == ua.name && aua.value == ua.value))",
description = "Check if authenticated user and target user share any matching attributes"
),
AbacRuleExampleJsonV600(
category = "Complex - Aggregation",
title = "Allowed Transaction Attributes",
code = "transactionAttributes.forall(ta => accountAttributes.exists(aa => aa.name == \"allowed_transaction_\" + ta.name))",
description = "Check if all transaction attributes are allowed for this account"
),
AbacRuleExampleJsonV600(
category = "Business Logic - Loan Approval",
title = "Credit Score and Balance Check",
code = "customerAttributes.exists(ca => ca.name == \"credit_score\" && ca.value.toInt > 650) && accountOpt.exists(_.balance > 5000)",
description = "Check if customer has good credit score and sufficient balance for loan"
),
AbacRuleExampleJsonV600(
category = "Business Logic - Wire Transfer",
title = "Wire Transfer Authorization",
code = "transactionOpt.exists(t => t.amount < 100000 && t.transactionType.exists(_.contains(\"WIRE\"))) && authenticatedUserAttributes.exists(_.name == \"wire_authorized\")",
description = "Check if wire transfer is under limit and user is authorized"
),
AbacRuleExampleJsonV600(
category = "Business Logic - Account Closure",
title = "Self-Service or Manager Account Closure",
code = "accountOpt.exists(a => (a.balance == 0 && userOpt.exists(_.userId == authenticatedUser.userId)) || authenticatedUserAttributes.exists(_.name == \"role\" && _.value == \"manager\"))",
description = "Allow account closure if zero balance self-service or manager override"
),
AbacRuleExampleJsonV600(
category = "Business Logic - VIP Processing",
title = "VIP Priority Check",
code = "(customerAttributes.exists(_.name == \"vip_status\" && _.value == \"true\") || accountAttributes.exists(_.name == \"account_tier\" && _.value == \"platinum\"))",
description = "Check if customer or account qualifies for VIP priority processing"
),
AbacRuleExampleJsonV600(
category = "Business Logic - Joint Account",
title = "Joint Account Access",
code = "accountOpt.exists(a => a.accountHolders.exists(h => h.userId == authenticatedUser.userId || h.emailAddress == authenticatedUser.emailAddress))",
description = "Check if authenticated user is one of the account holders"
)
),
available_operators = List(
"==", "!=", "&&", "||", "!", ">", "<", ">=", "<=",

View File

@ -410,10 +410,17 @@ case class AbacObjectTypeJsonV600(
properties: List[AbacObjectPropertyJsonV600]
)
case class AbacRuleExampleJsonV600(
category: String,
title: String,
code: String,
description: String
)
case class AbacRuleSchemaJsonV600(
parameters: List[AbacParameterJsonV600],
object_types: List[AbacObjectTypeJsonV600],
examples: List[String],
examples: List[AbacRuleExampleJsonV600],
available_operators: List[String],
notes: List[String]
)