From a166e0db02a509110083709722eb9b6496d8d6d6 Mon Sep 17 00:00:00 2001 From: David Ma <40131297+davidma415@users.noreply.github.com> Date: Fri, 16 Feb 2024 08:20:54 -0700 Subject: [PATCH] feat(app-reg-v2): display proper tables depending on auth strategy for app (#403) --- cypress/e2e/fixtures/consts.ts | 8 +- .../specs/application_registration.spec.ts | 119 +++++++++++++++++- .../e2e/specs/contextual-analytics.spec.ts | 2 +- .../e2e/support/utils/generateDocuments.ts | 10 +- package.json | 2 +- src/views/Applications/ApplicationDetail.vue | 30 ++++- src/views/Applications/ApplicationForm.vue | 2 +- src/views/MyApps.vue | 15 ++- yarn.lock | 8 +- 9 files changed, 174 insertions(+), 22 deletions(-) diff --git a/cypress/e2e/fixtures/consts.ts b/cypress/e2e/fixtures/consts.ts index f9f33635..9a3cecb2 100644 --- a/cypress/e2e/fixtures/consts.ts +++ b/cypress/e2e/fixtures/consts.ts @@ -1,4 +1,4 @@ -import { GetApplicationResponse, GetRegistrationResponse, PortalContext, Product, ProductVersion, RegistrationConfiguration } from '@kong/sdk-portal-js' +import { AuthStrategyKeyAuthCredentialTypeEnum, GetApplicationResponse, GetRegistrationResponse, PortalContext, Product, ProductVersion, RegistrationConfiguration } from '@kong/sdk-portal-js' const versions: ProductVersion[] = [ { @@ -133,7 +133,11 @@ export const appWithAuthStrategy: GetApplicationResponse = { id: crypto.randomUUID(), created_at: '2022-03-25T13:15:02.104Z', updated_at: '2022-03-25T13:15:02.104Z', - auth_strategy_id: '452664ba-7d37-4e12-875c-3ca8044446fd' + auth_strategy: { + id: crypto.randomUUID(), + name: 'keyauthstrat', + credential_type: AuthStrategyKeyAuthCredentialTypeEnum.KeyAuth + } } const productRegistration: GetRegistrationResponse = { diff --git a/cypress/e2e/specs/application_registration.spec.ts b/cypress/e2e/specs/application_registration.spec.ts index 5e0a3163..d8cf6fc5 100644 --- a/cypress/e2e/specs/application_registration.spec.ts +++ b/cypress/e2e/specs/application_registration.spec.ts @@ -1,4 +1,4 @@ -import { CredentialCreationResponse, GetApplicationResponse, ListAuthStrategiesItem, ListCredentialsResponse, ListCredentialsResponseDataInner, ListRegistrationsResponse } from '@kong/sdk-portal-js' +import { AuthStrategyClientCredentialsCredentialTypeEnum, AuthStrategyKeyAuthCredentialTypeEnum, CredentialCreationResponse, GetApplicationResponse, ListAuthStrategiesItem, ListCredentialsResponse, ListCredentialsResponseDataInner, ListRegistrationsResponse } from '@kong/sdk-portal-js' import { product, versions, productRegistration, apps, productWithKeyAuthAppAuthStrategy, appWithAuthStrategy, versionWithKeyAuthAuthStrategy, versionWithOidcAuthStrategy } from '../fixtures/consts' const mockApplicationWithCredAndReg = ( @@ -1223,6 +1223,123 @@ describe('Application Registration', () => { cy.get('.credentials-list').should('exist') }) + it('app-reg-v2 - show credentials table if app is keyauth ', () => { + cy.mockLaunchDarklyFlags([ + { + name: 'tdx-3531-app-reg-v2', + value: true + } + ]) + const keyAuthApp = { + ...apps[0], + auth_strategy: { + id: 'key-auth-strat-id', + name: 'keyauthstrat', + credential_type: AuthStrategyKeyAuthCredentialTypeEnum.KeyAuth + } + } + + cy.mockApplications([{ ...keyAuthApp }], 1) + mockApplicationWithCredAndReg({ ...keyAuthApp }) + + cy.visit('/my-apps') + + cy.get('[data-testid="applications-table"] tbody tr').click() + + cy.get('[data-testid="client-secret-table"]').should('not.exist') + cy.get('[data-testid="client-secret-table"] [data-testid="refresh-secret-button"]').should('not.exist') + cy.get('.credentials-list').should('exist') + }) + it('app-reg-v2 - does not show any tables if app is oidc ', () => { + cy.mockLaunchDarklyFlags([ + { + name: 'tdx-3531-app-reg-v2', + value: true + } + ]) + const oidcApp = { + ...apps[0], + auth_strategy: { + id: 'oidc-strat-id', + name: 'oidc-strat', + auth_methods: [ + 'client_credentials', + 'session', + 'bearer' + ], + credential_type: AuthStrategyClientCredentialsCredentialTypeEnum.SelfManagedClientCredentials + } + } + + cy.mockApplications([{ ...oidcApp }], 1) + mockApplicationWithCredAndReg({ ...oidcApp }) + + cy.visit('/my-apps') + + cy.get('[data-testid="applications-table"] tbody tr').click() + + cy.get('[data-testid="client-secret-table"]').should('not.exist') + cy.get('[data-testid="client-secret-table"] [data-testid="refresh-secret-button"]').should('not.exist') + cy.get('.credentials-list').should('not.exist') + }) + + it('app-reg-v2 - show dcr token table if app is DCR ', () => { + cy.mockLaunchDarklyFlags([ + { + name: 'tdx-3531-app-reg-v2', + value: true + } + ]) + cy.intercept('POST', `api/v2/applications/${apps[0].id}/refresh-token`, { + statusCode: 200, + body: { client_secret: 'SECRET_TOKEN' } + }).as('refreshToken') + + const dcrApp = { + ...apps[0], + auth_strategy: { + id: 'okta-strat-id', + name: 'dcr-strat', + auth_methods: [ + 'bearer', + 'client_credentials', + 'session' + ], + credential_type: AuthStrategyClientCredentialsCredentialTypeEnum.ClientCredentials + } + } + + cy.mockApplications([{ ...dcrApp }], 1) + mockApplicationWithCredAndReg({ ...dcrApp }) + + cy.visit('/my-apps') + + cy.get('[data-testid="applications-table"] tbody tr').click() + cy.get('.credentials-list').should('not.exist') + + cy.get('[data-testid="client-secret-table"]').should('exist') + cy.get('[data-testid="client-secret-table"] [data-testid="refresh-secret-button"]').should('exist').click() + + cy.wait('@refreshToken') + + cy.get('.toaster-container-outer .message').should( + 'contain', + 'Successfully refreshed secret' + ) + + cy.get('[data-testid="application-secret-token-modal"]').should('exist') + cy.get('[data-testid="copy-button"]').should('contain', 'SECRET_TOKEN').click() + + cy.get('.toaster-container-outer .message').should( + 'contain', + '"SECRET_TOKEN" copied to clipboard' + ) + + cy.get('[data-testid="close-btn"]').click() + + cy.get('[data-testid="application-secret-token-modal"]').should('not.exist') + }) + describe('Credential management with DCR', () => { it('can refresh token of existing application with dcr', () => { cy.mockDcrPortal() diff --git a/cypress/e2e/specs/contextual-analytics.spec.ts b/cypress/e2e/specs/contextual-analytics.spec.ts index 01453d62..52dce0e4 100644 --- a/cypress/e2e/specs/contextual-analytics.spec.ts +++ b/cypress/e2e/specs/contextual-analytics.spec.ts @@ -1,4 +1,4 @@ -import { apps, productRegistrations, versions } from '../fixtures/consts' +import { apps, productRegistrations } from '../fixtures/consts' describe('Contextual Developer Analytics', () => { beforeEach(() => { diff --git a/cypress/e2e/support/utils/generateDocuments.ts b/cypress/e2e/support/utils/generateDocuments.ts index bffe4812..326dfa51 100644 --- a/cypress/e2e/support/utils/generateDocuments.ts +++ b/cypress/e2e/support/utils/generateDocuments.ts @@ -7,7 +7,7 @@ export function generateDocuments (docId: any): DocumentTree[] { parent_document_id: null, slug: 'mock-document-1', title: 'Mock Document #1', - meta: {}, + metadata: {}, children: [] }, { @@ -15,7 +15,7 @@ export function generateDocuments (docId: any): DocumentTree[] { parent_document_id: null, slug: 'mock-document-2', title: 'Mock Document #3', - meta: {}, + metadata: {}, children: [] }, { @@ -23,14 +23,14 @@ export function generateDocuments (docId: any): DocumentTree[] { parent_document_id: null, slug: 'mock-document-3', title: 'Mock Document #3', - meta: {}, + metadata: {}, children: [ { id: crypto.randomUUID(), parent_document_id: docId, slug: 'child-document-1', title: 'Child Document #1', - meta: {}, + metadata: {}, children: [] }, { @@ -38,7 +38,7 @@ export function generateDocuments (docId: any): DocumentTree[] { parent_document_id: docId, slug: 'child-document-2', title: 'Child Document #2', - meta: {}, + metadata: {}, children: [] } ] diff --git a/package.json b/package.json index 01d76868..1b4c336b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@kong-ui-public/spec-renderer": "0.13.5", "@kong/kong-auth-elements": "2.11.1", "@kong/kongponents": "8.127.0", - "@kong/sdk-portal-js": "2.7.0", + "@kong/sdk-portal-js": "2.8.0", "@xstate/vue": "2.0.0", "axios": "1.6.0", "date-fns": "3.3.0", diff --git a/src/views/Applications/ApplicationDetail.vue b/src/views/Applications/ApplicationDetail.vue index e1fde5a9..bdd5724f 100644 --- a/src/views/Applications/ApplicationDetail.vue +++ b/src/views/Applications/ApplicationDetail.vue @@ -86,12 +86,12 @@
@@ -122,6 +122,9 @@ import { TimeframeKeys, TimePeriods } from '@kong-ui-public/analytics-utilities' +import { FeatureFlags } from '@/constants/feature-flags' +import useLDFeatureFlag from '@/hooks/useLDFeatureFlag' +import { AuthStrategyCredentialType } from '@kong/sdk-portal-js' export default defineComponent({ name: 'ApplicationDetail', @@ -130,6 +133,7 @@ export default defineComponent({ setup () { const errorMessage = ref('') const application = ref(null) + const appRegV2Enabled = useLDFeatureFlag(FeatureFlags.AppRegV2, false) const helpText = useI18nStore().state.helpText const $route = useRoute() @@ -142,7 +146,7 @@ export default defineComponent({ const { portalApiV2 } = usePortalApi() const appStore = useAppStore() - const { isDcr, allowedTimePeriod } = storeToRefs(appStore) + const { isDcr: isPortalDcr, allowedTimePeriod } = storeToRefs(appStore) const vitalsLoading = ref(false) const fixedTimeframe = allowedTimePeriod.value === PortalTimeframeKeys.NINETY_DAYS ? ref(TimePeriods.get(TimeframeKeys.THIRTY_DAY) as Timeframe) @@ -160,6 +164,23 @@ export default defineComponent({ } })) + const isApplicationOIDC = computed(() => { + return application.value.auth_strategy?.credential_type === AuthStrategyCredentialType.SelfManagedClientCredentials + }) + + const isApplicationDcr = computed(() => { + if (appRegV2Enabled && application.value) { + // check the application type + if (application.value.auth_strategy?.credential_type === AuthStrategyCredentialType.ClientCredentials) { + return true + } else { + return false + } + } + + return isPortalDcr.value + }) + const analyticsCardTitle = allowedTimePeriod.value === PortalTimeframeKeys.NINETY_DAYS ? `${helpText.analytics.summary30Days} ${helpText.analytics.summary}` : `${helpText.analytics.summary24Hours} ${helpText.analytics.summary}` @@ -190,7 +211,8 @@ export default defineComponent({ helpText, id, breadcrumbs, - isDcr, + isApplicationDcr, + isApplicationOIDC, fixedTimeframe, vitalsLoading } diff --git a/src/views/Applications/ApplicationForm.vue b/src/views/Applications/ApplicationForm.vue index 23a615e4..ddfb5307 100644 --- a/src/views/Applications/ApplicationForm.vue +++ b/src/views/Applications/ApplicationForm.vue @@ -472,7 +472,7 @@ export default defineComponent({ description: res.data.description || '', redirect_uri: res.data.redirect_uri, reference_id: res.data.reference_id, - auth_strategy_id: res.data.auth_strategy_id + auth_strategy_id: res.data.auth_strategy?.id } if (isDcr.value) { delete newFormData.reference_id diff --git a/src/views/MyApps.vue b/src/views/MyApps.vue index d0ccaba0..98871b1c 100644 --- a/src/views/MyApps.vue +++ b/src/views/MyApps.vue @@ -102,7 +102,7 @@ {{ helpTextVitals.viewAnalytics }}
{ + if (appRegV2Enabled) { + return application.auth_strategy?.credential_type === AuthStrategyCredentialType.ClientCredentials + } + + return isPortalDcr.value + } + const modalTitle = computed(() => `Delete ${deleteItem.value?.name}`) const appIds = ref([]) @@ -395,7 +404,7 @@ export default defineComponent({ tableHeaders, handleDelete, fetchingAuthStrategies, - isDcr, + isApplicationDcr, deleteItem, showSecretModal, appRegV2Enabled, diff --git a/yarn.lock b/yarn.lock index 0056bbd3..43ffdf76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1119,10 +1119,10 @@ v-calendar "3.0.0-alpha.8" vue-draggable-next "^2.2.1" -"@kong/sdk-portal-js@2.7.0": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@kong/sdk-portal-js/-/sdk-portal-js-2.7.0.tgz#8bacc5d27ff4c6ad656c48c809598b1f8d976cce" - integrity sha512-KrbIqStnE/43eGmfZpwhrnZ5f1P0oAxw0Ot4YmcyH9J0J+icyIoZBfBMZLexkeUUkWzE3Hjd0UsNlJ/0sf1yhg== +"@kong/sdk-portal-js@2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@kong/sdk-portal-js/-/sdk-portal-js-2.8.0.tgz#38046c8fa3fd48defa2c3d516ae2deebe925a5dd" + integrity sha512-inSNL+aCA8GHJUPyztjxX5a3WweAdjEH2+4f9okM6qhxvs1J0+eKJV+PDpttpb8FX47BZNqmq2W096KgJYqMAQ== dependencies: axios "1.6.0"