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..1f0442fc0 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,32 +294,6 @@ 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', () => { const mockGitOauth = { name: 'github', @@ -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, };