зеркало из
1
0
Форкнуть 0
opensource-management-portal/routes/settings/personalAccessTokens.ts

161 строка
4.8 KiB
TypeScript
Исходник Обычный вид История

2018-05-02 20:40:50 +03:00
//
// Copyright (c) Microsoft.
2018-05-02 20:40:50 +03:00
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
2023-06-12 03:42:22 +03:00
import { NextFunction, Response, Router } from 'express';
import asyncHandler from 'express-async-handler';
2021-04-27 23:58:45 +03:00
const router: Router = Router();
2024-01-03 22:44:13 +03:00
import { getProviders } from '../../lib/transitional';
import { PersonalAccessToken } from '../../business/entities/token/token';
import { ReposAppRequest } from '../../interfaces';
2023-06-12 03:42:22 +03:00
type ResponseWithNewKey = Response & {
newKey: string;
};
interface IPersonalAccessTokenForDisplay {
active: boolean;
expired: boolean;
expires: string;
identifier: string;
description: string;
apis: string[];
tokenEntity: PersonalAccessToken;
}
2022-10-08 01:25:28 +03:00
export interface IRequestForSettingsPersonalAccessTokens extends ReposAppRequest {
personalAccessTokens?: IPersonalAccessTokenForDisplay[];
}
2018-05-02 20:40:50 +03:00
const serviceName = 'repos-pat';
const tokenExpirationMs = 1000 * 60 * 60 * 24 * 365; // 365 days
2022-10-07 09:59:30 +03:00
function translateTableToEntities(
personalAccessTokens: PersonalAccessToken[]
): IPersonalAccessTokenForDisplay[] {
return personalAccessTokens.map((pat) => {
// So that we do not share the hashed key with the user, we
// build a hash of that and the timestamp to offer a single-version
// tag to use for delete operations, etc.
const displayToken: IPersonalAccessTokenForDisplay = {
active: pat.active,
expired: pat.isExpired(),
expires: pat.expires ? pat.expires.toDateString() : null,
description: pat.description,
apis: pat.scopes ? pat.scopes.split(',') : [],
identifier: pat.getIdentifier(),
tokenEntity: pat,
};
return displayToken;
});
2018-05-02 20:40:50 +03:00
}
2023-06-12 03:42:22 +03:00
function getPersonalAccessTokens(req: ReposAppRequest, res: Response, next: NextFunction) {
const providers = getProviders(req);
const tokenProvider = providers.tokenProvider;
const corporateId = req.individualContext.corporateIdentity.id;
2022-10-07 09:59:30 +03:00
tokenProvider
.queryTokensForCorporateId(corporateId)
.then((tokens) => {
req['personalAccessTokens'] = translateTableToEntities(tokens);
return next();
})
.catch((error) => {
return next(error);
});
2018-05-02 20:40:50 +03:00
}
function view(req: IRequestForSettingsPersonalAccessTokens, res) {
2018-05-02 20:40:50 +03:00
const personalAccessTokens = req.personalAccessTokens;
req.individualContext.webContext.render({
view: 'settings/personalAccessTokens',
title: 'Personal access tokens',
state: {
personalAccessTokens,
newKey: res.newKey,
isPreviewUser: true, //req.isPreviewUser,
},
2018-05-02 20:40:50 +03:00
});
}
router.use(getPersonalAccessTokens);
router.get('/', view);
2023-06-12 03:42:22 +03:00
function createToken(req: ReposAppRequest, res: ResponseWithNewKey, next: NextFunction) {
const providers = getProviders(req);
const tokenProvider = providers.tokenProvider;
2018-05-02 20:40:50 +03:00
const insights = req.insights;
const description = req.body.description;
if (!description) {
2022-10-08 01:25:28 +03:00
return next(new Error('A description is required to create a new Personal Access Token'));
2018-05-02 20:40:50 +03:00
}
const corporateId = req.individualContext.corporateIdentity.id;
const token = PersonalAccessToken.CreateNewToken();
token.corporateId = corporateId;
token.description = description;
2018-05-02 20:40:50 +03:00
const now = new Date();
token.expires = new Date(now.getTime() + tokenExpirationMs);
token.source = serviceName;
token.scopes = 'extension,links';
2018-05-02 20:40:50 +03:00
insights.trackEvent({
name: 'ReposCreateTokenStart',
properties: {
id: corporateId,
2018-05-02 20:40:50 +03:00
description: description,
},
});
2022-10-07 09:59:30 +03:00
tokenProvider
.saveNewToken(token)
.then((ok) => {
insights.trackEvent({
name: 'ReposCreateTokenFinish',
properties: {
id: corporateId,
description: description,
},
});
const newKey = token.getPrivateKey();
getPersonalAccessTokens(req, res, () => {
res.newKey = newKey;
return view(req, res);
});
})
.catch((insertError) => {
insights.trackEvent({
name: 'ReposCreateTokenFailure',
properties: {
id: corporateId,
description: description,
},
});
return next(insertError);
});
2018-05-02 20:40:50 +03:00
}
router.post('/create', createToken);
router.post('/extension', createToken);
2018-05-02 20:40:50 +03:00
2022-10-07 09:59:30 +03:00
router.post(
'/delete',
2023-06-12 03:42:22 +03:00
asyncHandler(async (req: IRequestForSettingsPersonalAccessTokens, res: Response, next: NextFunction) => {
2022-10-08 01:25:28 +03:00
const providers = getProviders(req);
const tokenProvider = providers.tokenProvider;
const revokeAll = req.body.revokeAll === '1';
const revokeIdentifier = req.body.revoke;
const personalAccessTokens = req.personalAccessTokens;
for (const pat of personalAccessTokens) {
const token = pat.tokenEntity;
if (revokeAll || pat.identifier === revokeIdentifier) {
token.active = false;
await tokenProvider.updateToken(token);
2022-10-07 09:59:30 +03:00
}
2018-05-02 20:40:50 +03:00
}
2022-10-08 01:25:28 +03:00
return res.redirect('/settings/security/tokens');
})
2022-10-07 09:59:30 +03:00
);
2018-05-02 20:40:50 +03:00
export default router;