Merge remote-tracking branch 'Simon/develop' into feature/cardanoTest

# Conflicts:
#	.gitignore
#	obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala
This commit is contained in:
hongwei 2025-08-25 09:46:36 +02:00
commit d6c01e4d62
6 changed files with 846 additions and 277 deletions

4
.gitignore vendored
View File

@ -25,6 +25,10 @@ obp-api/src/main/scala/code/api/v3_0_0/custom/
/obp-api2/
/.java-version
.scannerwork
# Marketing diagram generation outputs
marketing_diagram_generation/outputs/*
.bloop
.bsp
.specstory

368
README.md
View File

@ -8,13 +8,13 @@ The Open Bank Project is an open-source API for banks that enables account holde
The OBP API supports transparency options (enabling account holders to share configurable views of their transaction data with trusted individuals and even the public), data blurring (to preserve sensitive information) and data enrichment (enabling users to add tags, comments and images to transactions).
The OBP API abstracts away the peculiarities of each core banking system so that a wide range of apps can interact with multiple banks on behalf of the account holder. We want to raise the bar of financial transparency and enable a rich ecosystem of innovative financial applications and services.
The OBP API abstracts away the peculiarities of each core banking system so that a wide range of apps can interact with multiple banks on behalf of the account holder. We want to raise the bar of financial transparency and enable a rich ecosystem of innovative financial applications and services.
Our tagline is: "Bank as a Platform. Transparency as an Asset".
The API supports [OAuth 1.0a](https://apiexplorer-ii-sandbox.openbankproject.com/glossary#OAuth%201.0a), [OAuth 2](https://apiexplorer-ii-sandbox.openbankproject.com/glossary#OAuth%202), [OpenID Connect OIDC](https://apiexplorer-ii-sandbox.openbankproject.com/glossary#OAuth%202%20with%20Google) and other authentication methods including [Direct Login](https://apiexplorer-ii-sandbox.openbankproject.com/glossary#Direct%20Login).
## Documentation
## Documentation
The API documentation is best viewed using the [OBP API Explorer](https://apiexplorer-ii-sandbox.openbankproject.com) or a third-party tool that has imported the OBP Swagger definitions.
@ -26,15 +26,15 @@ OBP instances support multiple versions of the API simultaneously (unless they a
To see the status (DRAFT, STABLE or BLEEDING-EDGE) of an API version, look at the root endpoint. For example, `/obp/v2.0.0/root` or `/obp/v3.0.0/root`.
```log
24.01.2017, [V1.2.1](https://apisandbox.openbankproject.com/obp/v1.2.1/root) was marked as stable.
24.01.2017, [V1.3.0](https://apisandbox.openbankproject.com/obp/v1.3.0/root) was marked as stable.
08.06.2017, [V2.0.0](https://apisandbox.openbankproject.com/obp/v2.0.0/root) was marked as stable.
27.10.2018, [V2.1.0](https://apisandbox.openbankproject.com/obp/v2.1.0/root) was marked as stable.
27.10.2018, [V2.2.0](https://apisandbox.openbankproject.com/obp/v2.2.0/root) was marked as stable.
18.11.2020, [V3.0.0](https://apisandbox.openbankproject.com/obp/v3.0.0/root) was marked as stable.
18.11.2020, [V3.1.0](https://apisandbox.openbankproject.com/obp/v3.1.0/root) was marked as stable.
16.12.2022, [V4.0.0](https://apisandbox.openbankproject.com/obp/v4.0.0/root) was marked as stable.
16.12.2022, [V5.0.0](https://apisandbox.openbankproject.com/obp/v5.0.0/root) was marked as stable.
24.01.2017, [V1.2.1](https://apisandbox.openbankproject.com/obp/v1.2.1/root) was marked as stable.
24.01.2017, [V1.3.0](https://apisandbox.openbankproject.com/obp/v1.3.0/root) was marked as stable.
08.06.2017, [V2.0.0](https://apisandbox.openbankproject.com/obp/v2.0.0/root) was marked as stable.
27.10.2018, [V2.1.0](https://apisandbox.openbankproject.com/obp/v2.1.0/root) was marked as stable.
27.10.2018, [V2.2.0](https://apisandbox.openbankproject.com/obp/v2.2.0/root) was marked as stable.
18.11.2020, [V3.0.0](https://apisandbox.openbankproject.com/obp/v3.0.0/root) was marked as stable.
18.11.2020, [V3.1.0](https://apisandbox.openbankproject.com/obp/v3.1.0/root) was marked as stable.
16.12.2022, [V4.0.0](https://apisandbox.openbankproject.com/obp/v4.0.0/root) was marked as stable.
16.12.2022, [V5.0.0](https://apisandbox.openbankproject.com/obp/v5.0.0/root) was marked as stable.
```
## License
@ -57,46 +57,73 @@ In case the above command fails try the next one:
export MAVEN_OPTS="-Xss128m" && mvn install -pl .,obp-commons && mvn jetty:run -pl obp-api
```
Note: depending on your Java version you might need to do this in the OBP-API directory.
This creates a .mvn/jvm.config File
```sh
mkdir -p .mvn
cat > .mvn/jvm.config << 'EOF'
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/java.util.jar=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
EOF
```
Then try the above command.
Or use this approach:
```sh
export MAVEN_OPTS="-Xss128m \
--add-opens=java.base/java.util.jar=ALL-UNNAMED \
--add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"
```
[Note: How to run via IntelliJ IDEA](obp-api/src/main/docs/glossary/Run_via_IntelliJ_IDEA.md)
## Run some tests
* In `obp-api/src/main/resources/props` create a `test.default.props` for tests. Set `connector=mapped`.
* Run a single test. For instance, right-click on `obp-api/test/scala/code/branches/MappedBranchProviderTest` and select "Run Mapp"...
- In `obp-api/src/main/resources/props` create a `test.default.props` for tests. Set `connector=mapped`.
* Run multiple tests: Right-click on `obp-api/test/scala/code` and select Run. If need be:
- Run a single test. For instance, right-click on `obp-api/test/scala/code/branches/MappedBranchProviderTest` and select "Run Mapp"...
Goto Run / Debug configurations
Test Kind: Select All in Package
Package: Select code
Add the absolute /path-to-your-OBP-API in the "working directory" field
You might need to assign more memory via VM Options. For example:
- Run multiple tests: Right-click on `obp-api/test/scala/code` and select Run. If need be:
```
-Xmx1512M -XX:MaxPermSize=512M
```
Goto Run / Debug configurations
Test Kind: Select All in Package
Package: Select code
Add the absolute /path-to-your-OBP-API in the "working directory" field
You might need to assign more memory via VM Options. For example:
or
```
-Xmx1512M -XX:MaxPermSize=512M
```
```
-Xmx2048m -Xms1024m -Xss2048k -XX:MaxPermSize=1024m
```
Ensure your `test.default.props` has the minimum settings (see `test.default.props.template`).
or
Right-click `obp-api/test/scala/code` and select the Scala Tests in the code to run them all.
Note: You may want to disable some tests not relevant to your setup e.g.:
set `bank_account_creation_listener=false` in `test.default.props`.
```
-Xmx2048m -Xms1024m -Xss2048k -XX:MaxPermSize=1024m
```
Ensure your `test.default.props` has the minimum settings (see `test.default.props.template`).
Right-click `obp-api/test/scala/code` and select the Scala Tests in the code to run them all.
Note: You may want to disable some tests not relevant to your setup e.g.:
set `bank_account_creation_listener=false` in `test.default.props`.
## Other ways to run tests
* See `pom.xml` for test configuration.
* See http://www.scalatest.org/user_guide.
- See `pom.xml` for test configuration.
- See http://www.scalatest.org/user_guide.
## From the command line
@ -141,7 +168,7 @@ Props values can be set as environment variables. Props need to be prefixed with
## Databases
The default database for testing etc is H2. PostgreSQL is used for the sandboxes (user accounts, metadata, transaction cache). The list of databases fully tested is: PostgreSQL, MS SQL and H2.
The default database for testing etc is H2. PostgreSQL is used for the sandboxes (user accounts, metadata, transaction cache). The list of databases fully tested is: PostgreSQL, MS SQL and H2.
### Notes on using H2 web console in Dev and Test mode:
@ -151,7 +178,7 @@ Set DB options in the props file:
db.driver=org.h2.Driver
db.url=jdbc:h2:./obp_api.db;DB_CLOSE_ON_EXIT=FALSE
```
In order to start H2 web console go to [http://127.0.0.1:8080/console](http://127.0.0.1:8080/console) and you will see a login screen.
Please use the following values:
Note: make sure the JDBC URL used matches your Props value!
@ -167,26 +194,26 @@ Password:
Once Postgres is installed (On macOS, use `brew`):
1. ```sh
psql postgres
```
1. ```sh
psql postgres
```
1. Create database `obpdb`; (or any other name of your choosing).
1. Create user `obp`; (this is the user that OBP-API will use to create and access tables etc).
1. Alter user obp with password `daniel.says`; (put this password in the OBP-API Props).
1. Grant all on database `obpdb` to `obp`; (So OBP-API can create tables etc.)
1. Then, set the `db.url` in your Props:
```
db.driver=org.postgresql.Driver
db.url=jdbc:postgresql://localhost:5432/obpdb?user=obp&password=daniel.says
```
1. Then, restart OBP-API.
1. Create database `obpdb`; (or any other name of your choosing).
1. Create user `obp`; (this is the user that OBP-API will use to create and access tables etc).
1. Alter user obp with password `daniel.says`; (put this password in the OBP-API Props).
1. Grant all on database `obpdb` to `obp`; (So OBP-API can create tables etc.)
1. Then, set the `db.url` in your Props:
```
db.driver=org.postgresql.Driver
db.url=jdbc:postgresql://localhost:5432/obpdb?user=obp&password=daniel.says
```
1. Then, restart OBP-API.
### Notes on using Postgres with SSL
@ -227,31 +254,31 @@ Restart OBP-API, if you get an error, check your Java environment can connect to
Note: You can copy the following example files to prepare your own configurations:
- `/obp-api/src/main/resources/logback.xml.example` -> `/obp-api/src/main/resources/logback.xml` (try TRACE or DEBUG).
- `/obp-api/src/main/resources/logback-test.xml.example` -> `/obp-api/src/main/resources/logback-test.xml` (try TRACE or DEBUG).
- `/obp-api/src/main/resources/logback.xml.example` -> `/obp-api/src/main/resources/logback.xml` (try TRACE or DEBUG).
- `/obp-api/src/main/resources/logback-test.xml.example` -> `/obp-api/src/main/resources/logback-test.xml` (try TRACE or DEBUG).
There is a gist/tool which is useful for this. Search the web for SSLPoke. Note this is an external repository.
For example:
* [https://gist.github.com/4ndrej/4547029](https://gist.github.com/4ndrej/4547029/84d3bff7bba262b3f77baa32a43873ea95993e45#file-sslpoke-java-L1-L40)
- [https://gist.github.com/4ndrej/4547029](https://gist.github.com/4ndrej/4547029/84d3bff7bba262b3f77baa32a43873ea95993e45#file-sslpoke-java-L1-L40)
or
or
* ```sh
git clone https://github.com/MichalHecko/SSLPoke.git .
- ```sh
git clone https://github.com/MichalHecko/SSLPoke.git .
gradle jar
cd ./build/libs/
gradle jar
cd ./build/libs/
java -jar SSLPoke-1.0.jar www.github.com 443
```
> Successfully connected
java -jar SSLPoke-1.0.jar www.github.com 443
```
```sh
java -jar SSLPoke-1.0.jar YOUR-POSTGRES-DATABASE-HOST PORT
```
> Successfully connected
```sh
java -jar SSLPoke-1.0.jar YOUR-POSTGRES-DATABASE-HOST PORT
```
You can add switches. For example, for debugging:
@ -290,7 +317,7 @@ To populate the OBP database with sandbox data:
## Production Options
* set the status of HttpOnly and Secure cookie flags for production, uncomment the following lines of `webapp/WEB-INF/web.xml`:
- set the status of HttpOnly and Secure cookie flags for production, uncomment the following lines of `webapp/WEB-INF/web.xml`:
```XML
<session-config>
@ -309,7 +336,7 @@ We use 9 to run the API in production mode.
2. jetty configuration
* Edit the `/etc/default/jetty9` file so that it contains the following settings:
- Edit the `/etc/default/jetty9` file so that it contains the following settings:
```
NO_START=0
@ -317,96 +344,95 @@ We use 9 to run the API in production mode.
JAVA_OPTIONS="-Drun.mode=production -XX:PermSize=256M -XX:MaxPermSize=512M -Xmx768m -verbose -Dobp.resource.dir=$JETTY_HOME/resources -Dprops.resource.dir=$JETTY_HOME/resources"
```
* In obp-api/src/main/resources/props create a `test.default.props` file for tests. Set `connector=mapped`.
- In obp-api/src/main/resources/props create a `test.default.props` file for tests. Set `connector=mapped`.
* In obp-api/src/main/resources/props create a `default.props file` for development. Set `connector=mapped`.
- In obp-api/src/main/resources/props create a `default.props file` for development. Set `connector=mapped`.
* In obp-api/src/main/resources/props create a `production.default.props` file for production. Set `connector=mapped`.
- In obp-api/src/main/resources/props create a `production.default.props` file for production. Set `connector=mapped`.
* This file could be similar to the `default.props` file created above, or it could include production settings, such as information about the Postgresql server if you are using one. For example, it could have the following line for Postgresql configuration.
- This file could be similar to the `default.props` file created above, or it could include production settings, such as information about the Postgresql server if you are using one. For example, it could have the following line for Postgresql configuration.
```
db.driver=org.postgresql.Driver
db.url=jdbc:postgresql://localhost:5432/yourdbname?user=yourdbusername&password=yourpassword
```
* Now, build the application to generate `.war` file which will be deployed on the jetty server:
- Now, build the application to generate `.war` file which will be deployed on the jetty server:
```sh
cd OBP-API/
mvn package
```
```sh
cd OBP-API/
mvn package
```
* This will generate OBP-API-1.0.war under `OBP-API/target/`.
- This will generate OBP-API-1.0.war under `OBP-API/target/`.
* Copy OBP-API-1.0.war to `/usr/share/jetty9/webapps/` directory and rename it to root.war
- Copy OBP-API-1.0.war to `/usr/share/jetty9/webapps/` directory and rename it to root.war
* Edit the `/etc/jetty9/jetty.conf` file and comment out the lines:
- Edit the `/etc/jetty9/jetty.conf` file and comment out the lines:
```
etc/jetty-logging.xml
etc/jetty-started.xml
```
```
etc/jetty-logging.xml
etc/jetty-started.xml
```
* Now restart jetty9:
- Now restart jetty9:
```sh
sudo service jetty9 restart
```
```sh
sudo service jetty9 restart
```
* You should now be able to browse to `localhost:8080` (or `yourIPaddress:8080`).
- You should now be able to browse to `localhost:8080` (or `yourIPaddress:8080`).
## Using OBP-API in different app modes
1) `portal` => OBP-API as a portal i.e. without REST API.
2) `apis` => OBP-API as an *APIs* app i.e. only REST APIs.
3) `apis,portal`=> OBP-API as portal and apis i.e. REST APIs and web portal.
1. `portal` => OBP-API as a portal i.e. without REST API.
2. `apis` => OBP-API as an _APIs_ app i.e. only REST APIs.
3. `apis,portal`=> OBP-API as portal and apis i.e. REST APIs and web portal.
* Edit your props file(s) to contain one of the next cases:
- Edit your props file(s) to contain one of the next cases:
1. `server_mode=portal`
2. `server_mode=apis`
3. `server_mode=apis,portal`
1. `server_mode=portal`
2. `server_mode=apis`
3. `server_mode=apis,portal`
In case it is not defined, the default case is the 3rd one. For example, `server_mode=apis,portal`.
In case it is not defined, the default case is the 3rd one. For example, `server_mode=apis,portal`.
## Using Akka remote storage
Most internal OBP model data access now occurs over Akka. This is so the machine that has JDBC access to the OBP database can be physically separated from the OBP API layer. In this configuration we run two instances of OBP-API on two different machines and they communicate over Akka. Please see README.Akka.md for instructions.
## Using SSL Encryption with RabbitMq
For SSL encryption we use JKS keystores. Note that both the keystore and the truststore (and all keys within) must have the same password for unlocking, for which the API will stop at boot up and ask for.
For SSL encryption we use JKS keystores. Note that both the keystore and the truststore (and all keys within) must have the same password for unlocking, for which the API will stop at boot up and ask for.
* Edit your props file(s) to contain:
- Edit your props file(s) to contain:
```
rabbitmq.use.ssl=true
keystore.path=/path/to/api.keystore.jks
keystore.password=123456
truststore.path=/path/to/api.truststore.jks
```
```
rabbitmq.use.ssl=true
keystore.path=/path/to/api.keystore.jks
keystore.password=123456
truststore.path=/path/to/api.truststore.jks
```
## Using SSL Encryption with props file
For SSL encryption we use jks keystores.
Note that keystore (and all keys within) must have the same password for unlocking, for which the API will stop at boot up and ask for.
Note that keystore (and all keys within) must have the same password for unlocking, for which the API will stop at boot up and ask for.
* Edit your props file(s) to contain:
- Edit your props file(s) to contain:
```
jwt.use.ssl=true
keystore.path=/path/to/api.keystore.jks
keystore.alias=SOME_KEYSTORE_ALIAS
```
```
jwt.use.ssl=true
keystore.path=/path/to/api.keystore.jks
keystore.alias=SOME_KEYSTORE_ALIAS
```
A props key value, XXX, is considered encrypted if has an encryption property (XXX.is_encrypted) in addition to the regular props key name in the props file e.g:
* db.url.is_encrypted=true
* db.url=BASE64URL(SOME_ENCRYPTED_VALUE)
- db.url.is_encrypted=true
- db.url=BASE64URL(SOME_ENCRYPTED_VALUE)
The Encrypt/Decrypt workflow is :
1. Encrypt: Array[Byte]
2. Helpers.base64Encode(encrypted)
3. Props file: String
@ -419,17 +445,17 @@ The Encrypt/Decrypt workflow is :
1. Export the public certificate from the keystone:
```sh
keytool -export -keystore /PATH/TO/KEYSTORE.jks -alias CERTIFICATE_ALIAS -rfc -file apipub.cert
```
3. Extract the public key from the public certificate:
```sh
keytool -export -keystore /PATH/TO/KEYSTORE.jks -alias CERTIFICATE_ALIAS -rfc -file apipub.cert
```
```sh
openssl x509 -pubkey -noout -in apipub.cert > PUBKEY.pub`
```
4. Get the encrypted `propsvalue` like in the following bash script (usage `./scriptname.sh /PATH/TO/PUBKEY.pub propsvalue`):
2. Extract the public key from the public certificate:
```sh
openssl x509 -pubkey -noout -in apipub.cert > PUBKEY.pub`
```
3. Get the encrypted `propsvalue` like in the following bash script (usage `./scriptname.sh /PATH/TO/PUBKEY.pub propsvalue`):
```
#!/bin/bash
@ -443,9 +469,8 @@ You can obfuscate passwords in the props file the same way as for jetty:
1. Create the obfuscated value as described here: [https://www.eclipse.org/jetty/documentation/9.3.x/configuring-security-secure-passwords.html](https://www.eclipse.org/jetty/documentation/9.3.x/configuring-security-secure-passwords.html).
2. A props key value, XXX, is considered obfuscated if has an obfuscation property (`XXX.is_obfuscated`) in addition to the regular props key name in the props file e.g:
* `db.url.is_obfuscated=true`
* `db.url=OBF:fdsafdsakwaetcetcetc`
- `db.url.is_obfuscated=true`
- `db.url=OBF:fdsafdsakwaetcetcetc`
## Code Generation
@ -462,32 +487,31 @@ You can obfuscate passwords in the props file the same way as for jetty:
1. Create the obfuscated value as described here: [https://www.eclipse.org/jetty/documentation/9.3.x/configuring-security-secure-passwords.html](https://www.eclipse.org/jetty/documentation/9.3.x/configuring-security-secure-passwords.html).
2. A props key value, XXX, is considered obfuscated if has an obfuscation property (XXX.is_obfuscated) in addition to the regular props key name in the props file e.g:
* db.url.is_obfuscated=true
* db.url=OBF:fdsafdsakwaetcetcetc
- db.url.is_obfuscated=true
- db.url=OBF:fdsafdsakwaetcetcetc
## Rate Limiting
We support rate limiting i.e functionality to limit calls per consumer key (App). Only `New Style Endpoins` support it. The list of they can be found at this file: [https://github.com/OpenBankProject/OBP-API/blob/develop/obp-api/src/main/scala/code/api/util/NewStyle.scala](https://github.com/OpenBankProject/OBP-API/blob/develop/obp-api/src/main/scala/code/api/util/NewStyle.scala).
We support rate limiting i.e functionality to limit calls per consumer key (App). Only `New Style Endpoins` support it. The list of they can be found at this file: [https://github.com/OpenBankProject/OBP-API/blob/develop/obp-api/src/main/scala/code/api/util/NewStyle.scala](https://github.com/OpenBankProject/OBP-API/blob/develop/obp-api/src/main/scala/code/api/util/NewStyle.scala).
There are two supported modes:
* In-Memory
* Redis
- In-Memory
- Redis
It is assumed that you have some Redis instances if you want to use the functionality in multi-node architecture.
We apply Rate Limiting for two types of access:
* Authorized
* Anonymous
- Authorized
- Anonymous
To set up Rate Limiting in case of anonymous access edit your props file in the following way:
```
user_consumer_limit_anonymous_access=100, In case isn't defined default value is 60
```
Te set up Rate Limiting in case of the authorized access use these endpoints:
1. `GET ../management/consumers/CONSUMER_ID/consumer/call-limits` - Get Call Limits for a Consumer
@ -613,24 +637,25 @@ Tested Identity providers: Google, MITREId.
allow_oauth2_login=true
oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs
```
---
## Frozen APIs
API versions may be marked as "STABLE", if changes are made to an API which has been marked as "STABLE", then unit test `FrozenClassTest` will fail.
API versions may be marked as "STABLE", if changes are made to an API which has been marked as "STABLE", then unit test `FrozenClassTest` will fail.
### Changes to "STABLE" API cause the tests to fail:
### Changes to "STABLE" API cause the tests to fail:
* modify request or response body structure of APIs
* add or delete APIs
* change the APIS' `versionStatus` from or to "STABLE"
- modify request or response body structure of APIs
- add or delete APIs
- change the APIS' `versionStatus` from or to "STABLE"
If it is required for a "STABLE" api to be changed, then the class metadata must be regenerated using the FrozenClassUtil (see how to freeze an API)
### Steps to freeze an API
* Run the FrozenClassUtil to regenerate persist file of frozen apis information, the file is `PROJECT_ROOT_PATH/obp-api/src/test/resources/frozen_type_meta_data`
* push the file `frozen_type_meta_data` to github
- Run the FrozenClassUtil to regenerate persist file of frozen apis information, the file is `PROJECT_ROOT_PATH/obp-api/src/test/resources/frozen_type_meta_data`
- push the file `frozen_type_meta_data` to github
There is a video about the detail: [demonstrate the detail of the feature](https://www.youtube.com/watch?v=m9iYCSM0bKA)
@ -640,17 +665,17 @@ The same as `Frozen APIs`, if a related unit test fails, make sure whether the m
## Scala / Lift
* We use scala and liftweb: [http://www.liftweb.net/](http://www.liftweb.net/).
- We use scala and liftweb: [http://www.liftweb.net/](http://www.liftweb.net/).
* Advanced architecture: [http://exploring.liftweb.net/master/index-9.html
](http://exploring.liftweb.net/master/index-9.html).
- Advanced architecture: [http://exploring.liftweb.net/master/index-9.html
](http://exploring.liftweb.net/master/index-9.html).
* A good book on Lift: "Lift in Action" by Timothy Perrett published by Manning.
- A good book on Lift: "Lift in Action" by Timothy Perrett published by Manning.
## Supported JDK Versions
* OracleJDK: 1.8, 13
* OpenJdk: 11
- OracleJDK: 1.8, 13
- OpenJdk: 11
OpenJDK 11 is available for download here: [https://jdk.java.net/archive/](https://jdk.java.net/archive/).
@ -659,13 +684,13 @@ OpenJDK 11 is available for download here: [https://jdk.java.net/archive/](https
```log
ResourceDoc#exampleRequestBody and ResourceDoc#successResponseBody can be the follow type
```
* Any Case class
* JObject
* Wrapper JArray: JArrayBody(jArray)
* Wrapper String: StringBody("Hello")
* Wrapper primary type: IntBody(1), BooleanBody(true), FloatBody(1.2F)...
* Empty: EmptyBody
- Any Case class
- JObject
- Wrapper JArray: JArrayBody(jArray)
- Wrapper String: StringBody("Hello")
- Wrapper primary type: IntBody(1), BooleanBody(true), FloatBody(1.2F)...
- Empty: EmptyBody
Example:
@ -678,11 +703,14 @@ resourceDocs += ResourceDoc(
```
## Language support
### Add a new language
An additional language can be added via props `supported_locales`
Steps to add Spanish language:
* tweak the property supported_locales = en_GB to `supported_locales = en_GB,es_ES`
* add file `lift-core_es_ES.properties` at the folder `/resources/i18n`
- tweak the property supported_locales = en_GB to `supported_locales = en_GB,es_ES`
- add file `lift-core_es_ES.properties` at the folder `/resources/i18n`
Please note that default translation file is `lift-core.properties`

View File

@ -11,7 +11,7 @@
### Base configuration
## Which data connector to use, if config `star` as connector, please also check `starConnector_supported_types`
## Which data connector to use, if config `star` as connector, please also check `starConnector_supported_types`
#connector=mapped
#connector=mongodb
#connector=akka_vDec2018
@ -31,7 +31,7 @@ connector=star
#hikari.keepaliveTime=
#hikari.maxLifetime=
## if connector = star, then need to set which connectors will be used. For now, obp support rest, akka.
## if connector = star, then need to set which connectors will be used. For now, obp support rest, akka.
starConnector_supported_types=mapped,internal
## whether export LocalMappedConnector methods as endpoints, it is just for develop, default is false
@ -61,7 +61,7 @@ starConnector_supported_types=mapped,internal
#connector.cache.ttl.seconds.APIMethods121.getStatusOfCreditCardOrderFuture=0
#connector.cache.ttl.seconds.APIMethods121.getStatusOfCheckbookOrdersFuture=0
#this is special cache, it is used only in obp side,
#this is special cache, it is used only in obp side,
#MapperCounterparties.cache.ttl.seconds.getOrCreateMetadata=0
#this cache is used in api level, will cache whole endpoint : v121.getTransactionsForBankAccount
#api.cache.ttl.seconds.APIMethods121.getTransactions=0
@ -99,7 +99,7 @@ staticResourceDocsObp.cache.ttl.seconds=3600
createLocalisedResourceDocJson.cache.ttl.seconds=3600
## This can change the behavior of `Get Resource Docs`/`Get API Glossary`. If we set it to `true`, OBP will check the authentication and CanReadResourceDoc/CanReadGlossary Role
# the default value is false, so the `Get Resource Docs`/`Get API Glossary` is anonymous as default.
# the default value is false, so the `Get Resource Docs`/`Get API Glossary` is anonymous as default.
resource_docs_requires_role=false
glossary_requires_role=false
read_json_schema_validation_requires_role=false
@ -135,7 +135,7 @@ jwt.use.ssl=false
## Enable SSL for rabbitmq, if set to true must set paths for the keystore locations
#rabbitmq.use.ssl=false
# Paths to the SSL keystore files - has to be jks
# Paths to the SSL keystore files - has to be jks
#keystore.path=/path/to/api.keystore.jks
#keystore password
#keystore.password = redf1234
@ -199,7 +199,7 @@ jwt.use.ssl=false
## Enable writing API metrics (which APIs are called) to RDBMS
write_metrics=false
## Enable writing connector metrics (which methods are called)to RDBMS
## Enable writing connector metrics (which methods are called)to RDBMS
write_connector_metrics=false
## ElasticSearch
@ -272,7 +272,7 @@ apiPathZero=obp
## Email Configuration (CommonsEmailWrapper)
## ===========================================
##
##
## This section configures email sending using CommonsEmailWrapper instead of Lift Mailer.
## All email functionality (password reset, validation, notifications) now uses these settings.
##
@ -541,7 +541,7 @@ webui_oauth_2_documentation_url =
# Link to Privacy Policy on signup page
#webui_signup_form_submit_button_value=
#webui_signup_form_title_text=Sign Up
#webui_signup_body_password_repeat_text=Repeat
#webui_signup_body_password_repeat_text=Repeat
#allow_pre_filled_password=true
#webui_agree_terms_html=<div id="signup-agree-terms" class="checkbox"><label><input type="checkbox" />I hereby agree to the <a href="$url" title="T &amp; C">Terms and Conditions</a></label></div>
webui_agree_privacy_policy_url = https://openbankproject.com/privacy-policy
@ -554,7 +554,7 @@ webui_main_partners=[\
{"logoUrl":"http://www.example.com/images/logo.png", "homePageUrl":"http://www.example.com", "altText":"Example 2"}]
# Prefix for all page titles (note the trailing space!)
webui_page_title_prefix = Open Bank Project:
webui_page_title_prefix = Open Bank Project:
# set the favicon icon
#webui_favicon_link_url =/favicon.ico
@ -623,12 +623,12 @@ webui_dummy_user_logins = Customer Logins\
# when this value is set to true and webui_dummy_user_logins value not empty, the register consumer key success page will show dummy customers Direct Login tokens.
webui_show_dummy_user_tokens=false
# when developer register the consumer successfully, it will show this message to developer on the webpage or email.
# when developer register the consumer successfully, it will show this message to developer on the webpage or email.
webui_register_consumer_success_message_webpage = Thanks for registering your consumer with the Open Bank Project API! Here is your developer information. Please save it in a secure location.
webui_register_consumer_success_message_email = Thank you for registering to use the Open Bank Project API.
#Log in page
#webui_login_button_text =
#webui_login_button_text =
## End of webui_ section ########
@ -680,7 +680,7 @@ super_admin_user_ids=USER_ID1,USER_ID2,
# "UKv3.1" or "v3.1"
#
# Note: we recommend to use the fullyQualifiedVersion. The apiShortVersion is depreciated here.
#
#
# For a VERSION (the version in path e.g. /obp/v4.0.0) to be allowed, it must be:
# 1) Absent from here (high priority):
@ -709,7 +709,7 @@ super_admin_user_ids=USER_ID1,USER_ID2,
##########################
## OpenId Connect can be used to retrieve User information from an
## Open Id Connect (OIDC) can be used to retrieve User information from an
## external OpenID Connect server.
## To use an external OpenID Connect server,
## you will need to change these values.
@ -717,7 +717,7 @@ super_admin_user_ids=USER_ID1,USER_ID2,
## CallbackURL 127.0.0.1:8080 should work in most cases.
## Note: The email address used for login must match one
## registered on OBP localy.
# openid_connect.enabled=true
# openid_connect.enabled=false
# openid_connect.check_session_state=true
# openid_connect.show_tokens=false
# Response mode
@ -837,6 +837,21 @@ display_internal_errors=false
# oauth2.keycloak.resource_access_key_name_to_trust = open-bank-project
# ------------------------------------------------------------------------ Keycloak OAuth 2 ------
# -- OBP OIDC OAuth 2 / OIDC ---------------------------------------------------
# To run OBP OIDC (for developer testing) see: https://github.com/OpenBankProject/OBP-OIDC
# OAuth2 Provider Selection (for well-known endpoint and token validation)
# Choose which OIDC provider to use: 'keycloak' or 'obp-oidc'
#oauth2.oidc_provider=obp-oidc
# OBP-OIDC OAuth2 Provider Settings
#oauth2.obp_oidc.host=http://localhost:9000
#oauth2.obp_oidc.well_known=http://localhost:9000/.well-known/openid-configuration
# After setting the above and restarting the server curl -s http://localhost:8080/obp/v5.1.0/well-known
# should advertise obp-oidc
# -----------------------------------------------------------------------------------
# -- PSU Authentication methods --------------------------------------------------------------
# The EBA notes that there would appear to currently be three main ways or methods
# of carrying out the authentication procedure of the PSU through a dedicated interface,
@ -950,9 +965,9 @@ featured_apis=elasticSearchWarehouseV300
# -- Rest connector --------------------------------------------
# If Rest Connector do not get the response in the following seconds, it will throw the error message back.
# This props can be omitted, the default value is 59. It should be less than Nginx timeout.
# rest2019_connector_timeout = 59
# If set it to `true`, it will add the x-sign (SHA256WithRSA) into each the rest connector http calls,
# please add the name of the field for the UserAuthContext and/or link to other documentation..
# rest2019_connector_timeout = 59
# If set it to `true`, it will add the x-sign (SHA256WithRSA) into each the rest connector http calls,
# please add the name of the field for the UserAuthContext and/or link to other documentation..
#rest_connector_sends_x-sign_header=false
# -- RabbitMQ connector --------------------------------------------
@ -965,7 +980,7 @@ featured_apis=elasticSearchWarehouseV300
#rabbitmq.adapter.enabled=false
# -- Scopes -----------------------------------------------------
# Scopes can be used to limit the APIs a Consumer can call.
@ -976,7 +991,7 @@ featured_apis=elasticSearchWarehouseV300
# i.e. instead of asking every user to have a Role, you can give the Role(s) to a Consumer in the form of a Scope
# allow_entitlements_or_scopes=false
# ---------------------------------------------------------------
# -- Just in Time Entitlements -------------------------------
create_just_in_time_entitlements=false
# if create_just_in_time_entitlements=true then OBP does the following:
@ -998,11 +1013,11 @@ database_messages_scheduler_interval=3600
# -- Consents ---------------------------------------------
# In case isn't defined default value is "false"
# consents.allowed=true
#
#
# In order to pin a consent to a consumer we can use the property consumer_validation_method_for_consent
# Possibile values are: CONSUMER_CERTIFICATE, CONSUMER_KEY_VALUE, NONE
# consumer_validation_method_for_consent=CONSUMER_CERTIFICATE
#
#
# consents.max_time_to_live=3600
# In case isn't defined default value is "false"
# consents.sca.enabled=true
@ -1013,7 +1028,7 @@ database_messages_scheduler_interval=3600
# sca_phone_api_key = ACobpb72ab850501b5obp8dobp9dobp111
# sca_phone_api_secret =7afobpdacobpd427obpff87a22obp222
# sca_phone_api_id =MGcobp8575119887f10b62a2461obpb333
#
#
# -- PSD2 Certificates --------------------------
# Possible cases: ONLINE, CERTIFICATE, NONE
@ -1054,12 +1069,12 @@ database_messages_scheduler_interval=3600
#energy_source.organisation=
#energy_source.organisation_website=
# GRPC
# GRPC
# the default GRPC is disabled
# grpc.server.enabled = false
# If do not set this props, the grpc port will be set randomly when OBP starts.
# And you can call `Get API Configuration` endpoint to see the `grpc_port` there.
# When you set this props, need to make sure this port is available.
# If do not set this props, the grpc port will be set randomly when OBP starts.
# And you can call `Get API Configuration` endpoint to see the `grpc_port` there.
# When you set this props, need to make sure this port is available.
# grpc.server.port = 50051
# Create System Views At Boot -----------------------------------------------
@ -1075,7 +1090,7 @@ database_messages_scheduler_interval=3600
ReadAccountsBerlinGroup, \
InitiatePaymentsBerlinGroup
# -----------------------------------------------------------------------------
@ -1099,7 +1114,7 @@ dynamic_entities_have_prefix=true
dynamic_endpoints_url_prefix=
# --- Locking a user due to consecutively failed login attempts ------
# Defines consecutively failed login attempts before a user is locked
# Defines consecutively failed login attempts before a user is locked
# In case is not defined default value is 5
# max.bad.login.attempts=5
# --------------------------------------------------------------------
@ -1158,9 +1173,39 @@ outboundAdapterCallContext.generalContext
# hydra_uses_obp_user_credentials=true
# ------------------------------ Hydra oauth2 props end ------------------------------
# ------------------------------ OBP-OIDC oauth2 props ------------------------------
## OBP-OIDC Provider Configuration
## Choose which OIDC provider to use: 'keycloak' or 'obp-oidc'
#oauth2.oidc_provider=obp-oidc
## OBP-OIDC OAuth2 Provider Settings
#oauth2.obp_oidc.host=http://localhost:9000
## The issuer URL that will be used in JWT tokens (URL-based format)
#oauth2.obp_oidc.issuer=http://localhost:9000/obp-oidc
## Well-known OpenID Connect configuration endpoint
#oauth2.obp_oidc.well_known=http://localhost:9000/obp-oidc/.well-known/openid-configuration
## OAuth2 JWKS URI configuration for token validation
## This should include the JWKS URI for OBP-OIDC provider
## Multiple JWKS URIs can be comma-separated
#oauth2.jwk_set.url=http://localhost:9000/obp-oidc/jwks
## OpenID Connect Client Configuration for OBP-OIDC
#openid_connect_1.button_text=OBP-OIDC
#openid_connect_1.client_id=obp-api-client
#openid_connect_1.client_secret=your-client-secret-here
#openid_connect_1.callback_url=http://localhost:8080/auth/openid-connect/callback
#openid_connect_1.endpoint.discovery=http://localhost:9000/obp-oidc/.well-known/openid-configuration
#openid_connect_1.endpoint.authorization=http://localhost:9000/obp-oidc/auth
#openid_connect_1.endpoint.userinfo=http://localhost:9000/obp-oidc/userinfo
#openid_connect_1.endpoint.token=http://localhost:9000/obp-oidc/token
#openid_connect_1.endpoint.jwks_uri=http://localhost:9000/obp-oidc/jwks
#openid_connect_1.access_type_offline=true
# ------------------------------ OBP-OIDC oauth2 props end ------------------------------
# ------------------------------ default entitlements ------------------------------
## the default entitlements list, you can added the roles here.
#entitlement_list_1=[]
#entitlement_list_1=[]
# when new User is validated, grant the following role list to that user.
#new_user_entitlement_list=entitlement_list_1
# ------------------------------ default entitlements end ------------------------------
@ -1199,7 +1244,7 @@ default_auth_context_update_request_key=CUSTOMER_NUMBER
## This props is used for the featured api collection, eg: API_Explore will use the api collection to tweak the Home Page
#featured_api_collection_ids=
# the alias prefix path for BerlinGroupV1.3 (OBP built-in is berlin-group/v1.3), the format must be xxx/yyy, eg: 0.6/v1
# the alias prefix path for BerlinGroupV1.3 (OBP built-in is berlin-group/v1.3), the format must be xxx/yyy, eg: 0.6/v1
#berlin_group_v1_3_alias_path=
# Berlin Group URL version
@ -1218,17 +1263,17 @@ default_auth_context_update_request_key=CUSTOMER_NUMBER
## Berlin Group Create Consent ASPSP-SCA-Approach response header value
#berlin_group_aspsp_sca_approach = redirect
# Support multiple brands on one instance. Note this needs checking on a clustered environment
# Support multiple brands on one instance. Note this needs checking on a clustered environment
#brands_enabled=false
# Support removing the app type checkbox during consumer registration
# Support removing the app type checkbox during consumer registration
#consumer_registration.display_app_type=true
# Default logo URL during of consumer
#consumer_default_logo_url=
# if set this props, we can automatically grant the Entitlements required to use all the Dynamic Endpoint roles belonging
# to the bank_ids (Spaces) the User has access to via their validated email domain. Entitlements are generated /refreshed
# if set this props, we can automatically grant the Entitlements required to use all the Dynamic Endpoint roles belonging
# to the bank_ids (Spaces) the User has access to via their validated email domain. Entitlements are generated /refreshed
# both following manual login and Direct Login token generation (POST).
# the default value is empty
#email_domain_to_space_mappings=
@ -1322,8 +1367,8 @@ webui_developer_user_invitation_email_html_text=<!DOCTYPE html>\
# the subscription button,default is empty, will not show it on the homepage.
#webui_subscriptions_url=
#webui_subscriptions_button_text=
#webui_subscriptions_invitation_text=
#webui_subscriptions_invitation_text=
# List of countries where consent is not required for the collection of personal data
personal_data_collection_consent_country_waiver_list = Austria, Belgium, Bulgaria, Croatia, Republic of Cyprus, Czech Republic, Denmark, Estonia, Finland, France, Germany, Greece, Hungary, Ireland, Italy, Latvia, Lithuania, Luxembourg, Malta, Netherlands, Poland, Portugal, Romania, Slovakia, Slovenia, Spain, Sweden, England, Scotland, Wales, Northern Ireland
@ -1359,7 +1404,7 @@ dynamic_code_sandbox_permissions=[\
# enable dynamic code compile validation, default is false, if set it to true, it will validate all the dynamic method body when you create/update any
# dynamic scala method. Note, it only check all the obp code dependents for all the method in OBP code.
dynamic_code_compile_validate_enable=false
# The default support dependencies if set dynamic_code_compile_validate_enable = true. it can be the class level or the method level,
# The default support dependencies if set dynamic_code_compile_validate_enable = true. it can be the class level or the method level,
# you can add them in the following list. Better check search for comment code: val allowedCompilationMethods: Map[String, Set[String]] = Map( ...
# need to prepare the correct OBP scala code.
dynamic_code_compile_validate_dependencies=[\
@ -1461,8 +1506,8 @@ regulated_entities = []
# This feature can also be used in a "Break Glass" scenario.
# If you want to use this feature, please set up all three values properly at the same time.
# super_admin_username=TomWilliams
# super_admin_inital_password=681aeeb9f681aeeb9f681aeeb9
# super_admin_inital_password=681aeeb9f681aeeb9f681aeeb9
# super_admin_email=tom@tesobe.com
# Note: For secure and http only settings for cookies see resources/web.xml which is mentioned in the README.md
# Note: For secure and http only settings for cookies see resources/web.xml which is mentioned in the README.md

View File

@ -106,6 +106,8 @@ object OAuth2Login extends RestHelper with MdcLoggable {
Yahoo.applyIdTokenRulesFuture(value, cc)
} else if (Azure.isIssuer(value)) {
Azure.applyIdTokenRulesFuture(value, cc)
} else if (OBPOIDC.isIssuer(value)) {
OBPOIDC.applyIdTokenRulesFuture(value, cc)
} else if (Keycloak.isIssuer(value)) {
Keycloak.applyRulesFuture(value, cc)
} else if (UnknownProvider.isIssuer(value)) {
@ -228,7 +230,11 @@ object OAuth2Login extends RestHelper with MdcLoggable {
def checkUrlOfJwkSets(identityProvider: String) = {
val url: List[String] = Constant.oauth2JwkSetUrl.toList
val jwksUris: List[String] = url.map(_.toLowerCase()).map(_.split(",").toList).flatten
val jwksUri = jwksUris.filter(_.contains(identityProvider))
// Enhanced matching for both URL-based and semantic identifiers
val identityProviderLower = identityProvider.toLowerCase()
val jwksUri = jwksUris.filter(_.contains(identityProviderLower))
jwksUri match {
case x :: _ => Full(x)
case Nil => Failure(Oauth2CannotMatchIssuerAndJwksUriException)
@ -243,7 +249,13 @@ object OAuth2Login extends RestHelper with MdcLoggable {
}
}
def isIssuer(jwtToken: String, identityProvider: String): Boolean = {
JwtUtil.getIssuer(jwtToken).map(_.contains(identityProvider)).getOrElse(false)
JwtUtil.getIssuer(jwtToken).map { issuer =>
// Direct match or contains match for backward compatibility
issuer == identityProvider || issuer.contains(identityProvider) ||
// For URL-based issuers, also try exact match ignoring trailing slash
(issuer.endsWith("/") && issuer.dropRight(1) == identityProvider) ||
(identityProvider.endsWith("/") && identityProvider.dropRight(1) == issuer)
}.getOrElse(false)
}
def validateIdToken(idToken: String): Box[IDTokenClaimsSet] = {
urlOfJwkSets match {
@ -564,4 +576,20 @@ object OAuth2Login extends RestHelper with MdcLoggable {
}
}
}
object OBPOIDC extends OAuth2Util {
val obpOidcHost = APIUtil.getPropsValue(nameOfProperty = "oauth2.obp_oidc.host", "http://localhost:9000")
val obpOidcIssuer = "obp-oidc"
/**
* OBP-OIDC (Open Bank Project OIDC Provider)
* OBP-OIDC exposes OpenID Connect discovery documents at /.well-known/openid-configuration
* This is the native OIDC provider for OBP ecosystem
*/
override def wellKnownOpenidConfiguration: URI =
new URI(
APIUtil.getPropsValue(nameOfProperty = "oauth2.obp_oidc.well_known", s"$obpOidcHost/obp-oidc/.well-known/openid-configuration")
)
override def urlOfJwkSets: Box[String] = checkUrlOfJwkSets(identityProvider = obpOidcIssuer)
def isIssuer(jwt: String): Boolean = isIssuer(jwtToken=jwt, identityProvider = obpOidcIssuer)
}
}

