瀏覽代碼

SONAR-16567 Display the token expiration date and status in the list

tags/9.6.0.59041
Jeremy Davis 1 年之前
父節點
當前提交
a2d4cdc405

+ 1
- 0
server/sonar-web/src/main/js/api/mocks/UserTokensMock.ts 查看文件

@@ -69,6 +69,7 @@ export default class UserTokensMock {
login,
type,
projectKey,
isExpired: false,
token: Math.random()
.toString(RANDOM_RADIX)
.slice(RANDOM_PREFIX),

+ 28
- 0
server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx 查看文件

@@ -24,6 +24,7 @@ import selectEvent from 'react-select-event';
import { getMyProjects, getScannableProjects } from '../../../api/components';
import NotificationsMock from '../../../api/mocks/NotificationsMock';
import UserTokensMock from '../../../api/mocks/UserTokensMock';
import { mockUserToken } from '../../../helpers/mocks/token';
import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks';
import { renderApp } from '../../../helpers/testReactTestingUtils';
import { Permissions } from '../../../types/permissions';
@@ -295,6 +296,33 @@ describe('security page', () => {
}
);

it('should flag expired tokens as such', async () => {
tokenMock.tokens.push(
mockUserToken({
name: 'expired token',
isExpired: true,
expirationDate: '2021-01-23T19:25:19+0000'
})
);

renderAccountApp(
mockLoggedInUser({ permissions: { global: [Permissions.Scan] } }),
securityPagePath
);

expect(await screen.findByText('users.tokens')).toBeInTheDocument();

// expired token is flagged as such
const expiredTokenRow = screen.getByRole('row', { name: /expired token/ });
expect(within(expiredTokenRow).getByText('my_account.tokens.expired')).toBeInTheDocument();

// unexpired token is not flagged
const unexpiredTokenRow = screen.getAllByRole('row')[0];
expect(
within(unexpiredTokenRow).queryByText('my_account.tokens.expired')
).not.toBeInTheDocument();
});

it("should not suggest creating a Project token if the user doesn't have at least one scannable Projects", async () => {
(getScannableProjects as jest.Mock).mockResolvedValueOnce({
projects: []

+ 1
- 6
server/sonar-web/src/main/js/apps/account/account.css 查看文件

@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
.account-container {
width: 800px;
width: 1000px;
margin-left: auto;
margin-right: auto;
}
@@ -63,11 +63,6 @@
border-top: 1px solid var(--barBorderColor);
}

.account-projects-list {
margin-left: -20px;
margin-right: -20px;
}

.account-projects-list > li {
padding: 15px 20px;
}

+ 4
- 2
server/sonar-web/src/main/js/apps/users/components/TokensForm.tsx 查看文件

@@ -127,6 +127,7 @@ export class TokensForm extends React.PureComponent<Props, State> {
{
name: newToken.name,
createdAt: newToken.createdAt,
isExpired: false,
type: newTokenType,
...(newTokenType === TokenType.Project && {
project: { key: selectedProject.key, name: selectedProject.name }
@@ -254,7 +255,7 @@ export class TokensForm extends React.PureComponent<Props, State> {
if (tokens.length <= 0) {
return (
<tr>
<td className="note" colSpan={3}>
<td className="note" colSpan={7}>
{translate('users.no_tokens')}
</td>
</tr>
@@ -295,7 +296,8 @@ export class TokensForm extends React.PureComponent<Props, State> {
<th>{translate('my_account.project_name')}</th>
<th>{translate('my_account.tokens_last_usage')}</th>
<th className="text-right">{translate('created')}</th>
<th />
<th className="text-right">{translate('my_account.tokens.expiration')}</th>
<th aria-label={translate('actions')} />
</tr>
</thead>
<tbody>

+ 12
- 1
server/sonar-web/src/main/js/apps/users/components/TokensFormItem.tsx 查看文件

@@ -17,11 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { revokeToken } from '../../../api/user-tokens';
import { Button } from '../../../components/controls/buttons';
import ConfirmButton from '../../../components/controls/ConfirmButton';
import WarningIcon from '../../../components/icons/WarningIcon';
import DateFormatter from '../../../components/intl/DateFormatter';
import DateFromNow from '../../../components/intl/DateFromNow';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
@@ -82,9 +84,15 @@ export default class TokensFormItem extends React.PureComponent<Props, State> {
const { deleteConfirmation, token } = this.props;
const { loading, showConfirmation } = this.state;
return (
<tr>
<tr className={classNames({ 'text-muted-2': token.isExpired })}>
<td title={token.name} className="hide-overflow nowrap">
{token.name}
{token.isExpired && (
<div className="spacer-top text-warning">
<WarningIcon className="little-spacer-right" />
{translate('my_account.tokens.expired')}
</div>
)}
</td>
<td title={translate('users.tokens', token.type)} className="hide-overflow thin">
{translate('users.tokens', token.type, 'short')}
@@ -98,6 +106,9 @@ export default class TokensFormItem extends React.PureComponent<Props, State> {
<td className="thin nowrap text-right">
<DateFormatter date={token.createdAt} long={true} />
</td>
<td className={classNames('thin nowrap text-right', { 'text-warning': token.isExpired })}>
{token.expirationDate ? <DateFormatter date={token.expirationDate} long={true} /> : '–'}
</td>
<td className="thin nowrap text-right">
{deleteConfirmation === 'modal' ? (
<ConfirmButton

+ 17
- 3
server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensForm-test.tsx.snap 查看文件

@@ -50,7 +50,14 @@ exports[`should render correctly 1`] = `
>
created
</th>
<th />
<th
className="text-right"
>
my_account.tokens.expiration
</th>
<th
aria-label="actions"
/>
</tr>
</thead>
<tbody>
@@ -69,7 +76,7 @@ exports[`should render correctly 1`] = `
<tr>
<td
className="note"
colSpan={3}
colSpan={7}
>
users.no_tokens
</td>
@@ -130,7 +137,14 @@ exports[`should render correctly 2`] = `
>
created
</th>
<th />
<th
className="text-right"
>
my_account.tokens.expiration
</th>
<th
aria-label="actions"
/>
</tr>
</thead>
<tbody>

+ 16
- 2
server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/TokensFormItem-test.tsx.snap 查看文件

@@ -1,7 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<tr>
<tr
className=""
>
<td
className="hide-overflow nowrap"
title="foo"
@@ -33,6 +35,11 @@ exports[`should render correctly 1`] = `
long={true}
/>
</td>
<td
className="thin nowrap text-right"
>
</td>
<td
className="thin nowrap text-right"
>
@@ -52,7 +59,9 @@ exports[`should render correctly 1`] = `
`;

exports[`should render correctly 2`] = `
<tr>
<tr
className=""
>
<td
className="hide-overflow nowrap"
title="foo"
@@ -84,6 +93,11 @@ exports[`should render correctly 2`] = `
long={true}
/>
</td>
<td
className="thin nowrap text-right"
>
</td>
<td
className="thin nowrap text-right"
>

+ 1
- 0
server/sonar-web/src/main/js/helpers/mocks/token.ts 查看文件

@@ -25,6 +25,7 @@ export function mockUserToken(overrides: Partial<UserToken> = {}): UserToken {
name: 'Token name',
createdAt: '2019-06-14T09:45:52+0200',
type: TokenType.User,
isExpired: false,
...overrides
};
}

+ 2
- 0
server/sonar-web/src/main/js/types/token.ts 查看文件

@@ -28,6 +28,8 @@ export interface UserToken {
name: string;
createdAt: string;
lastConnectionDate?: string;
expirationDate?: string;
isExpired: boolean;
type: TokenType;
project?: { name: string; key: string };
}

+ 2
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties 查看文件

@@ -2027,6 +2027,8 @@ my_account.tokens_description=If you want to enforce security by not providing c
my_account.token_type=Type
my_account.project_name=Project
my_account.tokens_last_usage=Last use
my_account.tokens.expiration=Expiration
my_account.tokens.expired=Token is expired
my_account.projects=Projects
my_account.projects.description=Those projects are the ones you are administering.
my_account.projects.no_results=You are not administering any project yet.

Loading…
取消
儲存