Skip to content

Commit 8d36d13

Browse files
authored
Merge branch 'OpenBankProject:develop' into develop
2 parents 010684c + 1b5a36e commit 8d36d13

52 files changed

Lines changed: 2080 additions & 314 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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
.settings
99
.metals
1010
.vscode
11+
.zed
1112
.classpath
1213
.project
1314
.cache
@@ -30,8 +31,9 @@ obp-api/src/main/scala/code/api/v3_0_0/custom/
3031
marketing_diagram_generation/outputs/*
3132

3233
.bloop
34+
!.bloop/*.json
3335
.bsp
3436
.specstory
3537
project/project
3638
coursier
37-
*.code-workspace
39+
*.code-workspace

‎.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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,22 @@ OpenJDK 11 is available for download here: [https://jdk.java.net/archive/](https
6161
The project uses Maven 3 as its build tool.
6262

6363
To compile and run Jetty, install Maven 3, create your configuration in `obp-api/src/main/resources/props/default.props` and execute:
64+
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:
6465

6566
```sh
6667
mvn install -pl .,obp-commons && mvn jetty:run -pl obp-api
6768
```
6869

70+
### ZED IDE Setup
71+
72+
For ZED IDE users, we provide a complete development environment with Scala language server support:
73+
74+
```bash
75+
./zed/setup-zed-ide.sh
76+
```
77+
78+
This sets up automated build tasks, code navigation, and real-time error checking. See [`zed/README.md`](zed/README.md) for complete documentation.
79+
6980
In case the above command fails try the next one:
7081

7182
```sh
@@ -222,6 +233,7 @@ Once Postgres is installed (On macOS, use `brew`):
222233
1. Grant all on database `obpdb` to `obp`; (So OBP-API can create tables etc.)
223234

224235
#### For newer versions of postgres 16 and above, you need to follow the following instructions
236+
225237
-- Connect to the sandbox database
226238
\c sandbox;
227239

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,3 +1518,48 @@ regulated_entities = []
15181518

15191519

15201520
# 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+
}

‎obp-api/src/main/scala/code/api/OAuth2.scala‎

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -328,18 +328,26 @@ object OAuth2Login extends RestHelper with MdcLoggable {
328328
def getOrCreateResourceUser(idToken: String): Box[User] = {
329329
val uniqueIdGivenByProvider = JwtUtil.getSubject(idToken).getOrElse("")
330330
val provider = resolveProvider(idToken)
331-
Users.users.vend.getUserByProviderId(provider = provider, idGivenByProvider = uniqueIdGivenByProvider).or { // Find a user
332-
Users.users.vend.createResourceUser( // Otherwise create a new one
333-
provider = provider,
334-
providerId = Some(uniqueIdGivenByProvider),
335-
None,
336-
name = getClaim(name = "given_name", idToken = idToken).orElse(Some(uniqueIdGivenByProvider)),
337-
email = getClaim(name = "email", idToken = idToken),
338-
userId = None,
339-
createdByUserInvitationId = None,
340-
company = None,
341-
lastMarketingAgreementSignedDate = None
342-
)
331+
KeycloakFederatedUserReference.parse(uniqueIdGivenByProvider) match {
332+
case Right(fedRef) => // Users log on via Keycloak, which uses User Federation to access the external OBP database.
333+
logger.debug(s"External ID = ${fedRef.externalId}")
334+
logger.debug(s"Storage Provider ID = ${fedRef.storageProviderId}")
335+
Users.users.vend.getUserByResourceUserId(fedRef.externalId)
336+
case Left(error) =>
337+
logger.debug(s"Parse error: $error")
338+
Users.users.vend.getUserByProviderId(provider = provider, idGivenByProvider = uniqueIdGivenByProvider).or { // Find a user
339+
Users.users.vend.createResourceUser( // Otherwise create a new one
340+
provider = provider,
341+
providerId = Some(uniqueIdGivenByProvider),
342+
None,
343+
name = getClaim(name = "given_name", idToken = idToken).orElse(Some(uniqueIdGivenByProvider)),
344+
email = getClaim(name = "email", idToken = idToken),
345+
userId = None,
346+
createdByUserInvitationId = None,
347+
company = None,
348+
lastMarketingAgreementSignedDate = None
349+
)
350+
}
343351
}
344352
}
345353

@@ -526,7 +534,7 @@ object OAuth2Login extends RestHelper with MdcLoggable {
526534
*/
527535
override def wellKnownOpenidConfiguration: URI =
528536
new URI(
529-
APIUtil.getPropsValue(nameOfProperty = "oauth2.keycloak.well_known", "http://localhost:7070/realms/master/.well-known/openid-configuration")
537+
APIUtil.getPropsValue(nameOfProperty = "oauth2.keycloak.well_known", "http://localhost:8000/realms/master/.well-known/openid-configuration")
530538
)
531539
override def urlOfJwkSets: Box[String] = checkUrlOfJwkSets(identityProvider = keycloakHost)
532540
def isIssuer(jwt: String): Boolean = isIssuer(jwtToken=jwt, identityProvider = keycloakHost)

‎obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.openbankproject.commons.model._
2929
import com.openbankproject.commons.model.enums.TransactionRequestTypes._
3030
import com.openbankproject.commons.model.enums.{AttributeCategory, CardAttributeType, ChallengeType, TransactionRequestStatus}
3131
import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, ReflectUtils}
32+
import net.liftweb.common.Full
3233
import net.liftweb.json
3334

3435
import java.net.URLEncoder
@@ -4255,6 +4256,8 @@ object SwaggerDefinitionsJSON {
42554256
consent_reference_id = consentReferenceIdExample.value,
42564257
consumer_id = consumerIdExample.value,
42574258
created_by_user_id = userIdExample.value,
4259+
provider = Some(providerValueExample.value),
4260+
provider_id = Some(providerIdExample.value),
42584261
last_action_date = dateExample.value,
42594262
last_usage_date = dateTimeExample.value,
42604263
status = ConsentStatus.INITIATED.toString,
@@ -4282,7 +4285,7 @@ object SwaggerDefinitionsJSON {
42824285
consents = List(consentInfoJsonV510)
42834286
)
42844287

4285-
lazy val consentsJsonV510 = ConsentsJsonV510(List(allConsentJsonV510))
4288+
lazy val consentsJsonV510 = ConsentsJsonV510(number_of_rows = 1, List(allConsentJsonV510))
42864289

42874290
lazy val revokedConsentJsonV310 = ConsentJsonV310(
42884291
consent_id = "9d429899-24f5-42c8-8565-943ffa6a7945",

‎obp-api/src/main/scala/code/api/cache/Redis.scala‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,22 @@ object Redis extends MdcLoggable {
5757

5858
def jedisPoolDestroy: Unit = jedisPool.destroy()
5959

60+
def isRedisReady: Boolean = {
61+
var jedisConnection: Option[Jedis] = None
62+
try {
63+
jedisConnection = Some(jedisPool.getResource)
64+
val pong = jedisConnection.get.ping() // sends PING command
65+
pong == "PONG"
66+
} catch {
67+
case e: Throwable =>
68+
logger.error(s"Redis is not ready: ${e.getMessage}")
69+
false
70+
} finally {
71+
jedisConnection.foreach(_.close())
72+
}
73+
}
74+
75+
6076
private def configureSslContext(): SSLContext = {
6177

6278
// Load the CA certificate

0 commit comments

Comments
 (0)