mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 11:06:49 +00:00
Adding ABAC rules ideas
This commit is contained in:
parent
220007ee61
commit
45f55f1ac1
@ -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(
|
||||
"==", "!=", "&&", "||", "!", ">", "<", ">=", "<=",
|
||||
|
||||
@ -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]
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user