From c8b0df00413ac24e7a0a04f800e189fd11ec70c1 Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Mon, 2 Dec 2024 15:08:20 +0200 Subject: [PATCH] Add isOauth boolean parameter to the PersonalAccessToken type (#1269) Add isOauth boolean parameter to the PersonalAccessToken type to be able to filter tokens that was generated from oAuth. The tokenName parameter does not work for this case, because it is set from the original kubernetes secret object name, but not from the che.eclipse.org/scm-personal-access-token-name label. --- packages/common/src/dto/api/index.ts | 1 + .../__tests__/helpers.spec.ts | 4 + .../personalAccessTokenApi/helpers.ts | 4 + .../Form/__tests__/index.spec.tsx | 1 + .../AddEditModal/Form/index.tsx | 2 + .../DeleteModal/__tests__/stub.ts | 2 + .../PersonalAccessTokens/List/index.tsx | 2 +- .../PersonalAccessTokens/__tests__/stub.ts | 2 + .../GitOauthConfig/__tests__/actions.spec.ts | 78 +++++++++---------- .../src/store/GitOauthConfig/actions.ts | 42 ++-------- .../PersonalAccessTokens/__tests__/stub.ts | 2 + 11 files changed, 62 insertions(+), 78 deletions(-) diff --git a/packages/common/src/dto/api/index.ts b/packages/common/src/dto/api/index.ts index fced51621..a7137171f 100644 --- a/packages/common/src/dto/api/index.ts +++ b/packages/common/src/dto/api/index.ts @@ -43,6 +43,7 @@ export type PersonalAccessToken = { tokenName: string; tokenData: string; // base64 encoded gitProviderEndpoint: string; + isOauth: boolean; } & ( | { gitProvider: Exclude; diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/__tests__/helpers.spec.ts b/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/__tests__/helpers.spec.ts index b23ba0a95..7551e72d4 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/__tests__/helpers.spec.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/__tests__/helpers.spec.ts @@ -104,6 +104,7 @@ describe('Helpers for Personal Access Token API', () => { gitProviderEndpoint: 'https://github.com', gitProviderOrganization: undefined, tokenData: DUMMY_TOKEN_DATA, + isOauth: false, }); }); @@ -128,6 +129,7 @@ describe('Helpers for Personal Access Token API', () => { gitProvider: 'github', gitProviderEndpoint: 'https://github.com', tokenData: 'base64-encoded-token-data', + isOauth: false, }; const secret = toSecret(namespace, token); @@ -163,6 +165,7 @@ describe('Helpers for Personal Access Token API', () => { gitProviderEndpoint: 'https://dev.azure.com', gitProviderOrganization: 'azure-org', tokenData: 'base64-encoded-token-data', + isOauth: false, }; const secret = toSecret(namespace, token); @@ -198,6 +201,7 @@ describe('Helpers for Personal Access Token API', () => { gitProvider: 'github', gitProviderEndpoint: 'https://github.com', tokenData: DUMMY_TOKEN_DATA, + isOauth: false, }; expect(() => toSecret(namespace, token)).toThrowError(); diff --git a/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/helpers.ts b/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/helpers.ts index f1dc4ce4a..d3546db3b 100644 --- a/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/helpers.ts +++ b/packages/dashboard-backend/src/devworkspaceClient/services/personalAccessTokenApi/helpers.ts @@ -80,6 +80,10 @@ export function toToken(secret: k8s.V1Secret): api.PersonalAccessToken { gitProvider: secret.metadata.annotations['che.eclipse.org/scm-provider-name'], gitProviderEndpoint: secret.metadata.annotations['che.eclipse.org/scm-url'], gitProviderOrganization: secret.metadata.annotations['che.eclipse.org/scm-organization'], + isOauth: + secret.metadata.annotations['che.eclipse.org/scm-personal-access-token-name'].startsWith( + 'oauth2-', + ), tokenData: DUMMY_TOKEN_DATA, }; } diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/__tests__/index.spec.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/__tests__/index.spec.tsx index 1a956f427..9a2ab4d6e 100644 --- a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/__tests__/index.spec.tsx +++ b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/__tests__/index.spec.tsx @@ -71,6 +71,7 @@ describe('AddEditModalForm', () => { tokenData, gitProvider, gitProviderEndpoint, + isOauth: false, }; patWithOrganization = { ...pat, diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/index.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/index.tsx index 5d6c0101f..c80e770a6 100644 --- a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/index.tsx +++ b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/AddEditModal/Form/index.tsx @@ -92,6 +92,7 @@ export class AddEditModalForm extends React.PureComponent { gitProviderOrganization, tokenName, tokenData, + isOauth: false, }; const isValid = gitProviderEndpointIsValid && @@ -106,6 +107,7 @@ export class AddEditModalForm extends React.PureComponent { gitProvider, tokenName, tokenData, + isOauth: false, }; const isValid = gitProviderEndpointIsValid && tokenNameIsValid && tokenDataIsValid; this.props.onChange(token, isValid); diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/DeleteModal/__tests__/stub.ts b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/DeleteModal/__tests__/stub.ts index 71d5aef54..e1667f7d2 100644 --- a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/DeleteModal/__tests__/stub.ts +++ b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/DeleteModal/__tests__/stub.ts @@ -18,6 +18,7 @@ export const token1: api.PersonalAccessToken = { gitProviderEndpoint: 'https://github.com', tokenData: 'token-data-1', tokenName: 'token-name-1', + isOauth: false, }; export const token2: api.PersonalAccessToken = { @@ -26,4 +27,5 @@ export const token2: api.PersonalAccessToken = { gitProviderEndpoint: 'https://github.com', tokenData: 'token-data-2', tokenName: 'token-name-2', + isOauth: false, }; diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/List/index.tsx b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/List/index.tsx index 8aaffda62..eb7ec2bdf 100644 --- a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/List/index.tsx +++ b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/List/index.tsx @@ -29,7 +29,7 @@ import { PersonalAccessTokenListToolbar } from '@/pages/UserPreferences/Personal const COLUMN_NAMES: Omit< Record, - 'cheUserId' | 'tokenData' + 'cheUserId' | 'tokenData' | 'isOauth' > = { tokenName: 'Name', gitProvider: 'Provider', diff --git a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/__tests__/stub.ts b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/__tests__/stub.ts index 71d5aef54..e1667f7d2 100644 --- a/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/__tests__/stub.ts +++ b/packages/dashboard-frontend/src/pages/UserPreferences/PersonalAccessTokens/__tests__/stub.ts @@ -18,6 +18,7 @@ export const token1: api.PersonalAccessToken = { gitProviderEndpoint: 'https://github.com', tokenData: 'token-data-1', tokenName: 'token-name-1', + isOauth: false, }; export const token2: api.PersonalAccessToken = { @@ -26,4 +27,5 @@ export const token2: api.PersonalAccessToken = { gitProviderEndpoint: 'https://github.com', tokenData: 'token-data-2', tokenName: 'token-name-2', + isOauth: false, }; diff --git a/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/actions.spec.ts b/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/actions.spec.ts index ede4d4cda..e2ae76009 100644 --- a/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/actions.spec.ts +++ b/packages/dashboard-frontend/src/store/GitOauthConfig/__tests__/actions.spec.ts @@ -26,12 +26,11 @@ import { import { createMockStore } from '@/store/__mocks__/mockActionsTestStore'; import { actionCreators, - findUserToken, + findOauthToken, gitOauthDeleteAction, gitOauthErrorAction, gitOauthReceiveAction, gitOauthRequestAction, - isTokenGitProvider, skipOauthReceiveAction, } from '@/store/GitOauthConfig/actions'; import { IGitOauth } from '@/store/GitOauthConfig/reducer'; @@ -111,6 +110,7 @@ describe('GitOauthConfig', () => { gitProviderEndpoint: 'https://github.com', tokenData: 'test-token-data', tokenName: 'test-token', + isOauth: true, }, ] as api.PersonalAccessToken[]; const mockOauthProviders = ['github'] as api.GitOauthProvider[]; @@ -294,33 +294,7 @@ describe('GitOauthConfig', () => { }); }); - describe('isTokenGitProvider', () => { - it('should return true for oauth2 git provider', () => { - const gitProvider = 'oauth2-provider'; - const result = isTokenGitProvider(gitProvider); - expect(result).toBe(true); - }); - - it('should return true for bitbucket-server token format', () => { - const gitProvider = `che-token--<${window.location.hostname}>`; - const result = isTokenGitProvider(gitProvider); - expect(result).toBe(true); - }); - - it('should return false for non-oauth2 and non-bitbucket-server token format', () => { - const gitProvider = 'github'; - const result = isTokenGitProvider(gitProvider); - expect(result).toBe(false); - }); - - it('should return false for invalid bitbucket-server token format', () => { - const gitProvider = `che-token--`; - const result = isTokenGitProvider(gitProvider); - expect(result).toBe(false); - }); - }); - - describe('findUserToken', () => { + describe('findOauthToken', () => { const mockGitOauth = { name: 'github', endpointUrl: 'https://github.com/', @@ -330,20 +304,26 @@ describe('GitOauthConfig', () => { const mockTokens = [ { gitProviderEndpoint: 'https://github.com/', - gitProvider: 'oauth2-provider', + gitProvider: 'github', + tokenName: 'oauth2-provider', + isOauth: true, }, { gitProviderEndpoint: 'https://bitbucket.org/', - gitProvider: 'oauth2-provider', + gitProvider: 'bitbucket', + tokenName: 'oauth2-provider', + isOauth: true, }, { - gitProviderEndpoint: 'https://github.com/', - gitProvider: `che-token--<${window.location.hostname}>`, + gitProviderEndpoint: 'https://bitbucket-server.com/', + gitProvider: 'bitbucket-server', + tokenName: `che-token--<${window.location.hostname}>`, + isOauth: true, }, ] as unknown as api.PersonalAccessToken[]; it('should return providers with token when matching token is found', () => { - const result = findUserToken(mockGitOauth, mockTokens); + const result = findOauthToken(mockGitOauth, mockTokens); expect(result).toEqual(['github']); }); @@ -353,7 +333,20 @@ describe('GitOauthConfig', () => { endpointUrl: 'https://gitlab.com/', } as IGitOauth; - const result = findUserToken(mockGitOauthNoMatch, mockTokens); + const result = findOauthToken(mockGitOauthNoMatch, mockTokens); + expect(result).toEqual([]); + }); + + it('should return an empty array with PAT', () => { + const mockTokens = [ + { + gitProviderEndpoint: 'https://github.com/', + gitProvider: 'github', + tokenName: 'token-name', + }, + ] as unknown as api.PersonalAccessToken[]; + + const result = findOauthToken(mockGitOauth, mockTokens); expect(result).toEqual([]); }); @@ -369,28 +362,31 @@ describe('GitOauthConfig', () => { gitProvider: 'oauth2-provider', cheUserId: 'test-user', tokenData: 'test-token-data', - tokenName: 'test-token', + tokenName: 'oauth2-token-name', + isOauth: true, } as unknown as api.PersonalAccessToken, ]; - const result = findUserToken(mockGitOauthWithTrailingSlash, mockTokensWithTrailingSlash); + const result = findOauthToken(mockGitOauthWithTrailingSlash, mockTokensWithTrailingSlash); expect(result).toEqual(['github']); }); it('should handle bitbucket-server token format', () => { const mockGitOauthBitbucket = { name: 'bitbucket', - endpointUrl: 'https://bitbucket.org/', + endpointUrl: 'https://bitbucket-server.org/', } as IGitOauth; const mockTokensBitbucket = [ { - gitProviderEndpoint: 'https://bitbucket.org/', - gitProvider: `che-token--<${window.location.hostname}>`, + gitProviderEndpoint: 'https://bitbucket-server.org/', + gitProvider: 'bitbucket-server', + tokenName: `che-token--<${window.location.hostname}>`, + isOauth: true, } as unknown as api.PersonalAccessToken, ]; - const result = findUserToken(mockGitOauthBitbucket, mockTokensBitbucket); + const result = findOauthToken(mockGitOauthBitbucket, mockTokensBitbucket); expect(result).toEqual(['bitbucket']); }); }); diff --git a/packages/dashboard-frontend/src/store/GitOauthConfig/actions.ts b/packages/dashboard-frontend/src/store/GitOauthConfig/actions.ts index b4f023959..0d293c19d 100644 --- a/packages/dashboard-frontend/src/store/GitOauthConfig/actions.ts +++ b/packages/dashboard-frontend/src/store/GitOauthConfig/actions.ts @@ -14,11 +14,7 @@ import common, { api } from '@eclipse-che/common'; import { createAction } from '@reduxjs/toolkit'; import { provisionKubernetesNamespace } from '@/services/backend-client/kubernetesNamespaceApi'; -import { - deleteOAuthToken, - getOAuthProviders, - getOAuthToken, -} from '@/services/backend-client/oAuthApi'; +import { deleteOAuthToken, getOAuthProviders } from '@/services/backend-client/oAuthApi'; import { fetchTokens } from '@/services/backend-client/personalAccessTokenApi'; import { deleteSkipOauthProvider, @@ -80,22 +76,11 @@ export const actionCreators = { const defaultKubernetesNamespace = selectDefaultNamespace(getState()); const tokens = await fetchTokens(defaultKubernetesNamespace.name); - const promises: Promise[] = []; for (const gitOauth of supportedGitOauth) { - promises.push( - getOAuthToken(gitOauth.name) - .then(() => { - providersWithToken.push(gitOauth.name); - }) - .catch(() => { - // if `api/oauth/token` doesn't return a user's token, - // then check if there is the user's token in a Kubernetes Secret - providersWithToken.push(...findUserToken(gitOauth, tokens)); - }), - ); + // check if there is an oAuth token in a Kubernetes Secret + providersWithToken.push(...findOauthToken(gitOauth, tokens)); } - promises.push(dispatch(actionCreators.requestSkipAuthorizationProviders())); - await Promise.allSettled(promises); + await dispatch(actionCreators.requestSkipAuthorizationProviders()); dispatch( gitOauthReceiveAction({ @@ -162,7 +147,7 @@ export const actionCreators = { /** * Check the user's token in a Kubernetes Secret */ -export function findUserToken(gitOauth: IGitOauth, tokens: api.PersonalAccessToken[]) { +export function findOauthToken(gitOauth: IGitOauth, tokens: api.PersonalAccessToken[]) { const providersWithToken: api.GitOauthProvider[] = []; const normalizedGitOauthEndpoint = gitOauth.endpointUrl.endsWith('/') @@ -175,25 +160,10 @@ export function findUserToken(gitOauth: IGitOauth, tokens: api.PersonalAccessTok : token.gitProviderEndpoint; // compare Git OAuth Endpoint url ONLY with OAuth tokens - const gitProvider = token.gitProvider; - if ( - isTokenGitProvider(gitProvider) && - normalizedGitOauthEndpoint === normalizedTokenGitProviderEndpoint - ) { + if (token.isOauth && normalizedGitOauthEndpoint === normalizedTokenGitProviderEndpoint) { providersWithToken.push(gitOauth.name); break; } } return providersWithToken; } - -/** - * For compatibility with the old format of the git provider value - */ -export function isTokenGitProvider(gitProvider: string): boolean { - return ( - gitProvider.startsWith('oauth2') || - // The git provider value format of a bitbucket-server token is 'che-token--' - new RegExp(`^che-token-<.*>-<${window.location.hostname}>$`).test(gitProvider) - ); -} diff --git a/packages/dashboard-frontend/src/store/PersonalAccessTokens/__tests__/stub.ts b/packages/dashboard-frontend/src/store/PersonalAccessTokens/__tests__/stub.ts index 71d5aef54..e1667f7d2 100644 --- a/packages/dashboard-frontend/src/store/PersonalAccessTokens/__tests__/stub.ts +++ b/packages/dashboard-frontend/src/store/PersonalAccessTokens/__tests__/stub.ts @@ -18,6 +18,7 @@ export const token1: api.PersonalAccessToken = { gitProviderEndpoint: 'https://github.com', tokenData: 'token-data-1', tokenName: 'token-name-1', + isOauth: false, }; export const token2: api.PersonalAccessToken = { @@ -26,4 +27,5 @@ export const token2: api.PersonalAccessToken = { gitProviderEndpoint: 'https://github.com', tokenData: 'token-data-2', tokenName: 'token-name-2', + isOauth: false, };