diff --git a/.env b/.env new file mode 100644 index 0000000000..3a82a5fa08 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +WIREMOCK_STUBS_DIR=./opba-protocols/xs2a-protocol-tests/xs2a-bdd-wiremock/src/main/resources/mockedsandbox/restrecord/embedded/multi-sca/accounts/postbank \ No newline at end of file diff --git a/.github/workflows/daily-heavy-tests.yml b/.github/workflows/daily-heavy-tests.yml index 7606b64c8d..2ada29c615 100644 --- a/.github/workflows/daily-heavy-tests.yml +++ b/.github/workflows/daily-heavy-tests.yml @@ -7,7 +7,7 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 with: @@ -22,8 +22,17 @@ jobs: with: access_token: ${{ secrets.GITHUB_TOKEN }} + - name: Free disk space + run: | + sudo apt clean + docker rmi $(docker image ls -aq) + docker system prune -af + echo "Available space" + df -h + - name: Build project and run default test suite - run: ./scripts/build_and_test.sh + run: | + ./scripts/build_and_test.sh env: MAVEN_OPTS: "-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120" # Github-on-Azure tweaks - https://github.com/actions/virtual-environments/issues/1499 ENABLE_HEAVY_TESTS: true diff --git a/.github/workflows/deploy-to-integ.yml b/.github/workflows/deploy-to-integ.yml new file mode 100644 index 0000000000..b4a0a67cc6 --- /dev/null +++ b/.github/workflows/deploy-to-integ.yml @@ -0,0 +1,69 @@ +# Direct deploy to INTEG (no release-candidate step) +name: Build and deploy on integ + +on: + workflow_dispatch: + inputs: + gitRef: + description: 'Git reference (commit sha), optional, will use latest develop' + required: false + disableTests: + description: 'Disable tests' + required: true + default: 'false' + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Clone Repository (Latest) + uses: actions/checkout@v2 + if: github.event.inputs.gitRef == '' + + - name: Clone Repository (Custom commit SHA) + uses: actions/checkout@v2 + if: github.event.inputs.gitRef != '' + with: + ref: ${{ github.event.inputs.gitRef }} + + + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Free disk space + run: | + sudo apt clean + docker rmi $(docker image ls -aq) + docker system prune -af + echo "Available space" + df -h + + - name: Build project and run test suite + run: ./scripts/build_and_test.sh + env: + MAVEN_OPTS: "-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120" # Github-on-Azure tweaks - https://github.com/actions/virtual-environments/issues/1499 + MOZ_HEADLESS: 1 + MVN_TESTS_DISABLED: ${{ github.event.inputs.disableTests }} + + - name: Deploy release-candidate artifacts to OpenShift + run: ./scripts/deploy_openshift_release_candidate.sh scripts/service.list + env: + OPENSHIFT_TOKEN: ${{ secrets.OPENSHIFT_TOKEN }} + RELEASE_CANDIDATE_DOMAIN: openshift-registry.adorsys.de + RELEASE_CANDIDATE_PROJECT_NAME: open-banking-gateway-integ + + - name: Build FireFly improter + run: ./scripts/build_firefly_exporter_mvn.sh + env: + MAVEN_OPTS: "-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120" # Github-on-Azure tweaks - https://github.com/actions/virtual-environments/issues/1499 + MVN_TESTS_DISABLED: ${{ github.event.inputs.disableTests }} + + - name: Deploy FireFly Importer release-candidate artifacts to OpenShift + run: ./scripts/deploy_openshift_release_candidate.sh scripts/firefly-service.list + env: + OPENSHIFT_TOKEN: ${{ secrets.OPENSHIFT_TOKEN }} + RELEASE_CANDIDATE_DOMAIN: openshift-registry.adorsys.de + RELEASE_CANDIDATE_PROJECT_NAME: open-banking-gateway-integ diff --git a/.github/workflows/develop-push-merge.yml b/.github/workflows/develop-push-merge.yml index be76e73524..fcbb399d8b 100644 --- a/.github/workflows/develop-push-merge.yml +++ b/.github/workflows/develop-push-merge.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Set up JDK 11 @@ -21,6 +21,14 @@ jobs: with: access_token: ${{ secrets.GITHUB_TOKEN }} + - name: Free disk space + run: | + sudo apt clean + docker rmi $(docker image ls -aq) + docker system prune -af + echo "Available space" + df -h + - name: Build project and run default test suite run: ./scripts/build_and_test.sh env: @@ -53,8 +61,31 @@ jobs: env: OPENSHIFT_TOKEN: ${{ secrets.OPENSHIFT_TOKEN }} - - name: Upload coverage to CodeCov - run: ./scripts/upload_coverage_to_codecov.sh + # CODECOV Upload BEGIN + - uses: codecov/codecov-action@v1 + with: + directory: ./last-module-codecoverage/ + flags: backend + verbose: true + + - uses: codecov/codecov-action@v1 + with: + directory: ./consent-ui/ + flags: frontend + verbose: true + + - uses: codecov/codecov-action@v1 + with: + directory: ./fintech-examples/fintech-last-module-codecoverage/ + flags: fintech + verbose: true + + - uses: codecov/codecov-action@v1 + with: + directory: ./fintech-examples/fintech-ui/ + flags: fintech + verbose: true + # CODECOV Upload END - name: Update GitHub pages run: ./scripts/deploy_doc.sh diff --git a/.github/workflows/manual-heavy-tests.yml b/.github/workflows/manual-heavy-tests.yml new file mode 100644 index 0000000000..8bc8adc092 --- /dev/null +++ b/.github/workflows/manual-heavy-tests.yml @@ -0,0 +1,39 @@ +# Builds manually develop branch using `heavy tests` test suite +name: Develop branch heavy tests MANUAL build + +on: [workflow_dispatch] + +jobs: + build: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + ref: develop + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.5.0 + with: + access_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Free disk space + run: | + sudo apt clean + docker rmi $(docker image ls -aq) + docker system prune -af + echo "Available space" + df -h + + - name: Build project and run default test suite + run: | + (while true; do df -h; sleep 60; done;) & + ./scripts/build_and_test.sh + env: + MAVEN_OPTS: "-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120" # Github-on-Azure tweaks - https://github.com/actions/virtual-environments/issues/1499 + ENABLE_HEAVY_TESTS: true + MOZ_HEADLESS: 1 + diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 9ae842a65f..fd31b08743 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Set up JDK 11 @@ -22,7 +22,20 @@ jobs: with: access_token: ${{ secrets.GITHUB_TOKEN }} + - name: Free disk space + run: | + sudo apt clean + docker rmi $(docker image ls -aq) + docker system prune -af + echo "Available space" + df -h + - name: Build project and run default test suite run: ./scripts/build_and_test.sh env: MAVEN_OPTS: "-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120" # Github-on-Azure tweaks - https://github.com/actions/virtual-environments/issues/1499 + + - name: Build FireFly improter + run: ./scripts/build_firefly_exporter_mvn.sh + env: + MAVEN_OPTS: "-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120" # Github-on-Azure tweaks - https://github.com/actions/virtual-environments/issues/1499 diff --git a/.github/workflows/tag-release-candidate-push.yml b/.github/workflows/tag-release-candidate-push.yml index ee9006308a..5b0bede5b7 100644 --- a/.github/workflows/tag-release-candidate-push.yml +++ b/.github/workflows/tag-release-candidate-push.yml @@ -8,13 +8,22 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Set up JDK 11 uses: actions/setup-java@v1 with: java-version: 11 + + - name: Free disk space + run: | + sudo apt clean + docker rmi $(docker image ls -aq) + docker system prune -af + echo "Available space" + df -h + - name: Build project and run test suite run: ./scripts/build_and_test.sh env: diff --git a/.github/workflows/tag-release-push.yml b/.github/workflows/tag-release-push.yml index 4d5d618b9f..d39dfe806a 100644 --- a/.github/workflows/tag-release-push.yml +++ b/.github/workflows/tag-release-push.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Set up JDK 11 @@ -16,6 +16,14 @@ jobs: with: java-version: 11 + - name: Free disk space + run: | + sudo apt clean + docker rmi $(docker image ls -aq) + docker system prune -af + echo "Available space" + df -h + - name: Promote artifacts from OpenShift to DockerHub run: ./scripts/promote_oc_image_to_dockerhub.sh scripts/service.list env: diff --git a/.gitignore b/.gitignore index 3e31e201c5..8c0ed31ffb 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,9 @@ docs_for_site jgiven-reports logs site/ + +# Disable test profiles +opba-embedded-starter/src/main/resources/test-profiles/ + +# Disable developer secrets +**/application-my-secrets.yml diff --git a/README.md b/README.md index d2bc17657b..ae16edf860 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,24 @@ - Frontend: [![codecov-frontend](https://codecov.io/gh/adorsys/open-banking-gateway/branch/develop/graph/badge.svg?flag=frontend)](https://codecov.io/gh/adorsys/open-banking-gateway) - Example code: [![codecov-examples](https://codecov.io/gh/adorsys/open-banking-gateway/branch/develop/graph/badge.svg?flag=fintech)](https://codecov.io/gh/adorsys/open-banking-gateway) + +# Licensing model change to dual license: _AGPL v.3_ or _commercial license_ + +**Attention: this open-source project will change its licensing model as of _01.01.2022_!** + +Constantly evolving and extending scope, production traffic and support in open banking world call for high maintenance and service investments on our part. + +Henceforth, adorsys will offer all versions higher than v1.0 of Open Banking Gateway under a dual-license model. +Thus, this repository will be available either under Affero GNU General Public License v.3 (AGPL v.3) or alternatively under a commercial license agreement. + +We would like to thank all our users for their trust so far and are convinced that we will be able to provide an even better service going forward. + +For more information, advice for your implementation project or if your use case requires more time to adapt this change, +please contact us at [psd2@adorsys.com](mailto:psd2@adorsys.com). + +For additional details please see the section [FAQ on Licensing Change](#faq-on-licensing-change). + + # Open Banking Gateway Provides tools, adapters and connectors for transparent access to open banking apis. The initial effort focuses on the connectivity to banks that implement the European PSD2 directive either through one of the common market initiatives like : [The Berlin Group NextGenPSD2](https://www.berlin-group.org/psd2-access-to-bank-accounts), [The Open Banking UK](https://www.openbanking.org.uk/), [The Polish PSD2 API](https://polishapi.org/en/) or even through proprietary bank api like [the ING’s PSD2 API](https://developer.ing.com/openbanking/). @@ -35,7 +53,7 @@ In the Open Banking Context, a payment service user (PSU or banking account hold ## Big Picture The following picture displays the overall architecture of this banking gateway: -![High level architecture](docs/img/open-banking-gateway-arch-14-01-2020.png) +![High level architecture](docs/img/big-picture.png) ## Security concept @@ -75,6 +93,19 @@ The following picture displays the overall technical architecture concept of thi - [docker-compose-dev.yml](https://github.com/adorsys/open-banking-gateway/tree/develop/docker-compose-dev.yml) - docker-compose file in the project root for **Development** (requires building docker images) - [docker-compose.yml](https://github.com/adorsys/open-banking-gateway/tree/develop/docker-compose.yml) - docker-compose file in the project root for **Demo** (Images will be pulled from DockerHub) +## Postman scripts to play with API + +- [postman-collections](postman/collections) +- [postman-environments](postman/environments) (for playing use [this Postman environment](https://github.com/adorsys/open-banking-gateway/tree/develop/postman/environments/OPBA-DEV-NO-SIG.postman_environment.json)) + +### Postman collection details + +- [postman-ais-collection](https://github.com/adorsys/open-banking-gateway/tree/develop/postman/collections/OPBA-AIS-HBCI-OR-XS2A-EMBEDDED-2-SCA-METHODS.postman_collection.json) Xs2a-embedded or HBCI AIS (account information services) example - getting users' account and transactions list + +**Note:** Postman requires disabled request signing functionality - for that use Spring-profile `no-signature-filter`. +You can use our DEV environment (without signature check) if you import [this Postman environment](https://github.com/adorsys/open-banking-gateway/tree/develop/postman/environments/OPBA-DEV-NO-SIG.postman_environment.json) + + ## Information for developers: - Working with BPMN: As most protocols use BPMN, we have developed @@ -113,6 +144,7 @@ This project is designed to enable contribution from different sources, as the o * [Getting started](docs/getting_started.md) * [Contribution Guidelines](docs/ContributionGuidelines.md) + ## Authors & Contact * **[Francis Pouatcha](mailto:fpo@adorsys.de)** - *Initial work* - [adorsys](https://www.adorsys.de) @@ -121,7 +153,93 @@ See also the list of [contributors](https://github.com/adorsys/open-banking-gate For commercial support please contact **[adorsys Team](https://adorsys.de/)**. + ## License -This project is licensed under the Apache License version 2.0 - see the [LICENSE](LICENSE) file for details +This project is licensed **(until 01.01.2022)** under the Apache License version 2.0 - see the [LICENSE](LICENSE) file for details + + +## FAQ on Licensing Change + +### What is a dual-licensing model? + +Under a dual-licensing model, our product is available under two licenses: + +- [The Affero GNU General Public License v3 (AGPL v3)](https://www.gnu.org/licenses/agpl-3.0.en.html) +- A proprietary commercial license + +If you are a developer or business that would like to review our products in detail, test and implement in your +open-source projects and share the changes back to the community, the product repository is freely available under AGPL v3. + +If you are a business that would like to implement our products in a commercial setting and would like to protect your +individual changes, we offer the option to license our products under a commercial license. + +This change will still allow free access and ensure openness under AGPL v3 but with assurance of committing any +alterations or extensions back to the project and preventing redistribution of such implementations under commercial license. + + + +### Will there be any differences between the open-source and commercially licensed versions of your products? + +Our public release frequency will be reduced as our focus shifts towards the continuous maintenance of the commercial +version. Nevertheless, we are committed to also provide open-source releases of our products on a regular basis as per our release policy. + +For customers with a commercial license, we will offer new intermediate releases in a more frequent pace. + + + +### Does this mean that this product is no longer open source? + +No, the product will still be published and available on GitHub under an OSI-approved open-source license (AGPL v3). + + + +### What about adorsys’ commitment to open source? Will adorsys provide future product releases on GitHub? + +We at adorsys are committed to continue actively participating in the open-source community. Our products remain +licensed under OSI-approved open-source licenses, and we are looking forward to expanding our product portfolio on GitHub even further. + + + +### How does the change impact me if I already use the open-source edition of your product? + +All currently published versions until v1.0 will remain under their current Apache 2.0 license and its respective +requirements and you may continue using it as-is. To upgrade to future versions, you will be required to either abide +by the requirements of AGPL v3, including documenting and sharing your implemented changes to the product when +distributing, or alternatively approach us to obtain a commercial license. + + + +### What if I cannot adjust to the new licensing model until 01.01.2022? Can I extend the deadline? + +We understand that adjustment to licensing changes can take time and therefore are open to discuss extension options on +an individual basis. For inquiries please contact us at [psd2@adorsys.com](mailto:psd2@adorsys.com). + + + +### Which versions of the product are affected? + +All versions of Open Banking Gateway after v1.0 will be affected by the licensing changes and move to a dual-licensing model. + + + +### What will happen to older, Apache 2.0 licensed product versions? + +All older Apache 2.0 licensed versions prior and including v1.0 will remain available under their existing license. + + + +### What open-source products from Adorsys are affected by the licensing change? + +The following products are affected: + - [XS2A Core](https://github.com/adorsys/xs2a) + - [XS2A Sandbox & ModelBank](https://github.com/adorsys/XS2A-Sandbox) + - [Open Banking Gateway](https://github.com/adorsys/open-banking-gateway) incl. [XS2A Adapters](https://github.com/adorsys/xs2a-adapter) + - [SmartAnalytics](https://github.com/adorsys/smartanalytics) + - [Datasafe](https://github.com/adorsys/datasafe) + + +### I’m using one of these products indirectly via some software integrator. How does the licensing change affect me? +The licensing change does not affect you as user, but it is relevant to your provider who has used our product in their +solution implementation. In case of uncertainty please contact your service provider or approach us at [psd2@adorsys.com](mailto:psd2@adorsys.com). diff --git a/consent-ui/src/app/ais/ais-routing.module.ts b/consent-ui/src/app/ais/ais-routing.module.ts index 2726fd4701..a35dd0ef30 100644 --- a/consent-ui/src/app/ais/ais-routing.module.ts +++ b/consent-ui/src/app/ais/ais-routing.module.ts @@ -14,6 +14,7 @@ import { EntryPageTransactionsComponent } from './entry-page/initiation/transact import { TransactionsConsentReviewComponent } from './entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component'; import { ToAspspRedirectionComponent } from './to-aspsp-page/to-aspsp-redirection.component'; import { ConsentSharingComponent } from './entry-page/initiation/consent-sharing/consent-sharing.component'; +import { WaitForDecoupled } from "./wait-for-decoupled/wait-for-decoupled"; const routes: Routes = [ { @@ -39,6 +40,7 @@ const routes: Routes = [ ] }, { path: ToAspspRedirectionComponent.ROUTE, component: ToAspspRedirectionComponent }, + { path: WaitForDecoupled.ROUTE, component: WaitForDecoupled }, { path: ResultPageComponent.ROUTE, component: ResultPageComponent }, { path: 'authenticate', diff --git a/consent-ui/src/app/ais/ais.module.ts b/consent-ui/src/app/ais/ais.module.ts index 7f776f5e96..04ae59d488 100644 --- a/consent-ui/src/app/ais/ais.module.ts +++ b/consent-ui/src/app/ais/ais.module.ts @@ -7,7 +7,6 @@ import { EnterPinPageComponent } from './enter-pin-page/enter-pin-page.component import { EntryPageComponent } from './entry-page/entry-page.component'; import { ScaSelectPageComponent } from './sca-select-page/sca-select-page.component'; import { EnterTanPageComponent } from './enter-tan-page/enter-tan-page.component'; -import { AccountDetailsComponent } from './common/account-details/account-details.component'; import { ErrorPageComponent } from './error-page/error-page.component'; import { RouteBasedCardWithSidebarComponent } from './route-based-card-with-sidebar/route-based-card-with-sidebar.component'; import { SidebarComponent } from './sidebar/sidebar.component'; @@ -24,6 +23,7 @@ import { ToAspspRedirectionComponent } from './to-aspsp-page/to-aspsp-redirectio import { ConsentInfoComponent } from './components/consent-info/consent-info.component'; import { ConsentSharingComponent } from './entry-page/initiation/consent-sharing/consent-sharing.component'; import { SharedModule } from '../common/shared.module'; +import { WaitForDecoupled } from "./wait-for-decoupled/wait-for-decoupled"; @NgModule({ declarations: [ @@ -32,7 +32,6 @@ import { SharedModule } from '../common/shared.module'; EntryPageComponent, ScaSelectPageComponent, EnterTanPageComponent, - AccountDetailsComponent, ConsentAccountAccessSelectionComponent, EntryPageTransactionsComponent, ErrorPageComponent, @@ -47,7 +46,8 @@ import { SharedModule } from '../common/shared.module'; EntryPageAccountsComponent, ToAspspRedirectionComponent, ConsentInfoComponent, - ConsentSharingComponent + ConsentSharingComponent, + WaitForDecoupled ], imports: [SharedModule, AisRoutingModule, AngularIbanModule] }) diff --git a/consent-ui/src/app/ais/common/account-details/account-details.component.html b/consent-ui/src/app/ais/common/account-details/account-details.component.html deleted file mode 100644 index 8eeff2890d..0000000000 --- a/consent-ui/src/app/ais/common/account-details/account-details.component.html +++ /dev/null @@ -1 +0,0 @@ -

account-details works!

