Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5f7bbc3
ABAC Error message codes
simonredfern Jan 17, 2026
a9f42f9
Tagging Message Doc related endpoints.
simonredfern Jan 17, 2026
18d1884
Docfix: Added note about hasPersonalEntity in Dynamic Entity glossary
simonredfern Jan 20, 2026
0e9a69d
docfix: hasPersonalEntity flag note
simonredfern Jan 20, 2026
3a77043
docfix: Dynamic Linking glossary item.
simonredfern Jan 20, 2026
41a1506
Adding challengePurpose, challengeContextHash and
simonredfern Jan 20, 2026
3f85cb9
docfix: glossary Response format for GET /mydynamic-entities
simonredfern Jan 20, 2026
79c44db
Added v6.0.0 GET /my/dynamic-entities with explicit entity_name instead
simonredfern Jan 20, 2026
3f371cf
Adding Message Doc Json schema files
simonredfern Jan 20, 2026
d48c68f
added untracked_files
simonredfern Jan 20, 2026
ea266cd
Fixing new challenge fields
simonredfern Jan 20, 2026
ceba49c
Added /personal-dynamic-entities/available
simonredfern Jan 20, 2026
bfa3917
v6.0.0 of dynmaic endpoints with improved json
simonredfern Jan 20, 2026
7fdf8fa
Dynamic Entity definition to schema
simonredfern Jan 20, 2026
606f708
apiTagDynamicEntity
simonredfern Jan 20, 2026
afa7389
forcing lower_case entity names
simonredfern Jan 21, 2026
c0a0dfe
fix tricky behaviour with personal dynamic endpoints. /my dynamic
simonredfern Jan 21, 2026
aaf04ee
fixtest: GET my dynamic entity endpoint gets records the user created,
simonredfern Jan 21, 2026
7a27d3a
Schema Validation tests
simonredfern Jan 21, 2026
bb4e082
add context lines to build failures
Jan 21, 2026
191f867
docfix: Added Connector.User.Authentication
simonredfern Jan 21, 2026
0b63bfc
Test isolation
simonredfern Jan 21, 2026
b5282b4
fixing merge rebase v6.0.0 of dynmaic endpoints with improved json
simonredfern Jan 20, 2026
3462363
rebasefix: fixing errors introduced during rebase merging
simonredfern Jan 22, 2026
6466c8e
Updating frozen metadata for Challenge Commons (added 3 new fields, not
simonredfern Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build_container_develop_branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
echo "No maven-build.log found; skipping failure scan."
exit 0
fi
if grep -n "\*\*\* FAILED \*\*\*" maven-build.log; then
if grep -C 3 -n "\*\*\* FAILED \*\*\*" maven-build.log; then
echo "Failing tests detected above."
exit 1
else
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ project/project
coursier
metals.sbt
obp-http4s-runner/src/main/resources/git.properties
test-results
test-results
untracked_files/
2 changes: 2 additions & 0 deletions obp-api/src/main/scala/code/api/util/ApiTag.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ object ApiTag {
val apiTagFx = ResourceDocTag("FX")
val apiTagMessage = ResourceDocTag("Customer-Message")
val apiTagMetric = ResourceDocTag("Metric")
val apiTagMessageDoc = ResourceDocTag("Message-Doc")
val apiTagDocumentation = ResourceDocTag("Documentation")
val apiTagBerlinGroup = ResourceDocTag("Berlin-Group")
val apiTagSigningBaskets = ResourceDocTag("Signing Baskets")
Expand Down Expand Up @@ -105,6 +106,7 @@ object ApiTag {
val apiTagDynamic = ResourceDocTag("Dynamic")
val apiTagDynamicEntity = ResourceDocTag("Dynamic-Entity")
val apiTagManageDynamicEntity = ResourceDocTag("Dynamic-Entity-Manage")
val apiTagPersonalDynamicEntity = ResourceDocTag("Personal-Dynamic-Entity")
val apiTagDynamicEndpoint = ResourceDocTag("Dynamic-Endpoint")
val apiTagManageDynamicEndpoint = ResourceDocTag("Dynamic-Endpoint-Manage")

Expand Down
11 changes: 11 additions & 0 deletions obp-api/src/main/scala/code/api/util/ErrorMessages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ object ErrorMessages {
val DynamicDataNotFound = "OBP-09015: Dynamic Data not found. Please specify a valid value."
val DuplicateQueryParameters = "OBP-09016: Duplicate Query Parameters are not allowed."
val DuplicateHeaderKeys = "OBP-09017: Duplicate Header Keys are not allowed."
val InvalidDynamicEntityName = "OBP-09018: Invalid entity_name format. Entity names must be lowercase with underscores (snake_case), e.g. 'customer_preferences'. No uppercase letters or spaces allowed."


// General messages (OBP-10XXX)
Expand Down Expand Up @@ -649,6 +650,16 @@ object ErrorMessages {
val CannotGetUserInvitation = "OBP-37882: Cannot get user invitation."
val CannotFindUserInvitation = "OBP-37883: Cannot find user invitation."

// ABAC Rule related messages (OBP-38XXX)
val AbacRuleValidationFailed = "OBP-38001: ABAC rule validation failed. The rule code could not be validated."
val AbacRuleCompilationFailed = "OBP-38002: ABAC rule compilation failed. The rule code contains syntax errors or invalid Scala code."
val AbacRuleTypeMismatch = "OBP-38003: ABAC rule type mismatch. The rule code must return a Boolean value but returns a different type."
val AbacRuleSyntaxError = "OBP-38004: ABAC rule syntax error. The rule code contains invalid syntax."
val AbacRuleFieldReferenceError = "OBP-38005: ABAC rule field reference error. The rule code references fields or objects that do not exist."
val AbacRuleCodeEmpty = "OBP-38006: ABAC rule code must not be empty."
val AbacRuleNotFound = "OBP-38007: ABAC rule not found. Please specify a valid value for ABAC_RULE_ID."
val AbacRuleNotActive = "OBP-38008: ABAC rule is not active."
val AbacRuleExecutionFailed = "OBP-38009: ABAC rule execution failed. An error occurred while executing the rule."

// Transaction Request related messages (OBP-40XXX)
val InvalidTransactionRequestType = "OBP-40001: Invalid value for TRANSACTION_REQUEST_TYPE"
Expand Down
218 changes: 215 additions & 3 deletions obp-api/src/main/scala/code/api/util/Glossary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,119 @@ object Glossary extends MdcLoggable {
|"""
)


glossaryItems += GlossaryItem(
title = "Connector.User.Authentication",
description =
s"""
|### Overview
|
|The property `connector.user.authentication` (default: `false`) controls whether OBP can authenticate a user via the Connector when they are not found locally.
|
|OBP always checks for users locally first. When this property is enabled and a user is not found locally (or exists but is from an external provider), OBP will attempt to authenticate them against an external identity provider or Core Banking System (CBS) via the Connector.
|
|### Configuration
|
|In your props file:
|
|```
|connector.user.authentication=true
|```
|
|### Behavior When Enabled (true)
|
|**1. Login Authentication Flow:**
|
|When a user attempts to log in:
|
|```
|User Login Request
| │
| ▼
|┌─────────────────────────┐
|│ 1. Check if user exists │
|│ locally in OBP │
|└───────────┬─────────────┘
| │
| ┌────────┼────────┬─────────────────┐
| │ │ │ │
| ▼ ▼ ▼ ▼
|Found Found Found Not Found
|(local (external (external (and property
|provider) provider) provider enabled)
| │ property property │
| │ disabled) enabled) │
| │ │ │ │
| ▼ ▼ ▼ ▼
|┌────────┐ ┌────┐ ┌─────────────────────────┐
|│Check │ │Fail│ │ 2. Call Connector: │
|│local │ │ │ │ checkExternalUser │
|│password│ │ │ │ Credentials() │
|└───┬────┘ └────┘ └───────────┬─────────────┘
| │ │
| ▼ ┌────────┴────────┐
| Success/ │ │
| Failure ▼ ▼
| Success Failure
| │ │
| ▼ ▼
| ┌─────────────┐ ┌─────────────┐
| │Create local │ │Increment │
| │AuthUser if │ │bad login │
| │not exists │ │attempts │
| └─────────────┘ └─────────────┘
|```
|
|**2. Username Uniqueness Validation:**
|
|During user signup, OBP checks if the username already exists in the external system by calling `checkExternalUserExists()`.
|
|**3. Auto Creation of Local Users:**
|
|If external authentication succeeds but the user doesn't exist locally, OBP automatically creates a local `AuthUser` record linked to the external provider.
|
|### Behavior When Disabled (false, default)
|
|* Users must exist locally in OBP's database
|* Authentication is performed against locally stored credentials
|* No connector calls are made for authentication
|
|### Required Connector Methods
|
|When enabled, your Connector must implement:
|
|* ${messageDocLinkRabbitMQ("obp.checkExternalUserCredentials")} : Validates username and password against external system. Returns `InboundExternalUser` with user details (sub, iss, email, name, userAuthContexts).
|
|* ${messageDocLinkRabbitMQ("obp.checkExternalUserExists")} : Checks if a username exists in the external system. Used during signup validation.
|
|### InboundExternalUser Response
|
|The connector should return user information including:
|
|* `sub`: Subject identifier (username)
|* `iss`: Issuer (provider identifier)
|* `email`: User's email address
|* `name`: User's display name
|* `userAuthContexts`: Optional list of auth contexts (e.g., customer numbers)
|
|### Use Cases
|
|**Enable when:**
|* You have an external identity provider (LDAP, Active Directory, OAuth provider)
|* User credentials are managed by the Core Banking System
|* You want single sign on with an existing user directory
|
|**Disable when:**
|* OBP manages all user authentication locally
|* You're using OBP's built in user management
|* You don't have an external authentication system
|
|### Related Properties
|
|* `connector`: Specifies which connector implementation to use
|* `connector.user.authcontext.read.in.login`: Read user auth contexts during login
|
|"""
)



Expand Down Expand Up @@ -3152,6 +3264,35 @@ object Glossary extends MdcLoggable {
|
|OBP generates ONLY the regular endpoints. No 'my' endpoints are created. Use this when the entity represents shared data that should not be user-scoped.
|
|**Data Storage Differences:**
|
|Both personal and non-personal entities use the same database table (DynamicData), but the key difference is how user ownership is handled:
|
|When **hasPersonalEntity = true**:
|
|* Each record stores the UserId of the user who created it
|* The UserId is **actively used in all queries** to filter results
|* Users can only see, update, and delete their own records via 'my' endpoints
|* The 'my' endpoints **skip role checks** - user isolation provides the authorization
|* Cascade delete (deleting the entity definition and all data at once) is **not allowed**
|
|When **hasPersonalEntity = false**:
|
|* UserId may be stored for audit purposes but is **ignored in queries**
|* All authorized users see the same shared data
|* Role-based authorization is **required** (e.g., CanGetDynamicEntity_FooBar)
|* Cascade delete **is allowed** - you can delete the entity definition and all its records in one operation
|
|**Summary table:**
|
|| Feature | hasPersonalEntity=true | hasPersonalEntity=false |
||---------|------------------------|-------------------------|
|| Data visibility | Per-user (isolated) | Shared (all users) |
|| UserId in queries | Yes (filters results) | No (ignored) |
|| 'my' endpoints | Generated | Not generated |
|| Authorization | User-scoped (no roles needed for 'my' endpoints) | Role-based |
|| Cascade delete | Blocked | Allowed |
|
|**For bank-level entities**, endpoints include the bank ID:
|
|* POST /banks/BANK_ID/CustomerPreferences
Expand Down Expand Up @@ -3249,11 +3390,72 @@ object Glossary extends MdcLoggable {
|
|**Note:** If hasPersonalEntity is set to false, no 'my' endpoints are generated.
|
|**Management endpoints for Dynamic Entity definitions:**
|**Management endpoints for Dynamic Entity definitions (available from v4.0.0):**
|
|* GET /my/dynamic-entities - Get all Dynamic Entity definitions I created
|* PUT /my/dynamic-entities/DYNAMIC_ENTITY_ID - Update a definition I created
|
|**Discovery endpoint (available from v6.0.0):**
|
|* GET /personal-dynamic-entities/available - Discover all Dynamic Entities that support personal data storage
|
|This endpoint allows regular users (without admin roles) to discover which dynamic entities they can interact with for storing personal data via the /my/ENTITY_NAME endpoints. No special roles required - just needs to be logged in.
|
|**Response format for GET /my/dynamic-entities and GET /personal-dynamic-entities/available:**
|
|**v6.0.0 format (recommended):**
|
|The v6.0.0 response uses snake_case field names and an explicit `entity_name` field:
|
|```json
|{
| "dynamic_entities": [
| {
| "dynamic_entity_id": "abc-123-def",
| "entity_name": "CustomerPreferences",
| "user_id": "user-456",
| "bank_id": null,
| "has_personal_entity": true,
| "definition": {
| "description": "User preferences",
| "required": ["theme"],
| "properties": {
| "theme": {"type": "string"},
| "language": {"type": "string"}
| }
| }
| }
| ]
|}
|```
|
|**v4.0.0 format (legacy):**
|
|The v4.0.0 response uses camelCase field names and the **entity name is a dynamic key** (not a fixed property name):
|
|```json
|{
| "dynamic_entities": [
| {
| "CustomerPreferences": {
| "description": "User preferences",
| "required": ["theme"],
| "properties": {
| "theme": {"type": "string"},
| "language": {"type": "string"}
| }
| },
| "dynamicEntityId": "abc-123-def",
| "userId": "user-456",
| "hasPersonalEntity": true,
| "bankId": null
| }
| ]
|}
|```
|
|To extract the entity name from the v4.0.0 format programmatically, find the key that is NOT one of the standard properties: dynamicEntityId, userId, hasPersonalEntity, bankId.
|
|**Required roles:**
|
|* CanCreateSystemLevelDynamicEntity - To create system level dynamic entities
Expand Down Expand Up @@ -3571,7 +3773,17 @@ object Glossary extends MdcLoggable {
glossaryItems += GlossaryItem(
title = "Dynamic linking (PSD2 context)",
description =
s"""""".stripMargin)
s"""Dynamic linking is a security requirement under PSD2's Strong Customer Authentication (SCA) rules.
|
|When a payer initiates an electronic payment transaction, the authentication code must be dynamically linked to:
|
|1. **The amount** of the transaction
|2. **The payee** (recipient) of the transaction
|
|This means if either the amount or payee is modified after authentication, the authentication code becomes invalid. This protects against man-in-the-middle attacks where an attacker might try to redirect funds or change the payment amount after the user has authenticated.
|
|The requirement is specified in Article 97(2) of PSD2 and further detailed in the Regulatory Technical Standards (RTS) on SCA (Articles 5 and 6).
|""".stripMargin)

glossaryItems += GlossaryItem(
title = "TPP",
Expand Down
Loading