diff --git a/.github/workflows/ci-build-deploy.yaml b/.github/workflows/ci-build-deploy.yaml index cc6d01244..d4bf13449 100644 --- a/.github/workflows/ci-build-deploy.yaml +++ b/.github/workflows/ci-build-deploy.yaml @@ -132,7 +132,7 @@ jobs: runAsUser: ${{ secrets.RUNNING_UID_GID }} ' > values.yaml helm repo add bitnami https://charts.bitnami.com/bitnami - helm upgrade --install proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}-db -f values.yaml bitnami/mongodb + helm upgrade --install proto-asp-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}-db --version 10.31.5 -f values.yaml bitnami/mongodb - name: 'Deploy Backend' run: | @@ -167,14 +167,17 @@ jobs: oauthProxy: enabled: true image: - tag: v7.2.0 + repository: ${{ env.REGISTRY }}/bcgov-dss/api-serv-infra/oauth2-proxy + tag: 7.2.1-8c743f0c + pullPolicy: IfNotPresent + config: upstream: http://127.0.0.1:3000 client-id: ${{ secrets.OIDC_CLIENT_ID }} client-secret: ${{ secrets.OIDC_CLIENT_SECRET }} oidc-issuer-url: ${{ secrets.OIDC_ISSUER }} redirect-url: https://api-services-portal-${{ steps.set-deploy-id.outputs.DEPLOY_ID }}.apps.silver.devops.gov.bc.ca/oauth2/callback - skip-auth-regex: '/health|/public|/docs|/redirect|/_next|/images|/devportal|/manager|/about|/maintenance|/admin/session|/ds/api|/feed/|/signout|^[/]$' + skip-auth-regex: '/login|/health|/public|/docs|/redirect|/_next|/images|/devportal|/manager|/about|/maintenance|/admin/session|/ds/api|/feed/|/signout|^[/]$' whitelist-domain: authz-apps-gov-bc-ca.dev.api.gov.bc.ca skip-provider-button: 'true' profile-url: ${{ secrets.OIDC_ISSUER }}/protocol/openid-connect/userinfo diff --git a/.github/workflows/scripts/feeder-init/dataset-test.yaml b/.github/workflows/scripts/feeder-init/dataset-test.yaml new file mode 100644 index 000000000..92d94e419 --- /dev/null +++ b/.github/workflows/scripts/feeder-init/dataset-test.yaml @@ -0,0 +1,13 @@ +entity: Dataset +record: + id: 'B234567890000000' + name: 'test-feature-product' + title: 'Test Feature Product' + notes: 'Product Notes' + tags: ['gateway', 'feature'] + sector: 'Service' + license_title: 'Access Only' + view_audience: 'Government' + security_class: 'LOW-PUBLIC' + org: '7a66db63-26f4-4052-9cd5-3272b63910f8' + sub_org: '319b3297-846d-4b97-8095-ceb3ec505fb8' diff --git a/.github/workflows/scripts/feeder-init/organization-unit.yaml b/.github/workflows/scripts/feeder-init/organization-unit.yaml new file mode 100644 index 000000000..b66a766af --- /dev/null +++ b/.github/workflows/scripts/feeder-init/organization-unit.yaml @@ -0,0 +1,20 @@ +entity: Organization +record: + id: 7a66db63-26f4-4052-9cd5-3272b63910f8 + type: organization + name: ministry-of-citizens-services + sector: '' + title: 'Ministry of Citizens Services' + tags: [] + description: '' + extSource: '' + extRecordHash: '' + orgUnits: + - id: 319b3297-846d-4b97-8095-ceb3ec505fb8 + name: databc + sector: '' + title: 'DataBC' + tags: [] + description: '' + extSource: '' + extRecordHash: '' diff --git a/.github/workflows/scripts/init.sh b/.github/workflows/scripts/init.sh index f6db6b4fa..c8036ff13 100755 --- a/.github/workflows/scripts/init.sh +++ b/.github/workflows/scripts/init.sh @@ -18,6 +18,8 @@ while true; do curl --fail -v http://localhost:8080/push -F yaml=@platform-authz-profile.yaml curl --fail -v http://localhost:8080/push -F yaml=@platform-dataset.yaml curl --fail -v http://localhost:8080/push -F yaml=@platform-gwa-api.yaml + curl --fail -v http://localhost:8080/push -F yaml=@scripts/feeder-init/organization-unit.yaml + curl --fail -v http://localhost:8080/push -F yaml=@scripts/feeder-init/dataset-test.yaml kill $FWD_PID break else diff --git a/e2e/cypress.json b/e2e/cypress.json index e0415af52..d0f20ee6a 100644 --- a/e2e/cypress.json +++ b/e2e/cypress.json @@ -23,5 +23,9 @@ "JWKS_URL": "http://cypress-jwks-url.localtest.me:3500", "KONG_CONFIG_URL": "http://kong.localtest.me:8001", "BASE_URL": "http://oauth2proxy.localtest.me:4180" + }, + "retries": { + "runMode": 2, + "openMode": 0 } } diff --git a/e2e/cypress/fixtures/access-manager.json b/e2e/cypress/fixtures/access-manager.json index d40b3de41..8d31d7bf4 100644 --- a/e2e/cypress/fixtures/access-manager.json +++ b/e2e/cypress/fixtures/access-manager.json @@ -5,7 +5,7 @@ "password": "mark" } }, - "namespace": "platform", + "namespace": "newplatform", "clientCredentials": { "namespace": "ccplatform" }, diff --git a/e2e/cypress/fixtures/api.json b/e2e/cypress/fixtures/api.json index 722799562..875a0600c 100644 --- a/e2e/cypress/fixtures/api.json +++ b/e2e/cypress/fixtures/api.json @@ -3,6 +3,74 @@ "headers": { "accept": "application/json" }, - "endPoint": "ds/api/v2/organizations" + "endPoint": "ds/api/v2/organizations", + "orgExpectedList": + { + "name": "planning-and-innovation-division", + "title": "Planning and Innovation Division" + }, + "orgName": "ministry-of-health" + }, + "documentation": { + "endPoint": "ds/api/v2/namespaces/apiplatform/contents", + "headers": { + "accept": "application/json", + "content-type": "application/json" + }, + "body": { + "externalLink": "https://externalsite/my_content", + "title": "my_content", + "description": "Summary of Test content", + "content": "Markdown content", + "order": 0, + "isPublic": true, + "isComplete": true, + "tags": ["tag1", "tag2"] + } + }, + "authorizationProfiles": { + "body": { + "name": "my-auth-profile", + "description": "Auth connection to my IdP", + "flow": "client-credentials", + "clientAuthenticator": "client-secret", + "mode": "auto", + "environmentDetails": [ + { + "environment": "dev", + "issuerUrl": "http://keycloak.localtest.me:9080/auth/realms/master", + "clientRegistration": "managed", + "clientId": "cypress-auth-profile", + "clientSecret": "43badfc1-c06f-4bec-bab6-ccdc764071ac" + } + ], + "owner": "janis@idir" + }, + "endPoint": "ds/api/v2/namespaces/apiplatform/issuers", + "headers": { + "accept": "application/json", + "content-type": "application/json" + } + }, + "products": { + "headers": { + "accept": "application/json", + "content-type": "application/json" + }, + "endPoint": "ds/api/v2/namespaces/apiplatform/products", + "deleteEnvironmentEndPoint": "ds/api/v2/namespaces/apiplatform/environments", + "body": { + "name": "my-new-product", + "appId": "DRE123456", + "environments": [ + { + "name": "test", + "active": false, + "approval": false, + "flow": "public", + "appId": "6754" + } + ] + } } -} \ No newline at end of file +} diff --git a/e2e/cypress/fixtures/apiowner.json b/e2e/cypress/fixtures/apiowner.json index c8e998b3f..b001b1fbf 100644 --- a/e2e/cypress/fixtures/apiowner.json +++ b/e2e/cypress/fixtures/apiowner.json @@ -5,12 +5,24 @@ "password": "awsummer" } }, - "namespace": "platform", + "namespace": "newplatform", "serviceAccount": { "scopes": ["GatewayConfig.Publish", "Namespace.Manage", "Content.Publish"] }, "deleteResources": { - "namespace": "platform1" + "namespace": "deleteplatform", + "product": { + "name": "Delete-Auto Test Product", + "environment": { + "name": "dev", + "config": { + "terms": "Terms of Use for API Gateway", + "authorization": "Kong API Key with ACL Flow", + "optionalInstructions": "This is a automation test", + "serviceName": "a-service-for-deleteplatform" + } + } + } }, "namespaceAccessPermissions": ["CredentialIssuer.Admin"], "product": { @@ -23,7 +35,7 @@ "terms": "Terms of Use for API Gateway", "authorization": "Kong API Key with ACL Flow", "optionalInstructions": "This is a automation test", - "serviceName": "a-service-for-platform" + "serviceName": "a-service-for-newplatform" } } }, @@ -33,6 +45,7 @@ "authProfile": { "name": "cy-jwt-kp-auth", "flow": "Client Credential Flow", + "element":"cc-jwt-key", "clientAuthenticator": "Signed JWT - Generated Key Pair", "environmentConfig": { "environment": "Development", @@ -58,6 +71,7 @@ "authProfile": { "name": "cy-jwks-url-auth", "flow": "Client Credential Flow", + "element":"cc-jwt-jwks", "clientAuthenticator": "Signed JWT with JWKS URL", "environmentConfig": { "environment": "Sandbox", @@ -96,6 +110,7 @@ "authProfile": { "name": "cy-client-id-secret-auth", "flow": "Client Credential Flow", + "element":"cc-id-secret", "clientAuthenticator": "Client ID and Secret", "environmentConfig": { "environment": "Test", @@ -172,5 +187,8 @@ "userName" :"mark", "accessRole" :["Access.Manage","Namespace.View"] } + }, + "apiTest": { + "namespace": "apiplatform" } } \ No newline at end of file diff --git a/e2e/cypress/fixtures/credential-issuer.json b/e2e/cypress/fixtures/credential-issuer.json index 62c21cdb7..943a169fd 100644 --- a/e2e/cypress/fixtures/credential-issuer.json +++ b/e2e/cypress/fixtures/credential-issuer.json @@ -10,14 +10,15 @@ }, "clientCredentials": { "authProfile": { - "name": "cy-jwt-kp-auth", + "name": "cc-jwt-key-123", "flow": "Client Credential Flow", + "element":"cc-jwt-key", "clientAuthenticator": "Signed JWT - Generated Key Pair", "environmentConfig": { "environment": "Development", "clientRegistration": "Managed", "idpIssuerUrl": "http://keycloak.localtest.me:9080/auth/realms/master", - "clientId": "cypress-auth-profile", + "clientId": "cc-auth-profile", "clientSecret": "43badfc1-c06f-4bec-bab6-ccdc764071ac" } } diff --git a/e2e/cypress/fixtures/developer.json b/e2e/cypress/fixtures/developer.json index 00f96ffc4..3c4344d90 100644 --- a/e2e/cypress/fixtures/developer.json +++ b/e2e/cypress/fixtures/developer.json @@ -5,7 +5,7 @@ "password": "local" } }, - "namespace": "platform", + "namespace": "newplatform", "product": { "name": "Auto Test Product", "environment": "dev" diff --git a/e2e/cypress/fixtures/manage-control/kong-plugin-config.json b/e2e/cypress/fixtures/manage-control/kong-plugin-config.json index f1734ca53..5e621d49f 100644 --- a/e2e/cypress/fixtures/manage-control/kong-plugin-config.json +++ b/e2e/cypress/fixtures/manage-control/kong-plugin-config.json @@ -17,6 +17,6 @@ "username": "consumer1" }, "keyAuth": { - "config.anonymous": "5f1c6410-be1c-468c-864b-10d7da7a0552" + "config.anonymous": "e63c9474-efd0-4e27-bb6f-f649f9661262" } } \ No newline at end of file diff --git a/e2e/cypress/fixtures/service-clear-resources.yml b/e2e/cypress/fixtures/service-clear-resources.yml new file mode 100644 index 000000000..11a83ad3d --- /dev/null +++ b/e2e/cypress/fixtures/service-clear-resources.yml @@ -0,0 +1,19 @@ +services: +- name: service-for-deleteplatform + host: httpbin.org + tags: [ns.deleteplatform] + port: 443 + protocol: https + retries: 0 + routes: + - name: service-for-deleteplatform-route + tags: [ns.deleteplatform] + hosts: + - service-for-deleteplatform.api.gov.bc.ca + paths: + - / + methods: + - GET + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 \ No newline at end of file diff --git a/e2e/cypress/fixtures/service-plugin-key-auth-only.yml b/e2e/cypress/fixtures/service-plugin-key-auth-only.yml index 6b6043c33..6c3a15539 100644 --- a/e2e/cypress/fixtures/service-plugin-key-auth-only.yml +++ b/e2e/cypress/fixtures/service-plugin-key-auth-only.yml @@ -1,15 +1,15 @@ services: -- name: a-service-for-platform +- name: a-service-for-newplatform host: httpbin.org - tags: [ns.platform] + tags: [ns.newplatform] port: 443 protocol: https retries: 0 routes: - - name: a-service-for-platform-route - tags: [ns.platform] + - name: a-service-for-newplatform-route + tags: [ns.newplatform] hosts: - - a-service-for-platform.api.gov.bc.ca + - a-service-for-newplatform.api.gov.bc.ca paths: - / methods: @@ -21,7 +21,7 @@ services: plugins: - name: key-auth - tags: [ ns.platform ] + tags: [ ns.newplatform ] protocols: [ http, https ] config: key_names: ["X-API-KEY"] diff --git a/e2e/cypress/fixtures/service.yml b/e2e/cypress/fixtures/service.yml index 594298d4c..307cb9230 100644 --- a/e2e/cypress/fixtures/service.yml +++ b/e2e/cypress/fixtures/service.yml @@ -1,15 +1,15 @@ services: -- name: a-service-for-platform +- name: a-service-for-newplatform host: httpbin.org - tags: [ns.platform] + tags: [ns.newplatform] port: 443 protocol: https retries: 0 routes: - - name: a-service-for-platform-route - tags: [ns.platform] + - name: a-service-for-newplatform-route + tags: [ns.newplatform] hosts: - - a-service-for-platform.api.gov.bc.ca + - a-service-for-newplatform.api.gov.bc.ca paths: - / methods: diff --git a/e2e/cypress/fixtures/test_data/authorizationProfile.json b/e2e/cypress/fixtures/test_data/authorizationProfile.json new file mode 100644 index 000000000..c0f0ced55 --- /dev/null +++ b/e2e/cypress/fixtures/test_data/authorizationProfile.json @@ -0,0 +1,65 @@ +[ + { + "body": { + "environmentDetails": [ + { + "environment": "dev", + "issuerUrl": "http://keycloak.localtest.me:9080/auth/realms/master", + "clientRegistration": "managed", + "clientId": "cypress-auth-profile", + "clientSecret": "43badfc1-c06f-4bec-bab6-ccdc764071ac" + } + ], + "mode": "auto", + "availableScopes": ["aps-portal"], + "clientRoles": ["administrator"], + "clientMappers": ["test-audience"], + "resourceType": "test_UMA", + "resourceScopes": ["RS"], + "resourceAccessScope": "Access1", + "flow": "client-credentials", + "clientAuthenticator": "client-secret", + "name": "my-auth-client-secret", + "description": "Auth connection to my IdP", + "owner": "janis@idir" + } + }, + { + "body": { + "name": "my-auth-client-jwt", + "description": "Auth connection to my IdP", + "flow": "client-credentials", + "clientAuthenticator": "client-jwt", + "mode": "auto", + "environmentDetails": [ + { + "environment": "dev", + "issuerUrl": "http://keycloak.localtest.me:9080/auth/realms/master", + "clientRegistration": "managed", + "clientId": "cypress-auth-profile", + "clientSecret": "43badfc1-c06f-4bec-bab6-ccdc764071ac" + } + ], + "owner": "janis@idir" + } + }, + { + "body": { + "name": "my-auth-client-jwt-jwks-url", + "description": "Auth connection to my IdP", + "flow": "client-credentials", + "clientAuthenticator": "client-jwt-jwks-url", + "mode": "auto", + "environmentDetails": [ + { + "environment": "dev", + "issuerUrl": "http://keycloak.localtest.me:9080/auth/realms/master", + "clientRegistration": "managed", + "clientId": "cypress-auth-profile", + "clientSecret": "43badfc1-c06f-4bec-bab6-ccdc764071ac" + } + ], + "owner": "janis@idir" + } + } +] diff --git a/e2e/cypress/pageObjects/apiDirectory.ts b/e2e/cypress/pageObjects/apiDirectory.ts index e7f849c4d..c95514a94 100644 --- a/e2e/cypress/pageObjects/apiDirectory.ts +++ b/e2e/cypress/pageObjects/apiDirectory.ts @@ -2,33 +2,42 @@ import { checkElementExists } from '../support' class ApiDirectoryPage { path: string = '/devportal/api-directory' - rqstAccessBtn: string = '[data-testid=api-rqst-access-btn]' - appSelect: string = '[data-testid=access-rqst-app-select]' + rqstAccessBtn: string = '[data-testid=request-access-button]' + appSelect: string = '[data-testid=access-application-select]' additionalNotes: string = '[data-testid=access-rqst-add-notes-text]' - submitBtn: string = '[data-testid=access-rqst-submit-btn]' + submitBtn: string = '[data-testid=access-request-submit-button]' generateSecretsBtn: string = '[data-testid=access-rqst-gen-scrts-btn]' clientIdField: string = '[data-testid=sa-new-creds-client-id]'; clientSecretField: string = '[data-testid=sa-new-creds-client-secret]'; tokenEndpointField: string = '[data-testid=sa-new-creds-token-endpoint]'; acceptTermsBtn: string = '[data-testid=access-rqst-legal-terms-cb]'; - jwksUrlField: string = '[data-testid=access-rqst-jwks-url]'; - + // jwksUrlField: string = '[data-testid=access-rqst-jwks-url]'; + jwksUrlField: string = '[name=jwksUrl]'; + legatTermCheckBox: string = '[data-testid=acceptLegalTerm]'; + jwksURLField: string = '[name=jwksUrl]' createAccessRequest(product: any, app: any, accessRqst: any) { - cy.contains('a',product.name, { timeout: 10000 }).should('be.visible'); + cy.contains('a', product.name, { timeout: 10000 }).should('be.visible'); cy.contains(product.name).click() cy.get(this.rqstAccessBtn).click() cy.get(this.appSelect).select(app.name) cy.get('[data-testid=access-rqst-app-env-' + product.environment + ']').click() - cy.document().then((doc) => { - if (doc.querySelector(this.acceptTermsBtn)) { - cy.contains('Terms of Use for API Gateway').click() + cy.get('body', { log: false }).then(($body) => { + if ($body.find(this.legatTermCheckBox).length > 0) { + cy.get(this.legatTermCheckBox).first().click() } - if (doc.querySelector(this.jwksUrlField)) { + }) + + cy.get('body', { log: false }).then(($body) => { + if ($body.find(this.jwksUrlField).length > 0) { cy.get(this.jwksUrlField).click().type(Cypress.env('JWKS_URL')) } }) - + // cy.document().then((doc) => { + // if (doc.querySelector(this.jwksUrlField)) { + // cy.get(this.jwksUrlField).click().type(Cypress.env('JWKS_URL')) + // } + // }) cy.get(this.additionalNotes).type(accessRqst.notes) cy.get(this.submitBtn).click() }; diff --git a/e2e/cypress/pageObjects/authProfile.ts b/e2e/cypress/pageObjects/authProfile.ts index f734e3f42..8fdc8fa3e 100644 --- a/e2e/cypress/pageObjects/authProfile.ts +++ b/e2e/cypress/pageObjects/authProfile.ts @@ -41,7 +41,7 @@ class AuthorizationProfile { cy.get(this.kongApiKey).type(authProfile.apiKey) cy.get(this.authenticationContinueBtn).click() } else if (flow === 'Client Credential Flow') { - cy.get(this.clientCredentialFlow).click() + cy.get('[data-testid='+ authProfile.element + '-chkBox]').click() cy.get(this.authenticationContinueBtn).click() cy.get(this.authorizationContinueBtn).click() // cy.get(this.clientAuthenticator).contains(authProfile.clientAuthenticator).click() @@ -106,7 +106,6 @@ class AuthorizationProfile { cy.get(this.envAddBtn).click() } } - cy.get(this.createBtn).click() cy.wait(2000) if (isCreated === true) diff --git a/e2e/cypress/pageObjects/myAccess.ts b/e2e/cypress/pageObjects/myAccess.ts index e79be3fb7..894181bc5 100644 --- a/e2e/cypress/pageObjects/myAccess.ts +++ b/e2e/cypress/pageObjects/myAccess.ts @@ -1,5 +1,5 @@ class myAccessPage { - generateSecretsBtn: string = '[data-testid=access-rqst-gen-scrts-btn]' + generateSecretsBtn: string = '[data-testid=generate-secrets-button]' apiKyeValueTxt: string = '[data-testid=sa-new-creds-api-key]' clientId: string = '[data-testid=sa-new-creds-client-id]' clientSecret: string = '[data-testid=sa-new-creds-client-secret]' @@ -7,74 +7,80 @@ class myAccessPage { privateKey: string = '[data-testid=sa-new-creds-signing-private-key]' publicKey: string = '[data-testid=sa-new-creds-signing-public-certificate]' issuer: string = '[data-testid=sa-new-creds-issuer]' + closeRequestAccesss: string = '[data-testid=doneAcceptRequest]' clickOnGenerateSecretButton() { cy.get(this.generateSecretsBtn).click() } saveAPIKeyValue(): void { - cy.get(this.apiKyeValueTxt).then(($apiKey) => { - cy.saveState('apikey', $apiKey.text()) + cy.get(this.apiKyeValueTxt).invoke('val').then(($apiKey: any) => { + debugger + cy.saveState('apikey', $apiKey) }) + cy.get(this.closeRequestAccesss).click() } saveClientCredentials(): void { - cy.get(this.clientId).then(($clientId) => { - cy.get(this.clientSecret).then(($clientSecret) => { - cy.get(this.tokenEndpoint).then(($tokenEndpoint) => { + cy.get(this.clientId).invoke('val').then(($clientId: any) => { + cy.get(this.clientSecret).invoke('val').then(($clientSecret: any) => { + cy.get(this.tokenEndpoint).invoke('val').then(($tokenEndpoint: any) => { cy.saveState( 'clientidsecret', '{"clientId": "' + - $clientId.text() + + $clientId + '", "clientSecret": "' + - $clientSecret.text() + + $clientSecret + '", "tokenEndpoint": "' + - $tokenEndpoint.text() + + $tokenEndpoint + '"}' ) }) }) }) + cy.get(this.closeRequestAccesss).click() } saveJwtKeyPairCredentials(): void { - cy.get(this.clientId).then(($clientId) => { - cy.get(this.privateKey).then(($privateKey) => { - cy.get(this.publicKey).then(($publicKey) => { - cy.get(this.tokenEndpoint).then(($tokenEndpoint) => { + cy.get(this.clientId).invoke('val').then(($clientId:any) => { + cy.get(this.privateKey).invoke('val').then(($privateKey:any) => { + cy.get(this.publicKey).invoke('val').then(($publicKey:any) => { + cy.get(this.tokenEndpoint).invoke('val').then(($tokenEndpoint:any) => { cy.saveState( 'jwtkeypaircredentials', '{"clientId": "' + - $clientId.text() + + $clientId + '", "tokenEndpoint": "' + - $tokenEndpoint.text() + + $tokenEndpoint + '"}' ) - cy.writeFile('cypress/fixtures/state/jwtGenPrivateKey.pem', $privateKey.text()) - cy.writeFile('cypress/fixtures/state/jwtGenPublicKey.pub', $publicKey.text()) + cy.writeFile('cypress/fixtures/state/jwtGenPrivateKey.pem', $privateKey) + cy.writeFile('cypress/fixtures/state/jwtGenPublicKey.pub', $publicKey) }) }) }) }) + cy.get(this.closeRequestAccesss).click() } saveJwksUrlCredentials(): void { - cy.get(this.clientId).then(($clientId) => { - cy.get(this.issuer).then(($issuer) => { - cy.get(this.tokenEndpoint).then(($tokenEndpoint) => { + cy.get(this.clientId).invoke('val').then(($clientId:any) => { + cy.get(this.issuer).invoke('val').then(($issuer:any) => { + cy.get(this.tokenEndpoint).invoke('val').then(($tokenEndpoint:any) => { cy.saveState( 'jwksurlcredentials', '{"clientId": "' + - $clientId.text() + + $clientId + '", "issuer": "' + - $issuer.text() + + $issuer + '", "tokenEndpoint": "' + - $tokenEndpoint.text() + + $tokenEndpoint + '"}' ) }) }) }) + cy.get(this.closeRequestAccesss).click() } } diff --git a/e2e/cypress/pageObjects/products.ts b/e2e/cypress/pageObjects/products.ts index b392136d3..099298d7a 100644 --- a/e2e/cypress/pageObjects/products.ts +++ b/e2e/cypress/pageObjects/products.ts @@ -71,12 +71,13 @@ class Products { cy.get(this.envCfgActivateRadio).click() cy.get(this.envCfgApprovalCheckbox).click() - cy.get(this.envCfgTermsDropdown).select(config.terms) + cy.get(this.envCfgTermsDropdown).select(config.terms, { force: true }).invoke('val') let authType = config.authorization cy.get(this.envCfgAuthzDropdown) - .select(authType) + .select(authType, { force: true }).invoke('val') .then(() => { + if ( authType === 'Oauth2 Authorization Code Flow' || authType === 'Oauth2 Client Credentials Flow' @@ -90,7 +91,7 @@ class Products { }) cy.get(this.envCfgOptText).type(config.optionalInstructions) - + // cy.get(`[data-testid=${config.serviceName}`).click() cy.get(this.envCfgApplyChangesBtn).click() } @@ -137,6 +138,11 @@ class Products { cy.get(this.deleteProductBtn).click() cy.get(this.deleteProductConfirmationBtn).click() } + + verifyProductIsVisible(productName: string) + { + cy.get(`[data-testid=${productName}-edit-btn]`).should('be.visible') + } } export default Products diff --git a/e2e/cypress/pageObjects/serviceAccounts.ts b/e2e/cypress/pageObjects/serviceAccounts.ts index e8ca9487e..99c77737c 100644 --- a/e2e/cypress/pageObjects/serviceAccounts.ts +++ b/e2e/cypress/pageObjects/serviceAccounts.ts @@ -20,14 +20,14 @@ checkServiceAccountNotExist() : void } saveServiceAcctCreds(): void { - cy.get(this.clientId).then(($clientId) => { - cy.get(this.clientSecret).then(($clientSecret) => { + cy.get(this.clientId).invoke('val').then(($clientId) => { + cy.get(this.clientSecret).invoke('val').then(($clientSecret) => { cy.saveState( 'credentials', '{"clientId": "' + - $clientId.text() + + $clientId + '", "clientSecret": "' + - $clientSecret.text() + + $clientSecret + '"}' ) }) diff --git a/e2e/cypress/support/auth-commands.ts b/e2e/cypress/support/auth-commands.ts index 001f88805..5b84c4591 100644 --- a/e2e/cypress/support/auth-commands.ts +++ b/e2e/cypress/support/auth-commands.ts @@ -6,12 +6,15 @@ import { method } from 'cypress/types/bluebird' import { url } from 'inspector' import { checkElementExists } from '.' import NamespaceAccessPage from '../pageObjects/namespaceAccess' +import _ = require('cypress/types/lodash') const config = require('../fixtures/manage-control/kong-plugin-config.json') const jose = require('node-jose') -let headers : any +let headers: any + +let requestBody: any = {} interface formDataRequestOptions { method: string url: string @@ -21,18 +24,23 @@ Cypress.Commands.add('login', (username: string, password: string) => { cy.log('< Log in with user ' + username) const login = new LoginPage() const home = new HomePage() - cy.get(login.loginButton).click() - const log = Cypress.log({ - name: 'Login to Dev', - displayName: 'LOGIN_DEV', - message: [`🔐 Authenticating | ${username}`], - autoEnd: false, + cy.get('header').then(($a) => { + if ($a.text().includes('Login')) { + debugger + cy.get(login.loginButton).click() + const log = Cypress.log({ + name: 'Login to Dev', + displayName: 'LOGIN_DEV', + message: [`🔐 Authenticating | ${username}`], + autoEnd: false, + }) + cy.get(login.usernameInput).click().type(username) + cy.get(login.passwordInput).click().type(password) + cy.get(login.loginSubmitButton).click() + } }) - cy.get(login.usernameInput).click().type(username) - cy.get(login.passwordInput).click().type(password) - cy.get(login.loginSubmitButton).click() - + debugger if (checkElementExists('.alert')) { cy.reload() cy.get(login.usernameInput).click().type(username) @@ -40,7 +48,15 @@ Cypress.Commands.add('login', (username: string, password: string) => { cy.get(login.loginSubmitButton).click() } - log.end() + // log.end() + // cy.getLoginCallback().then(() => { + // cy.get('@login1').should((response :any) => { + // debugger + // if (response.status == 403) + // cy.wait(60000); + // cy.log("Trigger the block") + // }) + // }) cy.get(home.nsDropdown, { timeout: 6000 }).then(($el) => { expect($el).to.exist expect($el).to.be.visible @@ -260,23 +276,57 @@ Cypress.Commands.add('setHeaders', (headerValues: any) => { headers = headerValues }) +Cypress.Commands.add('setRequestBody', (body: any) => { + debugger + requestBody = JSON.stringify(body) +}) + +Cypress.Commands.add('setAuthorizationToken', (token: string) => { + headers["Authorization"] = "Bearer " + token + headers = headers +}) + Cypress.Commands.add('makeAPIRequest', (endPoint: string, methodType: string) => { let body = {} - // var serviceEndPoint = endpoint - // body = config[requestName] - // if (requestName == '') { - // body = {} - // } + + if (methodType.toUpperCase() === 'PUT' || methodType.toUpperCase() === 'POST') { + body = requestBody + } return cy.request({ url: Cypress.env('BASE_URL') + '/' + endPoint, method: methodType, body: body, - form: true, - headers:headers, + headers: headers, failOnStatusCode: false }) }) +Cypress.Commands.add('getUserSession', () => { + cy.intercept(Cypress.config('baseUrl') + '/admin/session').as('login') +}) + +Cypress.Commands.add('compareJSONObjects', (actualResponse: any, expectedResponse: any, indexFlag = false) => { + let response = actualResponse + if (indexFlag) { + const index = actualResponse.findIndex((x: { name: string }) => x.name === expectedResponse.name); + response = actualResponse[index] + } + for (var p in expectedResponse) { + if (typeof (expectedResponse[p]) === "object") { + var objectValue1 = expectedResponse[p], + objectValue2 = response[p]; + for (var value in objectValue1) { + cy.compareJSONObjects(objectValue2[value], objectValue1[value]); + } + } else { + if ((response[p] !== expectedResponse[p]) && !(['clientSecret', 'appId'].includes(p))) { + cy.log("Different Value ->" + expectedResponse[p]) + assert.fail("JSON value mismatch for " + p) + } + } + } +}) + const formDataRequest = ( options: formDataRequestOptions, accessToken: string, diff --git a/e2e/cypress/support/global.d.ts b/e2e/cypress/support/global.d.ts index b8d8435fc..182e7a68a 100644 --- a/e2e/cypress/support/global.d.ts +++ b/e2e/cypress/support/global.d.ts @@ -47,6 +47,14 @@ declare namespace Cypress { setHeaders(headerValues : any) : void - makeAPIRequest(methodType: string): Chainable> + setRequestBody(requestBody : any) : void + + setAuthorizationToken (token : string) : void + + makeAPIRequest(endPoint: string,methodType: string): Chainable> + + getUserSession(): Chainable> + + compareJSONObjects(actualResponse: any, expectedResponse:any, indexFlag?: boolean) : Chainable> } } diff --git a/e2e/cypress/tests/01-api-key/01-create-api.spec.ts b/e2e/cypress/tests/01-api-key/01-create-api.spec.ts index 9314abc70..7630698b2 100644 --- a/e2e/cypress/tests/01-api-key/01-create-api.spec.ts +++ b/e2e/cypress/tests/01-api-key/01-create-api.spec.ts @@ -8,6 +8,8 @@ describe('Create API Spec', () => { const home = new HomePage() const sa = new ServiceAccountsPage() const pd = new Products() + var nameSpace: string + let userSession: string before(() => { cy.visit('/') @@ -19,6 +21,7 @@ describe('Create API Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('api').as('api') cy.visit(login.path) }) @@ -29,8 +32,14 @@ describe('Create API Spec', () => { }) it('creates and activates new namespace', () => { - cy.get('@apiowner').then(({ namespace }: any) => { - home.createNamespace(namespace) + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ namespace }: any) => { + nameSpace = namespace + home.createNamespace(namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) }) }) @@ -57,6 +66,23 @@ describe('Create API Spec', () => { pd.createNewProduct(product.name, product.environment.name) }) }) + + it('Associate Namespace to the organization Unit', () => { + cy.get('@api').then(({ organization }: any) => { + cy.setHeaders(organization.headers) + cy.setAuthorizationToken(userSession) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + it('update the Dataset in BC Data Catelogue to appear the API in the Directory', () => { + + cy.visit(pd.path) + cy.get('@apiowner').then(({ product }: any) => { + pd.updateDatasetNameToCatelogue(product.name, product.environment.name) + }) + }) it('publish product to directory', () => { cy.visit(pd.path) cy.get('@apiowner').then(({ product }: any) => { @@ -74,16 +100,10 @@ describe('Create API Spec', () => { }) }) }) - it('update the Dataset in BC Data Catelogue to appear the API in the Directory', () => { - cy.visit(pd.path) - cy.get('@apiowner').then(({ product }: any) => { - pd.updateDatasetNameToCatelogue(product.name, product.environment.name) - }) - }) after(() => { cy.logout() - cy.clearLocalStorage({log:true}) + cy.clearLocalStorage({ log: true }) cy.deleteAllCookies() }) }) diff --git a/e2e/cypress/tests/01-api-key/04-approve-pending-rqst.spec.ts b/e2e/cypress/tests/01-api-key/04-approve-pending-rqst.spec.ts index f0ef53811..c01ee2e5a 100644 --- a/e2e/cypress/tests/01-api-key/04-approve-pending-rqst.spec.ts +++ b/e2e/cypress/tests/01-api-key/04-approve-pending-rqst.spec.ts @@ -1,7 +1,5 @@ -import ApiDirectoryPage from '../../pageObjects/apiDirectory' import ConsumersPage from '../../pageObjects/consumers' import LoginPage from '../../pageObjects/login' -import ApplicationPage from '../../pageObjects/applications' import HomePage from '../../pageObjects/home' describe('Approve Pending Request Spec', () => { @@ -20,7 +18,6 @@ describe('Approve Pending Request Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('access-manager').as('access-manager') - cy.fixture('developer').as('developer') cy.fixture('apiowner').as('apiowner') cy.fixture('state/store').as('store') cy.visit(login.path) @@ -28,28 +25,30 @@ describe('Approve Pending Request Spec', () => { it('Verify that API is not accessible with the generated API Key when the request is not approved', () => { cy.get('@apiowner').then(({ product }: any) => { - cy.makeKongRequest(product.environment.config.serviceName,'GET').then((response) => { + cy.makeKongRequest(product.environment.config.serviceName, 'GET').then((response) => { expect(response.status).to.be.equal(403) expect(response.body.message).to.be.contain('You cannot consume this service') }) }) }) - it('approves an access request', () => { + it('authenticates Mark (Access-Manager)', () => { cy.get('@access-manager').then(({ user, namespace }: any) => { - cy.login(user.credentials.username, user.credentials.password).then(() => { - cy.visit(consumers.path); - home.useNamespace(namespace); - cy.contains('Review').click() - cy.contains('Approve').click() - cy.contains('span','Complete', { timeout: 10000 }).should('be.visible'); - }) + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(namespace); }) }) + it('approves an access request', () => { + cy.visit(consumers.path); + cy.contains('Review').click() + cy.contains('Approve').click() + cy.contains('span', 'Complete', { timeout: 10000 }).should('be.visible'); + }) + it('Verify that API is accessible with the generated API Key', () => { cy.get('@apiowner').then(({ product }: any) => { - cy.makeKongRequest(product.environment.config.serviceName,'GET').then((response) => { + cy.makeKongRequest(product.environment.config.serviceName, 'GET').then((response) => { cy.log(response) expect(response.status).to.be.equal(200) }) @@ -76,7 +75,7 @@ describe('Turn off the Authentication', () => { it('Verify that API is not accessible with the generated API Key', () => { cy.get('@apiowner').then(({ product }: any) => { - cy.makeKongRequest(product.environment.config.serviceName,'GET').then((response) => { + cy.makeKongRequest(product.environment.config.serviceName, 'GET').then((response) => { expect(response.status).to.be.equal(403) expect(response.body.message).to.be.contain('You cannot consume this service') }) @@ -86,7 +85,7 @@ describe('Turn off the Authentication', () => { after(() => { consumers.turnOnACLSwitch(true) cy.logout() - cy.clearLocalStorage({log:true}) + cy.clearLocalStorage({ log: true }) cy.deleteAllCookies() }) }) \ No newline at end of file diff --git a/e2e/cypress/tests/01-api-key/05-apply-kong-api-only-consumer.ts b/e2e/cypress/tests/01-api-key/05-apply-kong-api-only-consumer.ts index b314eabb3..e86e04461 100644 --- a/e2e/cypress/tests/01-api-key/05-apply-kong-api-only-consumer.ts +++ b/e2e/cypress/tests/01-api-key/05-apply-kong-api-only-consumer.ts @@ -24,7 +24,7 @@ describe('Apply Kong API key only plugin', () => { it('Get the plugin ID of Key-auth plugin', () => { cy.makeKongGatewayRequest('plugins', '', 'GET').then((response) => { expect(response.status).to.be.equal(200) - pluginID = response.body.data[0].id + pluginID = _.get((_.filter(response.body.data,["name","key-auth"]))[0],'id') }) }) diff --git a/e2e/cypress/tests/03-access-permission/01-create-api.spec.ts b/e2e/cypress/tests/03-access-permission/01-create-api.spec.ts index 335b800a1..a2a9404bb 100644 --- a/e2e/cypress/tests/03-access-permission/01-create-api.spec.ts +++ b/e2e/cypress/tests/03-access-permission/01-create-api.spec.ts @@ -8,6 +8,8 @@ describe('Create API Spec', () => { const home = new HomePage() const sa = new ServiceAccountsPage() const pd = new Products() + var nameSpace: string + let userSession: string before(() => { cy.visit('/') @@ -19,6 +21,7 @@ describe('Create API Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('api').as('api') cy.visit(login.path) }) @@ -29,8 +32,14 @@ describe('Create API Spec', () => { }) it('creates and activates new namespace', () => { - cy.get('@apiowner').then(({ checkPermission }: any) => { - home.createNamespace(checkPermission.namespace) + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ checkPermission }: any) => { + nameSpace = checkPermission.namespace + home.createNamespace(checkPermission.namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) }) }) @@ -59,6 +68,24 @@ describe('Create API Spec', () => { }) }) + it('Associate Namespace to the organization Unit', () => { + cy.get('@api').then(({ organization }: any) => { + cy.setHeaders(organization.headers) + cy.setAuthorizationToken(userSession) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('update the Dataset in BC Data Catelogue to appear the API in the Directory', () => { + + cy.visit(pd.path) + cy.get('@apiowner').then(({ checkPermission }: any) => { + pd.updateDatasetNameToCatelogue(checkPermission.product.name, checkPermission.product.environment.name) + }) + }) + it('publish product to directory', () => { cy.visit(pd.path) cy.get('@apiowner').then(({ checkPermission }: any) => { @@ -78,13 +105,6 @@ describe('Create API Spec', () => { }) }) - it('update the Dataset in BC Data Catelogue to appear the API in the Directory', () => { - - cy.visit(pd.path) - cy.get('@apiowner').then(({ checkPermission }: any) => { - pd.updateDatasetNameToCatelogue(checkPermission.product.name, checkPermission.product.environment.name) - }) - }) after(() => { cy.logout() cy.clearLocalStorage({log:true}) diff --git a/e2e/cypress/tests/03-access-permission/03-rqst-access.spec.ts b/e2e/cypress/tests/03-access-permission/03-rqst-access.spec.ts index 35ab80fcd..8ace8e15d 100644 --- a/e2e/cypress/tests/03-access-permission/03-rqst-access.spec.ts +++ b/e2e/cypress/tests/03-access-permission/03-rqst-access.spec.ts @@ -40,6 +40,7 @@ describe('Request Access Spec', () => { apiDir.createAccessRequest(checkPermission.product, checkPermission.application, accessRequest) myAccessPage.clickOnGenerateSecretButton() cy.contains("API Key").should('be.visible') + myAccessPage.saveAPIKeyValue() }) }) diff --git a/e2e/cypress/tests/03-access-permission/06-credential-issuer.ts b/e2e/cypress/tests/03-access-permission/06-credential-issuer.ts index 6637dce4a..5114c8709 100644 --- a/e2e/cypress/tests/03-access-permission/06-credential-issuer.ts +++ b/e2e/cypress/tests/03-access-permission/06-credential-issuer.ts @@ -75,10 +75,6 @@ describe('Verify that Wendy is able to generate authorization profile', () => { }) }) - it('Verify that only "CredentialIssuer.Admin" permission is displayed in the profile', () => { - mp.checkScopeOfProfile("CredentialIssuer.Admin") - }) - it('Verify that only Authorization Profile option is displayed in Namespace page', () => { cy.visit(ns.path) ns.verifyThatOnlyAuthorizationProfileLinkIsExist() @@ -92,18 +88,10 @@ describe('Verify that Wendy is able to generate authorization profile', () => { }) }) - it('Verify that authorization profile for Kong API key is generated', () => { - cy.visit(authProfile.path) - cy.get('@credential-issuer').then(({ kongAPI }: any) => { - let ap = kongAPI.authProfile - authProfile.createAuthProfile(ap) - }) - }) - after(() => { cy.logout() cy.clearLocalStorage({ log: true }) cy.deleteAllCookies() - cy.resetCredential('Wendy') + // cy.resetCredential('Wendy') }) }) \ No newline at end of file diff --git a/e2e/cypress/tests/03-access-permission/07-namespace-view.ts b/e2e/cypress/tests/03-access-permission/07-namespace-view.ts index b8024aadd..74ab82771 100644 --- a/e2e/cypress/tests/03-access-permission/07-namespace-view.ts +++ b/e2e/cypress/tests/03-access-permission/07-namespace-view.ts @@ -77,10 +77,6 @@ describe('Verify that Mark is unable to create service account', () => { }) }) - it('Verify that only "Namespace.View" permission is displayed in the profile', () => { - mp.checkScopeOfProfile("Namespace.View") - }) - it('Navigate to Consumer Page to see the Approve Request option', () => { cy.visit(consumers.path) }) diff --git a/e2e/cypress/tests/04-client-credential-flow/01-client-cred-team-access.ts b/e2e/cypress/tests/04-client-credential-flow/01-client-cred-team-access.ts index f7fff5a56..089e51563 100644 --- a/e2e/cypress/tests/04-client-credential-flow/01-client-cred-team-access.ts +++ b/e2e/cypress/tests/04-client-credential-flow/01-client-cred-team-access.ts @@ -12,6 +12,7 @@ describe('Grant appropriate permissions to team members for client credential fl cy.visit('/') cy.deleteAllCookies() cy.reload() + cy.resetState() }) beforeEach(() => { diff --git a/e2e/cypress/tests/04-client-credential-flow/02-client-cred-create-api-prod-auth-pro.ts b/e2e/cypress/tests/04-client-credential-flow/02-client-cred-create-api-prod-auth-pro.ts index 71301a9ac..39573049a 100644 --- a/e2e/cypress/tests/04-client-credential-flow/02-client-cred-create-api-prod-auth-pro.ts +++ b/e2e/cypress/tests/04-client-credential-flow/02-client-cred-create-api-prod-auth-pro.ts @@ -12,6 +12,8 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t const sa = new ServiceAccountsPage() const pd = new Products() const authProfile = new AuthorizationProfile() + var nameSpace: string + let userSession: string before(() => { cy.visit('/') @@ -22,6 +24,7 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('api').as('api') cy.visit(login.path) }) it('Authenticates api owner', () => { @@ -30,8 +33,14 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t }) }) it('Activates namespace for client credential flow tests', () => { - cy.get('@apiowner').then(({ clientCredentials }: any) => { - home.useNamespace(clientCredentials.namespace) + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ clientCredentials }: any) => { + nameSpace = clientCredentials.namespace + home.useNamespace(clientCredentials.namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) }) }) it('Creates authorization profile for Client ID/Secret', () => { @@ -83,6 +92,25 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t ) }) }) + + it('Associate Namespace to the organization Unit', () => { + cy.get('@api').then(({ organization }: any) => { + cy.setHeaders(organization.headers) + cy.setAuthorizationToken(userSession) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('Update the Dataset in BC Data Catalogue to appear the API in the Directory', () => { + cy.visit(pd.path) + cy.get('@apiowner').then(({ clientCredentials }: any) => { + let product = clientCredentials.clientIdSecret.product + pd.updateDatasetNameToCatelogue(product.name, product.environment.name) + }) + }) + it('Adds environment with Client ID/Secret authenticator to product', () => { cy.visit(pd.path) cy.get('@apiowner').then(({ clientCredentials }: any) => { @@ -130,15 +158,8 @@ describe('Create API, Product, and Authorization Profiles; Apply Auth Profiles t }) }) }) - it('Update the Dataset in BC Data Catalogue to appear the API in the Directory', () => { - cy.visit(pd.path) - cy.get('@apiowner').then(({ clientCredentials }: any) => { - let product = clientCredentials.clientIdSecret.product - pd.updateDatasetNameToCatelogue(product.name, product.environment.name) - }) - }) + after(() => { - cy.logout() cy.clearLocalStorage({ log: true }) cy.deleteAllCookies() }) diff --git a/e2e/cypress/tests/05-clear-resources/01-create-api.spec.ts b/e2e/cypress/tests/05-clear-resources/01-create-api.spec.ts index 31fb18a22..554c934b2 100644 --- a/e2e/cypress/tests/05-clear-resources/01-create-api.spec.ts +++ b/e2e/cypress/tests/05-clear-resources/01-create-api.spec.ts @@ -8,6 +8,8 @@ describe('Create API Spec', () => { const home = new HomePage() const sa = new ServiceAccountsPage() const pd = new Products() + var nameSpace: string + let userSession: string before(() => { cy.visit('/') @@ -19,6 +21,7 @@ describe('Create API Spec', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') + cy.fixture('api').as('api') cy.visit(login.path) }) @@ -29,8 +32,14 @@ describe('Create API Spec', () => { }) it('creates and activates new namespace', () => { - cy.get('@apiowner').then(({ deleteResources }: any) => { - home.createNamespace(deleteResources.namespace) + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ deleteResources }: any) => { + nameSpace = deleteResources.namespace + home.createNamespace(deleteResources.namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) }) }) @@ -53,34 +62,47 @@ describe('Create API Spec', () => { }) it('creates as new product in the directory', () => { cy.visit(pd.path) - cy.get('@apiowner').then(({ product }: any) => { - pd.createNewProduct(product.name, product.environment.name) + cy.get('@apiowner').then(({ deleteResources }: any) => { + pd.createNewProduct(deleteResources.product.name, deleteResources.product.environment.name) + }) + }) + + it('Associate Namespace to the organization Unit', () => { + cy.get('@api').then(({ organization }: any) => { + cy.setHeaders(organization.headers) + cy.setAuthorizationToken(userSession) + cy.makeAPIRequest(organization.endPoint + '/' + organization.orgName + '/' + organization.orgExpectedList.name + '/namespaces/' + nameSpace, 'PUT').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('update the Dataset in BC Data Catelogue to appear the API in the Directory', () => { + + cy.visit(pd.path) + cy.get('@apiowner').then(({ deleteResources }: any) => { + pd.updateDatasetNameToCatelogue(deleteResources.product.name, deleteResources.product.environment.name) }) }) + it('publish product to directory', () => { cy.visit(pd.path) - cy.get('@apiowner').then(({ product }: any) => { - pd.editProductEnvironment(product.name, product.environment.name) - pd.editProductEnvironmentConfig(product.environment.config) + cy.get('@apiowner').then(({ deleteResources }: any) => { + pd.editProductEnvironment(deleteResources.product.name, deleteResources.product.environment.name) + pd.editProductEnvironmentConfig(deleteResources.product.environment.config) }) - pd.generateKongPluginConfig('service.yml') + pd.generateKongPluginConfig('service-clear-resources.yml') }) it('applies authorization plugin to service published to Kong Gateway', () => { cy.get('@apiowner').then(({ deleteResources }: any) => { - cy.publishApi('service-plugin.yml', deleteResources.namespace).then(() => { + cy.publishApi('service-clear-resources-plugin.yml', deleteResources.namespace).then(() => { cy.get('@publishAPIResponse').then((res: any) => { cy.log(JSON.stringify(res.body)) }) }) }) }) - it('update the Dataset in BC Data Catelogue to appear the API in the Directory', () => { - cy.visit(pd.path) - cy.get('@apiowner').then(({ product }: any) => { - pd.updateDatasetNameToCatelogue(product.name, product.environment.name) - }) - }) after(() => { cy.logout() cy.clearLocalStorage({log:true}) diff --git a/e2e/cypress/tests/05-clear-resources/02-delete-resources.ts b/e2e/cypress/tests/05-clear-resources/02-delete-resources.ts index 483979f35..97d9cadbf 100644 --- a/e2e/cypress/tests/05-clear-resources/02-delete-resources.ts +++ b/e2e/cypress/tests/05-clear-resources/02-delete-resources.ts @@ -19,7 +19,7 @@ describe('Delete created resources', () => { beforeEach(() => { cy.preserveCookies() cy.fixture('apiowner').as('apiowner') - // cy.visit(login.path) + cy.visit(login.path) }) it('authenticates Janis (api owner)', () => { @@ -31,27 +31,24 @@ describe('Delete created resources', () => { it('Navigates to Product page', () => { cy.visit(pd.path) - cy.wait(3000) }) it('Delete Product Environment', () => { - cy.get('@apiowner').then(({ product }: any) => { - // cy.visit(pd.path) - pd.deleteProductEnvironment(product.name, product.environment.name) + cy.visit(pd.path) + cy.get('@apiowner').then(({ deleteResources }: any) => { + pd.deleteProductEnvironment(deleteResources.product.name, deleteResources.product.environment.name) }) }) it('Delete the Product', () => { - cy.get('@apiowner').then(({ product }: any) => { - pd.deleteProduct(product.name) + cy.visit(pd.path) + cy.get('@apiowner').then(({ deleteResources }: any) => { + pd.deleteProduct(deleteResources.product.name) }) }) - it('Navigates to Service Account Page', () => { - cy.visit(sa.path) - }) - it('Delete Service Accounts', () => { + cy.visit(sa.path) sa.deleteAllServiceAccounts() }) diff --git a/e2e/cypress/tests/06-API Demo/01-organization.ts b/e2e/cypress/tests/06-API Demo/01-organization.ts deleted file mode 100644 index 62a53b87c..000000000 --- a/e2e/cypress/tests/06-API Demo/01-organization.ts +++ /dev/null @@ -1,29 +0,0 @@ -describe('Organization API Demo', () => { - - before(() => { - cy.visit('/') - }) - - beforeEach(() => { - cy.preserveCookies() - cy.fixture('api').as('api') - cy.request("ds/api/v2/organizations") - }) - - it('Prepare the Request Specification for the API', () => { - cy.get('@api').then((organization: any) => { - cy.setHeaders(organization.headers) - }) - }) - - it('Send the request and verify the responser', () => { - cy.makeAPIRequest('ds/api/v2/organizations','GET').then((response) => { - debugger - expect(response.status).to.be.equal(200) - expect(response.body[0].name).to.eq("ministry-of-health") - expect(response.body[0].title).to.eq("Ministry of Health") - expect(response.body[0]).has.property('title','Ministry of Health') - }) - }) - -}) \ No newline at end of file diff --git a/e2e/cypress/tests/06-aps-api/01-create-api.spec.ts b/e2e/cypress/tests/06-aps-api/01-create-api.spec.ts new file mode 100644 index 000000000..57d6e4b2c --- /dev/null +++ b/e2e/cypress/tests/06-aps-api/01-create-api.spec.ts @@ -0,0 +1,43 @@ +import HomePage from '../../pageObjects/home' +import LoginPage from '../../pageObjects/login' +import Products from '../../pageObjects/products' +import ServiceAccountsPage from '../../pageObjects/serviceAccounts' + +describe('Create API Spec', () => { + const login = new LoginPage() + const home = new HomePage() + const sa = new ServiceAccountsPage() + const pd = new Products() + + before(() => { + cy.visit('/') + cy.deleteAllCookies() + cy.reload() + cy.resetState() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('apiowner').as('apiowner') + cy.visit(login.path) + }) + + it('authenticates Janis (api owner)', () => { + cy.get('@apiowner').then(({ user }: any) => { + cy.login(user.credentials.username, user.credentials.password) + }) + }) + + it('creates and activates new namespace', () => { + cy.get('@apiowner').then(({ apiTest }: any) => { + home.createNamespace(apiTest.namespace) + }) + }) + + + after(() => { + cy.logout() + cy.clearLocalStorage({log:true}) + cy.deleteAllCookies() + }) +}) diff --git a/e2e/cypress/tests/06-aps-api/02-organization.ts b/e2e/cypress/tests/06-aps-api/02-organization.ts new file mode 100644 index 000000000..02960f8ed --- /dev/null +++ b/e2e/cypress/tests/06-aps-api/02-organization.ts @@ -0,0 +1,51 @@ +describe('API Tests for Organization Manage Control End points', () => { + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('api').as('api') + cy.request("ds/api/v2/organizations") + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ organization }: any) => { + cy.setHeaders(organization.headers) + }) + }) + + it('Get the resource and verify the Organization details in the response', () => { + cy.get('@api').then(({ organization }: any) => { + cy.makeAPIRequest(organization.endPoint, 'GET').then((response) => { + expect(response.status).to.be.equal(200) + expect(response.body[0].name).to.eq("ministry-of-health") + expect(response.body[0].title).to.eq("Ministry of Health") + expect(response.body[0]).has.property('title', 'Ministry of Health') + }) + }) + }) +}) + +describe('Verify /Organization/{Org} end point', () => { + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('api').as('api') + cy.request("ds/api/v2/organizations") + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ organization }: any) => { + cy.setHeaders(organization.headers) + }) + }) + + it('Get the resource and verify the org Names in the response', () => { + cy.get('@api').then(({ organization }: any) => { + cy.makeAPIRequest(organization.endPoint +'/'+ organization.orgName, 'GET').then((response) => { + debugger + expect(response.status).to.be.equal(200) + var expectedResult = organization.orgExpectedList + assert.isTrue(_.isEqual(response.body.orgUnits[0], organization.orgExpectedList)) + }) + }) + }) +}) \ No newline at end of file diff --git a/e2e/cypress/tests/06-aps-api/03-documentation.ts b/e2e/cypress/tests/06-aps-api/03-documentation.ts new file mode 100644 index 000000000..b2c930188 --- /dev/null +++ b/e2e/cypress/tests/06-aps-api/03-documentation.ts @@ -0,0 +1,152 @@ +import HomePage from "../../pageObjects/home" +import LoginPage from "../../pageObjects/login" +let userSession: string +let slugValue: string + +describe('Get the user session token', () => { + + const login = new LoginPage() + const home = new HomePage() + + before(() => { + cy.visit('/') + cy.deleteAllCookies() + cy.reload() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('apiowner').as('apiowner') + cy.visit(login.path) + }) + + it('authenticates Janis (api owner) to get the user session token', () => { + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ user, apiTest }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(apiTest.namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) + }) + }) + +}) + +describe('API Tests for Updating documentation', () => { + + const login = new LoginPage() + const home = new HomePage() + + beforeEach(() => { + cy.fixture('api').as('api') + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ documentation }: any) => { + cy.setHeaders(documentation.headers) + cy.setAuthorizationToken(userSession) + cy.setRequestBody(documentation.body) + }) + }) + + it('Put the resource and verify the success code in the response', () => { + cy.get('@api').then(({ documentation }: any) => { + cy.makeAPIRequest(documentation.endPoint, 'PUT').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) +}) + +describe('API Tests for Fetching documentation', () => { + + const login = new LoginPage() + const home = new HomePage() + var response: any + + beforeEach(() => { + cy.fixture('api').as('api') + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ documentation }: any) => { + cy.setHeaders(documentation.headers) + cy.setAuthorizationToken(userSession) + cy.setRequestBody(documentation.body) + }) + }) + + it('Get the resource and verify the success code in the response', () => { + cy.get('@api').then(({ documentation }: any) => { + debugger + cy.makeAPIRequest(documentation.endPoint, 'GET').then((res) => { + debugger + expect(res.status).to.be.equal(200) + slugValue = res.body[0].slug + response = res.body[0] + }) + }) + }) + + + it('Compare the values in response against the values passed in the request', () => { + cy.get('@api').then(({documentation}:any) => { + cy.compareJSONObjects(response, documentation.body) + }) + }) +}) + +describe('API Tests for Deleting documentation', () => { + + const login = new LoginPage() + const home = new HomePage() + + beforeEach(() => { + cy.fixture('api').as('api') + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ documentation }: any) => { + cy.setHeaders(documentation.headers) + cy.setAuthorizationToken(userSession) + cy.setRequestBody(documentation.body) + }) + }) + + it('Delete the documentation', () => { + cy.get('@api').then(({ documentation }: any) => { + cy.makeAPIRequest(documentation.endPoint + '/' + slugValue, 'DELETE').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) +}) + +describe('API Tests for to verify no value in Get call after deleting document content', () => { + + const login = new LoginPage() + const home = new HomePage() + + beforeEach(() => { + cy.fixture('api').as('api') + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ documentation }: any) => { + cy.setHeaders(documentation.headers) + cy.setAuthorizationToken(userSession) + cy.setRequestBody(documentation.body) + }) + }) + + it('Delete the documentation', () => { + cy.get('@api').then(({ documentation }: any) => { + cy.makeAPIRequest(documentation.endPoint, 'GET').then((response) => { + expect(response.status).to.be.equal(200) + expect(response.body).to.be.empty + }) + }) + }) +}) diff --git a/e2e/cypress/tests/06-aps-api/04-authorizationProfiles.ts b/e2e/cypress/tests/06-aps-api/04-authorizationProfiles.ts new file mode 100644 index 000000000..fd6330982 --- /dev/null +++ b/e2e/cypress/tests/06-aps-api/04-authorizationProfiles.ts @@ -0,0 +1,98 @@ +import HomePage from "../../pageObjects/home" +import LoginPage from "../../pageObjects/login" +let userSession: string +let testData = require("../../fixtures/test_data/authorizationProfile.json") + +describe('Get the user session token', () => { + + const login = new LoginPage() + const home = new HomePage() + + before(() => { + cy.visit('/') + cy.deleteAllCookies() + cy.reload() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('apiowner').as('apiowner') + cy.visit(login.path) + }) + + it('authenticates Janis (api owner) to get the user session token', () => { + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ user, apiTest }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(apiTest.namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) + }) + }) + +}) + +testData.forEach((testCase: any) => { + describe('API Tests for Authorization Profiles', () => { + + var response: any + var actualResponse: any = {} + var expectedResponse: any = {} + + beforeEach(() => { + cy.fixture('api').as('api') + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ authorizationProfiles }: any) => { + cy.setHeaders(authorizationProfiles.headers) + cy.setAuthorizationToken(userSession) + cy.setRequestBody(testCase.body) + }) + }) + + it('Put the resource and verify the success code in the response', () => { + cy.get('@api').then(({ authorizationProfiles }: any) => { + cy.makeAPIRequest(authorizationProfiles.endPoint, 'PUT').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('Get the resource and verify the success code in the response', () => { + cy.get('@api').then(({ authorizationProfiles }: any) => { + cy.makeAPIRequest(authorizationProfiles.endPoint, 'GET').then((res) => { + expect(res.status).to.be.equal(200) + response = res.body + }) + }) + }) + + it('Compare the values in response against the values passed in the request', () => { + cy.get('@api').then(({ authorizationProfiles }: any) => { + actualResponse = response + expectedResponse = testCase.body + cy.compareJSONObjects(actualResponse, expectedResponse, true) + }) + }) + + it('Delete the authorization profile', () => { + cy.get('@api').then(({ authorizationProfiles }: any) => { + cy.makeAPIRequest(authorizationProfiles.endPoint + '/' + testCase.body.name, 'DELETE').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('Verify that the authorization profile is deleted', () => { + cy.get('@api').then(({ authorizationProfiles }: any) => { + cy.makeAPIRequest(authorizationProfiles.endPoint, 'GET').then((response) => { + expect(response.status).to.be.equal(200) + expect(response.body.length).to.be.equal(0) + }) + }) + }) + }) +}) \ No newline at end of file diff --git a/e2e/cypress/tests/06-aps-api/05-products.ts b/e2e/cypress/tests/06-aps-api/05-products.ts new file mode 100644 index 000000000..e767f6fe7 --- /dev/null +++ b/e2e/cypress/tests/06-aps-api/05-products.ts @@ -0,0 +1,174 @@ +import HomePage from "../../pageObjects/home" +import LoginPage from "../../pageObjects/login" +import Products from "../../pageObjects/products" +let userSession: string +let productID: string +let envID: string + +describe('Get the user session token to check ', () => { + + const login = new LoginPage() + const home = new HomePage() + + before(() => { + cy.visit('/') + cy.deleteAllCookies() + cy.reload() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('apiowner').as('apiowner') + cy.visit(login.path) + }) + + it('authenticates Janis (api owner) to get the user session token', () => { + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ user, apiTest }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(apiTest.namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) + }) + }) + +}) + +describe('API Tests for Updating Products', () => { + + const login = new LoginPage() + const home = new HomePage() + var response: any + + beforeEach(() => { + cy.fixture('api').as('api') + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ products }: any) => { + cy.setHeaders(products.headers) + cy.setAuthorizationToken(userSession) + cy.setRequestBody(products.body) + }) + }) + + it('Put the resource and verify the success code in the response', () => { + cy.get('@api').then(({ products }: any) => { + cy.makeAPIRequest(products.endPoint, 'PUT').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('Get the resource and verify the success code and product name in the response', () => { + cy.get('@api').then(({ products }: any) => { + cy.makeAPIRequest(products.endPoint, 'GET').then((res) => { + expect(res.status).to.be.equal(200) + expect(res.body.length).to.be.equal(1) + response = res.body + productID = res.body[0].appId + envID = res.body[0].environments[0].appId + }) + }) + }) + it('Compare the values in response against the values passed in the request', () => { + cy.get('@api').then(({ products }: any) => { + cy.compareJSONObjects(response, products.body, true) + }) + }) +}) + +describe('Verify that created Product is displayed in UI', () => { + + const login = new LoginPage() + const home = new HomePage() + const pd = new Products() + + before(() => { + cy.visit('/') + cy.deleteAllCookies() + cy.reload() + }) + + beforeEach(() => { + cy.preserveCookies() + cy.fixture('apiowner').as('apiowner') + cy.fixture('api').as('api') + cy.visit(login.path) + }) + + it('authenticates Janis (api owner) to get the user session token', () => { + cy.getUserSession().then(() => { + cy.get('@apiowner').then(({ user, apiTest }: any) => { + cy.login(user.credentials.username, user.credentials.password) + home.useNamespace(apiTest.namespace) + cy.get('@login').then(function (xhr: any) { + userSession = xhr.response.headers['x-auth-request-access-token'] + }) + }) + }) + }) + + it('Verify that the product is visible in Manage Product Page', () => { + cy.visit(pd.path) + cy.get('@api').then(({ products }: any) => { + pd.verifyProductIsVisible(products.body.name) + }) + }) +}) + +describe('API Tests for Delete Products', () => { + + const login = new LoginPage() + const home = new HomePage() + var response: any + + beforeEach(() => { + cy.fixture('api').as('api') + }) + + it('Prepare the Request Specification for the API', () => { + cy.get('@api').then(({ products }: any) => { + cy.setHeaders(products.headers) + cy.setAuthorizationToken(userSession) + }) + }) + + it('Delete the product environment and verify the success code in the response', () => { + cy.get('@api').then(({ products }: any) => { + cy.makeAPIRequest(products.deleteEnvironmentEndPoint+'/'+envID, 'Delete').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('Get the resource and verify that product environment is deleted', () => { + cy.get('@api').then(({ products }: any) => { + cy.makeAPIRequest(products.endPoint, 'GET').then((res) => { + expect(res.status).to.be.equal(200) + expect(res.body.length).to.be.equal(1) + expect(res.body[0].environments).to.be.empty + }) + }) + }) + + it('Delete the product and verify the success code in the response', () => { + cy.get('@api').then(({ products }: any) => { + cy.makeAPIRequest(products.endPoint+'/'+productID, 'Delete').then((response) => { + expect(response.status).to.be.equal(200) + }) + }) + }) + + it('Get the resource and verify that product is deleted', () => { + cy.get('@api').then(({ products }: any) => { + cy.makeAPIRequest(products.endPoint, 'GET').then((res) => { + expect(res.status).to.be.equal(200) + expect(res.body.length).to.be.equal(0) + }) + }) + }) +}) + diff --git a/local/cypress-jwks-url/keys.json b/local/cypress-jwks-url/keys.json index 75eef08ae..818cc4456 100644 --- a/local/cypress-jwks-url/keys.json +++ b/local/cypress-jwks-url/keys.json @@ -1 +1 @@ -{"keys":[{"kty":"RSA","kid":"3Fyo7y3XmzFd8NOD6lRgQ4KeQrhKP7TSbuyNgn-25gI","use":"sig","alg":"RS256","e":"AQAB","n":"xxuN5RBc6M7tJIPlDSwlTm3chW74BrEJjSfI6mYETXkTD6W10DCU_vMEoYoU-VWdvzD2sakVcVbGGzXCqLGEi_GgpWvPdW4yLdPIUxfboni2Q7tb-8u0ao8X0NZkSv3rwJ1hKjd5VLmxV0t2pfwGUh9t6xnWFeWXHGjwWJoGljeoSg9e5MfBRWMfyMTiZUF7J5lbOzmYEpHp4Xg3W57ijA7sQ2b1a_R-MV1HMP065-i38hcq85TFxvfxy_eNWd1L0KzvSJkW3q1JG6RHCv84E9YBnolBewDMm6hd9zsh2YokDxrfUGsv4JZN48y436jTJMtMGNLm2B7CKyOUoIZPTw"}]} \ No newline at end of file +{"keys":[{"kty":"RSA","kid":"Wpr43RNueZcp_RlUt98U-jE7JlyefAR2xthpuS3_gmA","use":"sig","alg":"RS256","e":"AQAB","n":"uunezj32jDwpewrfebvvHHRhedEHwyneu6TVK57199zEjAcR2SvRXOvr3reBMmvxvbWpAPUPCX5CRraog0hrMxY_Htw5SPeLF_Vm8p2HfLLIiuL3i7VgE-T3POV28de13JGwxbYyp-2RNGVMSMH1A01C9UFNQNe24-AZwvniVAN9pLmIhtaMroI2DUn7bIWw8B1HKYDEK5ssrp5gG9Q_QkMeLgILgoNhw3Ip5WrxC3h5jh8Fx3abU9GYJ4e1njsnKSyMHEPDRebCPGOHPHxKA5UPuYDJhJzQiX0UYRe1lNyA5qSXYaWU8EebpawTbrJ5LXNNuAGHaF-yiZtVLcC1jQ"}]} \ No newline at end of file diff --git a/local/feeder-init/cc-dataset-gwa.yaml b/local/feeder-init/cc-dataset-gwa.yaml index f77061eda..8150397d5 100644 --- a/local/feeder-init/cc-dataset-gwa.yaml +++ b/local/feeder-init/cc-dataset-gwa.yaml @@ -1,11 +1,13 @@ entity: Dataset record: - id: "B234567890000000" - name: "client-credentials-test-product" - title: "Client Credentials Test Product" + id: "B234567890000027" + name: "delete-auto-test-product" + title: "Delete-Auto Test Product" notes: "API Gateway Services provides a way to configure services on the API Gateway, manage access to APIs and get insight into the use of them." tags: [ 'gateway', 'kong', 'openapi' ] sector: "Service" license_title: "Access Only" view_audience: "Government" security_class: "LOW-PUBLIC" + org: "7a66db63-26f4-4052-9cd5-3272b63910f8" + sub_org: "319b3297-846d-4b97-8095-ceb3ec505fb8" diff --git a/local/feeder-init/cc-product-initializer.yaml b/local/feeder-init/cc-product-initializer.yaml index dd96dbdac..abec59bf8 100644 --- a/local/feeder-init/cc-product-initializer.yaml +++ b/local/feeder-init/cc-product-initializer.yaml @@ -2,6 +2,7 @@ kind: Product appId: 2B04C28E08AW name: Client Credentials Test Product dataset: client-credentials-test-product +namespace: ccplatform environments: - id: 2G8DB030 name: dev @@ -10,4 +11,4 @@ environments: legal: terms-of-use-for-api-gateway-1 flow: kong-api-key-acl additionalDetailsToRequest: Please provide a bit more of this - services: [] \ No newline at end of file + services: [] diff --git a/local/feeder-init/cr-dataset-gwa.yaml b/local/feeder-init/cr-dataset-gwa.yaml new file mode 100644 index 000000000..0dbf42027 --- /dev/null +++ b/local/feeder-init/cr-dataset-gwa.yaml @@ -0,0 +1,13 @@ +entity: Dataset +record: + id: "B234567890000000" + name: "client-credentials-test-product" + title: "Client Credentials Test Product" + notes: "API Gateway Services provides a way to configure services on the API Gateway, manage access to APIs and get insight into the use of them." + tags: [ 'gateway', 'kong', 'openapi' ] + sector: "Service" + license_title: "Access Only" + view_audience: "Government" + security_class: "LOW-PUBLIC" + org: "7a66db63-26f4-4052-9cd5-3272b63910f8" + sub_org: "319b3297-846d-4b97-8095-ceb3ec505fb8" diff --git a/local/feeder-init/cr-product-initializer.yaml b/local/feeder-init/cr-product-initializer.yaml new file mode 100644 index 000000000..5f43e70d4 --- /dev/null +++ b/local/feeder-init/cr-product-initializer.yaml @@ -0,0 +1,14 @@ +kind: Product +appId: 2B04C28E08AW +name: Delete-Auto Test Product +dataset: delete-auto-test-product +namespace: deleteplatform +environments: + - id: 2G8DB030 + name: dev + active: true + approval: true + legal: terms-of-use-for-api-gateway-1 + flow: kong-api-key-acl + additionalDetailsToRequest: Please provide a bit more of this + services: [] diff --git a/local/feeder-init/dataset-gwa.yaml b/local/feeder-init/dataset-gwa.yaml index 8eba4dfda..b9f971e41 100644 --- a/local/feeder-init/dataset-gwa.yaml +++ b/local/feeder-init/dataset-gwa.yaml @@ -1,6 +1,6 @@ entity: Dataset record: - id: "A123456789000000" + id: "A12345678900000175161673" name: "auto-test-product" title: "Auto Test Product" notes: "API Gateway Services provides a way to configure services on the API Gateway, manage access to APIs and get insight into the use of them." @@ -9,3 +9,5 @@ record: license_title: "Access Only" view_audience: "Government" security_class: "LOW-PUBLIC" + org: "7a66db63-26f4-4052-9cd5-3272b63910f8" + sub_org: "319b3297-846d-4b97-8095-ceb3ec505fb8" diff --git a/local/feeder-init/init.sh b/local/feeder-init/init.sh index 5a08ab047..f1d568108 100755 --- a/local/feeder-init/init.sh +++ b/local/feeder-init/init.sh @@ -17,8 +17,11 @@ while true; do curl http://feeder.localtest.me:6000/push -F yaml=@organization-unit.yaml curl http://feeder.localtest.me:6000/push -F yaml=@dataset-gwa.yaml curl http://feeder.localtest.me:6000/push -F yaml=@product-initializer.yaml + curl http://feeder.localtest.me:6000/push -F yaml=@product-initializer-permission.yaml curl http://feeder.localtest.me:6000/push -F yaml=@cc-dataset-gwa.yaml curl http://feeder.localtest.me:6000/push -F yaml=@cc-product-initializer.yaml + curl http://feeder.localtest.me:6000/push -F yaml=@cr-dataset-gwa.yaml + curl http://feeder.localtest.me:6000/push -F yaml=@cr-product-initializer.yaml curl http://feeder.localtest.me:6000/push -F yaml=@permission-dataset-gwa.yaml break diff --git a/local/feeder-init/organization-unit.yaml b/local/feeder-init/organization-unit.yaml index d7e176570..7ce1cf097 100644 --- a/local/feeder-init/organization-unit.yaml +++ b/local/feeder-init/organization-unit.yaml @@ -9,6 +9,7 @@ record: description: 'The Ministry of Health has overall responsibility for ensuring that quality, appropriate, cost effective and timely health services are available for all British Columbians.' extSource: '' extRecordHash: '' + namespace: newplatform orgUnits: - id: 319b3297-846d-4b97-8095-ceb3ec505fb8 name: planning-and-innovation-division diff --git a/local/feeder-init/permission-dataset-gwa.yaml b/local/feeder-init/permission-dataset-gwa.yaml index 74c178de2..37b6d4405 100644 --- a/local/feeder-init/permission-dataset-gwa.yaml +++ b/local/feeder-init/permission-dataset-gwa.yaml @@ -9,3 +9,5 @@ record: license_title: "Access Only" view_audience: "Government" security_class: "LOW-PUBLIC" + org: "7a66db63-26f4-4052-9cd5-3272b63910f8" + sub_org: "319b3297-846d-4b97-8095-ceb3ec505fb8" diff --git a/local/feeder-init/platform-authz-profile.yaml b/local/feeder-init/platform-authz-profile.yaml index d0b63af8e..505d63084 100644 --- a/local/feeder-init/platform-authz-profile.yaml +++ b/local/feeder-init/platform-authz-profile.yaml @@ -1,7 +1,7 @@ entity: CredentialIssuer record: id: 'Gateway Services Resource Server' - namespace: platform + namespace: newplatform description: 'Authorization Profile for protecting the Gateway Services API' flow: client-credentials mode: auto @@ -17,6 +17,7 @@ record: - Access.Manage - GatewayConfig.Publish - Content.Publish + - Namespace.Assign owner: janis@idir environmentDetails: - environment: prod diff --git a/local/feeder-init/platform-gwa-api.yaml b/local/feeder-init/platform-gwa-api.yaml index 94da52ba7..499979bef 100644 --- a/local/feeder-init/platform-gwa-api.yaml +++ b/local/feeder-init/platform-gwa-api.yaml @@ -2,7 +2,7 @@ entity: Product record: appId: 748D98F1F56C name: Gateway Services API - namespace: platform + namespace: newplatform environments: - appId: E0000000 name: prod diff --git a/local/feeder-init/product-initializer-permission.yaml b/local/feeder-init/product-initializer-permission.yaml new file mode 100644 index 000000000..298e116f1 --- /dev/null +++ b/local/feeder-init/product-initializer-permission.yaml @@ -0,0 +1,14 @@ +kind: Product +appId: 2B04C28E08AT +name: New-Auto Test Product +dataset: new-auto-test-product +namespace: permission +environments: + - id: 1F7CA929 + name: dev + active: true + approval: true + legal: terms-of-use-for-api-gateway-1 + flow: kong-api-key-acl + additionalDetailsToRequest: Please provide a bit more of this + services: [] diff --git a/local/feeder-init/product-initializer.yaml b/local/feeder-init/product-initializer.yaml index 185d4f761..e477d12f9 100644 --- a/local/feeder-init/product-initializer.yaml +++ b/local/feeder-init/product-initializer.yaml @@ -2,6 +2,7 @@ kind: Product appId: 2B04C28E08AW name: Auto Test Product dataset: auto-test-product +namespace: newplatform environments: - id: 1F7CA929 name: dev diff --git a/local/keycloak/master-realm.json b/local/keycloak/master-realm.json index 4e6b94854..4fd190a55 100644 --- a/local/keycloak/master-realm.json +++ b/local/keycloak/master-realm.json @@ -332,7 +332,9 @@ "containerId" : "62f2227e-e1ac-4c74-b278-1eab7f7664ae", "attributes" : { } } ], + "sa-platform-e0000000-fa46551361b4" : [ ], "security-admin-console" : [ ], + "sa-platform1-e0000000-5be82156d61f" : [ ], "admin-cli" : [ ], "account-console" : [ ], "broker" : [ { @@ -576,14 +578,49 @@ "realmRoles" : [ ], "clientRoles" : { }, "subGroups" : [ { - "id" : "69f338a9-a264-4a22-ad97-a99c45e3165d", + "id" : "5c875007-2e0e-4c01-8418-143e2a420722", "name" : "platform", "path" : "/ns/platform", - "attributes" : { }, + "attributes" : { + "org-unit" : [ "planning-and-innovation-division" ], + "org" : [ "ministry-of-health" ] + }, "realmRoles" : [ ], "clientRoles" : { }, "subGroups" : [ ] } ] + }, { + "id" : "6a0b857d-3951-444d-823e-193bf9f12f67", + "name" : "organization-admin", + "path" : "/organization-admin", + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { }, + "subGroups" : [ { + "id" : "17434902-eda6-4d6a-a366-2dbfdbf3866c", + "name" : "ca.bc.gov", + "path" : "/organization-admin/ca.bc.gov", + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { }, + "subGroups" : [ { + "id" : "711b0078-fb8c-4ce9-aa50-06a690360508", + "name" : "ministry-of-health", + "path" : "/organization-admin/ca.bc.gov/ministry-of-health", + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { }, + "subGroups" : [ { + "id" : "614278c5-b60c-4101-9986-3d031720dec0", + "name" : "planning-and-innovation-division", + "path" : "/organization-admin/ca.bc.gov/ministry-of-health/planning-and-innovation-division", + "attributes" : { }, + "realmRoles" : [ ], + "clientRoles" : { }, + "subGroups" : [ ] + } ] + } ] + } ] } ], "defaultRoles" : [ "uma_authorization", "offline_access" ], "requiredCredentials" : [ "password" ], @@ -683,7 +720,7 @@ } ], "disableableCredentialTypes" : [ ], "requiredActions" : [ ], - "realmRoles" : [ "uma_authorization", "offline_access", "api-owner" ], + "realmRoles" : [ "uma_authorization", "aps-admin", "offline_access", "api-owner" ], "clientRoles" : { "account" : [ "manage-account", "view-profile" ] }, @@ -789,7 +826,41 @@ "realmRoles" : [ "uma_authorization", "offline_access" ], "clientRoles" : { "gwa-api" : [ "uma_protection" ], - "master-realm" : [ "manage-clients", "view-users", "create-client", "manage-users" ], + "master-realm" : [ "manage-clients", "view-users", "create-client", "manage-users", "manage-authorization" ], + "account" : [ "manage-account", "view-profile" ] + }, + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "c795236e-fb09-4b4e-93e0-8bffb7dda7b3", + "createdTimestamp" : 1650644631111, + "username" : "service-account-sa-platform1-e0000000-5be82156d61f", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "serviceAccountClientId" : "sa-platform1-e0000000-5be82156d61f", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "uma_authorization", "offline_access" ], + "clientRoles" : { + "account" : [ "manage-account", "view-profile" ] + }, + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "54adc719-3687-4886-9a7c-18c719a31014", + "createdTimestamp" : 1651510581479, + "username" : "service-account-sa-platform-e0000000-fa46551361b4", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "serviceAccountClientId" : "sa-platform-e0000000-fa46551361b4", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "uma_authorization", "offline_access" ], + "clientRoles" : { "account" : [ "manage-account", "view-profile" ] }, "notBefore" : 0, @@ -1279,6 +1350,105 @@ "attributes" : { }, "_id" : "054b9d22-ce05-4b1c-86bf-424eb7f3ca13", "uris" : [ "/*" ] + }, { + "name" : "platform1", + "type" : "namespace", + "ownerManagedAccess" : true, + "attributes" : { }, + "_id" : "fa9f93b8-b1c1-45ab-ad65-672befbdaedc", + "uris" : [ ], + "scopes" : [ { + "name" : "GatewayConfig.Publish" + }, { + "name" : "Namespace.Manage" + }, { + "name" : "Access.Manage" + }, { + "name" : "Content.Publish" + }, { + "name" : "Namespace.View" + }, { + "name" : "CredentialIssuer.Admin" + } ] + }, { + "name" : "platform2", + "type" : "namespace", + "ownerManagedAccess" : true, + "attributes" : { }, + "_id" : "c6ad734c-6d8f-4b90-bcf9-cb9c19eadc22", + "uris" : [ ], + "scopes" : [ { + "name" : "GatewayConfig.Publish" + }, { + "name" : "Namespace.Manage" + }, { + "name" : "Access.Manage" + }, { + "name" : "Content.Publish" + }, { + "name" : "Namespace.View" + }, { + "name" : "CredentialIssuer.Admin" + } ] + }, { + "name" : "platform", + "type" : "namespace", + "ownerManagedAccess" : true, + "attributes" : { }, + "_id" : "501a70b7-546a-43f3-8992-a4c170f0bab7", + "uris" : [ ], + "scopes" : [ { + "name" : "GatewayConfig.Publish" + }, { + "name" : "Namespace.Manage" + }, { + "name" : "Access.Manage" + }, { + "name" : "Content.Publish" + }, { + "name" : "Namespace.View" + }, { + "name" : "CredentialIssuer.Admin" + } ] + }, { + "name" : "org/ca.bc.gov", + "type" : "organization", + "ownerManagedAccess" : true, + "displayName" : "org/ca.bc.gov", + "attributes" : { }, + "_id" : "228c26be-3ef4-43d6-92ec-8441ebf5887c", + "uris" : [ ], + "scopes" : [ { + "name" : "GroupAccess.Manage" + } ] + }, { + "name" : "org/ministry-of-health", + "type" : "organization", + "ownerManagedAccess" : true, + "attributes" : { }, + "_id" : "2367ab08-6c6e-42e4-b1db-3a86de3a028d", + "uris" : [ ], + "scopes" : [ { + "name" : "GroupAccess.Manage" + }, { + "name" : "Dataset.Manage" + }, { + "name" : "Namespace.Assign" + } ] + }, { + "name" : "org/planning-and-innovation-division", + "type" : "organization", + "ownerManagedAccess" : true, + "attributes" : { }, + "_id" : "032644b7-a3a0-489e-bda3-193bd14d861a", + "uris" : [ ], + "scopes" : [ { + "name" : "GroupAccess.Manage" + }, { + "name" : "Dataset.Manage" + }, { + "name" : "Namespace.Assign" + } ] } ], "policies" : [ { "id" : "35dcd837-d215-4036-84fe-452605b0a065", @@ -1290,6 +1460,45 @@ "config" : { "code" : "// by default, grants any permission associated with this policy\n$evaluation.grant();\n" } + }, { + "id" : "ca06ef6c-d7f8-42c9-b0d6-0c9be85c1cc1", + "name" : "janis", + "type" : "user", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "users" : "[\"janis@idir\"]" + } + }, { + "id" : "c3848ff4-76b0-4f2d-afe9-7dfd77467fcb", + "name" : "group-organization-admin-ca.bc.gov-policy", + "description" : "Group '/organization-admin' / 'ca.bc.gov' Policy", + "type" : "group", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "groups" : "[{\"path\":\"/organization-admin\",\"extendChildren\":false},{\"path\":\"/organization-admin/ca.bc.gov\",\"extendChildren\":false}]" + } + }, { + "id" : "6f49c571-fbbb-4f86-a72a-c1591a446bb3", + "name" : "group-organization-admin-ca.bc.gov-ministry-of-health-policy", + "description" : "Group '/organization-admin/ca.bc.gov' / 'ministry-of-health' Policy", + "type" : "group", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "groups" : "[{\"path\":\"/organization-admin\",\"extendChildren\":false},{\"path\":\"/organization-admin/ca.bc.gov/ministry-of-health\",\"extendChildren\":false},{\"path\":\"/organization-admin/ca.bc.gov\",\"extendChildren\":false}]" + } + }, { + "id" : "99fdf1a9-d5ec-48c7-a2cd-1ddeb130b058", + "name" : "group-organization-admin-ca.bc.gov-ministry-of-health-planning-and-innovation-division-policy", + "description" : "Group '/organization-admin/ca.bc.gov/ministry-of-health' / 'planning-and-innovation-division' Policy", + "type" : "group", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "groups" : "[{\"path\":\"/organization-admin/ca.bc.gov/ministry-of-health\",\"extendChildren\":false},{\"path\":\"/organization-admin/ca.bc.gov/ministry-of-health/planning-and-innovation-division\",\"extendChildren\":false},{\"path\":\"/organization-admin\",\"extendChildren\":false},{\"path\":\"/organization-admin/ca.bc.gov\",\"extendChildren\":false}]" + } }, { "id" : "31be7436-e9d8-42a3-b42e-69a1869a7eea", "name" : "Default Permission", @@ -1301,6 +1510,60 @@ "defaultResourceType" : "urn:gwa-api:resources:default", "applyPolicies" : "[\"Default Policy\"]" } + }, { + "id" : "65f0c0a4-e2ac-4364-800d-ebd4e11ce393", + "name" : "janis full access", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "scopes" : "[\"GroupAccess.Manage\",\"Namespace.Assign\"]", + "applyPolicies" : "[\"janis\"]" + } + }, { + "id" : "854f0dfe-952d-48f8-9d53-d49b0b4ed122", + "name" : "Access to 'org/ca.bc.gov' services for role organization-admin", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "resources" : "[\"org/ca.bc.gov\"]", + "scopes" : "[\"GroupAccess.Manage\"]", + "applyPolicies" : "[\"group-organization-admin-ca.bc.gov-policy\"]" + } + }, { + "id" : "3f934d3f-d231-48fc-9f5a-924da7808989", + "name" : "Access to 'org/ministry-of-health' services for role organization-admin", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "resources" : "[\"org/ministry-of-health\"]", + "scopes" : "[\"Dataset.Manage\",\"GroupAccess.Manage\",\"Namespace.Assign\"]", + "applyPolicies" : "[\"group-organization-admin-ca.bc.gov-ministry-of-health-policy\"]" + } + }, { + "id" : "56a4857d-a0be-472c-85d0-2dca93a1fdac", + "name" : "Access to 'org/planning-and-innovation-division' services for role organization-admin", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "resources" : "[\"org/planning-and-innovation-division\"]", + "scopes" : "[\"Dataset.Manage\",\"GroupAccess.Manage\",\"Namespace.Assign\"]", + "applyPolicies" : "[\"group-organization-admin-ca.bc.gov-ministry-of-health-planning-and-innovation-division-policy\"]" + } + }, { + "id" : "f2e764aa-c355-4e81-a5e6-e76ffb86041f", + "name" : "Access to 'platform' services for role organization-admin", + "type" : "scope", + "logic" : "POSITIVE", + "decisionStrategy" : "UNANIMOUS", + "config" : { + "resources" : "[\"platform\"]", + "scopes" : "[\"Namespace.View\"]", + "applyPolicies" : "[\"group-organization-admin-ca.bc.gov-ministry-of-health-planning-and-innovation-division-policy\"]" + } } ], "scopes" : [ { "id" : "6871ce8b-5d5f-455b-86ff-7cf5940930eb", @@ -1320,6 +1583,17 @@ }, { "id" : "dfc132ca-aa87-40b5-bc33-3e972a88f638", "name" : "CredentialIssuer.Admin" + }, { + "id" : "95893c25-6b83-4e59-9518-a25568d95542", + "name" : "GroupAccess.Manage", + "iconUri" : "", + "displayName" : "GroupAccess.Manage" + }, { + "id" : "b0b007b1-1ecb-4b3f-9f0c-41b3fa34754c", + "name" : "Dataset.Manage" + }, { + "id" : "f3bf8d43-54a4-4594-aeea-f61b99411f92", + "name" : "Namespace.Assign" } ], "decisionStrategy" : "AFFIRMATIVE" } @@ -1350,6 +1624,175 @@ "nodeReRegistrationTimeout" : 0, "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "4bbc355d-7a87-4476-9593-7f9359dc8859", + "clientId" : "sa-platform1-e0000000-5be82156d61f", + "name" : "", + "description" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "c01a7839-2679-4cdd-96c4-173223b49ee6", + "redirectUris" : [ "https://*" ], + "webOrigins" : [ "*" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "saml.assertion.signature" : "false", + "saml.multivalued.roles" : "false", + "saml.force.post.binding" : "false", + "saml.encrypt" : "false", + "saml.server.signature" : "false", + "saml.server.signature.keyinfo.ext" : "false", + "exclude.session.state.from.auth.response" : "false", + "client_credentials.use_refresh_token" : "false", + "saml_force_name_id_format" : "false", + "saml.client.signature" : "false", + "tls.client.certificate.bound.access.tokens" : "false", + "saml.authnstatement" : "false", + "display.on.consent.screen" : "false", + "saml.onetimeuse.condition" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "d5b56ac0-01af-4241-991e-1cd25edeb739", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientId", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientId", + "jsonType.label" : "String" + } + }, { + "id" : "8c119e4b-b308-41aa-be7b-91e1d299e499", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + }, { + "id" : "3ba852b4-71b8-4942-950b-80968346b0e2", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ ], + "optionalClientScopes" : [ ] + }, { + "id" : "25ee1923-6323-4c4c-ae70-178615ace3b2", + "clientId" : "sa-platform-e0000000-fa46551361b4", + "name" : "", + "description" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "dc96e3d3-23cc-4345-aa5e-6f89b5d20c91", + "redirectUris" : [ "https://*" ], + "webOrigins" : [ "*" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "saml.assertion.signature" : "false", + "saml.multivalued.roles" : "false", + "saml.force.post.binding" : "false", + "saml.encrypt" : "false", + "saml.server.signature" : "false", + "saml.server.signature.keyinfo.ext" : "false", + "exclude.session.state.from.auth.response" : "false", + "client_credentials.use_refresh_token" : "false", + "saml_force_name_id_format" : "false", + "saml.client.signature" : "false", + "tls.client.certificate.bound.access.tokens" : "false", + "saml.authnstatement" : "false", + "display.on.consent.screen" : "false", + "saml.onetimeuse.condition" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "270af568-11bf-4208-bccd-58583e44f09c", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "9dda9072-1e47-43fd-a482-6830b252ca5b", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + }, { + "id" : "a35858ac-39ad-46bc-9227-e40698049c62", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientId", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientId", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ ], + "optionalClientScopes" : [ ] }, { "id" : "5c797848-2f03-4085-a03a-e4f7c22d0050", "clientId" : "security-admin-console", @@ -1912,7 +2355,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper" ] } }, { "id" : "013bd2ad-80e7-40fe-ba41-b90642d536cd", @@ -1956,7 +2399,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper" ] } } ], "org.keycloak.keys.KeyProvider" : [ { @@ -2005,7 +2448,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "b5f79174-a900-4c45-9468-4c542234d96c", + "id" : "99f94050-3509-41c8-849c-0db912e12b62", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -2025,7 +2468,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "d601b0d5-66eb-4b1a-8a58-451b7581d9d4", + "id" : "1b0955d1-c468-4c8f-aaa3-1e41274fb587", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -2051,7 +2494,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "34547866-749c-4daf-993c-ced46c4712cd", + "id" : "17b48f55-526a-48fb-93bd-71d660425f5a", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2071,7 +2514,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "613cb1c6-9d00-40cc-990c-31f5f4d72962", + "id" : "644d063a-434e-45ea-9030-822fdba2d996", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2091,7 +2534,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "e1cc9426-e1ac-4d2f-b64c-b122f8380ce9", + "id" : "e9b97743-3468-451a-8f5b-17458b452ed8", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2111,7 +2554,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "6ce277b3-48ed-4043-a109-d7ac4a90e1a9", + "id" : "a0b9df12-356f-4862-8fa7-e966b77e6428", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -2131,7 +2574,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "d9f67f05-e98a-47be-93bb-fd0471c1aab2", + "id" : "f2bd5d3a-70d8-4ec7-ad18-57d7a8b9688c", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -2151,7 +2594,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "45578930-9168-42a5-b30f-8129bcd2a678", + "id" : "160f38f1-8e2f-4517-8e73-815d47c34336", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -2172,7 +2615,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "7b2f5eaa-2588-487b-8663-f0954abd9d6b", + "id" : "85ee86bf-920e-4e7b-a787-c3a6c350fdf7", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -2192,7 +2635,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "eafcda3d-e7f8-4198-b3ef-1d3a3b722567", + "id" : "877f1367-38ac-4922-a924-bda1eda0b4b6", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -2224,7 +2667,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "ecb43ad1-63af-40e7-832a-f12476f0687a", + "id" : "0473e270-f900-4aca-b0d8-ec957f6ab913", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -2256,7 +2699,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "221fa3ed-50e8-4001-a294-fc1c6605e5f2", + "id" : "3010f038-6084-4b93-b1f1-81f81e4fd904", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -2282,7 +2725,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "7d1b8e0a-b145-49b6-a728-510b61ecb7a6", + "id" : "5188ae51-fdb3-4fb6-af25-9698748b07ee", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -2296,7 +2739,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "a518be11-48ed-47fc-942f-b4fe50948313", + "id" : "956daebc-8f19-4cc2-90e0-00ee7b2377b4", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -2317,7 +2760,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "1253f50b-dc27-4410-8e46-cb23282f473d", + "id" : "82795879-c9c0-4a2d-9121-3007b501ed39", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -2337,7 +2780,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "760aff36-b4c7-438b-b076-ca1349da7c6c", + "id" : "12a20ef9-613b-41ce-83ad-0dc0dc6b4e59", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -2357,7 +2800,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "f80b39a0-a6ac-4ee3-93e2-9991239cdd11", + "id" : "84ac627e-e416-46b6-abb8-67de4decf98c", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -2372,7 +2815,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "902c4a30-a3aa-49fc-aa92-af3c312c6f7f", + "id" : "0a0ee5b0-77e7-44c9-9bda-ab2367b02f9b", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -2404,7 +2847,7 @@ "autheticatorFlow" : false } ] }, { - "id" : "3720edba-f95f-4757-b297-cfb355000c71", + "id" : "9c731f7f-6171-4b70-ae31-af22322cbc38", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -2436,7 +2879,7 @@ "autheticatorFlow" : true } ] }, { - "id" : "ff451d58-4027-4128-aa4e-b72d789d84f8", + "id" : "46519bb6-519f-48ba-a8ca-45ffed695ed1", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -2451,13 +2894,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "0dd16f84-6fc7-4bbd-9eb0-913c9c5d050c", + "id" : "3e5ee503-2681-4e20-b0c8-9fed28637e10", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "8716e3b6-36f3-4679-a494-f3ff63bd8abf", + "id" : "41f8a23b-fa93-4fb9-81a3-efe1bc4da394", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/oauth2-proxy/README.md b/oauth2-proxy/README.md index 5354b8244..742e4c3bc 100644 --- a/oauth2-proxy/README.md +++ b/oauth2-proxy/README.md @@ -8,8 +8,12 @@ export OIDC_ISSUER="" hostip=$(ifconfig en0 | awk '$1 == "inet" {print $2}') +# built image using: +# git clone -b kc_idp_hint https://github.com/ikethecoder/oauth2-proxy.git +# (cd oauth2-proxy && docker build --tag oauth2-proxy.local .) +# docker run -ti --rm --name proxy -p 4180:4180 \ - quay.io/oauth2-proxy/oauth2-proxy:v7.2.0 \ + oauth2-proxy.local \ --http-address=0.0.0.0:4180 \ --cookie-secret=$COOKIE_SECRET \ --cookie-secure=False \ @@ -18,7 +22,7 @@ docker run -ti --rm --name proxy -p 4180:4180 \ --insecure-oidc-allow-unverified-email=true \ --insecure-oidc-skip-issuer-verification=true \ --email-domain=* \ - --provider=keycloak \ + --provider=oidc \ --client-id=${OIDC_CLIENT_ID} \ --client-secret=${OIDC_CLIENT_SECRET} \ --scope=openid \ @@ -33,7 +37,7 @@ docker run -ti --rm --name proxy -p 4180:4180 \ --skip-jwt-bearer-tokens=false \ --set-authorization-header=false \ --pass-authorization-header=false \ - --skip-auth-regex="/health|/public|/docs|/redirect|/_next|/images|/devportal|/manager|/about|/maintenance|/admin/session|/ds/api|/feed/|/signout|^[/]$" \ + --skip-auth-regex="/login|/health|/public|/docs|/redirect|/_next|/images|/devportal|/manager|/about|/maintenance|/admin/session|/ds/api|/feed/|/signout|^[/]$" \ --whitelist-domain="${OIDC_ISSUER_HOSTNAME}" \ --upstream="http://${hostip}:3000" ``` diff --git a/src/authz/graphql-whitelist/httplocalhost4180devportalaccess-c3bf09.gql b/src/authz/graphql-whitelist/httplocalhost4180devportalaccess-c3bf09.gql new file mode 100644 index 000000000..8c1ee38ae --- /dev/null +++ b/src/authz/graphql-whitelist/httplocalhost4180devportalaccess-c3bf09.gql @@ -0,0 +1,6 @@ + + mutation genCredential($id: ID!) { + regenerateCredentials(id: $id) { + credential + } + } diff --git a/src/authz/graphql-whitelist/httplocalhost4180devportalaccess-fabe95.gql b/src/authz/graphql-whitelist/httplocalhost4180devportalaccess-fabe95.gql new file mode 100644 index 000000000..ab9c63cdf --- /dev/null +++ b/src/authz/graphql-whitelist/httplocalhost4180devportalaccess-fabe95.gql @@ -0,0 +1,39 @@ + + query GetMyServiceAccesses { + myServiceAccesses(where: { productEnvironment_is_null: false }) { + id + name + active + application { + name + } + productEnvironment { + id + name + flow + product { + id + name + } + } + } + myAccessRequests( + where: { productEnvironment_is_null: false, serviceAccess_is_null: true } + ) { + id + application { + name + } + productEnvironment { + id + name + product { + id + name + } + } + isComplete + isApproved + isIssued + } + } diff --git a/src/authz/matrix.csv b/src/authz/matrix.csv index 0b82fb012..221908acc 100644 --- a/src/authz/matrix.csv +++ b/src/authz/matrix.csv @@ -163,6 +163,7 @@ API Owner Role - All Fields,,,,,read,,*,,,,"api-owner,provider-user",allow, API Owner Role - All Fields,,,,,"update,create",,*,,api-owner,,,allow, Portal User,,DiscoverableProduct,,,,,,,portal-user,,,allow, Portal User,,myServiceAccesses,,,,,,,portal-user,,,allow,filterByAppOwner +Portal User,,regenerateCredentials,,,,,,,portal-user,,,allow,filterByAppOwner Portal User,,myApplications,,,,,,,portal-user,,,allow,filterByOwner API Owner Role Rules,,allGatewayServicesByNamespace,,,,,,,,,"api-owner,provider-user",allow,filterByUserNS API Owner Role Rules,,allGatewayRoutesByNamespace,,,,,,,,,"api-owner,provider-user",allow,filterByUserNS diff --git a/src/codegen.yaml b/src/codegen.yaml index 7c2db7ed6..e965d0b1a 100644 --- a/src/codegen.yaml +++ b/src/codegen.yaml @@ -1,4 +1,4 @@ -schema: http://localhost:3000/admin/api +schema: http://localhost:3000/gql/api generates: ./nextapp/shared/types/query.types.ts: plugins: diff --git a/src/lists/extensions/CredentialRegenerate.ts b/src/lists/extensions/CredentialRegenerate.ts new file mode 100644 index 000000000..317d03878 --- /dev/null +++ b/src/lists/extensions/CredentialRegenerate.ts @@ -0,0 +1,117 @@ +import { + linkCredRefsToServiceAccess, + lookupApplication, + lookupCredentialReferenceByServiceAccess, +} from '../../services/keystone'; +import { EnforcementPoint } from '../../authz/enforcement'; +import { KeycloakClientService } from '../../services/keycloak'; +import { NewCredential } from '../../services/workflow/types'; +import { getEnvironmentContext } from '../../services/workflow/get-namespaces'; +import { replaceApiKey } from '../../services/workflow/kong-api-key-replace'; + +module.exports = { + extensions: [ + (keystone: any) => { + keystone.extendGraphQLSchema({ + types: [], + queries: [], + mutations: [ + { + schema: 'regenerateCredentials(id: ID!): AccessRequest', + resolver: async ( + item: any, + args: any, + context: any, + info: any, + { query, access }: any + ) => { + const serviceAccess = await lookupCredentialReferenceByServiceAccess( + context, + args.id + ); + + const flow = serviceAccess.productEnvironment.flow; + const clientAuthenticator = + serviceAccess.productEnvironment?.credentialIssuer + ?.clientAuthenticator; + + if (flow === 'kong-api-key-acl' || flow === 'kong-api-key-only') { + const clientId = serviceAccess.consumer.customId; + + const newApiKey = await replaceApiKey( + clientId, + (serviceAccess.credentialReference as any).id + ); + + const credentialReference = { + id: newApiKey.apiKey.keyAuthPK, + clientId, + }; + const noauthContext = keystone.createContext({ + skipAccessControl: true, + }); + await linkCredRefsToServiceAccess( + noauthContext, + serviceAccess.id, + credentialReference + ); + + const newCredential = { + flow: serviceAccess.productEnvironment.flow, + apiKey: newApiKey.apiKey.apiKey, + } as NewCredential; + + return { + credential: JSON.stringify(newCredential), + }; + } else if (flow === 'client-credentials') { + const noauthContext = keystone.createContext({ + skipAccessControl: true, + }); + const envCtx = await getEnvironmentContext( + noauthContext, + serviceAccess.productEnvironment.id, + {}, + false + ); + + const kcClientService = new KeycloakClientService( + envCtx.issuerEnvConfig.issuerUrl + ); + await kcClientService.login( + envCtx.issuerEnvConfig.clientId, + envCtx.issuerEnvConfig.clientSecret + ); + + const client = await kcClientService.findByClientId( + serviceAccess.consumer.customId + ); + const newSecret = await kcClientService.regenerateSecret( + client.id + ); + + const newCredential = { + flow: serviceAccess.productEnvironment.flow, + clientId: serviceAccess.consumer.customId, + issuer: envCtx.openid.issuer, + tokenEndpoint: envCtx.openid.token_endpoint, + } as NewCredential; + + if (clientAuthenticator === 'client-secret') { + newCredential['clientSecret'] = newSecret; + } + + return { + credential: JSON.stringify(newCredential), + }; + } else { + throw new Error('Invalid Service Access Action'); + } + }, + access: EnforcementPoint, + }, + ], + }); + }, + ], +}; diff --git a/src/nextapp/components/access-list/access-list-item.tsx b/src/nextapp/components/access-list/access-list-item.tsx index b11e548d4..481910b52 100644 --- a/src/nextapp/components/access-list/access-list-item.tsx +++ b/src/nextapp/components/access-list/access-list-item.tsx @@ -22,16 +22,13 @@ import { AccessRequest, } from '@/shared/types/query.types'; import { FaBook } from 'react-icons/fa'; -import has from 'lodash/has'; import { gql } from 'graphql-request'; import { QueryKey, useQueryClient } from 'react-query'; import { useApiMutation } from '@/shared/services/api'; -import { IoEllipsisHorizontal } from 'react-icons/io5'; import Card from '@/components/card'; import { uid } from 'react-uid'; -import AccessStatus from './access-status'; -import GenerateCredentialsDialog from '../access-request-form/generate-credentials-dialog'; +import AccessListRow from './access-list-row'; interface AccessListItemProps { data: (AccessRequest | ServiceAccess)[]; @@ -49,10 +46,7 @@ const AccessListItem: React.FC = ({ const cancelRequest = useApiMutation(requestMutation); const toast = useToast(); const handleRevoke = React.useCallback( - (id, isRequest) => async (event: React.MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - + async (id, isRequest) => { try { if (isRequest) { await cancelRequest.mutateAsync({ id }); @@ -96,50 +90,12 @@ const AccessListItem: React.FC = ({ {data.map((d: AccessRequest & ServiceAccess, index) => ( - - - - - - - {d.productEnvironment?.name} - - - {d.application?.name} - - {(has(d, 'isApproved') || - has(d, 'isIssued') || - has(d, 'isComplete')) && - (!d.isIssued || !d.isApproved) && ( - - )} - - } - color="bc-blue" - variant="ghost" - /> - - - Revoke Access - - - - - + ))} diff --git a/src/nextapp/components/access-list/access-list-row.tsx b/src/nextapp/components/access-list/access-list-row.tsx new file mode 100644 index 000000000..94ef442eb --- /dev/null +++ b/src/nextapp/components/access-list/access-list-row.tsx @@ -0,0 +1,104 @@ +import * as React from 'react'; +import { + Icon, + IconButton, + Menu, + MenuButton, + MenuItem, + MenuList, + Tag, + Td, + Tr, + useDisclosure, +} from '@chakra-ui/react'; +import has from 'lodash/has'; +import type { ServiceAccess, AccessRequest } from '@/shared/types/query.types'; + +import AccessStatus from './access-status'; +import GenerateCredentialsDialog from '../access-request-form/generate-credentials-dialog'; +import RegenerateCredentialsDialog from '../access-request-form/regenerate-credentials-dialog'; +import { IoEllipsisHorizontal } from 'react-icons/io5'; + +interface AccessListRowProps { + data: AccessRequest & ServiceAccess; + index: number; + onRevoke: (id: string, isRequest: boolean) => void; +} + +const AccessListRow: React.FC = ({ + data, + index, + onRevoke, +}) => { + const { isOpen, onClose, onOpen } = useDisclosure(); + const handleRevoke = React.useCallback( + (id, isRequest) => async (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + + onRevoke(id, isRequest); + }, + [onRevoke] + ); + + return ( + + + + + + + {data.productEnvironment?.name} + + + {data.application?.name} + + {(has(data, 'isApproved') || + has(data, 'isIssued') || + has(data, 'isComplete')) && + (!data.isIssued || !data.isApproved) && ( + + )} + + + } + color="bc-blue" + variant="ghost" + /> + + {[ + 'kong-api-key-only', + 'kong-api-key-acl', + 'client-credentials', + ].includes(data.productEnvironment.flow) && ( + Regenerate Credentials + )} + + {data.active === false ? 'Cancel Request' : 'Revoke Access'} + + + + + + ); +}; + +export default AccessListRow; diff --git a/src/nextapp/components/access-request-form/access-request-credentials.tsx b/src/nextapp/components/access-request-form/access-request-credentials.tsx index aabca9213..29927fa2e 100644 --- a/src/nextapp/components/access-request-form/access-request-credentials.tsx +++ b/src/nextapp/components/access-request-form/access-request-credentials.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Box, Heading, Icon } from '@chakra-ui/react'; +import { Box, Heading, Icon, Text } from '@chakra-ui/react'; import { FaKey } from 'react-icons/fa'; import GenerateCredential from '../generate-credential'; @@ -7,25 +7,30 @@ import GenerateCredential from '../generate-credential'; interface AccessRequestCredentialsProps { id: string; onCredentialGenerated?: () => void; + regenerate?: boolean; } const AccessRequestCredentials: React.FC = ({ id, onCredentialGenerated, + regenerate, }) => { return ( - - Your Credentials - + + + Your Credentials + + diff --git a/src/nextapp/components/access-request-form/access-request-dialog.tsx b/src/nextapp/components/access-request-form/access-request-dialog.tsx index 2fdd2e5a9..2d6776dab 100644 --- a/src/nextapp/components/access-request-form/access-request-dialog.tsx +++ b/src/nextapp/components/access-request-form/access-request-dialog.tsx @@ -28,6 +28,7 @@ import AccessRequestForm from './access-request-form'; import AccessRequestCredentials from './access-request-credentials'; import AccessRequestFormLoading from './access-request-form-loading'; import { useAuth } from '@/shared/services/auth'; +import LoginDialog from '../login-dialog/login-dialog'; interface AccessRequestDialogProps { defaultTab?: number; @@ -156,11 +157,7 @@ const AccessRequestDialog: React.FC = ({ Request Access )} - {!auth.user && ( - - )} + {!auth.user && } = ({ )} - {tab === 1 && } + {tab === 1 && } diff --git a/src/nextapp/components/access-request-form/access-request-form.tsx b/src/nextapp/components/access-request-form/access-request-form.tsx index 8ef40c0d3..ebe420d25 100644 --- a/src/nextapp/components/access-request-form/access-request-form.tsx +++ b/src/nextapp/components/access-request-form/access-request-form.tsx @@ -95,7 +95,7 @@ const AccessRequestForm: React.FC = ({ .filter((e) => e.active) .filter((e) => e.flow !== 'public') .map((e) => ( - + {e.name} ))} @@ -131,12 +131,12 @@ const AccessRequestForm: React.FC = ({ )} -