Merge remote-tracking branch 'OBP-API/develop' into develop

# Conflicts:
#	obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala
This commit is contained in:
Hongwei 2024-07-01 14:13:47 +02:00
commit 4a42ed71b8
71 changed files with 2970 additions and 282 deletions

9
.github/Dockerfile_PreBuild_Jmx vendored Normal file
View File

@ -0,0 +1,9 @@
FROM jetty:9.4-jdk11-alpine
# Copy OBP source code
# Copy build artifact (.war file) into jetty from 'maven' stage.
COPY /jmx_prometheus_javaagent-0.20.0.jar /var/lib/jetty/jmx_prometheus_javaagent-0.20.0.jar
COPY /.github/jmx_exporter.config /var/lib/jetty/prometheus_config.yml
COPY /obp-api/target/obp-api-1.*.war /var/lib/jetty/webapps/ROOT.war
CMD ["java -jar $JETTY_HOME/start.jar -javaagent:$JETTY_BASE/jmx_prometheus_javaagent-0.20.0.jar=8090:$JETTY_BASE/prometheus_config.yml"]

View File

@ -0,0 +1,81 @@
name: Build and publish container develop
# read-write repo token
# access to secrets
on:
workflow_run:
workflows: [build maven artifact]
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' }}
steps:
- uses: actions/checkout@v3
- name: 'Download artifact'
uses: actions/github-script@v3.1.0
with:
script: |
var artifacts = await github.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.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
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 }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
docker build . --file .github/Dockerfile_PreBuild_OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:${GITHUB_REF##*/}-OC
docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags
echo docker done
- uses: sigstore/cosign-installer@main
- name: Write signing key to disk (only needed for `cosign sign --key`)
run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key
- name: Sign container image
run: |
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC
env:
COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}"

View File

@ -0,0 +1,82 @@
name: Build and publish container non develop
# read-write repo token
# access to secrets
on:
workflow_run:
workflows: [build maven artifact]
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' }}
steps:
- uses: actions/checkout@v3
- name: 'Download artifact'
uses: actions/github-script@v3.1.0
with:
script: |
var artifacts = await github.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.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
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 }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
docker build . --file .github/Dockerfile_PreBuild_OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:${GITHUB_REF##*/}-OC
docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags
echo docker done
- uses: sigstore/cosign-installer@main
- name: Write signing key to disk (only needed for `cosign sign --key`)
run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key
- name: Sign container image
run: |
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC
env:
COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}"

View File

@ -0,0 +1,57 @@
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@v3.1.0
with:
script: |
var artifacts = await github.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.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_SHA --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

View File

@ -0,0 +1,96 @@
name: Build and publish jmx container develop
# read-write repo token
# access to secrets
on: workflow_dispatch
env:
## Sets environment variable
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
DOCKER_HUB_REPOSITORY: obp-api
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 'Download artifact'
uses: actions/github-script@v3.1.0
with:
script: |
var matchRun = workflowRuns.data.workflow_runs.filter((run) => {
return run.head_sha == context.sha
var workflowRuns = await github.actions.listWorkflowRunsForRepo({
})[0];
owner: context.repo.owner,
repo: context.repo.repo,
});
var artifacts = await github.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
var matchRun = workflowRuns.data.workflow_runs.filter((run) => {
return run.head_sha == context.sha
})[0];
run_id: matchRun.id,
});
if (!matchRun) {
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
console.log('No matching workflow run found for this commit');
return;
}
return artifact.name == "push"
})[0];
var artifacts = await github.actions.listWorkflowRunArtifacts({
var download = await github.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
owner: context.repo.owner,
repo: context.repo.repo,
run_id: matchRun.id,
artifact_id: matchArtifact.id,
});
archive_format: 'zip',
});
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
var fs = require('fs');
return artifact.name == "push"
})[0];
if (!matchArtifact) {
fs.writeFileSync('${{github.workspace}}/push.zip', Buffer.from(download.data));
- run: unzip push.zip
- name: prepare the artifact
run: |
mkdir -p obp-api/target/
cp push/obp-api-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 }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags
echo docker done
- uses: sigstore/cosign-installer@main
- name: Write signing key to disk (only needed for `cosign sign --key`)
run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key
- name: Sign container image
run: |
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC
env:
COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}"

View File

@ -1,11 +1,6 @@
name: build and publish container
name: build maven artifact
on: [push]
env:
## Sets environment variable
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
DOCKER_HUB_REPOSITORY: obp-api
jobs:
build:
@ -73,33 +68,18 @@ jobs:
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
MAVEN_OPTS="-Xmx3G -Xss2m" mvn clean package -Pprod
- name: Build the Docker image
- name: Save .war artifact
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 }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
docker build . --file .github/Dockerfile_PreBuild_OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC --tag docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:${GITHUB_REF##*/}-OC
docker push docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }} --all-tags
echo docker done
- uses: sigstore/cosign-installer@main
- name: Write signing key to disk (only needed for `cosign sign --key`)
run: echo "${{ secrets.COSIGN_PRIVATE_KEY }}" > cosign.key
- name: Sign container image
run: |
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:$GITHUB_SHA
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:develop-OC
cosign sign -y --key cosign.key \
docker.io/${{ env.DOCKER_HUB_ORGANIZATION }}/${{ env.DOCKER_HUB_REPOSITORY }}:latest-OC
env:
COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}"
mkdir -p ./push
cp obp-api/target/obp-api-1.*.war ./push/
- uses: actions/upload-artifact@v2
with:
name: push
path: push/

View File

@ -0,0 +1,89 @@
name: Build on Pull Request
on:
pull_request:
branches:
- '**'
env:
## Sets environment variable
DOCKER_HUB_ORGANIZATION: ${{ vars.DOCKER_HUB_ORGANIZATION }}
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 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 user name and .war artifact
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@v2
with:
name: pr
path: pr/

View File

@ -2,7 +2,9 @@ name: scan container image
on:
workflow_run:
workflows: [build and publish container]
workflows:
- Build and publish container develop
- Build and publish container non develop
types:
- completed
env:

View File

@ -58,7 +58,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
@ -114,7 +114,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.4.3</version>
<version>42.4.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8-production -->
<dependency>
@ -212,7 +212,7 @@
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>8.8.1</version>
<version>8.14.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sksamuel.elastic4s/elastic4s-client-esjava -->
<dependency>
@ -266,7 +266,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.23.0</version>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
@ -299,7 +299,7 @@
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.31</version>
<version>9.37.2</version>
</dependency>
<dependency>
<groupId>com.github.OpenBankProject</groupId>

View File

@ -385,4 +385,16 @@ your.secret.link.is.not.valid = Looks like the invitation link is invalid. Still
privacy_policy_checkbox_text =I agree to the above Privacy Policy
privacy_policy_checkbox_label = Privacy Policy
terms_and_conditions_checkbox_text = I agree to the above Terms and Conditions
terms_and_conditions_checkbox_label = Terms and Conditions
terms_and_conditions_checkbox_label = Terms and Conditions
# Terms and conditions page
outdated.terms.button.skip = Skip
outdated.terms.button.accept = Accept
outdated.terms.warning.1 = Dear
outdated.terms.warning.2 = . We have updated our terms since you last agreed to them! Please review the text and agree if you agree!
# Privacy policy page
outdated.policy.button.skip = Skip
outdated.policy.button.accept = Accept
outdated.policy.warning.1 = Dear
outdated.policy.warning.2 = . We have updated our policy since you last agreed to them! Please review the text and agree if you agree!

View File

@ -881,7 +881,11 @@ database_messages_scheduler_interval=3600
# -- Consents ---------------------------------------------
# In case isn't defined default value is "false"
# consents.allowed=true
# consumer_validation_method_for_consent=CONSUMER_KEY_VALUE
#
# 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

View File

@ -63,6 +63,7 @@ import code.connectormethod.ConnectorMethod
import code.consent.{ConsentRequest, MappedConsent}
import code.consumer.Consumers
import code.context.{MappedConsentAuthContext, MappedUserAuthContext, MappedUserAuthContextUpdate}
import code.counterpartylimit.CounterpartyLimit
import code.crm.MappedCrmEvent
import code.customer.internalMapping.MappedCustomerIdMapping
import code.customer.{MappedCustomer, MappedCustomerMessage}
@ -129,7 +130,7 @@ import code.util.Helper.{MdcLoggable, ObpS, SILENCE_IS_GOLDEN}
import code.util.{Helper, HydraUtil}
import code.validation.JsonSchemaValidation
import code.views.Views
import code.views.system.{AccountAccess, ViewDefinition}
import code.views.system.{AccountAccess, ViewDefinition, ViewPermission}
import code.webhook.{BankAccountNotificationWebhook, MappedAccountWebhook, SystemAccountNotificationWebhook}
import code.webuiprops.WebUiProps
import com.openbankproject.commons.model.ErrorMessage
@ -143,7 +144,7 @@ import net.liftweb.http.LiftRules.DispatchPF
import net.liftweb.http._
import net.liftweb.http.provider.HTTPCookie
import net.liftweb.json.Extraction
import net.liftweb.mapper.{DefaultConnectionIdentifier=>_, _}
import net.liftweb.mapper.{DefaultConnectionIdentifier => _, _}
import net.liftweb.sitemap.Loc._
import net.liftweb.sitemap._
import net.liftweb.util.Helpers._
@ -1086,7 +1087,9 @@ object ToSchemify {
DynamicMessageDoc,
EndpointTag,
ProductFee,
UserInitAction
ViewPermission,
UserInitAction,
CounterpartyLimit
)
// start grpc server

View File

