add lifespan and expiresAt fields

This commit is contained in:
Ben Irvin 2022-08-19 16:33:58 +02:00
parent c8af2cef6b
commit 36d37706ce
4 changed files with 58 additions and 2 deletions

View File

@ -60,5 +60,15 @@ module.exports = {
configurable: false, configurable: false,
required: false, required: false,
}, },
expiresAt: {
type: 'datetime',
configurable: false,
required: false,
},
lifespan: {
type: 'number',
configurable: false,
required: false,
},
}, },
}; };

View File

@ -10,7 +10,17 @@ describe('API Token', () => {
hexedString: '6170692d746f6b656e5f746573742d72616e646f6d2d6279746573', hexedString: '6170692d746f6b656e5f746573742d72616e646f6d2d6279746573',
}; };
const SELECT_FIELDS = ['id', 'name', 'description', 'lastUsed', 'type', 'createdAt', 'updatedAt']; const SELECT_FIELDS = [
'id',
'name',
'description',
'lastUsed',
'type',
'lifespan',
'expiresAt',
'createdAt',
'updatedAt',
];
beforeAll(() => { beforeAll(() => {
jest jest
@ -48,12 +58,16 @@ describe('API Token', () => {
data: { data: {
...attributes, ...attributes,
accessKey: apiTokenService.hash(mockedApiToken.hexedString), accessKey: apiTokenService.hash(mockedApiToken.hexedString),
expiresAt: null,
lifespan: null,
}, },
populate: ['permissions'], populate: ['permissions'],
}); });
expect(res).toEqual({ expect(res).toEqual({
...attributes, ...attributes,
accessKey: mockedApiToken.hexedString, accessKey: mockedApiToken.hexedString,
expiresAt: null,
lifespan: null,
}); });
}); });
}); });

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const crypto = require('crypto'); const crypto = require('crypto');
const { isNumber, isNil } = require('lodash');
const { omit, difference, isEmpty, map, isArray } = require('lodash/fp'); const { omit, difference, isEmpty, map, isArray } = require('lodash/fp');
const { ValidationError, NotFoundError } = require('@strapi/utils').errors; const { ValidationError, NotFoundError } = require('@strapi/utils').errors;
const constants = require('./constants'); const constants = require('./constants');
@ -17,6 +18,8 @@ const constants = require('./constants');
* @property {string} [description] * @property {string} [description]
* @property {string} accessKey * @property {string} accessKey
* @property {number} lastUsed * @property {number} lastUsed
* @property {number} lifespan
* @property {number} expiresAt
* @property {TokenType} type * @property {TokenType} type
* @property {(number|ApiTokenPermission)[]} [permissions] * @property {(number|ApiTokenPermission)[]} [permissions]
*/ */
@ -30,7 +33,17 @@ const constants = require('./constants');
*/ */
/** @constant {Array<string>} */ /** @constant {Array<string>} */
const SELECT_FIELDS = ['id', 'name', 'description', 'lastUsed', 'type', 'createdAt', 'updatedAt']; const SELECT_FIELDS = [
'id',
'name',
'description',
'lastUsed',
'type',
'lifespan',
'expiresAt',
'createdAt',
'updatedAt',
];
/** @constant {Array<string>} */ /** @constant {Array<string>} */
const POPULATE_FIELDS = ['permissions']; const POPULATE_FIELDS = ['permissions'];
@ -75,10 +88,27 @@ const hash = (accessKey) => {
.digest('hex'); .digest('hex');
}; };
/**
* @param {number} lifespan
*
* @returns {null|number}
*/
const getExpirationFields = (lifespan) => {
if (!isNumber(lifespan) && !isNil(lifespan)) {
throw new ValidationError('lifespan must be a number or null');
}
return {
lifespan: lifespan || null,
expiresAt: lifespan ? Date.now() + lifespan : null,
};
};
/** /**
* @param {Object} attributes * @param {Object} attributes
* @param {TokenType} attributes.type * @param {TokenType} attributes.type
* @param {string} attributes.name * @param {string} attributes.name
* @param {number} attributes.lifespan
* @param {string[]} [attributes.permissions] * @param {string[]} [attributes.permissions]
* @param {string} [attributes.description] * @param {string} [attributes.description]
* *
@ -96,6 +126,7 @@ const create = async (attributes) => {
data: { data: {
...omit('permissions', attributes), ...omit('permissions', attributes),
accessKey: hash(accessKey), accessKey: hash(accessKey),
...getExpirationFields(attributes.lifespan),
}, },
}); });

View File

@ -10,6 +10,7 @@ const apiTokenCreationSchema = yup
description: yup.string().optional(), description: yup.string().optional(),
type: yup.string().oneOf(Object.values(constants.API_TOKEN_TYPE)).required(), type: yup.string().oneOf(Object.values(constants.API_TOKEN_TYPE)).required(),
permissions: yup.array().of(yup.string()).nullable(), permissions: yup.array().of(yup.string()).nullable(),
lifespan: yup.number().nullable(),
}) })
.noUnknown(); .noUnknown();