{ "parameters": [ { "name": "authenticatedUser", "type": "User", "description": "The logged-in user (always present)", "required": true, "category": "User" }, { "name": "authenticatedUserAttributes", "type": "List[UserAttributeTrait]", "description": "Non-personal attributes of authenticated user", "required": true, "category": "User" }, { "name": "authenticatedUserAuthContext", "type": "List[UserAuthContext]", "description": "Auth context of authenticated user", "required": true, "category": "User" }, { "name": "onBehalfOfUserOpt", "type": "Option[User]", "description": "User being acted on behalf of (delegation)", "required": false, "category": "User" }, { "name": "onBehalfOfUserAttributes", "type": "List[UserAttributeTrait]", "description": "Attributes of delegation user", "required": false, "category": "User" }, { "name": "onBehalfOfUserAuthContext", "type": "List[UserAuthContext]", "description": "Auth context of delegation user", "required": false, "category": "User" }, { "name": "userOpt", "type": "Option[User]", "description": "Target user being evaluated", "required": false, "category": "User" }, { "name": "userAttributes", "type": "List[UserAttributeTrait]", "description": "Attributes of target user", "required": false, "category": "User" }, { "name": "bankOpt", "type": "Option[Bank]", "description": "Bank context", "required": false, "category": "Bank" }, { "name": "bankAttributes", "type": "List[BankAttributeTrait]", "description": "Bank attributes", "required": false, "category": "Bank" }, { "name": "accountOpt", "type": "Option[BankAccount]", "description": "Account context", "required": false, "category": "Account" }, { "name": "accountAttributes", "type": "List[AccountAttribute]", "description": "Account attributes", "required": false, "category": "Account" }, { "name": "transactionOpt", "type": "Option[Transaction]", "description": "Transaction context", "required": false, "category": "Transaction" }, { "name": "transactionAttributes", "type": "List[TransactionAttribute]", "description": "Transaction attributes", "required": false, "category": "Transaction" }, { "name": "transactionRequestOpt", "type": "Option[TransactionRequest]", "description": "Transaction request context", "required": false, "category": "TransactionRequest" }, { "name": "transactionRequestAttributes", "type": "List[TransactionRequestAttributeTrait]", "description": "Transaction request attributes", "required": false, "category": "TransactionRequest" }, { "name": "customerOpt", "type": "Option[Customer]", "description": "Customer context", "required": false, "category": "Customer" }, { "name": "customerAttributes", "type": "List[CustomerAttribute]", "description": "Customer attributes", "required": false, "category": "Customer" }, { "name": "callContext", "type": "Option[CallContext]", "description": "Request call context with metadata (IP, user agent, etc.)", "required": false, "category": "Context" } ], "object_types": [ { "name": "User", "description": "User object with profile and authentication information", "properties": [ { "name": "userId", "type": "String", "description": "Unique user ID" }, { "name": "emailAddress", "type": "String", "description": "User email address" }, { "name": "provider", "type": "String", "description": "Authentication provider (e.g., 'obp')" }, { "name": "name", "type": "String", "description": "User display name" }, { "name": "isDeleted", "type": "Option[Boolean]", "description": "Whether user is deleted" } ] }, { "name": "BankAccount", "description": "Bank account object", "properties": [ { "name": "accountId", "type": "AccountId", "description": "Account ID" }, { "name": "bankId", "type": "BankId", "description": "Bank ID" }, { "name": "accountType", "type": "String", "description": "Account type" }, { "name": "balance", "type": "BigDecimal", "description": "Account balance" }, { "name": "currency", "type": "String", "description": "Account currency" }, { "name": "label", "type": "String", "description": "Account label" } ] } ], "examples": [ { "category": "Authenticated User", "title": "Check Email Domain", "code": "authenticatedUser.emailAddress.contains(\"@example.com\")", "description": "Verify authenticated user's email belongs to a specific domain" }, { "category": "Authenticated User", "title": "Check Provider", "code": "authenticatedUser.provider == \"obp\"", "description": "Verify the authentication provider is OBP" }, { "category": "Authenticated User", "title": "User Not Deleted", "code": "!authenticatedUser.isDeleted.getOrElse(false)", "description": "Ensure the authenticated user account is not marked as deleted" }, { "category": "Authenticated User Attributes", "title": "Admin Role Check", "code": "authenticatedUserAttributes.exists(attr => attr.name == \"role\" && attr.value == \"admin\")", "description": "Check if authenticated user has admin role attribute" }, { "category": "Authenticated User Attributes", "title": "Department Check", "code": "authenticatedUserAttributes.find(_.name == \"department\").exists(_.value == \"finance\")", "description": "Check if user belongs to finance department" }, { "category": "Authenticated User Attributes", "title": "Multiple Role Check", "code": "authenticatedUserAttributes.exists(attr => attr.name == \"role\" && List(\"admin\", \"manager\", \"supervisor\").contains(attr.value))", "description": "Check if user has any of the specified management roles" }, { "category": "Target User", "title": "Self Access", "code": "userOpt.exists(_.userId == authenticatedUser.userId)", "description": "Check if target user is the authenticated user (self-access)" }, { "category": "Target User", "title": "Provider Match", "code": "userOpt.exists(_.provider == \"obp\")", "description": "Verify target user uses OBP provider" }, { "category": "Target User", "title": "Trusted Domain", "code": "userOpt.exists(_.emailAddress.endsWith(\"@trusted.com\"))", "description": "Check if target user's email is from trusted domain" }, { "category": "User Attributes", "title": "Premium Account Type", "code": "userAttributes.exists(attr => attr.name == \"account_type\" && attr.value == \"premium\")", "description": "Check if target user has premium account type attribute" }, { "category": "User Attributes", "title": "KYC Verified", "code": "userAttributes.exists(attr => attr.name == \"kyc_status\" && attr.value == \"verified\")", "description": "Verify target user has completed KYC verification" }, { "category": "User Attributes", "title": "Minimum Tier Level", "code": "userAttributes.find(_.name == \"tier\").exists(_.value.toInt >= 2)", "description": "Check if user's tier level is 2 or higher" }, { "category": "Account", "title": "Balance Threshold", "code": "accountOpt.exists(_.balance > 1000)", "description": "Check if account balance exceeds threshold" }, { "category": "Account", "title": "Currency and Balance", "code": "accountOpt.exists(acc => acc.currency == \"USD\" && acc.balance > 5000)", "description": "Check account has USD currency and balance over 5000" }, { "category": "Account", "title": "Savings Account Type", "code": "accountOpt.exists(_.accountType == \"SAVINGS\")", "description": "Verify account is a savings account" }, { "category": "Account Attributes", "title": "Active Status", "code": "accountAttributes.exists(attr => attr.name == \"status\" && attr.value == \"active\")", "description": "Check if account status is active" }, { "category": "Transaction", "title": "Amount Limit", "code": "transactionOpt.exists(_.amount < 10000)", "description": "Check transaction amount is below limit" }, { "category": "Transaction", "title": "Transfer Type", "code": "transactionOpt.exists(_.transactionType.contains(\"TRANSFER\"))", "description": "Verify transaction is a transfer type" }, { "category": "Customer", "title": "Email Matches User", "code": "customerOpt.exists(_.email == authenticatedUser.emailAddress)", "description": "Verify customer email matches authenticated user" }, { "category": "Customer", "title": "Active Relationship", "code": "customerOpt.exists(_.relationshipStatus == \"ACTIVE\")", "description": "Check customer has active relationship status" }, { "category": "Object Comparisons - User", "title": "Self Access by User ID", "code": "userOpt.exists(_.userId == authenticatedUser.userId)", "description": "Verify target user ID matches authenticated user (self-access)" }, { "category": "Object Comparisons - User", "title": "Same Email Domain", "code": "userOpt.exists(u => authenticatedUser.emailAddress.split(\"@\")(1) == u.emailAddress.split(\"@\")(1))", "description": "Check both users share the same email domain" }, { "category": "Object Comparisons - Customer/User", "title": "Customer Email Matches Target User", "code": "customerOpt.exists(c => userOpt.exists(u => c.email == u.emailAddress))", "description": "Verify customer email matches target user" }, { "category": "Object Comparisons - Account/Transaction", "title": "Transaction Within Balance", "code": "transactionOpt.exists(t => accountOpt.exists(a => t.amount < a.balance))", "description": "Verify transaction amount is less than account balance" }, { "category": "Object Comparisons - Account/Transaction", "title": "Currency Match", "code": "transactionOpt.exists(t => accountOpt.exists(a => t.currency == a.currency))", "description": "Verify transaction currency matches account currency" }, { "category": "Object Comparisons - Account/Transaction", "title": "No Overdraft", "code": "transactionOpt.exists(t => accountOpt.exists(a => a.balance - t.amount >= 0))", "description": "Ensure transaction won't overdraw account" }, { "category": "Object Comparisons - Attributes", "title": "User Tier Matches Account Tier", "code": "userAttributes.exists(ua => ua.name == \"tier\" && accountAttributes.exists(aa => aa.name == \"tier\" && ua.value == aa.value))", "description": "Verify user tier level matches account tier level" }, { "category": "Object Comparisons - Attributes", "title": "Department Match", "code": "authenticatedUserAttributes.exists(ua => ua.name == \"department\" && accountAttributes.exists(aa => aa.name == \"department\" && ua.value == aa.value))", "description": "Verify user department matches account department" }, { "category": "Object Comparisons - Attributes", "title": "Risk Tolerance Check", "code": "transactionAttributes.exists(ta => ta.name == \"risk_score\" && userAttributes.exists(ua => ua.name == \"risk_tolerance\" && ta.value.toInt <= ua.value.toInt))", "description": "Check transaction risk score is within user's risk tolerance" }, { "category": "Complex Scenarios", "title": "Trusted Employee Access", "code": "authenticatedUser.emailAddress.endsWith(\"@bank.com\") && accountOpt.exists(_.balance > 0) && bankOpt.exists(_.bankId.value == \"gh.29.uk\")", "description": "Allow bank employees to access accounts with positive balance at specific bank" }, { "category": "Complex Scenarios", "title": "Manager Accessing Team Data", "code": "authenticatedUserAttributes.exists(_.name == \"role\" && _.value == \"manager\") && userOpt.exists(_.userId != authenticatedUser.userId)", "description": "Allow managers to access other users' data" }, { "category": "Complex Scenarios", "title": "Delegation with Balance Check", "code": "(onBehalfOfUserOpt.isEmpty || onBehalfOfUserOpt.exists(_.userId == authenticatedUser.userId)) && accountOpt.exists(_.balance > 1000)", "description": "Allow self-access or no delegation with minimum balance requirement" }, { "category": "Complex Scenarios", "title": "VIP with Premium Account", "code": "customerAttributes.exists(_.name == \"vip_status\" && _.value == \"true\") && accountAttributes.exists(_.name == \"account_tier\" && _.value == \"premium\")", "description": "Check for VIP customer with premium account combination" }, { "category": "Chained Validation", "title": "Full Customer 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: User → Customer → Account → Transaction" }, { "category": "Chained Validation", "title": "Bank to 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 chain: Bank → Account → Transaction Request" }, { "category": "Business Logic", "title": "Loan Approval", "code": "customerAttributes.exists(ca => ca.name == \"credit_score\" && ca.value.toInt > 650) && accountOpt.exists(_.balance > 5000)", "description": "Check credit score above 650 and minimum balance for loan approval" }, { "category": "Business Logic", "title": "Wire Transfer Authorization", "code": "transactionOpt.exists(t => t.amount < 100000 && t.transactionType.exists(_.contains(\"WIRE\"))) && authenticatedUserAttributes.exists(_.name == \"wire_authorized\")", "description": "Verify user is authorized for wire transfers under limit" }, { "category": "Business Logic", "title": "Joint Account Access", "code": "accountOpt.exists(a => a.accountHolders.exists(h => h.userId == authenticatedUser.userId || h.emailAddress == authenticatedUser.emailAddress))", "description": "Allow access if user is one of the joint account holders" }, { "category": "Safe Patterns", "title": "Pattern Matching", "code": "userOpt match { case Some(u) => u.userId == authenticatedUser.userId case None => false }", "description": "Safe Option handling using pattern matching" }, { "category": "Safe Patterns", "title": "Using exists()", "code": "accountOpt.exists(_.balance > 0)", "description": "Safe way to check Option value using exists method" }, { "category": "Safe Patterns", "title": "Using forall()", "code": "userOpt.forall(!_.isDeleted.getOrElse(false))", "description": "Safe negative condition using forall (returns true if None)" }, { "category": "Safe Patterns", "title": "Using map() with getOrElse()", "code": "accountOpt.map(_.balance).getOrElse(0) > 100", "description": "Safe value extraction with default using map and getOrElse" }, { "category": "Common Mistakes", "title": "WRONG - Unsafe get()", "code": "accountOpt.get.balance > 1000", "description": "❌ WRONG: Using .get without checking isDefined (can throw exception)" }, { "category": "Common Mistakes", "title": "CORRECT - Safe exists()", "code": "accountOpt.exists(_.balance > 1000)", "description": "✅ CORRECT: Safe way to check account balance using exists()" } ], "available_operators": [ "==", "!=", "&&", "||", "!", ">", "<", ">=", "<=", "contains", "startsWith", "endsWith", "isDefined", "isEmpty", "nonEmpty", "exists", "forall", "find", "filter", "get", "getOrElse" ], "notes": [ "PARAMETER NAMES: Use authenticatedUser, userOpt, accountOpt, bankOpt, transactionOpt, etc. (NOT user, account, bank)", "PROPERTY NAMES: Use camelCase - userId (NOT user_id), accountId (NOT account_id), emailAddress (NOT email_address)", "OPTION TYPES: Only authenticatedUser is guaranteed to exist. All others are Option types - check isDefined before using .get", "ATTRIBUTES: All attributes are Lists - use Scala collection methods like exists(), find(), filter()", "SAFE OPTION HANDLING: Use pattern matching: userOpt match { case Some(u) => u.userId == ... case None => false }", "RETURN TYPE: Rule must return Boolean - true = access granted, false = access denied", "AUTO-FETCHING: Objects are automatically fetched based on IDs passed to execute endpoint", "COMMON MISTAKE: Writing 'user.user_id' instead of 'userOpt.get.userId' or 'authenticatedUser.userId'" ] }