Merge branch 'api-token-v2/regenerate-tokens-api' into api-token-v2/backend-expirations

This commit is contained in:
Ben Irvin 2022-08-22 12:11:46 +02:00
commit ae90cf5fd0
5 changed files with 93 additions and 1 deletions

View File

@ -38,6 +38,21 @@ module.exports = {
ctx.created({ data: apiToken }); ctx.created({ data: apiToken });
}, },
async regenerate(ctx) {
const { id } = ctx.params;
const apiTokenService = getService('api-token');
const apiTokenExists = await apiTokenService.getById(id);
if (!apiTokenExists) {
ctx.notFound('API Token not found');
return;
}
const accessToken = await apiTokenService.regenerate(id);
ctx.created({ data: accessToken });
},
async list(ctx) { async list(ctx) {
const apiTokenService = getService('api-token'); const apiTokenService = getService('api-token');
const apiTokens = await apiTokenService.list(); const apiTokens = await apiTokenService.list();
@ -60,7 +75,6 @@ module.exports = {
if (!apiToken) { if (!apiToken) {
ctx.notFound('API Token not found'); ctx.notFound('API Token not found');
return; return;
} }

View File

@ -56,4 +56,15 @@ module.exports = [
], ],
}, },
}, },
{
method: 'POST',
path: '/api-tokens/:id/regenerate',
handler: 'api-token.regenerate',
config: {
policies: [
'admin::isAuthenticatedAdmin',
{ name: 'admin::hasPermissions', config: { actions: ['admin::api-tokens.update'] } },
],
},
},
]; ];

View File

@ -256,6 +256,33 @@ describe('API Token', () => {
}); });
}); });
describe('regenerate', () => {
test('It regenerates the accessKey', async () => {
const update = jest.fn(({ data }) => Promise.resolve(data));
global.strapi = {
query() {
return { update };
},
config: {
get: jest.fn(() => ''),
},
};
const id = 1;
const res = await apiTokenService.regenerate(id);
expect(update).toHaveBeenCalledWith({
where: { id },
select: ['id', 'accessKey'],
data: {
accessKey: apiTokenService.hash(mockedApiToken.hexedString),
},
});
expect(res).toEqual({ accessKey: mockedApiToken.hexedString });
});
});
describe('update', () => { describe('update', () => {
test('Updates a non-custom token', async () => { test('Updates a non-custom token', async () => {
const token = { const token = {

View File

@ -161,6 +161,28 @@ const create = async (attributes) => {
return result; return result;
}; };
/**
* @param {string|number} id
*
* @returns {Promise<ApiToken>}
*/
const regenerate = async (id) => {
const accessKey = crypto.randomBytes(128).toString('hex');
const apiToken = await strapi.query('admin::api-token').update({
select: ['id', 'accessKey'],
where: { id },
data: {
accessKey: hash(accessKey),
},
});
return {
...apiToken,
accessKey,
};
};
/** /**
* @returns {void} * @returns {void}
*/ */
@ -372,6 +394,7 @@ const mapTokenPermissions = (token) => {
module.exports = { module.exports = {
create, create,
regenerate,
exists, exists,
checkSaltIsDefined, checkSaltIsDefined,
hash, hash,

View File

@ -609,6 +609,23 @@ describe('Admin API Token v2 CRUD (e2e)', () => {
}); });
}); });
test('Regenerates an api token access key', async () => {
const token = await createValidToken();
const res = await rq({
url: `/admin/api-tokens/${token.id}/regenerate`,
method: 'POST',
});
expect(res.statusCode).toBe(201);
expect(res.body.data).toMatchObject({
accessKey: expect.any(String),
});
});
test.todo('Regenerated access key works');
test.todo('Tokens access content for which they are authorized');
test.todo('Tokens fail to access content for which they are not authorized');
test.todo('Sets expiration time correctly'); test.todo('Sets expiration time correctly');
test.todo("Doesn't have expiration if not set"); test.todo("Doesn't have expiration if not set");
}); });