From: Jeremy Davis Date: Thu, 7 Jul 2022 15:21:08 +0000 (+0200) Subject: SONAR-16566 Handle max token lifetime setting when downgrading X-Git-Tag: 9.6.0.59041~274 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=f6c5f762aa28325142ae14176684b9d29eb9c7af;p=sonarqube.git SONAR-16566 Handle max token lifetime setting when downgrading --- diff --git a/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx b/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx index cf48ac04015..3afaf428006 100644 --- a/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx +++ b/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx @@ -31,6 +31,7 @@ import { mockUserToken } from '../../../helpers/mocks/token'; import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks'; import { renderApp } from '../../../helpers/testReactTestingUtils'; import { Permissions } from '../../../types/permissions'; +import { SettingsKey } from '../../../types/settings'; import { TokenExpiration, TokenType } from '../../../types/token'; import { CurrentUser } from '../../../types/users'; import routes from '../routes'; @@ -46,10 +47,12 @@ jest.mock('../../../helpers/dates', () => { }); jest.mock('../../../api/settings', () => { + const { SettingsKey } = jest.requireActual('../../../types/settings'); return { ...jest.requireActual('../../../api/settings'), getValues: jest.fn().mockResolvedValue([ { + key: SettingsKey.TokenMaxAllowedLifetime, value: 'No expiration' } ]) @@ -269,8 +272,8 @@ describe('security page', () => { ])( 'should display expiration date inferior or equal to the settings limit %s', async (settingMaxLifetime, expectedTime, notExpectedTime) => { - (getValues as jest.Mock).mockImplementation(() => - Promise.resolve([{ value: settingMaxLifetime }]) + (getValues as jest.Mock).mockImplementationOnce(() => + Promise.resolve([{ key: SettingsKey.TokenMaxAllowedLifetime, value: settingMaxLifetime }]) ); renderAccountApp( @@ -293,6 +296,29 @@ describe('security page', () => { } ); + it('should handle absent setting', async () => { + (getValues as jest.Mock).mockImplementationOnce(() => Promise.resolve([])); + + renderAccountApp( + mockLoggedInUser({ permissions: { global: [Permissions.Scan] } }), + securityPagePath + ); + + await selectEvent.openMenu(screen.getAllByRole('textbox')[2]); + + [ + TokenExpiration.OneMonth, + TokenExpiration.ThreeMonths, + TokenExpiration.OneYear, + TokenExpiration.NoExpiration + ].forEach(time => { + // TokenExpiration.OneMonth is expected twice has it is the default value. + expect(screen.getAllByText(`users.tokens.expiration.${time}`).length).toBe( + time === TokenExpiration.OneMonth ? 2 : 1 + ); + }); + }); + it.each([ [TokenExpiration.OneMonth, '2022-07-01'], [TokenExpiration.ThreeMonths, '2022-08-30'], diff --git a/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx b/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx index d80749b2c75..cff1307a7c2 100644 --- a/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx @@ -30,6 +30,7 @@ import { now, toShortNotSoISOString } from '../../../helpers/dates'; import { translate } from '../../../helpers/l10n'; import { hasGlobalPermission } from '../../../helpers/users'; import { Permissions } from '../../../types/permissions'; +import { SettingsKey } from '../../../types/settings'; import { TokenExpiration, TokenType, UserToken } from '../../../types/token'; import { CurrentUser } from '../../../types/users'; import TokensFormItem, { TokenDeleteConfirmation } from './TokensFormItem'; @@ -119,16 +120,25 @@ export class TokensForm extends React.PureComponent { }; fetchTokenSettings = async () => { - const setting = await getValues({ keys: 'sonar.auth.token.max.allowed.lifetime' }); - if (setting === undefined || setting[0].value === undefined) { + /* + * We intentionally fetch all settings, because fetching a specific setting will + * return it from the DB as a fallback, even if the setting is not defined at startup. + */ + const settings = await getValues({ keys: '' }); + const maxTokenLifetime = settings.find( + ({ key }) => key === SettingsKey.TokenMaxAllowedLifetime + ); + + if (maxTokenLifetime === undefined || maxTokenLifetime.value === undefined) { return; } - const maxTokenLifetime = setting[0].value; - if (SETTINGS_EXPIRATION_MAP[maxTokenLifetime] !== TokenExpiration.NoExpiration) { + + const maxTokenExpirationOption = SETTINGS_EXPIRATION_MAP[maxTokenLifetime.value]; + + if (maxTokenExpirationOption !== TokenExpiration.NoExpiration) { const tokenExpirationOptions = EXPIRATION_OPTIONS.filter( option => - option.value <= SETTINGS_EXPIRATION_MAP[maxTokenLifetime] && - option.value !== TokenExpiration.NoExpiration + option.value <= maxTokenExpirationOption && option.value !== TokenExpiration.NoExpiration ); if (this.mounted) { this.setState({ tokenExpirationOptions }); diff --git a/server/sonar-web/src/main/js/types/settings.ts b/server/sonar-web/src/main/js/types/settings.ts index a9b9b5f610a..9374cf76253 100644 --- a/server/sonar-web/src/main/js/types/settings.ts +++ b/server/sonar-web/src/main/js/types/settings.ts @@ -25,7 +25,8 @@ export const enum SettingsKey { DefaultProjectVisibility = 'projects.default.visibility', ServerBaseUrl = 'sonar.core.serverBaseURL', PluginRiskConsent = 'sonar.plugins.risk.consent', - LicenceRemainingLocNotificationThreshold = 'sonar.license.notifications.remainingLocThreshold' + LicenceRemainingLocNotificationThreshold = 'sonar.license.notifications.remainingLocThreshold', + TokenMaxAllowedLifetime = 'sonar.auth.token.max.allowed.lifetime' } export enum GlobalSettingKeys {