OBP-API/ideas/obp-abac-schema-endpoint-response-example.json

506 lines
18 KiB
JSON
Raw Permalink Normal View History

{
"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'"
]
}