@ -36,7 +36,7 @@ import com.openbankproject.commons.util.{ApiVersion, FieldNameApiVersions, Refle
import net.liftweb.json
import java.net.URLEncoder
import code.api.v5_1_0.{AtmsJsonV510, _}
import code.api.v5_1_0.{AtmsJsonV510, CustomViewJsonV510, _}
import code.endpointMapping.EndpointMappingCommons
import net.liftweb.json.Extraction
@ -52,6 +52,7 @@ import scala.collection.immutable.List
*/
object SwaggerDefinitionsJSON {
implicit def convertStringToBoolean(value:String) = value.toBoolean
lazy val regulatedEntitiesJsonV510: RegulatedEntitiesJsonV510 = RegulatedEntitiesJsonV510(List(regulatedEntityJsonV510))
lazy val regulatedEntityJsonV510: RegulatedEntityJsonV510 = RegulatedEntityJsonV510(
@ -314,18 +315,48 @@ object SwaggerDefinitionsJSON {
"can_revoke_access_to_custom_views"
)
val createCustomViewJson = CreateCustomViewJson(
name = viewNameExample.value,
description= viewDescriptionExample.value,
metadata_view= metadataViewExample.value,
is_public = isPublicExample.value,
which_alias_to_use = whichAliasToUseExample.value,
hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean,
allowed_permissions= allowedActionsV500,
)
val customViewJsonV510 = CustomViewJsonV510(
id = viewIdExample.value,
name = viewNameExample.value,
description = viewDescriptionExample.value,
metadata_view = metadataViewExample.value,
is_public = isPublicExample.value,
alias = whichAliasToUseExample.value,
hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean,
allowed_permissions = allowedActionsV500
)
val createSystemViewJsonV500 = CreateViewJsonV500(
name = "_test",
description = "This view is for family",
metadata_view ="_test",
is_public = false,
which_alias_to_use = "family",
hide_metadata_if_alias_used = false,
name = viewNameExample.value,
description = viewDescriptionExample.value,
metadata_view =viewDescriptionExample.value,
is_public = isPublicExample.value,
which_alias_to_use = whichAliasToUseExample.value,
hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean,
allowed_actions = allowedActionsV500,
// Version 5.0.0
can_grant_access_to_views = Some(List(Constant.SYSTEM_OWNER_VIEW_ID)),
can_revoke_access_to_views = Some(List(Constant.SYSTEM_OWNER_VIEW_ID))
)
val updateCustomViewJson = UpdateCustomViewJson(
description = viewDescriptionExample.value,
metadata_view = metadataViewExample.value,
is_public = isPublicExample.value,
which_alias_to_use = whichAliasToUseExample.value,
hide_metadata_if_alias_used = hideMetadataIfAliasUsedExample.value.toBoolean,
allowed_permissions = allowedActionsV500
)
val updateViewJsonV300 = UpdateViewJsonV300(
description = "this is for family",
@ -5396,6 +5427,27 @@ object SwaggerDefinitionsJSON {
phone = phoneExample.value,
)
val postCounterpartyLimitV510 = PostCounterpartyLimitV510(
max_single_amount = maxSingleAmountExample.value.toInt,
max_monthly_amount = maxMonthlyAmountExample.value.toInt,
max_number_of_monthly_transactions = maxNumberOfMonthlyTransactionsExample.value.toInt,
max_yearly_amount = maxYearlyAmountExample.value.toInt,
max_number_of_yearly_transactions = maxNumberOfYearlyTransactionsExample.value.toInt
)
val counterpartyLimitV510 = CounterpartyLimitV510(
counterparty_limit_id = counterpartyLimitIdExample.value,
bank_id = bankIdExample.value,
account_id = accountIdExample.value,
view_id = viewIdExample.value,
counterparty_id = counterpartyIdExample.value,
max_single_amount = maxSingleAmountExample.value.toInt,
max_monthly_amount = maxMonthlyAmountExample.value.toInt,
max_number_of_monthly_transactions = maxNumberOfMonthlyTransactionsExample.value.toInt,
max_yearly_amount = maxYearlyAmountExample.value.toInt,
max_number_of_yearly_transactions = maxNumberOfYearlyTransactionsExample.value.toInt
)
val atmsJsonV510 = AtmsJsonV510(
atms = List(atmJsonV510)
)

View File

@ -1,7 +1,6 @@
package code.api.builder.AccountInformationServiceAISApi
import java.text.SimpleDateFormat
import code.api.APIFailureNewStyle
import code.api.Constant.{SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID}
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{PostConsentResponseJson, _}
@ -12,6 +11,7 @@ import code.api.util.APIUtil.{passesPsd2Aisp, _}
import code.api.util.ApiTag._
import code.api.util.ErrorMessages._
import code.api.util.NewStyle.HttpCode
import code.api.util.newstyle.BalanceNewStyle
import code.api.util.{APIUtil, ApiTag, CallContext, Consent, ExampleValue, NewStyle}
import code.bankconnectors.Connector
import code.consent.{ConsentStatus, Consents}
@ -367,7 +367,7 @@ The account-id is constant at least throughout the lifecycle of a given consent.
_ <- passesPsd2Aisp(callContext)
(account: BankAccount, callContext) <- NewStyle.function.getBankAccountByAccountId(accountId, callContext)
_ <- checkAccountAccess(ViewId(SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID), u, account, callContext)
(accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext)
(accountBalances, callContext)<- BalanceNewStyle.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext)
} yield {
(JSONFactory_BERLIN_GROUP_1_3.createAccountBalanceJSON(account, accountBalances), HttpCode.`200`(callContext))
}
@ -482,7 +482,7 @@ This account-id then can be retrieved by the
_ <- passesPsd2Aisp(callContext)
(account: BankAccount, callContext) <- NewStyle.function.getBankAccountByAccountId(AccountId(accountId), callContext)
_ <- checkAccountAccess(ViewId(SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID), u, account, callContext)
(accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext)
(accountBalances, callContext)<- BalanceNewStyle.getBankAccountBalances(BankIdAccountId(account.bankId,account.accountId), callContext)
} yield {
(JSONFactory_BERLIN_GROUP_1_3.createCardAccountBalanceJSON(account, accountBalances), HttpCode.`200`(callContext))
}

View File

@ -122,6 +122,7 @@ import code.api.v2_1_0.OBPAPI2_1_0.Implementations2_1_0
import code.api.v2_2_0.OBPAPI2_2_0.Implementations2_2_0
import code.etag.MappedETag
import code.users.Users
import code.views.system.{AccountAccess, ViewDefinition}
import net.liftweb.mapper.By
import scala.collection.mutable
@ -1695,6 +1696,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
private val isNeedCheckView = errorResponseBodies.contains($UserNoPermissionAccessView) &&
requestUrlPartPath.contains("BANK_ID") && requestUrlPartPath.contains("ACCOUNT_ID") && requestUrlPartPath.contains("VIEW_ID")
private val isNeedCheckCounterparty = errorResponseBodies.contains($CounterpartyNotFoundByCounterpartyId) && requestUrlPartPath.contains("COUNTERPARTY_ID")
private val reversedRequestUrl = requestUrlPartPath.reverse
def getPathParams(url: List[String]): Map[String, String] =
reversedRequestUrl.zip(url.reverse) collect {
@ -1774,6 +1777,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
Future.successful(null.asInstanceOf[View])
}
}
def checkCounterparty(counterpartyId: Option[CounterpartyId], callContext: Option[CallContext]): OBPReturnType[CounterpartyTrait] = {
if(isNeedCheckCounterparty && counterpartyId.isDefined) {
checkCounterpartyFun(counterpartyId.get)(callContext)
} else {
Future.successful(null.asInstanceOf[CounterpartyTrait] -> callContext)
}
}
// reset connectorMethods
{
val checkerFunctions = mutable.ListBuffer[PartialFunction[_, _]]()
@ -1794,6 +1805,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
if (isNeedCheckView) {
checkerFunctions += checkViewFun
}
if (isNeedCheckCounterparty) {
checkerFunctions += checkCounterpartyFun
}
val addedMethods: List[String] = checkerFunctions.toList.flatMap(getDependentConnectorMethods(_))
.map(value =>("obp." +value).intern())
@ -1840,6 +1854,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
val bankId = pathParams.get("BANK_ID").map(BankId(_))
val accountId = pathParams.get("ACCOUNT_ID").map(AccountId(_))
val viewId = pathParams.get("VIEW_ID").map(ViewId(_))
val counterpartyId = pathParams.get("COUNTERPARTY_ID").map(CounterpartyId(_))
val request: Box[Req] = S.request
val session: Box[LiftSession] = S.session
@ -1850,7 +1865,8 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
* 2. check bankId
* 3. roles check
* 4. check accountId
* 5. view
* 5. view access
* 6. check counterpartyId
*
* A Bank MUST be checked before Roles.
* In opposite case we get next paradox:
@ -1877,6 +1893,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
// check user access permission of this viewId corresponding view
view <- checkView(viewId, bankId, accountId, boxUser, callContext)
counterparty <- checkCounterparty(counterpartyId, callContext)
} yield {
val newCallContext = if(boxUser.isDefined) callContext.map(_.copy(user=boxUser)) else callContext
@ -2970,8 +2989,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
} else if (APIUtil.hasConsentJWT(reqHeaders)) { // Open Bank Project's Consent
val consentValue = APIUtil.getConsentJWT(reqHeaders)
Consent.getConsentJwtValueByConsentId(consentValue.getOrElse("")) match {
case Some(jwt) => // JWT value obtained via "Consent-Id" request header
Consent.applyRules(Some(jwt), cc)
case Some(consent) => // JWT value obtained via "Consent-Id" request header
Consent.applyRules(
Some(consent.jsonWebToken),
// Note: At this point we are getting the Consumer from the Consumer in the Consent.
// This may later be cross checked via the value in consumer_validation_method_for_consent.
// Get the source of truth for Consumer (e.g. CONSUMER_CERTIFICATE) as early as possible.
cc.copy(consumer = Consent.getCurrentConsumerViaMtls(callContext = cc))
)
case _ =>
JwtUtil.checkIfStringIsJWTValue(consentValue.getOrElse("")).isDefined match {
case true => // It's JWT obtained via "Consent-JWT" request header
@ -4225,6 +4250,9 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
private val checkViewFun: PartialFunction[ViewId, (BankIdAccountId, Option[User], Option[CallContext]) => Future[View]] = {
case x => NewStyle.function.checkViewAccessAndReturnView(x, _, _, _)
}
private val checkCounterpartyFun: PartialFunction[CounterpartyId, Option[CallContext] => OBPReturnType[CounterpartyTrait]] = {
case x => NewStyle.function.getCounterpartyByCounterpartyId(x, _)
}
// cache for method -> called obp methods:
// (className, methodName, signature) -> List[(className, methodName, signature)]
@ -4925,4 +4953,26 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
else
UserLacksPermissionCanGrantAccessToCustomViewForTargetAccount + s"Current source viewId(${sourceViewId.value}) and target viewId (${targetViewId.value})"
def intersectAccountAccessAndView(accountAccesses: List[AccountAccess], views: List[View]): List[BankIdAccountId] = {
val intersectedViewIds = accountAccesses.map(item => item.view_id.get)
.intersect(views.map(item => item.viewId.value)).distinct // Join view definition and account access via view_id
accountAccesses
.filter(i => intersectedViewIds.contains(i.view_id.get))
.map(item => BankIdAccountId(BankId(item.bank_id.get), AccountId(item.account_id.get)))
.distinct // List pairs (bank_id, account_id)
}
//get all the permission Pair from one record, eg:
//List("can_see_transaction_this_bank_account","can_see_transaction_requests"....)
//Note, do not contain can_revoke_access_to_views and can_grant_access_to_views permission yet.
def getViewPermissions(view: ViewDefinition) = view.allFields.map(x => (x.name, x.get))
.filter(pair =>pair._2.isInstanceOf[Boolean])
.filter(pair => pair._1.startsWith("can"))
.filter(pair => pair._2.equals(true))
.map(pair =>
StringHelpers.snakify(pair._1)
.dropRight(1) //Remove the "_" in the end, eg canCreateStandingOrder_ --> canCreateStandingOrder
).toSet
}

View File

@ -73,6 +73,7 @@ object ApiTag {
val apiTagWebUiProps = ResourceDocTag("WebUi-Props")
val apiTagEndpointMapping = ResourceDocTag("Endpoint-Mapping")
val apiTagRateLimits = ResourceDocTag("Rate-Limits")
val apiTagCounterpartyLimits = ResourceDocTag("Counterparty-Limits")
val apiTagApiCollection = ResourceDocTag("Api-Collection")

View File

@ -15,6 +15,7 @@ import code.context.{ConsentAuthContextProvider, UserAuthContextProvider}
import code.entitlement.Entitlement
import code.model.Consumer
import code.users.Users
import code.util.Helper.MdcLoggable
import code.util.HydraUtil
import code.views.Views
import com.nimbusds.jwt.JWTClaimsSet
@ -25,7 +26,7 @@ import net.liftweb.http.provider.HTTPParam
import net.liftweb.json.JsonParser.ParseException
import net.liftweb.json.{Extraction, MappingException, compactRender, parse}
import net.liftweb.mapper.By
import net.liftweb.util.ControlHelpers
import net.liftweb.util.{ControlHelpers, Props}
import sh.ory.hydra.model.OAuth2TokenIntrospection
import scala.collection.immutable.{List, Nil}
@ -104,7 +105,7 @@ case class Consent(createdByUserId: String,
}
}
object Consent {
object Consent extends MdcLoggable {
final lazy val challengeAnswerAtTestEnvironment = "123"
@ -118,6 +119,20 @@ object Consent {
case _ => None
}
}
/**
* Purpose of this helper function is to get the Consumer via MTLS info i.e. PEM certificate.
* @return the boxed Consumer
*/
def getCurrentConsumerViaMtls(callContext: CallContext): Box[Consumer] = {
val clientCert: String = APIUtil.`getPSD2-CERT`(callContext.requestHeaders)
.getOrElse(SecureRandomUtil.csprng.nextLong().toString)
def removeBreakLines(input: String) = input
.replace("\n", "")
.replace("\r", "")
Consumers.consumers.vend.getConsumerByPemCertificate(clientCert).or(
Consumers.consumers.vend.getConsumerByPemCertificate(removeBreakLines(clientCert))
)
}
private def verifyHmacSignedJwt(jwtToken: String, c: MappedConsent): Boolean = {
JwtUtil.verifyHmacSignedJwt(jwtToken, c.secret)
@ -126,7 +141,11 @@ object Consent {
private def checkConsumerIsActiveAndMatched(consent: ConsentJWT, callContext: CallContext): Box[Boolean] = {
Consumers.consumers.vend.getConsumerByConsumerId(consent.aud) match {
case Full(consumerFromConsent) if consumerFromConsent.isActive.get == true => // Consumer is active
APIUtil.getPropsValue(nameOfProperty = "consumer_validation_method_for_consent", defaultValue = "CONSUMER_KEY_VALUE") match {
val validationMetod = APIUtil.getPropsValue(nameOfProperty = "consumer_validation_method_for_consent", defaultValue = "CONSUMER_CERTIFICATE")
if(validationMetod != "CONSUMER_CERTIFICATE" && Props.mode == Props.RunModes.Production) {
logger.warn(s"consumer_validation_method_for_consent is not set to CONSUMER_CERTIFICATE! The current value is: ${validationMetod}")
}
validationMetod match {
case "CONSUMER_KEY_VALUE" =>
val requestHeaderConsumerKey = getConsumerKey(callContext.requestHeaders)
requestHeaderConsumerKey match {
@ -272,7 +291,7 @@ object Consent {
if (result.forall(_ == "Added")) Full(user) else Failure("Cannot add permissions to the user with id: " + user.userId)
}
private def hasConsentInternalOldStyle(consentIdAsJwt: String, calContext: CallContext): Box[User] = {
private def applyConsentRulesCommonOldStyle(consentIdAsJwt: String, calContext: CallContext): Box[User] = {
implicit val dateFormats = CustomJsonFormats.formats
def applyConsentRules(consent: ConsentJWT): Box[User] = {
@ -316,7 +335,7 @@ object Consent {
}
}
private def hasConsentCommon(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
private def applyConsentRulesCommon(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
implicit val dateFormats = CustomJsonFormats.formats
def applyConsentRules(consent: ConsentJWT): Future[(Box[User], Option[CallContext])] = {
@ -351,13 +370,16 @@ object Consent {
case Full(jsonAsString) =>
try {
val consent = net.liftweb.json.parse(jsonAsString).extract[ConsentJWT]
checkConsent(consent, consentAsJwt, callContext) match { // Check is it Consent-JWT expired
// Set Consumer into Call Context
val consumer = getCurrentConsumerViaMtls(callContext)
val updatedCallContext = callContext.copy(consumer = consumer)
checkConsent(consent, consentAsJwt, updatedCallContext) match { // Check is it Consent-JWT expired
case (Full(true)) => // OK
applyConsentRules(consent)
case failure@Failure(_, _, _) => // Handled errors
Future(failure, Some(callContext))
Future(failure, Some(updatedCallContext))
case _ => // Unexpected errors
Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(callContext))
Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(updatedCallContext))
}
} catch { // Possible exceptions
case e: ParseException => Future(Failure("ParseException: " + e.getMessage), Some(callContext))
@ -371,27 +393,20 @@ object Consent {
}
}
private def hasConsentOldStyle(consentIdAsJwt: String, callContext: CallContext): (Box[User], CallContext) = {
(hasConsentInternalOldStyle(consentIdAsJwt, callContext), callContext)
}
private def hasConsent(consentAsJwt: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
hasConsentCommon(consentAsJwt, callContext)
}
def applyRules(consentJwt: Option[String], callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
val allowed = APIUtil.getPropsAsBoolValue(nameOfProperty="consents.allowed", defaultValue=false)
(consentJwt, allowed) match {
case (Some(consentId), true) => hasConsent(consentId, callContext)
case (Some(jwt), true) => applyConsentRulesCommon(jwt, callContext)
case (_, false) => Future((Failure(ErrorMessages.ConsentDisabled), Some(callContext)))
case (None, _) => Future((Failure(ErrorMessages.ConsentHeaderNotFound), Some(callContext)))
}
}
def getConsentJwtValueByConsentId(consentId: String): Option[String] = {
def getConsentJwtValueByConsentId(consentId: String): Option[MappedConsent] = {
APIUtil.checkIfStringIsUUIDVersion1(consentId) match {
case true => // String is a UUID
Consents.consentProvider.vend.getConsentByConsentId(consentId) match {
case Full(consent) => Some(consent.jsonWebToken)
case Full(consent) => Some(consent)
case _ => None // It's not valid UUID value
}
case false => None // It's not UUID at all
@ -407,7 +422,7 @@ object Consent {
Full(Nil)
}
}
private def hasBerlinGroupConsentInternal(consentId: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
private def applyBerlinGroupConsentRulesCommon(consentId: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
implicit val dateFormats = CustomJsonFormats.formats
def applyConsentRules(consent: ConsentJWT): Future[(Box[User], Option[CallContext])] = {
@ -464,6 +479,9 @@ object Consent {
// 1st we need to find a Consent via the field MappedConsent.consentId
Consents.consentProvider.vend.getConsentByConsentId(consentId) match {
case Full(storedConsent) =>
// Set Consumer into Call Context
val consumer = getCurrentConsumerViaMtls(callContext)
val updatedCallContext = callContext.copy(consumer = consumer)
// This function MUST be called only once per call. I.e. it's date dependent
val (canBeUsed, currentCounterState) = checkFrequencyPerDay(storedConsent)
if(canBeUsed) {
@ -471,28 +489,28 @@ object Consent {
case Full(jsonAsString) =>
try {
val consent = net.liftweb.json.parse(jsonAsString).extract[ConsentJWT]
checkConsent(consent, storedConsent.jsonWebToken, callContext) match { // Check is it Consent-JWT expired
checkConsent(consent, storedConsent.jsonWebToken, updatedCallContext) match { // Check is it Consent-JWT expired
case (Full(true)) => // OK
// Update MappedConsent.usesSoFarTodayCounter field
Consents.consentProvider.vend.updateBerlinGroupConsent(consentId, currentCounterState + 1)
applyConsentRules(consent)
case failure@Failure(_, _, _) => // Handled errors
Future(failure, Some(callContext))
Future(failure, Some(updatedCallContext))
case _ => // Unexpected errors
Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(callContext))
Future(Failure(ErrorMessages.ConsentCheckExpiredIssue), Some(updatedCallContext))
}
} catch { // Possible exceptions
case e: ParseException => Future(Failure("ParseException: " + e.getMessage), Some(callContext))
case e: MappingException => Future(Failure("MappingException: " + e.getMessage), Some(callContext))
case e: Exception => Future(Failure("parsing failed: " + e.getMessage), Some(callContext))
case e: ParseException => Future(Failure("ParseException: " + e.getMessage), Some(updatedCallContext))
case e: MappingException => Future(Failure("MappingException: " + e.getMessage), Some(updatedCallContext))
case e: Exception => Future(Failure("parsing failed: " + e.getMessage), Some(updatedCallContext))
}
case failure@Failure(_, _, _) =>
Future(failure, Some(callContext))
Future(failure, Some(updatedCallContext))
case _ =>
Future(Failure("Cannot extract data from: " + consentId), Some(callContext))
Future(Failure("Cannot extract data from: " + consentId), Some(updatedCallContext))
}
} else {
Future(Failure(ErrorMessages.TooManyRequests + s" ${RequestHeader.`Consent-ID`}: $consentId"), Some(callContext))
Future(Failure(ErrorMessages.TooManyRequests + s" ${RequestHeader.`Consent-ID`}: $consentId"), Some(updatedCallContext))
}
case failure@Failure(_, _, _) =>
Future(failure, Some(callContext))
@ -500,13 +518,10 @@ object Consent {
Future(Failure(ErrorMessages.ConsentNotFound + s" ($consentId)"), Some(callContext))
}
}
private def hasBerlinGroupConsent(consentId: String, callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
hasBerlinGroupConsentInternal(consentId, callContext)
}
def applyBerlinGroupRules(consentId: Option[String], callContext: CallContext): Future[(Box[User], Option[CallContext])] = {
val allowed = APIUtil.getPropsAsBoolValue(nameOfProperty="consents.allowed", defaultValue=false)
(consentId, allowed) match {
case (Some(consentId), true) => hasBerlinGroupConsent(consentId, callContext)
case (Some(consentId), true) => applyBerlinGroupConsentRulesCommon(consentId, callContext)
case (_, false) => Future((Failure(ErrorMessages.ConsentDisabled), Some(callContext)))
case (None, _) => Future((Failure(ErrorMessages.ConsentHeaderNotFound), Some(callContext)))
}
@ -514,7 +529,7 @@ object Consent {
def applyRulesOldStyle(consentId: Option[String], callContext: CallContext): (Box[User], CallContext) = {
val allowed = APIUtil.getPropsAsBoolValue(nameOfProperty="consents.allowed", defaultValue=false)
(consentId, allowed) match {
case (Some(consentId), true) => hasConsentOldStyle(consentId, callContext)
case (Some(consentId), true) => (applyConsentRulesCommonOldStyle(consentId, callContext), callContext)
case (_, false) => (Failure(ErrorMessages.ConsentDisabled), callContext)
case (None, _) => (Failure(ErrorMessages.ConsentHeaderNotFound), callContext)
}

View File

@ -198,6 +198,8 @@ object ErrorMessages {
s"OBP-20048: If target viewId is system view, the current view.can_revoke_access_to_views does not contains it. Or" +
s"if target viewId is custom view, the current view.can_revoke_access_to_custom_views is false."
val SourceViewHasLessPermission = "OBP-20049: Source view contains less permissions than target view."
val UserNotSuperAdmin = "OBP-20050: Current User is not a Super Admin!"
val ElasticSearchIndexNotFound = "OBP-20051: Elasticsearch index or indices not found."
@ -399,8 +401,8 @@ object ErrorMessages {
val ApiCollectionEndpointNotFound = "OBP-30082: ApiCollectionEndpoint not found."
val CreateApiCollectionEndpointError = "OBP-30083: Could not create ApiCollectionEndpoint."
val DeleteApiCollectionEndpointError = "OBP-30084: Could not delete ApiCollectionEndpoint."
val ApiCollectionEndpointAlreadyExisting = "OBP-30085: The ApiCollectionEndpoint is already Existing."
val ApiCollectionAlreadyExisting = "OBP-30086: The ApiCollection is already Existing."
val ApiCollectionEndpointAlreadyExists = "OBP-30085: The ApiCollectionEndpoint is already exists."
val ApiCollectionAlreadyExists = "OBP-30086: The ApiCollection is already exists."
val DoubleEntryTransactionNotFound = "OBP-30087: Double Entry Transaction not found."
@ -467,7 +469,7 @@ object ErrorMessages {
val InsufficientAuthorisationToDeleteBranch = "OBP-30218: Insufficient authorisation to Create Branch. You do not have the role CanCreateBranch." // was OBP-20019
val InsufficientAuthorisationToCreateBank = "OBP-30210: Insufficient authorisation to Create Bank. You do not have the role CanCreateBank." // was OBP-20020
val InvalidConnector = "OBP-30211: Invalid Connector Version. Please specify a valid value for CONNECTOR."
val InvalidConnector = "OBP-30211: Invalid Connector. Please specify a valid value for CONNECTOR."
val EntitlementNotFound = "OBP-30212: EntitlementId not found"
val UserDoesNotHaveEntitlement = "OBP-30213: USER_ID does not have the ENTITLEMENT_ID."
@ -492,13 +494,19 @@ object ErrorMessages {
val DeleteSystemViewError = "OBP-30251: Could not delete the system view"
val SystemViewNotFound = "OBP-30252: System view not found. Please specify a valid value for VIEW_ID"
val UpdateSystemViewError = "OBP-30253: Could not update the system view"
val ExistingSystemViewError = "OBP-30254: There is already a view with permalink"
val SystemViewAlreadyExistsError = "OBP-30254: The system view is already exists."
val EmptyNameOfSystemViewError = "OBP-30255: You cannot create a View with an empty Name"
val DeleteCustomViewError = "OBP-30256: Could not delete the custom view"
val CannotFindCustomViewError = "OBP-30257: Could not find the custom view"
val SystemViewCannotBePublicError = "OBP-30258: System view cannot be public"
val CreateCustomViewError = "OBP-30259: Could not create the custom view"
val UpdateCustomViewError = "OBP-30260: Could not update the custom view"
val CreateCounterpartyLimitError = "OBP-30261: Could not create the counterparty limit."
val UpdateCounterpartyLimitError = "OBP-30262: Could not update the counterparty limit."
val GetCounterpartyLimitError = "OBP-30263: Counterparty limit not found. Please specify a valid value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID."
val CounterpartyLimitAlreadyExists = "OBP-30264: Counterparty limit already exists. Please specify a different value for BANK_ID, ACCOUNT_ID, VIEW_ID or COUNTERPARTY_ID."
val DeleteCounterpartyLimitError = "OBP-30265: Could not delete the counterparty limit."
val CustomViewAlreadyExistsError = "OBP-30266: The custom view is already exists."
val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. "
val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. "
@ -719,7 +727,7 @@ object ErrorMessages {
// MethodRouting Exceptions (OBP-7XXXX)
val InvalidBankIdRegex = "OBP-70001: Incorrect regex for bankIdPattern."
val MethodRoutingNotFoundByMethodRoutingId = "OBP-70002: MethodRouting not found. Please specify a valid value for method_routing_id."
val ExistingMethodRoutingError = "OBP-70003: Method Routing is already existing."
val MethodRoutingAlreadyExistsError = "OBP-70003: Method Routing is already exists."
// Cascade Deletion Exceptions (OBP-8XXXX)
val CouldNotDeleteCascade = "OBP-80001: Could not delete cascade."
@ -841,6 +849,11 @@ object ErrorMessages {
* validate method: NewStyle.function.checkViewAccessAndReturnView
*/
def $UserNoPermissionAccessView = UserNoPermissionAccessView
/**
* validate method: NewStyle.function.getCounterpartyByCounterpartyId
*/
def $CounterpartyNotFoundByCounterpartyId = CounterpartyNotFoundByCounterpartyId
def getDuplicatedMessageNumbers = {

View File

@ -26,7 +26,7 @@ object ExampleValue {
val NoDescriptionProvided = "no-description-provided"
val NoExampleProvided = ""
val booleanTrue = "true"
val booleanFalse = "false"
lazy val bankIdGlossary = glossaryItems.find(_.title == "Bank.bank_id").map(_.textDescription)
@ -105,7 +105,7 @@ object ExampleValue {
lazy val dependentsExample = ConnectorField("2", s"the number of dependents") // Dominant form in American English
glossaryItems += makeGlossaryItem("Customer.dependents", dependentsExample)
lazy val kycStatusExample = ConnectorField(booleanTrue, s"This is boolean to indicate if the cusomter's KYC has been checked.")
lazy val kycStatusExample = ConnectorField(booleanFalse, s"This is boolean to indicate if the cusomter's KYC has been checked.")
glossaryItems += makeGlossaryItem("Customer.kycStatus", kycStatusExample)
lazy val urlExample = ConnectorField("http://www.example.com/id-docs/123/image.png", s"The URL ")
@ -179,7 +179,7 @@ object ExampleValue {
lazy val otherAccountProviderExample = ConnectorField("", s"")//TODO, not sure what is this field for?
glossaryItems += makeGlossaryItem("Transaction.otherAccountProvider", otherAccountProviderExample)
lazy val isBeneficiaryExample = ConnectorField(booleanTrue, s"This is a boolean. True if the originAccount can send money to the Counterparty")
lazy val isBeneficiaryExample = ConnectorField(booleanFalse, s"This is a boolean. True if the originAccount can send money to the Counterparty")
glossaryItems += makeGlossaryItem("Counterparty.isBeneficiary", isBeneficiaryExample)
lazy val counterpartyNameExample = ConnectorField("John Smith Ltd.", s"The name of a Counterparty. Ideally unique for an Account")
@ -560,7 +560,7 @@ object ExampleValue {
lazy val inboundAvroSchemaExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("DynamicMessageDoc.inboundAvroSchema", inboundAvroSchemaExample)
lazy val canSeeImagesExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeImagesExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_images", canSeeImagesExample)
lazy val topConsumersExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -572,7 +572,7 @@ object ExampleValue {
lazy val maximumResponseTimeExample = ConnectorField("60",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("maximum_response_time", maximumResponseTimeExample)
lazy val cancelledExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val cancelledExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("cancelled", cancelledExample)
lazy val entitlementRequestsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -620,7 +620,7 @@ object ExampleValue {
lazy val canSeeOtherAccountRoutingSchemeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_other_account_routing_scheme", canSeeOtherAccountRoutingSchemeExample)
lazy val canDeleteCorporateLocationExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canDeleteCorporateLocationExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_delete_corporate_location", canDeleteCorporateLocationExample)
lazy val fromExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -692,7 +692,7 @@ object ExampleValue {
lazy val corporateLocationExample = ConnectorField("10",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("corporate_location", corporateLocationExample)
lazy val enabledExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val enabledExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("enabled", enabledExample)
lazy val durationExample = ConnectorField("5.123"," This is a decimal number in seconds, eg: 1 for 1 second, 0.001 for 1 ms")
@ -704,7 +704,7 @@ object ExampleValue {
lazy val toSepaExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("to_sepa", toSepaExample)
lazy val whichAliasToUseExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
lazy val whichAliasToUseExample = ConnectorField("public",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("which_alias_to_use", whichAliasToUseExample)
lazy val canAddImageExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -773,7 +773,7 @@ object ExampleValue {
lazy val creatorExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("creator", creatorExample)
lazy val activeExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val activeExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("active", activeExample)
lazy val canSeeOtherAccountMetadataExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -965,7 +965,7 @@ object ExampleValue {
lazy val canSeeTransactionFinishDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_transaction_finish_date", canSeeTransactionFinishDateExample)
lazy val satisfiedExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val satisfiedExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("satisfied", satisfiedExample)
lazy val canSeeOtherAccountIbanExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1172,7 +1172,7 @@ object ExampleValue {
lazy val accountOtpExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("account_otp", accountOtpExample)
lazy val hideMetadataIfAliasUsedExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
lazy val hideMetadataIfAliasUsedExample = ConnectorField(booleanFalse, NoDescriptionProvided)
glossaryItems += makeGlossaryItem("hide_metadata_if_alias_used", hideMetadataIfAliasUsedExample)
lazy val canSeeBankAccountCurrencyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1576,7 +1576,7 @@ object ExampleValue {
lazy val isFirehoseExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("is_firehose", isFirehoseExample)
lazy val okExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val okExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("ok", okExample)
lazy val bankRoutingExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1600,7 +1600,7 @@ object ExampleValue {
lazy val dependentEndpointsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("dependent_endpoints", dependentEndpointsExample)
lazy val hasDepositCapabilityExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val hasDepositCapabilityExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("ATM.has_deposit_capability", hasDepositCapabilityExample)
lazy val toCounterpartyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1624,10 +1624,10 @@ object ExampleValue {
lazy val canSeeCommentsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_comments", canSeeCommentsExample)
lazy val canEditOwnerCommentExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canEditOwnerCommentExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_edit_owner_comment", canEditOwnerCommentExample)
lazy val canAddCounterpartyExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canAddCounterpartyExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_add_counterparty", canAddCounterpartyExample)
lazy val markdownExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1660,7 +1660,7 @@ object ExampleValue {
lazy val narrativeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("narrative", narrativeExample)
lazy val canSeeOtherAccountRoutingAddressExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeOtherAccountRoutingAddressExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_other_account_routing_address", canSeeOtherAccountRoutingAddressExample)
lazy val statusesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1696,7 +1696,7 @@ object ExampleValue {
lazy val tuesdayExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("tuesday", tuesdayExample)
lazy val canQueryAvailableFundsExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canQueryAvailableFundsExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_query_available_funds", canQueryAvailableFundsExample)
lazy val otherAccountSecondaryRoutingSchemeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1782,7 +1782,7 @@ object ExampleValue {
lazy val roleExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("role", roleExample)
lazy val requireScopesForListedRolesExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val requireScopesForListedRolesExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("require_scopes_for_listed_roles", requireScopesForListedRolesExample)
lazy val branchTypeExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1791,7 +1791,7 @@ object ExampleValue {
lazy val fullNameExample = ConnectorField("full name string",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("full_name", fullNameExample)
lazy val canCreateDirectDebitExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canCreateDirectDebitExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_create_direct_debit", canCreateDirectDebitExample)
lazy val futureDateExample = ConnectorField("20200127",NoDescriptionProvided)
@ -1809,7 +1809,7 @@ object ExampleValue {
lazy val documentNumberExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("document_number", documentNumberExample)
lazy val canSeeOtherAccountNationalIdentifierExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeOtherAccountNationalIdentifierExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_other_account_national_identifier", canSeeOtherAccountNationalIdentifierExample)
lazy val canSeeTransactionStartDateExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1821,7 +1821,7 @@ object ExampleValue {
lazy val cacheExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("cache", cacheExample)
lazy val canSeeBankRoutingAddressExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeBankRoutingAddressExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_bank_routing_address", canSeeBankRoutingAddressExample)
lazy val usersExample = ConnectorField("user list", "Please refer to the user object.")
@ -1833,7 +1833,7 @@ object ExampleValue {
lazy val ktyExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("kty", ktyExample)
lazy val canBeSeenOnViewsExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canBeSeenOnViewsExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_be_seen_on_views", canBeSeenOnViewsExample)
lazy val kidExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1851,7 +1851,7 @@ object ExampleValue {
lazy val metadataExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("metadata", metadataExample)
lazy val canSeeTransactionAmountExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeTransactionAmountExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_transaction_amount", canSeeTransactionAmountExample)
lazy val methodRoutingIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1875,10 +1875,10 @@ object ExampleValue {
lazy val countryCodeExample = ConnectorField("1254",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("country_code", countryCodeExample)
lazy val canSeeBankAccountCreditLimitExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeBankAccountCreditLimitExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_bank_account_credit_limit", canSeeBankAccountCreditLimitExample)
lazy val canSeeOtherAccountNumberExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeOtherAccountNumberExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_other_account_number", canSeeOtherAccountNumberExample)
lazy val orderExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1896,10 +1896,10 @@ object ExampleValue {
lazy val taxResidenceExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("tax_residence", taxResidenceExample)
lazy val isActiveExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val isActiveExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("is_active", isActiveExample)
lazy val canSeeBankAccountBankNameExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeBankAccountBankNameExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_bank_account_bank_name", canSeeBankAccountBankNameExample)
lazy val firstNameExample = ConnectorField("Tom","The first name")
@ -1914,7 +1914,7 @@ object ExampleValue {
lazy val transactionIdsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("transaction_ids", transactionIdsExample)
lazy val canSeeBankAccountOwnersExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeBankAccountOwnersExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_bank_account_owners", canSeeBankAccountOwnersExample)
lazy val actualDateExample = ConnectorField("2020-01-27",NoDescriptionProvided)
@ -1923,10 +1923,10 @@ object ExampleValue {
lazy val exampleOutboundMessageExample = ConnectorField("{}","this will the json object")
glossaryItems += makeGlossaryItem("example_outbound_message", exampleOutboundMessageExample)
lazy val canDeleteWhereTagExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canDeleteWhereTagExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_delete_where_tag", canDeleteWhereTagExample)
lazy val canSeeUrlExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeUrlExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_url", canSeeUrlExample)
lazy val versionExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1953,7 +1953,7 @@ object ExampleValue {
lazy val implementedInVersionExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("implemented_in_version", implementedInVersionExample)
lazy val canSeeImageUrlExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeImageUrlExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_image_url", canSeeImageUrlExample)
lazy val toTransferToPhoneExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -1998,7 +1998,7 @@ object ExampleValue {
lazy val eExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("e", eExample)
lazy val canSeeCorporateLocationExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeCorporateLocationExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_corporate_location", canSeeCorporateLocationExample)
lazy val userExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -2046,7 +2046,7 @@ object ExampleValue {
lazy val requiredfieldinfoExample = ConnectorField("false",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("requiredfieldinfo", requiredfieldinfoExample)
lazy val canSeeWhereTagExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeWhereTagExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_where_tag", canSeeWhereTagExample)
lazy val bankidExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -2109,10 +2109,10 @@ object ExampleValue {
lazy val toSandboxTanExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("to_sandbox_tan", toSandboxTanExample)
lazy val canAddTagExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canAddTagExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_add_tag", canAddTagExample)
lazy val canSeeBankAccountLabelExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeBankAccountLabelExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_bank_account_label", canSeeBankAccountLabelExample)
lazy val serviceAvailableExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -2136,7 +2136,7 @@ object ExampleValue {
lazy val driveUpExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("drive_up", driveUpExample)
lazy val canAddMoreInfoExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canAddMoreInfoExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_add_more_info", canAddMoreInfoExample)
lazy val detailExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -2148,13 +2148,31 @@ object ExampleValue {
lazy val transactionRequestTypesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("transaction_request_types", transactionRequestTypesExample)
lazy val canAddImageUrlExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val counterpartyLimitIdExample = ConnectorField("abc9a7e4-6d02-40e3-a129-0b2bf89de9b1","A string that MUST uniquely identify the Counterparty Limit on this OBP instance.")
glossaryItems += makeGlossaryItem("counterparty_limit_id", counterpartyLimitIdExample)
lazy val maxSingleAmountExample = ConnectorField("1000",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("max_single_amount", maxSingleAmountExample)
lazy val maxMonthlyAmountExample = ConnectorField("10000",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("max_monthly_amount", maxMonthlyAmountExample)
lazy val maxNumberOfMonthlyTransactionsExample = ConnectorField("10",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("max_number_of_monthly_transactions", maxNumberOfMonthlyTransactionsExample)
lazy val maxYearlyAmountExample = ConnectorField("12000",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("max_yearly_amount", maxYearlyAmountExample)
lazy val maxNumberOfYearlyTransactionsExample = ConnectorField("100",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("max_number_of_yearly_transactions", maxNumberOfYearlyTransactionsExample)
lazy val canAddImageUrlExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_add_image_url", canAddImageUrlExample)
lazy val jwksUrisExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("jwks_uris", jwksUrisExample)
lazy val canSeeOtherAccountSwiftBicExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeOtherAccountSwiftBicExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_other_account_swift_bic", canSeeOtherAccountSwiftBicExample)
lazy val staffUserIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -2166,7 +2184,7 @@ object ExampleValue {
lazy val validFromExample = ConnectorField("2020-01-27",NoDescriptionProvided)
glossaryItems += makeGlossaryItem("valid_from", validFromExample)
lazy val canDeleteImageExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canDeleteImageExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_delete_image", canDeleteImageExample)
lazy val toExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
@ -2178,25 +2196,25 @@ object ExampleValue {
lazy val productAttributesExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("product_attributes", productAttributesExample)
lazy val canSeeTransactionDescriptionExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeTransactionDescriptionExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_transaction_description", canSeeTransactionDescriptionExample)
lazy val faceImageExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("face_image", faceImageExample)
lazy val canSeeBankAccountNumberExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val canSeeBankAccountNumberExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("can_see_bank_account_number", canSeeBankAccountNumberExample)
lazy val glossaryItemsExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("glossary_items", glossaryItemsExample)
lazy val isBankIdExactMatchExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val isBankIdExactMatchExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("is_bank_id_exact_match", isBankIdExactMatchExample)
lazy val isPublicExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val isPublicExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("is_public", isPublicExample)
lazy val isAccessibleExample = ConnectorField(booleanTrue,NoDescriptionProvided)
lazy val isAccessibleExample = ConnectorField(booleanFalse,NoDescriptionProvided)
glossaryItems += makeGlossaryItem("ATM.is_accessible", isAccessibleExample)
lazy val entitlementIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided)

View File

@ -3009,10 +3009,10 @@ object Glossary extends MdcLoggable {
title = "Account Access",
description =
s"""
|Account Access is OBP View system. The Account owners can create the view themselves.
|And they can grant/revoke the view to other users to use their view.
|Account Access governs access to Bank Accounts by end Users. It is an intersecting entity between the User and the View Definition.
|A User must have at least one Account Access record record in order to interact with a Bank Account over the OBP API.
|""".stripMargin)
// val allTagNames: Set[String] = ApiTag.allDisplayTagNames
// val existingItems: Set[String] = glossaryItems.map(_.title).toSet
// allTagNames.diff(existingItems).map(title => glossaryItems += GlossaryItem(title, title))

View File

@ -70,6 +70,7 @@ 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, CounterpartyLimitTrait}
import code.crm.CrmEvent
import code.crm.CrmEvent.CrmEvent
import code.customeraccountlinks.CustomerAccountLinkTrait
@ -372,10 +373,14 @@ object NewStyle extends MdcLoggable{
}
}
}
def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]): OBPReturnType[AccountsBalances] = {
Connector.connector.vend.getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) map { i =>
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2)
def getAccountListThroughView(user : User, viewId: ViewId, callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = {
val viewIds = List(viewId)
Views.views.vend.getPrivateBankAccountsFuture(user, viewIds) map { i =>
if(i.isEmpty) {
(unboxFullOrFail(Empty, callContext, NoViewReadAccountsBerlinGroup , 403), callContext)
} else {
(i, callContext )
}
}
}
@ -385,12 +390,6 @@ object NewStyle extends MdcLoggable{
}
}
def getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]): OBPReturnType[AccountBalances] = {
Connector.connector.vend.getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]) map { i =>
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2)
}
}
def getAccountRouting(bankId: Option[BankId], scheme: String, address: String, callContext: Option[CallContext]) : OBPReturnType[BankAccountRouting] = {
Future(Connector.connector.vend.getAccountRouting(bankId: Option[BankId], scheme: String, address : String, callContext: Option[CallContext])) map { i =>
unboxFullOrFail(i, callContext,s"$AccountRoutingNotFound Current scheme is $scheme, current address is $address, current bankId is $bankId", 404 )
@ -2927,7 +2926,7 @@ object NewStyle extends MdcLoggable{
}
val notExists = if(exists) Empty else Full(true)
(unboxFullOrFail(notExists, callContext, s"$ExistingMethodRoutingError Please modify the following parameters:" +
(unboxFullOrFail(notExists, callContext, s"$MethodRoutingAlreadyExistsError Please modify the following parameters:" +
s"is_bank_id_exact_match(${methodRouting.isBankIdExactMatch}), " +
s"method_name(${methodRouting.methodName}), " +
s"bank_id_pattern(${methodRouting.bankIdPattern.getOrElse("")})"
@ -3464,7 +3463,7 @@ object NewStyle extends MdcLoggable{
def getConnectorByName(connectorName: String): Option[Connector] = {
if(supportedConnectorNames.exists(connectorName.startsWith _)) {
Connector.nameToConnector.get(connectorName).map(_())
Connector.nameToConnector.get(connectorName)
} else {
None
}
@ -4060,6 +4059,67 @@ object NewStyle extends MdcLoggable{
(unboxFullOrFail(i, callContext, s"$DeleteCustomViewError"), callContext)
}
def createOrUpdateCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
maxSingleAmount: Int,
maxMonthlyAmount: Int,
maxNumberOfMonthlyTransactions: Int,
maxYearlyAmount: Int,
maxNumberOfYearlyTransactions: Int,
callContext: Option[CallContext]
): OBPReturnType[CounterpartyLimitTrait] =
Connector.connector.vend.createOrUpdateCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
maxSingleAmount: Int,
maxMonthlyAmount: Int,
maxNumberOfMonthlyTransactions: Int,
maxYearlyAmount: Int,
maxNumberOfYearlyTransactions: Int,
callContext: Option[CallContext]
) map {
i => (unboxFullOrFail(i._1, callContext, CreateCounterpartyLimitError), i._2)
}
def getCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
): OBPReturnType[CounterpartyLimitTrait] =
Connector.connector.vend.getCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
) map {
i => (unboxFullOrFail(i._1, callContext, s"$GetCounterpartyLimitError Current BANK_ID($bankId), " +
s"ACCOUNT_ID($accountId), VIEW_ID($viewId),COUNTERPARTY_ID($counterpartyId)"), i._2)
}
def deleteCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
): OBPReturnType[Boolean] =
Connector.connector.vend.deleteCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
) map {
i => (unboxFullOrFail(i._1, callContext, s"$DeleteCounterpartyLimitError"), i._2)
}
}
}

View File

@ -0,0 +1,60 @@
package code.api.util.newstyle
import code.api.util.APIUtil.{OBPReturnType, unboxFullOrFail}
import code.api.util.ErrorMessages.InvalidConnectorResponseForGetBankAccounts
import code.api.util.{APIUtil, CallContext}
import code.bankconnectors.Connector
import code.views.Views
import com.openbankproject.commons.model.{AccountBalances, AccountsBalances, BankId, BankIdAccountId, User, ViewId}
import scala.concurrent.Future
object BalanceNewStyle {
import com.openbankproject.commons.ExecutionContext.Implicits.global
def getAccountAccessAtBankThroughView(user: User,
bankId: BankId,
viewId: ViewId,
callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = {
Future {
val (views, accountAccesses) = Views.views.vend.getAccountAccessAtBankThroughView(user, bankId, viewId)
// Filter views which can read the balance
val canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance)
// Filter accounts the user has permission to see balances and remove duplicates
val allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews)
allowedAccounts
} map {
(_, callContext)
}
}
def getAccountAccessAtBank(user: User,
bankId: BankId,
callContext: Option[CallContext]): OBPReturnType[List[BankIdAccountId]] = {
Future {
val (views, accountAccesses) = Views.views.vend.privateViewsUserCanAccessAtBank(user, bankId)
// Filter views which can read the balance
val canSeeBankAccountBalanceViews = views.filter(_.canSeeBankAccountBalance)
// Filter accounts the user has permission to see balances and remove duplicates
val allowedAccounts = APIUtil.intersectAccountAccessAndView(accountAccesses, canSeeBankAccountBalanceViews)
allowedAccounts
} map {
(_, callContext)
}
}
def getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]): OBPReturnType[AccountBalances] = {
Connector.connector.vend.getBankAccountBalances(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]) map { i =>
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2)
}
}
def getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]): OBPReturnType[AccountsBalances] = {
Connector.connector.vend.getBankAccountsBalances(bankIdAccountIds: List[BankIdAccountId], callContext: Option[CallContext]) map { i =>
(unboxFullOrFail(i._1, callContext,s"$InvalidConnectorResponseForGetBankAccounts", 400 ), i._2)
}
}
}

View File

@ -568,7 +568,7 @@ trait APIMethods121 {
| The 'alias' field in the JSON can take one of three values:
|
| * _public_: to use the public alias if there is one specified for the other account.
| * _private_: to use the public alias if there is one specified for the other account.
| * _private_: to use the private alias if there is one specified for the other account.
|
| * _''(empty string)_: to use no alias; the view shows the real name of the other account.
|
@ -645,7 +645,8 @@ trait APIMethods121 {
),
List(apiTagAccount, apiTagView, apiTagOldStyle)
)
//TODO. remove and replace it with V510.
lazy val updateViewForBankAccount: OBPEndpoint = {
//updates a view on a bank account
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId

View File

@ -606,15 +606,15 @@ trait APIMethods210 {
"Answer Transaction Request Challenge",
"""In Sandbox mode, any string that can be converted to a positive integer will be accepted as an answer.
|
|This endpoint totally depends on createTransactionRequest, it need get the following data from createTransactionRequest response body.
|This endpoint expects the following data as provided in the createTransactionRequest response body:
|
|1)`TRANSACTION_REQUEST_TYPE` : is the same as createTransactionRequest request URL .
|1)`TRANSACTION_REQUEST_TYPE` : as per the selected createTransactionRequest type, part of the request URL.
|
|2)`TRANSACTION_REQUEST_ID` : is the `id` field in createTransactionRequest response body.
|2)`TRANSACTION_REQUEST_ID` : the value of the `id` field of the createTransactionRequest response body.
|
|3) `id` : is `challenge.id` field in createTransactionRequest response body.
|3) `id` : the value of `challenge.id` in the createTransactionRequest response body.
|
|4) `answer` : must be `123`. if it is in sandbox mode. If it kafka mode, the answer can be got by phone message or other security ways.
|4) `answer` : Defaults to `123`, if running in sandbox mode. In production mode, the value will be sent via the configured SCA method.
|
""".stripMargin,
challengeAnswerJSON,
@ -1735,4 +1735,4 @@ trait APIMethods210 {
}
}
}
}
}

View File

@ -164,7 +164,7 @@ trait APIMethods220 {
| The 'alias' field in the JSON can take one of three values:
|
| * _public_: to use the public alias if there is one specified for the other account.
| * _private_: to use the public alias if there is one specified for the other account.
| * _private_: to use the private alias if there is one specified for the other account.
|
| * _''(empty string)_: to use no alias; the view shows the real name of the other account.
|
@ -442,7 +442,7 @@ trait APIMethods220 {
""".stripMargin,
emptyObjectJson,
messageDocsJson,
List(UnknownError),
List(InvalidConnector, UnknownError),
List(apiTagDocumentation, apiTagApi)
)
@ -452,7 +452,7 @@ trait APIMethods220 {
implicit val ec = EndpointContext(Some(cc))
for {
connectorObject <- Future(tryo{Connector.getConnectorInstance(connector)}) map { i =>
val msg = "$InvalidConnector Current Input is $connector. It should be eg: kafka_vSept2018..."
val msg = s"$InvalidConnector Current Input is $connector. It should be eg: kafka_vSept2018..."
unboxFullOrFail(i, cc.callContext, msg)
}
} yield {

View File

@ -178,7 +178,7 @@ trait APIMethods300 {
| The 'alias' field in the JSON can take one of three values:
|
| * _public_: to use the public alias if there is one specified for the other account.
| * _private_: to use the public alias if there is one specified for the other account.
| * _private_: to use the private alias if there is one specified for the other account.
|
| * _''(empty string)_: to use no alias; the view shows the real name of the other account.
|
@ -198,6 +198,7 @@ trait APIMethods300 {
),
List(apiTagView, apiTagAccount))
//TODO. remove and replace it with V510.
lazy val createViewForBankAccount : OBPEndpoint = {
//creates a view on an bank account
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: Nil JsonPost json -> _ => {
@ -500,7 +501,7 @@ trait APIMethods300 {
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u)
(coreAccounts, callContext) <- getFilteredCoreAccounts(availablePrivateAccounts, req, callContext)
} yield {
(JSONFactory300.createCoreAccountsByCoreAccountsJSON(coreAccounts), HttpCode.`200`(callContext))
(JSONFactory300.createCoreAccountsByCoreAccountsJSON(coreAccounts, u), HttpCode.`200`(callContext))
}
}
}
@ -1710,7 +1711,7 @@ trait APIMethods300 {
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId)
(accounts, callContext) <- getFilteredCoreAccounts(availablePrivateAccounts, req, callContext)
} yield {
(JSONFactory300.createCoreAccountsByCoreAccountsJSON(accounts), HttpCode.`200`(callContext))
(JSONFactory300.createCoreAccountsByCoreAccountsJSON(accounts, u), HttpCode.`200`(callContext))
}
}
}

View File

@ -846,7 +846,7 @@ object JSONFactory300{
)
)
def createCoreAccountsByCoreAccountsJSON(coreAccounts : List[CoreAccount]): CoreAccountsJsonV300 =
def createCoreAccountsByCoreAccountsJSON(coreAccounts : List[CoreAccount], user: User): CoreAccountsJsonV300 =
CoreAccountsJsonV300(coreAccounts.map(coreAccount => CoreAccountJson(
coreAccount.id,
coreAccount.label,
@ -854,7 +854,7 @@ object JSONFactory300{
coreAccount.accountType,
coreAccount.accountRoutings.map(accountRounting =>AccountRoutingJsonV121(accountRounting.scheme, accountRounting.address)),
views = Views.views.vend
.assignedViewsForAccount(BankIdAccountId(BankId(coreAccount.bankId), AccountId(coreAccount.id))).filter(_.isPrivate)
.privateViewsUserCanAccessForAccount(user, BankIdAccountId(BankId(coreAccount.bankId), AccountId(coreAccount.id))).filter(_.isPrivate)
.map(mappedView =>
ViewBasicV300(
mappedView.viewId.value,

View File

@ -17,6 +17,7 @@ import code.api.util.ExampleValue._
import code.api.util.FutureUtil.EndpointContext
import code.api.util.NewStyle.HttpCode
import code.api.util._
import code.api.util.newstyle.BalanceNewStyle
import code.api.v1_2_1.{JSONFactory, RateLimiting}
import code.api.v2_0_0.CreateMeetingJson
import code.api.v2_1_0._
@ -3951,7 +3952,7 @@ trait APIMethods310 {
| The 'alias' field in the JSON can take one of two values:
|
| * _public_: to use the public alias if there is one specified for the other account.
| * _private_: to use the public alias if there is one specified for the other account.
| * _private_: to use the private alias if there is one specified for the other account.
|
| * _''(empty string)_: to use no alias; the view shows the real name of the other account.
|
@ -5935,7 +5936,7 @@ trait APIMethods310 {
(Full(u), callContext) <- authenticatedAccess(cc)
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId)
(accountsBalances, callContext)<- NewStyle.function.getBankAccountsBalances(availablePrivateAccounts, callContext)
(accountsBalances, callContext)<- BalanceNewStyle.getBankAccountsBalances(availablePrivateAccounts, callContext)
} yield{
(createBalancesJson(accountsBalances), HttpCode.`200`(callContext))
}

View File

@ -29,7 +29,7 @@ import code.api.util._
import code.api.util.migration.Migration
import code.api.util.newstyle.AttributeDefinition._
import code.api.util.newstyle.Consumer._
import code.api.util.newstyle.UserCustomerLinkNewStyle
import code.api.util.newstyle.{BalanceNewStyle, UserCustomerLinkNewStyle}
import code.api.util.newstyle.UserCustomerLinkNewStyle.getUserCustomerLinks
import code.api.v1_2_1.{JSONFactory, PostTransactionTagJSON}
import code.api.v1_4_0.JSONFactory1_4_0
@ -3346,9 +3346,9 @@ trait APIMethods400 extends MdcLoggable {
}
staticResourceDocs += ResourceDoc(
getBankAccountsBalances,
getBankAccountsBalancesForCurrentUser,
implementedInApiVersion,
nameOf(getBankAccountsBalances),
nameOf(getBankAccountsBalancesForCurrentUser),
"GET",
"/banks/BANK_ID/balances",
"Get Accounts Balances",
@ -3359,23 +3359,23 @@ trait APIMethods400 extends MdcLoggable {
apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil
)
lazy val getBankAccountsBalances : OBPEndpoint = {
lazy val getBankAccountsBalancesForCurrentUser : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "balances" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- SS.user
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId)
(accountsBalances, callContext)<- NewStyle.function.getBankAccountsBalances(availablePrivateAccounts, callContext)
} yield{
(allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBank(u, bankId, callContext)
(accountsBalances, callContext)<- BalanceNewStyle.getBankAccountsBalances(allowedAccounts, callContext)
} yield {
(createBalancesJson(accountsBalances), HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getBankAccountBalances,
getBankAccountBalancesForCurrentUser,
implementedInApiVersion,
nameOf(getBankAccountBalances),
nameOf(getBankAccountBalancesForCurrentUser),
"GET",
"/banks/BANK_ID/accounts/ACCOUNT_ID/balances",
"Get Account Balances",
@ -3386,15 +3386,18 @@ trait APIMethods400 extends MdcLoggable {
apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil
)
lazy val getBankAccountBalances : OBPEndpoint = {
lazy val getBankAccountBalancesForCurrentUser : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "balances" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- SS.user
availablePrivateAccounts <- Views.views.vend.getPrivateBankAccountsFuture(u, bankId)
bankIdAcconutId <- NewStyle.function.tryons(s"$CannotFindAccountAccess AccountId(${accountId.value})", 400, cc.callContext) {availablePrivateAccounts.find(_.accountId==accountId).get}
(accountBalances, callContext)<- NewStyle.function.getBankAccountBalances(bankIdAcconutId, callContext)
} yield{
(allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBank(u, bankId, callContext)
msg = s"$CannotFindAccountAccess AccountId(${accountId.value})"
bankIdAccountId <- NewStyle.function.tryons(msg, 400, cc.callContext) {
allowedAccounts.find(_.accountId==accountId).get
}
(accountBalances, callContext)<- BalanceNewStyle.getBankAccountBalances(bankIdAccountId, callContext)
} yield {
(createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext))
}
}
@ -9013,7 +9016,7 @@ trait APIMethods400 extends MdcLoggable {
json.extract[PostApiCollectionJson400]
}
apiCollection <- Future{MappedApiCollectionsProvider.getApiCollectionByUserIdAndCollectionName(cc.userId, postJson.api_collection_name)}
_ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionAlreadyExisting Current api_collection_name(${postJson.api_collection_name}) is already existing for the log in user.", cc=cc.callContext) {
_ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionAlreadyExists Current api_collection_name(${postJson.api_collection_name}) is already existing for the log in user.", cc=cc.callContext) {
apiCollection.isEmpty
}
(apiCollection, callContext) <- NewStyle.function.createApiCollection(
@ -9296,7 +9299,7 @@ trait APIMethods400 extends MdcLoggable {
}
(apiCollection, callContext) <- NewStyle.function.getApiCollectionByUserIdAndCollectionName(cc.userId, apiCollectionName, Some(cc))
apiCollectionEndpoint <- Future{MappedApiCollectionEndpointsProvider.getApiCollectionEndpointByApiCollectionIdAndOperationId(apiCollection.apiCollectionId, postJson.operation_id)}
_ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExisting Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_NAME($apiCollectionName) ", cc=callContext) {
_ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExists Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_NAME($apiCollectionName) ", cc=callContext) {
apiCollectionEndpoint.isEmpty
}
(apiCollectionEndpoint, callContext) <- NewStyle.function.createApiCollectionEndpoint(
@ -9345,7 +9348,7 @@ trait APIMethods400 extends MdcLoggable {
}
(apiCollection, callContext) <- NewStyle.function.getApiCollectionById(apiCollectionId, Some(cc))
apiCollectionEndpoint <- Future{MappedApiCollectionEndpointsProvider.getApiCollectionEndpointByApiCollectionIdAndOperationId(apiCollection.apiCollectionId, postJson.operation_id)}
_ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExisting Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_ID($apiCollectionId) ", cc=callContext) {
_ <- Helper.booleanToFuture(failMsg = s"$ApiCollectionEndpointAlreadyExists Current OPERATION_ID(${postJson.operation_id}) is already in API_COLLECTION_ID($apiCollectionId) ", cc=callContext) {
apiCollectionEndpoint.isEmpty
}
(apiCollectionEndpoint, callContext) <- NewStyle.function.createApiCollectionEndpoint(

View File

@ -45,6 +45,7 @@ import java.util.concurrent.ThreadLocalRandom
import code.accountattribute.AccountAttributeX
import code.api.Constant.SYSTEM_OWNER_VIEW_ID
import code.api.util.FutureUtil.EndpointContext
import code.consumer.Consumers
import code.util.Helper.booleanToFuture
import code.views.system.{AccountAccess, ViewDefinition}
@ -991,7 +992,15 @@ trait APIMethods500 {
case Props.RunModes.Test => Consent.challengeAnswerAtTestEnvironment
case _ => SecureRandomUtil.numeric()
}
createdConsent <- Future(Consents.consentProvider.vend.createObpConsent(user, challengeAnswer, Some(consentRequestId))) map {
consumer = Consumers.consumers.vend.getConsumerByConsumerId(calculatedConsumerId.getOrElse("None"))
createdConsent <- Future(
Consents.consentProvider.vend.createObpConsent(
user,
challengeAnswer,
Some(consentRequestId),
consumer
)
) map {
i => connectorEmptyResponse(i, callContext)
}
@ -1852,18 +1861,19 @@ trait APIMethods500 {
s"""Create a system view
|
| ${authenticationRequiredMessage(true)} and the user needs to have access to the $canCreateSystemView entitlement.
| The 'alias' field in the JSON can take one of two values:
|
| The 'allowed_actions' field is a list containing the names of the actions allowed through this view.
| All the actions contained in the list will be set to `true` on the view creation, the rest will be set to `false`.
|
| The 'alias' field in the JSON can take one of three values:
|
| * _public_: to use the public alias if there is one specified for the other account.
| * _private_: to use the public alias if there is one specified for the other account.
|
| * _private_: to use the private alias if there is one specified for the other account.
| * _''(empty string)_: to use no alias; the view shows the real name of the other account.
|
| The 'hide_metadata_if_alias_used' field in the JSON can take boolean values. If it is set to `true` and there is an alias on the other account then the other accounts' metadata (like more_info, url, image_url, open_corporates_url, etc.) will be hidden. Otherwise the metadata will be shown.
|
| The 'allowed_actions' field is a list containing the name of the actions allowed on this view, all the actions contained will be set to `true` on the view creation, the rest will be set to `false`.
|
| Please note that system views cannot be public. In case you try to set it you will get the error $SystemViewCannotBePublicError
| System views cannot be public. In case you try to set it you will get the error $SystemViewCannotBePublicError
| """,
createSystemViewJsonV500,
viewJsonV500,

View File

@ -1,6 +1,7 @@
package code.api.v5_1_0
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.{Constant, UserNotFound}
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
import code.api.util.APIUtil._
@ -12,13 +13,17 @@ import code.api.util.JwtUtil.{getSignedPayloadAsJson, verifyJwt}
import code.api.util.NewStyle.HttpCode
import code.api.util.X509.{getCommonName, getEmailAddress, getOrganization}
import code.api.util._
import code.api.util.newstyle.BalanceNewStyle
import code.api.util.newstyle.Consumer.createConsumerNewStyle
import code.api.util.newstyle.RegulatedEntityNewStyle.{createRegulatedEntityNewStyle, deleteRegulatedEntityNewStyle, getRegulatedEntitiesNewStyle, getRegulatedEntityByEntityIdNewStyle}
import code.api.v1_2_1.CreateViewJsonV121
import code.api.v2_1_0.{ConsumerRedirectUrlJSON, JSONFactory210}
import code.api.v2_2_0.JSONFactory220
import code.api.v3_0_0.JSONFactory300
import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson
import code.api.v3_1_0.ConsentJsonV310
import code.api.v3_1_0.JSONFactory310.createBadLoginStatusJson
import code.api.v4_0_0.JSONFactory400.{createAccountBalancesJson, createBalancesJson, createNewCoreBankAccountJson}
import code.api.v4_0_0.{JSONFactory400, PostAccountAccessJsonV400, PostApiCollectionJson400, RevokedJsonV400}
import code.api.v5_1_0.JSONFactory510.{createRegulatedEntitiesJson, createRegulatedEntityJson}
import code.atmattribute.AtmAttribute
@ -26,14 +31,14 @@ import code.bankconnectors.Connector
import code.consent.Consents
import code.loginattempts.LoginAttempt
import code.metrics.APIMetrics
import code.model.AppType
import code.model.{AppType, BankAccountX}
import code.model.dataAccess.MappedBankAccount
import code.regulatedentities.MappedRegulatedEntityProvider
import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{apply => _}
import code.userlocks.UserLocksProvider
import code.users.Users
import code.util.Helper
import code.util.Helper.ObpS
import code.util.Helper.{ObpS, booleanToBox}
import code.views.Views
import code.views.system.{AccountAccess, ViewDefinition}
import com.github.dwickern.macros.NameOf.nameOf
@ -43,9 +48,9 @@ import com.openbankproject.commons.model._
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
import net.liftweb.common.Full
import net.liftweb.http.rest.RestHelper
import net.liftweb.json.{compactRender, parse, prettyRender}
import net.liftweb.json.{Extraction, compactRender, parse, prettyRender}
import net.liftweb.mapper.By
import net.liftweb.util.Helpers
import net.liftweb.util.{Helpers, StringHelpers}
import net.liftweb.util.Helpers.tryo
import scala.collection.immutable.{List, Nil}
@ -992,9 +997,9 @@ trait APIMethods510 {
cc => implicit val ec = EndpointContext(Some(cc))
for {
consent <- Future { Consents.consentProvider.vend.getConsentByConsentId(consentId)} map {
unboxFullOrFail(_, cc.callContext, ConsentNotFound)
unboxFullOrFail(_, cc.callContext, ConsentNotFound, 404)
}
_ <- Helper.booleanToFuture(failMsg = ConsentNotFound, cc = cc.callContext) {
_ <- Helper.booleanToFuture(failMsg = ConsentNotFound, failCode = 404, cc = cc.callContext) {
consent.mUserId == cc.userId
}
} yield {
@ -1093,7 +1098,7 @@ trait APIMethods510 {
(Full(user), callContext) <- authenticatedAccess(cc)
consentId = getConsentIdRequestHeaderValue(cc.requestHeaders).getOrElse("")
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
unboxFullOrFail(_, callContext, ConsentNotFound)
unboxFullOrFail(_, callContext, ConsentNotFound, 404)
}
consent <- Future(Consents.consentProvider.vend.revoke(consentId)) map {
i => connectorEmptyResponse(i, callContext)
@ -2155,11 +2160,570 @@ trait APIMethods510 {
staticResourceDocs += ResourceDoc(
getCoreAccountByIdThroughView,
implementedInApiVersion,
nameOf(getCoreAccountByIdThroughView),
"GET",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID",
"Get Account by Id (Core) through the VIEW_ID",
s"""Information returned about the account through VIEW_ID :
|""".stripMargin,
EmptyBody,
moderatedCoreAccountJsonV400,
List($UserNotLoggedIn, $BankAccountNotFound,UnknownError),
apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil
)
lazy val getCoreAccountByIdThroughView : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: Nil JsonGet req => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(user @Full(u), account, callContext) <- SS.userAccount
bankIdAccountId = BankIdAccountId(account.bankId, account.accountId)
view <- NewStyle.function.checkViewAccessAndReturnView(viewId , bankIdAccountId, user, callContext)
moderatedAccount <- NewStyle.function.moderatedBankAccountCore(account, view, user, callContext)
} yield {
val availableViews: List[View] = Views.views.vend.privateViewsUserCanAccessForAccount(u, BankIdAccountId(account.bankId, account.accountId))
(createNewCoreBankAccountJson(moderatedAccount, availableViews), HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getBankAccountBalances,
implementedInApiVersion,
nameOf(getBankAccountBalances),
"GET",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/balances",
"Get Account Balances by BANK_ID and ACCOUNT_ID through the VIEW_ID",
"""Get the Balances for the Account specified by BANK_ID and ACCOUNT_ID through the VIEW_ID.""",
EmptyBody,
accountBalanceV400,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
UserNoPermissionAccessView,
UnknownError
),
apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil
)
lazy val getBankAccountBalances : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: "balances" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- SS.user
bankIdAccountId = BankIdAccountId(bankId, accountId)
view <- NewStyle.function.checkViewAccessAndReturnView(viewId, bankIdAccountId, Full(u), callContext)
// Note we do one explicit check here rather than use moderated account because this provide an explicit message
failMsg = ViewDoesNotPermitAccess + " You need the permission canSeeBankAccountBalance."
_ <- Helper.booleanToFuture(failMsg, 403, cc = callContext) {
view.canSeeBankAccountBalance
}
(accountBalances, callContext) <- BalanceNewStyle.getBankAccountBalances(bankIdAccountId, callContext)
} yield {
(createAccountBalancesJson(accountBalances), HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getBankAccountsBalances,
implementedInApiVersion,
nameOf(getBankAccountsBalances),
"GET",
"/banks/BANK_ID/balances",
"Get Account Balances by BANK_ID",
"""Get the Balances for the Account specified by BANK_ID.""",
EmptyBody,
accountBalancesV400Json,
List(
$UserNotLoggedIn,
$BankNotFound,
UnknownError
),
apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil
)
lazy val getBankAccountsBalances : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "balances" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- SS.user
(allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBank(u, bankId, callContext)
(accountsBalances, callContext) <- BalanceNewStyle.getBankAccountsBalances(allowedAccounts, callContext)
} yield {
(createBalancesJson(accountsBalances), HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getBankAccountsBalancesThroughView,
implementedInApiVersion,
nameOf(getBankAccountsBalancesThroughView),
"GET",
"/banks/BANK_ID/views/VIEW_ID/balances",
"Get Account Balances by BANK_ID",
"""Get the Balances for the Account specified by BANK_ID.""",
EmptyBody,
accountBalancesV400Json,
List(
$UserNotLoggedIn,
$BankNotFound,
UnknownError
),
apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil
)
lazy val getBankAccountsBalancesThroughView : OBPEndpoint = {
case "banks" :: BankId(bankId) :: "views" :: ViewId(viewId) :: "balances" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(Full(u), callContext) <- SS.user
(allowedAccounts, callContext) <- BalanceNewStyle.getAccountAccessAtBankThroughView(u, bankId, viewId, callContext)
(accountsBalances, callContext) <- BalanceNewStyle.getBankAccountsBalances(allowedAccounts, callContext)
} yield {
(createBalancesJson(accountsBalances), HttpCode.`200`(callContext))
}
}
}
lazy val counterPartyLimitIntro: String =
"""Counter Party Limits can be used to restrict the Transaction Request amounts and frequencies (per month and year) that can be made to a Counterparty (Beneficiary).
|
|In order to implement VRP (Variable Recurring Payments) perform the following steps:
|1) Create a Custom View named e.g. VRP1.
|2) Place a Beneficiary Counterparty on that view.
|3) Add Counterparty Limits for that Counterparty.
|4) Generate a Consent containing the bank, account and view (e.g. VRP1)
|5) Let the App use the consent to trigger Transaction Requests.
|""".stripMargin
staticResourceDocs += ResourceDoc(
createCounterpartyLimit,
implementedInApiVersion,
nameOf(createCounterpartyLimit),
"POST",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits",
"Create Counterparty Limit",
s"""Create Counterparty Limit.
|
|$counterPartyLimitIntro
|
|""".stripMargin,
postCounterpartyLimitV510,
counterpartyLimitV510,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
$CounterpartyNotFoundByCounterpartyId,
InvalidJsonFormat,
UnknownError
),
List(apiTagCounterpartyLimits),
)
lazy val createCounterpartyLimit: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonPost json -> _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) {
json.extract[PostCounterpartyLimitV510]
}
(counterpartyLimitBox, callContext) <- Connector.connector.vend.getCounterpartyLimit(
bankId.value,
accountId.value,
viewId.value,
counterpartyId.value,
cc.callContext
)
failMsg = s"$CounterpartyLimitAlreadyExists Current BANK_ID($bankId), ACCOUNT_ID($accountId), VIEW_ID($viewId),COUNTERPARTY_ID($counterpartyId)"
_ <- Helper.booleanToFuture(failMsg, cc = callContext) {
counterpartyLimitBox.isEmpty
}
(counterpartyLimit,callContext) <- NewStyle.function.createOrUpdateCounterpartyLimit(
bankId.value,
accountId.value,
viewId.value,
counterpartyId.value,
postCounterpartyLimitV510.max_single_amount,
postCounterpartyLimitV510.max_monthly_amount,
postCounterpartyLimitV510.max_number_of_monthly_transactions,
postCounterpartyLimitV510.max_yearly_amount,
postCounterpartyLimitV510.max_number_of_yearly_transactions,
cc.callContext
)
} yield {
(counterpartyLimit.toJValue, HttpCode.`201`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
updateCounterpartyLimit,
implementedInApiVersion,
nameOf(updateCounterpartyLimit),
"PUT",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits",
"Update Counterparty Limit",
s"""Update Counterparty Limit.""",
postCounterpartyLimitV510,
counterpartyLimitV510,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
$CounterpartyNotFoundByCounterpartyId,
InvalidJsonFormat,
UnknownError
),
List(apiTagCounterpartyLimits),
)
lazy val updateCounterpartyLimit: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonPut json -> _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
postCounterpartyLimitV510 <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[AtmJsonV510]}", 400, cc.callContext) {
json.extract[PostCounterpartyLimitV510]
}
(counterpartyLimit,callContext) <- NewStyle.function.createOrUpdateCounterpartyLimit(
bankId.value,
accountId.value,
viewId.value,
counterpartyId.value,
postCounterpartyLimitV510.max_single_amount,
postCounterpartyLimitV510.max_monthly_amount,
postCounterpartyLimitV510.max_number_of_monthly_transactions,
postCounterpartyLimitV510.max_yearly_amount,
postCounterpartyLimitV510.max_number_of_yearly_transactions,
cc.callContext
)
} yield {
(counterpartyLimit.toJValue, HttpCode.`200`(cc.callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getCounterpartyLimit,
implementedInApiVersion,
nameOf(getCounterpartyLimit),
"GET",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits",
"Get Counterparty Limit",
s"""Get Counterparty Limit.""",
EmptyBody,
counterpartyLimitV510,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
$CounterpartyNotFoundByCounterpartyId,
InvalidJsonFormat,
UnknownError
),
List(apiTagCounterpartyLimits),
)
lazy val getCounterpartyLimit: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonGet _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(counterpartyLimit, callContext) <- NewStyle.function.getCounterpartyLimit(
bankId.value,
accountId.value,
viewId.value,
counterpartyId.value,
cc.callContext
)
} yield {
(counterpartyLimit.toJValue, HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
deleteCounterpartyLimit,
implementedInApiVersion,
nameOf(deleteCounterpartyLimit),
"DELETE",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/counterparties/COUNTERPARTY_ID/limits",
"Delete Counterparty Limit",
s"""Delete Counterparty Limit.""",
EmptyBody,
EmptyBody,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
$CounterpartyNotFoundByCounterpartyId,
InvalidJsonFormat,
UnknownError
),
List(apiTagCounterpartyLimits),
)
lazy val deleteCounterpartyLimit: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"counterparties" :: CounterpartyId(counterpartyId) ::"limits" :: Nil JsonDelete _ => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(counterpartyLimit, callContext)<- NewStyle.function.deleteCounterpartyLimit(
bankId.value,
accountId.value,
viewId.value,
counterpartyId.value,
cc.callContext
)
} yield {
(Full(counterpartyLimit), HttpCode.`204`(cc.callContext))
}
}
}
resourceDocs += ResourceDoc(
createCustomView,
implementedInApiVersion,
nameOf(createCustomView),
"POST",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views",
"Create Custom View",
s"""Create a custom view on bank account
|
| ${authenticationRequiredMessage(true)} and the user needs to have access to the owner view.
| The 'alias' field in the JSON can take one of three values:
|
| * _public_: to use the public alias if there is one specified for the other account.
| * _private_: to use the private alias if there is one specified for the other account.
|
| * _''(empty string)_: to use no alias; the view shows the real name of the other account.
|
| The 'hide_metadata_if_alias_used' field in the JSON can take boolean values. If it is set to `true` and there is an alias on the other account then the other accounts' metadata (like more_info, url, image_url, open_corporates_url, etc.) will be hidden. Otherwise the metadata will be shown.
|
| The 'allowed_actions' field is a list containing the name of the actions allowed on this view, all the actions contained will be set to `true` on the view creation, the rest will be set to `false`.
|
| You MUST use a leading _ (underscore) in the view name because other view names are reserved for OBP [system views](/index#group-View-System).
| """,
createCustomViewJson,
customViewJsonV510,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
InvalidJsonFormat,
UnknownError
),
List(apiTagView, apiTagAccount)
)
lazy val createCustomView: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) ::"target-views" :: Nil JsonPost json -> _ => {
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
(_, _, _, view, callContext) <- SS.userBankAccountView
createCustomViewJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[CreateViewJson]}", 400, cc.callContext) {
json.extract[CreateCustomViewJson]
}
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
_ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current view_name (${createCustomViewJson.name})", cc = callContext) {
isValidCustomViewName(createCustomViewJson.name)
}
permissionsFromSource = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition])
permissionsFromTarget = createCustomViewJson.allowed_permissions
_ <- Helper.booleanToFuture(failMsg = SourceViewHasLessPermission + s"Current source viewId($viewId) permissions ($permissionsFromSource), target viewName${createCustomViewJson.name} permissions ($permissionsFromTarget)", cc = callContext) {
permissionsFromTarget.toSet.subsetOf(permissionsFromSource)
}
failMsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canCreateCustomView))}` permission on VIEW_ID(${viewId.value})"
_ <- Helper.booleanToFuture(failMsg, cc = callContext) {
view.canCreateCustomView
}
(view, callContext) <- NewStyle.function.createCustomView(BankIdAccountId(bankId, accountId), createCustomViewJson.toCreateViewJson, callContext)
} yield {
(JSONFactory510.createViewJson(view), HttpCode.`201`(callContext))
}
}
}
resourceDocs += ResourceDoc(
updateCustomView,
implementedInApiVersion,
nameOf(updateCustomView),
"PUT",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views/TARGET_VIEW_ID",
"Update Custom View",
s"""Update an existing custom view on a bank account
|
|${authenticationRequiredMessage(true)} and the user needs to have access to the owner view.
|
|The json sent is the same as during view creation (above), with one difference: the 'name' field
|of a view is not editable (it is only set when a view is created)""",
updateCustomViewJson,
customViewJsonV510,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
InvalidJsonFormat,
UnknownError
),
List(apiTagView, apiTagAccount)
)
lazy val updateCustomView: OBPEndpoint = {
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: "target-views" :: ViewId(targetViewId) :: Nil JsonPut json -> _ => {
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
(_, _, _, view, callContext) <- SS.userBankAccountView
targetCreateCustomViewJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should be the ${classOf[UpdateCustomViewJson]}", 400, cc.callContext) {
json.extract[UpdateCustomViewJson]
}
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
_ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current TARGET_VIEW_ID (${targetViewId})", cc = callContext) {
isValidCustomViewId(targetViewId.value)
}
permissionsFromSource = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition])
permissionsFromTarget = targetCreateCustomViewJson.allowed_permissions
_ <- Helper.booleanToFuture(failMsg = SourceViewHasLessPermission + s"Current source view permissions ($permissionsFromSource), target view permissions ($permissionsFromTarget)", cc = callContext) {
permissionsFromTarget.toSet.subsetOf(permissionsFromSource)
}
failmsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canUpdateCustomView))}` permission on VIEW_ID(${viewId.value})"
_ <- Helper.booleanToFuture(failmsg, cc = callContext) {
view.canCreateCustomView
}
(view, callContext) <- NewStyle.function.updateCustomView(BankIdAccountId(bankId, accountId), targetViewId, targetCreateCustomViewJson.toUpdateViewJson, callContext)
} yield {
(JSONFactory510.createViewJson(view), HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
getCustomView,
implementedInApiVersion,
nameOf(getCustomView),
"GET",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views/TARGET_VIEW_ID",
"Get Custom View",
s"""#Views
|
|
|Views in Open Bank Project provide a mechanism for fine grained access control and delegation to Accounts and Transactions. Account holders use the 'owner' view by default. Delegated access is made through other views for example 'accountants', 'share-holders' or 'tagging-application'. Views can be created via the API and each view has a list of entitlements.
|
|Views on accounts and transactions filter the underlying data to redact certain fields for certain users. For instance the balance on an account may be hidden from the public. The way to know what is possible on a view is determined in the following JSON.
|
|**Data:** When a view moderates a set of data, some fields my contain the value `null` rather than the original value. This indicates either that the user is not allowed to see the original data or the field is empty.
|
|There is currently one exception to this rule; the 'holder' field in the JSON contains always a value which is either an alias or the real name - indicated by the 'is_alias' field.
|
|**Action:** When a user performs an action like trying to post a comment (with POST API call), if he is not allowed, the body response will contain an error message.
|
|**Metadata:**
|Transaction metadata (like images, tags, comments, etc.) will appears *ONLY* on the view where they have been created e.g. comments posted to the public view only appear on the public view.
|
|The other account metadata fields (like image_URL, more_info, etc.) are unique through all the views. Example, if a user edits the 'more_info' field in the 'team' view, then the view 'authorities' will show the new value (if it is allowed to do it).
|
|# All
|*Optional*
|
|Returns the list of the views created for account ACCOUNT_ID at BANK_ID.
|
|${authenticationRequiredMessage(true)} and the user needs to have access to the owner view.""",
EmptyBody,
customViewJsonV510,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
UnknownError
),
List(apiTagView, apiTagAccount)
)
lazy val getCustomView: OBPEndpoint = {
//get the available views on an bank account
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: "target-views" :: ViewId(targetViewId):: Nil JsonGet req => {
cc =>
implicit val ec = EndpointContext(Some(cc))
for {
(_, _, _, view, callContext) <- SS.userBankAccountView
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
_ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current TARGET_VIEW_ID (${targetViewId.value})", cc = callContext) {
isValidCustomViewId(targetViewId.value)
}
failmsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canSeeAvailableViewsForBankAccount))}`permission on any your views. Current VIEW_ID (${viewId.value})"
_ <- Helper.booleanToFuture(failmsg, cc = callContext) {
view.canSeeAvailableViewsForBankAccount
}
targetView <- NewStyle.function.customView(targetViewId, BankIdAccountId(bankId, accountId), callContext)
} yield {
(JSONFactory510.createViewJson(targetView), HttpCode.`200`(callContext))
}
}
}
staticResourceDocs += ResourceDoc(
deleteCustomView,
implementedInApiVersion,
nameOf(deleteCustomView),
"DELETE",
"/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views/TARGET_VIEW_ID",
"Delete Custom View",
"Deletes the custom view specified by VIEW_ID on the bank account specified by ACCOUNT_ID at bank BANK_ID",
EmptyBody,
EmptyBody,
List(
$UserNotLoggedIn,
$BankNotFound,
$BankAccountNotFound,
$UserNoPermissionAccessView,
UnknownError
),
List(apiTagView, apiTagAccount)
)
lazy val deleteCustomView: OBPEndpoint = {
//deletes a view on an bank account
case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId ) :: "views" :: ViewId(viewId) :: "target-views" :: ViewId(targetViewId) :: Nil JsonDelete req => {
cc => implicit val ec = EndpointContext(Some(cc))
for {
(_, _, _, view, callContext) <- SS.userBankAccountView
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
_ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat + s"Current TARGET_VIEW_ID (${targetViewId.value})", cc = callContext) {
isValidCustomViewId(targetViewId.value)
}
failMsg = s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(view.canDeleteCustomView))}` permission on any your views.Current VIEW_ID (${viewId.value})"
_ <- Helper.booleanToFuture(failMsg, cc = callContext) {
view.canDeleteCustomView
}
_ <- NewStyle.function.customView(targetViewId, BankIdAccountId(bankId, accountId), callContext)
deleted <- NewStyle.function.removeCustomView(targetViewId, BankIdAccountId(bankId, accountId), callContext)
} yield {
(Full(deleted), HttpCode.`204`(callContext))
}
}
}
}
}
object APIMethods510 extends RestHelper with APIMethods510 {
lazy val newStyleEndpoints: List[(String, String)] = Implementations5_1_0.resourceDocs.map {
rd => (rd.partialFunctionName, rd.implementedInApiVersion.toString())

View File

@ -28,17 +28,17 @@ package code.api.v5_1_0
import code.api.Constant
import code.api.util.{APIUtil, ConsentJWT, CustomJsonFormats, JwtUtil, Role}
import code.api.util.APIUtil.gitCommit
import code.api.util.APIUtil.{gitCommit, stringOrNull}
import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140}
import code.api.v2_1_0.ResourceUserJSON
import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300}
import code.api.v3_0_0.{AccountIdJson, AccountsIdsJsonV300, AddressJsonV300, OpeningTimesV300}
import code.api.v3_0_0.{AccountIdJson, AccountsIdsJsonV300, AddressJsonV300, OpeningTimesV300, ViewJsonV300}
import code.api.v4_0_0.{EnergySource400, HostedAt400, HostedBy400, PostViewJsonV400}
import code.atmattribute.AtmAttribute
import code.atms.Atms.Atm
import code.users.{UserAttribute, Users}
import code.views.system.{AccountAccess, ViewDefinition}
import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, BankIdAccountId, Customer, Location, Meta, RegulatedEntityTrait}
import com.openbankproject.commons.model.{Address, AtmId, AtmT, BankId, BankIdAccountId, CreateViewJson, Customer, Location, Meta, RegulatedEntityTrait, UpdateViewJSON, View}
import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion}
import java.util.Date
@ -160,6 +160,27 @@ case class PostAtmJsonV510 (
phone: String
)
case class PostCounterpartyLimitV510(
max_single_amount: Int,
max_monthly_amount: Int,
max_number_of_monthly_transactions: Int,
max_yearly_amount: Int,
max_number_of_yearly_transactions: Int
)
case class CounterpartyLimitV510(
counterparty_limit_id: String,
bank_id: String,
account_id: String,
view_id: String,
counterparty_id: String,
max_single_amount: Int,
max_monthly_amount: Int,
max_number_of_monthly_transactions: Int,
max_yearly_amount: Int,
max_number_of_yearly_transactions: Int
)
case class AtmJsonV510 (
id : Option[String],
bank_id : String,
@ -311,8 +332,77 @@ case class PostCreateUserAccountAccessJsonV510(username: String, provider:String
case class PostAccountAccessJsonV510(user_id: String, view_id: String)
case class CreateCustomViewJson(
name: String,
description: String,
metadata_view: String,
is_public: Boolean,
which_alias_to_use: String,
hide_metadata_if_alias_used: Boolean,
allowed_permissions : List[String],
) {
def toCreateViewJson = CreateViewJson(
name: String,
description: String,
metadata_view: String,
is_public: Boolean,
which_alias_to_use: String,
hide_metadata_if_alias_used: Boolean,
allowed_actions = allowed_permissions,
)
}
case class UpdateCustomViewJson(
description: String,
metadata_view: String,
is_public: Boolean,
which_alias_to_use: String,
hide_metadata_if_alias_used: Boolean,
allowed_permissions: List[String]
) {
def toUpdateViewJson = UpdateViewJSON(
description: String,
metadata_view: String,
is_public: Boolean,
is_firehose= None,
which_alias_to_use: String,
hide_metadata_if_alias_used: Boolean,
allowed_actions = allowed_permissions
)
}
case class CustomViewJsonV510(
id: String,
name: String,
description: String,
metadata_view: String,
is_public: Boolean,
alias: String,
hide_metadata_if_alias_used: Boolean,
allowed_permissions: List[String]
)
object JSONFactory510 extends CustomJsonFormats {
def createViewJson(view: View): CustomViewJsonV510 = {
val alias =
if (view.usePublicAliasIfOneExists)
"public"
else if (view.usePrivateAliasIfOneExists)
"private"
else
""
CustomViewJsonV510(
id = view.viewId.value,
name = stringOrNull(view.name),
description = stringOrNull(view.description),
metadata_view = view.metadataView,
is_public = view.isPublic,
alias = alias,
hide_metadata_if_alias_used = view.hideOtherAccountMetadataIfAlias,
allowed_permissions = APIUtil.getViewPermissions(view.asInstanceOf[ViewDefinition]).toList
)
}
def createCustomersIds(customers : List[Customer]): CustomersIdsJsonV510 =
CustomersIdsJsonV510(customers.map(x => CustomerIdJson(x.customerId)))

View File

@ -23,6 +23,7 @@ import code.bankconnectors.rest.RestConnector_vMar2019
import code.bankconnectors.storedprocedure.StoredProcedureConnector_vDec2019
import code.bankconnectors.vMay2019.KafkaMappedConnector_vMay2019
import code.bankconnectors.vSept2018.KafkaMappedConnector_vSept2018
import code.counterpartylimit.{CounterpartyLimit, CounterpartyLimitTrait}
import code.customeraccountlinks.CustomerAccountLinkTrait
import code.endpointTag.EndpointTagT
import code.fx.fx.TTL
@ -78,25 +79,27 @@ Could consider a Map of ("resourceType" -> "provider") - this could tell us whic
*/
object Connector extends SimpleInjector {
val nameToConnector: Map[String, () => Connector] = Map(
"mapped" -> lazyValue(LocalMappedConnector),
"akka_vDec2018" -> lazyValue(AkkaConnector_vDec2018),
"kafka_vSept2018" -> lazyValue(KafkaMappedConnector_vSept2018),
"kafka_vMay2019" -> lazyValue(KafkaMappedConnector_vMay2019),
"rest_vMar2019" -> lazyValue(RestConnector_vMar2019),
"stored_procedure_vDec2019" -> lazyValue(StoredProcedureConnector_vDec2019),
// An object is a class that has exactly one instance. It is created lazily when it is referenced, like a lazy val.
// As a top-level value, an object is a singleton.
// As a member of an enclosing class or as a local value, it behaves exactly like a lazy val.
// Previously the right hand part was surrounded by Functions.lazyValue function
val nameToConnector: Map[String, Connector] = Map(
"mapped" -> LocalMappedConnector,
"akka_vDec2018" -> AkkaConnector_vDec2018,
"kafka_vSept2018" -> KafkaMappedConnector_vSept2018,
"kafka_vMay2019" -> KafkaMappedConnector_vMay2019,
"rest_vMar2019" -> RestConnector_vMar2019,
"stored_procedure_vDec2019" -> StoredProcedureConnector_vDec2019,
// this proxy connector only for unit test, can set connector=proxy in test.default.props, but never set it in default.props
"proxy" -> lazyValue(ConnectorUtils.proxyConnector),
"internal" -> lazyValue(InternalConnector.instance)
"proxy" -> ConnectorUtils.proxyConnector,
"internal" -> InternalConnector.instance
)
def getConnectorInstance(connectorVersion: String): Connector = {
connectorVersion match {
case "star" => StarConnector
case k => nameToConnector.get(k)
.map(f => f())
.getOrElse(throw new RuntimeException(s"Do not Support this connector version: $k"))
.getOrElse(throw new RuntimeException(s"$InvalidConnector Current Input is $k"))
}
}
@ -2662,5 +2665,34 @@ trait Connector extends MdcLoggable {
def updateCustomerAccountLinkById(customerAccountLinkId: String, relationshipType: String, callContext: Option[CallContext]): OBPReturnType[Box[CustomerAccountLinkTrait]] = Future{(Failure(setUnimplementedError), callContext)}
def getConsentImplicitSCA(user: User, callContext: Option[CallContext]): OBPReturnType[Box[ConsentImplicitSCAT]] = Future{(Failure(setUnimplementedError), callContext)}
def createOrUpdateCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
maxSingleAmount: Int,
maxMonthlyAmount: Int,
maxNumberOfMonthlyTransactions: Int,
maxYearlyAmount: Int,
maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext]
): OBPReturnType[Box[CounterpartyLimitTrait]] = Future{(Failure(setUnimplementedError), callContext)}
def getCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
): OBPReturnType[Box[CounterpartyLimitTrait]] = Future{(Failure(setUnimplementedError), callContext)}
def deleteCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
): OBPReturnType[Box[Boolean]] = Future{(Failure(setUnimplementedError), callContext)}
}

View File

@ -30,6 +30,7 @@ import code.branches.MappedBranch
import code.cardattribute.CardAttributeX
import code.cards.MappedPhysicalCard
import code.context.{UserAuthContextProvider, UserAuthContextUpdateProvider}
import code.counterpartylimit.{CounterpartyLimit, CounterpartyLimitProvider, CounterpartyLimitTrait}
import code.customer._
import code.customeraccountlinks.CustomerAccountLinkTrait
import code.customeraddress.CustomerAddressX
@ -5911,5 +5912,58 @@ object LocalMappedConnector extends Connector with MdcLoggable {
)), callContext)
}
override def createOrUpdateCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
maxSingleAmount: Int,
maxMonthlyAmount: Int,
maxNumberOfMonthlyTransactions: Int,
maxYearlyAmount: Int,
maxNumberOfYearlyTransactions: Int, callContext: Option[CallContext]) =
CounterpartyLimitProvider.counterpartyLimit.vend.createOrUpdateCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
maxSingleAmount: Int,
maxMonthlyAmount: Int,
maxNumberOfMonthlyTransactions: Int,
maxYearlyAmount: Int,
maxNumberOfYearlyTransactions: Int) map {
(_, callContext)
}
override def getCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
) =
CounterpartyLimitProvider.counterpartyLimit.vend.getCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String
) map {
(_, callContext)
}
override def deleteCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
callContext: Option[CallContext]
): OBPReturnType[Box[Boolean]] =
CounterpartyLimitProvider.counterpartyLimit.vend.deleteCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String) map {
(_, callContext)
}
}

View File

@ -21,7 +21,7 @@ trait ConsentProvider {
def updateConsentStatus(consentId: String, status: ConsentStatus): Box[MappedConsent]
def updateConsentUser(consentId: String, user: User): Box[MappedConsent]
def getConsentsByUser(userId: String): List[MappedConsent]
def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String]): Box[MappedConsent]
def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String], consumer: Option[Consumer] = None): Box[MappedConsent]
def setJsonWebToken(consentId: String, jwt: String): Box[MappedConsent]
def revoke(consentId: String): Box[MappedConsent]
def checkAnswer(consentId: String, challenge: String): Box[MappedConsent]

View File

@ -62,13 +62,14 @@ object MappedConsentProvider extends ConsentProvider {
override def getConsentsByUser(userId: String): List[MappedConsent] = {
MappedConsent.findAll(By(MappedConsent.mUserId, userId))
}
override def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String]): Box[MappedConsent] = {
override def createObpConsent(user: User, challengeAnswer: String, consentRequestId:Option[String], consumer: Option[Consumer]): Box[MappedConsent] = {
tryo {
val salt = BCrypt.gensalt()
val challengeAnswerHashed = BCrypt.hashpw(challengeAnswer, salt).substring(0, 44)
MappedConsent
.create
.mUserId(user.userId)
.mConsumerId(consumer.map(_.consumerId.get).getOrElse(null))
.mConsentRequestId(consentRequestId.getOrElse(null))
.mChallenge(challengeAnswerHashed)
.mSalt(salt)

View File

@ -28,6 +28,7 @@ trait ConsumersProvider {
def getConsumerByPrimaryId(id: Long): Box[Consumer]
def getConsumerByConsumerKey(consumerKey: String): Box[Consumer]
def getConsumerByConsumerKeyFuture(consumerKey: String): Future[Box[Consumer]]
def getConsumerByPemCertificate(pem: String): Box[Consumer]
def getConsumerByConsumerId(consumerId: String): Box[Consumer]
def getConsumerByConsumerIdFuture(consumerId: String): Future[Box[Consumer]]
def getConsumersByUserIdFuture(userId: String): Future[List[Consumer]]
@ -61,6 +62,7 @@ class RemotedataConsumersCaseClasses {
case class getConsumerByPrimaryId(id: Long)
case class getConsumerByConsumerKey(consumerKey: String)
case class getConsumerByConsumerKeyFuture(consumerKey: String)
case class getConsumerByPemCertificate(pem: String)
case class getConsumerByConsumerId(consumerId: String)
case class getConsumerByConsumerIdFuture(consumerId: String)
case class getConsumersByUserIdFuture(userId: String)

View File

@ -0,0 +1,57 @@
package code.counterpartylimit
import code.api.util.APIUtil
import com.openbankproject.commons.util.JsonAble
import net.liftweb.util.SimpleInjector
import net.liftweb.common.Box
import scala.concurrent.Future
object CounterpartyLimitProvider extends SimpleInjector {
val counterpartyLimit = new Inject(buildOne _) {}
def buildOne: CounterpartyLimitProviderTrait = APIUtil.getPropsAsBoolValue("use_akka", false) match {
case _ => MappedCounterpartyLimitProvider
// case true => RemotedataCounterpartyLimit // we are getting rid of the akka now. so do not implement it here
}
}
trait CounterpartyLimitProviderTrait {
def getCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String
): Future[Box[CounterpartyLimitTrait]]
def deleteCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String
): Future[Box[Boolean]]
def createOrUpdateCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
maxSingleAmount: Int,
maxMonthlyAmount: Int,
maxNumberOfMonthlyTransactions: Int,
maxYearlyAmount: Int,
maxNumberOfYearlyTransactions: Int): Future[Box[CounterpartyLimitTrait]]
}
trait CounterpartyLimitTrait extends JsonAble{
def counterpartyLimitId: String
def bankId: String
def accountId: String
def viewId: String
def counterpartyId: String
def maxSingleAmount: Int
def maxMonthlyAmount: Int
def maxNumberOfMonthlyTransactions: Int
def maxYearlyAmount: Int
def maxNumberOfYearlyTransactions: Int
}

View File

@ -0,0 +1,149 @@
package code.counterpartylimit
import code.util.MappedUUID
import net.liftweb.common.{Box, Full}
import net.liftweb.mapper._
import net.liftweb.util.Helpers.tryo
import com.openbankproject.commons.ExecutionContext.Implicits.global
import net.liftweb.json
import net.liftweb.json.Formats
import net.liftweb.json.JsonAST.{JValue,JString}
import net.liftweb.json.JsonDSL._
import scala.concurrent.Future
object MappedCounterpartyLimitProvider extends CounterpartyLimitProviderTrait {
def getCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String
): Future[Box[CounterpartyLimitTrait]] = Future {
CounterpartyLimit.find(
By(CounterpartyLimit.BankId, bankId),
By(CounterpartyLimit.AccountId, accountId),
By(CounterpartyLimit.ViewId, viewId),
By(CounterpartyLimit.CounterpartyId, counterpartyId)
)
}
def deleteCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String
): Future[Box[Boolean]] = Future {
CounterpartyLimit.find(
By(CounterpartyLimit.BankId, bankId),
By(CounterpartyLimit.AccountId, accountId),
By(CounterpartyLimit.ViewId, viewId),
By(CounterpartyLimit.CounterpartyId, counterpartyId)
).map(_.delete_!)
}
def createOrUpdateCounterpartyLimit(
bankId: String,
accountId: String,
viewId: String,
counterpartyId: String,
maxSingleAmount: Int,
maxMonthlyAmount: Int,
maxNumberOfMonthlyTransactions: Int,
maxYearlyAmount: Int,
maxNumberOfYearlyTransactions: Int)= Future {
def createCounterpartyLimit(counterpartyLimit: CounterpartyLimit)= {
tryo {
counterpartyLimit.BankId(bankId)
counterpartyLimit.AccountId(accountId)
counterpartyLimit.ViewId(viewId)
counterpartyLimit.CounterpartyId(counterpartyId)
counterpartyLimit.MaxSingleAmount(maxSingleAmount)
counterpartyLimit.MaxMonthlyAmount(maxMonthlyAmount)
counterpartyLimit.MaxNumberOfMonthlyTransactions(maxNumberOfMonthlyTransactions)
counterpartyLimit.MaxYearlyAmount(maxYearlyAmount)
counterpartyLimit.MaxNumberOfYearlyTransactions(maxNumberOfYearlyTransactions)
counterpartyLimit.saveMe()
}
}
def getCounterpartyLimit = CounterpartyLimit.find(
By(CounterpartyLimit.BankId, bankId),
By(CounterpartyLimit.AccountId, accountId),
By(CounterpartyLimit.ViewId, viewId),
By(CounterpartyLimit.CounterpartyId, counterpartyId),
)
val result = getCounterpartyLimit match {
case Full(counterpartyLimit) => createCounterpartyLimit(counterpartyLimit)
case _ => createCounterpartyLimit(CounterpartyLimit.create)
}
result
}
}
class CounterpartyLimit extends CounterpartyLimitTrait with LongKeyedMapper[CounterpartyLimit] with IdPK with CreatedUpdated {
override def getSingleton = CounterpartyLimit
object CounterpartyLimitId extends MappedUUID(this)
object BankId extends MappedString(this, 255){
override def dbNotNull_? = true
}
object AccountId extends MappedString(this, 255){
override def dbNotNull_? = true
}
object ViewId extends MappedString(this, 255){
override def dbNotNull_? = true
}
object CounterpartyId extends MappedString(this, 255){
override def dbNotNull_? = true
}
object MaxSingleAmount extends MappedInt(this) {
override def defaultValue = -1
}
object MaxMonthlyAmount extends MappedInt(this) {
override def defaultValue = -1
}
object MaxNumberOfMonthlyTransactions extends MappedInt(this) {
override def defaultValue = -1
}
object MaxYearlyAmount extends MappedInt(this) {
override def defaultValue = -1
}
object MaxNumberOfYearlyTransactions extends MappedInt(this) {
override def defaultValue = -1
}
def counterpartyLimitId: String = CounterpartyLimitId.get
def bankId: String = BankId.get
def accountId: String = AccountId.get
def viewId: String = ViewId.get
def counterpartyId: String = CounterpartyId.get
def maxSingleAmount: Int = MaxSingleAmount.get
def maxMonthlyAmount: Int = MaxMonthlyAmount.get
def maxNumberOfMonthlyTransactions: Int = MaxNumberOfMonthlyTransactions.get
def maxYearlyAmount: Int = MaxYearlyAmount.get
def maxNumberOfYearlyTransactions: Int = MaxNumberOfYearlyTransactions.get
override def toJValue(implicit format: Formats): JValue ={
("counterparty_limit_id", counterpartyLimitId) ~
("bank_id", bankId) ~
("account_id",accountId) ~
("view_id",viewId) ~
("counterparty_id",counterpartyId) ~
("max_single_amount", maxSingleAmount) ~
("max_monthly_amount", maxMonthlyAmount) ~
("max_number_of_monthly_transactions", maxNumberOfMonthlyTransactions) ~
("max_yearly_amount", maxYearlyAmount) ~
("max_number_of_yearly_transactions", maxNumberOfYearlyTransactions)
}
}
object CounterpartyLimit extends CounterpartyLimit with LongKeyedMetaMapper[CounterpartyLimit] {
override def dbIndexes = UniqueIndex(CounterpartyLimitId) :: UniqueIndex(BankId, AccountId, ViewId, CounterpartyId) :: super.dbIndexes
}

View File

@ -103,6 +103,10 @@ object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
}
}
def getConsumerByPemCertificate(pem: String): Box[Consumer] = {
Consumer.find(By(Consumer.clientCertificate, pem))
}
def getConsumerByConsumerId(consumerId: String): Box[Consumer] = {
Consumer.find(By(Consumer.consumerId, consumerId))
}

View File

@ -1038,14 +1038,34 @@ def restoreSomeSessions(): Unit = {
override def login: NodeSeq = {
// This query parameter is specific to ORY Hydra login request
val loginChallenge: Box[String] = ObpS.param("login_challenge").or(S.getSessionAttribute("login_challenge"))
def redirectUri(): String = {
loginRedirect.get match {
case Full(url) =>
loginRedirect(Empty)
url
case _ =>
homePage
def redirectUri(user: Box[ResourceUser]): String = {
val userId = user.map(_.userId).getOrElse("")
val hashedAgreementTextOfUser =
UserAgreementProvider.userAgreementProvider.vend.getUserAgreement(userId, "terms_and_conditions")
.map(_.agreementHash).getOrElse(HashUtil.Sha256Hash("not set"))
val agreementText = getWebUiPropsValue("webui_terms_and_conditions", "not set")
val hashedAgreementText = HashUtil.Sha256Hash(agreementText)
if(hashedAgreementTextOfUser == hashedAgreementText) { // Chech terms and conditions
val hashedAgreementTextOfUser =
UserAgreementProvider.userAgreementProvider.vend.getUserAgreement(userId, "privacy_conditions")
.map(_.agreementHash).getOrElse(HashUtil.Sha256Hash("not set"))
val agreementText = getWebUiPropsValue("webui_privacy_policy", "not set")
val hashedAgreementText = HashUtil.Sha256Hash(agreementText)
if(hashedAgreementTextOfUser == hashedAgreementText) { // Check privacy policy
loginRedirect.get match {
case Full(url) =>
loginRedirect(Empty)
url
case _ =>
homePage
}
} else {
"/privacy-policy"
}
} else {
"/terms-and-conditions"
}
}
//Check the internal redirect, in case for open redirect issue.
// variable redirect is from loginRedirect, it is set-up in OAuthAuthorisation.scala as following code:
@ -1131,7 +1151,7 @@ def restoreSomeSessions(): Unit = {
// User init actions
AfterApiAuth.innerLoginUserInitAction(Full(user))
logger.info("login redirect: " + loginRedirect.get)
val redirect = redirectUri()
val redirect = redirectUri(user.user.foreign)
checkInternalRedirectAndLogUserIn(preLoginState, redirect, user)
} else { // If user is NOT locked AND password is wrong => increment bad login attempt counter.
LoginAttempt.incrementBadLoginAttempts(user.getProvider(),usernameFromGui)
@ -1151,7 +1171,7 @@ def restoreSomeSessions(): Unit = {
LoginAttempt.resetBadLoginAttempts(user.getProvider(), usernameFromGui)
val preLoginState = capturePreLoginState()
logger.info("login redirect: " + loginRedirect.get)
val redirect = redirectUri()
val redirect = redirectUri(user.user.foreign)
//This method is used for connector = kafka* || obpjvm*
//It will update the views and createAccountHolder ....
registeredUserHelper(user.getProvider(),user.username.get)
@ -1176,7 +1196,7 @@ def restoreSomeSessions(): Unit = {
val preLoginState = capturePreLoginState()
logger.info("login redirect: " + loginRedirect.get)
val redirect = redirectUri()
val redirect = redirectUri(user.foreign)
externalUserHelper(usernameFromGui, passwordFromGui) match {
case Full(user: AuthUser) =>
LoginAttempt.resetBadLoginAttempts(user.getProvider(), usernameFromGui)

View File

@ -20,6 +20,9 @@ object RemotedataConsumers extends ObpActorInit with ConsumersProvider {
def getConsumerByPrimaryId(id: Long): Box[Consumer] = getValueFromFuture(
(actor ? cc.getConsumerByPrimaryId(id)).mapTo[Box[Consumer]]
)
def getConsumerByPemCertificate(pem: String): Box[Consumer] = getValueFromFuture(
(actor ? cc.getConsumerByPemCertificate(pem)).mapTo[Box[Consumer]]
)
def getConsumerByConsumerId(consumerId: String): Box[Consumer] = getValueFromFuture(
(actor ? cc.getConsumerByConsumerId(consumerId)).mapTo[Box[Consumer]]
)

View File

@ -119,6 +119,9 @@ object RemotedataViews extends ObpActorInit with Views {
def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess]) = getValueFromFuture(
(actor ? cc.privateViewsUserCanAccessAtBank(user, bankId)).mapTo[(List[View], List[AccountAccess])]
)
def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): ((List[View], List[AccountAccess])) = getValueFromFuture(
(actor ? cc.privateViewsUserCanAccessAtBankThroughView(user, bankId, viewId)).mapTo[(List[View], List[AccountAccess])]
)
def privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId): List[View] = getValueFromFuture(
(actor ? cc.privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId)).mapTo[List[View]]

View File

@ -0,0 +1,77 @@
/**
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.model.dataAccess.AuthUser
import code.users.UserAgreementProvider
import code.util.Helper
import code.util.Helper.MdcLoggable
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
import net.liftweb.http.{S, SHtml}
import net.liftweb.util.CssSel
import net.liftweb.util.Helpers._
class PrivacyPolicy extends MdcLoggable {
def updateForm: CssSel = {
def submitButtonDefense(): Unit = {
updateUserAgreement()
}
def skipButtonDefense(): Unit = {
S.redirectTo("/")
}
def displayContent = {
if(AuthUser.currentUser.isDefined) {
"block"
} else {
"none"
}
}
def update = {
val username = AuthUser.currentUser.flatMap(_.user.foreign.map(_.name)).getOrElse("")
"#privacy-policy-username *" #> username &
"type=submit" #> SHtml.submit(s"${Helper.i18n("outdated.terms.button.accept")}", () => submitButtonDefense) &
"type=reset" #> SHtml.submit(s"${Helper.i18n("outdated.terms.button.skip")}", () => skipButtonDefense) &
"#form_privacy_policy [style]" #> s"display: $displayContent;"
}
update
}
private def updateUserAgreement() = {
if(AuthUser.currentUser.isDefined) {
val agreementText = getWebUiPropsValue("webui_privacy_policy", "not set")
UserAgreementProvider.userAgreementProvider.vend.createOrUpdateUserAgreement(
AuthUser.currentUser.flatMap(_.user.foreign.map(_.userId)).getOrElse(""), "privacy_conditions", agreementText)
S.redirectTo("/")
}
}
}

View File

@ -0,0 +1,78 @@
/**
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.model.dataAccess.AuthUser
import code.users.UserAgreementProvider
import code.util.Helper
import code.util.Helper.MdcLoggable
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
import net.liftweb.http.{S, SHtml}
import net.liftweb.util.CssSel
import net.liftweb.util.Helpers._
class TermsAndConditions extends MdcLoggable {
def updateForm: CssSel = {
def submitButtonDefense(): Unit = {
updateUserAgreement()
}
def skipButtonDefense(): Unit = {
S.redirectTo("/")
}
def displayContent = {
if(AuthUser.currentUser.isDefined) {
"block"
} else {
"none"
}
}
def update = {
val username = AuthUser.currentUser.flatMap(_.user.foreign.map(_.name)).getOrElse("")
"#terms-and-conditions-username *" #> username &
"type=submit" #> SHtml.submit(s"${Helper.i18n("outdated.policy.button.accept")}", () => submitButtonDefense) &
"type=reset" #> SHtml.submit(s"${Helper.i18n("outdated.policy.button.skip")}", () => skipButtonDefense) &
"#form_terms_and_conditions [style]" #> s"display: $displayContent;"
}
update
}
private def updateUserAgreement() = {
if(AuthUser.currentUser.isDefined) {
val agreementText = getWebUiPropsValue("webui_terms_and_conditions", "not set")
// val hashedAgreementText = HashUtil.Sha256Hash(agreementText)
UserAgreementProvider.userAgreementProvider.vend.createOrUpdateUserAgreement(
AuthUser.currentUser.flatMap(_.user.foreign.map(_.userId)).getOrElse(""), "terms_and_conditions", agreementText)
S.redirectTo("/")
}
}
}

View File

@ -328,12 +328,7 @@ class WebUI extends MdcLoggable{
def sandboxIntroductionLink: CssSel = {
val webUiApiDocumentation = getWebUiPropsValue("webui_api_documentation_url",s"${getServerUrl}/introduction")
val apiDocumentation =
if (webUiApiDocumentation == s"${getServerUrl}/introduction")
webUiApiDocumentation
else
webUiApiDocumentation + "#Sandbox-Introduction"
"#sandbox-introduction-link [href]" #> scala.xml.Unparsed(apiDocumentation)
"#sandbox-introduction-link [href]" #> scala.xml.Unparsed(webUiApiDocumentation)
}
def subscriptionsButton: CssSel = {

View File

@ -7,6 +7,7 @@ import code.api.util.HashUtil
import code.util.UUIDString
import net.liftweb.common.{Box, Empty, Full}
import net.liftweb.mapper._
import net.liftweb.common.Box.tryo
object MappedUserAgreementProvider extends UserAgreementProvider {
override def createOrUpdateUserAgreement(userId: String, agreementType: String, agreementText: String): Box[UserAgreement] = {
@ -75,5 +76,13 @@ class UserAgreement extends UserAgreementTrait with LongKeyedMapper[UserAgreemen
object UserAgreement extends UserAgreement with LongKeyedMetaMapper[UserAgreement] {
override def dbIndexes: List[BaseIndex[UserAgreement]] = UniqueIndex(UserAgreementId) :: super.dbIndexes
override def beforeSave = List(
agreement =>
tryo {
val hash = HashUtil.Sha256Hash(agreement.agreementText)
agreement.AgreementHash(hash ).save
}
)
}

View File

@ -211,7 +211,12 @@ object Helper extends Loggable {
*/
def isValidInternalRedirectUrl(url: String) : Boolean = {
//set the default value is "/" and "/oauth/authorize"
val validUrls = List("/","/oauth/authorize","/consumer-registration","/dummy-user-tokens","/create-sandbox-account", "/add-user-auth-context-update-request","/otp")
val validUrls = List(
"/","/oauth/authorize","/consumer-registration",
"/dummy-user-tokens","/create-sandbox-account",
"/add-user-auth-context-update-request","/otp",
"/terms-and-conditions", "/privacy-policy"
)
//case1: OBP-API login: url = "/"
//case2: API-Explore oauth login: url = "/oauth/authorize?oauth_token=V0JTCDYXWUNTXDZ3VUDNM1HE3Q1PZR2WJ4PURXQA&logUserOut=false"

View File

@ -9,7 +9,7 @@ import code.api.util.APIUtil._
import code.api.util.ErrorMessages._
import code.util.Helper.MdcLoggable
import code.views.system.ViewDefinition.create
import code.views.system.{AccountAccess, ViewDefinition}
import code.views.system.{AccountAccess, ViewDefinition, ViewPermission}
import com.openbankproject.commons.model.{UpdateViewJSON, _}
import net.liftweb.common._
import net.liftweb.mapper.{Ascending, By, ByList, NullRef, OrderBy, PreCache, Schemifier}
@ -399,7 +399,7 @@ object MapperViews extends Views with MdcLoggable {
existing match {
case true =>
Failure(s"$ExistingSystemViewError $viewId")
Failure(s"$SystemViewAlreadyExistsError Current VIEW_ID($viewId)")
case false =>
val createdView = ViewDefinition.create.name_(view.name).view_id(viewId)
createdView.setFromViewData(view)
@ -436,7 +436,7 @@ object MapperViews extends Views with MdcLoggable {
) == 1
if (existing)
Failure(s"There is already a view with permalink $viewId on this bank account")
Failure(s"$CustomViewAlreadyExistsError Current BankId(${bankAccountId.bankId.value}), AccountId(${bankAccountId.accountId.value}), ViewId($viewId).")
else {
val createdView = ViewDefinition.create.
name_(view.name).
@ -584,6 +584,17 @@ object MapperViews extends Views with MdcLoggable {
})
PrivateViewsUserCanAccessCommon(accountAccess)
}
def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): (List[View], List[AccountAccess]) ={
val accountAccess = AccountAccess.findAll(
By(AccountAccess.user_fk, user.userPrimaryKey.value),
By(AccountAccess.bank_id, bankId.value),
By(AccountAccess.view_id, viewId.value)
).filter(accountAccess => {
val view = getViewFromAccountAccess(accountAccess)
view.isDefined && view.map(_.isPrivate) == Full(true)
})
PrivateViewsUserCanAccessCommon(accountAccess)
}
private def PrivateViewsUserCanAccessCommon(accountAccess: List[AccountAccess]): (List[ViewDefinition], List[AccountAccess]) = {
val listOfTuples: List[(AccountAccess, Box[ViewDefinition])] = accountAccess.map(
@ -622,11 +633,126 @@ object MapperViews extends Views with MdcLoggable {
logger.debug(s"-->getOrCreateSystemViewFromCbs --- finish.${viewId } : ${theView} ")
theView
}
private def migrateViewPermissions(view: View): Unit = {
val permissionNames = List(
"canSeeTransactionOtherBankAccount",
"canSeeTransactionMetadata",
"canSeeTransactionDescription",
"canSeeTransactionAmount",
"canSeeTransactionType",
"canSeeTransactionCurrency",
"canSeeTransactionStartDate",
"canSeeTransactionFinishDate",
"canSeeTransactionBalance",
"canSeeComments",
"canSeeOwnerComment",
"canSeeTags",
"canSeeImages",
"canSeeBankAccountOwners",
"canSeeBankAccountType",
"canSeeBankAccountBalance",
"canQueryAvailableFunds",
"canSeeBankAccountLabel",
"canSeeBankAccountNationalIdentifier",
"canSeeBankAccountSwift_bic",
"canSeeBankAccountIban",
"canSeeBankAccountNumber",
"canSeeBankAccountBankName",
"canSeeBankAccountBankPermalink",
"canSeeBankRoutingScheme",
"canSeeBankRoutingAddress",
"canSeeBankAccountRoutingScheme",
"canSeeBankAccountRoutingAddress",
"canSeeOtherAccountNationalIdentifier",
"canSeeOtherAccountSWIFT_BIC",
"canSeeOtherAccountIBAN",
"canSeeOtherAccountBankName",
"canSeeOtherAccountNumber",
"canSeeOtherAccountMetadata",
"canSeeOtherAccountKind",
"canSeeOtherBankRoutingScheme",
"canSeeOtherBankRoutingAddress",
"canSeeOtherAccountRoutingScheme",
"canSeeOtherAccountRoutingAddress",
"canSeeMoreInfo",
"canSeeUrl",
"canSeeImageUrl",
"canSeeOpenCorporatesUrl",
"canSeeCorporateLocation",
"canSeePhysicalLocation",
"canSeePublicAlias",
"canSeePrivateAlias",
"canAddMoreInfo",
"canAddURL",
"canAddImageURL",
"canAddOpenCorporatesUrl",
"canAddCorporateLocation",
"canAddPhysicalLocation",
"canAddPublicAlias",
"canAddPrivateAlias",
"canAddCounterparty",
"canGetCounterparty",
"canDeleteCounterparty",
"canDeleteCorporateLocation",
"canDeletePhysicalLocation",
"canEditOwnerComment",
"canAddComment",
"canDeleteComment",
"canAddTag",
"canDeleteTag",
"canAddImage",
"canDeleteImage",
"canAddWhereTag",
"canSeeWhereTag",
"canDeleteWhereTag",
"canAddTransactionRequestToOwnAccount",
"canAddTransactionRequestToAnyAccount",
"canSeeBankAccountCreditLimit",
"canCreateDirectDebit",
"canCreateStandingOrder",
"canRevokeAccessToCustomViews",
"canGrantAccessToCustomViews",
"canSeeTransactionRequests",
"canSeeTransactionRequestTypes",
"canSeeAvailableViewsForBankAccount",
"canUpdateBankAccountLabel",
"canCreateCustomView",
"canDeleteCustomView",
"canUpdateCustomView",
"canSeeViewsWithPermissionsForAllUsers",
"canSeeViewsWithPermissionsForOneUser"
)
permissionNames.foreach { permissionName =>
// Get permission value
val permissionValue = view.getClass.getMethod(permissionName).invoke(view).asInstanceOf[Boolean]
ViewPermission.findViewPermissions(view.viewId).find(_.permission.get == permissionName) match {
case Some(permission) if !permissionValue =>
ViewPermission.delete_!(permission)
case Some(permission) if permissionValue =>
// View definition is in accordance with View permission
case _ =>
ViewPermission.create
.bank_id(null)
.account_id(null)
.view_id(view.viewId.value)
.permission(permissionName)
.save
}
}
}
def getOrCreateSystemView(viewId: String) : Box[View] = {
getExistingSystemView(viewId) match {
case Empty => createDefaultSystemView(viewId)
case Full(v) => Full(v)
case Empty =>
val view = createDefaultSystemView(viewId)
view.map(v => migrateViewPermissions(v))
view
case Full(v) =>
migrateViewPermissions(v)
Full(v)
case Failure(msg, t, c) => Failure(msg, t, c)
case ParamFailure(x,y,z,q) => ParamFailure(x,y,z,q)
}

View File

@ -84,6 +84,7 @@ trait Views {
def privateViewsUserCanAccess(user: User): (List[View], List[AccountAccess])
def privateViewsUserCanAccess(user: User, viewIds: List[ViewId]): (List[View], List[AccountAccess])
def privateViewsUserCanAccessAtBank(user: User, bankId: BankId): (List[View], List[AccountAccess])
def getAccountAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId): (List[View], List[AccountAccess])
def privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId) : List[View]
//the following return list[BankIdAccountId], just use the list[View] method, the View object contains enough data for it.
@ -150,6 +151,7 @@ class RemotedataViewsCaseClasses {
case class privateViewsUserCanAccess(user: User)
case class privateViewsUserCanAccessViaViewId(user: User, viewIds: List[ViewId])
case class privateViewsUserCanAccessAtBank(user: User, bankId: BankId)
case class privateViewsUserCanAccessAtBankThroughView(user: User, bankId: BankId, viewId: ViewId)
case class privateViewsUserCanAccessForAccount(user: User, bankIdAccountId : BankIdAccountId)
case class getAllFirehoseAccounts(bank: Bank, user : User)
case class publicViews()

View File

@ -0,0 +1,29 @@
package code.views.system
import code.util.UUIDString
import com.openbankproject.commons.model._
import net.liftweb.mapper._
class ViewPermission extends LongKeyedMapper[ViewPermission] with IdPK with CreatedUpdated {
def getSingleton = ViewPermission
object bank_id extends MappedString(this, 255)
object account_id extends MappedString(this, 255)
object view_id extends UUIDString(this)
object permission extends MappedString(this, 255)
}
object ViewPermission extends ViewPermission with LongKeyedMetaMapper[ViewPermission] {
override def dbIndexes: List[BaseIndex[ViewPermission]] = UniqueIndex(bank_id, account_id, view_id, permission) :: super.dbIndexes
// Very working progress
def findViewPermissions(bankId: BankId, accountId: AccountId, viewId: ViewId): List[ViewPermission] =
ViewPermission.findAll(
By(ViewPermission.bank_id, bankId.value),
By(ViewPermission.account_id, accountId.value),
By(ViewPermission.view_id, viewId.value)
) // Very working progress
def findViewPermissions(viewId: ViewId): List[ViewPermission] =
ViewPermission.findAll(
NullRef(ViewPermission.bank_id),
NullRef(ViewPermission.account_id),
By(ViewPermission.view_id, viewId.value)
)
}

View File

@ -51,11 +51,10 @@
</button>
</h3>
<p id="main-faq-item2" class="collapse">
<lift:loc locid="There.are.two.ways.to.authenticate.a.user">There are two ways to authenticate a user:</lift:loc><a
<lift:loc locid="There.are.two.ways.to.authenticate.a.user">There are multiple ways to authenticate a user including: </lift:loc><a
href="https://github.com/OpenBankProject/OBP-API/wiki/OAuth-Client-SDKS">OAuth</a><lift:loc locid="and"> and </lift:loc><a
href="https://github.com/OpenBankProject/OBP-API/wiki/Direct-Login"><lift:loc locid="Direct.Login"> Direct Login </lift:loc></a>. <lift:loc locid="If.you.are.using.this.sandbox.for.a.hackathon.we.recommend.you.use.Direct.Login.to.authenticate.as.it.is.easier.than.the.OAuth.workflow"> If you
are using this sandbox for a hackathon, we recommend you use Direct Login to authenticate as it
is easier than the OAuth workflow. </lift:loc>
are using this sandbox for a hackathon, we recommend you use Direct Login to start with. </lift:loc>
</p>
<img src="/media/images/icons/chevron_down_thick.svg" alt="">
</div>
@ -78,10 +77,10 @@
href="">Direct Login</a>.
<lift:loc locid="For.an.OAuth.walkthrough.example.with.sample.code.please.see">For an OAuth walkthrough example with sample code, please see</lift:loc> <a
href="https://github.com/OpenBankProject/Hello-OBP-OAuth1.0a-Python">here</a>.
<lift:loc locid="We.use.OAuth.1.0a.For.deepish.technical.details.of.the.flow">We use OAuth 1.0a. For deepish technical details of the flow</lift:loc> <a
<lift:loc locid="We.use.OAuth.1.0a.For.deepish.technical.details.of.the.flow">We support OAuth 1.0a. For deepish technical details of the flow</lift:loc> <a
class="oauth-1-documentation-url" data-lift="WebUI.oauth1DocumentationUrl" href=""><lift:loc locid="see.here">see
here.</lift:loc></a><br/>
<lift:loc locid="We.also.support.OAuth.2.0.For.the.technical.details.of.using.OBP.API.with.OAuth.2.0.please">We also support OAuth 2.0. For the technical details of using OBP API with OAuth 2.0, please</lift:loc> <a
<lift:loc locid="We.also.support.OAuth.2.0.For.the.technical.details.of.using.OBP.API.with.OAuth.2.0.please">We support OAuth 2.0. For the technical details of using OBP API with OAuth 2.0, please</lift:loc> <a
class="oauth-2-documentation-url" data-lift="WebUI.oauth2DocumentationUrl" href=""><lift:loc locid="see.here">see
here.</lift:loc></a><br/>
</p>

View File

@ -26,7 +26,18 @@ Berlin 13359, Germany
<div data-lift="surround?with=default;at=content">
<div data-lift="WebUI.privacyPolicyText">
<div id="api_documentation_div">
<div id="api_documentation_content" tabindex="-1">
<form id="form_privacy_policy" method="post" data-lift="PrivacyPolicy.updateForm">
<div class="row" >
<div><lift:loc locid="outdated.policy.warning.1">Dear</lift:loc> <em id="privacy-policy-username"></em> <lift:loc locid="outdated.policy.warning.2">. We have updated our policy since you last agreed to them! Please review the text and agree if you agree!</lift:loc></div>
<div class="col-xs-6">
<input type="reset" value="Skip" class="btn btn-default pull-right" tabindex="0"/>
</div>
<div class="col-xs-6">
<input type="submit" value="accept terms" class="btn btn-default" tabindex="0"/>
</div>
</div>
</form>
<div>
<div id="privacy-policy-page"></div>
</div>
</div>

View File

@ -26,7 +26,18 @@ Berlin 13359, Germany
<div data-lift="surround?with=default;at=content">
<div data-lift="WebUI.termsAndConditionsText">
<div id="api_documentation_div">
<div id="api_documentation_content" tabindex="-1">
<form id="form_terms_and_conditions" method="post" data-lift="TermsAndConditions.updateForm">
<div class="row" >
<div><lift:loc locid="outdated.terms.warning.1">Dear</lift:loc> <em id="terms-and-conditions-username"></em> <lift:loc locid="outdated.terms.warning.2">. We have updated our terms since you last agreed to them! Please review the text and agree if you agree!</lift:loc></div>
<div class="col-xs-6">
<input type="reset" value="Skip" class="btn btn-default pull-right" tabindex="0"/>
</div>
<div class="col-xs-6">
<input type="submit" value="accept terms" class="btn btn-default" tabindex="0"/>
</div>
</div>
</form>
<div>
<div id="terms-and-conditions-page"></div>
</div>
</div>

View File

@ -103,19 +103,27 @@ class ConsentTest extends V310ServerSetup {
}
scenario("We will call the endpoint with user credentials", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) {
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE")
wholeFunctionality(RequestHeader.`Consent-JWT`)
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE")
}
scenario("We will call the endpoint with user credentials and deprecated header name", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) {
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE")
wholeFunctionality(RequestHeader.`Consent-Id`)
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE")
}
scenario("We will call the endpoint with user credentials-Implicit", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) {
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE")
wholeFunctionalityImplicit(RequestHeader.`Consent-JWT`)
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE")
}
scenario("We will call the endpoint with user credentials and deprecated header name-Implicit", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) {
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_KEY_VALUE")
wholeFunctionalityImplicit(RequestHeader.`Consent-Id`)
setPropsValues("consumer_validation_method_for_consent"-> "CONSUMER_CERTIFICATE")
}
}

View File

@ -16,8 +16,8 @@ class AccountBalanceTest extends V400ServerSetup {
* This is made possible by the scalatest maven plugin
*/
object VersionOfApi extends Tag(ApiVersion.v4_0_0.toString)
object ApiEndpoint1 extends Tag(nameOf(Implementations4_0_0.getBankAccountsBalances))
object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getBankAccountBalances))
object ApiEndpoint1 extends Tag(nameOf(Implementations4_0_0.getBankAccountsBalancesForCurrentUser))
object ApiEndpoint2 extends Tag(nameOf(Implementations4_0_0.getBankAccountBalancesForCurrentUser))
lazy val bankId = randomBankId
lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId)

View File

@ -5,10 +5,11 @@ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300
import code.api.util.APIUtil.OAuth._
import code.api.util.ApiRole
import code.api.util.ErrorMessages.{UserLacksPermissionCanGrantAccessToCustomViewForTargetAccount, UserLacksPermissionCanGrantAccessToSystemViewForTargetAccount, UserLacksPermissionCanGrantAccessToViewForTargetAccount, UserLacksPermissionCanRevokeAccessToCustomViewForTargetAccount, UserLacksPermissionCanRevokeAccessToSystemViewForTargetAccount, UserLacksPermissionCanRevokeAccessToViewForTargetAccount, UserNotLoggedIn}
import code.api.util.ApiRole.CanSeeAccountAccessForAnyUser
import code.api.util.ErrorMessages._
import code.api.v3_0_0.ViewJsonV300
import code.api.v3_1_0.CreateAccountResponseJsonV310
import code.api.v4_0_0.RevokedJsonV400
import code.api.v4_0_0.{AccountsMinimalJson400, RevokedJsonV400}
import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0
import code.entitlement.Entitlement
import com.github.dwickern.macros.NameOf.nameOf
@ -30,6 +31,7 @@ class AccountAccessTest extends V510ServerSetup {
object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.grantUserAccessToViewById))
object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.revokeUserAccessToViewById))
object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.createUserWithAccountAccessById))
object GetAccountAccessByUserId extends Tag(nameOf(Implementations5_1_0.getAccountAccessByUserId))
lazy val bankId = randomBankId
@ -54,6 +56,34 @@ class AccountAccessTest extends V510ServerSetup {
createViewViaEndpoint(bankId, accountId, postBodyViewJson, user1)
}
feature(s"test ${GetAccountAccessByUserId.name}") {
scenario(s"We will test ${GetAccountAccessByUserId.name}", GetAccountAccessByUserId, VersionOfApi) {
val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "account-access").GET
// Anonymous call fails
val anonymousResponseGet = makeGetRequest(requestGet)
anonymousResponseGet.code should equal(401)
anonymousResponseGet.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
// Call endpoint without the entitlement
val badResponseGet = makeGetRequest(requestGet <@ user1)
badResponseGet.code should equal(403)
val errorMessage = badResponseGet.body.extract[ErrorMessage].message
errorMessage contains UserHasMissingRoles should be (true)
errorMessage contains CanSeeAccountAccessForAnyUser.toString() should be (true)
// All good
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSeeAccountAccessForAnyUser.toString())
val goodResponseGet = makeGetRequest(requestGet <@ user1)
goodResponseGet.code should equal(200)
goodResponseGet.body.extract[AccountsMinimalJson400]
}
}
feature(s"test $ApiEndpoint1 Authorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {

View File

@ -0,0 +1,111 @@
package code.api.v5_1_0
import code.api.util.APIUtil.OAuth._
import code.api.util.ErrorMessages.UserNotLoggedIn
import code.api.v4_0_0.{AccountsBalancesJsonV400, BalanceJsonV400}
import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.model.ErrorMessage
import com.openbankproject.commons.util.ApiVersion
import dispatch.Req
import net.liftweb.json
import org.scalatest.Tag
class AccountBalanceTest extends V510ServerSetup {
/**
* Test tags
* Example: To run tests with tag "getPermissions":
* mvn test -D tagsToInclude
*
* This is made possible by the scalatest maven plugin
*/
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.getBankAccountBalances))
object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getBankAccountsBalances))
object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.getBankAccountsBalancesThroughView))
lazy val bankId = randomBankId
lazy val bankAccount = randomPrivateAccountViaEndpoint(bankId)
def requestGetAccountBalances(viewId: String = "None"): Req = (v5_1_0_Request / "banks" / bankAccount.bank_id / "accounts" / bankAccount.id / "views" / viewId / "balances").GET
def requestGetAccountsBalances(): Req = (v5_1_0_Request / "banks" / bankAccount.bank_id / "balances").GET
def requestGetAccountsBalancesThroughView(viewId: String = "None"): Req = (v5_1_0_Request / "banks" / bankAccount.bank_id / "views" / viewId / "balances").GET
feature(s"test $ApiEndpoint1 version $VersionOfApi - Unauthorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
When(s"We make a request $ApiEndpoint1")
val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances())
Then("We should get a 401")
responseGetAccountBalances.code should equal(401)
responseGetAccountBalances.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access, no proper view") {
scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) {
val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances() <@ user1)
Then("We should get a 403")
responseGetAccountBalances.code should equal(403)
}
}
feature(s"test $ApiEndpoint1 version $VersionOfApi - Authorized access with proper view") {
scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) {
val responseGetAccountBalances = makeGetRequest(requestGetAccountBalances("owner") <@ user1)
Then("We should get a 200")
responseGetAccountBalances.code should equal(200)
}
}
feature(s"test $ApiEndpoint2 version $VersionOfApi - Unauthorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
When(s"We make a request $ApiEndpoint1")
val responseGetAccountBalances = makeGetRequest(requestGetAccountsBalances())
Then("We should get a 401")
responseGetAccountBalances.code should equal(401)
responseGetAccountBalances.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
feature(s"test $ApiEndpoint2 version $VersionOfApi - Authorized access with proper view") {
scenario("We will call the endpoint with user credentials", VersionOfApi, ApiEndpoint1) {
val responseGetAccountBalances = makeGetRequest(requestGetAccountsBalances() <@ user1)
Then("We should get a 200")
responseGetAccountBalances.code should equal(200)
val accountsBefore = responseGetAccountBalances.body.extract[AccountsBalancesJsonV400].accounts.length
// Make transaction
val amountOfMoney = "10.00"
// Create from and to account with 0 balance and transfer money
val (fromAccountId, toAccountId, _) = createTransactionRequest(bankId, amountOfMoney)
val responseGetAccountBalances2 = makeGetRequest(requestGetAccountsBalances() <@ user1)
Then("We should get a 200")
responseGetAccountBalances2.code should equal(200)
val accountsAfter = responseGetAccountBalances2.body.extract[AccountsBalancesJsonV400].accounts.length
accountsAfter should equal(accountsBefore + 2)
val balances = responseGetAccountBalances2.body.extract[AccountsBalancesJsonV400]
val toAccountBalance = balances.accounts.filter(_.account_id == toAccountId)
val filteredBalances: List[BalanceJsonV400] = toAccountBalance.flatMap(_.balances.filter(i => i.amount == amountOfMoney && i.currency == "EUR"))
filteredBalances.length should equal(1)
val fromAccountBalance = balances.accounts.filter(_.account_id == fromAccountId)
val filteredFromAccountBalance: List[BalanceJsonV400] = fromAccountBalance.flatMap(_.balances.filter(i => i.amount == s"-${amountOfMoney}" && i.currency == "EUR"))
filteredFromAccountBalance.length should equal(1)
}
}
feature(s"test $ApiEndpoint3 version $VersionOfApi - Unauthorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, VersionOfApi) {
When(s"We make a request $ApiEndpoint1")
val responseGetAccountBalances = makeGetRequest(requestGetAccountsBalancesThroughView("owner"))
Then("We should get a 401")
responseGetAccountBalances.code should equal(401)
responseGetAccountBalances.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
}

View File

@ -20,31 +20,18 @@ class AccountTest extends V510ServerSetup {
* This is made possible by the scalatest maven plugin
*/
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
object GetAccountAccessByUserId extends Tag(nameOf(Implementations5_1_0.getAccountAccessByUserId))
object GetCoreAccountByIdThroughView extends Tag(nameOf(Implementations5_1_0.getCoreAccountByIdThroughView))
feature(s"test ${GetAccountAccessByUserId.name}") {
scenario(s"We will test ${GetAccountAccessByUserId.name}", GetAccountAccessByUserId, VersionOfApi) {
feature(s"test ${GetCoreAccountByIdThroughView.name}") {
scenario(s"We will test ${GetCoreAccountByIdThroughView.name}", GetCoreAccountByIdThroughView, VersionOfApi) {
val requestGet = (v5_1_0_Request / "users" / resourceUser2.userId / "account-access").GET
val requestGet = (v5_1_0_Request / "banks" / "BANK_ID" / "accounts" / "ACCOUNT_ID"/ "views" / "VIEW_ID").GET
// Anonymous call fails
val anonymousResponseGet = makeGetRequest(requestGet)
anonymousResponseGet.code should equal(401)
anonymousResponseGet.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
// Call endpoint without the entitlement
val badResponseGet = makeGetRequest(requestGet <@ user1)
badResponseGet.code should equal(403)
val errorMessage = badResponseGet.body.extract[ErrorMessage].message
errorMessage contains UserHasMissingRoles should be (true)
errorMessage contains CanSeeAccountAccessForAnyUser.toString() should be (true)
// All good
Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanSeeAccountAccessForAnyUser.toString())
val goodResponseGet = makeGetRequest(requestGet <@ user1)
goodResponseGet.code should equal(200)
goodResponseGet.body.extract[AccountsMinimalJson400]
}
}

View File

@ -0,0 +1,185 @@
package code.api.v5_1_0
import java.util.UUID
import code.api.Constant.{SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, SYSTEM_OWNER_VIEW_ID}
import code.api.util.ErrorMessages._
import code.api.util.APIUtil.OAuth._
import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.model.ErrorMessage
import com.openbankproject.commons.util.ApiVersion
import net.liftweb.json.Serialization.write
import org.scalatest.Tag
class CounterpartyLimitTest extends V510ServerSetup {
/**
* Test tags
* Example: To run tests with tag "getPermissions":
* mvn test -D tagsToInclude
*
* This is made possible by the scalatest maven plugin
*/
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createCounterpartyLimit))
object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.getCounterpartyLimit))
object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.updateCounterpartyLimit))
object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.deleteCounterpartyLimit))
val bankId = testBankId1.value
val accountId = testAccountId1.value
val ownerView = SYSTEM_OWNER_VIEW_ID
val postCounterpartyLimitV510 = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.postCounterpartyLimitV510
val putCounterpartyLimitV510 = PostCounterpartyLimitV510(
max_single_amount = 1,
max_monthly_amount = 2,
max_number_of_monthly_transactions = 3,
max_yearly_amount = 4,
max_number_of_yearly_transactions = 5
)
feature(s"test $ApiEndpoint1,$ApiEndpoint2, $ApiEndpoint3, $ApiEndpoint4, Authorized access") {
scenario("We will call the endpoint without user credentials", ApiEndpoint1, ApiEndpoint2,ApiEndpoint3,ApiEndpoint4,VersionOfApi) {
val counterparty = createCounterparty(bankId, accountId, accountId, true, UUID.randomUUID.toString);
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").POST
val response510 = makePostRequest(request510, write(postCounterpartyLimitV510))
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").PUT
val response510 = makePutRequest(request510, write(postCounterpartyLimitV510))
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET
val response510 = makeGetRequest(request510)
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").DELETE
val response510 = makeDeleteRequest(request510)
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
scenario("We will call the endpoint success case", ApiEndpoint1, ApiEndpoint2,ApiEndpoint3,ApiEndpoint4,VersionOfApi) {
val counterparty = createCounterparty(bankId, accountId, accountId, true, UUID.randomUUID.toString);
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").POST <@ (user1)
val response510 = makePostRequest(request510, write(postCounterpartyLimitV510))
Then("We should get a 201")
response510.code should equal(201)
response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(postCounterpartyLimitV510.max_monthly_amount)
response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(postCounterpartyLimitV510.max_number_of_monthly_transactions)
response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(postCounterpartyLimitV510.max_number_of_yearly_transactions)
response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(postCounterpartyLimitV510.max_single_amount)
response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(postCounterpartyLimitV510.max_yearly_amount)
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 200")
response510.code should equal(200)
response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(postCounterpartyLimitV510.max_monthly_amount)
response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(postCounterpartyLimitV510.max_number_of_monthly_transactions)
response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(postCounterpartyLimitV510.max_number_of_yearly_transactions)
response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(postCounterpartyLimitV510.max_single_amount)
response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(postCounterpartyLimitV510.max_yearly_amount)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").PUT<@ (user1)
val response510 = makePutRequest(request510, write(putCounterpartyLimitV510))
Then("We should get a 200")
response510.code should equal(200)
response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(putCounterpartyLimitV510.max_monthly_amount)
response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(putCounterpartyLimitV510.max_number_of_monthly_transactions)
response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(putCounterpartyLimitV510.max_number_of_yearly_transactions)
response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(putCounterpartyLimitV510.max_single_amount)
response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(putCounterpartyLimitV510.max_yearly_amount)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 200")
response510.code should equal(200)
response510.body.extract[CounterpartyLimitV510].max_monthly_amount should equal(putCounterpartyLimitV510.max_monthly_amount)
response510.body.extract[CounterpartyLimitV510].max_number_of_monthly_transactions should equal(putCounterpartyLimitV510.max_number_of_monthly_transactions)
response510.body.extract[CounterpartyLimitV510].max_number_of_yearly_transactions should equal(putCounterpartyLimitV510.max_number_of_yearly_transactions)
response510.body.extract[CounterpartyLimitV510].max_single_amount should equal(putCounterpartyLimitV510.max_single_amount)
response510.body.extract[CounterpartyLimitV510].max_yearly_amount should equal(putCounterpartyLimitV510.max_yearly_amount)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").DELETE<@ (user1)
val response510 = makeDeleteRequest(request510)
Then("We should get a 204")
response510.code should equal(204)
}
}
scenario("We will call the endpoint wrong bankId case", ApiEndpoint1, ApiEndpoint2,ApiEndpoint3,ApiEndpoint4,VersionOfApi) {
val counterparty = createCounterparty(bankId, accountId, accountId, true, UUID.randomUUID.toString);
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId / "views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").POST <@ (user1)
val response510 = makePostRequest(request510, write(postCounterpartyLimitV510))
Then("We should get a 404")
response510.code should equal(404)
response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true)
{
val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 404")
response510.code should equal(404)
response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true)
}
{
val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").PUT<@ (user1)
val response510 = makePutRequest(request510, write(putCounterpartyLimitV510))
Then("We should get a 404")
response510.code should equal(404)
response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true)
}
{
val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").GET<@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 404")
response510.code should equal(404)
response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true)
}
{
val request510 = (v5_1_0_Request / "banks" / "wrongId" / "accounts" / accountId /"views" / ownerView /"counterparties" / counterparty.counterpartyId /"limits").DELETE<@ (user1)
val response510 = makeDeleteRequest(request510)
Then("We should get a 404")
response510.code should equal(404)
response510.body.extract[ErrorMessage].message contains(BankNotFound) shouldBe (true)
}
}
}
}

View File

@ -0,0 +1,231 @@
package code.api.v5_1_0
import code.api.Constant.{SYSTEM_AUDITOR_VIEW_ID, SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, SYSTEM_OWNER_VIEW_ID, SYSTEM_STAGE_ONE_VIEW_ID}
import code.api.util.APIUtil.OAuth._
import code.api.util.ErrorMessages._
import code.api.v5_1_0.OBPAPI5_1_0.Implementations5_1_0
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.model.ErrorMessage
import com.openbankproject.commons.util.ApiVersion
import net.liftweb.json.Serialization.write
import net.liftweb.util.StringHelpers
import org.scalatest.Tag
import java.util.UUID
class CustomViewTest extends V510ServerSetup {
/**
* Test tags
* Example: To run tests with tag "getPermissions":
* mvn test -D tagsToInclude
*
* This is made possible by the scalatest maven plugin
*/
object VersionOfApi extends Tag(ApiVersion.v5_1_0.toString)
object ApiEndpoint1 extends Tag(nameOf(Implementations5_1_0.createCustomView))
object ApiEndpoint2 extends Tag(nameOf(Implementations5_1_0.updateCustomView))
object ApiEndpoint3 extends Tag(nameOf(Implementations5_1_0.getCustomView))
object ApiEndpoint4 extends Tag(nameOf(Implementations5_1_0.deleteCustomView))
val bankId = testBankId1.value
val accountId = testAccountId1.value
val ownerView = SYSTEM_OWNER_VIEW_ID
val manageCustomView = SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID
val targetViewId = "_test"
val postCustomViewJsonWithFullPermissinos = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createCustomViewJson.copy(name=targetViewId)
val postCustomViewJson = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createCustomViewJson.copy(
name=targetViewId,
metadata_view=targetViewId,
allowed_permissions = List("can_see_transaction_start_date", "can_add_url")
)
val putCustomViewJsonWithFullPermissinos = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.updateCustomViewJson
val putCustomViewJson = code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.updateCustomViewJson.copy(
description = "1",
metadata_view = "2",
is_public = false,
which_alias_to_use = "",//TODO please check this field later.
hide_metadata_if_alias_used = false,
allowed_permissions = List("can_see_transaction_this_bank_account", "can_see_bank_account_owners")
)
feature(s"test Authorized access") {
scenario(s"We will call the endpoint, $UserNotLoggedIn", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"target-views").POST
val response510 = makePostRequest(request510, write(postCustomViewJson))
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).PUT
val response510 = makePutRequest(request510, write(putCustomViewJson))
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).GET
val response510 = makeGetRequest(request510)
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).DELETE
val response510 = makeDeleteRequest(request510)
Then("We should get a 401")
response510.code should equal(401)
response510.body.extract[ErrorMessage].message should equal(UserNotLoggedIn)
}
}
scenario(s"We will call the endpoint, $SourceViewHasLessPermission", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"target-views").POST <@ (user1)
val response510 = makePostRequest(request510, write(postCustomViewJsonWithFullPermissinos))
Then("We should get a 400")
response510.code should equal(400)
response510.body.extract[ErrorMessage].message contains(SourceViewHasLessPermission) shouldBe(true)
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).PUT <@ (user1)
val response510 = makePutRequest(request510, write(putCustomViewJsonWithFullPermissinos))
Then("We should get a 400")
response510.code should equal(400)
response510.body.extract[ErrorMessage].message contains(SourceViewHasLessPermission) shouldBe(true)
}
}
scenario(s"We will call the endpoint, $ViewDoesNotPermitAccess ", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView /"target-views").POST <@ (user1)
val response510 = makePostRequest(request510, write(postCustomViewJson))
Then("We should get a 400")
response510.code should equal(400)
response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true)
response510.body.extract[ErrorMessage].message contains ("can_create_custom_view") shouldBe (true)
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).PUT <@ (user1)
val response510 = makePutRequest(request510, write(putCustomViewJson))
Then("We should get a 400")
response510.code should equal(400)
response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true)
response510.body.extract[ErrorMessage].message contains ("can_update_custom_view") shouldBe (true)
}
{
When("we need to prepare the custom view for get and delete")
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / manageCustomView / "target-views").POST <@ (user1)
val response510 = makePostRequest(request510, write(postCustomViewJson))
Then("We should get a 201")
response510.code should equal(201)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / SYSTEM_AUDITOR_VIEW_ID /"target-views" / targetViewId ).GET <@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 400")
response510.code should equal(400)
response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true)
response510.body.extract[ErrorMessage].message contains ("can_see_available_views_for_bank_account") shouldBe (true)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView / "target-views" / targetViewId).DELETE <@ (user1)
val response510 = makeDeleteRequest(request510)
Then("We should get a 400")
response510.code should equal(400)
response510.body.extract[ErrorMessage].message contains (ViewDoesNotPermitAccess) shouldBe (true)
response510.body.extract[ErrorMessage].message contains ("can_delete_custom_view") shouldBe (true)
}
}
scenario("We will call the endpoint with user credentials", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, VersionOfApi) {
When("We make a request v5.1.0")
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / manageCustomView /"target-views").POST <@ (user1)
val response510 = makePostRequest(request510, write(postCustomViewJson))
Then("We should get a 201")
response510.code should equal(201)
response510.body.extract[CustomViewJsonV510].name should equal(postCustomViewJson.name)
response510.body.extract[CustomViewJsonV510].description should equal(postCustomViewJson.description)
response510.body.extract[CustomViewJsonV510].metadata_view should equal(postCustomViewJson.metadata_view)
response510.body.extract[CustomViewJsonV510].is_public should equal(postCustomViewJson.is_public)
response510.body.extract[CustomViewJsonV510].alias should equal(postCustomViewJson.which_alias_to_use)
response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(postCustomViewJson.hide_metadata_if_alias_used)
response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(postCustomViewJson.allowed_permissions.sorted)
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView / "target-views" / targetViewId).GET <@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 200")
response510.code should equal(200)
response510.body.extract[CustomViewJsonV510].name should equal(postCustomViewJson.name)
response510.body.extract[CustomViewJsonV510].description should equal(postCustomViewJson.description)
response510.body.extract[CustomViewJsonV510].metadata_view should equal(postCustomViewJson.metadata_view)
response510.body.extract[CustomViewJsonV510].is_public should equal(postCustomViewJson.is_public)
response510.body.extract[CustomViewJsonV510].alias should equal(postCustomViewJson.which_alias_to_use)
response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(postCustomViewJson.hide_metadata_if_alias_used)
response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(postCustomViewJson.allowed_permissions.sorted)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / manageCustomView /"target-views" / targetViewId ).PUT <@ (user1)
val response510 = makePutRequest(request510, write(putCustomViewJson))
Then("We should get a 200")
response510.code should equal(200)
response510.body.extract[CustomViewJsonV510].description should equal(putCustomViewJson.description)
response510.body.extract[CustomViewJsonV510].metadata_view should equal(putCustomViewJson.metadata_view)
response510.body.extract[CustomViewJsonV510].is_public should equal(putCustomViewJson.is_public)
response510.body.extract[CustomViewJsonV510].alias should equal(putCustomViewJson.which_alias_to_use)
response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(putCustomViewJson.hide_metadata_if_alias_used)
response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(putCustomViewJson.allowed_permissions.sorted)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / ownerView /"target-views" / targetViewId ).GET <@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 200")
response510.code should equal(200)
response510.body.extract[CustomViewJsonV510].description should equal(putCustomViewJson.description)
response510.body.extract[CustomViewJsonV510].metadata_view should equal(putCustomViewJson.metadata_view)
response510.body.extract[CustomViewJsonV510].is_public should equal(putCustomViewJson.is_public)
response510.body.extract[CustomViewJsonV510].alias should equal(putCustomViewJson.which_alias_to_use)
response510.body.extract[CustomViewJsonV510].hide_metadata_if_alias_used should equal(putCustomViewJson.hide_metadata_if_alias_used)
response510.body.extract[CustomViewJsonV510].allowed_permissions.sorted should equal(putCustomViewJson.allowed_permissions.sorted)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId /"views" / manageCustomView /"target-views" / targetViewId ).DELETE <@ (user1)
val response510 = makeDeleteRequest(request510)
Then("We should get a 204")
response510.code should equal(204)
}
{
val request510 = (v5_1_0_Request / "banks" / bankId / "accounts" / accountId / "views" / ownerView / "target-views" / targetViewId).GET <@ (user1)
val response510 = makeGetRequest(request510)
Then("We should get a 400")
response510.code should equal(400)
response510.body.extract[ErrorMessage].message contains (ViewNotFound) shouldBe(true)
}
}
}
}

View File

@ -2,22 +2,26 @@ package code.api.v5_1_0
import code.api.Constant.SYSTEM_OWNER_VIEW_ID
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createViewJsonV300
import code.api.util.APIUtil.OAuth.{Consumer, Token, _}
import code.api.util.ApiRole
import code.api.util.ApiRole.CanCreateCustomer
import code.api.v1_2_1.{AccountJSON, AccountsJSON, ViewsJSONV121}
import code.api.v2_0_0.BasicAccountsJSON
import code.api.v1_2_1.{AccountJSON, AccountsJSON, PostTransactionCommentJSON, ViewsJSONV121}
import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140
import code.api.v2_0_0.{BasicAccountsJSON, TransactionRequestBodyJsonV200}
import code.api.v3_0_0.ViewJsonV300
import code.api.v3_1_0.CustomerJsonV310
import code.api.v4_0_0.{AtmJsonV400, BanksJson400}
import code.api.v3_1_0.{CreateAccountRequestJsonV310, CreateAccountResponseJsonV310, CustomerJsonV310}
import code.api.v4_0_0.{AtmJsonV400, BanksJson400, PostAccountAccessJsonV400, PostViewJsonV400, TransactionRequestWithChargeJSON400}
import code.api.v5_0_0.PostCustomerJsonV500
import code.entitlement.Entitlement
import code.setup.{APIResponse, DefaultUsers, ServerSetupWithTestData}
import com.openbankproject.commons.model.CreateViewJson
import com.openbankproject.commons.model.{AccountRoutingJsonV121, AmountOfMoneyJsonV121, CreateViewJson}
import com.openbankproject.commons.util.ApiShortVersions
import dispatch.Req
import net.liftweb.json.Serialization.write
import net.liftweb.util.Helpers.randomString
import scala.util.Random
import scala.util.Random.nextInt
trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers {
@ -89,5 +93,88 @@ trait V510ServerSetup extends ServerSetupWithTestData with DefaultUsers {
response.code should equal(201)
response.body.extract[CustomerJsonV310]
}
def createTransactionRequestViaEndpoint(fromBankId: String,
fromAccountId: String,
fromCurrency: String,
fromViewId: String,
amount: String,
toBankId: String,
toAccountId: String,
consumerAndToken: Option[(Consumer, Token)]): TransactionRequestWithChargeJSON400 = {
val toAccountJson = TransactionRequestAccountJsonV140(toBankId, toAccountId)
val bodyValue = AmountOfMoneyJsonV121(fromCurrency, amount)
val description = "Just test it!"
val transactionRequestBody = TransactionRequestBodyJsonV200(toAccountJson, bodyValue, description)
val createTransReqRequest = (v5_1_0_Request / "banks" / fromBankId / "accounts" / fromAccountId /
fromViewId / "transaction-request-types" / "SANDBOX_TAN" / "transaction-requests").POST <@ (consumerAndToken)
makePostRequest(createTransReqRequest, write(transactionRequestBody)).body.extract[TransactionRequestWithChargeJSON400]
}
def createAccountViaEndpoint(bankId : String, json: CreateAccountRequestJsonV310, consumerAndToken: Option[(Consumer, Token)]) = {
val entitlement = Entitlement.entitlement.vend.addEntitlement(bankId, resourceUser1.userId, ApiRole.canCreateAccount.toString)
And("We make a request v4.0.0")
val request400 = (v5_1_0_Request / "banks" / bankId / "accounts" ).POST <@(consumerAndToken)
val response400 = makePostRequest(request400, write(json))
Then("We should get a 201")
response400.code should equal(201)
val account = response400.body.extract[CreateAccountResponseJsonV310]
account.account_id should not be empty
Entitlement.entitlement.vend.deleteEntitlement(entitlement)
account
}
def grantUserAccessToViewViaEndpoint(bankId: String,
accountId: String,
userId: String,
consumerAndToken: Option[(Consumer, Token)],
postBody: PostViewJsonV400
): ViewJsonV300 = {
val postJson = PostAccountAccessJsonV400(userId, postBody)
val request = (v4_0_0_Request / "banks" / bankId / "accounts" / accountId / "account-access" / "grant").POST <@ (consumerAndToken)
val response = makePostRequest(request, write(postJson))
Then("We should get a 201 and check the response body")
response.code should equal(201)
response.body.extract[ViewJsonV300]
}
def createTransactionRequest(bankId: String, amountOnMoney: String): (String, String, String) = {
// Create a Bank
val bank = createBank(bankId)
val addAccountJson1 = SwaggerDefinitionsJSON.createAccountRequestJsonV310
.copy(user_id = resourceUser1.userId, balance = AmountOfMoneyJsonV121("EUR","0"),
account_routings = List(AccountRoutingJsonV121(Random.nextString(10), Random.nextString(10))))
val addAccountJson2 = SwaggerDefinitionsJSON.createAccountRequestJsonV310
.copy(user_id = resourceUser1.userId, balance = AmountOfMoneyJsonV121("EUR","0"),
account_routings = List(AccountRoutingJsonV121(Random.nextString(10), Random.nextString(10))))
// Create from account
val fromAccount = createAccountViaEndpoint(bank.bankId.value, addAccountJson1, user1)
// Create to account
val toAccount = createAccountViaEndpoint(bank.bankId.value, addAccountJson2, user1)
// Create a custom view
val customViewJson = createViewJsonV300.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false).toCreateViewJson
val customView = createViewViaEndpoint(bank.bankId.value, fromAccount.account_id, customViewJson, user1)
// Grant access to the view
grantUserAccessToViewViaEndpoint(
bank.bankId.value,
fromAccount.account_id,
resourceUser1.userId,
user1,
PostViewJsonV400(view_id = customView.id, is_system = false)
)
// Create a Transaction Request
val transactionRequest = createTransactionRequestViaEndpoint(
fromBankId = bank.bankId.value,
fromAccountId = fromAccount.account_id,
fromCurrency = fromAccount.balance.currency,
fromViewId = customView.id,
amount = amountOnMoney,
toBankId = bank.bankId.value,
toAccountId = toAccount.account_id,
user1
)
val transactionId = transactionRequest.transaction_ids.headOption.getOrElse("")
(fromAccount.account_id, toAccount.account_id, transactionId)
}
}

View File

@ -1,7 +1,6 @@
package code.connector
import code.api.Constant
import code.api.Constant.SYSTEM_STANDARD_VIEW_ID
import code.api.Constant._
import code.api.util.{CallContext, CustomJsonFormats}
import code.bankconnectors._
import code.setup.{DefaultConnectorTestSetup, DefaultUsers, ServerSetup}
@ -39,7 +38,7 @@ object MockedCbsConnector extends ServerSetup
balanceAmount = "",
balanceCurrency = "",
owners = List(""),
viewsToGenerate = SYSTEM_STANDARD_VIEW_ID :: Constant.SYSTEM_OWNER_VIEW_ID :: "_Public" :: "Accountant" :: "Auditor" :: Nil,
viewsToGenerate = SYSTEM_STANDARD_VIEW_ID :: SYSTEM_OWNER_VIEW_ID :: CUSTOM_PUBLIC_VIEW_ID :: SYSTEM_ACCOUNTANT_VIEW_ID :: SYSTEM_AUDITOR_VIEW_ID :: Nil,
bankRoutingScheme = "",
bankRoutingAddress = "",
branchRoutingScheme = "",
@ -55,7 +54,7 @@ object MockedCbsConnector extends ServerSetup
balanceAmount = "",
balanceCurrency = "",
owners = List(""),
viewsToGenerate = SYSTEM_STANDARD_VIEW_ID :: Constant.SYSTEM_OWNER_VIEW_ID :: "_Public" :: "Accountant" :: "Auditor" :: Nil,
viewsToGenerate = SYSTEM_STANDARD_VIEW_ID :: SYSTEM_OWNER_VIEW_ID :: CUSTOM_PUBLIC_VIEW_ID :: SYSTEM_ACCOUNTANT_VIEW_ID :: SYSTEM_AUDITOR_VIEW_ID :: Nil,
bankRoutingScheme = "",
bankRoutingAddress = "",
branchRoutingScheme = "",

View File

@ -27,6 +27,7 @@ TESOBE (http://www.tesobe.com/)
package code.util
import code.api.Constant.SYSTEM_OWNER_VIEW_ID
import code.api.UKOpenBanking.v2_0_0.{APIMethods_UKOpenBanking_200, OBP_UKOpenBanking_200}
import code.api.UKOpenBanking.v3_1_0.{APIMethods_AccountAccessApi, OBP_UKOpenBanking_310}
import code.api.berlin.group.v1_3.OBP_BERLIN_GROUP_1_3
@ -37,6 +38,7 @@ import code.api.v3_1_0.OBPAPI3_1_0
import code.api.v4_0_0.OBPAPI4_0_0.Implementations4_0_0
import code.api.v4_0_0.{OBPAPI4_0_0, V400ServerSetup}
import code.setup.PropsReset
import code.views.system.ViewDefinition
import com.openbankproject.commons.util.ApiVersion
class APIUtilHeavyTest extends V400ServerSetup with PropsReset {
@ -164,5 +166,30 @@ class APIUtilHeavyTest extends V400ServerSetup with PropsReset {
allowedOperationIds5 contains("UKv3.1-createAccountAccessConsents") should be (true)
allowedOperationIds5 contains("UKv3.1-deleteConsent") should be (false)
}
feature("test APIUtil.getPermissionPairFromViewDefinition method") {
scenario(s"Test the getPermissionPairFromViewDefinition method") {
val subList = List(
"can_see_transaction_request_types",
"can_see_available_views_for_bank_account",
"can_see_views_with_permissions_for_all_users",
"can_see_views_with_permissions_for_one_user",
"can_see_transaction_this_bank_account",
"can_see_transaction_other_bank_account",
"can_see_transaction_description",
"can_see_transaction_start_date",
"can_see_transaction_finish_date",
"can_see_bank_account_national_identifier",
"can_see_bank_account_swift_bic"
).toSet
val systemOwnerView = getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID)
val permissions = APIUtil.getViewPermissions(systemOwnerView.asInstanceOf[ViewDefinition])
subList.subsetOf(permissions)
}
}
}

View File

@ -60,7 +60,9 @@ class MappedViewsTest extends ServerSetup with DefaultUsers{
val wrongViewId = "WrongViewId"
val wrongView = MapperViews.getOrCreateSystemViewFromCbs(wrongViewId)
wrongView should equal(Failure(ViewIdNotSupported+ s"Your input viewId is :$wrongViewId"))
wrongView.toString contains ViewIdNotSupported shouldBe (true)
wrongView.toString contains wrongViewId shouldBe(true)
}

View File

@ -75,7 +75,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
<version>32.0.0-jre</version>
</dependency>
</dependencies>

View File

@ -57,7 +57,7 @@ trait ViewSpecification {
}
/*
The JSON that should be supplied to create a view. Conforms to ViewSpecification
The JSON that should be supplied to create a custom view. Conforms to ViewSpecification
*/
case class CreateViewJson(
name: String,
@ -73,7 +73,7 @@ case class CreateViewJson(
/*
The JSON that should be supplied to update a view. Conforms to ViewSpecification
The JSON that should be supplied to update a system view. Conforms to ViewSpecification
*/
case class UpdateViewJSON(
description: String,