Changed from full timestamp to hour-only format to match implementation. OLD: /active-rate-limits/2025-12-31T13:34:46Z (YYYY-MM-DDTHH:MM:SSZ) NEW: /active-rate-limits/2025-12-31-13 (YYYY-MM-DD-HH) Benefits: - API now matches actual implementation (hour-level caching) - Eliminates timezone/minute truncation confusion - Clearer semantics: 'active during this hour' not 'at this second' - Direct cache key mapping improves performance - Simpler date parsing (no timezone handling needed) Files changed: - APIMethods600.scala: Updated endpoint and date parsing - RateLimitsTest.scala: Updated all test cases to new format - Glossary.scala: Updated API documentation - introductory_system_documentation.md: Updated user docs This is a breaking change but necessary to align API with implementation. Rate limits are cached and queried at hour granularity, so the API should reflect that reality.
15 KiB
OBP ABAC Structured Examples Implementation Plan
Goal
Convert the ABAC rule schema examples from simple strings to structured objects with:
category: String - Grouping/category of the exampletitle: String - Short descriptive titlecode: String - The actual Scala code exampledescription: String - Detailed explanation of what the code does
Example Structure
{
"category": "User Attributes",
"title": "Account Type Check",
"code": "userAttributes.exists(attr => attr.name == \"account_type\" && attr.value == \"premium\")",
"description": "Check if target user has premium account type attribute"
}
Implementation Steps
Step 1: Update JSON Case Class
File: OBP-API/obp-api/src/main/scala/code/api/v6_0_0/JSONFactory6.0.0.scala
Current code (around line 413-419):
case class AbacRuleSchemaJsonV600(
parameters: List[AbacParameterJsonV600],
object_types: List[AbacObjectTypeJsonV600],
examples: List[String],
available_operators: List[String],
notes: List[String]
)
Change to:
case class AbacRuleExampleJsonV600(
category: String,
title: String,
code: String,
description: String
)
case class AbacRuleSchemaJsonV600(
parameters: List[AbacParameterJsonV600],
object_types: List[AbacObjectTypeJsonV600],
examples: List[AbacRuleExampleJsonV600], // Changed from List[String]
available_operators: List[String],
notes: List[String]
)
Step 2: Update API Endpoint
File: OBP-API/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala
Location: The getAbacRuleSchema endpoint (around line 4891-5070)
Find this line (around line 5021):
examples = List(
Replace the entire examples List with structured examples.
See the comprehensive list in Section 3 below.
Step 3: Structured Examples List
Replace the examples = List(...) with this:
examples = List(
// === Authenticated User Examples ===
AbacRuleExampleJsonV600(
category = "Authenticated User",
title = "Check Email Domain",
code = """authenticatedUser.emailAddress.contains("@example.com")""",
description = "Verify authenticated user's email belongs to a specific domain"
),
AbacRuleExampleJsonV600(
category = "Authenticated User",
title = "Check Provider",
code = """authenticatedUser.provider == "obp"""",
description = "Verify the authentication provider is OBP"
),
AbacRuleExampleJsonV600(
category = "Authenticated User",
title = "User Not Deleted",
code = """!authenticatedUser.isDeleted.getOrElse(false)""",
description = "Ensure the authenticated user account is not marked as deleted"
),
// === Authenticated User Attributes ===
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
category = "Authenticated User Attributes",
title = "Department Check",
code = """authenticatedUserAttributes.find(_.name == "department").exists(_.value == "finance")""",
description = "Check if user belongs to finance department"
),
AbacRuleExampleJsonV600(
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"
),
// === Target User Examples ===
AbacRuleExampleJsonV600(
category = "Target User",
title = "Self Access",
code = """userOpt.exists(_.userId == authenticatedUser.userId)""",
description = "Check if target user is the authenticated user (self-access)"
),
AbacRuleExampleJsonV600(
category = "Target 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"
),
// === User Attributes ===
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
// === Account Examples ===
AbacRuleExampleJsonV600(
category = "Account",
title = "Balance Threshold",
code = """accountOpt.exists(_.balance > 1000)""",
description = "Check if account balance exceeds threshold"
),
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
category = "Account",
title = "Savings Account Type",
code = """accountOpt.exists(_.accountType == "SAVINGS")""",
description = "Verify account is a savings account"
),
// === Transaction Examples ===
AbacRuleExampleJsonV600(
category = "Transaction",
title = "Amount Limit",
code = """transactionOpt.exists(_.amount < 10000)""",
description = "Check transaction amount is below limit"
),
AbacRuleExampleJsonV600(
category = "Transaction",
title = "Transfer Type",
code = """transactionOpt.exists(_.transactionType.contains("TRANSFER"))""",
description = "Verify transaction is a transfer type"
),
// === Customer Examples ===
AbacRuleExampleJsonV600(
category = "Customer",
title = "Email Matches User",
code = """customerOpt.exists(_.email == authenticatedUser.emailAddress)""",
description = "Verify customer email matches authenticated user"
),
AbacRuleExampleJsonV600(
category = "Customer",
title = "Active Relationship",
code = """customerOpt.exists(_.relationshipStatus == "ACTIVE")""",
description = "Check customer has active relationship status"
),
// === Object-to-Object Comparisons ===
AbacRuleExampleJsonV600(
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)"
),
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
// === Attribute Cross-Comparisons ===
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
// === Complex Multi-Object Examples ===
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
// === Chained Validation ===
AbacRuleExampleJsonV600(
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"
),
// === Real-World Business Logic ===
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
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"
),
// === Safe Option Handling ===
AbacRuleExampleJsonV600(
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"
),
AbacRuleExampleJsonV600(
category = "Safe Patterns",
title = "Using exists()",
code = """accountOpt.exists(_.balance > 0)""",
description = "Safe way to check Option value using exists method"
),
AbacRuleExampleJsonV600(
category = "Safe Patterns",
title = "Using forall()",
code = """userOpt.forall(!_.isDeleted.getOrElse(false))""",
description = "Safe negative condition using forall (returns true if None)"
),
// === Error Prevention ===
AbacRuleExampleJsonV600(
category = "Common Mistakes",
title = "WRONG - Unsafe get()",
code = """accountOpt.get.balance > 1000""",
description = "❌ WRONG: Using .get without checking isDefined (can throw exception)"
),
AbacRuleExampleJsonV600(
category = "Common Mistakes",
title = "CORRECT - Safe exists()",
code = """accountOpt.exists(_.balance > 1000)""",
description = "✅ CORRECT: Safe way to check account balance using exists()"
)
),
Benefits of Structured Examples
1. Better UI/UX
- Examples can be grouped by category in the UI
- Searchable by title or description
- Code can be syntax highlighted separately
- Easier to filter and navigate
2. Better for AI/LLM Integration
- Clear structure for AI to understand
- Category helps with semantic search
- Description provides context for code generation
- Title provides quick summary
3. Better for Documentation
- Can generate categorized documentation automatically
- Can create searchable example libraries
- Easier to maintain and update
- Better for auto-completion in IDEs
4. API Response Example
Before (flat strings):
{
"examples": [
"// Check if authenticated user matches target user",
"authenticatedUser.userId == userOpt.get.userId"
]
}
After (structured):
{
"examples": [
{
"category": "Target User",
"title": "Self Access",
"code": "userOpt.exists(_.userId == authenticatedUser.userId)",
"description": "Check if target user is the authenticated user (self-access)"
}
]
}
Testing
After implementation, test:
- API Response: Call
GET /obp/v6.0.0/management/abac-rules-schemaand verify JSON structure - Compilation: Ensure Scala code compiles without errors
- Frontend: Update any frontend code that consumes this endpoint
- Backward Compatibility: Consider if any clients depend on the old string format
Rollout Strategy
Option A: Breaking Change (Recommended)
- Implement in v6.0.0 as shown above
- Document as breaking change in release notes
- Provide migration guide for clients
Option B: Maintain Backward Compatibility
- Add new field
structured_examplesalongside existingexamples - Keep old
examplesas List[String] with just the code - Deprecate old field, remove in v7.0.0
Full Example Count
The implementation should include approximately 60-80 structured examples covering:
- 3-4 examples per parameter (19 parameters) = ~60 examples
- 10-15 object-to-object comparison examples
- 5-10 complex multi-object scenarios
- 5 real-world business logic examples
- 4-5 safe pattern examples
- 2-3 error prevention examples
Total: ~80-100 examples
Notes
- Use triple quotes
"""for code strings to avoid escaping issues - Keep code examples concise but realistic
- Ensure all examples are valid Scala syntax
- Test examples can actually compile/execute
- Categories should be consistent and logical
- Descriptions should explain the "why" not just the "what"
Related Files
- Enhancement spec:
obp-abac-schema-examples-enhancement.md - Implementation summary (after):
obp-abac-schema-examples-implementation-summary.md
Status: Ready for Implementation
Priority: Medium
Estimated Effort: 2-3 hours
Version: OBP API v6.0.0