diff --git a/consent-ui/src/app/ais/common/account-details/account-details.component.spec.ts b/consent-ui/src/app/ais/common/account-details/account-details.component.spec.ts deleted file mode 100644 index ab75813700..0000000000 --- a/consent-ui/src/app/ais/common/account-details/account-details.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { AccountDetailsComponent } from './account-details.component'; - -describe('AccountDetailsComponent', () => { - let component: AccountDetailsComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ AccountDetailsComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(AccountDetailsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/consent-ui/src/app/ais/common/account-details/account-details.component.ts b/consent-ui/src/app/ais/common/account-details/account-details.component.ts deleted file mode 100644 index be1e250aa6..0000000000 --- a/consent-ui/src/app/ais/common/account-details/account-details.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'consent-app-account-details', - templateUrl: './account-details.component.html', - styleUrls: ['./account-details.component.scss'] -}) -export class AccountDetailsComponent implements OnInit { - - constructor() { } - - ngOnInit() { - } - -} diff --git a/consent-ui/src/app/ais/common/consent-util.ts b/consent-ui/src/app/ais/common/consent-util.ts index c2a072c6ea..4a08ed228d 100644 --- a/consent-ui/src/app/ais/common/consent-util.ts +++ b/consent-ui/src/app/ais/common/consent-util.ts @@ -10,6 +10,20 @@ export class ConsentUtil { return storageService.getConsentObject(authorizationId, () => new AisConsentToGrant()); } + public static rollbackConsent(authorizationId: string, sessionService: SessionService) { + const consentObj = ConsentUtil.getOrDefault(authorizationId, sessionService); + consentObj.consent.access.availableAccounts = null; + consentObj.consent.access.allPsd2 = null; + consentObj.consent.access.accounts = null; + consentObj.consent.access.balances = null; + consentObj.consent.access.transactions = null; + sessionService.setConsentObject(authorizationId, consentObj); + } + + public static isEmptyObject(obj: Object) { + return null === obj || !obj || (Object.keys(obj).length === 0); + } + private static initializeConsentObject(): AisConsentToGrant { const aisConsent = new AisConsentToGrant(); // FIXME: These fields MUST be initialized by FinTech through API and user can only adjust it. diff --git a/consent-ui/src/app/ais/common/constant/constant.ts b/consent-ui/src/app/ais/common/constant/constant.ts new file mode 100644 index 0000000000..217214127f --- /dev/null +++ b/consent-ui/src/app/ais/common/constant/constant.ts @@ -0,0 +1,3 @@ +export const DATA_PATTERN = '\\d{4}-\\d{2}-\\d{2}'; +// The consent can be used max times per day +export const MAX_FREQUENCY_PER_DAY = 999; diff --git a/consent-ui/src/app/ais/common/date-util.ts b/consent-ui/src/app/ais/common/date-util.ts new file mode 100644 index 0000000000..a98d793262 --- /dev/null +++ b/consent-ui/src/app/ais/common/date-util.ts @@ -0,0 +1,25 @@ +import {AbstractControl, ValidatorFn} from '@angular/forms'; + +export class DateUtil { + public static getActualDate(): string { + const result = new Date(); + result.setDate(result.getDate()); + return result.toISOString().split('T')[0]; + } + + public static isDateNotInThePastValidator(): ValidatorFn { + return (control: AbstractControl): { [key: string]: boolean } | null => { + + if(!(control && control.value)) { + return null; + } + + const actualDate = new Date(this.getActualDate()); + const date = new Date(control.value); + + return date < actualDate + ? { invalidDate: true } + : null; + } + } +} diff --git a/consent-ui/src/app/ais/common/dto/ais-consent.ts b/consent-ui/src/app/ais/common/dto/ais-consent.ts index c5acbeb1fd..c987b45478 100644 --- a/consent-ui/src/app/ais/common/dto/ais-consent.ts +++ b/consent-ui/src/app/ais/common/dto/ais-consent.ts @@ -1,3 +1,6 @@ +import {ConsentAuth} from '../../../api'; +import SupportedType = ConsentAuth.SupportedConsentTypesEnum; + export class AisConsentToGrant { level: AccountAccessLevel; consent: AisConsent; @@ -40,3 +43,11 @@ export enum AccountAccessLevel { ALL_ACCOUNTS_WITH_BALANCES = 'ALL_ACCOUNTS_WITH_BALANCES', FINE_GRAINED = 'FINE_GRAINED' } + + +export const AccountAccessLevelAspspConsentSupport = new Map>([ + [AccountAccessLevel.ALL_ACCOUNTS, new Set([SupportedType.GLOBALACCOUNTS, SupportedType.GLOBALALL])], + [AccountAccessLevel.ALL_PSD2, new Set([SupportedType.GLOBALALL])], + [AccountAccessLevel.ALL_ACCOUNTS_WITH_BALANCES, new Set([SupportedType.GLOBALALL])], + [AccountAccessLevel.FINE_GRAINED, new Set([SupportedType.DEDICATEDALL, SupportedType.GLOBALALL, SupportedType.GLOBALACCOUNTS])] +]); diff --git a/consent-ui/src/app/ais/components/consent-info/consent-info.component.ts b/consent-ui/src/app/ais/components/consent-info/consent-info.component.ts index f5827c1a49..714a410b3c 100644 --- a/consent-ui/src/app/ais/components/consent-info/consent-info.component.ts +++ b/consent-ui/src/app/ais/components/consent-info/consent-info.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { AccountAccessLevel, AisConsentToGrant } from '../../common/dto/ais-consent'; -import { StubUtil } from '../../../common/utils/stub-util'; import { ActivatedRoute, Router } from '@angular/router'; import { SessionService } from '../../../common/session.service'; import { ConsentUtil } from '../../common/consent-util'; diff --git a/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.html b/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.html index c5189aa56a..2d1f7dc85c 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.html +++ b/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.html @@ -6,6 +6,7 @@

Access permission for accounts in {{ aspspName }}

Review an account access level for {{ finTechName }}

+
@@ -33,23 +34,65 @@

Access permission for accounts in {{ aspspName }}

-
+
approval

- The consent will be valid until {{ aisConsent.consent.validUntil }} + The consent will be valid until:

+
-
+
24_hours

- The consent can be used {{ aisConsent.consent.frequencyPerDay }} times per day + The consent can be used:

+ + times per day +
+
+ + +
+
+ + The value must be greater than or equal to 1. + + + Incorrect date format or date (past dates cannot be used). +
+
- +
diff --git a/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.ts b/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.ts index 8a9d164410..bd02c168b5 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.ts +++ b/consent-ui/src/app/ais/entry-page/initiation/accounts/accounts-consent-review/accounts-consent-review.component.ts @@ -4,11 +4,13 @@ import { SharedRoutes } from '../../common/shared-routes'; import { AccountAccessLevel, AisConsentToGrant } from '../../../../common/dto/ais-consent'; import { StubUtil } from '../../../../../common/utils/stub-util'; import { ActivatedRoute, Router } from '@angular/router'; -import { FormBuilder } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { SessionService } from '../../../../../common/session.service'; import { ConsentUtil } from '../../../../common/consent-util'; import { ApiHeaders } from '../../../../../api/api.headers'; import { ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest } from '../../../../../api'; +import { DATA_PATTERN, MAX_FREQUENCY_PER_DAY } from '../../../../common/constant/constant'; +import { DateUtil } from '../../../../common/date-util'; @Component({ selector: 'consent-app-accounts-consent-review', @@ -16,6 +18,9 @@ import { ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest } from ' styleUrls: ['./accounts-consent-review.component.scss'] }) export class AccountsConsentReviewComponent implements OnInit { + + consentReviewForm: FormGroup; + constructor( private location: Location, private router: Router, @@ -29,6 +34,7 @@ export class AccountsConsentReviewComponent implements OnInit { accountAccessLevel = AccountAccessLevel; + public actualDate: string; public finTechName: string; public aspspName: string; public aisConsent: AisConsentToGrant; @@ -41,10 +47,22 @@ export class AccountsConsentReviewComponent implements OnInit { this.aisConsent = ConsentUtil.getOrDefault(this.authorizationId, this.sessionService); this.aspspName = this.sessionService.getBankName(res.authId); this.finTechName = this.sessionService.getFintechName(res.authId); + this.actualDate = DateUtil.getActualDate(); }); + this.createForm(); } onConfirm() { + if (this.consentReviewForm.invalid) { + return; + } + + this.aisConsent.consent.recurringIndicator = this.consentReviewForm.value.recurringIndicator; + this.aisConsent.consent.validUntil = this.consentReviewForm.value.validUntilDate; + this.aisConsent.consent.frequencyPerDay = this.consentReviewForm.value.frequencyPerDay; + + this.sessionService.setConsentObject(this.authorizationId, this.aisConsent); + const body = { extras: this.aisConsent.extras } as PsuAuthRequest; if (this.aisConsent) { @@ -54,14 +72,13 @@ export class AccountsConsentReviewComponent implements OnInit { this.updateConsentAuthorizationService .embeddedUsingPOST( this.authorizationId, - StubUtil.X_XSRF_TOKEN, StubUtil.X_REQUEST_ID, this.sessionService.getRedirectCode(this.authorizationId), body, 'response' ) .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); window.location.href = res.headers.get(ApiHeaders.LOCATION); }); } @@ -69,4 +86,34 @@ export class AccountsConsentReviewComponent implements OnInit { onBack() { this.location.back(); } + + private createForm() { + this.consentReviewForm = this.formBuilder.group({ + recurringIndicator: this.aisConsent.consent.recurringIndicator, + validUntilDate: [ + this.aisConsent.consent.validUntil, + [ + Validators.required, + Validators.pattern(DATA_PATTERN), + DateUtil.isDateNotInThePastValidator() + ] + ], + frequencyPerDay: [ + this.aisConsent.consent.frequencyPerDay, + [ + Validators.required, + Validators.min(1), + Validators.max(MAX_FREQUENCY_PER_DAY) + ] + ] + }) + } + + get validUntilDate() { + return this.consentReviewForm.get('validUntilDate') + } + + get frequencyPerDay() { + return this.consentReviewForm.get('frequencyPerDay') + } } diff --git a/consent-ui/src/app/ais/entry-page/initiation/accounts/entry-page-accounts/entry-page-accounts.component.ts b/consent-ui/src/app/ais/entry-page/initiation/accounts/entry-page-accounts/entry-page-accounts.component.ts index 4053ef675d..db0dc1d448 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/accounts/entry-page-accounts/entry-page-accounts.component.ts +++ b/consent-ui/src/app/ais/entry-page/initiation/accounts/entry-page-accounts/entry-page-accounts.component.ts @@ -1,8 +1,8 @@ -import { Component, OnInit } from '@angular/core'; -import { Access } from '../../common/initial-consent/consent-account-access-selection.component'; -import { AccountsConsentReviewComponent } from '../accounts-consent-review/accounts-consent-review.component'; -import { DedicatedAccessComponent } from '../../common/dedicated-access/dedicated-access.component'; -import { AccountAccessLevel } from '../../../../common/dto/ais-consent'; +import {Component, OnInit} from '@angular/core'; +import {Access} from '../../common/initial-consent/consent-account-access-selection.component'; +import {AccountsConsentReviewComponent} from '../accounts-consent-review/accounts-consent-review.component'; +import {DedicatedAccessComponent} from '../../common/dedicated-access/dedicated-access.component'; +import {AccountAccessLevel} from '../../../../common/dto/ais-consent'; @Component({ selector: 'consent-app-entry-page-accounts', diff --git a/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.spec.ts b/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.spec.ts index 1c7eb39076..7f8615d4db 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.spec.ts +++ b/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.spec.ts @@ -64,7 +64,7 @@ describe('DedicatedAccessComponent', () => { }, frequencyPerDay: 24, recurringIndicator: true, - validUntil: '2021-06-24' + validUntil: '2099-06-24' } }; consentUtilSpy = spyOn(ConsentUtil, 'getOrDefault').and.returnValue(mockData); diff --git a/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.ts b/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.ts index c1c64657bc..f073aeb8d0 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.ts +++ b/consent-ui/src/app/ais/entry-page/initiation/common/dedicated-access/dedicated-access.component.ts @@ -61,14 +61,7 @@ export class DedicatedAccessComponent implements OnInit { } onBack() { - const consentObj = ConsentUtil.getOrDefault(this.authorizationId, this.sessionService); - consentObj.consent.access.availableAccounts = null; - consentObj.consent.access.allPsd2 = null; - consentObj.consent.access.accounts = null; - consentObj.consent.access.balances = null; - consentObj.consent.access.transactions = null; - this.sessionService.setConsentObject(this.authorizationId, consentObj); - + ConsentUtil.rollbackConsent(this.authorizationId, this.sessionService); this.location.back(); } diff --git a/consent-ui/src/app/ais/entry-page/initiation/common/initial-consent/consent-account-access-selection.component.html b/consent-ui/src/app/ais/entry-page/initiation/common/initial-consent/consent-account-access-selection.component.html index 7ace1e54ba..d7e1df22c0 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/common/initial-consent/consent-account-access-selection.component.html +++ b/consent-ui/src/app/ais/entry-page/initiation/common/initial-consent/consent-account-access-selection.component.html @@ -19,7 +19,7 @@

Access permission for accounts in {{ aspspName }}

Please choose an account access level for {{ finTechName }}

-
+
-
+
approval

- The consent will be valid until {{ aisConsent.consent.validUntil }} + The consent will be valid until:

+
-
+
24_hours

- The consent can be used {{ aisConsent.consent.frequencyPerDay }} times per day + The consent can be used:

+ + times per day +
+
+ + +
+
+ + The value must be greater than or equal to 1. + + + Incorrect date format or date (past dates cannot be used). +
+
- +
diff --git a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts index 15f48bbe93..ea22e5c79c 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts +++ b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.spec.ts @@ -42,6 +42,7 @@ describe('TransactionsConsentReviewComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(TransactionsConsentReviewComponent); component = fixture.componentInstance; + component.ngOnInit(); fixture.detectChanges(); consentAuthorizationService = TestBed.inject(UpdateConsentAuthorizationService); }); @@ -56,6 +57,7 @@ describe('TransactionsConsentReviewComponent', () => { expect(location.back).toHaveBeenCalled(); }); + // FIXME Disabled as DateUtil.isDateNotInThePastValidator seem to cause 'undefined' error in control validation it('should confirm transaction when confirm button is pressed', () => { consentAuthorizationServiceSpy = spyOn(consentAuthorizationService, 'embeddedUsingPOST').and.returnValue(of()); component.onConfirm(); diff --git a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts index 05cac4b2ab..3285d5d9c2 100644 --- a/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts +++ b/consent-ui/src/app/ais/entry-page/initiation/transactions/transactions-consent-review/transactions-consent-review.component.ts @@ -1,14 +1,16 @@ -import { Component, OnInit } from '@angular/core'; -import { Location } from '@angular/common'; -import { SharedRoutes } from '../../common/shared-routes'; -import { ActivatedRoute, Router } from '@angular/router'; -import { FormBuilder } from '@angular/forms'; -import { SessionService } from '../../../../../common/session.service'; -import { AccountAccessLevel, AisConsentToGrant } from '../../../../common/dto/ais-consent'; -import { StubUtil } from '../../../../../common/utils/stub-util'; -import { ConsentUtil } from '../../../../common/consent-util'; -import { ApiHeaders } from '../../../../../api/api.headers'; -import { ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest } from '../../../../../api'; +import {Component, OnInit} from '@angular/core'; +import {Location} from '@angular/common'; +import {SharedRoutes} from '../../common/shared-routes'; +import {ActivatedRoute, Router} from '@angular/router'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {SessionService} from '../../../../../common/session.service'; +import {AccountAccessLevel, AisConsentToGrant} from '../../../../common/dto/ais-consent'; +import {StubUtil} from '../../../../../common/utils/stub-util'; +import {ConsentUtil} from '../../../../common/consent-util'; +import {ApiHeaders} from '../../../../../api/api.headers'; +import {ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest} from '../../../../../api'; +import {DATA_PATTERN, MAX_FREQUENCY_PER_DAY} from '../../../../common/constant/constant'; +import {DateUtil} from '../../../../common/date-util'; @Component({ selector: 'consent-app-transactions-consent-review', @@ -16,6 +18,9 @@ import { ConsentAuth, UpdateConsentAuthorizationService, PsuAuthRequest } from ' styleUrls: ['./transactions-consent-review.component.scss'] }) export class TransactionsConsentReviewComponent implements OnInit { + + consentReviewForm: FormGroup; + constructor( private location: Location, private router: Router, @@ -28,9 +33,9 @@ export class TransactionsConsentReviewComponent implements OnInit { public static ROUTE = SharedRoutes.REVIEW; accountAccessLevel = AccountAccessLevel; + public actualDate: string; public finTechName: string; public aspspName: string; - public aisConsent: AisConsentToGrant; private authorizationId: string; @@ -41,10 +46,22 @@ export class TransactionsConsentReviewComponent implements OnInit { this.aspspName = this.sessionService.getBankName(res.authId); this.finTechName = this.sessionService.getFintechName(res.authId); this.aisConsent = ConsentUtil.getOrDefault(this.authorizationId, this.sessionService); + this.actualDate = DateUtil.getActualDate(); }); + this.createForm(); } onConfirm() { + if (this.consentReviewForm.invalid) { + return; + } + + this.aisConsent.consent.recurringIndicator = this.consentReviewForm.value.recurringIndicator; + this.aisConsent.consent.validUntil = this.consentReviewForm.value.validUntilDate; + this.aisConsent.consent.frequencyPerDay = this.consentReviewForm.value.frequencyPerDay; + + this.sessionService.setConsentObject(this.authorizationId, this.aisConsent); + const body = { extras: this.aisConsent.extras } as PsuAuthRequest; if (this.aisConsent) { @@ -54,14 +71,13 @@ export class TransactionsConsentReviewComponent implements OnInit { this.updateConsentAuthorizationService .embeddedUsingPOST( this.authorizationId, - StubUtil.X_XSRF_TOKEN, StubUtil.X_REQUEST_ID, this.sessionService.getRedirectCode(this.authorizationId), body, 'response' ) .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); window.location.href = res.headers.get(ApiHeaders.LOCATION); }); } @@ -69,4 +85,34 @@ export class TransactionsConsentReviewComponent implements OnInit { onBack() { this.location.back(); } + + private createForm() { + this.consentReviewForm = this.formBuilder.group({ + recurringIndicator: this.aisConsent.consent.recurringIndicator, + validUntilDate: [ + this.aisConsent.consent.validUntil, + [ + Validators.required, + Validators.pattern(DATA_PATTERN), + DateUtil.isDateNotInThePastValidator() + ] + ], + frequencyPerDay: [ + this.aisConsent.consent.frequencyPerDay, + [ + Validators.required, + Validators.min(1), + Validators.max(MAX_FREQUENCY_PER_DAY) + ] + ] + }) + } + + get validUntilDate() { + return this.consentReviewForm.get('validUntilDate') + } + + get frequencyPerDay() { + return this.consentReviewForm.get('frequencyPerDay') + } } diff --git a/consent-ui/src/app/ais/result-page/result-page.component.ts b/consent-ui/src/app/ais/result-page/result-page.component.ts index 5d1ca6f5f6..08ceede21d 100644 --- a/consent-ui/src/app/ais/result-page/result-page.component.ts +++ b/consent-ui/src/app/ais/result-page/result-page.component.ts @@ -1,14 +1,13 @@ -import { Component, OnInit } from '@angular/core'; -import { Location } from '@angular/common'; -import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; +import {Component, OnInit} from '@angular/core'; +import {Location} from '@angular/common'; +import {ActivatedRoute, ActivatedRouteSnapshot} from '@angular/router'; -import { StubUtil } from '../../common/utils/stub-util'; -import { AisConsentToGrant } from '../common/dto/ais-consent'; -import { SessionService } from '../../common/session.service'; -import { ConsentUtil } from '../common/consent-util'; -import { ApiHeaders } from '../../api/api.headers'; -import { UpdateConsentAuthorizationService } from '../../api'; -import { AuthStateConsentAuthorizationService, DenyRequest } from '../../api'; +import {StubUtil} from '../../common/utils/stub-util'; +import {AisConsentToGrant} from '../common/dto/ais-consent'; +import {SessionService} from '../../common/session.service'; +import {ConsentUtil} from '../common/consent-util'; +import {ApiHeaders} from '../../api/api.headers'; +import {AuthStateConsentAuthorizationService, UpdateConsentAuthorizationService} from '../../api'; @Component({ selector: 'consent-app-result-page', @@ -53,8 +52,6 @@ export class ResultPageComponent implements OnInit { .denyUsingPOST( this.authorizationId, StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs - {} as DenyRequest, 'response' ) .subscribe((res) => { @@ -65,7 +62,7 @@ export class ResultPageComponent implements OnInit { private loadRedirectUri(authId: string, redirectCode: string) { this.authStateConsentAuthorizationService.authUsingGET(authId, redirectCode, 'response').subscribe((res) => { console.log(res); - this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); this.redirectTo = res.headers.get(ApiHeaders.LOCATION); }); } diff --git a/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts b/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts index 631213e61d..c78e061771 100644 --- a/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts +++ b/consent-ui/src/app/ais/to-aspsp-page/to-aspsp-redirection.component.ts @@ -1,15 +1,15 @@ -import { Component, OnInit } from '@angular/core'; -import { Location } from '@angular/common'; -import { ActivatedRoute } from '@angular/router'; +import {Component, OnInit} from '@angular/core'; +import {Location} from '@angular/common'; +import {ActivatedRoute} from '@angular/router'; -import { AisConsentToGrant } from '../common/dto/ais-consent'; -import { StubUtil } from '../../common/utils/stub-util'; -import { SessionService } from '../../common/session.service'; -import { ConsentUtil } from '../common/consent-util'; -import { ApiHeaders } from '../../api/api.headers'; -import { Action } from '../../common/utils/action'; -import { AuthStateConsentAuthorizationService, DenyRequest, UpdateConsentAuthorizationService } from '../../api'; -import { combineLatest } from 'rxjs'; +import {AisConsentToGrant} from '../common/dto/ais-consent'; +import {StubUtil} from '../../common/utils/stub-util'; +import {SessionService} from '../../common/session.service'; +import {ConsentUtil} from '../common/consent-util'; +import {ApiHeaders} from '../../api/api.headers'; +import {Action} from '../../common/utils/action'; +import {AuthStateConsentAuthorizationService, UpdateConsentAuthorizationService} from '../../api'; +import {combineLatest} from 'rxjs'; @Component({ selector: 'consent-app-to-aspsp-redirection', @@ -57,7 +57,7 @@ export class ToAspspRedirectionComponent implements OnInit { this.authStateConsentAuthorizationService .authUsingGET(this.authorizationId, this.sessionService.getRedirectCode(this.authorizationId), 'response') .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); this.redirectTo = res.headers.get(ApiHeaders.LOCATION); }); } @@ -67,8 +67,6 @@ export class ToAspspRedirectionComponent implements OnInit { .denyUsingPOST( this.authorizationId, StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs - {} as DenyRequest, 'response' ) .subscribe((res) => { diff --git a/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.html b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.html new file mode 100644 index 0000000000..29201e5112 --- /dev/null +++ b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.html @@ -0,0 +1,10 @@ +
+
+
+

Confirm consent on your application

+

+ {{authResponse?.scaMethodSelected.explanation}} +

+
+
+
diff --git a/consent-ui/src/app/ais/common/account-details/account-details.component.scss b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.scss similarity index 100% rename from consent-ui/src/app/ais/common/account-details/account-details.component.scss rename to consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.scss diff --git a/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.ts b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.ts new file mode 100644 index 0000000000..9c31723ee6 --- /dev/null +++ b/consent-ui/src/app/ais/wait-for-decoupled/wait-for-decoupled.ts @@ -0,0 +1,64 @@ +import {Component, OnInit} from '@angular/core'; +import {ActivatedRoute} from "@angular/router"; +import {ApiHeaders} from "../../api/api.headers"; +import {SessionService} from "../../common/session.service"; +import { + AuthStateConsentAuthorizationService, + ConsentAuth, + PsuAuthRequest, + UpdateConsentAuthorizationService +} from '../../api'; +import {StubUtil} from '../../common/utils/stub-util'; +import {delay, repeatWhen, shareReplay, single, switchMap, takeUntil, tap} from "rxjs/operators"; +import {interval, Observable, of, Subject} from "rxjs"; + +@Component({ + selector: 'wait-for-decoupled-redirection', + templateUrl: './wait-for-decoupled.html', + styleUrls: ['./wait-for-decoupled.scss'] +}) +export class WaitForDecoupled implements OnInit { + public static ROUTE = 'wait-sca-finalization'; + + private readonly POLLING_DELAY_MS = 3000; + + authResponse: ConsentAuth | undefined; + + private authId: string; + + private decoupledCompleted = new Subject(); + + constructor( + private consentAuthorizationService: UpdateConsentAuthorizationService, + private consentStatusService: AuthStateConsentAuthorizationService, + private sessionService: SessionService, + private activatedRoute: ActivatedRoute + ) { + const route = this.activatedRoute.snapshot; + this.authId = route.parent.params.authId; + this.sessionService.setRedirectCode(this.authId, route.queryParams.redirectCode); + } + + ngOnInit() { + of(true) + .pipe(switchMap(_ => this.consentAuthorizationService.embeddedUsingPOST( + this.authId, + StubUtil.X_REQUEST_ID, + this.sessionService.getRedirectCode(this.authId), + {} as PsuAuthRequest, + 'response' + ))) + .pipe(repeatWhen(completed => completed.pipe(delay(this.POLLING_DELAY_MS))), tap()) + .pipe(takeUntil(this.decoupledCompleted)) + .subscribe(res => { + if (res.headers.get(ApiHeaders.X_XSRF_TOKEN)) { + this.sessionService.setRedirectCode(this.authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); + } + this.authResponse = res.body; + + if (res.status === 202) { + window.location.href = res.headers.get(ApiHeaders.LOCATION); + } + }); + } +} diff --git a/consent-ui/src/app/api-auth/.openapi-generator/VERSION b/consent-ui/src/app/api-auth/.openapi-generator/VERSION index 078bf8b7dd..ecedc98d1d 100644 --- a/consent-ui/src/app/api-auth/.openapi-generator/VERSION +++ b/consent-ui/src/app/api-auth/.openapi-generator/VERSION @@ -1 +1 @@ -4.2.2 \ No newline at end of file +4.3.1 \ No newline at end of file diff --git a/consent-ui/src/app/api-auth/README.md b/consent-ui/src/app/api-auth/README.md index 6fdf5e362f..e0c9192e33 100644 --- a/consent-ui/src/app/api-auth/README.md +++ b/consent-ui/src/app/api-auth/README.md @@ -92,6 +92,31 @@ export function apiConfigFactory (): Configuration => { export class AppModule {} ``` +``` +// configuring providers with an authentication service that manages your access tokens +import { ApiModule, Configuration } from ''; + +@NgModule({ + imports: [ ApiModule ], + declarations: [ AppComponent ], + providers: [ + { + provide: Configuration, + useFactory: (authService: AuthService) => new Configuration( + { + basePath: environment.apiUrl, + accessToken: authService.getAccessToken.bind(authService) + } + ), + deps: [AuthService], + multi: false + } + ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + ``` import { DefaultApi } from ''; diff --git a/consent-ui/src/app/api-auth/api.module.ts b/consent-ui/src/app/api-auth/api.module.ts index 32aa339db8..322fbd1a03 100644 --- a/consent-ui/src/app/api-auth/api.module.ts +++ b/consent-ui/src/app/api-auth/api.module.ts @@ -10,12 +10,10 @@ import { PsuAuthenticationAndConsentApprovalService } from './api/psuAuthenticat imports: [], declarations: [], exports: [], - providers: [ - PsuAuthenticationService, - PsuAuthenticationAndConsentApprovalService ] + providers: [] }) export class ApiModule { - public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders { + public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders { return { ngModule: ApiModule, providers: [ { provide: Configuration, useFactory: configurationFactory } ] diff --git a/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts b/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts index 80a046ee72..c9e8e2571d 100644 --- a/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts +++ b/consent-ui/src/app/api-auth/api/psuAuthentication.service.ts @@ -17,9 +17,9 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; -import { GeneralError } from '../model/generalError'; -import { LoginResponse } from '../model/loginResponse'; -import { PsuAuthBody } from '../model/psuAuthBody'; +import { GeneralError } from '../model/models'; +import { LoginResponse } from '../model/models'; +import { PsuAuthBody } from '../model/models'; import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; import { Configuration } from '../configuration'; @@ -51,6 +51,42 @@ export class PsuAuthenticationService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** * Login user to open-banking * TBD @@ -59,10 +95,10 @@ export class PsuAuthenticationService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable; - public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>; - public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>; - public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable { + public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public login(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling login.'); } @@ -75,11 +111,14 @@ export class PsuAuthenticationService { headers = headers.set('X-Request-ID', String(xRequestID)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } @@ -94,9 +133,15 @@ export class PsuAuthenticationService { headers = headers.set('Content-Type', httpContentTypeSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.post(`${this.configuration.basePath}/v1/psu/login`, psuAuthBody, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -113,10 +158,10 @@ export class PsuAuthenticationService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable; - public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>; - public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>; - public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable { + public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public registration(xRequestID: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling registration.'); } @@ -129,11 +174,14 @@ export class PsuAuthenticationService { headers = headers.set('X-Request-ID', String(xRequestID)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } @@ -148,9 +196,15 @@ export class PsuAuthenticationService { headers = headers.set('Content-Type', httpContentTypeSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.post(`${this.configuration.basePath}/v1/psu/register`, psuAuthBody, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -167,10 +221,10 @@ export class PsuAuthenticationService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'body', reportProgress?: boolean): Observable; - public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'response', reportProgress?: boolean): Observable>; - public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'events', reportProgress?: boolean): Observable>; - public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public renewalAuthorizationSessionKey(xRequestID: string, authorizationId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling renewalAuthorizationSessionKey.'); } @@ -183,18 +237,27 @@ export class PsuAuthenticationService { headers = headers.set('X-Request-ID', String(xRequestID)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/psu/ais/${encodeURIComponent(String(authorizationId))}/renewal-authorization-session-key`, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts b/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts index 56f46bb9ad..671854eb6f 100644 --- a/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts +++ b/consent-ui/src/app/api-auth/api/psuAuthenticationAndConsentApproval.service.ts @@ -3,7 +3,7 @@ * This API provides PSU login and registration functionality on TPP side. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -11,15 +11,19 @@ */ /* tslint:disable:no-unused-variable member-ordering */ -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpEvent, HttpHeaders, HttpParameterCodec, HttpParams, HttpResponse } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; -import { LoginResponse } from '../model/loginResponse'; -import { PsuAuthBody } from '../model/psuAuthBody'; +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +import { GeneralError } from '../model/models'; +import { LoginResponse } from '../model/models'; +import { PsuAuthBody } from '../model/models'; + +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; -import { BASE_PATH } from '../variables'; -import { Configuration } from '../configuration'; @Injectable({ @@ -47,32 +51,69 @@ export class PsuAuthenticationAndConsentApprovalService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** * Login user to open-banking to perform payment (anonymous to OPBA) * TBD - * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. + * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. * @param authorizationId Authorization session ID to approve * @param redirectCode Redirect code that acts as a password protecting FinTech requested consent specification * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'body', reportProgress?: boolean): Observable; - public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'response', reportProgress?: boolean): Observable>; - public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'events', reportProgress?: boolean): Observable>; - public loginForAnonymousPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public loginForAnonymousApproval(xRequestID: string, authorizationId: string, redirectCode: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { - throw new Error('Required parameter xRequestID was null or undefined when calling loginForAnonymousPaymentApproval.'); + throw new Error('Required parameter xRequestID was null or undefined when calling loginForAnonymousApproval.'); } if (authorizationId === null || authorizationId === undefined) { - throw new Error('Required parameter authorizationId was null or undefined when calling loginForAnonymousPaymentApproval.'); + throw new Error('Required parameter authorizationId was null or undefined when calling loginForAnonymousApproval.'); } if (redirectCode === null || redirectCode === undefined) { - throw new Error('Required parameter redirectCode was null or undefined when calling loginForAnonymousPaymentApproval.'); + throw new Error('Required parameter redirectCode was null or undefined when calling loginForAnonymousApproval.'); } let queryParameters = new HttpParams({encoder: this.encoder}); if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); + queryParameters = this.addToHttpParams(queryParameters, + redirectCode, 'redirectCode'); } let headers = this.defaultHeaders; @@ -80,20 +121,29 @@ export class PsuAuthenticationAndConsentApprovalService { headers = headers.set('X-Request-ID', String(xRequestID)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } - return this.httpClient.post(`${this.configuration.basePath}/v1/psu/pis/${encodeURIComponent(String(authorizationId))}/anonymous`, + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.post(`${this.configuration.basePath}/v1/psu/${encodeURIComponent(String(authorizationId))}/for-approval/anonymous`, null, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -105,17 +155,17 @@ export class PsuAuthenticationAndConsentApprovalService { /** * Login user to open-banking * TBD - * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. + * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. * @param authorizationId Authorization session ID to approve * @param redirectCode Redirect code that acts as a password protecting FinTech requested consent specification * @param psuAuthBody User credentials object * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable; - public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>; - public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>; - public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable { + public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public loginForApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling loginForApproval.'); } @@ -131,7 +181,8 @@ export class PsuAuthenticationAndConsentApprovalService { let queryParameters = new HttpParams({encoder: this.encoder}); if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); + queryParameters = this.addToHttpParams(queryParameters, + redirectCode, 'redirectCode'); } let headers = this.defaultHeaders; @@ -139,11 +190,14 @@ export class PsuAuthenticationAndConsentApprovalService { headers = headers.set('X-Request-ID', String(xRequestID)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } @@ -158,78 +212,16 @@ export class PsuAuthenticationAndConsentApprovalService { headers = headers.set('Content-Type', httpContentTypeSelected); } - return this.httpClient.post(`${this.configuration.basePath}/v1/psu/ais/${encodeURIComponent(String(authorizationId))}/for-approval/login`, - psuAuthBody, - { - params: queryParameters, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Login user to open-banking to perform payment - * TBD - * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. - * @param authorizationId Authorization session ID to approve - * @param redirectCode Redirect code that acts as a password protecting FinTech requested consent specification - * @param psuAuthBody User credentials object - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'body', reportProgress?: boolean): Observable; - public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'response', reportProgress?: boolean): Observable>; - public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe?: 'events', reportProgress?: boolean): Observable>; - public loginForPaymentApproval(xRequestID: string, authorizationId: string, redirectCode: string, psuAuthBody: PsuAuthBody, observe: any = 'body', reportProgress: boolean = false ): Observable { - if (xRequestID === null || xRequestID === undefined) { - throw new Error('Required parameter xRequestID was null or undefined when calling loginForPaymentApproval.'); - } - if (authorizationId === null || authorizationId === undefined) { - throw new Error('Required parameter authorizationId was null or undefined when calling loginForPaymentApproval.'); - } - if (redirectCode === null || redirectCode === undefined) { - throw new Error('Required parameter redirectCode was null or undefined when calling loginForPaymentApproval.'); - } - if (psuAuthBody === null || psuAuthBody === undefined) { - throw new Error('Required parameter psuAuthBody was null or undefined when calling loginForPaymentApproval.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); - } - - let headers = this.defaultHeaders; - if (xRequestID !== undefined && xRequestID !== null) { - headers = headers.set('X-Request-ID', String(xRequestID)); - } - - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/v1/psu/pis/${encodeURIComponent(String(authorizationId))}/for-approval/login`, + return this.httpClient.post(`${this.configuration.basePath}/v1/psu/${encodeURIComponent(String(authorizationId))}/for-approval/login`, psuAuthBody, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/consent-ui/src/app/api/api.headers.ts b/consent-ui/src/app/api/api.headers.ts index 5454ab05e7..0d40d4085e 100644 --- a/consent-ui/src/app/api/api.headers.ts +++ b/consent-ui/src/app/api/api.headers.ts @@ -1,5 +1,6 @@ export enum ApiHeaders { REDIRECT_CODE = 'Redirect-Code', + X_XSRF_TOKEN = 'X-XSRF-TOKEN', LOCATION = 'Location', COOKIE_TTL = 'Cookie-TTL' } diff --git a/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts b/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts index a66b0929a1..611d87b531 100644 --- a/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts +++ b/consent-ui/src/app/api/api/authStateConsentAuthorization.service.ts @@ -88,24 +88,24 @@ export class AuthStateConsentAuthorizationService { /** * Redirect entry point for initiating a consent authorization process. - * This is the <b>entry point</b> for processing a consent redirected by the TppBankingApi to this ConsentAuthorisationApi. At this entry point, the ConsentAuthorisationApi will use the redirectCode to retrieve the RedirectSession from the TppServer. An analysis of the RedirectSession will help decide if the ConsentAuthorisationApi will proceed with an embedded approach (E<sub>1</sub>) or a redirect approach (R<sub>1</sub>). + * This is the <b>entry point</b> for processing a consent redirected by the TppBankingApi to this ConsentAuthorisationApi. At this entry point, the ConsentAuthorisationApi will use the xXsrfToken to retrieve the RedirectSession from the TppServer. An analysis of the RedirectSession will help decide if the ConsentAuthorisationApi will proceed with an embedded approach (E<sub>1</sub>) or a redirect approach (R<sub>1</sub>). * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter + * @param xXsrfToken XSRF parameter used to retrieve a redirect session. This is generaly transported as a query parameter. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public authUsingGET(authId: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public authUsingGET(authId: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public authUsingGET(authId: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public authUsingGET(authId: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + public authUsingGET(authId: string, xXsrfToken?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public authUsingGET(authId: string, xXsrfToken?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public authUsingGET(authId: string, xXsrfToken?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public authUsingGET(authId: string, xXsrfToken?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (authId === null || authId === undefined) { throw new Error('Required parameter authId was null or undefined when calling authUsingGET.'); } let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { + if (xXsrfToken !== undefined && xXsrfToken !== null) { queryParameters = this.addToHttpParams(queryParameters, - redirectCode, 'redirectCode'); + xXsrfToken, 'xXsrfToken'); } let headers = this.defaultHeaders; diff --git a/consent-ui/src/app/api/api/consentAuthorization.service.ts b/consent-ui/src/app/api/api/consentAuthorization.service.ts deleted file mode 100644 index 271238c543..0000000000 --- a/consent-ui/src/app/api/api/consentAuthorization.service.ts +++ /dev/null @@ -1,400 +0,0 @@ -/** - * Open Banking Gateway - Consent Authorization API. - * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU. - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -/* tslint:disable:no-unused-variable member-ordering */ - -import { Inject, Injectable, Optional } from '@angular/core'; -import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; -import { CustomHttpParameterCodec } from '../encoder'; -import { Observable } from 'rxjs'; - -import { AuthorizeRequest } from '../model/authorizeRequest'; -import { DenyRequest } from '../model/denyRequest'; -import { InlineResponse200 } from '../model/inlineResponse200'; -import { PsuAuthRequest } from '../model/psuAuthRequest'; -import { PsuMessage } from '../model/psuMessage'; - -import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - - - -@Injectable({ - providedIn: 'root' -}) -export class ConsentAuthorizationService { - - protected basePath = 'http://localhost'; - public defaultHeaders = new HttpHeaders(); - public configuration = new Configuration(); - public encoder: HttpParameterCodec; - - constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { - if (configuration) { - this.configuration = configuration; - } - if (typeof this.configuration.basePath !== 'string') { - if (typeof basePath !== 'string') { - basePath = this.basePath; - } - this.configuration.basePath = basePath; - } - this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); - } - - - - /** - * Redirect entry point for initiating a consent authorization process. - * This is the <b>entry point</b> for processing a consent redirected by the TppBankingApi to this ConsentAuthorisationApi. At this entry point, the ConsentAuthorisationApi will use the redirectCode to retrieve the RedirectSession from the TppServer. An analysis of the RedirectSession will help decide if the ConsentAuthorisationApi will proceed with an embedded approach (E<sub>1</sub>) or a redirect approach (R<sub>1</sub>). - * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public authUsingGET(authId: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean): Observable; - public authUsingGET(authId: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean): Observable>; - public authUsingGET(authId: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean): Observable>; - public authUsingGET(authId: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false ): Observable { - if (authId === null || authId === undefined) { - throw new Error('Required parameter authId was null or undefined when calling authUsingGET.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); - } - - let headers = this.defaultHeaders; - - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}`, - { - params: queryParameters, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Consent authorization is denied - consent is blocked. Closes this session and redirects the PSU back to the FinTechApi or close the application window. - * Closes this session and redirects the PSU back to the FinTechApi or close the application window. In any case, the session of the user will be closed and cookies will be deleted with the response to this request. - * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie. - * @param denyRequest - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe?: 'body', reportProgress?: boolean): Observable; - public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe?: 'response', reportProgress?: boolean): Observable>; - public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe?: 'events', reportProgress?: boolean): Observable>; - public denyUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, denyRequest: DenyRequest, observe: any = 'body', reportProgress: boolean = false ): Observable { - if (authId === null || authId === undefined) { - throw new Error('Required parameter authId was null or undefined when calling denyUsingPOST.'); - } - if (xRequestID === null || xRequestID === undefined) { - throw new Error('Required parameter xRequestID was null or undefined when calling denyUsingPOST.'); - } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling denyUsingPOST.'); - } - if (denyRequest === null || denyRequest === undefined) { - throw new Error('Required parameter denyRequest was null or undefined when calling denyUsingPOST.'); - } - - let headers = this.defaultHeaders; - if (xRequestID !== undefined && xRequestID !== null) { - headers = headers.set('X-Request-ID', String(xRequestID)); - } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); - } - - // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/deny`, - denyRequest, - { - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Generic challenge response end point for updating consent session with PSU authentication data while requesting remaining challenges for the ongoing authorization process. - * Update consent session with PSU auth data whereby requesting remaining challenges for the ongoing authorization process. Returns 202 if one should proceed to some other link. Link to follow is in \'Location\' header. - * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter - * @param psuAuthRequest - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'body', reportProgress?: boolean): Observable; - public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'response', reportProgress?: boolean): Observable>; - public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'events', reportProgress?: boolean): Observable>; - public embeddedUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe: any = 'body', reportProgress: boolean = false ): Observable { - if (authId === null || authId === undefined) { - throw new Error('Required parameter authId was null or undefined when calling embeddedUsingPOST.'); - } - if (xRequestID === null || xRequestID === undefined) { - throw new Error('Required parameter xRequestID was null or undefined when calling embeddedUsingPOST.'); - } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling embeddedUsingPOST.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); - } - - let headers = this.defaultHeaders; - if (xRequestID !== undefined && xRequestID !== null) { - headers = headers.set('X-Request-ID', String(xRequestID)); - } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); - } - - // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/embedded`, - psuAuthRequest, - { - params: queryParameters, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Redirecting back from ASPSP to TPP after a failed consent authorization. - * Redirecting back from ASPSP to TPP after a failed consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. - * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean): Observable; - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean): Observable>; - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean): Observable>; - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false ): Observable { - if (authId === null || authId === undefined) { - throw new Error('Required parameter authId was null or undefined when calling fromAspspNokUsingGET.'); - } - if (redirectState === null || redirectState === undefined) { - throw new Error('Required parameter redirectState was null or undefined when calling fromAspspNokUsingGET.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); - } - - let headers = this.defaultHeaders; - - // authentication (redirectCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/fromAspsp/${encodeURIComponent(String(redirectState))}/nok`, - { - params: queryParameters, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. - * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. ##### Desiging the BackRedirectURL (R<sub>6</sub>) The BackRedirectURL (OkUrl, NokUrl, etc... depending of ASPSP API) is the URL used by the ASPSP to send the PsuUserAgent back to the ConsentAuthorisationApi. Event though the structure of this URL might be constrained by the nature of the ASPSP OpenBankingApi, the BackRedirectURL must contains atleast : * A redirect-id (as a path parameter) used to isolate many redirect processes form each order. * A consentAuthState (as a path or query parameter) used to protect the TppConsentSessionCookie as a XSRF parameter. * The consentAuthState might if necessary be used to encrypt the attached ConsentAuthSessionCookie. ##### Back-Redirecting PSU to the FinTechApi (4<sub>b</sub>) Prior to redirecting the PSU back to the FinTechApi, consent information will be stored by the ConsentAuthorisationApi in a RedirectSession as well. * The one time resulting redirectCode will be attached as a query parameter to the location URL leading back to the FinTechApi. * After verifying the FinTechRedirectSessionCookie (4<sub>b</sub>), the FinTechApi must forward this redirectCode to the token endpoint of the TppBankingAPi (4<sub>c</sub>). * The TppBankingApi will then retrieve the RedirectSession using the redirectCode and proceed forward with the authorization process. - * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean): Observable; - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean): Observable>; - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean): Observable>; - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false ): Observable { - if (authId === null || authId === undefined) { - throw new Error('Required parameter authId was null or undefined when calling fromAspspOkUsingGET.'); - } - if (redirectState === null || redirectState === undefined) { - throw new Error('Required parameter redirectState was null or undefined when calling fromAspspOkUsingGET.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); - } - - let headers = this.defaultHeaders; - - // authentication (redirectCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/fromAspsp/${encodeURIComponent(String(redirectState))}/ok`, - { - params: queryParameters, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - - /** - * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP. - * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP. - * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie. - * @param authorizeRequest - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'body', reportProgress?: boolean): Observable; - public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'response', reportProgress?: boolean): Observable>; - public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'events', reportProgress?: boolean): Observable>; - public toAspspGrantUsingPOST(authId: string, xRequestID: string, X_XSRF_TOKEN: string, authorizeRequest: AuthorizeRequest, observe: any = 'body', reportProgress: boolean = false ): Observable { - if (authId === null || authId === undefined) { - throw new Error('Required parameter authId was null or undefined when calling toAspspGrantUsingPOST.'); - } - if (xRequestID === null || xRequestID === undefined) { - throw new Error('Required parameter xRequestID was null or undefined when calling toAspspGrantUsingPOST.'); - } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling toAspspGrantUsingPOST.'); - } - if (authorizeRequest === null || authorizeRequest === undefined) { - throw new Error('Required parameter authorizeRequest was null or undefined when calling toAspspGrantUsingPOST.'); - } - - let headers = this.defaultHeaders; - if (xRequestID !== undefined && xRequestID !== null) { - headers = headers.set('X-Request-ID', String(xRequestID)); - } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); - } - - // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/toAspsp/grant`, - authorizeRequest, - { - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - -} diff --git a/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts b/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts index b8eb908f94..c9c5aae285 100644 --- a/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts +++ b/consent-ui/src/app/api/api/fromASPSPConsentAuthorization.service.ts @@ -90,15 +90,14 @@ export class FromASPSPConsentAuthorizationService { * Redirecting back from ASPSP to TPP after a failed consent authorization. * Redirecting back from ASPSP to TPP after a failed consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter + * @param redirectState Code used to retrieve a redirect session. This is generaly transported as a path parameter due to some banks limitiations (ING ASPSP) instead of being transported as query parameter * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public fromAspspNokUsingGET(authId: string, redirectState: string, redirectCode?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + public fromAspspNokUsingGET(authId: string, redirectState: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public fromAspspNokUsingGET(authId: string, redirectState: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromAspspNokUsingGET(authId: string, redirectState: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromAspspNokUsingGET(authId: string, redirectState: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (authId === null || authId === undefined) { throw new Error('Required parameter authId was null or undefined when calling fromAspspNokUsingGET.'); } @@ -106,12 +105,6 @@ export class FromASPSPConsentAuthorizationService { throw new Error('Required parameter redirectState was null or undefined when calling fromAspspNokUsingGET.'); } - let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = this.addToHttpParams(queryParameters, - redirectCode, 'redirectCode'); - } - let headers = this.defaultHeaders; // authentication (redirectCookie) required @@ -141,7 +134,6 @@ export class FromASPSPConsentAuthorizationService { return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/fromAspsp/${encodeURIComponent(String(redirectState))}/nok`, { - params: queryParameters, responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, @@ -153,18 +145,17 @@ export class FromASPSPConsentAuthorizationService { /** * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. - * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. ##### Desiging the BackRedirectURL (R<sub>6</sub>) The BackRedirectURL (OkUrl, NokUrl, etc... depending of ASPSP API) is the URL used by the ASPSP to send the PsuUserAgent back to the ConsentAuthorisationApi. Event though the structure of this URL might be constrained by the nature of the ASPSP OpenBankingApi, the BackRedirectURL must contains atleast : * A redirect-id (as a path parameter) used to isolate many redirect processes form each order. * A consentAuthState (as a path or query parameter) used to protect the TppConsentSessionCookie as a XSRF parameter. * The consentAuthState might if necessary be used to encrypt the attached ConsentAuthSessionCookie. ##### Back-Redirecting PSU to the FinTechApi (4<sub>b</sub>) Prior to redirecting the PSU back to the FinTechApi, consent information will be stored by the ConsentAuthorisationApi in a RedirectSession as well. * The one time resulting redirectCode will be attached as a query parameter to the location URL leading back to the FinTechApi. * After verifying the FinTechRedirectSessionCookie (4<sub>b</sub>), the FinTechApi must forward this redirectCode to the token endpoint of the TppBankingAPi (4<sub>c</sub>). * The TppBankingApi will then retrieve the RedirectSession using the redirectCode and proceed forward with the authorization process. + * Redirecting back from ASPSP to ConsentAuthorisationApi after a successful consent authorization. In any case, the corresponding redirect session of the user will be closed and cookies will be deleted with the response to this request. ##### Desiging the BackRedirectURL (R<sub>6</sub>) The BackRedirectURL (OkUrl, NokUrl, etc... depending of ASPSP API) is the URL used by the ASPSP to send the PsuUserAgent back to the ConsentAuthorisationApi. Event though the structure of this URL might be constrained by the nature of the ASPSP OpenBankingApi, the BackRedirectURL must contains atleast : * A redirect-id (as a path parameter) used to isolate many redirect processes form each order. * A consentAuthState (as a path or query parameter) used to protect the TppConsentSessionCookie as a XSRF parameter. * The consentAuthState might if necessary be used to encrypt the attached ConsentAuthSessionCookie. ##### Back-Redirecting PSU to the FinTechApi (4<sub>b</sub>) Prior to redirecting the PSU back to the FinTechApi, consent information will be stored by the ConsentAuthorisationApi in a RedirectSession as well. * The one time resulting xXsrfToken will be attached as a query parameter to the location URL leading back to the FinTechApi. * After verifying the FinTechRedirectSessionCookie (4<sub>b</sub>), the FinTechApi must forward this xXsrfToken to the token endpoint of the TppBankingAPi (4<sub>c</sub>). * The TppBankingApi will then retrieve the RedirectSession using the xXsrfToken and proceed forward with the authorization process. * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param redirectState XSRF parameter used to validate an RedirectCookie. This is generaly transported as a path parameter. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter + * @param redirectState Code used to retrieve a redirect session. This is generaly transported as a path parameter due to some banks limitiations (ING ASPSP) instead of being transported as query parameter * @param code Oauth2 code to exchange for token. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public fromAspspOkUsingGET(authId: string, redirectState: string, redirectCode?: string, code?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromAspspOkUsingGET(authId: string, redirectState: string, code?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (authId === null || authId === undefined) { throw new Error('Required parameter authId was null or undefined when calling fromAspspOkUsingGET.'); } @@ -173,10 +164,6 @@ export class FromASPSPConsentAuthorizationService { } let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = this.addToHttpParams(queryParameters, - redirectCode, 'redirectCode'); - } if (code !== undefined && code !== null) { queryParameters = this.addToHttpParams(queryParameters, code, 'code'); diff --git a/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts b/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts index 001c047c76..0722bbb161 100644 --- a/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts +++ b/consent-ui/src/app/api/api/updateConsentAuthorization.service.ts @@ -17,9 +17,7 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; -import { AuthorizeRequest } from '../model/models'; import { ConsentAuth } from '../model/models'; -import { DenyRequest } from '../model/models'; import { PsuAuthRequest } from '../model/models'; import { PsuMessage } from '../model/models'; @@ -94,35 +92,24 @@ export class UpdateConsentAuthorizationService { * Closes this session and redirects the PSU back to the FinTechApi or close the application window. In any case, the session of the user will be closed and cookies will be deleted with the response to this request. * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. - * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie. - * @param denyRequest * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public denyUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, denyRequest: DenyRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + public denyUsingPOST(authId: string, xRequestID: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public denyUsingPOST(authId: string, xRequestID: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public denyUsingPOST(authId: string, xRequestID: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public denyUsingPOST(authId: string, xRequestID: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (authId === null || authId === undefined) { throw new Error('Required parameter authId was null or undefined when calling denyUsingPOST.'); } if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling denyUsingPOST.'); } - if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { - throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling denyUsingPOST.'); - } - if (denyRequest === null || denyRequest === undefined) { - throw new Error('Required parameter denyRequest was null or undefined when calling denyUsingPOST.'); - } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); - } // authentication (sessionCookie) required if (this.configuration.apiKeys) { @@ -144,22 +131,13 @@ export class UpdateConsentAuthorizationService { } - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - let responseType: 'text' | 'json' = 'json'; if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { responseType = 'text'; } return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/deny`, - denyRequest, + null, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -175,39 +153,32 @@ export class UpdateConsentAuthorizationService { * Update consent session with PSU auth data whereby requesting remaining challenges for the ongoing authorization process. Returns 202 if one should proceed to some other link. Link to follow is in \'Location\' header. * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. - * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie. - * @param redirectCode Code used to retrieve a redirect session. This is generaly transported as a query parameter + * @param xXsrfToken XSRF parameter used to retrieve a redirect session. This is generaly transported as a query parameter. * @param psuAuthRequest * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public embeddedUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, redirectCode?: string, psuAuthRequest?: PsuAuthRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public embeddedUsingPOST(authId: string, xRequestID: string, xXsrfToken?: string, psuAuthRequest?: PsuAuthRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (authId === null || authId === undefined) { throw new Error('Required parameter authId was null or undefined when calling embeddedUsingPOST.'); } if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling embeddedUsingPOST.'); } - if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { - throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling embeddedUsingPOST.'); - } let queryParameters = new HttpParams({encoder: this.encoder}); - if (redirectCode !== undefined && redirectCode !== null) { + if (xXsrfToken !== undefined && xXsrfToken !== null) { queryParameters = this.addToHttpParams(queryParameters, - redirectCode, 'redirectCode'); + xXsrfToken, 'xXsrfToken'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); - } // authentication (sessionCookie) required if (this.configuration.apiKeys) { @@ -256,85 +227,4 @@ export class UpdateConsentAuthorizationService { ); } - /** - * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP. - * Provides the ConsentAuthorisationApi with the opportunity to redirect the PSU to the ASPSP. - * @param authId Used to distinguish between different consent authorization processes started by the same PSU. Also included in the corresponding cookie path to limit visibility of the consent cookie to the corresponding consent process. - * @param xRequestID Unique ID that identifies this request through common workflow. Shall be contained in HTTP Response as well. - * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie. The token matches the auth-id included in the requestpath and prefixing the cookie. - * @param authorizeRequest - * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. - * @param reportProgress flag to report request and response progress. - */ - public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public toAspspGrantUsingPOST(authId: string, xRequestID: string, xXSRFTOKEN: string, authorizeRequest: AuthorizeRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { - if (authId === null || authId === undefined) { - throw new Error('Required parameter authId was null or undefined when calling toAspspGrantUsingPOST.'); - } - if (xRequestID === null || xRequestID === undefined) { - throw new Error('Required parameter xRequestID was null or undefined when calling toAspspGrantUsingPOST.'); - } - if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { - throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling toAspspGrantUsingPOST.'); - } - if (authorizeRequest === null || authorizeRequest === undefined) { - throw new Error('Required parameter authorizeRequest was null or undefined when calling toAspspGrantUsingPOST.'); - } - - let headers = this.defaultHeaders; - if (xRequestID !== undefined && xRequestID !== null) { - headers = headers.set('X-Request-ID', String(xRequestID)); - } - if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); - } - - // authentication (sessionCookie) required - if (this.configuration.apiKeys) { - const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"]; - if (key) { - } - } - - let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; - if (httpHeaderAcceptSelected === undefined) { - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); - } - if (httpHeaderAcceptSelected !== undefined) { - headers = headers.set('Accept', httpHeaderAcceptSelected); - } - - - // to determine the Content-Type header - const consumes: string[] = [ - 'application/json' - ]; - const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); - if (httpContentTypeSelected !== undefined) { - headers = headers.set('Content-Type', httpContentTypeSelected); - } - - let responseType: 'text' | 'json' = 'json'; - if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { - responseType = 'text'; - } - - return this.httpClient.post(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(authId))}/toAspsp/grant`, - authorizeRequest, - { - responseType: responseType, - withCredentials: this.configuration.withCredentials, - headers: headers, - observe: observe, - reportProgress: reportProgress - } - ); - } - } diff --git a/consent-ui/src/app/api/model/authorizeRequest.ts b/consent-ui/src/app/api/model/authorizeRequest.ts deleted file mode 100644 index cea4b4d948..0000000000 --- a/consent-ui/src/app/api/model/authorizeRequest.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Open Banking Gateway - Consent Authorization API. - * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU. - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ConsentAuthRequirement } from './consentAuthRequirement'; - - -/** - * Contains information used to legitimate a request. - */ -export interface AuthorizeRequest { - consentAuth?: ConsentAuthRequirement; -} - diff --git a/consent-ui/src/app/api/model/consentAuth.ts b/consent-ui/src/app/api/model/consentAuth.ts index 0a229eaf82..a7a2f18f1f 100644 --- a/consent-ui/src/app/api/model/consentAuth.ts +++ b/consent-ui/src/app/api/model/consentAuth.ts @@ -27,6 +27,7 @@ export interface ConsentAuth { action?: ConsentAuth.ActionEnum; violations?: Array; accounts?: Array; + supportedConsentTypes?: Array; authMessageTemplate?: string; /** * An identification provided by the ASPSP for the later identification of the authentication method selection. @@ -53,6 +54,7 @@ export interface ConsentAuth { * List of sca methods for selection if necessary. */ scaMethods?: Array; + scaMethodSelected?: ScaUserData; scaStatus?: ScaStatus; singlePayment?: SinglePayment; challengeData?: ChallengeData; @@ -64,6 +66,12 @@ export namespace ConsentAuth { LISTTRANSACTIONS: 'LIST_TRANSACTIONS' as ActionEnum, INITIATEPAYMENT: 'INITIATE_PAYMENT' as ActionEnum }; + export type SupportedConsentTypesEnum = 'DEDICATED_ALL' | 'GLOBAL_ALL' | 'GLOBAL_ACCOUNTS'; + export const SupportedConsentTypesEnum = { + DEDICATEDALL: 'DEDICATED_ALL' as SupportedConsentTypesEnum, + GLOBALALL: 'GLOBAL_ALL' as SupportedConsentTypesEnum, + GLOBALACCOUNTS: 'GLOBAL_ACCOUNTS' as SupportedConsentTypesEnum + }; } diff --git a/consent-ui/src/app/api/model/consentAuthRequiredField.ts b/consent-ui/src/app/api/model/consentAuthRequiredField.ts deleted file mode 100644 index 27f1a9fa59..0000000000 --- a/consent-ui/src/app/api/model/consentAuthRequiredField.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Open Banking Gateway - Consent Authorization API. - * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU. - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -/** - * Fields that are required to be filled in order to authorize consent - */ -export interface ConsentAuthRequiredField { - /** - * Field data type - boolean, string, etc. - */ - type?: string; - /** - * Scope of the field. - */ - scope?: string; - /** - * Field code - what does the field mean. - */ - code?: string; - /** - * Custom message that describes field meaning - */ - captionMessage?: string; -} - diff --git a/consent-ui/src/app/api/model/consentAuthRequirement.ts b/consent-ui/src/app/api/model/consentAuthRequirement.ts deleted file mode 100644 index f65b422233..0000000000 --- a/consent-ui/src/app/api/model/consentAuthRequirement.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Open Banking Gateway - Consent Authorization API. - * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU. - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ConsentAuthRequiredField } from './consentAuthRequiredField'; - - -/** - * Transport object for consent API request response - */ -export interface ConsentAuthRequirement { - fields?: Array; -} - diff --git a/consent-ui/src/app/api/model/denyRedirectRequest.ts b/consent-ui/src/app/api/model/denyRedirectRequest.ts deleted file mode 100644 index 160228642c..0000000000 --- a/consent-ui/src/app/api/model/denyRedirectRequest.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Open Banking Gateway - Consent Authorization API. - * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU. - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ConsentAuth } from './consentAuth'; - - -/** - * Denies a redirect to ASPSP requested by the ConsentAuthorisationApi - */ -export interface DenyRedirectRequest { - consentAuth?: ConsentAuth; - /** - * Will indicate if PSU wants to be sent back to FinTechApi. - */ - backToFinTech?: boolean; - /** - * In case there is no redirect back to TPP desired, exit page can be specified by ConsentAuthorisationApi - */ - exitPage?: string; - /** - * Set to true if consent object shall be forgotten or frozen. - */ - forgetConsent?: boolean; -} - diff --git a/consent-ui/src/app/api/model/denyRequest.ts b/consent-ui/src/app/api/model/denyRequest.ts deleted file mode 100644 index 703a40a0a2..0000000000 --- a/consent-ui/src/app/api/model/denyRequest.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Open Banking Gateway - Consent Authorization API. - * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU. - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ConsentAuth } from './consentAuth'; - - -/** - * Consent authorization denied descriptor - */ -export interface DenyRequest { - consentAuth?: ConsentAuth; - /** - * Will indicate if PSU wants to be sent back to FinTechApi. - */ - backToFinTech?: boolean; - /** - * In case there is no redirect back to TPP desired, exit page can be specified by ConsentAuthorisationApi - */ - exitPage?: string; - /** - * Set to true if consent object shall be forgotten or frozen. - */ - forgetConsent?: boolean; -} - diff --git a/consent-ui/src/app/api/model/inlineResponse200.ts b/consent-ui/src/app/api/model/inlineResponse200.ts deleted file mode 100644 index 1a326dcbea..0000000000 --- a/consent-ui/src/app/api/model/inlineResponse200.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Open Banking Gateway - Consent Authorization API. - * Interface used by the PsuUserAgent to present consent authorization services to the PSU. The consent authorization process is triggered by redirecting the PSU from the [TppBankingApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#TppBankingApi) (2a) over the [FinTechApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#FinTechApi) (2b) to the /consent/{auth-id} entry point of this [ConsentAuthorisationApi](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#ConsentAuthorisationApi) (2c). The decision on whether the authorization process is embedded or redirected is taken by this ConsentAuthorisationApi. The following picture displays the overall architecture of this open banking consent authorisation api: ![High level architecture](/img/open-banking-consent-authorisation-api.png) #### User Agent This Api assumes that the PsuUserAgent is a modern browsers that : * automatically detects the \"302 Found\" response code and proceeds with the associated location url, * stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). This Api also assumes any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of amodern browser with respect to 30X and Cookies. #### Redirecting to the ConsentAuthorisationApi (2a) Any service request of the FinTechUI to the FinTechApi (1a) will be forwarded to the TppBankingApi (1b). This forward might contain a [PsuConsentSession](https://adorsys.github.io/open-banking-gateway/doc/latest/architecture/dictionary#PsuConsentSession) that is used to identify the PSU in the world of the TPP. The TppBankingApi uses the provided PsuConsentSession to retrieve an eventualy suitable consent that will be used to forward the corresponding service request to the OpenBankingApi (1c) of the ASPSP. If there is no suitable consent, the TPP might still send a consent initiation request to the OpenBankingApi (1c). Whether this request is sent or not depends on the design of the target OpenBankingApi. Finally, the TppBankingApi will if necessary instruct the FinTechApi (2a) to redirect the PsuUgerAgent (2b) to the /consent/{auth-id} entry point of the ConsentAuthorisationApi (2c). #### Issolation Authorisation Request Processing The auth-id parameter is used to make sure paralell authorization requests are not mixup. #### SessionCookies and XSRF Each authorisation session started will be associated with a proper SessionCookie and a corresponding XSRF-TOKEN. * The request that sets a session cookie (E1) also add the X-XSRF-TOKEN to the response header. * The cookie path is always extended with the corresponding auth-id, so two Authorization processes can not share state. * Each authenticated request sent to the ConsentAuthorisationApi will provide the X-XSRF-TOKEN matching the sent SessionCookie. #### RedirectCookie and XSRF (R1) In a redirect approach (Redirecting PSU to the ASPSP), the The retruned AuthorizeResponse object contains information needed to present a suitable redirect info page to the PSU. Redirection can either be actively performed by the UIApplication or performed as a result of a 30x redirect response to the PsuUserAgent. In both cases, a RedirectCookie will be associated with the PsuUserAgent and a corresponding XSRF-TOKEN named redirectState will be addedto the back redirect url. #### Final Result of the Authorization Process The final result of the authorization process is a PsuCosentSession that is returned by the token endpoint of the TppBankingAPi to the FinTechApi (4c). This handle will (PsuCosentSession) will be stored by the FinTechApi and added a PSU identifying information to each service request associated with this PSU. - * - * The version of the OpenAPI document: 1.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ -import { ConsentAuth } from './consentAuth'; - - -export interface InlineResponse200 { - consentAuth?: ConsentAuth; -} - diff --git a/consent-ui/src/app/api/model/models.ts b/consent-ui/src/app/api/model/models.ts index 6059635bb8..0ae600ff23 100644 --- a/consent-ui/src/app/api/model/models.ts +++ b/consent-ui/src/app/api/model/models.ts @@ -8,14 +8,10 @@ export * from './aisAccountAccessInfo'; export * from './aisConsentRequest'; export * from './amount'; export * from './authViolation'; -export * from './authorizeRequest'; export * from './balanceType'; export * from './bulkPayment'; export * from './challengeData'; export * from './consentAuth'; -export * from './consentAuthRequiredField'; -export * from './consentAuthRequirement'; -export * from './denyRequest'; export * from './paymentProduct'; export * from './paymentStatus'; export * from './periodicPayment'; diff --git a/consent-ui/src/app/api/model/scaUserData.ts b/consent-ui/src/app/api/model/scaUserData.ts index 267f2711f8..5e8518a7e5 100644 --- a/consent-ui/src/app/api/model/scaUserData.ts +++ b/consent-ui/src/app/api/model/scaUserData.ts @@ -15,6 +15,7 @@ export interface ScaUserData { decoupled?: boolean; id?: string; methodValue?: string; + explanation?: string; scaMethod?: ScaUserData.ScaMethodEnum; staticTan?: string; usesStaticTan?: boolean; diff --git a/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts b/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts index d070f3f46e..9c13563fb3 100644 --- a/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts +++ b/consent-ui/src/app/auth/anonymous/anonymous.component.spec.ts @@ -45,7 +45,7 @@ describe('AnonymousComponent', () => { }); it('should call login service', () => { - authServiceSpy = spyOn(authService, 'userLoginForAnonymousPayment').and.callThrough(); + authServiceSpy = spyOn(authService, 'userLoginForAnonymous').and.callThrough(); const authID = route.snapshot.parent.params.authId; const redirectCode = 'redirectCode654'; diff --git a/consent-ui/src/app/auth/anonymous/anonymous.component.ts b/consent-ui/src/app/auth/anonymous/anonymous.component.ts index 4d48d2e273..5a69ad81ca 100644 --- a/consent-ui/src/app/auth/anonymous/anonymous.component.ts +++ b/consent-ui/src/app/auth/anonymous/anonymous.component.ts @@ -38,7 +38,7 @@ export class AnonymousComponent implements OnInit { private doLoginAsAnonymous() { localStorage.setItem(ApiHeaders.COOKIE_TTL, '0'); - this.authService.userLoginForAnonymousPayment(this.authId, this.redirectCode).subscribe((res) => { + this.authService.userLoginForAnonymous(this.authId, this.redirectCode).subscribe((res) => { this.sessionService.setTTL(this.authId, res.headers.get(ApiHeaders.COOKIE_TTL)); window.location.href = res.headers.get(ApiHeaders.LOCATION); }); diff --git a/consent-ui/src/app/auth/login/login.component.spec.ts b/consent-ui/src/app/auth/login/login.component.spec.ts index 880867d4e9..d5ede135e7 100644 --- a/consent-ui/src/app/auth/login/login.component.spec.ts +++ b/consent-ui/src/app/auth/login/login.component.spec.ts @@ -59,7 +59,7 @@ describe('LoginComponent', () => { }); it('should be true if the form is invalid', () => { - authServiceSpy = spyOn(authService, 'userLoginForConsent').and.returnValue(of(response)); + authServiceSpy = spyOn(authService, 'userLogin').and.returnValue(of(response)); form.controls.login.setValue(usernameInput); form.controls.password.setValue(''); @@ -70,7 +70,7 @@ describe('LoginComponent', () => { }); it('should call login service', () => { - authServiceSpy = spyOn(authService, 'userLoginForConsent').and.callThrough(); + authServiceSpy = spyOn(authService, 'userLogin').and.callThrough(); const authID = route.snapshot.parent.params.authId; const redirectCode = 'redirectCode654'; @@ -86,7 +86,7 @@ describe('LoginComponent', () => { }); it('should be invalid if password is not set', () => { - authServiceSpy = spyOn(authService, 'userLoginForConsent').and.returnValue(of(response)); + authServiceSpy = spyOn(authService, 'userLogin').and.returnValue(of(response)); form.controls.login.setValue(usernameInput); form.controls.password.setValue(''); @@ -96,7 +96,7 @@ describe('LoginComponent', () => { expect(component.loginForm.invalid).toBe(true); }); it('should be invalid if username is not set', () => { - authServiceSpy = spyOn(authService, 'userLoginForConsent').and.returnValue(of(response)); + authServiceSpy = spyOn(authService, 'userLogin').and.returnValue(of(response)); form.controls.login.setValue(''); form.controls.password.setValue(passwordInput); diff --git a/consent-ui/src/app/auth/login/login.component.ts b/consent-ui/src/app/auth/login/login.component.ts index 95dc908b9b..d49e75a1df 100644 --- a/consent-ui/src/app/auth/login/login.component.ts +++ b/consent-ui/src/app/auth/login/login.component.ts @@ -47,7 +47,7 @@ export class LoginComponent implements OnInit { onSubmit() { localStorage.setItem(ApiHeaders.COOKIE_TTL, '0'); - this.authService.userLoginForConsent(this.authId, this.redirectCode, this.loginForm.value).subscribe((res) => { + this.authService.userLogin(this.authId, this.redirectCode, this.loginForm.value).subscribe((res) => { this.sessionService.setTTL(this.authId, res.headers.get(ApiHeaders.COOKIE_TTL)); window.location.href = res.headers.get(ApiHeaders.LOCATION); }); diff --git a/consent-ui/src/app/common/auth.service.ts b/consent-ui/src/app/common/auth.service.ts index f05dcef9f2..3c3f4faaa3 100644 --- a/consent-ui/src/app/common/auth.service.ts +++ b/consent-ui/src/app/common/auth.service.ts @@ -11,13 +11,13 @@ export class AuthService { constructor( private http: HttpClient, private psuAuthService: PsuAuthenticationService, - private psuAuthForConsentApproval: PsuAuthenticationAndConsentApprovalService, + private psuAuth: PsuAuthenticationAndConsentApprovalService, private sessionService: SessionService ) {} - public userLoginForConsent(authorizationId: string, redirectCode: string, credentials: PsuAuthBody) { + public userLogin(authorizationId: string, redirectCode: string, credentials: PsuAuthBody) { const xRequestID = uuid.v4(); - return this.psuAuthForConsentApproval.loginForApproval( + return this.psuAuth.loginForApproval( xRequestID, authorizationId, redirectCode, @@ -26,9 +26,9 @@ export class AuthService { ); } - public userLoginForAnonymousPayment(authorizationId: string, redirectCode: string) { + public userLoginForAnonymous(authorizationId: string, redirectCode: string) { const xRequestID = uuid.v4(); - return this.psuAuthForConsentApproval.loginForAnonymousPaymentApproval( + return this.psuAuth.loginForAnonymousApproval( xRequestID, authorizationId, redirectCode, @@ -36,21 +36,6 @@ export class AuthService { ); } - public userLoginForPayment(authorizationId: string, redirectCode: string, credentials: PsuAuthBody) { - const xRequestID = uuid.v4(); - return this.psuAuthForConsentApproval.loginForPaymentApproval( - xRequestID, - authorizationId, - redirectCode, - credentials, - 'response' - ); - } - - public userLogin(credentials: PsuAuthBody) { - const xRequestID = uuid.v4(); - return this.psuAuthService.login(xRequestID, credentials, 'response'); - } public userRegister(credentials: PsuAuthBody) { const xRequestID = uuid.v4(); return this.psuAuthService.registration(xRequestID, credentials, 'response'); diff --git a/consent-ui/src/app/common/enter-pin/enter-pin.component.ts b/consent-ui/src/app/common/enter-pin/enter-pin.component.ts index a05bbc2e23..2549fa8081 100644 --- a/consent-ui/src/app/common/enter-pin/enter-pin.component.ts +++ b/consent-ui/src/app/common/enter-pin/enter-pin.component.ts @@ -38,13 +38,12 @@ export class EnterPinComponent implements OnInit { .embeddedUsingPOST( this.authorizationSessionId, StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs this.redirectCode, { scaAuthenticationData: { PSU_PASSWORD: this.pinForm.get('pin').value } }, 'response' ) .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); this.enteredPin.emit(res); }); } diff --git a/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts b/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts index 88e699822d..351ae6d6fb 100644 --- a/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts +++ b/consent-ui/src/app/common/enter-tan/enter-tan.component.spec.ts @@ -8,7 +8,7 @@ import { EnterTanComponent } from './enter-tan.component'; import { StubUtilTests } from '../../ais/common/stub-util-tests'; import { SessionService } from '../session.service'; import { UpdateConsentAuthorizationService } from '../../api'; -import { ConsentAuthorizationService } from '../../api/api/consentAuthorization.service'; +import { AuthStateConsentAuthorizationService } from '../../api'; describe('EnterTanComponent', () => { let component: EnterTanComponent; @@ -34,7 +34,7 @@ describe('EnterTanComponent', () => { component = fixture.componentInstance; sessionService = TestBed.inject(SessionService); updateConsentAuthorizationService = TestBed.inject(UpdateConsentAuthorizationService); - consentAuthorizationService = TestBed.inject(ConsentAuthorizationService); + consentAuthorizationService = TestBed.inject(AuthStateConsentAuthorizationService); sessionServiceSpy = spyOn(sessionService, 'getRedirectCode').and.returnValue(StubUtilTests.REDIRECT_ID); updateConsentAuthorizationServiceSpy = spyOn( updateConsentAuthorizationService, diff --git a/consent-ui/src/app/common/enter-tan/enter-tan.component.ts b/consent-ui/src/app/common/enter-tan/enter-tan.component.ts index 674779859f..fee6a2a9c2 100644 --- a/consent-ui/src/app/common/enter-tan/enter-tan.component.ts +++ b/consent-ui/src/app/common/enter-tan/enter-tan.component.ts @@ -5,7 +5,7 @@ import { StubUtil } from '../utils/stub-util'; import { UpdateConsentAuthorizationService } from '../../api'; import { ApiHeaders } from '../../api/api.headers'; import { SessionService } from '../session.service'; -import { ConsentAuthorizationService } from '../../api/api/consentAuthorization.service'; +import { AuthStateConsentAuthorizationService } from '../../api/api/authStateConsentAuthorization.service'; @Component({ selector: 'consent-app-enter-tan', @@ -30,7 +30,7 @@ export class EnterTanComponent implements OnInit { constructor( private formBuilder: FormBuilder, private sessionService: SessionService, - private consentAuthorizationService: ConsentAuthorizationService, + private consentAuthorizationService: AuthStateConsentAuthorizationService, private updateConsentAuthorizationService: UpdateConsentAuthorizationService ) {} @@ -46,7 +46,7 @@ export class EnterTanComponent implements OnInit { .subscribe((response) => { this.sessionService.setRedirectCode( this.authorizationSessionId, - response.headers.get(ApiHeaders.REDIRECT_CODE) + response.headers.get(ApiHeaders.X_XSRF_TOKEN) ); const authStateResponseBody: any = response.body; @@ -70,14 +70,13 @@ export class EnterTanComponent implements OnInit { this.updateConsentAuthorizationService .embeddedUsingPOST( this.authorizationSessionId, - StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs + StubUtil.X_REQUEST_ID, // TODO: real values instead of stub this.sessionService.getRedirectCode(this.authorizationSessionId), { scaAuthenticationData: { SCA_CHALLENGE_DATA: this.reportScaResultForm.get('tan').value } }, 'response' ) .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); this.enteredSca.emit(res); }); } diff --git a/consent-ui/src/app/common/select-sca/select-sca.component.ts b/consent-ui/src/app/common/select-sca/select-sca.component.ts index 5a89c94a50..57c30835a4 100644 --- a/consent-ui/src/app/common/select-sca/select-sca.component.ts +++ b/consent-ui/src/app/common/select-sca/select-sca.component.ts @@ -40,14 +40,13 @@ export class SelectScaComponent implements OnInit { this.updateConsentAuthorizationService .embeddedUsingPOST( this.authorizationSessionId, - StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs + StubUtil.X_REQUEST_ID, // TODO: real values instead of stub this.redirectCode, { scaAuthenticationData: { SCA_CHALLENGE_ID: this.selectedMethod.value } }, 'response' ) .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationSessionId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); this.selectedValue.emit(res); }); } @@ -58,7 +57,7 @@ export class SelectScaComponent implements OnInit { .subscribe((consentAuth) => { this.sessionService.setRedirectCode( this.authorizationSessionId, - consentAuth.headers.get(ApiHeaders.REDIRECT_CODE) + consentAuth.headers.get(ApiHeaders.X_XSRF_TOKEN) ); this.redirectCode = this.sessionService.getRedirectCode(this.authorizationSessionId); this.scaMethods = consentAuth.body.scaMethods; diff --git a/consent-ui/src/app/common/session.service.ts b/consent-ui/src/app/common/session.service.ts index 1914d583bb..ba223f8c1e 100644 --- a/consent-ui/src/app/common/session.service.ts +++ b/consent-ui/src/app/common/session.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import {ConsentAuth} from '../api'; @Injectable({ providedIn: 'root' @@ -37,6 +38,18 @@ export class SessionService { sessionStorage.setItem(authorizationId + Session.FINTECH_NAME, fintechName); } + public getConsentTypesSupported(authorizationId: string): ConsentAuth.SupportedConsentTypesEnum[] { + const supportedTypes = sessionStorage.getItem(authorizationId + Session.CONSENT_TYPES_SUPPORTED); + if (!supportedTypes) { + return null; + } + return JSON.parse(supportedTypes); + } + + public setConsentTypesSupported(authorizationId: string, consentTypes: ConsentAuth.SupportedConsentTypesEnum[]) { + sessionStorage.setItem(authorizationId + Session.CONSENT_TYPES_SUPPORTED, !consentTypes ? null : JSON.stringify(consentTypes)); + } + public getBankName(authorizationId: string): string { return sessionStorage.getItem(authorizationId + Session.BANK_NAME); } @@ -108,6 +121,7 @@ enum Session { PAYMENT_OBJECT = ':PAYMENT_OBJECT', PAYMENT_STATE = ':PAYMENT_STATE', FINTECH_NAME = ':FINTECH_NAME', + CONSENT_TYPES_SUPPORTED = ':CONSENT_TYPES_SUPPORTED', BANK_NAME = ':BANK_NAME', XSRF_TOKEN = 'XSRF_TOKEN', COOKIE_TTL = 'Cookie-TTL', diff --git a/consent-ui/src/app/common/utils/stub-util.ts b/consent-ui/src/app/common/utils/stub-util.ts index 6be08df249..821d86c56f 100644 --- a/consent-ui/src/app/common/utils/stub-util.ts +++ b/consent-ui/src/app/common/utils/stub-util.ts @@ -2,6 +2,5 @@ export class StubUtil { public static FINTECH_NAME = 'Awesome FinTech'; public static ASPSP_NAME = 'adorsys Sandbox'; - public static X_XSRF_TOKEN = '12b34483-242a-428b-8295-2f4805bb0a30'; public static X_REQUEST_ID = 'a8b24683-242a-428b-8295-2f4805bb0a30'; } diff --git a/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts b/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts index ed464fd4c7..1092904e67 100644 --- a/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts +++ b/consent-ui/src/app/pis/consent-payment-access-selection/consent-payment-access-selection.component.ts @@ -1,14 +1,14 @@ -import { AfterContentChecked, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; - -import { DenyRequest, UpdateConsentAuthorizationService } from '../../api'; -import { ApiHeaders } from '../../api/api.headers'; -import { SessionService } from '../../common/session.service'; -import { AuthConsentState } from '../../ais/common/dto/auth-state'; -import { StubUtil } from '../../common/utils/stub-util'; -import { PaymentUtil } from '../common/payment-util'; -import { PisPayment } from '../common/models/pis-payment.model'; +import {AfterContentChecked, ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; +import {FormBuilder, FormGroup} from '@angular/forms'; +import {ActivatedRoute, Router} from '@angular/router'; + +import {UpdateConsentAuthorizationService} from '../../api'; +import {ApiHeaders} from '../../api/api.headers'; +import {SessionService} from '../../common/session.service'; +import {AuthConsentState} from '../../ais/common/dto/auth-state'; +import {StubUtil} from '../../common/utils/stub-util'; +import {PaymentUtil} from '../common/payment-util'; +import {PisPayment} from '../common/models/pis-payment.model'; @Component({ selector: 'consent-app-payment-access-selection', @@ -70,8 +70,6 @@ export class ConsentPaymentAccessSelectionComponent implements OnInit, AfterCont .denyUsingPOST( this.authorizationId, StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs - {} as DenyRequest, 'response' ) .subscribe((res) => { diff --git a/consent-ui/src/app/pis/initiation/payment-initiate.component.ts b/consent-ui/src/app/pis/initiation/payment-initiate.component.ts index 1e6855aaa3..70c45780e4 100644 --- a/consent-ui/src/app/pis/initiation/payment-initiate.component.ts +++ b/consent-ui/src/app/pis/initiation/payment-initiate.component.ts @@ -49,7 +49,7 @@ export class PaymentInitiateComponent implements OnInit { this.authStateConsentAuthorizationService .authUsingGET(authorizationId, redirectCode, 'response') .subscribe((res) => { - this.sessionService.setRedirectCode(authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); // setting bank and fintech names this.sessionService.setBankName(authorizationId, (res.body as ConsentAuth).bankName); diff --git a/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts b/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts index e6d95099d2..198b1baef9 100644 --- a/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts +++ b/consent-ui/src/app/pis/payments-consent-review/payments-consent-review.component.ts @@ -56,14 +56,13 @@ export class PaymentsConsentReviewComponent implements OnInit { this.updateConsentAuthorizationService .embeddedUsingPOST( this.authorizationId, - StubUtil.X_XSRF_TOKEN, StubUtil.X_REQUEST_ID, this.sessionService.getRedirectCode(this.authorizationId), body, 'response' ) .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); window.location.href = res.headers.get(ApiHeaders.LOCATION); }); } diff --git a/consent-ui/src/app/pis/pis-routing.module.ts b/consent-ui/src/app/pis/pis-routing.module.ts index d958ebe0b6..4370c3c9d2 100644 --- a/consent-ui/src/app/pis/pis-routing.module.ts +++ b/consent-ui/src/app/pis/pis-routing.module.ts @@ -9,6 +9,7 @@ import { ToAspspPageComponent } from './to-aspsp-page/to-aspsp-page.component'; import { ResultPageComponent } from './result-page/result-page.component'; import { EntryPagePaymentsComponent } from './entry-page-payments/entry-page-payments.component'; import { PaymentsConsentReviewComponent } from './payments-consent-review/payments-consent-review.component'; +import { WaitForDecoupled } from './wait-for-decoupled/wait-for-decoupled'; const routes: Routes = [ { @@ -19,6 +20,7 @@ const routes: Routes = [ { path: EnterTanPageComponent.ROUTE, component: EnterTanPageComponent }, { path: SelectScaPageComponent.ROUTE, component: SelectScaPageComponent }, { path: ToAspspPageComponent.ROUTE, component: ToAspspPageComponent }, + { path: WaitForDecoupled.ROUTE, component: WaitForDecoupled }, { path: ResultPageComponent.ROUTE, component: ResultPageComponent }, { path: EntryPagePaymentsComponent.ROUTE, diff --git a/consent-ui/src/app/pis/pis.module.ts b/consent-ui/src/app/pis/pis.module.ts index fd100c78e3..c91117180c 100644 --- a/consent-ui/src/app/pis/pis.module.ts +++ b/consent-ui/src/app/pis/pis.module.ts @@ -13,6 +13,7 @@ import { PaymentsConsentReviewComponent } from './payments-consent-review/paymen import { ConsentPaymentAccessSelectionComponent } from './consent-payment-access-selection/consent-payment-access-selection.component'; import { DynamicInputsComponent } from './dynamic-inputs/dynamic-inputs.component'; import { PaymentInfoComponent } from './payment-info/payment-info.component'; +import {WaitForDecoupled} from "./wait-for-decoupled/wait-for-decoupled"; @NgModule({ declarations: [ @@ -27,7 +28,8 @@ import { PaymentInfoComponent } from './payment-info/payment-info.component'; PaymentsConsentReviewComponent, ConsentPaymentAccessSelectionComponent, DynamicInputsComponent, - PaymentInfoComponent + PaymentInfoComponent, + WaitForDecoupled ], imports: [SharedModule, PisRoutingModule] }) diff --git a/consent-ui/src/app/pis/result-page/result-page.component.ts b/consent-ui/src/app/pis/result-page/result-page.component.ts index d5e1aade08..3ac041bf5f 100644 --- a/consent-ui/src/app/pis/result-page/result-page.component.ts +++ b/consent-ui/src/app/pis/result-page/result-page.component.ts @@ -5,7 +5,6 @@ import { Location } from '@angular/common'; import { SessionService } from '../../common/session.service'; import { AuthStateConsentAuthorizationService, - DenyRequest, SinglePayment, UpdateConsentAuthorizationService } from '../../api'; @@ -74,8 +73,6 @@ export class ResultPageComponent implements OnInit { .denyUsingPOST( this.authorizationId, StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs - {} as DenyRequest, 'response' ) .subscribe((res) => { @@ -94,7 +91,7 @@ export class ResultPageComponent implements OnInit { private loadRedirectUri(authId: string, redirectCode: string) { this.authStateConsentAuthorizationService.authUsingGET(authId, redirectCode, 'response').subscribe((res) => { console.log(res); - this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); this.redirectTo = res.headers.get(ApiHeaders.LOCATION); }); } diff --git a/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts b/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts index 3e953a9002..4fa7340a19 100644 --- a/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts +++ b/consent-ui/src/app/pis/to-aspsp-page/to-aspsp-page.component.ts @@ -1,14 +1,14 @@ -import { Component, OnInit } from '@angular/core'; -import { StubUtil } from '../../common/utils/stub-util'; -import { Action } from '../../common/utils/action'; -import { Location } from '@angular/common'; -import { ActivatedRoute } from '@angular/router'; -import { SessionService } from '../../common/session.service'; -import { AuthStateConsentAuthorizationService, DenyRequest, UpdateConsentAuthorizationService } from '../../api'; -import { ApiHeaders } from '../../api/api.headers'; -import { PaymentUtil } from '../common/payment-util'; -import { PisPayment } from '../common/models/pis-payment.model'; -import { combineLatest } from 'rxjs'; +import {Component, OnInit} from '@angular/core'; +import {StubUtil} from '../../common/utils/stub-util'; +import {Action} from '../../common/utils/action'; +import {Location} from '@angular/common'; +import {ActivatedRoute} from '@angular/router'; +import {SessionService} from '../../common/session.service'; +import {AuthStateConsentAuthorizationService, UpdateConsentAuthorizationService} from '../../api'; +import {ApiHeaders} from '../../api/api.headers'; +import {PaymentUtil} from '../common/payment-util'; +import {PisPayment} from '../common/models/pis-payment.model'; +import {combineLatest} from 'rxjs'; @Component({ selector: 'consent-app-to-aspsp-page', @@ -56,8 +56,6 @@ export class ToAspspPageComponent implements OnInit { .denyUsingPOST( this.authorizationId, StubUtil.X_REQUEST_ID, // TODO: real values instead of stubs - StubUtil.X_XSRF_TOKEN, // TODO: real values instead of stubs - {} as DenyRequest, 'response' ) .subscribe((res) => { @@ -69,7 +67,7 @@ export class ToAspspPageComponent implements OnInit { this.authStateConsentAuthorizationService .authUsingGET(this.authorizationId, this.sessionService.getRedirectCode(this.authorizationId), 'response') .subscribe((res) => { - this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.REDIRECT_CODE)); + this.sessionService.setRedirectCode(this.authorizationId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); this.redirectTo = res.headers.get(ApiHeaders.LOCATION); }); } diff --git a/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.html b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.html new file mode 100644 index 0000000000..29201e5112 --- /dev/null +++ b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.html @@ -0,0 +1,10 @@ +
+
+
+

Confirm consent on your application

+

+ {{authResponse?.scaMethodSelected.explanation}} +

+
+
+
diff --git a/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.scss b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.ts b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.ts new file mode 100644 index 0000000000..e10ee3c429 --- /dev/null +++ b/consent-ui/src/app/pis/wait-for-decoupled/wait-for-decoupled.ts @@ -0,0 +1,64 @@ +import {Component, OnInit} from '@angular/core'; +import { + AuthStateConsentAuthorizationService, + ConsentAuth, + PsuAuthRequest, + UpdateConsentAuthorizationService +} from "../../api"; +import {of, Subject} from "rxjs"; +import {SessionService} from "../../common/session.service"; +import {ActivatedRoute} from "@angular/router"; +import {delay, repeatWhen, switchMap, takeUntil, tap} from "rxjs/operators"; +import {StubUtil} from "../../common/utils/stub-util"; +import {ApiHeaders} from "../../api/api.headers"; + +@Component({ + selector: 'wait-for-decoupled-redirection', + templateUrl: './wait-for-decoupled.html', + styleUrls: ['./wait-for-decoupled.scss'] +}) +export class WaitForDecoupled implements OnInit { + public static ROUTE = 'wait-sca-finalization'; + + private readonly POLLING_DELAY_MS = 3000; + + authResponse: ConsentAuth | undefined; + + private authId: string; + + private decoupledCompleted = new Subject(); + + constructor( + private consentAuthorizationService: UpdateConsentAuthorizationService, + private consentStatusService: AuthStateConsentAuthorizationService, + private sessionService: SessionService, + private activatedRoute: ActivatedRoute + ) { + const route = this.activatedRoute.snapshot; + this.authId = route.parent.params.authId; + this.sessionService.setRedirectCode(this.authId, route.queryParams.redirectCode); + } + + ngOnInit() { + of(true) + .pipe(switchMap(_ => this.consentAuthorizationService.embeddedUsingPOST( + this.authId, + StubUtil.X_REQUEST_ID, + this.sessionService.getRedirectCode(this.authId), + {} as PsuAuthRequest, + 'response' + ))) + .pipe(repeatWhen(completed => completed.pipe(delay(this.POLLING_DELAY_MS))), tap()) + .pipe(takeUntil(this.decoupledCompleted)) + .subscribe(res => { + if (res.headers.get(ApiHeaders.X_XSRF_TOKEN)) { + this.sessionService.setRedirectCode(this.authId, res.headers.get(ApiHeaders.X_XSRF_TOKEN)); + } + this.authResponse = res.body; + + if (res.status === 202) { + window.location.href = res.headers.get(ApiHeaders.LOCATION); + } + }); + } +} diff --git a/consent-ui/src/styles.scss b/consent-ui/src/styles.scss index f8c14de3f1..a6a47ff132 100644 --- a/consent-ui/src/styles.scss +++ b/consent-ui/src/styles.scss @@ -89,6 +89,21 @@ a { padding-left: 1rem; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; font-size: 1.125rem; + + } + .customDate { + width: 12.5rem; + padding-top: 18px; + margin-left: 1rem; + } + .customNumber { + width: 7rem; + padding-top: 18px; + margin-left: 1rem; + margin-right: 1rem; + } + .customStrong { + padding: 5px 0 0 0px; } } @@ -110,6 +125,10 @@ a { .form-check { padding-left: 0; + .checkboxLabel { + padding: 0 0 0 20.5px; + } + label { font-family: var(--fontFamilyMedium); color: #21242c; @@ -149,6 +168,11 @@ a { } } +.inputWrapper { + //margin-left: 3.375rem; + margin-left: 2.03124rem; +} + @media (max-width: 767px) { .bg-sm-light { background-color: $COLOR_LIGHT_GREY-10; diff --git a/docker-compose.yml b/docker-compose.yml index 6f5f828194..35d464d833 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,7 +31,7 @@ services: - FACADE_ENCRYPTION_KEYSETPATH=/keysetpath/example-keyset.json - FACADE_URLS_EMBEDDED-UI-BASE-URL=http://localhost:14200 - PROTOCOL_GATEWAY-BASE-URL=http://localhost:18085 - image: "adorsys/open-banking-gateway-open-banking-gateway:0.19.1" + image: "adorsys/open-banking-gateway-open-banking-gateway:0.30.0.1" ports: - "18085:8085" volumes: @@ -46,7 +46,7 @@ services: environment: - BACKEND_URL=http://fintech-server:8086 restart: on-failure - image: "adorsys/open-banking-gateway-fintech-ui:0.19.1" + image: "adorsys/open-banking-gateway-fintech-ui:0.30.0.1" ports: - "24200:4200" depends_on: @@ -61,7 +61,7 @@ services: - FINTECH-UI_HOST=http://localhost:24200 - image: "adorsys/open-banking-gateway-fintech-server:0.19.1" + image: "adorsys/open-banking-gateway-fintech-server:0.30.0.1" ports: - "18086:8086" depends_on: @@ -73,7 +73,7 @@ services: environment: - BACKEND_URL=http://open-banking-gateway:8085 restart: on-failure - image: "adorsys/open-banking-gateway-consent-ui:0.19.1" + image: "adorsys/open-banking-gateway-consent-ui:0.30.0.1" ports: - "14200:4200" depends_on: @@ -83,7 +83,7 @@ services: hbci-sandbox-server: restart: on-failure - image: "adorsys/open-banking-gateway-hbci-sandbox-server:0.19.1" + image: "adorsys/open-banking-gateway-hbci-sandbox-server:0.30.0.1" ports: - "18090:8090" networks: diff --git a/docs/img/big-picture.png b/docs/img/big-picture.png new file mode 100644 index 0000000000..c29f4b6e04 Binary files /dev/null and b/docs/img/big-picture.png differ diff --git a/fintech-examples/fintech-api/pom.xml b/fintech-examples/fintech-api/pom.xml index 7552fa3002..1a2bddc152 100644 --- a/fintech-examples/fintech-api/pom.xml +++ b/fintech-examples/fintech-api/pom.xml @@ -5,7 +5,7 @@ de.adorsys.opba fintech-examples - 0.30.0.1 + 1.0.0 fintech-api diff --git a/fintech-examples/fintech-api/src/main/resources/static/fintech_api.yml b/fintech-examples/fintech-api/src/main/resources/static/fintech_api.yml index ed24876a14..da37fff661 100644 --- a/fintech-examples/fintech-api/src/main/resources/static/fintech_api.yml +++ b/fintech-examples/fintech-api/src/main/resources/static/fintech_api.yml @@ -211,14 +211,14 @@ paths: get: tags: - FinTechBankSearch - summary: Request the profile of the bank identified with id (bankId). - description: Request the profile of the bank identified with id (bankId). + summary: Request the profile of the bank identified with id (bankProfileId). + description: Request the profile of the bank identified with id (bankProfileId). operationId: bankProfileGET parameters: #header - $ref: "#/components/parameters/X-Request-ID" - $ref: "#/components/parameters/X-XSRF-TOKEN" - - name: bankId + - name: bankProfileId in: query description: Identifier of the bank to be loaded. required: true @@ -256,9 +256,11 @@ paths: #header - $ref: "#/components/parameters/X-Request-ID" - $ref: "#/components/parameters/X-XSRF-TOKEN" + - $ref: "#/components/parameters/X-Psu-Authentication-Required" - $ref: "#/components/parameters/Fintech-Redirect-URL-OK" - $ref: "#/components/parameters/Fintech-Redirect-URL-NOK" - $ref: "#/components/parameters/LoARetrievalInformation" + - $ref: "#/components/parameters/X-Create-Consent-If-None" #query - $ref: "#/components/parameters/withBalance" - $ref: "#/components/parameters/online" @@ -286,6 +288,14 @@ paths: #path - $ref: "#/components/parameters/bank-id" - $ref: "#/components/parameters/account-id" + #header + - $ref: "#/components/parameters/X-Request-ID" + - $ref: "#/components/parameters/X-XSRF-TOKEN" + - $ref: "#/components/parameters/X-Psu-Authentication-Required" + - $ref: "#/components/parameters/Fintech-Redirect-URL-OK" + - $ref: "#/components/parameters/Fintech-Redirect-URL-NOK" + - $ref: "#/components/parameters/LoTRetrievalInformation" + - $ref: "#/components/parameters/X-Create-Consent-If-None" #query - $ref: "#/components/parameters/dateFrom" - $ref: "#/components/parameters/dateTo" @@ -294,13 +304,6 @@ paths: - $ref: "#/components/parameters/deltaList" - $ref: "#/components/parameters/online" - #header - - $ref: "#/components/parameters/X-Request-ID" - - $ref: "#/components/parameters/X-XSRF-TOKEN" - - $ref: "#/components/parameters/Fintech-Redirect-URL-OK" - - $ref: "#/components/parameters/Fintech-Redirect-URL-NOK" - - $ref: "#/components/parameters/LoTRetrievalInformation" - security: - sessionCookie: [] responses: @@ -401,7 +404,7 @@ paths: #header - $ref: "#/components/parameters/X-Request-ID" - $ref: "#/components/parameters/X-XSRF-TOKEN" - - $ref: "#/components/parameters/X-Pis-Psu-Authentication-Required" + - $ref: "#/components/parameters/X-Psu-Authentication-Required" - $ref: "#/components/parameters/Fintech-Redirect-URL-OK" - $ref: "#/components/parameters/Fintech-Redirect-URL-NOK" @@ -418,6 +421,31 @@ paths: "401": $ref: "#/components/responses/401_Unauthorized" + /v1/ais/banks/{bank-id}/consents: + delete: + tags: + - FinTechAccountInformation + operationId: aisConsentsDELETE + summary: Deletes all consents that are associated with bank + description: Deletes all consents that are associated with bank + parameters: + #path + - $ref: "#/components/parameters/bank-id" + + #header + - $ref: "#/components/parameters/X-Request-ID" + - $ref: "#/components/parameters/X-XSRF-TOKEN" + + security: + - sessionCookie: [ ] + responses: + "200": + $ref: "#/components/responses/200_ConsentRemovalResult" + "401": + $ref: "#/components/responses/401_Unauthorized" + "404": + $ref: "#/components/responses/404_NotFound" + /v1/consent/{userid}/{password}: get: summary: ask for existing consent of user @@ -579,6 +607,15 @@ components: - "FROM_TPP_WITH_AVAILABLE_CONSENT" - "FROM_TPP_WITH_NEW_CONSENT" + X-Create-Consent-If-None: + name: X-Create-Consent-If-None + required: false + in: header + schema: + type: string + description: json representation of AisConsentRequest object + example: {"access":{"allPsd2":"ALL_ACCOUNTS_WITH_BALANCES"},"frequencyPerDay":4,"validUntil":"2021-06-18","recurringIndicator":true,"combinedServiceIndicator":false} + X-Request-ID: name: X-Request-ID in: header @@ -591,8 +628,19 @@ components: type: string format: uuid - X-Pis-Psu-Authentication-Required: - name: X-Pis-Psu-Authentication-Required + X-Ais-Psu-Authentication-Required: + name: X-Ais-Psu-Authentication-Required + in: header + description: | + If false, login form to OPBA will not be displayed, so + that authentication is not necessary. If absent or true - login form for consents will be displayed. + example: "true" + schema: + type: boolean + default: "true" + + X-Psu-Authentication-Required: + name: X-Psu-Authentication-Required in: header description: | If false, login form to OPBA will not be displayed as there might be nothing to share for payments, so @@ -958,6 +1006,13 @@ components: schema: type: object + 200_ConsentRemovalResult: + description: List of all removed consents + content: + application/json: + schema: + type: object + schemas: paymentInitiationWithStatusResponse: description: response from open banking gateway @@ -1089,6 +1144,11 @@ components: type: string uuid: type: string + format: uuid + profiles: + type: array + items: + $ref: "#/components/schemas/bankProfile" bankProfile: title: Bank Profile @@ -1098,13 +1158,26 @@ components: type: string bankName: type: string + name: + type: string bic: type: string + uuid: + type: string + format: uuid services: type: array items: # can be an enum type: string + externalId: + type: string + externalInterfaces: + type: string + protocolType: + type: string + isSandbox: + type: boolean # Account and transaction information accountDetails: @@ -1234,6 +1307,67 @@ components: $ref: "#/components/schemas/transactionList" pending: $ref: "#/components/schemas/transactionList" + rawTransactions: + description: Raw bank response as String + type: string + + analyticsReportDetails: + description: | + JSON based analytics report. + This account report contains transaction categorization result. + type: object + properties: + transactionId: + description: The id of transaction this analytics result refers to. + type: string + mainCategory: + description: Main category of the booking. + type: string + subCategory: + description: Sub category of the booking. + type: string + specification: + description: Specification of the booking. + type: string + otherAccount: + description: Related account. + type: string + logo: + description: Logo. + type: string + homepage: + description: Homepage. + type: string + hotline: + description: Hotline. + type: string + email: + description: Email. + type: string + custom: + description: Custom information about analyzed transaction. + type: object + additionalProperties: + type: string + usedRules: + description: Rules that were used to analyze. + type: array + uniqueItems: true + items: + type: string + nextBookingDate: + description: Classification next booking date. + type: string + format: date + cycle: + description: Classification cycle result. + type: string + + analyticsReport: + description: Array of transaction details. + type: array + items: + $ref: "#/components/schemas/analyticsReportDetails" accountStatus: description: | @@ -1871,6 +2005,8 @@ components: $ref: "#/components/schemas/accountReference" transactions: $ref: "#/components/schemas/accountReport" + analytics: + $ref: "#/components/schemas/analyticsReport" ultimateCreditor: description: Ultimate Creditor. diff --git a/fintech-examples/fintech-db-schema/pom.xml b/fintech-examples/fintech-db-schema/pom.xml index 8e5b748931..94d4de6904 100644 --- a/fintech-examples/fintech-db-schema/pom.xml +++ b/fintech-examples/fintech-db-schema/pom.xml @@ -6,7 +6,7 @@ de.adorsys.opba fintech-examples - 0.30.0.1 + 1.0.0 jar diff --git a/fintech-examples/fintech-example-tests/pom.xml b/fintech-examples/fintech-example-tests/pom.xml index 46b4f03799..f9c5563c52 100644 --- a/fintech-examples/fintech-example-tests/pom.xml +++ b/fintech-examples/fintech-example-tests/pom.xml @@ -5,7 +5,7 @@ fintech-examples de.adorsys.opba - 0.30.0.1 + 1.0.0 4.0.0 diff --git a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechConsentUiSmokeE2ETest.java b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechConsentUiSmokeE2ETest.java index 61bc144244..71a59fa5ea 100644 --- a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechConsentUiSmokeE2ETest.java +++ b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechConsentUiSmokeE2ETest.java @@ -68,7 +68,7 @@ static void setupDriverArch() { void testRedirectUserWantsToSeeItsAccountsFromFintech(FirefoxDriver firefoxDriver) { given().create_new_user_in_sandbox_tpp_management(username, PIN) .enabled_redirect_sandbox_mode(smokeConfig.getAspspProfileServerUri()) - .fintech_points_to_fintechui_login_page(smokeConfig.getFintechServerUri()); + .fintech_points_to_fintech_server(smokeConfig.getFintechServerUri()); when().user_already_login_in_bank_profile(firefoxDriver, username, fintech_login, REDIRECT_MODE, username) .and() @@ -110,7 +110,7 @@ void testRedirectUserWantsToSeeItsAccountsFromFintech(FirefoxDriver firefoxDrive public void testEmbeddedUserWantsItsAccountsFromFintech(FirefoxDriver firefoxDriver) { given().create_new_user_in_sandbox_tpp_management(username, PIN) .enabled_embedded_sandbox_mode(smokeConfig.getAspspProfileServerUri()) - .fintech_points_to_fintechui_login_page(smokeConfig.getFintechServerUri()); + .fintech_points_to_fintech_server(smokeConfig.getFintechServerUri()); when().user_consent_authorization_in_embedded_mode(firefoxDriver, username, fintech_login, EMBEDDED_MODE, username); @@ -122,7 +122,7 @@ public void testEmbeddedUserWantsItsAccountsFromFintech(FirefoxDriver firefoxDri void testEmbeddedUserWantsToSeeItsAccountsFromFintech(FirefoxDriver firefoxDriver) { given().create_new_user_in_sandbox_tpp_management(username, PIN) .enabled_embedded_sandbox_mode(smokeConfig.getAspspProfileServerUri()) - .fintech_points_to_fintechui_login_page(smokeConfig.getFintechServerUri()); + .fintech_points_to_fintech_server(smokeConfig.getFintechServerUri()); when().user_already_login_in_bank_profile(firefoxDriver, username, fintech_login, EMBEDDED_MODE, username) .and() .user_for_embeeded_provided_to_consent_ui_initial_parameters_to_list_transactions_with_all_accounts_consent(firefoxDriver, username) @@ -150,7 +150,7 @@ void testEmbeddedUserWantsToSeeItsAccountsFromFintech(FirefoxDriver firefoxDrive public void testUserAfterLoginWantsToLogout(FirefoxDriver firefoxDriver) { given().create_new_user_in_sandbox_tpp_management(username, PIN) .enabled_embedded_sandbox_mode(smokeConfig.getAspspProfileServerUri()) - .fintech_points_to_fintechui_login_page(smokeConfig.getFintechServerUri()); + .fintech_points_to_fintech_server(smokeConfig.getFintechServerUri()); when().user_opens_fintechui_login_page(firefoxDriver) .and() @@ -160,7 +160,7 @@ public void testUserAfterLoginWantsToLogout(FirefoxDriver firefoxDriver) { .and() .user_looks_for_a_bank_in_the_bank_search_input_place(firefoxDriver, ADORSYS_XS2A) .and() - .user_wait_for_the_result_in_bank_search(firefoxDriver) + .user_wait_for_the_result_in_bank_search_and_select(firefoxDriver, REDIRECT_MODE) .and() .user_navigates_to_page(firefoxDriver) .and() @@ -178,9 +178,9 @@ public void testUserAfterLoginWantsToLogout(FirefoxDriver firefoxDriver) { public void testRedirectUserToSeeItsAccountsFromFintech(FirefoxDriver firefoxDriver) { given().create_new_user_in_sandbox_tpp_management(username, PIN) .enabled_embedded_sandbox_mode(smokeConfig.getAspspProfileServerUri()) - .fintech_points_to_fintechui_login_page(smokeConfig.getFintechServerUri()); + .fintech_points_to_fintech_server(smokeConfig.getFintechServerUri()); - when().user_authorizes_payment_in_redirect_mode(firefoxDriver, username, fintech_login, REDIRECT_MODE, username); + when().user_authorizes_payment_in_redirect_mode(firefoxDriver, username, fintech_login, ADORSYS_XS2A, username); then().fintech_can_read_user_accounts(); } diff --git a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechPaymentSmokeE2ETest.java b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechPaymentSmokeE2ETest.java index a192960e92..bfb333411c 100644 --- a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechPaymentSmokeE2ETest.java +++ b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/FintechPaymentSmokeE2ETest.java @@ -75,9 +75,9 @@ static void setupDriverArch() { void testUserAfterInitiatingAPaymentWantsToSeeItsTransactionsOnFintechRedirectMode(FirefoxDriver firefoxDriver) { given().create_new_user_in_sandbox_tpp_management(username, PIN) .enabled_redirect_sandbox_mode(smokeConfig.getAspspProfileServerUri()) - .fintech_points_to_fintechui_login_page(smokeConfig.getFintechServerUri()); + .fintech_points_to_fintech_server(smokeConfig.getFintechServerUri()); - when().user_authorizes_payment_in_redirect_mode(firefoxDriver, username, fintech_login, REDIRECT_MODE, username) + when().user_authorizes_payment_in_redirect_mode(firefoxDriver, username, fintech_login, ADORSYS_XS2A, username) .and() .user_navigates_to_page(firefoxDriver) .and() @@ -121,7 +121,7 @@ void testUserAfterInitiatingAPaymentWantsToSeeItsTransactionsOnFintechRedirectMo void testUserAfterInitiatingAPaymentWantsToSeeItsTransactionsOnFintechEmbeddedMode(FirefoxDriver firefoxDriver) { given().create_new_user_in_sandbox_tpp_management(username, PIN) .enabled_redirect_sandbox_mode(smokeConfig.getAspspProfileServerUri()) - .fintech_points_to_fintechui_login_page(smokeConfig.getFintechServerUri()); + .fintech_points_to_fintech_server(smokeConfig.getFintechServerUri()); when().user_consent_authorization_in_embedded_mode(firefoxDriver, username, fintech_login, EMBEDDED_MODE, username) .and() diff --git a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechServer.java b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechServer.java index 6b138bf5e8..e5a12ded85 100644 --- a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechServer.java +++ b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechServer.java @@ -62,8 +62,8 @@ void prepareRestAssured() { RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); RestAssured.config = RestAssured.config().redirect(RedirectConfig.redirectConfig().followRedirects(false)); } - public SELF fintech_points_to_fintechui_login_page(String fintechUri) { - RestAssured.baseURI = fintechUri; + public SELF fintech_points_to_fintech_server(String fintechServerUri) { + RestAssured.baseURI = fintechServerUri; RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); return self(); } diff --git a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechStagesUtils.java b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechStagesUtils.java index 30f0f8fc39..9202b72c7f 100644 --- a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechStagesUtils.java +++ b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/FintechStagesUtils.java @@ -15,9 +15,8 @@ public class FintechStagesUtils { public static final String TRANSACTION = "/accounts"; public static final String BANKSEARCH_LOGIN = "/login"; public static final String ADORSYS_XS2A = "adorsys xs2a"; - public static final String FINTECH_UI_URI = "https://obg-dev-fintechui.cloud.adorsys.de"; - public static final String REDIRECT_MODE = "adorsys redirect"; - public static final String EMBEDDED_MODE = "adorsys embedded"; + public static final String REDIRECT_MODE = "redirect"; + public static final String EMBEDDED_MODE = "embedded"; public static RequestSpecification withDefaultHeaders() { diff --git a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/WebDriverBasedUserInfoFintech.java b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/WebDriverBasedUserInfoFintech.java index 350a6b9535..407866234a 100644 --- a/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/WebDriverBasedUserInfoFintech.java +++ b/fintech-examples/fintech-example-tests/src/test/java/de/adorsys/fintech/tests/e2e/steps/WebDriverBasedUserInfoFintech.java @@ -2,7 +2,11 @@ import com.tngtech.jgiven.integration.spring.JGivenStage; import de.adorsys.opba.protocol.xs2a.tests.e2e.sandbox.servers.WebDriverBasedAccountInformation; -import org.openqa.selenium.*; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.springframework.beans.factory.annotation.Autowired; @@ -13,7 +17,7 @@ import java.net.URI; import java.time.Duration; -import static de.adorsys.fintech.tests.e2e.steps.FintechStagesUtils.FINTECH_UI_URI; +import static de.adorsys.fintech.tests.e2e.steps.FintechStagesUtils.ADORSYS_XS2A; import static de.adorsys.fintech.tests.e2e.steps.FintechStagesUtils.PIN; import static de.adorsys.opba.protocol.xs2a.tests.e2e.sandbox.servers.config.RetryableConfig.TEST_RETRY_OPS; @@ -30,8 +34,11 @@ public class WebDriverBasedUserInfoFintech fintech-examples de.adorsys.opba - 0.30.0.1 + 1.0.0 4.0.0 diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechAccountInformationImpl.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechAccountInformationImpl.java index 4fc132fbb5..2220c46bb9 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechAccountInformationImpl.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechAccountInformationImpl.java @@ -7,6 +7,7 @@ import de.adorsys.opba.fintech.impl.controller.utils.LoTRetrievalInformation; import de.adorsys.opba.fintech.impl.database.entities.SessionEntity; import de.adorsys.opba.fintech.impl.service.AccountService; +import de.adorsys.opba.fintech.impl.service.ConsentService; import de.adorsys.opba.fintech.impl.service.SessionLogicService; import de.adorsys.opba.fintech.impl.service.TransactionService; import lombok.RequiredArgsConstructor; @@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController; import java.time.LocalDate; +import java.util.Map; import java.util.UUID; @Slf4j @@ -26,34 +28,48 @@ public class FinTechAccountInformationImpl implements FinTechAccountInformationA private final SessionLogicService sessionLogicService; private final AccountService accountService; private final TransactionService transactionService; + private final ConsentService consentService; @Override public ResponseEntity aisAccountsGET(String bankId, UUID xRequestID, String xsrfToken, String fintechRedirectURLOK, String fintechRedirectURLNOK, - String loARetrievalInformation, Boolean withBalance, Boolean online) { + String loARetrievalInformation, Boolean xPsuAuthenticationRequired, + String createConsentIfNone, Boolean withBalance, Boolean online) { if (!sessionLogicService.isSessionAuthorized()) { log.warn("aisAccountsGET failed: user is not authorized!"); return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } SessionEntity sessionEntity = sessionLogicService.getSession(); - return sessionLogicService.addSessionMaxAgeToHeader(accountService.listAccounts(sessionEntity, - fintechRedirectURLOK, fintechRedirectURLNOK, bankId, LoARetrievalInformation.valueOf(loARetrievalInformation), withBalance, online)); + return sessionLogicService.addSessionMaxAgeToHeader( + accountService.listAccounts(sessionEntity, fintechRedirectURLOK, fintechRedirectURLNOK, + bankId, LoARetrievalInformation.valueOf(loARetrievalInformation), + createConsentIfNone, withBalance, xPsuAuthenticationRequired, online)); } @Override public ResponseEntity aisTransactionsGET(String bankId, String accountId, UUID xRequestID, String xsrfToken, String fintechRedirectURLOK, String fintechRedirectURLNOK, String loTRetrievalInformation, - LocalDate dateFrom, LocalDate dateTo, - String entryReferenceFrom, String bookingStatus, Boolean deltaList, Boolean online) { + Boolean xPsuAuthenticationRequired, String createConsentIfNone, + LocalDate dateFrom, LocalDate dateTo, String entryReferenceFrom, String bookingStatus, + Boolean deltaList, Boolean online) { if (!sessionLogicService.isSessionAuthorized()) { log.warn("aisTransactionsGET failed: user is not authorized!"); return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } SessionEntity sessionEntity = sessionLogicService.getSession(); return sessionLogicService.addSessionMaxAgeToHeader( - transactionService.listTransactions(sessionEntity, fintechRedirectURLOK, fintechRedirectURLNOK, - bankId, accountId, dateFrom, dateTo, entryReferenceFrom, bookingStatus, deltaList, LoTRetrievalInformation.valueOf(loTRetrievalInformation), online)); + transactionService.listTransactions(sessionEntity, fintechRedirectURLOK, fintechRedirectURLNOK, + bankId, accountId, createConsentIfNone, dateFrom, dateTo, + entryReferenceFrom, bookingStatus, deltaList, + LoTRetrievalInformation.valueOf(loTRetrievalInformation), xPsuAuthenticationRequired, online)); + } + + @Override + public ResponseEntity aisConsentsDELETE(String bankId, UUID xRequestID, String xsrfToken) { + SessionEntity sessionEntity = sessionLogicService.getSession(); + consentService.deleteAllConsentsOfBank(sessionEntity, bankId); + return ResponseEntity.ok().body(Map.of()); } } diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechBankSearchImpl.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechBankSearchImpl.java index 4fdb3245da..25349ea0f2 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechBankSearchImpl.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/controller/FinTechBankSearchImpl.java @@ -32,11 +32,11 @@ public ResponseEntity bankSearchGET(UUID xRequestID, String } @Override - public ResponseEntity bankProfileGET(UUID xRequestID, String fintechToken, String bankId) { + public ResponseEntity bankProfileGET(UUID xRequestID, String fintechToken, String bankProfileId) { if (!sessionLogicService.isSessionAuthorized()) { log.warn("bankProfileGET failed: user is not authorized!"); return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } - return sessionLogicService.addSessionMaxAgeToHeader(new ResponseEntity<>(bankSearchService.searchBankProfile(bankId), HttpStatus.OK)); + return sessionLogicService.addSessionMaxAgeToHeader(new ResponseEntity<>(bankSearchService.searchBankProfile(bankProfileId), HttpStatus.OK)); } } diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/database/repositories/ConsentRepository.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/database/repositories/ConsentRepository.java index 64f6615c3d..5b5fc7e392 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/database/repositories/ConsentRepository.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/database/repositories/ConsentRepository.java @@ -3,6 +3,7 @@ import de.adorsys.opba.fintech.impl.database.entities.ConsentEntity; import de.adorsys.opba.fintech.impl.database.entities.UserEntity; import de.adorsys.opba.fintech.impl.tppclients.ConsentType; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.repository.CrudRepository; import java.util.List; @@ -18,4 +19,7 @@ List findListByUserEntityAndBankIdAndConsentTypeAndConsentConfirm bankId, ConsentType consentType, Boolean consentConfirmed); List findByUserEntityAndConsentTypeAndConsentConfirmedOrderByCreationTimeDesc(UserEntity userEntity, ConsentType consentType, Boolean consentConfirmed); + + @Modifying + long deleteByUserEntityAndBankId(UserEntity entity, String bankId); } diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/AccountService.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/AccountService.java index 19c7454bb3..7989073213 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/AccountService.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/AccountService.java @@ -13,26 +13,29 @@ import de.adorsys.opba.fintech.impl.tppclients.AisErrorDecoder; import de.adorsys.opba.fintech.impl.tppclients.ConsentType; import de.adorsys.opba.fintech.impl.tppclients.TppAisClient; -import de.adorsys.opba.tpp.banksearch.api.model.generated.BankProfileResponse; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import java.util.Map; import java.util.Optional; import java.util.UUID; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_FINTECH_ID; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_X_REQUEST_SIGNATURE; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_X_TIMESTAMP_UTC; +import static de.adorsys.opba.fintech.impl.tppclients.Consts.HEADER_COMPUTE_PSU_IP_ADDRESS; @Service @Slf4j @RequiredArgsConstructor public class AccountService { + private final FintechUiConfig uiConfig; private final TppAisClient tppAisClient; private final RestRequestContext restRequestContext; @@ -44,13 +47,14 @@ public class AccountService { public ResponseEntity listAccounts(SessionEntity sessionEntity, String fintechOkUrl, String fintechNOKUrl, - String bankId, LoARetrievalInformation loARetrievalInformation, boolean withBalance, Boolean online) { + String bankId, LoARetrievalInformation loARetrievalInformation, String createConsentIfNone, + boolean withBalance, Boolean psuAuthenticationRequired, Boolean online) { log.info("List of accounts {} with balance {}", loARetrievalInformation, withBalance); final String fintechRedirectCode = UUID.randomUUID().toString(); try { - ResponseEntity accounts = readOpbaResponse(bankId, sessionEntity, fintechRedirectCode, loARetrievalInformation, withBalance, online); + ResponseEntity accounts = readOpbaResponse(bankId, sessionEntity, fintechRedirectCode, loARetrievalInformation, createConsentIfNone, withBalance, psuAuthenticationRequired, online); switch (accounts.getStatusCode()) { case OK: @@ -67,13 +71,14 @@ public ResponseEntity listAccounts(SessionEntity sessionEntity, } catch (ConsentException consentException) { HttpHeaders headers = new HttpHeaders(); headers.add(AisErrorDecoder.X_ERROR_CODE, consentException.getXErrorCode()); - return new ResponseEntity(headers, HttpStatus.valueOf(consentException.getHttpResponseCode())); + return new ResponseEntity<>(headers, HttpStatus.valueOf(consentException.getHttpResponseCode())); } } - + @SneakyThrows private ResponseEntity readOpbaResponse(String bankID, SessionEntity sessionEntity, String redirectCode, - LoARetrievalInformation loARetrievalInformation, boolean withBalance, Boolean online) { + LoARetrievalInformation loARetrievalInformation, String createConsentIfNone, + boolean withBalance, Boolean psuAuthenticationRequired, Boolean online) { UUID xRequestId = UUID.fromString(restRequestContext.getRequestId()); Optional optionalConsent = Optional.empty(); if (loARetrievalInformation.equals(LoARetrievalInformation.FROM_TPP_WITH_AVAILABLE_CONSENT)) { @@ -86,22 +91,22 @@ private ResponseEntity readOpbaResponse(String bankID, SessionEntity sessionEnti optionalConsent.get().getUserEntity().getLoginUserName(), optionalConsent.get().getBankId(), optionalConsent.get().getCreationTime()); - return consentAvailable(bankID, sessionEntity, redirectCode, xRequestId, optionalConsent, withBalance, online); + return consentAvailable(bankID, sessionEntity, redirectCode, xRequestId, optionalConsent, createConsentIfNone, withBalance, psuAuthenticationRequired, online); } - BankProfileResponse bankProfile = searchService.getBankProfileById(bankID).getBody(); - if (null != bankProfile.getBankProfileDescriptor().getConsentSupportByService() - && "true".equals(bankProfile.getBankProfileDescriptor().getConsentSupportByService().get(Actions.LIST_ACCOUNTS.name()))) { + Map consentSupportByService = searchService.getBankProfileById(bankID).getBody().getBankProfileDescriptor().getConsentSupportByService(); + if (null != consentSupportByService && "true".equals(consentSupportByService.get(Actions.LIST_ACCOUNTS.name()))) { log.info("LoA no valid ais consent for user {} bank {} available", sessionEntity.getUserEntity().getLoginUserName(), bankID); - return consentNotYetAvailable(bankID, sessionEntity, redirectCode, xRequestId, optionalConsent); + return consentNotYetAvailable(bankID, sessionEntity, redirectCode, xRequestId, psuAuthenticationRequired, optionalConsent, withBalance, online, createConsentIfNone); } - return consentAvailable(bankID, sessionEntity, redirectCode, xRequestId, optionalConsent, withBalance, online); + return consentAvailable(bankID, sessionEntity, redirectCode, xRequestId, optionalConsent, createConsentIfNone, withBalance, psuAuthenticationRequired, online); } - private ResponseEntity consentAvailable(String bankID, SessionEntity sessionEntity, String redirectCode, - UUID xRequestId, Optional optionalConsent, boolean withBalance, Boolean online) { - log.info("do LOA for bank {} {} consent", bankID, optionalConsent.isPresent() ? "with" : "without"); + private ResponseEntity consentAvailable(String bankProfileID, SessionEntity sessionEntity, String redirectCode, + UUID xRequestId, Optional optionalConsent, String createConsentIfNone, boolean withBalance, + Boolean psuAuthenticationRequired, Boolean online) { + log.info("do LOA for bank {} {} consent", bankProfileID, optionalConsent.isPresent() ? "with" : "without"); UUID serviceSessionID = optionalConsent.map(ConsentEntity::getTppServiceSessionId).orElse(null); return tppAisClient.getAccounts( tppProperties.getServiceSessionPassword(), @@ -112,16 +117,23 @@ private ResponseEntity consentAvailable(String bankID, SessionEntity sessionEnti COMPUTE_X_TIMESTAMP_UTC, COMPUTE_X_REQUEST_SIGNATURE, COMPUTE_FINTECH_ID, - bankID, + UUID.fromString(bankProfileID), + psuAuthenticationRequired, serviceSessionID, + createConsentIfNone, + null, + HEADER_COMPUTE_PSU_IP_ADDRESS, + null, withBalance, online); } - private ResponseEntity consentNotYetAvailable(String bankID, SessionEntity sessionEntity, String redirectCode, UUID xRequestId, Optional optionalConsent) { - log.info("do LOT (instead of loa) for bank {} {} consent", bankID, optionalConsent.isPresent() ? "with" : "without"); + private ResponseEntity consentNotYetAvailable(String bankProfileID, SessionEntity sessionEntity, String redirectCode, UUID xRequestId, + Boolean psuAuthenticationRequired, Optional optionalConsent, + boolean withBalance, Boolean online, String createConsentIfNone) { + log.info("do LOT (instead of loa) for bank {} {} consent", bankProfileID, optionalConsent.isPresent() ? "with" : "without"); UUID serviceSessionID = optionalConsent.map(ConsentEntity::getTppServiceSessionId).orElse(null); - return tppAisClient.getTransactionsWithoutAccountId( + var response = tppAisClient.getTransactionsWithoutAccountId( tppProperties.getServiceSessionPassword(), sessionEntity.getUserEntity().getFintechUserId(), RedirectUrlsEntity.buildOkUrl(uiConfig, redirectCode), @@ -130,6 +142,32 @@ private ResponseEntity consentNotYetAvailable(String bankID, SessionEntity sessi COMPUTE_X_TIMESTAMP_UTC, COMPUTE_X_REQUEST_SIGNATURE, COMPUTE_FINTECH_ID, - bankID, serviceSessionID, null, null, null, null, null); + UUID.fromString(bankProfileID), + psuAuthenticationRequired, + serviceSessionID, + createConsentIfNone, + null, + HEADER_COMPUTE_PSU_IP_ADDRESS, + null, + null, + null, + null, + null, + null, null, null + ); + if (response.getStatusCode() != HttpStatus.OK) { + return response; + } + return consentAvailable( + bankProfileID, + sessionEntity, + redirectCode, + UUID.randomUUID(), + optionalConsent, + createConsentIfNone, + withBalance, + psuAuthenticationRequired, + online + ); } } diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/BankSearchService.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/BankSearchService.java index 9d684ac9e9..21dfca0a86 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/BankSearchService.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/BankSearchService.java @@ -35,12 +35,13 @@ public InlineResponse2001 searchBank(String keyword, Integer start, Integer max) BankSearchResponse bankSearchResponse = tppBankSearchClient.bankSearchGET( xRequestId, - keyword, COMPUTE_X_TIMESTAMP_UTC, COMPUTE_X_REQUEST_SIGNATURE, COMPUTE_FINTECH_ID, + keyword, start, - max).getBody(); + max, + true).getBody(); InlineResponse2001 inlineResponse2001 = new InlineResponse2001().bankDescriptor(Collections.emptyList()); if (bankSearchResponse.getBankDescriptor() != null) { @@ -59,13 +60,14 @@ public InlineResponse2002 searchBankProfile(String bankId) { return new InlineResponse2002().bankProfile(ManualMapper.fromTppToFintech(getBankProfileById(bankId).getBody().getBankProfileDescriptor())); } - public ResponseEntity getBankProfileById(String bankId) { + public ResponseEntity getBankProfileById(String bankProfileId) { UUID xRequestId = UUID.fromString(restRequestContext.getRequestId()); return tppBankSearchClient.bankProfileGET( xRequestId, - bankId, + UUID.fromString(bankProfileId), COMPUTE_X_TIMESTAMP_UTC, COMPUTE_X_REQUEST_SIGNATURE, - COMPUTE_FINTECH_ID); + COMPUTE_FINTECH_ID, + true); } } diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/ConsentService.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/ConsentService.java index 090e690ad6..0f876c2e83 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/ConsentService.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/ConsentService.java @@ -1,5 +1,6 @@ package de.adorsys.opba.fintech.impl.service; +import de.adorsys.opba.fintech.impl.database.entities.SessionEntity; import de.adorsys.opba.fintech.impl.database.repositories.ConsentRepository; import de.adorsys.opba.fintech.impl.properties.TppProperties; import de.adorsys.opba.fintech.impl.tppclients.TppConsentClient; @@ -9,6 +10,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import javax.transaction.Transactional; import java.util.UUID; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_FINTECH_ID; @@ -50,4 +52,9 @@ public boolean confirmPayment(String authId, UUID xRequestId) { log.debug("consent confirmation response code: {}", statusCode); return statusCode.is2xxSuccessful(); } + + @Transactional + public void deleteAllConsentsOfBank(SessionEntity sessionEntity, String bankId) { + consentRepository.deleteByUserEntityAndBankId(sessionEntity.getUserEntity(), bankId); + } } diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/HandleAcceptedService.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/HandleAcceptedService.java index 47dee08f89..61dbeb1c11 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/HandleAcceptedService.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/HandleAcceptedService.java @@ -52,7 +52,7 @@ ResponseEntity handleAccepted(ConsentRepository consentRepository, ConsentType c consentRepository.save(consent); URI location = headers.getLocation(); - log.info("call was accepted, but redirect has to be done for authID:{} location:{}", consent.getTppAuthId(), location); + log.info("call was accepted, but redirect has to be done for serviceSessionId:{} authID:{} location:{}", consent.getTppServiceSessionId(), consent.getTppAuthId(), location); HttpHeaders responseHeaders = sessionLogicService.startRedirect(sessionEntity.getUserEntity(), consent.getTppAuthId()); responseHeaders.add(FIN_TECH_REDIRECT_CODE, fintechRedirectCode); diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/PaymentService.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/PaymentService.java index 424955ae43..1eae42ae94 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/PaymentService.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/PaymentService.java @@ -32,6 +32,7 @@ import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_FINTECH_ID; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_X_REQUEST_SIGNATURE; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_X_TIMESTAMP_UTC; +import static de.adorsys.opba.fintech.impl.tppclients.Consts.HEADER_COMPUTE_PSU_IP_ADDRESS; @Slf4j @Service @@ -51,7 +52,7 @@ public class PaymentService { private final HandleAcceptedService handleAcceptedService; private final PaymentRepository paymentRepository; - public ResponseEntity initiateSinglePayment(String bankId, String accountId, SinglePaymentInitiationRequest singlePaymentInitiationRequest, + public ResponseEntity initiateSinglePayment(String bankProfileId, String accountId, SinglePaymentInitiationRequest singlePaymentInitiationRequest, String fintechOkUrl, String fintechNOkUrl, Boolean xPisPsuAuthenticationRequired) { log.info("fill paramemeters for payment"); final String fintechRedirectCode = UUID.randomUUID().toString(); @@ -76,21 +77,23 @@ public ResponseEntity initiateSinglePayment(String bankId, String accountI COMPUTE_X_TIMESTAMP_UTC, COMPUTE_X_REQUEST_SIGNATURE, COMPUTE_FINTECH_ID, - bankId, - xPisPsuAuthenticationRequired); + UUID.fromString(bankProfileId), + xPisPsuAuthenticationRequired, + HEADER_COMPUTE_PSU_IP_ADDRESS, + null); if (responseOfTpp.getStatusCode() != HttpStatus.ACCEPTED) { throw new RuntimeException("Did expect status 202 from tpp, but got " + responseOfTpp.getStatusCodeValue()); } redirectHandlerService.registerRedirectStateForSession(fintechRedirectCode, fintechOkUrl, fintechNOkUrl); - return handleAcceptedService.handleAccepted(paymentRepository, ConsentType.PIS, bankId, accountId, fintechRedirectCode, sessionEntity, + return handleAcceptedService.handleAccepted(paymentRepository, ConsentType.PIS, bankProfileId, accountId, fintechRedirectCode, sessionEntity, responseOfTpp.getHeaders()); } - public ResponseEntity> retrieveAllSinglePayments(String bankId, String accountId) { + public ResponseEntity> retrieveAllSinglePayments(String bankProfileID, String accountId) { SessionEntity sessionEntity = sessionLogicService.getSession(); // TODO https://app.zenhub.com/workspaces/open-banking-gateway-5dd3b3daf010250001260675/issues/adorsys/open-banking-gateway/812 // TODO https://app.zenhub.com/workspaces/open-banking-gateway-5dd3b3daf010250001260675/issues/adorsys/open-banking-gateway/794 - List payments = paymentRepository.findByUserEntityAndBankIdAndAccountIdAndPaymentConfirmed(sessionEntity.getUserEntity(), bankId, accountId, true); + List payments = paymentRepository.findByUserEntityAndBankIdAndAccountIdAndPaymentConfirmed(sessionEntity.getUserEntity(), bankProfileID, accountId, true); if (payments.isEmpty()) { return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK); } @@ -98,13 +101,12 @@ public ResponseEntity> retrieveAllSing for (PaymentEntity payment : payments) { PaymentInformationResponse body = tppPisPaymentStatusClient.getPaymentInformation(tppProperties.getServiceSessionPassword(), - sessionEntity.getUserEntity().getFintechUserId(), UUID.fromString(restRequestContext.getRequestId()), paymentProduct, COMPUTE_X_TIMESTAMP_UTC, COMPUTE_X_REQUEST_SIGNATURE, COMPUTE_FINTECH_ID, - bankId, + UUID.fromString(bankProfileID), payment.getTppServiceSessionId()).getBody(); PaymentInitiationWithStatusResponse paymentInitiationWithStatusResponse = Mappers .getMapper(PaymentInitiationWithStatusResponseMapper.class) diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/TransactionService.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/TransactionService.java index 76277ddba4..9ece6bf5b8 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/TransactionService.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/service/TransactionService.java @@ -1,5 +1,6 @@ package de.adorsys.opba.fintech.impl.service; +import com.fasterxml.jackson.databind.ObjectMapper; import de.adorsys.opba.fintech.impl.config.FintechUiConfig; import de.adorsys.opba.fintech.impl.controller.utils.LoTRetrievalInformation; import de.adorsys.opba.fintech.impl.controller.utils.RestRequestContext; @@ -13,6 +14,7 @@ import de.adorsys.opba.fintech.impl.tppclients.TppAisClient; import de.adorsys.opba.tpp.ais.api.model.generated.TransactionsResponse; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -25,12 +27,14 @@ import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_FINTECH_ID; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_X_REQUEST_SIGNATURE; import static de.adorsys.opba.fintech.impl.tppclients.Consts.COMPUTE_X_TIMESTAMP_UTC; +import static de.adorsys.opba.fintech.impl.tppclients.Consts.HEADER_COMPUTE_PSU_IP_ADDRESS; @Service @Slf4j @RequiredArgsConstructor public class TransactionService { + private final FintechUiConfig uiConfig; private final TppAisClient tppAisClient; private final RestRequestContext restRequestContext; @@ -38,24 +42,27 @@ public class TransactionService { private final RedirectHandlerService redirectHandlerService; private final ConsentRepository consentRepository; private final HandleAcceptedService handleAcceptedService; + private final ObjectMapper mapper; - public ResponseEntity listTransactions(SessionEntity sessionEntity, String fintechOkUrl, String fintechNOkUrl, String bankId, - String accountId, LocalDate dateFrom, LocalDate dateTo, String entryReferenceFrom, + @SuppressWarnings("checkstyle:MethodLength") // FIXME - It is just too many lines of text + @SneakyThrows + public ResponseEntity listTransactions(SessionEntity sessionEntity, String fintechOkUrl, String fintechNOkUrl, String bankProfileID, + String accountId, String createConsentIfNone, LocalDate dateFrom, LocalDate dateTo, String entryReferenceFrom, String bookingStatus, Boolean deltaList, LoTRetrievalInformation loTRetrievalInformation, - Boolean online) { + Boolean psuAuthenticationRequired, Boolean online) { log.info("LoT {}", loTRetrievalInformation); String fintechRedirectCode = UUID.randomUUID().toString(); Optional optionalConsent = Optional.empty(); if (loTRetrievalInformation.equals(LoTRetrievalInformation.FROM_TPP_WITH_AVAILABLE_CONSENT)) { optionalConsent = consentRepository.findFirstByUserEntityAndBankIdAndConsentTypeAndConsentConfirmedOrderByCreationTimeDesc(sessionEntity.getUserEntity(), - bankId, ConsentType.AIS, Boolean.TRUE); + bankProfileID, ConsentType.AIS, Boolean.TRUE); } if (optionalConsent.isPresent()) { log.info("LoT found valid {} consent for user {} bank {} from {}", optionalConsent.get().getConsentType(), optionalConsent.get().getUserEntity().getLoginUserName(), optionalConsent.get().getBankId(), optionalConsent.get().getCreationTime()); } else { - log.info("LoT no valid ais consent for user {} bank {} available", sessionEntity.getUserEntity().getLoginUserName(), bankId); + log.info("LoT no valid ais consent for user {} bank {} available", sessionEntity.getUserEntity().getLoginUserName(), bankProfileID); } ResponseEntity transactions = tppAisClient.getTransactions( accountId, tppProperties.getServiceSessionPassword(), @@ -65,16 +72,31 @@ public ResponseEntity listTransactions(SessionEntity sessionEntity, String finte UUID.fromString(restRequestContext.getRequestId()), COMPUTE_X_TIMESTAMP_UTC, COMPUTE_X_REQUEST_SIGNATURE, - COMPUTE_FINTECH_ID, bankId, + COMPUTE_FINTECH_ID, + UUID.fromString(bankProfileID), + psuAuthenticationRequired, optionalConsent.map(ConsentEntity::getTppServiceSessionId).orElse(null), - dateFrom, dateTo, entryReferenceFrom, bookingStatus, deltaList, online); + createConsentIfNone, + null, + HEADER_COMPUTE_PSU_IP_ADDRESS, + null, + dateFrom, + dateTo, + entryReferenceFrom, + bookingStatus, + deltaList, + online, + true, + null, + null + ); switch (transactions.getStatusCode()) { case OK: return new ResponseEntity<>(ManualMapper.fromTppToFintech(transactions.getBody()), HttpStatus.OK); case ACCEPTED: log.info("create redirect entity for lot for redirectcode {}", fintechRedirectCode); redirectHandlerService.registerRedirectStateForSession(fintechRedirectCode, fintechOkUrl, fintechNOkUrl); - return handleAcceptedService.handleAccepted(consentRepository, ConsentType.AIS, bankId, fintechRedirectCode, sessionEntity, transactions.getHeaders()); + return handleAcceptedService.handleAccepted(consentRepository, ConsentType.AIS, bankProfileID, fintechRedirectCode, sessionEntity, transactions.getHeaders()); case UNAUTHORIZED: return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); default: diff --git a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/tppclients/Consts.java b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/tppclients/Consts.java index 81650b90f1..a6992b2be3 100644 --- a/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/tppclients/Consts.java +++ b/fintech-examples/fintech-impl/src/main/java/de/adorsys/opba/fintech/impl/tppclients/Consts.java @@ -8,6 +8,7 @@ public class Consts { public static final String HEADER_SESSION_MAX_AGE = "X-SESSION-MAX-AGE"; public static final String HEADER_REDIRECT_MAX_AGE = "X-REDIRECT-MAX-AGE"; public static final String HEADER_X_REQUEST_ID = "X-REQUEST-ID"; + public static final Boolean HEADER_COMPUTE_PSU_IP_ADDRESS = true; // Actual values are set in feign request interceptor (FeignConfig.java) public static final String COMPUTE_X_TIMESTAMP_UTC = null; diff --git a/fintech-examples/fintech-impl/src/test/java/de/adorsys/opba/fintech/impl/service/BankSearchServiceTest.java b/fintech-examples/fintech-impl/src/test/java/de/adorsys/opba/fintech/impl/service/BankSearchServiceTest.java index 190f035dfd..427292a530 100644 --- a/fintech-examples/fintech-impl/src/test/java/de/adorsys/opba/fintech/impl/service/BankSearchServiceTest.java +++ b/fintech-examples/fintech-impl/src/test/java/de/adorsys/opba/fintech/impl/service/BankSearchServiceTest.java @@ -40,12 +40,13 @@ void searchBank_nonExistingBank() { when(restRequestContext.getRequestId()).thenReturn(UUID.randomUUID().toString()); when(tppBankSearchClient.bankSearchGET( any(), - eq(INVALID_KEYWORD), any(), any(), any(), + eq(INVALID_KEYWORD), eq(start), - eq(max))).thenReturn(ResponseEntity.ok().body(bankSearchResponse)); + eq(max), + any())).thenReturn(ResponseEntity.ok().body(bankSearchResponse)); // When InlineResponse2001 actual = bankSearchService.searchBank(INVALID_KEYWORD, start, max); diff --git a/fintech-examples/fintech-last-module-codecoverage/pom.xml b/fintech-examples/fintech-last-module-codecoverage/pom.xml index 422c22dbd7..1b2c7f9203 100644 --- a/fintech-examples/fintech-last-module-codecoverage/pom.xml +++ b/fintech-examples/fintech-last-module-codecoverage/pom.xml @@ -5,7 +5,7 @@ de.adorsys.opba fintech-examples - 0.30.0.1 + 1.0.0 4.0.0 diff --git a/fintech-examples/fintech-server/Dockerfile b/fintech-examples/fintech-server/Dockerfile index 2918f4d3f8..d84f117f30 100644 --- a/fintech-examples/fintech-server/Dockerfile +++ b/fintech-examples/fintech-server/Dockerfile @@ -3,7 +3,7 @@ FROM adoptopenjdk/openjdk11:jre-11.0.9_11-alpine ENV APP_HOME /usr/app WORKDIR $APP_HOME -COPY target/*.jar . +COPY target/*exec.jar . EXPOSE 8086 diff --git a/fintech-examples/fintech-server/pom.xml b/fintech-examples/fintech-server/pom.xml index a626d58a92..816cfc22c1 100644 --- a/fintech-examples/fintech-server/pom.xml +++ b/fintech-examples/fintech-server/pom.xml @@ -5,7 +5,7 @@ de.adorsys.opba fintech-examples - 0.30.0.1 + 1.0.0 fintech-server @@ -123,6 +123,8 @@ ${spring-boot.version} de.adorsys.opba.fintech.server.FinTechServer + false + exec diff --git a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechApiBaseTest.java b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechApiBaseTest.java index 7033bf8090..918ba5d66e 100644 --- a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechApiBaseTest.java +++ b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechApiBaseTest.java @@ -43,7 +43,7 @@ protected String getFilenameBankSearch(String keyword, Integer start, Integer ma + POSTFIX; } - protected String getFilenameBankProfile(String bankUUID) { + protected String getFilenameBankProfile(UUID bankUUID) { return BANK_PROFILE_RESPONSE_PREFIX + "-" + bankUUID + POSTFIX; diff --git a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechBankSearchApiTest.java b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechBankSearchApiTest.java index 598b92aebe..0e1594f16c 100644 --- a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechBankSearchApiTest.java +++ b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechBankSearchApiTest.java @@ -122,7 +122,7 @@ public void bankSearchAuthorized() { final Integer start = 1; final Integer max = 2; - when(tppBankSearchClientFeignMock.bankSearchGET(any(), eq(keyword), any(), any(), any(), eq(start), eq(max))) + when(tppBankSearchClientFeignMock.bankSearchGET(any(), any(), any(), any(), eq(keyword), eq(start), eq(max), any())) .thenReturn(ResponseEntity.ok(GSON.fromJson(readFile(getFilenameBankSearch(keyword, start, max)), BankSearchResponse.class))); LoginBody loginBody = new LoginBody("peter", "1234"); @@ -156,7 +156,7 @@ public void bankProfileAuthorized() { @NoArgsConstructor static class BankProfileTestResult { String sessionCookieValue = null; - String bankUUID = null; + UUID bankUUID = null; List services = null; } @@ -177,7 +177,7 @@ BankProfileTestResult getBankProfileTestResult() { final Integer max = 2; log.info("DO Bank Search ({}, {}, {}) ==============================", keyword, start, max); - when(tppBankSearchClientFeignMock.bankSearchGET(any(), eq(keyword), any(), any(), any(), eq(start), eq(max))) + when(tppBankSearchClientFeignMock.bankSearchGET(any(), any(), any(), any(), eq(keyword), eq(start), eq(max), any())) .thenReturn(ResponseEntity.ok(GSON.fromJson(readFile(getFilenameBankSearch(keyword, start, max)), BankSearchResponse.class))); result.setBankUUID(bankSearchOk(keyword, start, max)); @@ -185,7 +185,7 @@ BankProfileTestResult getBankProfileTestResult() { { log.info("DO Bank Profile ({}) ============================== ", result.getBankUUID()); - when(tppBankSearchClientFeignMock.bankProfileGET(any(), eq(result.getBankUUID()), any(), any(), any())) + when(tppBankSearchClientFeignMock.bankProfileGET(any(), eq(result.getBankUUID()), any(), any(), any(), any())) .thenReturn(ResponseEntity.ok(GSON.fromJson(readFile(getFilenameBankProfile(result.getBankUUID())), BankProfileResponse.class))); result.setServices(bankProfile(result.getBankUUID())); @@ -196,16 +196,16 @@ BankProfileTestResult getBankProfileTestResult() { } /** - * @param bankUUID + * @param bankProfileUUID * @return List of Services of Bank */ @SneakyThrows - List bankProfile(String bankUUID) { + List bankProfile(UUID bankProfileUUID) { MvcResult mvcResult = this.mvc .perform(get(FIN_TECH_BANK_PROFILE_URL) .header(Consts.HEADER_X_REQUEST_ID, UUID.randomUUID().toString()) .header(Consts.HEADER_XSRF_TOKEN, restRequestContext.getXsrfTokenHeaderField()) - .param("bankId", bankUUID)) + .param("bankProfileId", bankProfileUUID.toString())) .andDo(print()) .andExpect(status().isOk()) .andReturn(); @@ -276,10 +276,10 @@ MvcResult plainLogout() { * @return first BankUUID of found list */ @SneakyThrows - String bankSearchOk(String keyword, Integer start, Integer max) { + UUID bankSearchOk(String keyword, Integer start, Integer max) { MvcResult result = plainBankSearch(keyword, start, max); assertEquals(HttpStatus.OK.value(), result.getResponse().getStatus()); - return new JSONObject(result.getResponse().getContentAsString()).getJSONArray("bankDescriptor").getJSONObject(0).get("uuid").toString(); + return UUID.fromString(new JSONObject(result.getResponse().getContentAsString()).getJSONArray("bankDescriptor").getJSONObject(0).get("uuid").toString()); } @SneakyThrows diff --git a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListAccountsTest.java b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListAccountsTest.java index 4d686c7b8f..4e2cf273ab 100644 --- a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListAccountsTest.java +++ b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListAccountsTest.java @@ -51,7 +51,7 @@ public class FinTechListAccountsTest extends FinTechBankSearchApiTest { private static final String FIN_TECH_LIST_ACCOUNTS_URL = "/v1/ais/banks/{bank-id}/accounts"; - private static final String NO_CONSENT_BANK_ID = "aaaaaaaaa-ee6e-45f9-9163-b97320c6881a"; + private static final UUID NO_CONSENT_BANK_ID = UUID.fromString("356938ab-9561-408f-ac7a-a9089c1623b7"); private static final String USERNAME = "peter"; private static final String PASSWORD = "1234"; @@ -79,7 +79,7 @@ public void testListAccountsFor200() { @SneakyThrows List listAccountsForOk(BankProfileTestResult result) { - when(tppAisClientFeignMock.getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) + when(tppAisClientFeignMock.getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(ResponseEntity.ok(GSON.fromJson(readFile("TPP_LIST_ACCOUNTS.json"), AccountList.class))); MvcResult mvcResult = plainListAccounts(result.getBankUUID()); @@ -104,20 +104,20 @@ public void testListAccountsFor303() { .build(); BankProfileTestResult result = getBankProfileTestResult(); createConsent(null, null); - when(tppAisClientFeignMock.getTransactionsWithoutAccountId(any(), any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any())).thenReturn(accepted); + when(tppAisClientFeignMock.getTransactionsWithoutAccountId(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(accepted); MvcResult mvcResult = plainListAccounts(result.getBankUUID()); assertEquals(ACCEPTED.value(), mvcResult.getResponse().getStatus()); - verify(tppAisClientFeignMock).getTransactionsWithoutAccountId(any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any(), any()); - verify(tppAisClientFeignMock, never()).getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); + verify(tppAisClientFeignMock).getTransactionsWithoutAccountId(any(), any(), any(), any(), any(), any(), any(), any(), any(), + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); + verify(tppAisClientFeignMock, never()).getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); } @Test @SneakyThrows public void testListAccountsFor303NoConsentSupport() { - when(tppBankSearchClientFeignMock.bankProfileGET(any(), eq(NO_CONSENT_BANK_ID), any(), any(), any())) + when(tppBankSearchClientFeignMock.bankProfileGET(any(), eq(NO_CONSENT_BANK_ID), any(), any(), any(), any())) .thenReturn(ResponseEntity.ok(GSON.fromJson(readFile(getFilenameBankProfile(NO_CONSENT_BANK_ID)), BankProfileResponse.class))); when(restRequestContext.getRequestId()).thenReturn(UUID.randomUUID().toString()); @@ -128,22 +128,22 @@ public void testListAccountsFor303NoConsentSupport() { .location(new URI("affe")) .build(); createConsent(null, null); - when(tppAisClientFeignMock.getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any())).thenReturn(accepted); + when(tppAisClientFeignMock.getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), + any(), any(), any(), any())).thenReturn(accepted); MvcResult mvcResult = plainListAccounts(NO_CONSENT_BANK_ID); assertEquals(ACCEPTED.value(), mvcResult.getResponse().getStatus()); - verify(tppAisClientFeignMock, never()).getTransactions(any(), any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any(), any(), any()); - verify(tppAisClientFeignMock).getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); + verify(tppAisClientFeignMock, never()).getTransactions(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), // CPD-OFF + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); + verify(tppAisClientFeignMock).getAccounts(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); // CPD-ON } @SneakyThrows - MvcResult plainListAccounts(String bankUUID) { - log.info("bankUUID {}", bankUUID); + MvcResult plainListAccounts(UUID bankProfileUUID) { + log.info("bankProfileUUID {}", bankProfileUUID); return this.mvc - .perform(get(FIN_TECH_LIST_ACCOUNTS_URL, bankUUID) + .perform(get(FIN_TECH_LIST_ACCOUNTS_URL, bankProfileUUID) .header(Consts.HEADER_X_REQUEST_ID, restRequestContext.getRequestId()) .header(Consts.HEADER_XSRF_TOKEN, restRequestContext.getXsrfTokenHeaderField()) .header("Fintech-Redirect-URL-OK", "ok") diff --git a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListTransactionsTest.java b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListTransactionsTest.java index 99d95e6112..bbb8019e49 100644 --- a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListTransactionsTest.java +++ b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/FinTechListTransactionsTest.java @@ -6,6 +6,7 @@ import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; +import org.mockito.stubbing.OngoingStubbing; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.web.servlet.MvcResult; @@ -34,9 +35,7 @@ public void testListTransactionsForOk() { BankProfileTestResult result= getBankProfileTestResult(); createConsent(UUID.randomUUID().toString(), UUID.randomUUID()); List accountIDs = listAccountsForOk(result); - when(tppAisClientFeignMock.getTransactions(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any(), any())) - .thenReturn(ResponseEntity.ok(GSON.fromJson(readFile("TPP_LIST_TRANSACTIONS.json"), TransactionsResponse.class))); + mockTransactions().thenReturn(ResponseEntity.ok(GSON.fromJson(readFile("TPP_LIST_TRANSACTIONS.json"), TransactionsResponse.class))); List amounts = listAmounts(result.getBankUUID(), accountIDs.get(0)); assertTrue(amounts.containsAll(Arrays.asList(new String[]{"1000"}))); } @@ -52,16 +51,20 @@ public void testListTransactionsForRedirect() { BankProfileTestResult result = getBankProfileTestResult(); createConsent(UUID.randomUUID().toString(), UUID.randomUUID()); - when(tppAisClientFeignMock.getTransactions(any(), any(), any(), any(), any(), any(), any(), any(), any(), - any(), any(), any(), any(), any(), any(), any(), any())) + mockTransactions() .thenReturn(accepted); MvcResult mvcResult = plainListAmounts(result.getBankUUID(), listAccountsForOk(result).get(0)); assertEquals(HttpStatus.ACCEPTED.value(), mvcResult.getResponse().getStatus()); } + private OngoingStubbing> mockTransactions() { + return when(tppAisClientFeignMock.getTransactions(any(), any(), any(), any(), any(), any(), any(), any(), any(), + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())); + } + @SneakyThrows - List listAmounts(String bankUUID, String accountID) { - MvcResult mvcResult = plainListAmounts(bankUUID, accountID); + List listAmounts(UUID bankProfileUUID, String accountID) { + MvcResult mvcResult = plainListAmounts(bankProfileUUID, accountID); assertEquals(HttpStatus.OK.value(), mvcResult.getResponse().getStatus()); List amountList = new ArrayList<>(); @@ -74,9 +77,9 @@ List listAmounts(String bankUUID, String accountID) { return amountList; } - private MvcResult plainListAmounts(String bankUUID, String accountID) throws Exception { + private MvcResult plainListAmounts(UUID bankProfileUUID, String accountID) throws Exception { return this.mvc - .perform(get(FIN_TECH_LIST_TRANSACTIONS_URL, bankUUID, accountID) + .perform(get(FIN_TECH_LIST_TRANSACTIONS_URL, bankProfileUUID, accountID) .header(Consts.HEADER_X_REQUEST_ID, restRequestContext.getRequestId()) .header(Consts.HEADER_XSRF_TOKEN, restRequestContext.getXsrfTokenHeaderField()) .header("Fintech-Redirect-URL-OK", "ok") diff --git a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppAisClientFeignMock.java b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppAisClientFeignMock.java index c76d046dd8..73a50432c3 100644 --- a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppAisClientFeignMock.java +++ b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppAisClientFeignMock.java @@ -3,7 +3,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import de.adorsys.opba.fintech.impl.tppclients.TppAisClient; import de.adorsys.opba.tpp.ais.api.model.generated.AccountList; +import de.adorsys.opba.tpp.ais.api.model.generated.SessionStatusDetails; import de.adorsys.opba.tpp.ais.api.model.generated.TransactionsResponse; +import de.adorsys.opba.tpp.ais.api.model.generated.UpdateAisExternalSessionStatus; import org.springframework.http.ResponseEntity; import javax.servlet.http.HttpServletRequest; @@ -24,10 +26,15 @@ public ResponseEntity getAccounts( String xTimestampUTC, String xRequestSignature, String fintechId, - String bankID, + UUID bankProfileID, + Boolean xPsuAuthenticationRequired, UUID serviceSessionID, - Boolean useObgCache, - Boolean withBalance + String createConsentIfNone, + String importUserData, + Boolean computePsuIpAddress, + String psuIpAddress, + Boolean withBalance, + Boolean online ) { return null; } @@ -43,14 +50,22 @@ public ResponseEntity getTransactions( String xTimestampUTC, String xRequestSignature, String fintechId, - String bankID, + UUID bankProfileID, + Boolean xPsuAuthenticationRequired, UUID serviceSessionID, + String createConsentIfNone, + String importUserData, + Boolean computePsuIpAddress, + String psuIpAddress, LocalDate dateFrom, @Valid LocalDate dateTo, String entryReferenceFrom, @Valid String bookingStatus, @Valid Boolean deltaList, - Boolean online + Boolean online, + Boolean analytics, + Integer page, + Integer pageSize ) { return null; } @@ -65,17 +80,41 @@ public ResponseEntity getTransactionsWithoutAccountId( String xTimestampUTC, String xRequestSignature, String fintechID, - String bankID, + UUID bankProfileID, + Boolean xPsuAuthenticationRequired, UUID serviceSessionID, + String createConsentIfNone, + String importUserData, + Boolean computePsuIpAddress, + String psuIpAddress, LocalDate dateFrom, LocalDate dateTo, String entryReferenceFrom, String bookingStatus, - Boolean deltaList + Boolean deltaList, + Integer page, + Integer pageSize ) { return null; } + @Override + public ResponseEntity deleteConsent(UUID serviceSessionID, String serviceSessionPassword, UUID xRequestID, + String xTimestampUTC, String xRequestSignature, String fintechID, Boolean deleteAll) { + return null; + } + + @Override + public ResponseEntity getAisSessionStatus(UUID serviceSessionID, String serviceSessionPassword, + UUID xRequestID, String xTimestampUTC, String xRequestSignature, String fintechID) { + return null; + } + + @Override + public ResponseEntity updateExternalAisSession(UUID serviceSessionID, String serviceSessionPassword, UUID xRequestID, String xTimestampUTC, String xRequestSignature, String fintechID) { + return null; + } + // TODO: https://github.com/adorsys/open-banking-gateway/issues/559 @Override public Optional getObjectMapper() { diff --git a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppBankSearchClientFeignMock.java b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppBankSearchClientFeignMock.java index 07ae77ca43..0ec58add46 100644 --- a/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppBankSearchClientFeignMock.java +++ b/fintech-examples/fintech-server/src/test/java/de/adorsys/opba/fintech/server/feignmocks/TppBankSearchClientFeignMock.java @@ -16,10 +16,11 @@ public class TppBankSearchClientFeignMock implements TppBankSearchClient { @Override public ResponseEntity bankProfileGET(UUID xRequestID, - @NotNull @Valid String bankId, + @NotNull @Valid UUID bankProfileID, String xTimestampUTC, String xRequestSignature, - String fintechId) { + String fintechId, + Boolean onlyActive) { return null; } @@ -31,7 +32,8 @@ public ResponseEntity bankSearchGET( String xRequestSignature, String fintechId, @Valid Integer start, - @Valid Integer max) { + @Valid Integer max, + Boolean onlyActive) { return null; } diff --git a/fintech-examples/fintech-server/src/test/resources/TPP_BankProfileResponse-aaaaaaaaa-ee6e-45f9-9163-b97320c6881a.json b/fintech-examples/fintech-server/src/test/resources/TPP_BankProfileResponse-356938ab-9561-408f-ac7a-a9089c1623b7.json similarity index 100% rename from fintech-examples/fintech-server/src/test/resources/TPP_BankProfileResponse-aaaaaaaaa-ee6e-45f9-9163-b97320c6881a.json rename to fintech-examples/fintech-server/src/test/resources/TPP_BankProfileResponse-356938ab-9561-408f-ac7a-a9089c1623b7.json diff --git a/fintech-examples/fintech-server/src/test/resources/TPP_LIST_TRANSACTIONS.json b/fintech-examples/fintech-server/src/test/resources/TPP_LIST_TRANSACTIONS.json index f524036630..111593895c 100644 --- a/fintech-examples/fintech-server/src/test/resources/TPP_LIST_TRANSACTIONS.json +++ b/fintech-examples/fintech-server/src/test/resources/TPP_LIST_TRANSACTIONS.json @@ -112,5 +112,11 @@ "proprietaryBankTransactionCode": "string" } ] + }, + "paging": { + "page": 1, + "pageSize": 20, + "pageCount": 1, + "totalCount": 2 } } \ No newline at end of file diff --git a/fintech-examples/fintech-ui/angular.json b/fintech-examples/fintech-ui/angular.json index 8db324b56a..b1670be9ac 100644 --- a/fintech-examples/fintech-ui/angular.json +++ b/fintech-examples/fintech-ui/angular.json @@ -23,10 +23,7 @@ "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "aot": false, - "assets": [ - "src/favicon.ico", - "src/assets" - ], + "assets": ["src/favicon.ico", "src/assets"], "styles": [ "src/styles.scss", "node_modules/font-awesome/css/font-awesome.css", @@ -92,28 +89,16 @@ "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.scss" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], "scripts": [] } }, "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": [ - "tsconfig.app.json", - "tsconfig.spec.json", - "e2e/tsconfig.json" - ], - "exclude": [ - "**/node_modules/**", - "**/api/**" - ] + "tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"], + "exclude": ["**/node_modules/**", "**/api/**"] } }, "e2e": { diff --git a/fintech-examples/fintech-ui/package.json b/fintech-examples/fintech-ui/package.json index 7b0094b0f7..86b09df3fa 100644 --- a/fintech-examples/fintech-ui/package.json +++ b/fintech-examples/fintech-ui/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "openapi-gen": "openapi-generator generate -g typescript-angular -o src/app/api -i ../fintech-api/src/main/resources/static/fintech_api.yml", + "openapi-gen": "openapi-generator generate -g typescript-angular -o src/app/api -i ../fintech-api/src/main/resources/static/fintech_api.yml --skip-validate-spec", "start": "npm run serve", "serve": "ng serve --port=4444 --proxy-config=proxy.conf.json", "serve:dev": "ng serve --port=4444 --proxy-config=proxy-conf-dev-backend.js", diff --git a/fintech-examples/fintech-ui/src/app/api/.openapi-generator/VERSION b/fintech-examples/fintech-ui/src/app/api/.openapi-generator/VERSION index 078bf8b7dd..ecedc98d1d 100644 --- a/fintech-examples/fintech-ui/src/app/api/.openapi-generator/VERSION +++ b/fintech-examples/fintech-ui/src/app/api/.openapi-generator/VERSION @@ -1 +1 @@ -4.2.2 \ No newline at end of file +4.3.1 \ No newline at end of file diff --git a/fintech-examples/fintech-ui/src/app/api/README.md b/fintech-examples/fintech-ui/src/app/api/README.md index 6fdf5e362f..e0c9192e33 100644 --- a/fintech-examples/fintech-ui/src/app/api/README.md +++ b/fintech-examples/fintech-ui/src/app/api/README.md @@ -92,6 +92,31 @@ export function apiConfigFactory (): Configuration => { export class AppModule {} ``` +``` +// configuring providers with an authentication service that manages your access tokens +import { ApiModule, Configuration } from ''; + +@NgModule({ + imports: [ ApiModule ], + declarations: [ AppComponent ], + providers: [ + { + provide: Configuration, + useFactory: (authService: AuthService) => new Configuration( + { + basePath: environment.apiUrl, + accessToken: authService.getAccessToken.bind(authService) + } + ), + deps: [AuthService], + multi: false + } + ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + ``` import { DefaultApi } from ''; diff --git a/fintech-examples/fintech-ui/src/app/api/api.module.ts b/fintech-examples/fintech-ui/src/app/api/api.module.ts index 92fa029b8a..8dad7db14b 100644 --- a/fintech-examples/fintech-ui/src/app/api/api.module.ts +++ b/fintech-examples/fintech-ui/src/app/api/api.module.ts @@ -8,22 +8,17 @@ import { FinTechAuthorizationService } from './api/finTechAuthorization.service' import { FinTechBankSearchService } from './api/finTechBankSearch.service'; import { FinTechOauth2AuthenticationService } from './api/finTechOauth2Authentication.service'; import { FintechRetrieveAllSinglePaymentsService } from './api/fintechRetrieveAllSinglePayments.service'; +import { FintechRetrieveConsentService } from './api/fintechRetrieveConsent.service'; import { FintechSinglePaymentInitiationService } from './api/fintechSinglePaymentInitiation.service'; @NgModule({ imports: [], declarations: [], exports: [], - providers: [ - FinTechAccountInformationService, - FinTechAuthorizationService, - FinTechBankSearchService, - FinTechOauth2AuthenticationService, - FintechRetrieveAllSinglePaymentsService, - FintechSinglePaymentInitiationService ] + providers: [] }) export class ApiModule { - public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders { + public static forRoot(configurationFactory: () => Configuration): ModuleWithProviders { return { ngModule: ApiModule, providers: [ { provide: Configuration, useFactory: configurationFactory } ] diff --git a/fintech-examples/fintech-ui/src/app/api/api/api.ts b/fintech-examples/fintech-ui/src/app/api/api/api.ts index 06fe459d81..f15065d372 100644 --- a/fintech-examples/fintech-ui/src/app/api/api/api.ts +++ b/fintech-examples/fintech-ui/src/app/api/api/api.ts @@ -8,6 +8,8 @@ export * from './finTechOauth2Authentication.service'; import { FinTechOauth2AuthenticationService } from './finTechOauth2Authentication.service'; export * from './fintechRetrieveAllSinglePayments.service'; import { FintechRetrieveAllSinglePaymentsService } from './fintechRetrieveAllSinglePayments.service'; +export * from './fintechRetrieveConsent.service'; +import { FintechRetrieveConsentService } from './fintechRetrieveConsent.service'; export * from './fintechSinglePaymentInitiation.service'; import { FintechSinglePaymentInitiationService } from './fintechSinglePaymentInitiation.service'; -export const APIS = [FinTechAccountInformationService, FinTechAuthorizationService, FinTechBankSearchService, FinTechOauth2AuthenticationService, FintechRetrieveAllSinglePaymentsService, FintechSinglePaymentInitiationService]; +export const APIS = [FinTechAccountInformationService, FinTechAuthorizationService, FinTechBankSearchService, FinTechOauth2AuthenticationService, FintechRetrieveAllSinglePaymentsService, FintechRetrieveConsentService, FintechSinglePaymentInitiationService]; diff --git a/fintech-examples/fintech-ui/src/app/api/api/finTechAccountInformation.service.ts b/fintech-examples/fintech-ui/src/app/api/api/finTechAccountInformation.service.ts index e900915269..616c66a759 100644 --- a/fintech-examples/fintech-ui/src/app/api/api/finTechAccountInformation.service.ts +++ b/fintech-examples/fintech-ui/src/app/api/api/finTechAccountInformation.service.ts @@ -1,9 +1,9 @@ /** * Open Banking Gateway FinTech Example API - * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. + * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -17,10 +17,10 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; -import { AccountList } from '../model/accountList'; -import { ErrorResponse } from '../model/errorResponse'; -import { PsuMessage } from '../model/psuMessage'; -import { TransactionsResponse } from '../model/transactionsResponse'; +import { AccountList } from '../model/models'; +import { ErrorResponse } from '../model/models'; +import { PsuMessage } from '../model/models'; +import { TransactionsResponse } from '../model/models'; import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; import { Configuration } from '../configuration'; @@ -52,32 +52,70 @@ export class FinTechAccountInformationService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** * Provides list of available accounts for the given bank - * Read the identifiers of the available payment accounts. If required by the bank, PSU consent will be obtained before returning the list of bank accounts. Returns all identifiers of the accounts, to which an account access has been granted to by the PSU. In addition, relevant information about the accounts and hyperlinks to corresponding account information resources are provided if a related consent has been already granted. - * @param bankId - * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. - * @param fintechRedirectURLOK - * @param fintechRedirectURLNOK - * @param loARetrievalInformation + * Read the identifiers of the available payment accounts. If required by the bank, PSU consent will be obtained before returning the list of bank accounts. Returns all identifiers of the accounts, to which an account access has been granted to by the PSU. In addition, relevant information about the accounts and hyperlinks to corresponding account information resources are provided if a related consent has been already granted. + * @param bankId + * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param fintechRedirectURLOK + * @param fintechRedirectURLNOK + * @param loARetrievalInformation + * @param xPsuAuthenticationRequired If false, login form to OPBA will not be displayed as there might be nothing to share for payments, so that authentication is not necessary. If absent or true - login form for payments will be displayed. + * @param xCreateConsentIfNone * @param withBalance Provides balances for the given accounts - * @param online If false, new data will be requested and cache will be updated + * @param online If true, new data will be requested and cache will be updated * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public aisAccountsGET(bankId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', withBalance?: boolean, online?: boolean, observe?: 'body', reportProgress?: boolean): Observable; - public aisAccountsGET(bankId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', withBalance?: boolean, online?: boolean, observe?: 'response', reportProgress?: boolean): Observable>; - public aisAccountsGET(bankId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', withBalance?: boolean, online?: boolean, observe?: 'events', reportProgress?: boolean): Observable>; - public aisAccountsGET(bankId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', withBalance?: boolean, online?: boolean, observe: any = 'body', reportProgress: boolean = false ): Observable { + public aisAccountsGET(bankId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, withBalance?: boolean, online?: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public aisAccountsGET(bankId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, withBalance?: boolean, online?: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public aisAccountsGET(bankId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, withBalance?: boolean, online?: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public aisAccountsGET(bankId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loARetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, withBalance?: boolean, online?: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (bankId === null || bankId === undefined) { throw new Error('Required parameter bankId was null or undefined when calling aisAccountsGET.'); } if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling aisAccountsGET.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling aisAccountsGET.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling aisAccountsGET.'); } if (fintechRedirectURLOK === null || fintechRedirectURLOK === undefined) { throw new Error('Required parameter fintechRedirectURLOK was null or undefined when calling aisAccountsGET.'); @@ -91,18 +129,23 @@ export class FinTechAccountInformationService { let queryParameters = new HttpParams({encoder: this.encoder}); if (withBalance !== undefined && withBalance !== null) { - queryParameters = queryParameters.set('withBalance', withBalance); + queryParameters = this.addToHttpParams(queryParameters, + withBalance, 'withBalance'); } if (online !== undefined && online !== null) { - queryParameters = queryParameters.set('online', online); + queryParameters = this.addToHttpParams(queryParameters, + online, 'online'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); + } + if (xPsuAuthenticationRequired !== undefined && xPsuAuthenticationRequired !== null) { + headers = headers.set('X-Psu-Authentication-Required', String(xPsuAuthenticationRequired)); } if (fintechRedirectURLOK !== undefined && fintechRedirectURLOK !== null) { headers = headers.set('Fintech-Redirect-URL-OK', String(fintechRedirectURLOK)); @@ -113,21 +156,106 @@ export class FinTechAccountInformationService { if (loARetrievalInformation !== undefined && loARetrievalInformation !== null) { headers = headers.set('LoARetrievalInformation', String(loARetrievalInformation)); } + if (xCreateConsentIfNone !== undefined && xCreateConsentIfNone !== null) { + headers = headers.set('X-Create-Consent-If-None', String(xCreateConsentIfNone)); + } // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + if (this.configuration.apiKeys) { + const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"]; + if (key) { + } + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/ais/banks/${encodeURIComponent(String(bankId))}/accounts`, { params: queryParameters, + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Deletes all consents that are associated with bank + * Deletes all consents that are associated with bank + * @param bankId + * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public aisConsentsDELETE(bankId: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public aisConsentsDELETE(bankId: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public aisConsentsDELETE(bankId: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public aisConsentsDELETE(bankId: string, xRequestID: string, xXSRFTOKEN: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (bankId === null || bankId === undefined) { + throw new Error('Required parameter bankId was null or undefined when calling aisConsentsDELETE.'); + } + if (xRequestID === null || xRequestID === undefined) { + throw new Error('Required parameter xRequestID was null or undefined when calling aisConsentsDELETE.'); + } + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling aisConsentsDELETE.'); + } + + let headers = this.defaultHeaders; + if (xRequestID !== undefined && xRequestID !== null) { + headers = headers.set('X-Request-ID', String(xRequestID)); + } + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); + } + + // authentication (sessionCookie) required + if (this.configuration.apiKeys) { + const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"]; + if (key) { + } + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.delete(`${this.configuration.basePath}/v1/ais/banks/${encodeURIComponent(String(bankId))}/consents`, + { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -139,26 +267,28 @@ export class FinTechAccountInformationService { /** * Returns the list of transactions of the given account * Returns the list of transactions of the given account. - * @param bankId - * @param accountId - * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. - * @param fintechRedirectURLOK - * @param fintechRedirectURLNOK - * @param loTRetrievalInformation - * @param dateFrom Conditional: Starting date (inclusive the date dateFrom) of the transaction list, mandated if no delta access is required. For booked transactions, the relevant date is the booking date. For pending transactions, the relevant date is the entry date, which may not be transparent neither in this API nor other channels of the ASPSP. - * @param dateTo End date (inclusive the data dateTo) of the transaction list, default is \"now\" if not given. Might be ignored if a delta function is used. For booked transactions, the relevant date is the booking date. For pending transactions, the relevant date is the entry date, which may not be transparent neither in this API nor other channels of the ASPSP. - * @param entryReferenceFrom This data attribute is indicating that the AISP is in favour to get all transactions after the transaction with identification entryReferenceFrom alternatively to the above defined period. This is a implementation of a delta access. If this data element is contained, the entries \"dateFrom\" and \"dateTo\" might be ignored by the ASPSP if a delta report is supported. Optional if supported by API provider. - * @param bookingStatus Permitted codes are * \"booked\", * \"pending\" and * \"both\" To support the \"pending\" and \"both\" feature is optional for the ASPSP, Error code if not supported in the online banking frontend Default is \"booked\" - * @param deltaList This data attribute is indicating that the AISP is in favour to get all transactions after the last report access for this PSU on the addressed account. This is another implementation of a delta access-report. This delta indicator might be rejected by the ASPSP if this function is not supported. Optional if supported by API provider - * @param online If false, new data will be requested and cache will be updated + * @param bankId + * @param accountId + * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param fintechRedirectURLOK + * @param fintechRedirectURLNOK + * @param loTRetrievalInformation + * @param xPsuAuthenticationRequired If false, login form to OPBA will not be displayed as there might be nothing to share for payments, so that authentication is not necessary. If absent or true - login form for payments will be displayed. + * @param xCreateConsentIfNone + * @param dateFrom Conditional: Starting date (inclusive the date dateFrom) of the transaction list, mandated if no delta access is required. For booked transactions, the relevant date is the booking date. For pending transactions, the relevant date is the entry date, which may not be transparent neither in this API nor other channels of the ASPSP. + * @param dateTo End date (inclusive the data dateTo) of the transaction list, default is \"now\" if not given. Might be ignored if a delta function is used. For booked transactions, the relevant date is the booking date. For pending transactions, the relevant date is the entry date, which may not be transparent neither in this API nor other channels of the ASPSP. + * @param entryReferenceFrom This data attribute is indicating that the AISP is in favour to get all transactions after the transaction with identification entryReferenceFrom alternatively to the above defined period. This is a implementation of a delta access. If this data element is contained, the entries \"dateFrom\" and \"dateTo\" might be ignored by the ASPSP if a delta report is supported. Optional if supported by API provider. + * @param bookingStatus Permitted codes are * \"booked\", * \"pending\" and * \"both\" To support the \"pending\" and \"both\" feature is optional for the ASPSP, Error code if not supported in the online banking frontend Default is \"booked\" + * @param deltaList This data attribute is indicating that the AISP is in favour to get all transactions after the last report access for this PSU on the addressed account. This is another implementation of a delta access-report. This delta indicator might be rejected by the ASPSP if this function is not supported. Optional if supported by API provider + * @param online If true, new data will be requested and cache will be updated * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe?: 'body', reportProgress?: boolean): Observable; - public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe?: 'response', reportProgress?: boolean): Observable>; - public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe?: 'events', reportProgress?: boolean): Observable>; - public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe: any = 'body', reportProgress: boolean = false ): Observable { + public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public aisTransactionsGET(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, loTRetrievalInformation: 'FROM_TPP_WITH_AVAILABLE_CONSENT' | 'FROM_TPP_WITH_NEW_CONSENT', xPsuAuthenticationRequired?: boolean, xCreateConsentIfNone?: string, dateFrom?: string, dateTo?: string, entryReferenceFrom?: string, bookingStatus?: 'booked' | 'pending' | 'both', deltaList?: boolean, online?: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (bankId === null || bankId === undefined) { throw new Error('Required parameter bankId was null or undefined when calling aisTransactionsGET.'); } @@ -168,8 +298,8 @@ export class FinTechAccountInformationService { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling aisTransactionsGET.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling aisTransactionsGET.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling aisTransactionsGET.'); } if (fintechRedirectURLOK === null || fintechRedirectURLOK === undefined) { throw new Error('Required parameter fintechRedirectURLOK was null or undefined when calling aisTransactionsGET.'); @@ -183,30 +313,39 @@ export class FinTechAccountInformationService { let queryParameters = new HttpParams({encoder: this.encoder}); if (dateFrom !== undefined && dateFrom !== null) { - queryParameters = queryParameters.set('dateFrom', dateFrom); + queryParameters = this.addToHttpParams(queryParameters, + dateFrom, 'dateFrom'); } if (dateTo !== undefined && dateTo !== null) { - queryParameters = queryParameters.set('dateTo', dateTo); + queryParameters = this.addToHttpParams(queryParameters, + dateTo, 'dateTo'); } if (entryReferenceFrom !== undefined && entryReferenceFrom !== null) { - queryParameters = queryParameters.set('entryReferenceFrom', entryReferenceFrom); + queryParameters = this.addToHttpParams(queryParameters, + entryReferenceFrom, 'entryReferenceFrom'); } if (bookingStatus !== undefined && bookingStatus !== null) { - queryParameters = queryParameters.set('bookingStatus', bookingStatus); + queryParameters = this.addToHttpParams(queryParameters, + bookingStatus, 'bookingStatus'); } if (deltaList !== undefined && deltaList !== null) { - queryParameters = queryParameters.set('deltaList', deltaList); + queryParameters = this.addToHttpParams(queryParameters, + deltaList, 'deltaList'); } if (online !== undefined && online !== null) { - queryParameters = queryParameters.set('online', online); + queryParameters = this.addToHttpParams(queryParameters, + online, 'online'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); + } + if (xPsuAuthenticationRequired !== undefined && xPsuAuthenticationRequired !== null) { + headers = headers.set('X-Psu-Authentication-Required', String(xPsuAuthenticationRequired)); } if (fintechRedirectURLOK !== undefined && fintechRedirectURLOK !== null) { headers = headers.set('Fintech-Redirect-URL-OK', String(fintechRedirectURLOK)); @@ -217,21 +356,39 @@ export class FinTechAccountInformationService { if (loTRetrievalInformation !== undefined && loTRetrievalInformation !== null) { headers = headers.set('LoTRetrievalInformation', String(loTRetrievalInformation)); } + if (xCreateConsentIfNone !== undefined && xCreateConsentIfNone !== null) { + headers = headers.set('X-Create-Consent-If-None', String(xCreateConsentIfNone)); + } // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + if (this.configuration.apiKeys) { + const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"]; + if (key) { + } + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/ais/banks/${encodeURIComponent(String(bankId))}/accounts/${encodeURIComponent(String(accountId))}/transactions`, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/fintech-examples/fintech-ui/src/app/api/api/finTechAuthorization.service.ts b/fintech-examples/fintech-ui/src/app/api/api/finTechAuthorization.service.ts index 0bba6b26d1..938016cc90 100644 --- a/fintech-examples/fintech-ui/src/app/api/api/finTechAuthorization.service.ts +++ b/fintech-examples/fintech-ui/src/app/api/api/finTechAuthorization.service.ts @@ -17,10 +17,10 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; -import { ErrorResponse } from '../model/errorResponse'; -import { InlineResponse200 } from '../model/inlineResponse200'; -import { LoginRequest } from '../model/loginRequest'; -import { PsuMessage } from '../model/psuMessage'; +import { ErrorResponse } from '../model/models'; +import { InlineResponse200 } from '../model/models'; +import { LoginRequest } from '../model/models'; +import { PsuMessage } from '../model/models'; import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; import { Configuration } from '../configuration'; @@ -52,6 +52,42 @@ export class FinTechAuthorizationService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** * Oauth2 callback to identify user. * Oauth2 callback to authenticate user using some Oauth2 identity provider account. Provider id is set inside state. @@ -62,10 +98,10 @@ export class FinTechAuthorizationService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe?: 'body', reportProgress?: boolean): Observable; - public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe?: 'response', reportProgress?: boolean): Observable>; - public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe?: 'events', reportProgress?: boolean): Observable>; - public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public callbackGetLogin(code: string, state: string, scope: string, error?: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (code === null || code === undefined) { throw new Error('Required parameter code was null or undefined when calling callbackGetLogin.'); } @@ -78,33 +114,46 @@ export class FinTechAuthorizationService { let queryParameters = new HttpParams({encoder: this.encoder}); if (code !== undefined && code !== null) { - queryParameters = queryParameters.set('code', code); + queryParameters = this.addToHttpParams(queryParameters, + code, 'code'); } if (state !== undefined && state !== null) { - queryParameters = queryParameters.set('state', state); + queryParameters = this.addToHttpParams(queryParameters, + state, 'state'); } if (scope !== undefined && scope !== null) { - queryParameters = queryParameters.set('scope', scope); + queryParameters = this.addToHttpParams(queryParameters, + scope, 'scope'); } if (error !== undefined && error !== null) { - queryParameters = queryParameters.set('error', error); + queryParameters = this.addToHttpParams(queryParameters, + error, 'error'); } let headers = this.defaultHeaders; - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/login/oauth2`, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -120,14 +169,14 @@ export class FinTechAuthorizationService { * @param okOrNotok * @param redirectCode * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'body', reportProgress?: boolean): Observable; - public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'response', reportProgress?: boolean): Observable>; - public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'events', reportProgress?: boolean): Observable>; - public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromConsentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (authId === null || authId === undefined) { throw new Error('Required parameter authId was null or undefined when calling fromConsentGET.'); } @@ -140,36 +189,46 @@ export class FinTechAuthorizationService { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling fromConsentGET.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling fromConsentGET.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling fromConsentGET.'); } let queryParameters = new HttpParams({encoder: this.encoder}); if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); + queryParameters = this.addToHttpParams(queryParameters, + redirectCode, 'redirectCode'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/${encodeURIComponent(String(authId))}/fromConsent/${encodeURIComponent(String(okOrNotok))}`, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -185,14 +244,14 @@ export class FinTechAuthorizationService { * @param okOrNotok * @param redirectCode * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'body', reportProgress?: boolean): Observable; - public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'response', reportProgress?: boolean): Observable>; - public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'events', reportProgress?: boolean): Observable>; - public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, X_XSRF_TOKEN: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public fromPaymentGET(authId: string, okOrNotok: 'OK' | 'NOT_OK', redirectCode: string, xRequestID: string, xXSRFTOKEN: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (authId === null || authId === undefined) { throw new Error('Required parameter authId was null or undefined when calling fromPaymentGET.'); } @@ -205,36 +264,46 @@ export class FinTechAuthorizationService { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling fromPaymentGET.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling fromPaymentGET.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling fromPaymentGET.'); } let queryParameters = new HttpParams({encoder: this.encoder}); if (redirectCode !== undefined && redirectCode !== null) { - queryParameters = queryParameters.set('redirectCode', redirectCode); + queryParameters = this.addToHttpParams(queryParameters, + redirectCode, 'redirectCode'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/${encodeURIComponent(String(authId))}/fromPayment/${encodeURIComponent(String(okOrNotok))}`, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -251,10 +320,10 @@ export class FinTechAuthorizationService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe?: 'body', reportProgress?: boolean): Observable; - public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe?: 'response', reportProgress?: boolean): Observable>; - public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe?: 'events', reportProgress?: boolean): Observable>; - public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe: any = 'body', reportProgress: boolean = false ): Observable { + public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public loginPOST(xRequestID: string, loginRequest: LoginRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling loginPOST.'); } @@ -267,11 +336,14 @@ export class FinTechAuthorizationService { headers = headers.set('X-Request-ID', String(xRequestID)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } @@ -286,9 +358,15 @@ export class FinTechAuthorizationService { headers = headers.set('Content-Type', httpContentTypeSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.post(`${this.configuration.basePath}/v1/login`, loginRequest, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -301,43 +379,58 @@ export class FinTechAuthorizationService { * logs out user * If user can be authenticated, user will be logged out. * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public logoutPOST(xRequestID: string, X_XSRF_TOKEN: string, observe?: 'body', reportProgress?: boolean): Observable; - public logoutPOST(xRequestID: string, X_XSRF_TOKEN: string, observe?: 'response', reportProgress?: boolean): Observable>; - public logoutPOST(xRequestID: string, X_XSRF_TOKEN: string, observe?: 'events', reportProgress?: boolean): Observable>; - public logoutPOST(xRequestID: string, X_XSRF_TOKEN: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public logoutPOST(xRequestID: string, xXSRFTOKEN: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public logoutPOST(xRequestID: string, xXSRFTOKEN: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public logoutPOST(xRequestID: string, xXSRFTOKEN: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public logoutPOST(xRequestID: string, xXSRFTOKEN: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling logoutPOST.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling logoutPOST.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling logoutPOST.'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); } // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + if (this.configuration.apiKeys) { + const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"]; + if (key) { + } + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.post(`${this.configuration.basePath}/v1/logout`, null, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/fintech-examples/fintech-ui/src/app/api/api/finTechBankSearch.service.ts b/fintech-examples/fintech-ui/src/app/api/api/finTechBankSearch.service.ts index cd14d0a864..e648ef0805 100644 --- a/fintech-examples/fintech-ui/src/app/api/api/finTechBankSearch.service.ts +++ b/fintech-examples/fintech-ui/src/app/api/api/finTechBankSearch.service.ts @@ -17,10 +17,10 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; -import { ErrorResponse } from '../model/errorResponse'; -import { InlineResponse2001 } from '../model/inlineResponse2001'; -import { InlineResponse2002 } from '../model/inlineResponse2002'; -import { PsuMessage } from '../model/psuMessage'; +import { ErrorResponse } from '../model/models'; +import { InlineResponse2001 } from '../model/models'; +import { InlineResponse2002 } from '../model/models'; +import { PsuMessage } from '../model/models'; import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; import { Configuration } from '../configuration'; @@ -52,56 +52,108 @@ export class FinTechBankSearchService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** - * Request the profile of the bank identified with id (bankId). - * Request the profile of the bank identified with id (bankId). + * Request the profile of the bank identified with id (bankProfileId). + * Request the profile of the bank identified with id (bankProfileId). * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. - * @param bankId Identifier of the bank to be loaded. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param bankProfileId Identifier of the bank to be loaded. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public bankProfileGET(xRequestID: string, X_XSRF_TOKEN: string, bankId: string, observe?: 'body', reportProgress?: boolean): Observable; - public bankProfileGET(xRequestID: string, X_XSRF_TOKEN: string, bankId: string, observe?: 'response', reportProgress?: boolean): Observable>; - public bankProfileGET(xRequestID: string, X_XSRF_TOKEN: string, bankId: string, observe?: 'events', reportProgress?: boolean): Observable>; - public bankProfileGET(xRequestID: string, X_XSRF_TOKEN: string, bankId: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public bankProfileGET(xRequestID: string, xXSRFTOKEN: string, bankProfileId: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public bankProfileGET(xRequestID: string, xXSRFTOKEN: string, bankProfileId: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public bankProfileGET(xRequestID: string, xXSRFTOKEN: string, bankProfileId: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public bankProfileGET(xRequestID: string, xXSRFTOKEN: string, bankProfileId: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling bankProfileGET.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling bankProfileGET.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling bankProfileGET.'); } - if (bankId === null || bankId === undefined) { - throw new Error('Required parameter bankId was null or undefined when calling bankProfileGET.'); + if (bankProfileId === null || bankProfileId === undefined) { + throw new Error('Required parameter bankProfileId was null or undefined when calling bankProfileGET.'); } let queryParameters = new HttpParams({encoder: this.encoder}); - if (bankId !== undefined && bankId !== null) { - queryParameters = queryParameters.set('bankId', bankId); + if (bankProfileId !== undefined && bankProfileId !== null) { + queryParameters = this.addToHttpParams(queryParameters, + bankProfileId, 'bankProfileId'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); } // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + if (this.configuration.apiKeys) { + const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"]; + if (key) { + } + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/search/bankProfile`, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, @@ -114,22 +166,22 @@ export class FinTechBankSearchService { * Issues an incremental bank search request to the FinTechApi. * Issues an incremental bank search request to the FinTechApi. * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. * @param keyword * @param start * @param max * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public bankSearchGET(xRequestID: string, X_XSRF_TOKEN: string, keyword: string, start?: number, max?: number, observe?: 'body', reportProgress?: boolean): Observable; - public bankSearchGET(xRequestID: string, X_XSRF_TOKEN: string, keyword: string, start?: number, max?: number, observe?: 'response', reportProgress?: boolean): Observable>; - public bankSearchGET(xRequestID: string, X_XSRF_TOKEN: string, keyword: string, start?: number, max?: number, observe?: 'events', reportProgress?: boolean): Observable>; - public bankSearchGET(xRequestID: string, X_XSRF_TOKEN: string, keyword: string, start?: number, max?: number, observe: any = 'body', reportProgress: boolean = false ): Observable { + public bankSearchGET(xRequestID: string, xXSRFTOKEN: string, keyword: string, start?: number, max?: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public bankSearchGET(xRequestID: string, xXSRFTOKEN: string, keyword: string, start?: number, max?: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public bankSearchGET(xRequestID: string, xXSRFTOKEN: string, keyword: string, start?: number, max?: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public bankSearchGET(xRequestID: string, xXSRFTOKEN: string, keyword: string, start?: number, max?: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling bankSearchGET.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling bankSearchGET.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling bankSearchGET.'); } if (keyword === null || keyword === undefined) { throw new Error('Required parameter keyword was null or undefined when calling bankSearchGET.'); @@ -137,37 +189,55 @@ export class FinTechBankSearchService { let queryParameters = new HttpParams({encoder: this.encoder}); if (keyword !== undefined && keyword !== null) { - queryParameters = queryParameters.set('keyword', keyword); + queryParameters = this.addToHttpParams(queryParameters, + keyword, 'keyword'); } if (start !== undefined && start !== null) { - queryParameters = queryParameters.set('start', start); + queryParameters = this.addToHttpParams(queryParameters, + start, 'start'); } if (max !== undefined && max !== null) { - queryParameters = queryParameters.set('max', max); + queryParameters = this.addToHttpParams(queryParameters, + max, 'max'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); } // authentication (sessionCookie) required - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + if (this.configuration.apiKeys) { + const key: string | undefined = this.configuration.apiKeys["sessionCookie"] || this.configuration.apiKeys["sessionCookie"]; + if (key) { + } + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get(`${this.configuration.basePath}/v1/search/bankSearch`, { params: queryParameters, + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/fintech-examples/fintech-ui/src/app/api/api/finTechOauth2Authentication.service.ts b/fintech-examples/fintech-ui/src/app/api/api/finTechOauth2Authentication.service.ts index 82e36f1f2a..79a9d29dcd 100644 --- a/fintech-examples/fintech-ui/src/app/api/api/finTechOauth2Authentication.service.ts +++ b/fintech-examples/fintech-ui/src/app/api/api/finTechOauth2Authentication.service.ts @@ -48,6 +48,42 @@ export class FinTechOauth2AuthenticationService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** * Identifies the PSU in the Realm of the FinTechApi using his Gmail or other IDP provider account. * Use Oauth2 for Gmail users\' account to identify him @@ -56,10 +92,10 @@ export class FinTechOauth2AuthenticationService { * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe?: 'body', reportProgress?: boolean): Observable; - public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe?: 'response', reportProgress?: boolean): Observable>; - public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe?: 'events', reportProgress?: boolean): Observable>; - public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe: any = 'body', reportProgress: boolean = false ): Observable { + public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined}): Observable; + public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined}): Observable>; + public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined}): Observable>; + public oauthLoginPOST(xRequestID: string, idpProvider: 'gmail', observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined}): Observable { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling oauthLoginPOST.'); } @@ -72,18 +108,27 @@ export class FinTechOauth2AuthenticationService { headers = headers.set('X-Request-ID', String(xRequestID)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.post(`${this.configuration.basePath}/v1/oauth2/${encodeURIComponent(String(idpProvider))}/login`, null, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/fintech-examples/fintech-ui/src/app/api/api/fintechRetrieveAllSinglePayments.service.ts b/fintech-examples/fintech-ui/src/app/api/api/fintechRetrieveAllSinglePayments.service.ts index e47557badc..7ce7c7465e 100644 --- a/fintech-examples/fintech-ui/src/app/api/api/fintechRetrieveAllSinglePayments.service.ts +++ b/fintech-examples/fintech-ui/src/app/api/api/fintechRetrieveAllSinglePayments.service.ts @@ -17,8 +17,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; -import { ErrorResponse } from '../model/errorResponse'; -import { PaymentInitiationWithStatusResponse } from '../model/paymentInitiationWithStatusResponse'; +import { ErrorResponse } from '../model/models'; +import { PaymentInitiationWithStatusResponse } from '../model/models'; import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; import { Configuration } from '../configuration'; @@ -50,20 +50,56 @@ export class FintechRetrieveAllSinglePaymentsService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** * Ask for all payments of this account * This method is used to get payment status. * @param bankId * @param accountId * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'body', reportProgress?: boolean): Observable>; - public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'response', reportProgress?: boolean): Observable>>; - public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, observe?: 'events', reportProgress?: boolean): Observable>>; - public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, observe: any = 'body', reportProgress: boolean = false ): Observable { + public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; + public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>>; + public retrieveAllSinglePayments(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (bankId === null || bankId === undefined) { throw new Error('Required parameter bankId was null or undefined when calling retrieveAllSinglePayments.'); } @@ -73,30 +109,39 @@ export class FintechRetrieveAllSinglePaymentsService { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling retrieveAllSinglePayments.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling retrieveAllSinglePayments.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling retrieveAllSinglePayments.'); } let headers = this.defaultHeaders; if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.get>(`${this.configuration.basePath}/v1/pis/banks/${encodeURIComponent(String(bankId))}/accounts/${encodeURIComponent(String(accountId))}/payments/single`, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/fintech-examples/fintech-ui/src/app/api/api/fintechRetrieveConsent.service.ts b/fintech-examples/fintech-ui/src/app/api/api/fintechRetrieveConsent.service.ts new file mode 100644 index 0000000000..1012a08dfe --- /dev/null +++ b/fintech-examples/fintech-ui/src/app/api/api/fintechRetrieveConsent.service.ts @@ -0,0 +1,137 @@ +/** + * Open Banking Gateway FinTech Example API + * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + + +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; + + + +@Injectable({ + providedIn: 'root' +}) +export class FintechRetrieveConsentService { + + protected basePath = 'http://localhost'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * ask for existing consent of user + * This method is disabled by default. It can be enabled by profile \"CONSENT_RETRIEVAL\" + * @param userid + * @param password + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public retrieveConsent(userid: string, password: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public retrieveConsent(userid: string, password: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public retrieveConsent(userid: string, password: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public retrieveConsent(userid: string, password: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (userid === null || userid === undefined) { + throw new Error('Required parameter userid was null or undefined when calling retrieveConsent.'); + } + if (password === null || password === undefined) { + throw new Error('Required parameter password was null or undefined when calling retrieveConsent.'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/v1/consent/${encodeURIComponent(String(userid))}/${encodeURIComponent(String(password))}`, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + +} diff --git a/fintech-examples/fintech-ui/src/app/api/api/fintechSinglePaymentInitiation.service.ts b/fintech-examples/fintech-ui/src/app/api/api/fintechSinglePaymentInitiation.service.ts index 438b568f99..523c8b9af5 100644 --- a/fintech-examples/fintech-ui/src/app/api/api/fintechSinglePaymentInitiation.service.ts +++ b/fintech-examples/fintech-ui/src/app/api/api/fintechSinglePaymentInitiation.service.ts @@ -1,9 +1,9 @@ /** * Open Banking Gateway FinTech Example API - * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. + * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -17,8 +17,8 @@ import { HttpClient, HttpHeaders, HttpParams, import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; -import { ErrorResponse } from '../model/errorResponse'; -import { SinglePaymentInitiationRequest } from '../model/singlePaymentInitiationRequest'; +import { ErrorResponse } from '../model/models'; +import { SinglePaymentInitiationRequest } from '../model/models'; import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; import { Configuration } from '../configuration'; @@ -50,24 +50,60 @@ export class FintechSinglePaymentInitiationService { + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + /** * Single payment initiation request * This method is used to initiate a payment at the Fintech Server. - * @param bankId - * @param accountId - * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. - * @param X_XSRF_TOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. - * @param fintechRedirectURLOK - * @param fintechRedirectURLNOK + * @param bankId + * @param accountId + * @param xRequestID Unique ID that identifies this request through common workflow. Must be contained in HTTP Response as well. + * @param xXSRFTOKEN XSRF parameter used to validate a SessionCookie or RedirectCookie. + * @param fintechRedirectURLOK + * @param fintechRedirectURLNOK * @param singlePaymentInitiationRequest Single payment initiation request - * @param xPisPsuAuthenticationRequired If false, login form to OPBA will not be displayed as there might be nothing to share for payments, so that authentication is not necessary. If absent or true - login form for payments will be displayed. + * @param xPsuAuthenticationRequired If false, login form to OPBA will not be displayed as there might be nothing to share for payments, so that authentication is not necessary. If absent or true - login form for payments will be displayed. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ - public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPisPsuAuthenticationRequired?: boolean, observe?: 'body', reportProgress?: boolean): Observable; - public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPisPsuAuthenticationRequired?: boolean, observe?: 'response', reportProgress?: boolean): Observable>; - public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPisPsuAuthenticationRequired?: boolean, observe?: 'events', reportProgress?: boolean): Observable>; - public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, X_XSRF_TOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPisPsuAuthenticationRequired?: boolean, observe: any = 'body', reportProgress: boolean = false ): Observable { + public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPsuAuthenticationRequired?: boolean, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPsuAuthenticationRequired?: boolean, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPsuAuthenticationRequired?: boolean, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public initiateSinglePayment(bankId: string, accountId: string, xRequestID: string, xXSRFTOKEN: string, fintechRedirectURLOK: string, fintechRedirectURLNOK: string, singlePaymentInitiationRequest: SinglePaymentInitiationRequest, xPsuAuthenticationRequired?: boolean, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { if (bankId === null || bankId === undefined) { throw new Error('Required parameter bankId was null or undefined when calling initiateSinglePayment.'); } @@ -77,8 +113,8 @@ export class FintechSinglePaymentInitiationService { if (xRequestID === null || xRequestID === undefined) { throw new Error('Required parameter xRequestID was null or undefined when calling initiateSinglePayment.'); } - if (X_XSRF_TOKEN === null || X_XSRF_TOKEN === undefined) { - throw new Error('Required parameter X_XSRF_TOKEN was null or undefined when calling initiateSinglePayment.'); + if (xXSRFTOKEN === null || xXSRFTOKEN === undefined) { + throw new Error('Required parameter xXSRFTOKEN was null or undefined when calling initiateSinglePayment.'); } if (fintechRedirectURLOK === null || fintechRedirectURLOK === undefined) { throw new Error('Required parameter fintechRedirectURLOK was null or undefined when calling initiateSinglePayment.'); @@ -94,11 +130,11 @@ export class FintechSinglePaymentInitiationService { if (xRequestID !== undefined && xRequestID !== null) { headers = headers.set('X-Request-ID', String(xRequestID)); } - if (X_XSRF_TOKEN !== undefined && X_XSRF_TOKEN !== null) { - headers = headers.set('X-XSRF-TOKEN', String(X_XSRF_TOKEN)); + if (xXSRFTOKEN !== undefined && xXSRFTOKEN !== null) { + headers = headers.set('X-XSRF-TOKEN', String(xXSRFTOKEN)); } - if (xPisPsuAuthenticationRequired !== undefined && xPisPsuAuthenticationRequired !== null) { - headers = headers.set('X-Pis-Psu-Authentication-Required', String(xPisPsuAuthenticationRequired)); + if (xPsuAuthenticationRequired !== undefined && xPsuAuthenticationRequired !== null) { + headers = headers.set('X-Psu-Authentication-Required', String(xPsuAuthenticationRequired)); } if (fintechRedirectURLOK !== undefined && fintechRedirectURLOK !== null) { headers = headers.set('Fintech-Redirect-URL-OK', String(fintechRedirectURLOK)); @@ -107,11 +143,14 @@ export class FintechSinglePaymentInitiationService { headers = headers.set('Fintech-Redirect-URL-NOK', String(fintechRedirectURLNOK)); } - // to determine the Accept header - const httpHeaderAccepts: string[] = [ - 'application/json' - ]; - const httpHeaderAcceptSelected: string | undefined = this.configuration.selectHeaderAccept(httpHeaderAccepts); + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } if (httpHeaderAcceptSelected !== undefined) { headers = headers.set('Accept', httpHeaderAcceptSelected); } @@ -126,9 +165,15 @@ export class FintechSinglePaymentInitiationService { headers = headers.set('Content-Type', httpContentTypeSelected); } + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + return this.httpClient.post(`${this.configuration.basePath}/v1/pis/banks/${encodeURIComponent(String(bankId))}/accounts/${encodeURIComponent(String(accountId))}/payments/single`, singlePaymentInitiationRequest, { + responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, observe: observe, diff --git a/fintech-examples/fintech-ui/src/app/api/model/accountBalance.ts b/fintech-examples/fintech-ui/src/app/api/model/accountBalance.ts index 989ef34643..c23463625e 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/accountBalance.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/accountBalance.ts @@ -16,7 +16,7 @@ export interface AccountBalance { balanceAmount?: Amount; balanceType?: string; iban?: string; - lastChangeDateTime?: Date; + lastChangeDateTime?: string; lastCommittedTransaction?: string; referenceDate?: string; } diff --git a/fintech-examples/fintech-ui/src/app/api/model/accountDetails.ts b/fintech-examples/fintech-ui/src/app/api/model/accountDetails.ts index 75356efdd0..26cb0ecd3c 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/accountDetails.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/accountDetails.ts @@ -76,7 +76,7 @@ export interface AccountDetails { * Specifications that might be provided by the ASPSP: - characteristics of the account - characteristics of the relevant card */ details?: string; - links?: LinksAccountDetails; + _links?: LinksAccountDetails; /** * Name of the legal account owner. If there is more than one owner, then e.g. two names might be noted here. */ diff --git a/fintech-examples/fintech-ui/src/app/api/model/accountReport.ts b/fintech-examples/fintech-ui/src/app/api/model/accountReport.ts index bdc35c2b25..6a2ab38ef4 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/accountReport.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/accountReport.ts @@ -1,9 +1,9 @@ /** * Open Banking Gateway FinTech Example API - * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. + * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -13,9 +13,9 @@ import { TransactionDetails } from './transactionDetails'; /** - * JSON based account report. This account report contains transactions resulting from the query parameters. \'booked\' shall be contained if bookingStatus parameter is set to \"booked\" or \"both\". \'pending\' is not contained if the bookingStatus parameter is set to \"booked\". + * JSON based account report. This account report contains transactions resulting from the query parameters. \'booked\' shall be contained if bookingStatus parameter is set to \"booked\" or \"both\". \'pending\' is not contained if the bookingStatus parameter is set to \"booked\". */ -export interface AccountReport { +export interface AccountReport { /** * Array of transaction details. */ @@ -24,5 +24,9 @@ export interface AccountReport { * Array of transaction details. */ pending?: Array; + /** + * Raw bank response as String + */ + rawTransactions?: string; } diff --git a/fintech-examples/fintech-ui/src/app/api/model/aisAccountAccessInfo.ts b/fintech-examples/fintech-ui/src/app/api/model/aisAccountAccessInfo.ts new file mode 100644 index 0000000000..72cd184c6d --- /dev/null +++ b/fintech-examples/fintech-ui/src/app/api/model/aisAccountAccessInfo.ts @@ -0,0 +1,53 @@ +/** + * Open Banking Gateway FinTech Example API + * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { AccountReference } from './accountReference'; + + +/** + * Ais account access information + */ +export interface AisAccountAccessInfo { + /** + * Access to accounts + */ + accounts?: Array; + /** + * Consent on all accounts, balances and transactions of psu + */ + allPsd2?: AisAccountAccessInfo.AllPsd2Enum; + /** + * Consent on all available accounts of psu + */ + availableAccounts?: AisAccountAccessInfo.AvailableAccountsEnum; + /** + * Access to balances + */ + balances?: Array; + /** + * Access to transactions + */ + transactions?: Array; +} +export namespace AisAccountAccessInfo { + export type AllPsd2Enum = 'ALL_ACCOUNTS' | 'ALL_ACCOUNTS_WITH_BALANCES'; + export const AllPsd2Enum = { + ACCOUNTS: 'ALL_ACCOUNTS' as AllPsd2Enum, + ACCOUNTSWITHBALANCES: 'ALL_ACCOUNTS_WITH_BALANCES' as AllPsd2Enum + }; + export type AvailableAccountsEnum = 'ALL_ACCOUNTS' | 'ALL_ACCOUNTS_WITH_BALANCES'; + export const AvailableAccountsEnum = { + ACCOUNTS: 'ALL_ACCOUNTS' as AvailableAccountsEnum, + ACCOUNTSWITHBALANCES: 'ALL_ACCOUNTS_WITH_BALANCES' as AvailableAccountsEnum + }; +} + + diff --git a/fintech-examples/fintech-ui/src/app/api/model/aisConsentRequest.ts b/fintech-examples/fintech-ui/src/app/api/model/aisConsentRequest.ts new file mode 100644 index 0000000000..8f0fb7093d --- /dev/null +++ b/fintech-examples/fintech-ui/src/app/api/model/aisConsentRequest.ts @@ -0,0 +1,37 @@ +/** + * Open Banking Gateway FinTech Example API + * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { AisAccountAccessInfo } from './aisAccountAccessInfo'; + + +/** + * Ais consent request + */ +export interface AisConsentRequest { + access: AisAccountAccessInfo; + /** + * Maximum frequency for an access per day. For a once-off access, this attribute is set to 1 + */ + frequencyPerDay: number; + /** + * \'true\', if the consent is for recurring access to the account data , \'false\', if the consent is for one access to the account data + */ + recurringIndicator: boolean; + /** + * Consent`s expiration date. The content is the local ASPSP date in ISODate Format + */ + validUntil: string; + /** + * \'true\', if the consent is i.e. account list and then payment \'false\', if the consent is for one access to the account data + */ + combinedServiceIndicator?: boolean; +} + diff --git a/fintech-examples/fintech-ui/src/app/api/model/analyticsReportDetails.ts b/fintech-examples/fintech-ui/src/app/api/model/analyticsReportDetails.ts new file mode 100644 index 0000000000..7538548862 --- /dev/null +++ b/fintech-examples/fintech-ui/src/app/api/model/analyticsReportDetails.ts @@ -0,0 +1,71 @@ +/** + * Open Banking Gateway FinTech Example API + * This is a sample API that shows how to develop FinTech use cases that invoke banking APIs. #### User Agent and Cookies This Api assumes * that the PsuUserAgent (hosting the FinTechUI) is a modern web browser that stores httpOnly cookies sent with the redirect under the given domain and path as defined by [RFC 6265](https://tools.ietf.org/html/rfc6265). * that any other PsuUserAgent like a native mobile or a desktop application can simulate this same behavior of a modern browser with respect to Cookies. #### SessionCookies and XSRF After a PSU is authenticated with the FinTech environment (either through the simple login interface defined here, or through an identity provider), the FinTechApi will establish a session with the FinTechUI. This is done by the mean of using a cookie called SessionCookie. This SessionCookie is protected by a corresponding xsrfToken. The response that sets a SessionCookie also carries a corresponding xsrfToken in the response header named \"X-XSRF-TOKEN\". It is the responsibility of the FinTechUI to : * parse and store this xsrfToken so that a refresh of a browser window can work. This shall be done using user agent capabilities. A web browser application might decide to store the xsrfToken in the browser localStorage, as the cookie we set are all considered persistent. * make sure that each subsequent request that is carrying the SessionCookie also carries the corresponding xsrfToken as header field (see the request path). * remove this xsrfToken from the localStorage when the corresponding SessionCookie is deleted by a server response (setting cookie value to null). The main difference between an xsrfToken and a SessionCookie is that the sessionCookie is automatically sent with each matching request. The xsrfToken must be explicitely read and sent by application. #### API- vs. UI-Redirection For simplicity, this Framework is designed to redirect to FinTechUI not to FinTechApi. #### Explicite vs. Implicite Redirection We define an \"Implicite redirection\" a case where a web browser react to 30X reponse and automatically redirects to the attached endpoint. We define an \"Explicite Redirection\" as a case where the UI-Application reacts to a 20X response, explicitely parses the attached __Location__ header an uses it to reload the new page in the browser window (or start the new UI-Application in case of native apps). This framework advocates for explicite redirection passing a __20X__ response to the FinTechUI toghether with the __Location__ parameter. Processing a response that initiates a redirect, the FinTechUI makes sure following happens, * that the exisitng __SessionCookie__ is deleted, as the user will not have a chance for an explicite logout, * that the corresponding xsrfToken is deleted from the local storage, * that a RedirectCookie set is stored (in case UI is not a web browser), so the user can be authenticated against it when sent back to the FinTechUI. The expiration of the RedirectCookie shall be set to the expected duration of the redirect, * that the corresponding xsrfToken is stored in the local storage (under the same cookie path as the RedirectCookie) #### Redirecting to the ConsentAuthorisationApi For a redirection to the ConsentAuthorisationApi, a generated AUTH-ID is added to the cookie path and used to distinguish authorization processes from each order. This information (AUTH-ID) must be contained in the back redirect url sent to the ConsentAuthorisationApi in the back channel, so that the FinTechUI can invoke the correct code2Token endpoint when activated. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * JSON based analytics report. This account report contains transaction categorization result. + */ +export interface AnalyticsReportDetails { + /** + * The id of transaction this analytics result refers to. + */ + transactionId?: string; + /** + * Main category of the booking. + */ + mainCategory?: string; + /** + * Sub category of the booking. + */ + subCategory?: string; + /** + * Specification of the booking. + */ + specification?: string; + /** + * Related account. + */ + otherAccount?: string; + /** + * Logo. + */ + logo?: string; + /** + * Homepage. + */ + homepage?: string; + /** + * Hotline. + */ + hotline?: string; + /** + * Email. + */ + email?: string; + /** + * Custom information about analyzed transaction. + */ + custom?: { [key: string]: string; }; + /** + * Rules that were used to analyze. + */ + usedRules?: Array; + /** + * Classification next booking date. + */ + nextBookingDate?: string; + /** + * Classification cycle result. + */ + cycle?: string; +} + diff --git a/fintech-examples/fintech-ui/src/app/api/model/bankDescriptor.ts b/fintech-examples/fintech-ui/src/app/api/model/bankDescriptor.ts index d4d4179241..e9c2ae4498 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/bankDescriptor.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/bankDescriptor.ts @@ -9,6 +9,7 @@ * https://openapi-generator.tech * Do not edit the class manually. */ +import { BankProfile } from './bankProfile'; export interface BankDescriptor { @@ -16,5 +17,6 @@ export interface BankDescriptor { bic?: string; bankCode?: string; uuid?: string; + profiles?: Array; } diff --git a/fintech-examples/fintech-ui/src/app/api/model/bankProfile.ts b/fintech-examples/fintech-ui/src/app/api/model/bankProfile.ts index 24d9ffe6a0..37cc2bf72e 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/bankProfile.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/bankProfile.ts @@ -14,7 +14,13 @@ export interface BankProfile { bankId?: string; bankName?: string; + name?: string; bic?: string; + uuid?: string; services?: Array; + externalId?: string; + externalInterfaces?: string; + protocolType?: string; + isSandbox?: boolean; } diff --git a/fintech-examples/fintech-ui/src/app/api/model/models.ts b/fintech-examples/fintech-ui/src/app/api/model/models.ts index b372391f79..32c3c686e3 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/models.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/models.ts @@ -6,6 +6,7 @@ export * from './accountReport'; export * from './accountStatus'; export * from './address'; export * from './amount'; +export * from './analyticsReportDetails'; export * from './bankDescriptor'; export * from './bankProfile'; export * from './errorResponse'; diff --git a/fintech-examples/fintech-ui/src/app/api/model/transactionsResponse.ts b/fintech-examples/fintech-ui/src/app/api/model/transactionsResponse.ts index b3a92672cb..7601d965a7 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/transactionsResponse.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/transactionsResponse.ts @@ -11,6 +11,7 @@ */ import { AccountReference } from './accountReference'; import { AccountReport } from './accountReport'; +import { AnalyticsReportDetails } from './analyticsReportDetails'; /** @@ -19,5 +20,9 @@ import { AccountReport } from './accountReport'; export interface TransactionsResponse { account?: AccountReference; transactions?: AccountReport; + /** + * Array of transaction details. + */ + analytics?: Array; } diff --git a/fintech-examples/fintech-ui/src/app/api/model/userProfile.ts b/fintech-examples/fintech-ui/src/app/api/model/userProfile.ts index b4b4c347d0..335c108303 100644 --- a/fintech-examples/fintech-ui/src/app/api/model/userProfile.ts +++ b/fintech-examples/fintech-ui/src/app/api/model/userProfile.ts @@ -13,6 +13,6 @@ export interface UserProfile { name?: string; - lastLogin?: Date; + lastLogin?: string; } diff --git a/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.html b/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.html index 444f78e404..609d677111 100644 --- a/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.html +++ b/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.html @@ -5,7 +5,7 @@

Search your bank

diff --git a/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.spec.ts b/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.spec.ts index 9eecac9a0f..2f423acd5b 100644 --- a/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.spec.ts +++ b/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.spec.ts @@ -3,9 +3,8 @@ import { ReactiveFormsModule } from '@angular/forms'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { BankSearchComponent } from './bank-search.component'; +import { BankSearchComponent, BankSearchInfo } from './bank-search.component'; import { SearchComponent } from '../common/search/search.component'; -import { BankDescriptor } from '../api'; import { BankSearchService } from './services/bank-search.service'; import { StorageService } from '../services/storage.service'; import { Router } from '@angular/router'; @@ -48,17 +47,12 @@ describe('BankSearchComponent', () => { }); it('should call onBankSelect', () => { - const mockBank: BankDescriptor = { - bankName: 'adorsys', - bic: 'DE230334244232322323', - bankCode: '12345', - uuid: 'xxxxxxxxxx', - }; + const mockBank: BankSearchInfo = new BankSearchInfo('bank1', '123'); const routerSpy = spyOn(router, 'navigate'); spyOn(component, 'onBankSelect').withArgs(mockBank).and.callThrough(); component.onBankSelect(mockBank); expect(routerSpy).toHaveBeenCalledWith([RoutingPath.BANK, mockBank.uuid]); - expect(component.selectedBank).toEqual(mockBank.uuid); + expect(component.selectedBankProfile).toEqual(mockBank.uuid); }); }); diff --git a/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.ts b/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.ts index 99306e6317..51c657b53e 100644 --- a/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.ts +++ b/fintech-examples/fintech-ui/src/app/bank-search/bank-search.component.ts @@ -1,10 +1,9 @@ -import { Component } from '@angular/core'; -import { BankSearchService } from './services/bank-search.service'; -import { ActivatedRoute, Router } from '@angular/router'; -import { BankDescriptor } from '../api'; -import { StorageService } from '../services/storage.service'; -import { TimerService } from '../services/timer.service'; -import { RoutingPath } from '../models/routing-path.model'; +import {Component} from '@angular/core'; +import {BankSearchService} from './services/bank-search.service'; +import {ActivatedRoute, Router} from '@angular/router'; +import {StorageService} from '../services/storage.service'; +import {TimerService} from '../services/timer.service'; +import {RoutingPath} from '../models/routing-path.model'; @Component({ selector: 'app-bank-search', @@ -12,8 +11,8 @@ import { RoutingPath } from '../models/routing-path.model'; styleUrls: ['./bank-search.component.scss'] }) export class BankSearchComponent { - searchedBanks: BankDescriptor[] = []; - selectedBank: string; + searchedBanks: BankSearchInfo[] = []; + selectedBankProfile: string; constructor( private bankSearchService: BankSearchService, @@ -28,21 +27,38 @@ export class BankSearchComponent { onSearch(keyword: string): void { if (keyword && keyword.trim()) { this.bankSearchService.searchBanks(keyword).subscribe((bankDescriptor) => { - this.searchedBanks = bankDescriptor.bankDescriptor; + this.searchedBanks = []; + for (const descriptor of bankDescriptor.bankDescriptor) { + if (!descriptor.profiles) { + continue + } + + this.searchedBanks.push(...descriptor.profiles.map(it => new BankSearchInfo(`[${it.protocolType}${null === it.name ? '' : ',' + it.name}] ${it.bankName}`, it.uuid))) + } }); } else { this.bankUnselect(); } } - onBankSelect(bank: BankDescriptor): void { - this.selectedBank = bank.uuid; - this.storageService.setBankName(bank.bankName); - this.router.navigate([RoutingPath.BANK, bank.uuid]); + onBankSelect(profile: BankSearchInfo): void { + this.selectedBankProfile = profile.uuid; + this.storageService.setBankName(profile.name); + this.router.navigate([RoutingPath.BANK, profile.uuid]); } private bankUnselect(): void { this.searchedBanks = []; - this.selectedBank = null; + this.selectedBankProfile = null; + } +} + +export class BankSearchInfo { + name: string + uuid: string + + constructor(name: string, profileId: string) { + this.name = name; + this.uuid = profileId; } } diff --git a/fintech-examples/fintech-ui/src/app/bank/list-accounts/list-accounts.component.ts b/fintech-examples/fintech-ui/src/app/bank/list-accounts/list-accounts.component.ts index ed26d9e246..08e88337b8 100644 --- a/fintech-examples/fintech-ui/src/app/bank/list-accounts/list-accounts.component.ts +++ b/fintech-examples/fintech-ui/src/app/bank/list-accounts/list-accounts.component.ts @@ -35,35 +35,45 @@ export class ListAccountsComponent implements OnInit { const settings = this.storageService.getSettings(); const online = !this.storageService.isAfterRedirect() && !settings.cacheLoa; this.storageService.setAfterRedirect(false); - this.aisService.getAccounts(this.bankId, settings.loa, settings.withBalance, online).subscribe((response) => { - switch (response.status) { - case 202: - this.storageService.setRedirect( - response.headers.get(HeaderConfig.HEADER_FIELD_REDIRECT_CODE), - response.headers.get(HeaderConfig.HEADER_FIELD_AUTH_ID), - response.headers.get(HeaderConfig.HEADER_FIELD_X_XSRF_TOKEN), - parseInt(response.headers.get(HeaderConfig.HEADER_FIELD_REDIRECT_X_MAX_AGE), 0), - RedirectType.AIS - ); - const r = new RedirectStruct(); - r.redirectUrl = encodeURIComponent(response.headers.get(HeaderConfig.HEADER_FIELD_LOCATION)); - r.redirectCode = response.headers.get(HeaderConfig.HEADER_FIELD_REDIRECT_CODE); - r.bankId = this.bankId; - r.bankName = this.storageService.getBankName(); - this.router.navigate(['redirect', JSON.stringify(r)], { relativeTo: this.route }); - break; - case 200: - // this is added to register url where to forward - // if LoT is cancelled after redirect page is displayed - // to be removed when issue https://github.com/adorsys/open-banking-gateway/issues/848 is resolved - // or Fintech UI refactored - this.accounts = response.body.accounts; - const loa = []; - for (const accountDetail of this.accounts) { - loa.push(new AccountStruct(accountDetail.resourceId, accountDetail.iban, accountDetail.name)); - } - this.storageService.setLoa(this.bankId, loa); - } - }); + + this.aisService + .getAccounts( + this.bankId, + settings.loa, + JSON.stringify(settings.consent), + settings.withBalance, + online, + settings.consentRequiresAuthentication + ) + .subscribe((response) => { + switch (response.status) { + case 202: + this.storageService.setRedirect( + response.headers.get(HeaderConfig.HEADER_FIELD_REDIRECT_CODE), + response.headers.get(HeaderConfig.HEADER_FIELD_AUTH_ID), + response.headers.get(HeaderConfig.HEADER_FIELD_X_XSRF_TOKEN), + parseInt(response.headers.get(HeaderConfig.HEADER_FIELD_REDIRECT_X_MAX_AGE), 0), + RedirectType.AIS + ); + const r = new RedirectStruct(); + r.redirectUrl = encodeURIComponent(response.headers.get(HeaderConfig.HEADER_FIELD_LOCATION)); + r.redirectCode = response.headers.get(HeaderConfig.HEADER_FIELD_REDIRECT_CODE); + r.bankId = this.bankId; + r.bankName = this.storageService.getBankName(); + this.router.navigate(['redirect', JSON.stringify(r)], { relativeTo: this.route }); + break; + case 200: + // this is added to register url where to forward + // if LoT is cancelled after redirect page is displayed + // to be removed when issue https://github.com/adorsys/open-banking-gateway/issues/848 is resolved + // or Fintech UI refactored + this.accounts = response.body.accounts; + const loa = []; + for (const accountDetail of this.accounts) { + loa.push(new AccountStruct(accountDetail.resourceId, accountDetail.iban, accountDetail.name)); + } + this.storageService.setLoa(this.bankId, loa); + } + }); } } diff --git a/fintech-examples/fintech-ui/src/app/bank/list-accounts/list.accounts.component.spec.ts b/fintech-examples/fintech-ui/src/app/bank/list-accounts/list.accounts.component.spec.ts index 8f20ee902d..e0401a07a8 100644 --- a/fintech-examples/fintech-ui/src/app/bank/list-accounts/list.accounts.component.spec.ts +++ b/fintech-examples/fintech-ui/src/app/bank/list-accounts/list.accounts.component.spec.ts @@ -55,10 +55,10 @@ describe('ListAccountsComponent', () => { const mockAccounts: HttpResponse = {} as HttpResponse; spyOn(aisService, 'getAccounts') - .withArgs(bankId, loaRetrievalInformation, false, true) + .withArgs(bankId, loaRetrievalInformation, '', false, true, true) .and.returnValue(of(mockAccounts)); expect(component.bankId).toEqual(bankId); - aisService.getAccounts(bankId, loaRetrievalInformation, false, true).subscribe((res) => { + aisService.getAccounts(bankId, loaRetrievalInformation, '', false, true, true).subscribe((res) => { expect(res).toEqual(mockAccounts); }); }); diff --git a/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.html b/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.html index 83586b7ca7..2548d46810 100644 --- a/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.html +++ b/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.html @@ -13,4 +13,6 @@ [title]="'Booked'" [isBookedTransaction]="transactions?.booked?.length > 0" > + +
{{ transactions?.rawTransactions }}
diff --git a/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.ts b/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.ts index c985f20d7e..8590a5a3c3 100644 --- a/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.ts +++ b/fintech-examples/fintech-ui/src/app/bank/list-transactions/list-transactions.component.ts @@ -36,8 +36,16 @@ export class ListTransactionsComponent implements OnInit { private loadTransactions(): void { const settings = this.storageService.getSettings(); const online = !this.storageService.isAfterRedirect() && !settings.cacheLot; + this.aisService - .getTransactions(this.bankId, this.accountId, settings.lot, online) + .getTransactions( + this.bankId, + this.accountId, + settings.lot, + JSON.stringify(settings.consent), + online, + settings.consentRequiresAuthentication + ) .subscribe((response) => { switch (response.status) { case 202: diff --git a/fintech-examples/fintech-ui/src/app/bank/list-transactions/list.transactions.component.spec.ts b/fintech-examples/fintech-ui/src/app/bank/list-transactions/list.transactions.component.spec.ts index 0e41d48d02..92364b5f24 100644 --- a/fintech-examples/fintech-ui/src/app/bank/list-transactions/list.transactions.component.spec.ts +++ b/fintech-examples/fintech-ui/src/app/bank/list-transactions/list.transactions.component.spec.ts @@ -62,10 +62,10 @@ describe('ListTransactionsComponent', () => { const loTRetrievalInformation: LoTRetrievalInformation = LoTRetrievalInformation.FROM_TPP_WITH_AVAILABLE_CONSENT; spyOn(aisService, 'getTransactions') - .withArgs(bankId, accountId, loTRetrievalInformation, true) + .withArgs(bankId, accountId, loTRetrievalInformation, '', true, true) .and.returnValue(of(mockTransactions)); expect(component.bankId).toEqual(bankId); - aisService.getTransactions(bankId, accountId, loTRetrievalInformation, true).subscribe((res) => { + aisService.getTransactions(bankId, accountId, loTRetrievalInformation, '', true, true).subscribe((res) => { expect(res).toEqual(mockTransactions); }); }); diff --git a/fintech-examples/fintech-ui/src/app/bank/payment/payment-account-payments/payment-account-payments.component.spec.ts b/fintech-examples/fintech-ui/src/app/bank/payment/payment-account-payments/payment-account-payments.component.spec.ts index f880bee3d0..bd17b4b9f8 100644 --- a/fintech-examples/fintech-ui/src/app/bank/payment/payment-account-payments/payment-account-payments.component.spec.ts +++ b/fintech-examples/fintech-ui/src/app/bank/payment/payment-account-payments/payment-account-payments.component.spec.ts @@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { RouterTestingModule } from '@angular/router/testing'; -import {ReactiveFormsModule} from "@angular/forms"; +import { ReactiveFormsModule } from '@angular/forms'; import { PaymentAccountPaymentsComponent } from './payment-account-payments.component'; diff --git a/fintech-examples/fintech-ui/src/app/bank/payment/payment-accounts/payment-accounts.component.ts b/fintech-examples/fintech-ui/src/app/bank/payment/payment-accounts/payment-accounts.component.ts index 14eb15a55c..abb66556ec 100644 --- a/fintech-examples/fintech-ui/src/app/bank/payment/payment-accounts/payment-accounts.component.ts +++ b/fintech-examples/fintech-ui/src/app/bank/payment/payment-accounts/payment-accounts.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { StorageService } from '../../../services/storage.service'; import { Consts } from '../../../models/consts'; -import {ValidatorService} from "angular-iban"; -import {FormBuilder, FormGroup, Validators} from "@angular/forms"; +import { ValidatorService } from 'angular-iban'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-list-accounts-for-payment', @@ -17,8 +17,12 @@ export class PaymentAccountsComponent implements OnInit { accounts = []; ibanForm: FormGroup; - constructor(private storageService: StorageService, private formBuilder: FormBuilder, - private router: Router, private route: ActivatedRoute) {} + constructor( + private storageService: StorageService, + private formBuilder: FormBuilder, + private router: Router, + private route: ActivatedRoute + ) {} ngOnInit() { this.bankId = this.route.snapshot.params[Consts.BANK_ID_NAME]; @@ -36,8 +40,8 @@ export class PaymentAccountsComponent implements OnInit { } initiateSinglePayment() { - const iban = this.ibanForm.get('iban').value.replace(/\s/g, ""); - this.router.navigate(['../account/initiate'], {relativeTo: this.route, queryParams: {iban}}); + const iban = this.ibanForm.get('iban').value.replace(/\s/g, ''); + this.router.navigate(['../account/initiate'], { relativeTo: this.route, queryParams: { iban } }); } get iban() { diff --git a/fintech-examples/fintech-ui/src/app/bank/payment/payment-initiate/initiate.component.ts b/fintech-examples/fintech-ui/src/app/bank/payment/payment-initiate/initiate.component.ts index 967201c170..ee0f197faa 100644 --- a/fintech-examples/fintech-ui/src/app/bank/payment/payment-initiate/initiate.component.ts +++ b/fintech-examples/fintech-ui/src/app/bank/payment/payment-initiate/initiate.component.ts @@ -1,21 +1,19 @@ -import {Component, OnInit} from '@angular/core'; -import {FormBuilder, FormGroup, Validators} from '@angular/forms'; -import {HttpResponse} from '@angular/common/http'; -import {Location} from "@angular/common"; -import {ActivatedRoute, Router} from '@angular/router'; -import {ValidatorService} from 'angular-iban'; -import {FintechSinglePaymentInitiationService, SinglePaymentInitiationRequest} from '../../../api'; -import {Consts, HeaderConfig} from '../../../models/consts'; -import {RedirectStruct, RedirectType} from '../../redirect-page/redirect-struct'; -import {StorageService} from '../../../services/storage.service'; -import {ConfirmData} from '../payment-confirm/confirm.data'; +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { HttpResponse } from '@angular/common/http'; +import { Location } from '@angular/common'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ValidatorService } from 'angular-iban'; +import { FintechSinglePaymentInitiationService, SinglePaymentInitiationRequest } from '../../../api'; +import { Consts, HeaderConfig } from '../../../models/consts'; +import { RedirectStruct, RedirectType } from '../../redirect-page/redirect-struct'; +import { StorageService } from '../../../services/storage.service'; +import { ConfirmData } from '../payment-confirm/confirm.data'; class TestPayment { - constructor(public referenceName: string, public purpose: string) { - } + constructor(public referenceName: string, public purpose: string) {} } - @Component({ selector: 'app-initiate', templateUrl: './initiate.component.html', @@ -25,11 +23,11 @@ export class InitiateComponent implements OnInit { public static ROUTE = 'initiate'; static TEST_PAYMENTS: TestPayment[] = [ - new TestPayment("test user", "test transfer"), - new TestPayment("Anton", "Transfer to Anton (demo)"), - new TestPayment("Amazon payment", "Payment for order #12345 (demo)"), - new TestPayment("Apple", "Apple ITunes payment (demo)"), - new TestPayment("Netflix TV", "Netflix payment (demo)") + new TestPayment('test user', 'test transfer'), + new TestPayment('Anton', 'Transfer to Anton (demo)'), + new TestPayment('Amazon payment', 'Payment for order #12345 (demo)'), + new TestPayment('Apple', 'Apple ITunes payment (demo)'), + new TestPayment('Netflix TV', 'Netflix payment (demo)') ]; bankId = ''; @@ -52,7 +50,8 @@ export class InitiateComponent implements OnInit { ngOnInit() { this.debtorIban = this.debtorIban ? this.debtorIban : this.getDebitorIban(this.accountId); - const testPayment = InitiateComponent.TEST_PAYMENTS[Math.floor(Math.random() * InitiateComponent.TEST_PAYMENTS.length)] + const testPayment = + InitiateComponent.TEST_PAYMENTS[Math.floor(Math.random() * InitiateComponent.TEST_PAYMENTS.length)]; this.paymentForm = this.formBuilder.group({ name: [testPayment.referenceName, Validators.required], creditorIban: ['AL90208110080000001039531801', [ValidatorService.validateIban, Validators.required]], diff --git a/fintech-examples/fintech-ui/src/app/bank/services/ais.service.spec.ts b/fintech-examples/fintech-ui/src/app/bank/services/ais.service.spec.ts index 2422f9fab3..7a968c541c 100644 --- a/fintech-examples/fintech-ui/src/app/bank/services/ais.service.spec.ts +++ b/fintech-examples/fintech-ui/src/app/bank/services/ais.service.spec.ts @@ -26,13 +26,20 @@ describe('AisService', () => { it('should get accounts', () => { const getAccountsSpy = spyOn(aisService, 'getAccounts'); - aisService.getAccounts('1234', LoARetrievalInformation.FROM_TPP_WITH_AVAILABLE_CONSENT, true, false); + aisService.getAccounts('1234', LoARetrievalInformation.FROM_TPP_WITH_AVAILABLE_CONSENT, '', true, false, true); expect(getAccountsSpy).toHaveBeenCalled(); }); it('should get transactions', () => { const getTransactionsSpy = spyOn(aisService, 'getTransactions'); - aisService.getTransactions('1234', 'xxxxxxxxxx', LoTRetrievalInformation.FROM_TPP_WITH_AVAILABLE_CONSENT, false); + aisService.getTransactions( + '1234', + 'xxxxxxxxxx', + LoTRetrievalInformation.FROM_TPP_WITH_AVAILABLE_CONSENT, + '', + false, + true + ); expect(getTransactionsSpy).toHaveBeenCalled(); }); }); diff --git a/fintech-examples/fintech-ui/src/app/bank/services/ais.service.ts b/fintech-examples/fintech-ui/src/app/bank/services/ais.service.ts index 78b467cac3..c5b8aee4ee 100644 --- a/fintech-examples/fintech-ui/src/app/bank/services/ais.service.ts +++ b/fintech-examples/fintech-ui/src/app/bank/services/ais.service.ts @@ -12,7 +12,14 @@ export class AisService { return toConvert.toISOString().split('T')[0]; } - getAccounts(bankId: string, loARetrievalInformation: LoARetrievalInformation, withBalance: boolean, online: boolean) { + getAccounts( + bankId: string, + loARetrievalInformation: LoARetrievalInformation, + createConsentIfNone: string, + withBalance: boolean, + online: boolean, + authenticatePsu: boolean + ) { const okurl = window.location.pathname; const notOkUrl = okurl.replace(/account.*/, ''); @@ -23,6 +30,8 @@ export class AisService { okurl, notOkUrl, loARetrievalInformation, + authenticatePsu, + createConsentIfNone, withBalance, online, 'response' @@ -33,7 +42,9 @@ export class AisService { bankId: string, accountId: string, loTRetrievalInformation: LoTRetrievalInformation, - online: boolean + createConsentIfNone: string, + online: boolean, + authenticatePsu: boolean ) { const okurl = window.location.pathname; const notOkUrl = okurl.replace(/account.*/, 'accounts'); @@ -46,6 +57,8 @@ export class AisService { okurl, notOkUrl, loTRetrievalInformation, + authenticatePsu, + createConsentIfNone, '1970-01-01', AisService.isoDate(new Date()), null, diff --git a/fintech-examples/fintech-ui/src/app/bank/settings/settings.component.html b/fintech-examples/fintech-ui/src/app/bank/settings/settings.component.html index 1846adb164..cc6a48ef41 100644 --- a/fintech-examples/fintech-ui/src/app/bank/settings/settings.component.html +++ b/fintech-examples/fintech-ui/src/app/bank/settings/settings.component.html @@ -43,7 +43,7 @@

List of Transactions

- +
List of Transactions />
+
+ + +
+ + +
+

Consent Request

+
+
+ + + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+
@@ -68,8 +154,13 @@

Payments

+
+

Other settings

+ +
-