Skip to content

Commit f796169

Browse files
committed
Merge remote-tracking branch 'Simon/develop' into develop-Simon
# Conflicts: # .gitignore
2 parents 838a00d + 92870ea commit f796169

74 files changed

Lines changed: 3050 additions & 523 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
.settings
99
.metals
1010
.vscode
11+
*.code-workspace
12+
.zed
13+
.cursor
1114
.classpath
1215
.project
1316
.cache
@@ -30,9 +33,8 @@ obp-api/src/main/scala/code/api/v3_0_0/custom/
3033
marketing_diagram_generation/outputs/*
3134

3235
.bloop
36+
!.bloop/*.json
3337
.bsp
3438
.specstory
3539
project/project
36-
coursier
37-
*.code-workspace
38-
.cursor
40+
coursier

‎.metals-config.json‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"maven": {
3+
"enabled": true
4+
},
5+
"metals": {
6+
"serverVersion": "1.0.0",
7+
"javaHome": "/usr/lib/jvm/java-17-openjdk-amd64",
8+
"bloopVersion": "2.0.0",
9+
"superMethodLensesEnabled": true,
10+
"enableSemanticHighlighting": true,
11+
"compileOnSave": true,
12+
"testUserInterface": "Code Lenses",
13+
"inlayHints": {
14+
"enabled": true,
15+
"hintsInPatternMatch": {
16+
"enabled": true
17+
},
18+
"implicitArguments": {
19+
"enabled": true
20+
},
21+
"implicitConversions": {
22+
"enabled": true
23+
},
24+
"inferredTypes": {
25+
"enabled": true
26+
},
27+
"typeParameters": {
28+
"enabled": true
29+
}
30+
}
31+
},
32+
"buildTargets": [
33+
{
34+
"id": "obp-commons",
35+
"displayName": "obp-commons",
36+
"baseDirectory": "file:///home/marko/Tesobe/GitHub/constantine2nd/OBP-API/obp-commons/",
37+
"tags": ["library"],
38+
"languageIds": ["scala", "java"],
39+
"dependencies": [],
40+
"capabilities": {
41+
"canCompile": true,
42+
"canTest": true,
43+
"canRun": false,
44+
"canDebug": true
45+
},
46+
"dataKind": "scala",
47+
"data": {
48+
"scalaOrganization": "org.scala-lang",
49+
"scalaVersion": "2.12.20",
50+
"scalaBinaryVersion": "2.12",
51+
"platform": "jvm"
52+
}
53+
},
54+
{
55+
"id": "obp-api",
56+
"displayName": "obp-api",
57+
"baseDirectory": "file:///home/marko/Tesobe/GitHub/constantine2nd/OBP-API/obp-api/",
58+
"tags": ["application"],
59+
"languageIds": ["scala", "java"],
60+
"dependencies": ["obp-commons"],
61+
"capabilities": {
62+
"canCompile": true,
63+
"canTest": true,
64+
"canRun": true,
65+
"canDebug": true
66+
},
67+
"dataKind": "scala",
68+
"data": {
69+
"scalaOrganization": "org.scala-lang",
70+
"scalaVersion": "2.12.20",
71+
"scalaBinaryVersion": "2.12",
72+
"platform": "jvm"
73+
}
74+
}
75+
]
76+
}

‎README.md‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,22 @@ This project is dual licensed under the AGPL V3 (see NOTICE) and commercial lice
4646
The project uses Maven 3 as its build tool.
4747

4848
To compile and run Jetty, install Maven 3, create your configuration in `obp-api/src/main/resources/props/default.props` and execute:
49+
To compile and run Jetty, install Maven 3, create your configuration in `obp-api/src/main/resources/props/`, copy `sample.props.template` to `default.props` and edit the latter. Then:
4950

5051
```sh
5152
mvn install -pl .,obp-commons && mvn jetty:run -pl obp-api
5253
```
5354

55+
### ZED IDE Setup
56+
57+
For ZED IDE users, we provide a complete development environment with Scala language server support:
58+
59+
```bash
60+
./zed/setup-zed-ide.sh
61+
```
62+
63+
This sets up automated build tasks, code navigation, and real-time error checking. See [`zed/README.md`](zed/README.md) for complete documentation.
64+
5465
In case the above command fails try the next one:
5566

5667
```sh
@@ -206,6 +217,23 @@ Once Postgres is installed (On macOS, use `brew`):
206217

207218
1. Grant all on database `obpdb` to `obp`; (So OBP-API can create tables etc.)
208219

220+
#### For newer versions of postgres 16 and above, you need to follow the following instructions
221+
222+
-- Connect to the sandbox database
223+
\c sandbox;
224+
225+
-- Grant schema usage and creation privileges
226+
GRANT USAGE ON SCHEMA public TO obp;
227+
GRANT CREATE ON SCHEMA public TO obp;
228+
229+
-- Grant all privileges on existing tables (if any)
230+
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO obp;
231+
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO obp;
232+
233+
-- Grant privileges on future tables and sequences
234+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO obp;
235+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO obp;
236+
209237
1. Then, set the `db.url` in your Props:
210238

211239
```
@@ -638,6 +666,59 @@ allow_oauth2_login=true
638666
oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs
639667
```
640668

669+
### OAuth2 JWKS URI Configuration
670+
671+
The `oauth2.jwk_set.url` property is critical for OAuth2 JWT token validation. OBP-API uses this to verify the authenticity of JWT tokens by fetching the JSON Web Key Set (JWKS) from the specified URI(s).
672+
673+
#### Configuration Methods
674+
675+
The `oauth2.jwk_set.url` property is resolved in the following order of priority:
676+
677+
1. **Environment Variable**
678+
679+
```bash
680+
export OBP_OAUTH2_JWK_SET_URL="https://your-oidc-server.com/jwks"
681+
```
682+
683+
2. **Properties Files** (located in `obp-api/src/main/resources/props/`)
684+
- `production.default.props` (for production deployments)
685+
- `default.props` (for development)
686+
- `test.default.props` (for testing)
687+
688+
#### Supported Formats
689+
690+
- **Single URL**: `oauth2.jwk_set.url=http://localhost:9000/obp-oidc/jwks`
691+
- **Multiple URLs**: `oauth2.jwk_set.url=http://localhost:8080/jwk.json,https://www.googleapis.com/oauth2/v3/certs`
692+
693+
#### Common OAuth2 Provider Examples
694+
695+
- **Google**: `https://www.googleapis.com/oauth2/v3/certs`
696+
- **OBP-OIDC**: `http://localhost:9000/obp-oidc/jwks`
697+
- **Keycloak**: `http://localhost:7070/realms/master/protocol/openid-connect/certs`
698+
- **Azure AD**: `https://login.microsoftonline.com/common/discovery/v2.0/keys`
699+
700+
#### Troubleshooting OBP-20208 Error
701+
702+
If you encounter the error "OBP-20208: Cannot match the issuer and JWKS URI at this server instance", check the following:
703+
704+
1. **Verify JWT Issuer Claim**: The JWT token's `iss` (issuer) claim must match one of the configured identity providers
705+
2. **Check JWKS URL Configuration**: Ensure `oauth2.jwk_set.url` contains URLs that correspond to your JWT issuer
706+
3. **Case-Insensitive Matching**: OBP-API performs case-insensitive substring matching between the issuer and JWKS URLs
707+
4. **URL Format Consistency**: Check for trailing slashes or URL formatting differences
708+
709+
**Debug Logging**: Enable debug logging to see detailed information about the matching process:
710+
711+
```properties
712+
# Add to your logging configuration
713+
logger.code.api.OAuth2=DEBUG
714+
```
715+
716+
The debug logs will show:
717+
718+
- Expected identity provider vs actual JWT issuer claim
719+
- Available JWKS URIs from configuration
720+
- Matching logic results
721+
641722
---
642723

643724
## Frozen APIs

‎obp-api/src/main/resources/props/sample.props.template‎

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -652,13 +652,18 @@ defaultBank.bank_id=OBP
652652

653653

654654
################################################################################
655-
## Super Admin Users are used to boot strap User Entitlements (access to Roles).
656-
## Super Admins are receive **ONLY TWO** implicit entitlements which are:
655+
## Super Admin Users are used to boot-strap User Entitlements (access to Roles).
656+
## Super Admins listed below can grant them selves the following entitlements:
657657
## CanCreateEntitlementAtAnyBank
658658
## and
659659
## CanCreateEntitlementAtOneBank
660+
## After they have granted these roles, they can grant further roles and remove their
661+
# user_id from the super_admin_user_ids list because its redundant.
662+
## Once you have the roles above you can grant any other system or bank related roles to yourself.
663+
##
660664
## THUS, probably the first thing a Super Admin will do is to grant themselves or other users a number of Roles
661-
## For instance, a Super Admin *CANNOT delete an entitlement* unless they grant themselves CanDeleteEntitlementAtAnyBank or CanDeleteEntitlementAtOneBank
665+
## For instance, a Super Admin defined by their user_id in super_admin_user_ids CANNOT carry out actions unless they first give themselves an actual Entitlment to a Role.
666+
662667
## List the Users here, with their user_id(s), that should be Super Admins
663668
super_admin_user_ids=USER_ID1,USER_ID2,
664669
################################################################################
@@ -1491,6 +1496,8 @@ validate_iban=false
14911496
# sample props regulated_entities = [{"certificate_authority_ca_owner_id":"CY_CBC","entity_certificate_public_key":"-----BEGIN CERTIFICATE-----MIICsjCCAZqgAwIBAgIGAYwQ62R0MA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTAeFw0yMzExMjcxMzE1MTFaFw0yNTExMjYxMzE1MTFaMBoxGDAWBgNVBAMMD2FwcC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9WIodZHWzKyCcf9YfWEhPURbfO6zKuMqzHN27GdqHsVVEGxP4F/J4mso+0ENcRr6ur4u81iREaVdCc40rHDHVJNEtniD8Icbz7tcsqAewIVhc/q6WXGqImJpCq7hA0m247dDsaZT0lb/MVBiMoJxDEmAE/GYYnWTEn84R35WhJsMvuQ7QmLvNg6RkChY6POCT/YKe9NKwa1NqI1U+oA5RFzAaFtytvZCE3jtp+aR0brL7qaGfgxm6B7dEpGyhg0NcVCV7xMQNq2JxZTVdAr6lcsRGaAFulakmW3aNnmK+L35Wu8uW+OxNxwUuC6f3b4FVBa276FMuUTRfu7gc+k6kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAAU5CjEyAoyTn7PgFpQD48ZNPuUsEQ19gzYgJvHMzFIoZ7jKBodjO5mCzWBcR7A4mpeAsdyiNBl2sTiZscSnNqxk61jVzP5Ba1D7XtOjjr7+3iqowrThj6BY40QqhYh/6BSY9fDzVZQiHnvlo6ZUM5kUK6OavZOovKlp5DIl5sGqoP0qAJnpQ4nhB2WVVsKfPlOXc+2KSsbJ23g9l8zaTMr+X0umlvfEKqyEl1Fa2L1dO0y/KFQ+ILmxcZLpRdq1hRAjd0quq9qGC8ucXhRWDgM4hslVpau0da68g0aItWNez3mc5lB82b3dcZpFMzO41bgw7gvw10AvvTfQDqEYIuQ==-----END CERTIFICATE-----","entity_code":"PSD_PICY_CBC!12345","entity_type":"PSD_PI","entity_address":"EXAMPLE COMPANY LTD, 5 SOME STREET","entity_town_city":"SOME CITY","entity_post_code":"1060","entity_country":"CY","entity_web_site":"www.example.com","services":[{"CY":["PS_010","PS_020","PS_03C","PS_04C"]}]}]
14921497
regulated_entities = []
14931498

1499+
1500+
# Trusted Consumer pairs
14941501
#In OBP Create Consent if the app that is creating the consent (grantor_consumer_id) wants to create a consent for the grantee_consumer_id App, then we should skip SCA.
14951502
#The use case is API Explorer II giving a consent to Opey . In such a case API Explorer II and Opey are effectively the same App as far as the user is concerned.
14961503

@@ -1511,3 +1518,48 @@ regulated_entities = []
15111518

15121519

15131520
# Note: For secure and http only settings for cookies see resources/web.xml which is mentioned in the README.md
1521+
1522+
1523+
1524+
##########################################################
1525+
# Redis Logging #
1526+
##########################################################
1527+
## Enable Redis logging (true/false)
1528+
redis_logging_enabled = false
1529+
1530+
## Batch size for sending logs to Redis
1531+
## Smaller batch size reduces latency for logging critical messages.
1532+
redis_logging_batch_size = 50
1533+
1534+
## Flush interval for batch logs in milliseconds
1535+
## Flush every 500ms to keep Redis queues up-to-date without too much delay.
1536+
redis_logging_flush_interval_ms = 500
1537+
1538+
## Maximum number of retries for failed log writes
1539+
## Ensures transient Redis errors are retried before failing.
1540+
redis_logging_max_retries = 3
1541+
1542+
## Number of consecutive failures before opening circuit breaker
1543+
## Prevents hammering Redis when it is down.
1544+
redis_logging_circuit_breaker_threshold = 10
1545+
1546+
## Number of threads for asynchronous Redis operations
1547+
## Keep small for lightweight logging; adjust if heavy logging is expected.
1548+
redis_logging_thread_pool_size = 2
1549+
1550+
## SIX different FIFO Redis queues. Each queue has a maximum number of entries.
1551+
## These control how many messages are kept per log level.
1552+
## 1000 is a reasonable default; adjust if you expect higher traffic.
1553+
redis_logging_trace_queue_max_entries = 1000 # Max TRACE messages
1554+
redis_logging_debug_queue_max_entries = 1000 # Max DEBUG messages
1555+
redis_logging_info_queue_max_entries = 1000 # Max INFO messages
1556+
redis_logging_warning_queue_max_entries = 1000 # Max WARNING messages
1557+
redis_logging_error_queue_max_entries = 1000 # Max ERROR messages
1558+
redis_logging_all_queue_max_entries = 1000 # Max ALL messages
1559+
1560+
## Optional: Circuit breaker reset interval (ms)
1561+
## How long before retrying after circuit breaker opens. Default 60s.
1562+
redis_logging_circuit_breaker_reset_ms = 60000
1563+
##########################################################
1564+
# Redis Logging #
1565+
##########################################################

‎obp-api/src/main/scala/bootstrap/liftweb/CustomDBVendor.scala‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package bootstrap.liftweb
22

33
import code.api.util.APIUtil
4+
import code.util.Helper.MdcLoggable
45
import com.zaxxer.hikari.pool.ProxyConnection
56
import com.zaxxer.hikari.{HikariConfig, HikariDataSource}
67

@@ -21,19 +22,17 @@ import net.liftweb.util.Helpers.tryo
2122
class CustomDBVendor(driverName: String,
2223
dbUrl: String,
2324
dbUser: Box[String],
24-
dbPassword: Box[String]) extends CustomProtoDBVendor {
25-
26-
private val logger = Logger(classOf[CustomDBVendor])
25+
dbPassword: Box[String]) extends CustomProtoDBVendor with MdcLoggable {
2726

2827
object HikariDatasource {
2928
val config = new HikariConfig()
30-
29+
3130
val connectionTimeout = APIUtil.getPropsAsLongValue("hikari.connectionTimeout")
3231
val maximumPoolSize = APIUtil.getPropsAsIntValue("hikari.maximumPoolSize")
3332
val idleTimeout = APIUtil.getPropsAsLongValue("hikari.idleTimeout")
3433
val keepaliveTime = APIUtil.getPropsAsLongValue("hikari.keepaliveTime")
3534
val maxLifetime = APIUtil.getPropsAsLongValue("hikari.maxLifetime")
36-
35+
3736
if(connectionTimeout.isDefined){
3837
config.setConnectionTimeout(connectionTimeout.head)
3938
}
@@ -63,7 +62,7 @@ class CustomDBVendor(driverName: String,
6362
case _ =>
6463
config.setJdbcUrl(dbUrl)
6564
}
66-
65+
6766
config.addDataSourceProperty("cachePrepStmts", "true")
6867
config.addDataSourceProperty("prepStmtCacheSize", "250")
6968
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
@@ -79,8 +78,7 @@ class CustomDBVendor(driverName: String,
7978
def closeAllConnections_!(): Unit = HikariDatasource.ds.close()
8079
}
8180

82-
trait CustomProtoDBVendor extends ConnectionManager {
83-
private val logger = Logger(classOf[CustomProtoDBVendor])
81+
trait CustomProtoDBVendor extends ConnectionManager with MdcLoggable {
8482

8583
def createOne: Box[Connection]
8684

@@ -90,4 +88,4 @@ trait CustomProtoDBVendor extends ConnectionManager {
9088

9189
def releaseConnection(conn: Connection): Unit = {conn.asInstanceOf[ProxyConnection].close()}
9290

93-
}
91+
}

‎obp-api/src/main/scala/code/accountattribute/AccountAttribute.scala‎

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.openbankproject.commons.model.enums.AccountAttributeType
77
import com.openbankproject.commons.model.{AccountAttribute, AccountId, BankId, BankIdAccountId, ProductAttribute, ProductCode, ViewId}
88
import net.liftweb.common.{Box, Logger}
99
import net.liftweb.util.SimpleInjector
10+
import code.util.Helper.MdcLoggable
1011

1112
import scala.collection.immutable.List
1213
import scala.concurrent.Future
@@ -16,7 +17,7 @@ object AccountAttributeX extends SimpleInjector {
1617
val accountAttributeProvider = new Inject(buildOne _) {}
1718

1819
def buildOne: AccountAttributeProvider = MappedAccountAttributeProvider
19-
20+
2021
// Helper to get the count out of an option
2122
def countOfAccountAttribute(listOpt: Option[List[AccountAttribute]]): Int = {
2223
val count = listOpt match {
@@ -29,17 +30,15 @@ object AccountAttributeX extends SimpleInjector {
2930

3031
}
3132

32-
trait AccountAttributeProvider {
33-
34-
private val logger = Logger(classOf[AccountAttributeProvider])
33+
trait AccountAttributeProvider extends MdcLoggable {
3534

3635
def getAccountAttributesFromProvider(accountId: AccountId, productCode: ProductCode): Future[Box[List[AccountAttribute]]]
3736
def getAccountAttributesByAccount(bankId: BankId,
3837
accountId: AccountId): Future[Box[List[AccountAttribute]]]
39-
def getAccountAttributesByAccountCanBeSeenOnView(bankId: BankId,
40-
accountId: AccountId,
38+
def getAccountAttributesByAccountCanBeSeenOnView(bankId: BankId,
39+
accountId: AccountId,
4140
viewId: ViewId): Future[Box[List[AccountAttribute]]]
42-
def getAccountAttributesByAccountsCanBeSeenOnView(accounts: List[BankIdAccountId],
41+
def getAccountAttributesByAccountsCanBeSeenOnView(accounts: List[BankIdAccountId],
4342
viewId: ViewId): Future[Box[List[AccountAttribute]]]
4443

4544
def getAccountAttributeById(productAttributeId: String): Future[Box[AccountAttribute]]
@@ -58,10 +57,10 @@ trait AccountAttributeProvider {
5857
productCode: ProductCode,
5958
accountAttributes: List[ProductAttribute],
6059
productInstanceCode: Option[String]): Future[Box[List[AccountAttribute]]]
61-
60+
6261
def deleteAccountAttribute(accountAttributeId: String): Future[Box[Boolean]]
6362

6463
def getAccountIdsByParams(bankId: BankId, params: Map[String, List[String]]): Future[Box[List[String]]]
6564

6665
// End of Trait
67-
}
66+
}

0 commit comments

Comments
 (0)