mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 15:27:01 +00:00
Merge remote-tracking branch 'OBP-API/develop' into develop
# Conflicts: # .github/workflows/build_package.yml
This commit is contained in:
commit
c073aed733
100
.github/workflows/build_container_develop_branch.yml
vendored
100
.github/workflows/build_container_develop_branch.yml
vendored
@ -3,12 +3,9 @@ name: Build and publish container develop
|
||||
# read-write repo token
|
||||
# access to secrets
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [build maven artifact]
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
types:
|
||||
- completed
|
||||
|
||||
env:
|
||||
## Sets environment variable
|
||||
@ -19,35 +16,78 @@ env:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
services:
|
||||
# Label used to access the service container
|
||||
redis:
|
||||
# Docker Hub image
|
||||
image: redis
|
||||
ports:
|
||||
# Opens tcp port 6379 on the host and service container
|
||||
- 6379:6379
|
||||
# Set health checks to wait until redis has started
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v7
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
script: |
|
||||
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{github.event.workflow_run.id }},
|
||||
});
|
||||
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "push"
|
||||
})[0];
|
||||
var download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/push.zip', Buffer.from(download.data));
|
||||
- run: unzip push.zip
|
||||
|
||||
- name: prepare the artifact
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
mkdir -p obp-api/target/
|
||||
cp obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war
|
||||
cp obp-api/src/main/resources/props/sample.props.template obp-api/src/main/resources/props/production.default.props
|
||||
echo connector=star > obp-api/src/main/resources/props/test.default.props
|
||||
echo starConnector_supported_types=mapped,internal >> obp-api/src/main/resources/props/test.default.props
|
||||
echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props
|
||||
echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props
|
||||
echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo kafka.akka.timeout = 9 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo remotedata.timeout = 10 >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
|
||||
MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod
|
||||
|
||||
- name: Save .war artifact
|
||||
run: |
|
||||
mkdir -p ./push
|
||||
cp obp-api/target/obp-api-1.*.war ./push/
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: push/
|
||||
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
|
||||
@ -1,54 +1,93 @@
|
||||
name: Build and publish container non develop
|
||||
|
||||
# read-write repo token
|
||||
# access to secrets
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [build maven artifact]
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
- '!develop'
|
||||
types:
|
||||
- completed
|
||||
|
||||
env:
|
||||
## Sets environment variable
|
||||
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
|
||||
DOCKER_HUB_REPOSITORY: obp-api
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
services:
|
||||
# Label used to access the service container
|
||||
redis:
|
||||
# Docker Hub image
|
||||
image: redis
|
||||
ports:
|
||||
# Opens tcp port 6379 on the host and service container
|
||||
- 6379:6379
|
||||
# Set health checks to wait until redis has started
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v7
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}"
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
script: |
|
||||
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{github.event.workflow_run.id }},
|
||||
});
|
||||
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "push"
|
||||
})[0];
|
||||
var download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/push.zip', Buffer.from(download.data));
|
||||
- run: unzip push.zip
|
||||
|
||||
- name: prepare the artifact
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
mkdir -p obp-api/target/
|
||||
cp obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war
|
||||
cp obp-api/src/main/resources/props/sample.props.template obp-api/src/main/resources/props/production.default.props
|
||||
echo connector=star > obp-api/src/main/resources/props/test.default.props
|
||||
echo starConnector_supported_types=mapped,internal >> obp-api/src/main/resources/props/test.default.props
|
||||
echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props
|
||||
echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props
|
||||
echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo kafka.akka.timeout = 9 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo remotedata.timeout = 10 >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
|
||||
MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod
|
||||
|
||||
- name: Save .war artifact
|
||||
run: |
|
||||
mkdir -p ./push
|
||||
cp obp-api/target/obp-api-1.*.war ./push/
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.sha }}
|
||||
path: push/
|
||||
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
name: Build and publish commiter container
|
||||
|
||||
# read-write repo token
|
||||
# access to secrets
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [Build on Pull Request]
|
||||
types:
|
||||
- completed
|
||||
|
||||
env:
|
||||
## Sets environment variable
|
||||
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{github.event.workflow_run.id }},
|
||||
});
|
||||
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "pr"
|
||||
})[0];
|
||||
var download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data));
|
||||
- run: unzip pr.zip
|
||||
- name: Get user from file
|
||||
run: echo "USER_NAME=$(cat UN)" >> $GITHUB_ENV
|
||||
|
||||
- name: prepare the artifact
|
||||
run: |
|
||||
mkdir -p obp-api/target/
|
||||
cp obp-api-1.10.1.war obp-api/target/obp-api-1.10.1.war
|
||||
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io
|
||||
docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:${{ github.event.pull_request.head }} --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:latest
|
||||
docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }} --all-tags
|
||||
echo docker done
|
||||
82
.github/workflows/build_package.yml
vendored
82
.github/workflows/build_package.yml
vendored
@ -1,82 +0,0 @@
|
||||
name: build maven artifact
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
# Label used to access the service container
|
||||
redis:
|
||||
# Docker Hub image
|
||||
image: redis
|
||||
ports:
|
||||
# Opens tcp port 6379 on the host and service container
|
||||
- 6379:6379
|
||||
# Set health checks to wait until redis has started
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build with Maven
|
||||
run: |
|
||||
cp obp-api/src/main/resources/props/sample.props.template obp-api/src/main/resources/props/production.default.props
|
||||
echo connector=star > obp-api/src/main/resources/props/test.default.props
|
||||
echo starConnector_supported_types=mapped,internal >> obp-api/src/main/resources/props/test.default.props
|
||||
echo hostname=http://localhost:8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo tests.port=8016 >> obp-api/src/main/resources/props/test.default.props
|
||||
echo End of minimum settings >> obp-api/src/main/resources/props/test.default.props
|
||||
echo payments_enabled=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo importer_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.updateBankAccountsTransaction=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo messageQueue.createBankAccounts=false >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_account_creation=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_sandbox_data_import=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo sandbox_data_import_secret=change_me >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_account_deletion=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allowed_internal_redirect_urls = /,/oauth/authorize >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_enabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo transactionRequests_supported_types=SEPA,SANDBOX_TAN,FREE_FORM,COUNTERPARTY,ACCOUNT,SIMPLE >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo openredirects.hostname.whitlelist=http://127.0.0.1,http://localhost >> obp-api/src/main/resources/props/test.default.props
|
||||
echo remotedata.secret = foobarbaz >> obp-api/src/main/resources/props/test.default.props
|
||||
echo allow_public_views=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo SIMPLE_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo ACCOUNT_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo FREE_FORM_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo COUNTERPARTY_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
echo SEPA_CREDIT_TRANSFERS_OTP_INSTRUCTION_TRANSPORT=dummy >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo allow_oauth2_login=true >> obp-api/src/main/resources/props/test.default.props
|
||||
echo oauth2.jwk_set.url=https://www.googleapis.com/oauth2/v3/certs >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props
|
||||
|
||||
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
|
||||
MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod
|
||||
|
||||
- name: Save .war artifact
|
||||
run: |
|
||||
mkdir -p ./push
|
||||
cp obp-api/target/obp-api-1.*.war ./push/
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: push
|
||||
path: push/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
17
.github/workflows/build_pull_request.yml
vendored
17
.github/workflows/build_pull_request.yml
vendored
@ -27,9 +27,9 @@ jobs:
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
@ -73,15 +73,12 @@ jobs:
|
||||
|
||||
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
|
||||
MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod
|
||||
- name: Save user name and .war artifact
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo ${{ github.event.pull_request.user.login }} > ./pr/UN
|
||||
cp obp-api/target/obp-api-1.10.1.war ./pr/obp-api-1.10.1.war
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pr
|
||||
path: pr/
|
||||
echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin docker.io
|
||||
docker build . --file .github/Dockerfile_PreBuild --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }}:latest
|
||||
docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/obp-api-${{ env.USER_NAME }} --all-tags
|
||||
echo docker done
|
||||
|
||||
|
||||
|
||||
|
||||
6
.github/workflows/run_trivy.yml
vendored
6
.github/workflows/run_trivy.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- id: trivy-db
|
||||
name: Check trivy db sha
|
||||
env:
|
||||
@ -31,7 +31,7 @@ jobs:
|
||||
sha=$(gh api -H "${headers}" "${endpoint}" | jq --raw-output "${jqFilter}")
|
||||
echo "Trivy DB sha256:${sha}"
|
||||
echo "::set-output name=sha::${sha}"
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: .trivy
|
||||
key: ${{ runner.os }}-trivy-db-${{ steps.trivy-db.outputs.sha }}
|
||||
@ -49,6 +49,6 @@ jobs:
|
||||
- name: Fix .trivy permissions
|
||||
run: sudo chown -R $(stat . -c %u:%g) .trivy
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
@ -157,6 +157,12 @@ jwt.use.ssl=false
|
||||
# truststore.password.redis = truststore-password
|
||||
|
||||
|
||||
## Trust stores is a list of trusted CA certificates
|
||||
## Public certificate for the CA (used by clients and servers to validate signatures)
|
||||
# truststore.path.tpp_signature = path/to/ca.p12
|
||||
# truststore.password.tpp_signature = truststore-password
|
||||
|
||||
|
||||
## Enable writing API metrics (which APIs are called) to RDBMS
|
||||
write_metrics=true
|
||||
## Enable writing connector metrics (which methods are called)to RDBMS
|
||||
|
||||
@ -311,6 +311,8 @@ of the PSU at this ASPSP.
|
||||
(Full(u), callContext) <- authenticatedAccess(cc)
|
||||
_ <- passesPsd2Aisp(callContext)
|
||||
(availablePrivateAccounts, callContext) <- NewStyle.function.getAccountListOfBerlinGroup(u, callContext)
|
||||
(canReadBalancesAccounts, callContext) <- NewStyle.function.getAccountCanReadBalancesOfBerlinGroup(u, callContext)
|
||||
(canReadTransactionsAccounts, callContext) <- NewStyle.function.getAccountCanReadTransactionsOfBerlinGroup(u, callContext)
|
||||
(accounts, callContext) <- NewStyle.function.getBankAccounts(availablePrivateAccounts, callContext)
|
||||
bankAccountsFiltered = accounts.filter(bankAccount =>
|
||||
bankAccount.attributes.toList.flatten.find(attribute =>
|
||||
@ -320,7 +322,12 @@ of the PSU at this ASPSP.
|
||||
).isEmpty)
|
||||
|
||||
} yield {
|
||||
(JSONFactory_BERLIN_GROUP_1_3.createAccountListJson(bankAccountsFiltered, u), callContext)
|
||||
(JSONFactory_BERLIN_GROUP_1_3.createAccountListJson(
|
||||
bankAccountsFiltered,
|
||||
canReadBalancesAccounts,
|
||||
canReadTransactionsAccounts,
|
||||
u
|
||||
), callContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,8 +49,8 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
)
|
||||
|
||||
case class CoreAccountLinksJsonV13(
|
||||
balances: LinkHrefJson //,
|
||||
// trasactions: LinkHrefJson // These links are only supported, when the corresponding consent has been already granted.
|
||||
balances: LinkHrefJson,
|
||||
transactions: Option[LinkHrefJson] = None // These links are only supported, when the corresponding consent has been already granted.
|
||||
)
|
||||
|
||||
case class CoreAccountBalancesJson(
|
||||
@ -69,7 +69,6 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
product: String,
|
||||
cashAccountType: String,
|
||||
// status: String="enabled",
|
||||
bic: String,
|
||||
// linkedAccounts: String ="string",
|
||||
// usage: String ="PRIV",
|
||||
// details: String ="",
|
||||
@ -308,10 +307,17 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
)
|
||||
|
||||
|
||||
def createAccountListJson(bankAccounts: List[BankAccount], user: User): CoreAccountsJsonV13 = {
|
||||
def createAccountListJson(bankAccounts: List[BankAccount],
|
||||
canReadBalancesAccounts: List[BankIdAccountId],
|
||||
canReadTransactionsAccounts: List[BankIdAccountId],
|
||||
user: User): CoreAccountsJsonV13 = {
|
||||
CoreAccountsJsonV13(bankAccounts.map {
|
||||
x =>
|
||||
val (iBan: String, bBan: String) = getIbanAndBban(x)
|
||||
val commonPath = s"${OBP_BERLIN_GROUP_1_3.apiVersion.urlPrefix}/${OBP_BERLIN_GROUP_1_3.version}/accounts/${x.accountId.value}"
|
||||
val balanceRef = LinkHrefJson(s"/$commonPath/accounts/${x.accountId.value}/balances")
|
||||
val transactionRef = LinkHrefJson(s"/$commonPath/accounts/${x.accountId.value}/transactions")
|
||||
val canReadTransactions = canReadTransactionsAccounts.map(_.accountId.value).contains(x.accountId.value)
|
||||
|
||||
|
||||
CoreAccountJsonV13(
|
||||
@ -320,10 +326,12 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
bban = bBan,
|
||||
currency = x.currency,
|
||||
name = x.name,
|
||||
bic = getBicFromBankId(x.bankId.value),
|
||||
cashAccountType = x.accountType,
|
||||
product = x.accountType,
|
||||
_links = CoreAccountLinksJsonV13(LinkHrefJson(s"/${OBP_BERLIN_GROUP_1_3.apiVersion.urlPrefix}/${OBP_BERLIN_GROUP_1_3.version}/accounts/${x.accountId.value}/balances"))
|
||||
_links = CoreAccountLinksJsonV13(
|
||||
balances = balanceRef,
|
||||
transactions = if(canReadTransactions) Some(transactionRef) else None,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -340,7 +348,6 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
|
||||
bban = bBan,
|
||||
currency = x.currency,
|
||||
name = x.name,
|
||||
bic = getBicFromBankId(x.bankId.value),
|
||||
cashAccountType = x.accountType,
|
||||
product = x.accountType,
|
||||
_links = CoreAccountLinksJsonV13(LinkHrefJson(s"/${OBP_BERLIN_GROUP_1_3.apiVersion.urlPrefix}/${OBP_BERLIN_GROUP_1_3.version}/accounts/${x.accountId.value}/balances"))
|
||||
|
||||
@ -61,7 +61,7 @@ object Redis extends MdcLoggable {
|
||||
|
||||
// Load the CA certificate
|
||||
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType)
|
||||
val trustStorePassword = APIUtil.getPropsValue("keystore.password.redis")
|
||||
val trustStorePassword = APIUtil.getPropsValue("truststore.password.redis")
|
||||
.getOrElse(APIUtil.initPasswd).toCharArray
|
||||
val truststorePath = APIUtil.getPropsValue("truststore.path.redis").getOrElse("")
|
||||
val trustStoreStream = new FileInputStream(truststorePath)
|
||||
|
||||
@ -12,47 +12,48 @@
|
||||
"logo":"https://static.openbankproject.com/images/sandbox/bank_y.png",
|
||||
"website":"https://www.example.com"
|
||||
}],
|
||||
"users":[{
|
||||
"email":"robert.xuk.x@example.com",
|
||||
"user_name": "robert.xuk.x@example.com",
|
||||
"password":"5232e7",
|
||||
"display_name":"Robert XUk X"
|
||||
},{
|
||||
"email":"susan.xuk.x@example.com",
|
||||
"user_name": "susan.xuk.x@example.com",
|
||||
"password":"43ca4d",
|
||||
"display_name":"Susan XUk X"
|
||||
},{
|
||||
"email":"anil.xuk.x@example.com",
|
||||
"user_name": "anil.xuk.x@example.com",
|
||||
"password":"d8c716",
|
||||
"display_name":"Anil XUk X"
|
||||
},{
|
||||
"email":"ellie.xuk.x@example.com",
|
||||
"user_name": "ellie.xuk.x@example.com",
|
||||
"password":"6187b9",
|
||||
"display_name":"Ellie XUk X"
|
||||
},{
|
||||
"email":"robert.yuk.y@example.com",
|
||||
"user_name": "robert.yuk.y@example.com",
|
||||
"password":"e5046a",
|
||||
"display_name":"Robert YUk Y"
|
||||
},{
|
||||
"email":"susan.yuk.y@example.com",
|
||||
"user_name": "susan.yuk.y@example.com",
|
||||
"password":"5b38a6",
|
||||
"display_name":"Susan YUk Y"
|
||||
},{
|
||||
"email":"anil.yuk.y@example.com",
|
||||
"user_name": "anil.yuk.y@example.com",
|
||||
"password":"dcf03d",
|
||||
"display_name":"Anil YUk Y"
|
||||
},{
|
||||
"email":"ellie.yuk.y@example.com",
|
||||
"user_name": "ellie.yuk.y@example.com",
|
||||
"password":"4f9eaa",
|
||||
"display_name":"Ellie YUk Y"
|
||||
}],
|
||||
"users": [
|
||||
{
|
||||
"email": "robert.x.0.gh@example.com",
|
||||
"password": "V8%Ktssl(L",
|
||||
"user_name": "Robert.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "susan.x.0.gh@example.com",
|
||||
"password": "naW9u3C%bh",
|
||||
"user_name": "Susan.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "anil.x.0.gh@example.com",
|
||||
"password": "9W0RIrX-6f",
|
||||
"user_name": "Anil.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "ellie.x.0.gh@example.com",
|
||||
"password": "rMf_OHM0dW",
|
||||
"user_name": "Ellie.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "robert.y.9.gh@example.com",
|
||||
"password": "%1Z43kzt2L",
|
||||
"user_name": "Robert.Y.9.GH"
|
||||
},
|
||||
{
|
||||
"email": "susan.y.9.gh@example.com",
|
||||
"password": "oITehM!B2V",
|
||||
"user_name": "Susan.Y.9.GH"
|
||||
},
|
||||
{
|
||||
"email": "anil.y.9.gh@example.com",
|
||||
"password": "TuKaNO8oI-",
|
||||
"user_name": "Anil.Y.9.GH"
|
||||
},
|
||||
{
|
||||
"email": "ellie.y.9.gh@example.com",
|
||||
"password": "SkJDH+ds2_",
|
||||
"user_name": "Ellie.Y.9.GH"
|
||||
}
|
||||
],
|
||||
"accounts":[{
|
||||
"id":"05237266-b334-4704-a087-5b460a2ecf04",
|
||||
"bank":"psd201-bank-x--uk",
|
||||
|
||||
@ -12,39 +12,48 @@
|
||||
"logo":"https://static.openbankproject.com/images/sandbox/bank_y.png",
|
||||
"website":"https://www.example.com"
|
||||
}],
|
||||
"users":[{
|
||||
"email":"robert.x.0.gh@example.com",
|
||||
"password":"X!d1edcafd",
|
||||
"user_name":"Robert.X.0.GH"
|
||||
},{
|
||||
"email":"susan.x.0.gh@example.com",
|
||||
"password":"X!90e4e3e4",
|
||||
"user_name":"Susan.X.0.GH"
|
||||
},{
|
||||
"email":"anil.x.0.gh@example.com",
|
||||
"password":"X!eb06b005",
|
||||
"user_name":"Anil.X.0.GH"
|
||||
},{
|
||||
"email":"ellie.x.0.gh@example.com",
|
||||
"password":"X!5bc94405",
|
||||
"user_name":"Ellie.X.0.GH"
|
||||
},{
|
||||
"email":"robert.y.9.gh@example.com",
|
||||
"password":"X!039941de",
|
||||
"user_name":"Robert.Y.9.GH"
|
||||
},{
|
||||
"email":"susan.y.9.gh@example.com",
|
||||
"password":"X!bb4efa3d",
|
||||
"user_name":"Susan.Y.9.GH"
|
||||
},{
|
||||
"email":"anil.y.9.gh@example.com",
|
||||
"password":"X!098915cd",
|
||||
"user_name":"Anil.Y.9.GH"
|
||||
},{
|
||||
"email":"ellie.y.9.gh@example.com",
|
||||
"password":"X!6170b37b",
|
||||
"user_name":"Ellie.Y.9.GH"
|
||||
}],
|
||||
"users": [
|
||||
{
|
||||
"email": "robert.x.0.gh@example.com",
|
||||
"password": "V8%Ktssl(L",
|
||||
"user_name": "Robert.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "susan.x.0.gh@example.com",
|
||||
"password": "naW9u3C%bh",
|
||||
"user_name": "Susan.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "anil.x.0.gh@example.com",
|
||||
"password": "9W0RIrX-6f",
|
||||
"user_name": "Anil.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "ellie.x.0.gh@example.com",
|
||||
"password": "rMf_OHM0dW",
|
||||
"user_name": "Ellie.X.0.GH"
|
||||
},
|
||||
{
|
||||
"email": "robert.y.9.gh@example.com",
|
||||
"password": "%1Z43kzt2L",
|
||||
"user_name": "Robert.Y.9.GH"
|
||||
},
|
||||
{
|
||||
"email": "susan.y.9.gh@example.com",
|
||||
"password": "oITehM!B2V",
|
||||
"user_name": "Susan.Y.9.GH"
|
||||
},
|
||||
{
|
||||
"email": "anil.y.9.gh@example.com",
|
||||
"password": "TuKaNO8oI-",
|
||||
"user_name": "Anil.Y.9.GH"
|
||||
},
|
||||
{
|
||||
"email": "ellie.y.9.gh@example.com",
|
||||
"password": "SkJDH+ds2_",
|
||||
"user_name": "Ellie.Y.9.GH"
|
||||
}
|
||||
],
|
||||
"accounts":[{
|
||||
"id":"f65e28a5-9abe-428f-85bb-6c3c38122adb",
|
||||
"bank":"obp-bank-x-gh",
|
||||
|
||||
@ -2353,15 +2353,6 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
}
|
||||
}
|
||||
|
||||
// Function checks does a consumer specified by a parameter consumerId has at least one role provided by a parameter roles at a bank specified by a parameter bankId
|
||||
// i.e. does consumer has assigned at least one role from the list
|
||||
def hasAtLeastOneScope(bankId: String, consumerId: String, roles: List[ApiRole]): Boolean = {
|
||||
val list: List[Boolean] = for (role <- roles) yield {
|
||||
!Scope.scope.vend.getScope(if (role.requiresBankId == true) bankId else "", consumerId, role.toString).isEmpty
|
||||
}
|
||||
list.exists(_ == true)
|
||||
}
|
||||
|
||||
def hasEntitlement(bankId: String, userId: String, apiRole: ApiRole): Boolean = apiRole match {
|
||||
case RoleCombination(roles) => roles.forall(hasEntitlement(bankId, userId, _))
|
||||
case role =>
|
||||
@ -3683,7 +3674,12 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
}
|
||||
|
||||
final def checkAuthorisationToCreateTransactionRequest(viewId: ViewId, bankAccountId: BankIdAccountId, user: User, callContext: Option[CallContext]): Box[Boolean] = {
|
||||
lazy val hasCanCreateAnyTransactionRequestRole = APIUtil.hasEntitlement(bankAccountId.bankId.value, user.userId, canCreateAnyTransactionRequest)
|
||||
lazy val hasCanCreateAnyTransactionRequestRole = APIUtil.handleEntitlementsAndScopes(
|
||||
bankAccountId.bankId.value,
|
||||
user.userId,
|
||||
APIUtil.getConsumerPrimaryKey(callContext),
|
||||
List(canCreateAnyTransactionRequest)
|
||||
)
|
||||
|
||||
lazy val view = APIUtil.checkViewAccessAndReturnView(viewId, bankAccountId, Some(user), callContext)
|
||||
|
||||
|
||||
@ -130,7 +130,12 @@ object BerlinGroupSigning {
|
||||
val signatureHeaderValue = getHeaderValue(RequestHeader.Signature, requestHeaders)
|
||||
val signature = parseSignatureHeader(signatureHeaderValue).getOrElse("signature", "NONE")
|
||||
val isVerified = verifySignature(signingString, signature, certificatePem)
|
||||
if (isVerified) forwardResult else (Failure(ErrorMessages.X509PublicKeyCannotVerify), forwardResult._2)
|
||||
val isValidated = CertificateVerifier.validateCertificate(certificatePem)
|
||||
(isVerified, isValidated) match {
|
||||
case (true, true) => forwardResult
|
||||
case (true, false) => (Failure(ErrorMessages.X509PublicKeyCannotBeValidated), forwardResult._2)
|
||||
case (false, _) => (Failure(ErrorMessages.X509PublicKeyCannotVerify), forwardResult._2)
|
||||
}
|
||||
case Failure(msg, t, c) => (Failure(msg, t, c), forwardResult._2) // PEM certificate is not valid
|
||||
case _ => (Failure(ErrorMessages.X509GeneralError), forwardResult._2) // PEM certificate cannot be validated
|
||||
}
|
||||
|
||||
153
obp-api/src/main/scala/code/api/util/CertificateVerifier.scala
Normal file
153
obp-api/src/main/scala/code/api/util/CertificateVerifier.scala
Normal file
@ -0,0 +1,153 @@
|
||||
package code.api.util
|
||||
|
||||
import code.util.Helper.MdcLoggable
|
||||
|
||||
import java.io.{ByteArrayInputStream, FileInputStream}
|
||||
import java.security.KeyStore
|
||||
import java.security.cert._
|
||||
import java.util.{Base64, Collections}
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import scala.io.Source
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
object CertificateVerifier extends MdcLoggable {
|
||||
|
||||
/**
|
||||
* Loads a trust store (`.p12` file) from a configured path.
|
||||
*
|
||||
* This function:
|
||||
* - Reads the trust store password from the application properties (`truststore.path.tpp_signature`).
|
||||
* - Uses Java's `KeyStore` class to load the certificates.
|
||||
*
|
||||
* @return An `Option[KeyStore]` containing the loaded trust store, or `None` if loading fails.
|
||||
*/
|
||||
private def loadTrustStore(): Option[KeyStore] = {
|
||||
val trustStorePath = APIUtil.getPropsValue("truststore.path.tpp_signature")
|
||||
.or(APIUtil.getPropsValue("truststore.path")).getOrElse("")
|
||||
val trustStorePassword = APIUtil.getPropsValue("truststore.password.tpp_signature", "").toCharArray
|
||||
|
||||
Try {
|
||||
val trustStore = KeyStore.getInstance("PKCS12")
|
||||
val trustStoreInputStream = new FileInputStream(trustStorePath)
|
||||
try {
|
||||
trustStore.load(trustStoreInputStream, trustStorePassword)
|
||||
} finally {
|
||||
trustStoreInputStream.close()
|
||||
}
|
||||
trustStore
|
||||
} match {
|
||||
case Success(store) =>
|
||||
logger.info(s"Loaded trust store from: $trustStorePath")
|
||||
Some(store)
|
||||
case Failure(e) =>
|
||||
logger.info(s"Failed to load trust store: ${e.getMessage}")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies an X.509 certificate against the loaded trust store.
|
||||
*
|
||||
* This function:
|
||||
* - Parses the PEM certificate into an `X509Certificate` using `parsePemToX509Certificate`.
|
||||
* - Loads the trust store using `loadTrustStore()`.
|
||||
* - Extracts trusted root CAs from the trust store.
|
||||
* - Creates PKIX validation parameters and disables revocation checking.
|
||||
* - Validates the certificate using Java's `CertPathValidator`.
|
||||
*
|
||||
* @param pemCertificate The X.509 certificate in PEM format.
|
||||
* @return `true` if the certificate is valid and trusted, otherwise `false`.
|
||||
*/
|
||||
def validateCertificate(pemCertificate: String): Boolean = {
|
||||
Try {
|
||||
val certificate = parsePemToX509Certificate(pemCertificate)
|
||||
|
||||
// Load trust store
|
||||
val trustStore = loadTrustStore()
|
||||
.getOrElse(throw new Exception("Trust store could not be loaded."))
|
||||
|
||||
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
|
||||
trustManagerFactory.init(trustStore)
|
||||
|
||||
// Get trusted CAs from the trust store
|
||||
val trustAnchors = trustStore.aliases().asScala
|
||||
.filter(trustStore.isCertificateEntry)
|
||||
.map(alias => trustStore.getCertificate(alias).asInstanceOf[X509Certificate])
|
||||
.map(cert => new TrustAnchor(cert, null))
|
||||
.toSet
|
||||
.asJava // Convert Scala Set to Java Set
|
||||
|
||||
if (trustAnchors.isEmpty) throw new Exception("No trusted certificates found in trust store.")
|
||||
|
||||
// Set up PKIX parameters for validation
|
||||
val pkixParams = new PKIXParameters(trustAnchors)
|
||||
pkixParams.setRevocationEnabled(false) // Disable CRL checks
|
||||
|
||||
// Validate certificate chain
|
||||
val certPath = CertificateFactory.getInstance("X.509").generateCertPath(Collections.singletonList(certificate))
|
||||
val validator = CertPathValidator.getInstance("PKIX")
|
||||
validator.validate(certPath, pkixParams)
|
||||
|
||||
logger.info("Certificate is valid and trusted.")
|
||||
true
|
||||
} match {
|
||||
case Success(_) => true
|
||||
case Failure(e: CertPathValidatorException) =>
|
||||
logger.info(s"Certificate validation failed: ${e.getMessage}")
|
||||
false
|
||||
case Failure(e) =>
|
||||
logger.info(s"Error: ${e.getMessage}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a PEM certificate (Base64-encoded) into an `X509Certificate` object.
|
||||
*
|
||||
* This function:
|
||||
* - Removes the PEM header and footer (`-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`).
|
||||
* - Decodes the Base64-encoded certificate data.
|
||||
* - Generates and returns an `X509Certificate` object.
|
||||
*
|
||||
* @param pem The X.509 certificate in PEM format.
|
||||
* @return The parsed `X509Certificate` object.
|
||||
*/
|
||||
private def parsePemToX509Certificate(pem: String): X509Certificate = {
|
||||
val cleanedPem = pem.replaceAll("-----BEGIN CERTIFICATE-----", "")
|
||||
.replaceAll("-----END CERTIFICATE-----", "")
|
||||
.replaceAll("\\s", "")
|
||||
|
||||
val decoded = Base64.getDecoder.decode(cleanedPem)
|
||||
val certFactory = CertificateFactory.getInstance("X.509")
|
||||
certFactory.generateCertificate(new ByteArrayInputStream(decoded)).asInstanceOf[X509Certificate]
|
||||
}
|
||||
|
||||
def loadPemCertificateFromFile(filePath: String): Option[String] = {
|
||||
Try {
|
||||
val source = Source.fromFile(filePath)
|
||||
try source.getLines().mkString("\n") // Read entire file into a single string
|
||||
finally source.close()
|
||||
} match {
|
||||
case Success(pem) => Some(pem)
|
||||
case Failure(exception) =>
|
||||
logger.error(s"Failed to load PEM certificate from file: ${exception.getMessage}")
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
// change the following path if using this function to test on your localhost
|
||||
val certificatePath = "/path/to/certificate.pem"
|
||||
val pemCertificate = loadPemCertificateFromFile(certificatePath)
|
||||
|
||||
pemCertificate.foreach { pem =>
|
||||
val isValid = validateCertificate(pem)
|
||||
logger.info(s"Certificate verification result: $isValid")
|
||||
}
|
||||
|
||||
loadTrustStore().foreach { trustStore =>
|
||||
logger.info(s"Trust Store contains ${trustStore.size()} certificates.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,6 +279,7 @@ object ErrorMessages {
|
||||
val X509ThereAreNoPsd2Roles = "OBP-20308: PEM Encoded Certificate does not contain PSD2 roles."
|
||||
val X509CannotGetPublicKey = "OBP-20309: Public key cannot be found in the PEM Encoded Certificate."
|
||||
val X509PublicKeyCannotVerify = "OBP-20310: Certificate's public key cannot be used to verify signed request."
|
||||
val X509PublicKeyCannotBeValidated = "OBP-20312: Certificate's public key cannot be validated."
|
||||
val X509RequestIsNotSigned = "OBP-20311: The Request is not signed."
|
||||
|
||||
// OpenID Connect
|
||||
|
||||
@ -6,7 +6,7 @@ import java.util.UUID.randomUUID
|
||||
import akka.http.scaladsl.model.HttpMethod
|
||||
import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT}
|
||||
import code.api.{APIFailureNewStyle, Constant, JsonResponseException}
|
||||
import code.api.Constant.SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID
|
||||
import code.api.Constant.{SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID}
|
||||
import code.api.cache.Caching
|
||||
import code.api.util.APIUtil._
|
||||
import code.api.util.ApiRole.canCreateAnyTransactionRequest
|
||||
@ -73,11 +73,11 @@ import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo}
|
||||
import code.atmattribute.AtmAttribute
|
||||
import code.bankattribute.BankAttribute
|
||||
import code.connectormethod.{ConnectorMethodProvider, JsonConnectorMethod}
|
||||
import code.counterpartylimit.{CounterpartyLimit}
|
||||
import code.counterpartylimit.CounterpartyLimit
|
||||
import com.openbankproject.commons.model.CounterpartyLimitTrait
|
||||
import code.crm.CrmEvent
|
||||
import code.crm.CrmEvent.CrmEvent
|
||||
import com.openbankproject.commons.model.{CustomerAccountLinkTrait, AgentAccountLinkTrait}
|
||||
import com.openbankproject.commons.model.{AgentAccountLinkTrait, CustomerAccountLinkTrait}
|
||||
import code.dynamicMessageDoc.{DynamicMessageDocProvider, JsonDynamicMessageDoc}
|
||||
import code.dynamicResourceDoc.{DynamicResourceDocProvider, JsonDynamicResourceDoc}
|
||||
import code.endpointMapping.{EndpointMappingProvider, EndpointMappingT}
|
||||
@ -364,6 +364,20 @@ object NewStyle extends MdcLoggable{
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountCanReadBalancesOfBerlinGroup(user : User, callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = {
|
||||
val viewIds = List(ViewId(SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID))
|
||||
Views.views.vend.getPrivateBankAccountsFuture(user, viewIds) map { i =>
|
||||
(i, callContext )
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountCanReadTransactionsOfBerlinGroup(user : User, callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = {
|
||||
val viewIds = List(ViewId(Constant.SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID))
|
||||
Views.views.vend.getPrivateBankAccountsFuture(user, viewIds) map { i =>
|
||||
(i, callContext )
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountListOfBerlinGroup(user : User, callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = {
|
||||
val viewIds = List(ViewId(SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID))
|
||||
Views.views.vend.getPrivateBankAccountsFuture(user, viewIds) map { i =>
|
||||
|
||||
@ -72,7 +72,9 @@ trait OBPDataImport extends MdcLoggable {
|
||||
protected def dataOrFirstFailure[T](boxes : List[Box[T]]) = {
|
||||
val firstFailure = boxes.collectFirst{case f: Failure => f}
|
||||
firstFailure match {
|
||||
case Some(f) => f
|
||||
case Some(f) =>
|
||||
logger.debug(f)
|
||||
f
|
||||
case None => Full(boxes.flatten) //no failures, so we can return the results
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,89 +1,302 @@
|
||||
/**
|
||||
Open Bank Project - API
|
||||
Copyright (C) 2011-2019, TESOBE GmbH.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Email: contact@tesobe.com
|
||||
TESOBE GmbH.
|
||||
Osloer Strasse 16/17
|
||||
Berlin 13359, Germany
|
||||
|
||||
This product includes software developed at
|
||||
TESOBE (http://www.tesobe.com/)
|
||||
|
||||
*/
|
||||
* Open Bank Project - API
|
||||
* Copyright (C) 2011-2019, TESOBE GmbH.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Email: contact@tesobe.com
|
||||
* TESOBE GmbH.
|
||||
* Osloer Strasse 16/17
|
||||
* Berlin 13359, Germany
|
||||
*
|
||||
* This product includes software developed at
|
||||
* TESOBE (http://www.tesobe.com/)
|
||||
*/
|
||||
package code.snippet
|
||||
|
||||
import code.accountholders.AccountHolders
|
||||
import code.api.RequestHeader
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{GetConsentResponseJson, createGetConsentResponseJson}
|
||||
import code.api.util.{ConsentJWT, CustomJsonFormats, JwtUtil}
|
||||
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{ConsentAccessAccountsJson, ConsentAccessJson, GetConsentResponseJson, createGetConsentResponseJson}
|
||||
import code.api.util.ErrorMessages.ConsentNotFound
|
||||
import code.api.util._
|
||||
import code.api.v3_1_0.APIMethods310
|
||||
import code.api.v5_0_0.APIMethods500
|
||||
import code.api.v5_1_0.APIMethods510
|
||||
import code.consent.{ConsentStatus, Consents, MappedConsent}
|
||||
import code.consumer.Consumers
|
||||
import code.model.dataAccess.AuthUser
|
||||
import code.model.dataAccess.{AuthUser, BankAccountRouting}
|
||||
import code.util.Helper.{MdcLoggable, ObpS}
|
||||
import com.openbankproject.commons.ExecutionContext.Implicits.global
|
||||
import com.openbankproject.commons.model.BankIdAccountId
|
||||
import net.liftweb.common.{Box, Failure, Full}
|
||||
import net.liftweb.http.js.JsCmds
|
||||
import net.liftweb.http.rest.RestHelper
|
||||
import net.liftweb.http.{RequestVar, S, SHtml, SessionVar}
|
||||
import net.liftweb.http.{S, SHtml, SessionVar}
|
||||
import net.liftweb.json.{Formats, parse}
|
||||
import net.liftweb.mapper.By
|
||||
import net.liftweb.util.CssSel
|
||||
import net.liftweb.util.Helpers._
|
||||
|
||||
import scala.collection.immutable
|
||||
import scala.concurrent.Future
|
||||
import scala.xml.NodeSeq
|
||||
|
||||
/**
|
||||
* This class handles Berlin Group consent requests.
|
||||
* It provides functionality to confirm or deny consent requests,
|
||||
* and manages the consent process for accessing account data.
|
||||
*/
|
||||
class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510 with APIMethods500 with APIMethods310 {
|
||||
// Custom JSON formats for serialization/deserialization
|
||||
protected implicit override def formats: Formats = CustomJsonFormats.formats
|
||||
|
||||
private object otpValue extends SessionVar("123")
|
||||
private object redirectUriValue extends SessionVar("")
|
||||
// Session variables to store OTP, redirect URI, and other consent-related data
|
||||
private object otpValue extends SessionVar("123") // Stores the OTP value for SCA (Strong Customer Authentication)
|
||||
private object redirectUriValue extends SessionVar("") // Stores the redirect URI for post-consent actions
|
||||
private object updateConsentPayloadValue extends SessionVar(false) // Flag to indicate if consent payload needs updating
|
||||
private object userIsOwnerOfAccountsValue extends SessionVar(true) // Flag to check if the user owns the accounts
|
||||
|
||||
// Session variables to store selected IBANs for accounts, balances, and transactions
|
||||
private object selectedAccountsIbansValue extends SessionVar[Set[String]](Set()) {
|
||||
override def set(value: Set[String]): Set[String] = {
|
||||
logger.debug(s"selectedAccountsIbansValue changed to: ${value.mkString(", ")}")
|
||||
super.set(value)
|
||||
}
|
||||
}
|
||||
private object accessAccountsDefinedVar extends SessionVar(true)
|
||||
private object accessBalancesDefinedVar extends SessionVar(true)
|
||||
private object accessTransactionsDefinedVar extends SessionVar(true)
|
||||
/**
|
||||
* Creates a ConsentAccessJson object from lists of IBANs for accounts, balances, and transactions.
|
||||
*
|
||||
* @param accounts List of IBANs for accounts.
|
||||
* @param balances List of IBANs for balances.
|
||||
* @param transactions List of IBANs for transactions.
|
||||
* @return ConsentAccessJson object.
|
||||
*/
|
||||
def createConsentAccessJson(accounts: List[String], balances: List[String], transactions: List[String]): ConsentAccessJson = {
|
||||
val accountsList = accounts.map(iban => ConsentAccessAccountsJson(iban = Some(iban), None, None, None, None, None))
|
||||
val balancesList = balances.map(iban => ConsentAccessAccountsJson(iban = Some(iban), None, None, None, None, None))
|
||||
val transactionsList = transactions.map(iban => ConsentAccessAccountsJson(iban = Some(iban), None, None, None, None, None))
|
||||
|
||||
ConsentAccessJson(
|
||||
accounts = Some(accountsList), // Populate accounts
|
||||
balances = if (balancesList.nonEmpty) Some(balancesList) else None, // Populate balances
|
||||
transactions = if (transactionsList.nonEmpty) Some(transactionsList) else None // Populate transactions
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the consent with new IBANs for accounts, balances, and transactions.
|
||||
*
|
||||
* @param consentId The ID of the consent to update.
|
||||
* @param ibans List of IBANs for accounts.
|
||||
* @return Future[MappedConsent] representing the updated consent.
|
||||
*/
|
||||
private def updateConsent(consentId: String, ibans: List[String], canReadBalances: Boolean, canReadTransactions: Boolean): Future[MappedConsent] = {
|
||||
for {
|
||||
// Fetch the consent by ID
|
||||
consent: MappedConsent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
|
||||
APIUtil.unboxFullOrFail(_, None, s"$ConsentNotFound ($consentId)", 404)
|
||||
}
|
||||
// Update the consent JWT with new access details
|
||||
consentJWT <- Consent.updateAccountAccessOfBerlinGroupConsentJWT(
|
||||
createConsentAccessJson(
|
||||
ibans,
|
||||
if(canReadBalances) ibans else List(),
|
||||
if(canReadTransactions) ibans else List()
|
||||
),
|
||||
consent,
|
||||
None
|
||||
) map {
|
||||
i => APIUtil.connectorEmptyResponse(i, None)
|
||||
}
|
||||
// Save the updated consent
|
||||
updatedConsent <- Future(Consents.consentProvider.vend.setJsonWebToken(consent.consentId, consentJWT)) map {
|
||||
i => APIUtil.connectorEmptyResponse(i, None)
|
||||
}
|
||||
} yield {
|
||||
updatedConsent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Berlin Group consent confirmation form.
|
||||
*
|
||||
* @return CssSel for rendering the form.
|
||||
*/
|
||||
def confirmBerlinGroupConsentRequest: CssSel = {
|
||||
callGetConsentByConsentId() match {
|
||||
case Full(consent) =>
|
||||
// Set OTP and redirect URI from the consent
|
||||
otpValue.set(consent.challenge)
|
||||
val json: GetConsentResponseJson = createGetConsentResponseJson(consent)
|
||||
val consumer = Consumers.consumers.vend.getConsumerByConsumerId(consent.consumerId)
|
||||
val consentJwt: Box[ConsentJWT] = JwtUtil.getSignedPayloadAsJson(consent.jsonWebToken).map(parse(_)
|
||||
.extract[ConsentJWT])
|
||||
val tppRedirectUri: immutable.Seq[String] = consentJwt.map{ h =>
|
||||
val tppRedirectUri: immutable.Seq[String] = consentJwt.map { h =>
|
||||
h.request_headers.filter(h => h.name == RequestHeader.`TPP-Redirect-URL`)
|
||||
}.getOrElse(Nil).map((_.values.mkString("")))
|
||||
val consumerRedirectUri: Option[String] = consumer.map(_.redirectURL.get).toOption
|
||||
val uri: String = tppRedirectUri.headOption.orElse(consumerRedirectUri).getOrElse("https://not.defined.com")
|
||||
redirectUriValue.set(uri)
|
||||
|
||||
// Get all accounts held by the current user
|
||||
val userAccounts: Set[BankIdAccountId] =
|
||||
AccountHolders.accountHolders.vend.getAccountsHeldByUser(AuthUser.currentUser.flatMap(_.user.foreign).openOrThrowException(ErrorMessages.UserNotLoggedIn), Some(null)).toSet
|
||||
val userIbans: Set[String] = userAccounts.flatMap { acc =>
|
||||
BankAccountRouting.find(
|
||||
By(BankAccountRouting.BankId, acc.bankId.value),
|
||||
By(BankAccountRouting.AccountId, acc.accountId.value),
|
||||
By(BankAccountRouting.AccountRoutingScheme, "IBAN")
|
||||
).map(_.AccountRoutingAddress.get)
|
||||
}
|
||||
// Select all IBANs
|
||||
selectedAccountsIbansValue.set(userIbans)
|
||||
|
||||
// Determine which IBANs the user can access for accounts, balances, and transactions
|
||||
val canReadAccountsIbans: List[String] = json.access.accounts match {
|
||||
case Some(accounts) if accounts.isEmpty => // Access is requested
|
||||
updateConsentPayloadValue.set(true)
|
||||
accessAccountsDefinedVar.set(true)
|
||||
userIbans.toList
|
||||
case Some(accounts) if accounts.flatMap(_.iban).toSet.subsetOf(userIbans) => // Access is requested for specific IBANs
|
||||
accessAccountsDefinedVar.set(true)
|
||||
accounts.flatMap(_.iban)
|
||||
case Some(accounts) => // Logged in user is not an owner of IBAN/IBANs
|
||||
userIsOwnerOfAccountsValue.set(false)
|
||||
accessAccountsDefinedVar.set(true)
|
||||
accounts.flatMap(_.iban)
|
||||
case None => // Access is not requested
|
||||
accessAccountsDefinedVar.set(false)
|
||||
List()
|
||||
}
|
||||
val canReadBalancesIbans: List[String] = json.access.balances match {
|
||||
case Some(balances) if balances.isEmpty => // Access is requested
|
||||
updateConsentPayloadValue.set(true)
|
||||
accessBalancesDefinedVar.set(true)
|
||||
userIbans.toList
|
||||
case Some(balances) if balances.flatMap(_.iban).toSet.subsetOf(userIbans) => // Access is requested for specific IBANs
|
||||
accessBalancesDefinedVar.set(true)
|
||||
balances.flatMap(_.iban)
|
||||
case Some(balances) => // Logged in user is not an owner of IBAN/IBANs
|
||||
userIsOwnerOfAccountsValue.set(false)
|
||||
accessBalancesDefinedVar.set(true)
|
||||
balances.flatMap(_.iban)
|
||||
case None => // Access is not requested
|
||||
accessBalancesDefinedVar.set(false)
|
||||
List()
|
||||
}
|
||||
val canReadTransactionsIbans: List[String] = json.access.transactions match {
|
||||
case Some(transactions) if transactions.isEmpty => // Access is requested
|
||||
updateConsentPayloadValue.set(true)
|
||||
accessTransactionsDefinedVar.set(true)
|
||||
userIbans.toList
|
||||
case Some(transactions) if transactions.flatMap(_.iban).toSet.subsetOf(userIbans) => // Access is requested for specific IBANs
|
||||
accessTransactionsDefinedVar.set(true)
|
||||
transactions.flatMap(_.iban)
|
||||
case Some(transactions) => // Logged in user is not an owner of IBAN/IBANs
|
||||
userIsOwnerOfAccountsValue.set(false)
|
||||
accessTransactionsDefinedVar.set(true)
|
||||
transactions.flatMap(_.iban)
|
||||
case None => // Access is not requested
|
||||
accessTransactionsDefinedVar.set(false)
|
||||
List()
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates toggle switches for IBAN lists.
|
||||
*
|
||||
* @param scope The scope of the IBANs (e.g., "canReadAccountsIbans").
|
||||
* @param ibans List of IBANs to display.
|
||||
* @param selectedList Set of currently selected IBANs.
|
||||
* @param sessionVar Session variable to update when toggling.
|
||||
* @return Sequence of NodeSeq representing the toggle switches.
|
||||
*/
|
||||
def generateCheckboxes(scope: String, ibans: List[String], selectedList: Set[String], sessionVar: SessionVar[Set[String]]): immutable.Seq[NodeSeq] = {
|
||||
ibans.map { iban =>
|
||||
if (updateConsentPayloadValue.is) {
|
||||
// Show toggle switch when updateConsentPayloadValue is true
|
||||
<div class="toggle-container">
|
||||
<label class="switch">
|
||||
{SHtml.ajaxCheckbox(selectedList.contains(iban), checked => {
|
||||
if (checked) {
|
||||
sessionVar.set(selectedList + iban) // Add to selected
|
||||
} else {
|
||||
sessionVar.set(selectedList - iban) // Remove from selected
|
||||
}
|
||||
JsCmds.Noop // Prevents page reload
|
||||
}, "id" -> (iban + scope), "class" -> "toggle-input")}<span class="slider round"></span>
|
||||
</label>
|
||||
<span style="all: unset;" class="toggle-label">
|
||||
{iban}
|
||||
</span>
|
||||
</div>
|
||||
} else {
|
||||
// Show only the IBAN text when updateConsentPayloadValue is false
|
||||
<span style="all: unset;" class="toggle-label">
|
||||
{iban}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Form text and user details
|
||||
val currentUser = AuthUser.currentUser
|
||||
val firstName = currentUser.map(_.firstName.get).getOrElse("")
|
||||
val lastName = currentUser.map(_.lastName.get).getOrElse("")
|
||||
val consumerName = consumer.map(_.name.get).getOrElse("")
|
||||
val formText =
|
||||
s"""I, ${AuthUser.currentUser.map(_.firstName.get).getOrElse("")} ${AuthUser.currentUser.map(_.lastName.get).getOrElse("")}, consent to the service provider ${consumer.map(_.name.get).getOrElse("")} making actions on my behalf.
|
||||
|
|
||||
|This consent must respects the following actions:
|
||||
|
|
||||
| 1) Can read accounts: ${json.access.accounts.getOrElse(Nil).flatMap(_.iban).mkString(", ")}
|
||||
| 2) Can read balances: ${json.access.balances.getOrElse(Nil).flatMap(_.iban).mkString(", ")}
|
||||
| 3) Can read transactions: ${json.access.transactions.getOrElse(Nil).flatMap(_.iban).mkString(", ")}
|
||||
|
|
||||
|This consent will end on date ${json.validUntil}.
|
||||
|
|
||||
|I understand that I can revoke this consent at any time.
|
||||
|""".stripMargin
|
||||
s"""I, $firstName $lastName, consent to the service provider <strong>$consumerName</strong> making the following actions on my behalf:
|
||||
|""".stripMargin
|
||||
|
||||
// Converting formText into a NodeSeq for raw HTML
|
||||
val formTextHtml: NodeSeq = scala.xml.XML.loadString("<div>" + formText + "</div>")
|
||||
|
||||
"#confirm-bg-consent-request-form-title *" #> s"Please confirm or deny the following consent request:" &
|
||||
"#confirm-bg-consent-request-form-text *" #> s"""$formText""" &
|
||||
// Form rendering
|
||||
"#confirm-bg-consent-request-form-title *" #> "Please confirm or deny the following consent request:" &
|
||||
"#confirm-bg-consent-request-form-text *" #> (
|
||||
<div>
|
||||
<p>
|
||||
{formTextHtml}
|
||||
</p>
|
||||
<div>
|
||||
<p><strong>Allowed actions:</strong></p>
|
||||
<p style="padding-left: 20px">Read account details</p>
|
||||
<p style={if (accessBalancesDefinedVar.is) "padding-left: 20px;" else "padding-left: 20px; display: none;"}>Read account balances</p>
|
||||
<p style={if (accessTransactionsDefinedVar.is) "padding-left: 20px;" else "padding-left: 20px; display: none;"}>Read transactions</p>
|
||||
</div>
|
||||
<div>
|
||||
<p><strong>Accounts</strong>:</p>
|
||||
<div style="padding-left: 20px">
|
||||
{generateCheckboxes("canReadAccountsIbans", userIbans.toList, selectedAccountsIbansValue.is, selectedAccountsIbansValue)}
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
<p>This consent will end on date {json.validUntil}.</p>
|
||||
<p>I understand that I can revoke this consent at any time.</p>
|
||||
</div>
|
||||
) & {
|
||||
if (userIsOwnerOfAccountsValue) {
|
||||
"#confirm-bg-consent-request-confirm-submit-button" #> SHtml.onSubmitUnit(confirmConsentRequestProcess) &
|
||||
"#confirm-bg-consent-request-deny-submit-button" #> SHtml.onSubmitUnit(denyConsentRequestProcess)
|
||||
"#confirm-bg-consent-request-deny-submit-button" #> SHtml.onSubmitUnit(denyConsentRequestProcess)
|
||||
} else {
|
||||
S.error(s"User $firstName $lastName is not owner of listed accounts")
|
||||
"#confirm-bg-consent-request-confirm-submit-button" #> "" &
|
||||
"#confirm-bg-consent-request-deny-submit-button" #> ""
|
||||
}}
|
||||
|
||||
case everythingElse =>
|
||||
S.error(everythingElse.toString)
|
||||
"#confirm-bg-consent-request-form-title *" #> s"Please confirm or deny the following consent request:" &
|
||||
@ -91,6 +304,11 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a consent by its ID.
|
||||
*
|
||||
* @return Box[MappedConsent] containing the consent if found.
|
||||
*/
|
||||
private def callGetConsentByConsentId(): Box[MappedConsent] = {
|
||||
val requestParam = List(
|
||||
ObpS.param("CONSENT_ID"),
|
||||
@ -103,12 +321,31 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the confirmation of a consent request.
|
||||
*/
|
||||
private def confirmConsentRequestProcess() = {
|
||||
val consentId = ObpS.param("CONSENT_ID") openOr ("")
|
||||
S.redirectTo(
|
||||
s"/confirm-bg-consent-request-sca?CONSENT_ID=${consentId}"
|
||||
)
|
||||
if (selectedAccountsIbansValue.is.isEmpty) {
|
||||
S.error(s"Please select at least 1 account")
|
||||
} else {
|
||||
val consentId = ObpS.param("CONSENT_ID") openOr ("")
|
||||
if (updateConsentPayloadValue.is) {
|
||||
updateConsent(
|
||||
consentId,
|
||||
selectedAccountsIbansValue.is.toList,
|
||||
accessBalancesDefinedVar.is,
|
||||
accessTransactionsDefinedVar.is
|
||||
)
|
||||
}
|
||||
S.redirectTo(
|
||||
s"/confirm-bg-consent-request-sca?CONSENT_ID=${consentId}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the denial of a consent request.
|
||||
*/
|
||||
private def denyConsentRequestProcess() = {
|
||||
val consentId = ObpS.param("CONSENT_ID") openOr ("")
|
||||
Consents.consentProvider.vend.updateConsentStatus(consentId, ConsentStatus.rejected)
|
||||
@ -116,6 +353,10 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
s"$redirectUriValue?CONSENT_ID=${consentId}"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the confirmation of a consent request with SCA (Strong Customer Authentication).
|
||||
*/
|
||||
private def confirmConsentRequestProcessSca() = {
|
||||
val consentId = ObpS.param("CONSENT_ID") openOr ("")
|
||||
Consents.consentProvider.vend.getConsentByConsentId(consentId) match {
|
||||
@ -129,7 +370,11 @@ class BerlinGroupConsent extends MdcLoggable with RestHelper with APIMethods510
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders the SCA confirmation form for Berlin Group consent.
|
||||
*
|
||||
* @return CssSel for rendering the form.
|
||||
*/
|
||||
def confirmBgConsentRequest: CssSel = {
|
||||
"#otp-value" #> SHtml.text(otpValue, otpValue(_)) &
|
||||
"type=submit" #> SHtml.onSubmitUnit(confirmConsentRequestProcessSca)
|
||||
|
||||
@ -32,7 +32,7 @@ Berlin 13359, Germany
|
||||
<div class="form-group">
|
||||
<h3 id="confirm-bg-consent-request-form-title">Please check the Berlin Group Consent Request: </h3>
|
||||
<div id="confirm-bg-consent-request-form-text-div">
|
||||
<pre id="confirm-bg-consent-request-form-text"></pre>
|
||||
<div id="confirm-bg-consent-request-form-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user