View File

@ -3,7 +3,7 @@ package code.api.v5_1_0
import code.api.Constant
import code.api.Constant._
import code.api.OAuth2Login.Keycloak
import code.api.OAuth2Login.{Keycloak, OBPOIDC}
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{ConsentAccessAccountsJson, ConsentAccessJson}
import code.api.util.APIUtil._
@ -75,7 +75,7 @@ trait APIMethods510 {
val implementedInApiVersion: ScannedApiVersion = ApiVersion.v5_1_0
private val staticResourceDocs = ArrayBuffer[ResourceDoc]()
def resourceDocs = staticResourceDocs
def resourceDocs = staticResourceDocs
val apiRelations = ArrayBuffer[ApiRelation]()
val codeContext = CodeContext(staticResourceDocs, apiRelations)
@ -163,8 +163,15 @@ trait APIMethods510 {
for {
(_, callContext) <- anonymousAccess(cc)
} yield {
val keycloak: WellKnownUriJsonV510 = WellKnownUriJsonV510("keycloak", Keycloak.wellKnownOpenidConfiguration.toURL.toString)
(WellKnownUrisJsonV510(List(keycloak)), HttpCode.`200`(callContext))
// Advertise the configured OIDC provider for this OBP-API instance
// Check if OBP-OIDC is configured, otherwise default to Keycloak
val oidcProvider = APIUtil.getPropsValue("oauth2.oidc_provider", "keycloak").toLowerCase match {
case "obp-oidc" | "obpoidc" =>
WellKnownUriJsonV510("obp-oidc", OBPOIDC.wellKnownOpenidConfiguration.toURL.toString)
case _ =>
WellKnownUriJsonV510("keycloak", Keycloak.wellKnownOpenidConfiguration.toURL.toString)
}
(WellKnownUrisJsonV510(List(oidcProvider)), HttpCode.`200`(callContext))
}
}
}
@ -315,7 +322,7 @@ trait APIMethods510 {
}
}
staticResourceDocs += ResourceDoc(
waitingForGodot,
implementedInApiVersion,
@ -348,7 +355,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
getAllApiCollections,
implementedInApiVersion,
@ -403,7 +410,7 @@ trait APIMethods510 {
),
List(apiTagCustomer, apiTagPerson)
)
lazy val createAgent : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "agents" :: Nil JsonPost json -> _ => {
cc => implicit val ec = EndpointContext(Some(cc))
@ -433,7 +440,7 @@ trait APIMethods510 {
callContext
)
(_, callContext) <- NewStyle.function.createAgentAccountLink(agent.agentId, bankAccount.bankId.value, bankAccount.accountId.value, callContext)
} yield {
(JSONFactory510.createAgentJson(agent, bankAccount), HttpCode.`201`(callContext))
}
@ -463,7 +470,7 @@ trait APIMethods510 {
List(apiTagCustomer, apiTagPerson),
Some(canUpdateAgentStatusAtAnyBank :: canUpdateAgentStatusAtOneBank :: Nil)
)
lazy val updateAgentStatus : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "agents" :: agentId :: Nil JsonPut json -> _ => {
cc => implicit val ec = EndpointContext(Some(cc))
@ -487,7 +494,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
getAgent,
implementedInApiVersion,
@ -526,7 +533,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
createNonPersonalUserAttribute,
implementedInApiVersion,
@ -581,7 +588,7 @@ trait APIMethods510 {
}
}
}
resourceDocs += ResourceDoc(
deleteNonPersonalUserAttribute,
implementedInApiVersion,
@ -621,7 +628,7 @@ trait APIMethods510 {
}
}
}
resourceDocs += ResourceDoc(
getNonPersonalUserAttributes,
implementedInApiVersion,
@ -653,7 +660,7 @@ trait APIMethods510 {
(userAttributes,callContext) <- NewStyle.function.getNonPersonalUserAttributes(
user.userId,
callContext,
)
)
} yield {
(JSONFactory510.createUserAttributesJson(userAttributes), HttpCode.`200`(callContext))
}
@ -813,8 +820,8 @@ trait APIMethods510 {
userJsonV300,
List(
$UserNotLoggedIn,
UserNotFoundByUserId,
UserHasMissingRoles,
UserNotFoundByUserId,
UserHasMissingRoles,
UnknownError),
List(apiTagRole, apiTagEntitlement, apiTagUser),
Some(List(canGetEntitlementsForAnyUserAtAnyBank)))
@ -832,8 +839,8 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
customViewNamesCheck,
implementedInApiVersion,
@ -869,7 +876,7 @@ trait APIMethods510 {
(JSONFactory510.getCustomViewNamesCheck(incorrectViews), HttpCode.`200`(cc.callContext))
}
}
}
}
staticResourceDocs += ResourceDoc(
systemViewNamesCheck,
implementedInApiVersion,
@ -906,7 +913,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
accountAccessUniqueIndexCheck,
implementedInApiVersion,
@ -934,7 +941,7 @@ trait APIMethods510 {
cc => implicit val ec = EndpointContext(Some(cc))
for {
groupedRows: Map[String, List[AccountAccess]] <- Future {
AccountAccess.findAll().groupBy { a =>
AccountAccess.findAll().groupBy { a =>
s"${a.bank_id.get}-${a.account_id.get}-${a.view_id.get}-${a.user_fk.get}-${a.consumer_id.get}"
}.filter(_._2.size > 1) // Extract only duplicated rows
}
@ -942,7 +949,7 @@ trait APIMethods510 {
(JSONFactory510.getAccountAccessUniqueIndexCheck(groupedRows), HttpCode.`200`(cc.callContext))
}
}
}
}
staticResourceDocs += ResourceDoc(
accountCurrencyCheck,
implementedInApiVersion,
@ -1235,7 +1242,7 @@ trait APIMethods510 {
"PUT",
"/banks/BANK_ID/atms/ATM_ID/attributes/ATM_ATTRIBUTE_ID",
"Update ATM Attribute",
s""" Update ATM Attribute.
s""" Update ATM Attribute.
|
|Update an ATM Attribute by its id.
|
@ -1816,7 +1823,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
revokeConsentAtBank,
implementedInApiVersion,
@ -1845,7 +1852,7 @@ trait APIMethods510 {
BankNotFound,
UnknownError
),
List(apiTagConsent, apiTagPSD2AIS, apiTagPsd2),
List(apiTagConsent, apiTagPSD2AIS, apiTagPsd2),
Some(List(canRevokeConsentAtBank))
)
@ -1869,7 +1876,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
selfRevokeConsent,
implementedInApiVersion,
@ -2177,12 +2184,12 @@ trait APIMethods510 {
_ <- Future(Consents.consentProvider.vend.setValidUntil(createdConsent.consentId, validUntil)) map {
i => connectorEmptyResponse(i, callContext)
}
//we need to check `skip_consent_sca_for_consumer_id_pairs` props, to see if we really need the SCA flow.
//we need to check `skip_consent_sca_for_consumer_id_pairs` props, to see if we really need the SCA flow.
//this is from callContext
grantorConsumerId = callContext.map(_.consumer.toOption.map(_.consumerId.get)).flatten.getOrElse("Unknown")
//this is from json body
granteeConsumerId = consentJson.consumer_id.getOrElse("Unknown")
shouldSkipConsentScaForConsumerIdPair = APIUtil.skipConsentScaForConsumerIdPairs.contains(
APIUtil.ConsumerIdPair(
grantorConsumerId,
@ -2269,7 +2276,7 @@ trait APIMethods510 {
}
}
staticResourceDocs += ResourceDoc(
mtlsClientCertificateInfo,
implementedInApiVersion,
@ -2371,7 +2378,7 @@ trait APIMethods510 {
List(apiTagUser),
Some(List(canGetAnyUser))
)
lazy val getUserByProviderAndUsername: OBPEndpoint = {
case "users" :: "provider" :: provider :: "username" :: username :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
@ -2739,7 +2746,7 @@ trait APIMethods510 {
),
List(apiTagCustomer, apiTagUser)
)
lazy val getCustomersForUserIdsOnly : OBPEndpoint = {
case "users" :: "current" :: "customers" :: "customer_ids" :: Nil JsonGet _ => {
cc => {
@ -2798,7 +2805,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
createAtm,
@ -2940,7 +2947,7 @@ trait APIMethods510 {
(atm-> attributes)
}
)))
} yield {
(JSONFactory510.createAtmsJsonV510(atmAndAttributesTupleList), HttpCode.`200`(callContext))
}
@ -3014,7 +3021,7 @@ trait APIMethods510 {
for {
(atm, callContext) <- NewStyle.function.getAtm(bankId, atmId, cc.callContext)
(deleted, callContext) <- NewStyle.function.deleteAtm(atm, callContext)
(atmAttributes, callContext) <- NewStyle.function.deleteAtmAttributesByAtmId(atmId, callContext)
(atmAttributes, callContext) <- NewStyle.function.deleteAtmAttributesByAtmId(atmId, callContext)
} yield {
(Full(deleted && atmAttributes), HttpCode.`204`(callContext))
}
@ -3131,7 +3138,7 @@ trait APIMethods510 {
List(apiTagConsumer),
Some(List(canCreateConsumer))
)
lazy val createConsumer: OBPEndpoint = {
case "management" :: "consumers" :: Nil JsonPost json -> _ => {
cc => implicit val ec = EndpointContext(Some(cc))
@ -3213,8 +3220,8 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
updateConsumerRedirectURL,
implementedInApiVersion,
@ -3272,8 +3279,8 @@ trait APIMethods510 {
(json, HttpCode.`200`(callContext))
}
}
}
}
staticResourceDocs += ResourceDoc(
updateConsumerLogoURL,
implementedInApiVersion,
@ -3543,7 +3550,7 @@ trait APIMethods510 {
case false => ViewNewStyle.customView(targetViewId, BankIdAccountId(bankId, accountId), callContext)
}
addedView <- JSONFactory400.grantAccountAccessToUser(bankId, accountId, user, view, callContext)
} yield {
val viewJson = JSONFactory300.createViewJSON(addedView)
(viewJson, HttpCode.`201`(callContext))
@ -3595,9 +3602,9 @@ trait APIMethods510 {
json.extract[PostAccountAccessJsonV510]
}
targetViewId = ViewId(postJson.view_id)
msg = getUserLacksRevokePermissionErrorMessage(viewId, targetViewId)
_ <- Helper.booleanToFuture(msg, 403, cc = cc.callContext) {
APIUtil.canRevokeAccessToView(BankIdAccountIdViewId(bankId, accountId, viewId),targetViewId, u, callContext)
}
@ -3671,7 +3678,7 @@ trait APIMethods510 {
}
targetViewId = ViewId(postJson.view_id)
msg = getUserLacksGrantPermissionErrorMessage(viewId, targetViewId)
_ <- Helper.booleanToFuture(msg, 403, cc = Some(cc)) {
APIUtil.canGrantAccessToView(BankIdAccountIdViewId(bankId, accountId, viewId) ,targetViewId, u, callContext)
}
@ -3755,7 +3762,7 @@ trait APIMethods510 {
|This endpoint provides the charge that would be applied if the Transaction Request proceeds - and a record of that charge there after.
|The customer can proceed with the Transaction by answering the security challenge.
|
|We support query transaction request by attribute
|We support query transaction request by attribute
|URL params example:/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-requests?invoiceNumber=123&referenceNumber=456
|
""".stripMargin,
@ -3789,17 +3796,17 @@ trait APIMethods510 {
(transactionRequests, callContext) <- Future(Connector.connector.vend.getTransactionRequests210(u, fromAccount, callContext)) map {
unboxFullOrFail(_, callContext, GetTransactionRequestsException)
}
(transactionRequestAttributes, callContext) <- NewStyle.function.getByAttributeNameValues(bankId, req.params, true, callContext)
transactionRequestIds = transactionRequestAttributes.map(_.transactionRequestId)
(transactionRequestAttributes, callContext) <- NewStyle.function.getByAttributeNameValues(bankId, req.params, true, callContext)
transactionRequestIds = transactionRequestAttributes.map(_.transactionRequestId)
transactionRequestsFiltered = if(req.params.isEmpty)
transactionRequests
else
transactionRequests.filter(transactionRequest => transactionRequestIds.contains(transactionRequest.id))
transactionRequests.filter(transactionRequest => transactionRequestIds.contains(transactionRequest.id))
} yield {
val json = JSONFactory510.createTransactionRequestJSONs(transactionRequestsFiltered, transactionRequestAttributes)
(json, HttpCode.`200`(callContext))
}
}
@ -3848,7 +3855,7 @@ trait APIMethods510 {
}
}
staticResourceDocs += ResourceDoc(
getAccountAccessByUserId,
implementedInApiVersion,
@ -3881,8 +3888,8 @@ trait APIMethods510 {
(JSONFactory400.createAccountsMinimalJson400(accountAccess), HttpCode.`200`(callContext))
}
}
staticResourceDocs += ResourceDoc(
getApiTags,
implementedInApiVersion,
@ -4130,7 +4137,7 @@ trait APIMethods510 {
}
}
}
staticResourceDocs += ResourceDoc(
updateCounterpartyLimit,
implementedInApiVersion,
@ -4346,7 +4353,7 @@ trait APIMethods510 {
defaultToDate: Date,
callContext: Option[CallContext]
)
} yield {
(CounterpartyLimitStatusV510(
counterparty_limit_id = counterpartyLimit.counterpartyLimitId: String,
@ -4725,14 +4732,14 @@ trait APIMethods510 {
fromAccountRoutingSchemeOBPFormat = if(fromAccountRoutingScheme.equalsIgnoreCase("AccountNo")) "ACCOUNT_NUMBER" else StringHelpers.snakify(fromAccountRoutingScheme).toUpperCase
fromAccountRouting = postConsentRequestJsonV510.from_account.account_routing.copy(scheme =fromAccountRoutingSchemeOBPFormat)
fromAccountTweaked = postConsentRequestJsonV510.from_account.copy(account_routing = fromAccountRouting)
toAccountRoutingScheme = postConsentRequestJsonV510.to_account.account_routing.scheme
toAccountRoutingSchemeOBPFormat = if(toAccountRoutingScheme.equalsIgnoreCase("AccountNo")) "ACCOUNT_NUMBER" else StringHelpers.snakify(toAccountRoutingScheme).toUpperCase
toAccountRouting = postConsentRequestJsonV510.to_account.account_routing.copy(scheme =toAccountRoutingSchemeOBPFormat)
toAccountTweaked = postConsentRequestJsonV510.to_account.copy(account_routing = toAccountRouting)
fromBankAccountRoutings = BankAccountRoutings(
bank = BankRoutingJson(postConsentRequestJsonV510.from_account.bank_routing.scheme, postConsentRequestJsonV510.from_account.bank_routing.address),
account = BranchRoutingJsonV141(fromAccountRoutingSchemeOBPFormat, postConsentRequestJsonV510.from_account.account_routing.address),
@ -4745,7 +4752,7 @@ trait APIMethods510 {
(_, callContext) <- NewStyle.function.getBankAccountByRoutings(fromBankAccountRoutings, callContext)
postConsentRequestJsonTweaked = postConsentRequestJsonV510.copy(
from_account = fromAccountTweaked,
from_account = fromAccountTweaked,
to_account = toAccountTweaked
)
createdConsentRequest <- Future(ConsentRequests.consentRequestProvider.vend.createConsentRequest(
@ -4795,7 +4802,7 @@ trait APIMethods510 {
regulatedEntityAttributeType <- NewStyle.function.tryons(failMsg, 400, cc.callContext) {
RegulatedEntityAttributeType.withName(postedData.attribute_type)
}
(attribute, callContext) <- RegulatedEntityAttributeNewStyle.createOrUpdateRegulatedEntityAttribute(
regulatedEntityId = RegulatedEntityId(entityId),
regulatedEntityAttributeId = None,
@ -5195,11 +5202,11 @@ trait APIMethods510 {
|
|Get the all WebUiProps key values, those props key with "webui_" can be stored in DB, this endpoint get all from DB.
|
|url query parameter:
|url query parameter:
|active: It must be a boolean string. and If active = true, it will show
| combination of explicit (inserted) + implicit (default) method_routings.
|
|eg:
|eg:
|${getObpApiRoot}/v5.1.0/webui-props
|${getObpApiRoot}/v5.1.0/webui-props?active=true
|
@ -5266,7 +5273,7 @@ trait APIMethods510 {
List(apiTagSystemView),
Some(List(canCreateSystemViewPermission))
)
lazy val addSystemViewPermission : OBPEndpoint = {
case "system-views" :: ViewId(viewId) :: "permissions" :: Nil JsonPost json -> _ => {
cc => implicit val ec = EndpointContext(Some(cc))
@ -5289,7 +5296,7 @@ trait APIMethods510 {
}
}
resourceDocs += ResourceDoc(
deleteSystemViewPermission,
implementedInApiVersion,
@ -5316,6 +5323,8 @@ trait APIMethods510 {
} yield (true, HttpCode.`204`(cc.callContext))
}
}
}
}
@ -5326,4 +5335,3 @@ object APIMethods510 extends RestHelper with APIMethods510 {
rd => (rd.partialFunctionName, rd.implementedInApiVersion.toString())
}.toList
}

View File

@ -0,0 +1,456 @@
-- HOW TO RUN THIS SCRIPT
-- For those of us that don't use postgres every day:
-- 1) You will need to have access to a postgres user that can create roles and views etc.
-- 2) You will probably want that postgres user to have easy access to your file system so you can run this script and tweak it if need be.
--That means.
--1) You probably want to have a postgres user with the same name as your linux or mac username.
--So:
--sudo -u postgres psql
--CREATE ROLE <YOURLINUXUSERNAME> WITH LOGIN SUPERUSER CREATEDB CREATEROLE;
--this step is not required but
--CREATE DATABASE <YOURLINUXUSERNAME> OWNER <YOURLINUXUSERNAME>;
--now quit with \q
--now psql
--now you will be logged in and have access to your normal home directory.
--now connect to the OBP database you want e.g.:
--\c sandbox
--now run the script from within the psql shell:
--\i ~/Documents/workspace_2024/OBP-API-C/OBP-API/obp-api/src/main/scripts/sql/create_oidc_user_and_views.sql
--or run it from the linux terminal specifying the database
--psql -d sandbox -f ~/Documents/workspace_2024/OBP-API-C/OBP-API/obp-api/src/main/scripts/sql/create_oidc_user_and_views.sql
--either way, check the output of the script carefully.
--you might want to login as the oidc_user and try the two views you have access to.
-- =============================================================================
-- OBP-API OIDC User Setup Script
-- =============================================================================
-- This script creates a dedicated OIDC database user and provides read-only
-- access to the authuser table via a view.
--
-- ⚠️ SECURITY WARNING: This view exposes password hashes and salts!
-- Only run this script if you understand the security implications.
--
-- Prerequisites:
-- 1. Run this script as a PostgreSQL superuser or database owner
-- 2. Ensure the OBP database exists and authuser table is created
-- 3. Update the database connection parameters below as needed
-- 4. IMPORTANT: Review and implement additional security measures below
--
-- Required Security Measures:
-- 1. Use SSL/TLS encrypted connections to the database
-- 2. Restrict database access by IP address in pg_hba.conf
-- 3. Use a very strong password for the OIDC user
-- 4. Monitor and audit access to this view
-- 5. Consider regular password rotation for the OIDC user
--
-- Usage:
-- psql -h localhost -p 5432 -d your_obp_database -U your_admin_user -f create_oidc_user_and_views.sql
-- e.g.
-- psql -h localhost -p 5432 -d sandbox -U obp -f ~/Documents/workspace_2024/OBP-API-C/OBP-API/obp-api/src/main/scripts/sql/create_oidc_user_and_views.sql
--psql -d sandbox -f ~/Documents/workspace_2024/OBP-API-C/OBP-API/obp-api/src/main/scripts/sql/create_oidc_user_and_views.sql
-- If any difficulties see the TOP OF THIS FILE for step by step instructions.
-- =============================================================================
-- Database connection parameters (update these to match your OBP configuration)
-- These should match the values in your OBP-API props file (db.url)
\set DB_HOST 'localhost'
\set DB_PORT '5432'
\set DB_NAME 'sandbox'
-- OIDC user credentials
-- ⚠️ SECURITY: Change this to a strong password (20+ chars, mixed case, numbers, symbols)
\set OIDC_USER 'oidc_user'
\set OIDC_PASSWORD 'lakij8777fagg'
-- OIDC admin user credentials (for client administration)
-- ⚠️ SECURITY: Change this to a strong password (20+ chars, mixed case, numbers, symbols)
\set OIDC_ADMIN_USER 'oidc_admin'
\set OIDC_ADMIN_PASSWORD 'fhka77uefassEE'
-- =============================================================================
-- 1. Connect to the OBP database
-- =============================================================================
\echo 'Connecting to OBP database...'
\c :DB_NAME
-- =============================================================================
-- 2. Create OIDC user role
-- =============================================================================
\echo 'Creating OIDC user role...'
-- Drop the users if they already exist (for re-running the script)
DROP USER IF EXISTS :OIDC_USER;
DROP USER IF EXISTS :OIDC_ADMIN_USER;
-- NOTE above will NOT drop if the users own other objects (which they will)
-- so to make sure we change the password use:
ALTER ROLE :OIDC_USER WITH PASSWORD :OIDC_PASSWORD;
ALTER ROLE :OIDC_ADMIN_USER WITH PASSWORD :OIDC_ADMIN_PASSWORD;
-- Create the OIDC user with limited privileges
CREATE USER :OIDC_USER WITH
PASSWORD :'OIDC_PASSWORD'
NOSUPERUSER
NOCREATEDB
NOCREATEROLE
NOINHERIT
LOGIN
NOREPLICATION
NOBYPASSRLS;
-- Set connection limit for the OIDC user
ALTER USER :OIDC_USER CONNECTION LIMIT 10;
-- Create the OIDC admin user with limited privileges
CREATE USER :OIDC_ADMIN_USER WITH
PASSWORD :'OIDC_ADMIN_PASSWORD'
NOSUPERUSER
NOCREATEDB
NOCREATEROLE
NOINHERIT
LOGIN
NOREPLICATION
NOBYPASSRLS;
-- need this so the admin can create rows
GRANT USAGE, SELECT ON SEQUENCE consumer_id_seq TO :OIDC_ADMIN_USER;
-- double check this
GRANT USAGE, SELECT ON SEQUENCE consumer_id_seq TO oidc_admin;
-- Set connection limit for the OIDC admin user
ALTER USER :OIDC_ADMIN_USER CONNECTION LIMIT 5;
\echo 'OIDC users created successfully.'
-- =============================================================================
-- 3. Create read-only view for authuser table
-- =============================================================================
\echo 'Creating read-only view for OIDC access to authuser...'
-- Drop the view if it already exists
DROP VIEW IF EXISTS v_oidc_users CASCADE;
-- Create a read-only view exposing only necessary authuser fields for OIDC
-- TODO: Consider excluding locked users by joining with mappedbadloginattempt table
-- and checking mbadattemptssinceresetorsuccess against max.bad.login.attempts prop
CREATE VIEW v_oidc_users AS
SELECT
ru.userid_ AS user_id,
au.username,
au.firstname,
au.lastname,
au.email,
au.validated,
au.provider,
au.password_pw,
au.password_slt,
au.createdat,
au.updatedat
FROM authuser au
INNER JOIN resourceuser ru ON au.user_c = ru.id
WHERE au.validated = true -- Only expose validated users to OIDC service
ORDER BY au.username;
-- Add comment to the view for documentation
COMMENT ON VIEW v_oidc_users IS 'Read-only view of authuser and resourceuser tables for OIDC service access. Only includes validated users and returns user_id from resourceuser.userid_. WARNING: Includes password hash and salt for OIDC credential verification - ensure secure access.';
\echo 'OIDC users view created successfully.'
-- =============================================================================
-- 3b. Create read-only view for consumer table (OIDC clients)
-- =============================================================================
\echo 'Creating read-only view for OIDC access to consumers...'
-- Drop the view if it already exists
DROP VIEW IF EXISTS v_oidc_clients CASCADE;
-- Create a read-only view exposing necessary consumer fields for OIDC
-- Note: Some OIDC-specific fields like grant_types and scopes may not exist in current schema
-- TODO: Add grant_types and scopes fields to consumer table if needed for full OIDC compliance
CREATE VIEW v_oidc_clients AS
SELECT
key_c as client_id,
secret as client_secret,
redirecturl as redirect_uris,
'authorization_code,refresh_token' as grant_types, -- Default OIDC grant types
'openid,profile,email' as scopes, -- Default OIDC scopes
name as client_name,
'code' as response_types,
'client_secret_post' as token_endpoint_auth_method,
createdat as created_at
FROM consumer
WHERE isactive = true -- Only expose active consumers to OIDC service
ORDER BY client_name;
-- Add comment to the view for documentation
COMMENT ON VIEW v_oidc_clients IS 'Read-only view of consumer table for OIDC service access. Only includes active consumers. Note: grant_types and scopes are hardcoded defaults - consider adding these fields to consumer table for full OIDC compliance.';
\echo 'OIDC clients view created successfully.'
-- =============================================================================
-- 3c. Create read-write view for consumer table administration (OIDC clients admin)
-- =============================================================================
\echo 'Creating admin view for OIDC client management...'
-- Drop the view if it already exists
DROP VIEW IF EXISTS v_oidc_admin_clients CASCADE;
-- Create a view that exposes all consumer fields for full CRUD operations
CREATE VIEW v_oidc_admin_clients AS
SELECT
name
,apptype
,description
,developeremail
,sub
,consumerid
,createdat
,updatedat
,secret
,azp
,aud
,iss
,redirecturl
,logourl
,userauthenticationurl
,clientcertificate
,company
,key_c
,isactive
FROM consumer
ORDER BY name;
-- Add comment to the view for documentation
COMMENT ON VIEW v_oidc_admin_clients IS 'Full admin view of consumer table for OIDC service administration. Provides complete CRUD access to all consumer fields for client management operations.';
\echo 'OIDC admin clients view created successfully.'
-- =============================================================================
-- 4. Grant appropriate permissions to OIDC user
-- =============================================================================
\echo 'Granting permissions to OIDC user...'
-- Grant CONNECT privilege on the database
GRANT CONNECT ON DATABASE :DB_NAME TO :OIDC_USER;
GRANT CONNECT ON DATABASE :DB_NAME TO :OIDC_ADMIN_USER;
-- Grant USAGE on the public schema (or specific schema where authuser exists)
GRANT USAGE ON SCHEMA public TO :OIDC_USER;
GRANT USAGE ON SCHEMA public TO :OIDC_ADMIN_USER;
-- Grant SELECT permission on the OIDC views (oidc_user - read-only access)
GRANT SELECT ON v_oidc_users TO :OIDC_USER;
GRANT SELECT ON v_oidc_clients TO :OIDC_USER;
-- Grant full CRUD permissions on the admin view and underlying consumer table (oidc_admin_user only)
GRANT SELECT, INSERT, UPDATE, DELETE ON consumer TO :OIDC_ADMIN_USER;
GRANT SELECT, INSERT, UPDATE, DELETE ON v_oidc_admin_clients TO :OIDC_ADMIN_USER;
-- Explicitly revoke any other permissions to ensure proper access control
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM :OIDC_USER;
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM :OIDC_USER;
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM :OIDC_USER;
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM :OIDC_ADMIN_USER;
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM :OIDC_ADMIN_USER;
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM :OIDC_ADMIN_USER;
-- Grant permissions on the views again (in case they were revoked above)
-- OIDC_USER: Read-only access to users and active clients
GRANT SELECT ON v_oidc_users TO :OIDC_USER;
GRANT SELECT ON v_oidc_clients TO :OIDC_USER;
-- OIDC_ADMIN_USER: Full CRUD access to client administration
GRANT SELECT, INSERT, UPDATE, DELETE ON consumer TO :OIDC_ADMIN_USER;
GRANT SELECT, INSERT, UPDATE, DELETE ON v_oidc_admin_clients TO :OIDC_ADMIN_USER;
\echo 'Permissions granted successfully.'
-- =============================================================================
-- 5. Create additional security measures
-- =============================================================================
\echo 'Implementing additional security measures...'
-- Set default privileges to prevent future access to new objects
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE ALL ON TABLES FROM :OIDC_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE ALL ON SEQUENCES FROM :OIDC_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM :OIDC_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE ALL ON TABLES FROM :OIDC_ADMIN_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE ALL ON SEQUENCES FROM :OIDC_ADMIN_USER;
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM :OIDC_ADMIN_USER;
\echo 'Security measures implemented successfully.'
-- =============================================================================
-- 6. Verify the setup
-- =============================================================================
\echo 'Verifying OIDC setup...'
-- Check if users exist
SELECT 'OIDC User exists: ' || CASE WHEN EXISTS (
SELECT 1 FROM pg_user WHERE usename = :'OIDC_USER'
) THEN 'YES' ELSE 'NO' END AS oidc_user_check;
SELECT 'OIDC Admin User exists: ' || CASE WHEN EXISTS (
SELECT 1 FROM pg_user WHERE usename = :'OIDC_ADMIN_USER'
) THEN 'YES' ELSE 'NO' END AS oidc_admin_user_check;
-- Check if views exist and have data
SELECT 'Users view exists: ' || CASE WHEN EXISTS (
SELECT 1 FROM information_schema.views
WHERE table_name = 'v_oidc_users' AND table_schema = 'public'
) THEN 'YES' ELSE 'NO' END AS users_view_check;
SELECT 'Clients view exists: ' || CASE WHEN EXISTS (
SELECT 1 FROM information_schema.views
WHERE table_name = 'v_oidc_clients' AND table_schema = 'public'
) THEN 'YES' ELSE 'NO' END AS clients_view_check;
SELECT 'Admin clients view exists: ' || CASE WHEN EXISTS (
SELECT 1 FROM information_schema.views
WHERE table_name = 'v_oidc_admin_clients' AND table_schema = 'public'
) THEN 'YES' ELSE 'NO' END AS admin_clients_view_check;
-- Show row counts in the views (if accessible)
SELECT 'Validated users count: ' || COUNT(*) AS user_count
FROM v_oidc_users;
SELECT 'Active clients count: ' || COUNT(*) AS client_count
FROM v_oidc_clients;
SELECT 'Total clients count (admin view): ' || COUNT(*) AS total_client_count
FROM v_oidc_admin_clients;
-- Display the permissions granted to OIDC users
SELECT 'OIDC_USER permissions:' AS permission_info;
SELECT
table_schema,
table_name,
privilege_type,
is_grantable
FROM information_schema.role_table_grants
WHERE grantee = :'OIDC_USER'
ORDER BY table_schema, table_name;
SELECT 'OIDC_ADMIN_USER permissions:' AS permission_info;
SELECT
table_schema,
table_name,
privilege_type,
is_grantable
FROM information_schema.role_table_grants
WHERE grantee = :'OIDC_ADMIN_USER'
ORDER BY table_schema, table_name;
-- =============================================================================
-- 7. Display connection information
-- =============================================================================
\echo ''
\echo '====================================================================='
\echo 'OIDC User Setup Complete!'
\echo '====================================================================='
\echo ''
\echo 'Connection details for your OIDC service:'
\echo 'Database Host: ' :DB_HOST
\echo 'Database Port: ' :DB_PORT
\echo 'Database Name: ' :DB_NAME
\echo ''
\echo 'OIDC User (read-only):'
\echo 'Username: ' :OIDC_USER
\echo 'Password: [REDACTED - check script variables]'
\echo 'Available views: v_oidc_users, v_oidc_clients'
\echo 'Permissions: SELECT only (read-only access)'
\echo ''
\echo 'OIDC Admin User (full CRUD for client management):'
\echo 'Username: ' :OIDC_ADMIN_USER
\echo 'Password: [REDACTED - check script variables]'
\echo 'Available views: v_oidc_admin_clients'
\echo 'Permissions: SELECT, INSERT, UPDATE, DELETE (full CRUD access)'
\echo ''
\echo 'Test connection commands:'
\echo '# OIDC User (read-only):'
\echo 'psql -h ' :DB_HOST ' -p ' :DB_PORT ' -d ' :DB_NAME ' -U ' :OIDC_USER ' -c "SELECT COUNT(*) FROM v_oidc_users;"'
\echo 'psql -h ' :DB_HOST ' -p ' :DB_PORT ' -d ' :DB_NAME ' -U ' :OIDC_USER ' -c "SELECT COUNT(*) FROM v_oidc_clients;"'
\echo '# OIDC Admin User (full CRUD):'
\echo 'psql -h ' :DB_HOST ' -p ' :DB_PORT ' -d ' :DB_NAME ' -U ' :OIDC_ADMIN_USER ' -c "SELECT COUNT(*) FROM v_oidc_admin_clients;"'
\echo ''
\echo '====================================================================='
\echo '⚠️ CRITICAL SECURITY WARNINGS ⚠️'
\echo '====================================================================='
\echo 'This view exposes PASSWORD HASHES AND SALTS - implement these measures:'
\echo ''
\echo '1. DATABASE CONNECTION SECURITY:'
\echo ' - Configure SSL/TLS encryption in postgresql.conf'
\echo ' - Add "sslmode=require" to OIDC service connection string'
\echo ' - Use certificate-based authentication if possible'
\echo ''
\echo '2. ACCESS CONTROL:'
\echo ' - Restrict access by IP in pg_hba.conf:'
\echo ' "hostssl dbname oidc_user your.oidc.server.ip/32 md5"'
\echo ' - Use firewall rules to limit database port (5432) access'
\echo ''
\echo '3. MONITORING & AUDITING:'
\echo ' - Enable PostgreSQL query logging'
\echo ' - Monitor failed login attempts'
\echo ' - Set up alerts for unusual access patterns'
\echo ' - Regularly review access logs'
\echo ''
\echo '4. PASSWORD SECURITY:'
\echo ' - Use a strong password for oidc_user (min 20 chars, mixed case, symbols)'
\echo ' - Rotate the password regularly (e.g., quarterly)'
\echo ' - Store password securely (vault/secrets manager)'
\echo ''
\echo '5. ADDITIONAL RECOMMENDATIONS:'
\echo ' - Consider using connection pooling with authentication'
\echo ' - Implement rate limiting on the OIDC service side'
\echo ' - Use read-only database replicas if possible'
\echo ' - Regular security audits of database access'
\echo ''
\echo 'BASIC INFO:'
\echo '- OIDC_USER: Read-only access to validated authuser records and active clients'
\echo '- OIDC_ADMIN_USER: Full CRUD access to all client records for administration'
\echo '- OIDC_USER connection limit: 10 concurrent connections'
\echo '- OIDC_ADMIN_USER connection limit: 5 concurrent connections'
\echo '- Client view uses hardcoded grant_types and scopes (consider adding to schema)'
\echo ''
\echo '====================================================================='
-- =============================================================================
-- End of script
-- =============================================================================