@strapi/admin 5.13.0-beta.1 → 5.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/admin/src/features/Auth.js +2 -8
- package/dist/admin/admin/src/features/Auth.js.map +1 -1
- package/dist/admin/admin/src/features/Auth.mjs +2 -8
- package/dist/admin/admin/src/features/Auth.mjs.map +1 -1
- package/dist/admin/admin/src/features/Tracking.js.map +1 -1
- package/dist/admin/admin/src/features/Tracking.mjs.map +1 -1
- package/dist/admin/admin/src/hooks/useQueryParams.js +1 -7
- package/dist/admin/admin/src/hooks/useQueryParams.js.map +1 -1
- package/dist/admin/admin/src/hooks/useQueryParams.mjs +2 -8
- package/dist/admin/admin/src/hooks/useQueryParams.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Auth/components/Register.js +1 -1
- package/dist/admin/admin/src/pages/Auth/components/Register.js.map +1 -1
- package/dist/admin/admin/src/pages/Auth/components/Register.mjs +1 -1
- package/dist/admin/admin/src/pages/Auth/components/Register.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Home/HomePage.js +4 -76
- package/dist/admin/admin/src/pages/Home/HomePage.js.map +1 -1
- package/dist/admin/admin/src/pages/Home/HomePage.mjs +5 -77
- package/dist/admin/admin/src/pages/Home/HomePage.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/SettingsNav.js +29 -30
- package/dist/admin/admin/src/pages/Settings/components/SettingsNav.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/SettingsNav.mjs +32 -33
- package/dist/admin/admin/src/pages/Settings/components/SettingsNav.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.js +19 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.mjs +21 -3
- package/dist/admin/admin/src/pages/Settings/components/Tokens/FormHead.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.js +4 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.mjs +4 -1
- package/dist/admin/admin/src/pages/Settings/components/Tokens/TokenBox.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.js +32 -2
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.mjs +32 -2
- package/dist/admin/admin/src/pages/Settings/pages/ApiTokens/EditView/EditViewPage.mjs.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/TransferTokens/EditView.js +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/TransferTokens/EditView.js.map +1 -1
- package/dist/admin/admin/src/pages/Settings/pages/TransferTokens/EditView.mjs +1 -0
- package/dist/admin/admin/src/pages/Settings/pages/TransferTokens/EditView.mjs.map +1 -1
- package/dist/admin/admin/src/translations/ru.json.js +14 -1
- package/dist/admin/admin/src/translations/ru.json.js.map +1 -1
- package/dist/admin/admin/src/translations/ru.json.mjs +14 -1
- package/dist/admin/admin/src/translations/ru.json.mjs.map +1 -1
- package/dist/admin/index.js +0 -2
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +0 -1
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/features/Tracking.d.ts +2 -0
- package/dist/admin/src/index.d.ts +0 -1
- package/dist/admin/src/pages/Settings/components/Tokens/FormHead.d.ts +4 -1
- package/dist/server/server/src/content-types/api-token.js +7 -0
- package/dist/server/server/src/content-types/api-token.js.map +1 -1
- package/dist/server/server/src/content-types/api-token.mjs +7 -0
- package/dist/server/server/src/content-types/api-token.mjs.map +1 -1
- package/dist/server/server/src/services/api-token.js +21 -3
- package/dist/server/server/src/services/api-token.js.map +1 -1
- package/dist/server/server/src/services/api-token.mjs +21 -3
- package/dist/server/server/src/services/api-token.mjs.map +1 -1
- package/dist/server/server/src/services/encryption.js +62 -0
- package/dist/server/server/src/services/encryption.js.map +1 -0
- package/dist/server/server/src/services/encryption.mjs +60 -0
- package/dist/server/server/src/services/encryption.mjs.map +1 -0
- package/dist/server/server/src/services/index.js +3 -1
- package/dist/server/server/src/services/index.js.map +1 -1
- package/dist/server/server/src/services/index.mjs +3 -1
- package/dist/server/server/src/services/index.mjs.map +1 -1
- package/dist/server/src/content-types/api-token.d.ts +7 -0
- package/dist/server/src/content-types/api-token.d.ts.map +1 -1
- package/dist/server/src/content-types/index.d.ts +7 -0
- package/dist/server/src/content-types/index.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +11 -0
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/api-token.d.ts.map +1 -1
- package/dist/server/src/services/encryption.d.ts +6 -0
- package/dist/server/src/services/encryption.d.ts.map +1 -0
- package/dist/server/src/services/index.d.ts +4 -0
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/validation/project-settings.d.ts +4 -4
- package/dist/shared/contracts/api-token.d.ts +1 -0
- package/dist/shared/contracts/api-token.d.ts.map +1 -1
- package/package.json +8 -8
- package/dist/admin/admin/src/components/SubNav.js +0 -269
- package/dist/admin/admin/src/components/SubNav.js.map +0 -1
- package/dist/admin/admin/src/components/SubNav.mjs +0 -267
- package/dist/admin/admin/src/components/SubNav.mjs.map +0 -1
- package/dist/admin/admin/src/pages/Home/components/ContentManagerWidgets.js +0 -179
- package/dist/admin/admin/src/pages/Home/components/ContentManagerWidgets.js.map +0 -1
- package/dist/admin/admin/src/pages/Home/components/ContentManagerWidgets.mjs +0 -176
- package/dist/admin/admin/src/pages/Home/components/ContentManagerWidgets.mjs.map +0 -1
- package/dist/admin/admin/src/services/homepage.js +0 -28
- package/dist/admin/admin/src/services/homepage.js.map +0 -1
- package/dist/admin/admin/src/services/homepage.mjs +0 -26
- package/dist/admin/admin/src/services/homepage.mjs.map +0 -1
- package/dist/admin/src/components/SubNav.d.ts +0 -33
- package/dist/admin/src/pages/Home/components/ContentManagerWidgets.d.ts +0 -3
- package/dist/admin/src/services/homepage.d.ts +0 -5
- package/dist/server/server/src/controllers/homepage.js +0 -58
- package/dist/server/server/src/controllers/homepage.js.map +0 -1
- package/dist/server/server/src/controllers/homepage.mjs +0 -37
- package/dist/server/server/src/controllers/homepage.mjs.map +0 -1
- package/dist/server/server/src/routes/homepage.js +0 -22
- package/dist/server/server/src/routes/homepage.js.map +0 -1
- package/dist/server/server/src/routes/homepage.mjs +0 -20
- package/dist/server/server/src/routes/homepage.mjs.map +0 -1
- package/dist/server/server/src/services/homepage.js +0 -159
- package/dist/server/server/src/services/homepage.js.map +0 -1
- package/dist/server/server/src/services/homepage.mjs +0 -157
- package/dist/server/server/src/services/homepage.mjs.map +0 -1
- package/dist/server/src/controllers/homepage.d.ts +0 -8
- package/dist/server/src/controllers/homepage.d.ts.map +0 -1
- package/dist/server/src/routes/homepage.d.ts +0 -14
- package/dist/server/src/routes/homepage.d.ts.map +0 -1
- package/dist/server/src/services/homepage.d.ts +0 -14
- package/dist/server/src/services/homepage.d.ts.map +0 -1
|
@@ -4,6 +4,7 @@ var crypto = require('crypto');
|
|
|
4
4
|
var fp = require('lodash/fp');
|
|
5
5
|
var utils = require('@strapi/utils');
|
|
6
6
|
var constants = require('./constants.js');
|
|
7
|
+
var index = require('../utils/index.js');
|
|
7
8
|
|
|
8
9
|
const { ValidationError, NotFoundError } = utils.errors;
|
|
9
10
|
const SELECT_FIELDS = [
|
|
@@ -78,14 +79,25 @@ const POPULATE_FIELDS = [
|
|
|
78
79
|
return null;
|
|
79
80
|
}
|
|
80
81
|
const token = await strapi.db.query('admin::api-token').findOne({
|
|
81
|
-
select:
|
|
82
|
+
select: [
|
|
83
|
+
...SELECT_FIELDS,
|
|
84
|
+
'encryptedKey'
|
|
85
|
+
],
|
|
82
86
|
populate: POPULATE_FIELDS,
|
|
83
87
|
where: whereParams
|
|
84
88
|
});
|
|
85
89
|
if (!token) {
|
|
86
90
|
return token;
|
|
87
91
|
}
|
|
88
|
-
|
|
92
|
+
const { encryptedKey, ...rest } = token;
|
|
93
|
+
if (!encryptedKey) {
|
|
94
|
+
return flattenTokenPermissions(rest);
|
|
95
|
+
}
|
|
96
|
+
const accessKey = index.getService('encryption').decrypt(encryptedKey);
|
|
97
|
+
return flattenTokenPermissions({
|
|
98
|
+
...rest,
|
|
99
|
+
accessKey
|
|
100
|
+
});
|
|
89
101
|
};
|
|
90
102
|
/**
|
|
91
103
|
* Check if token exists
|
|
@@ -112,7 +124,9 @@ const getExpirationFields = (lifespan)=>{
|
|
|
112
124
|
/**
|
|
113
125
|
* Create a token and its permissions
|
|
114
126
|
*/ const create = async (attributes)=>{
|
|
127
|
+
const encryptionService = index.getService('encryption');
|
|
115
128
|
const accessKey = crypto.randomBytes(128).toString('hex');
|
|
129
|
+
const encryptedKey = encryptionService.encrypt(accessKey);
|
|
116
130
|
assertCustomTokenPermissionsValidity(attributes.type, attributes.permissions);
|
|
117
131
|
assertValidLifespan(attributes.lifespan);
|
|
118
132
|
// Create the token
|
|
@@ -122,6 +136,7 @@ const getExpirationFields = (lifespan)=>{
|
|
|
122
136
|
data: {
|
|
123
137
|
...fp.omit('permissions', attributes),
|
|
124
138
|
accessKey: hash(accessKey),
|
|
139
|
+
encryptedKey,
|
|
125
140
|
...getExpirationFields(attributes.lifespan)
|
|
126
141
|
}
|
|
127
142
|
});
|
|
@@ -153,6 +168,8 @@ const getExpirationFields = (lifespan)=>{
|
|
|
153
168
|
};
|
|
154
169
|
const regenerate = async (id)=>{
|
|
155
170
|
const accessKey = crypto.randomBytes(128).toString('hex');
|
|
171
|
+
const encryptionService = index.getService('encryption');
|
|
172
|
+
const encryptedKey = encryptionService.encrypt(accessKey);
|
|
156
173
|
const apiToken = await strapi.db.query('admin::api-token').update({
|
|
157
174
|
select: [
|
|
158
175
|
'id',
|
|
@@ -162,7 +179,8 @@ const regenerate = async (id)=>{
|
|
|
162
179
|
id
|
|
163
180
|
},
|
|
164
181
|
data: {
|
|
165
|
-
accessKey: hash(accessKey)
|
|
182
|
+
accessKey: hash(accessKey),
|
|
183
|
+
encryptedKey
|
|
166
184
|
}
|
|
167
185
|
});
|
|
168
186
|
if (!apiToken) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-token.js","sources":["../../../../../server/src/services/api-token.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { omit, difference, isNil, isEmpty, map, isArray, uniq, isNumber } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport type { Update, ApiToken, ApiTokenBody } from '../../../shared/contracts/api-token';\nimport constants from './constants';\n\nconst { ValidationError, NotFoundError } = errors;\n\ntype ApiTokenPermission = {\n id: number | `${number}`;\n action: string;\n token: DBApiToken | number;\n};\n\ntype DBApiToken = ApiToken & {\n permissions: (number | ApiTokenPermission)[];\n};\n\nconst SELECT_FIELDS = [\n 'id',\n 'name',\n 'description',\n 'lastUsedAt',\n 'type',\n 'lifespan',\n 'expiresAt',\n 'createdAt',\n 'updatedAt',\n];\n\nconst POPULATE_FIELDS = ['permissions'];\n\n// TODO: we need to ensure the permissions are actually valid registered permissions!\n\n/**\n * Assert that a token's permissions attribute is valid for its type\n */\nconst assertCustomTokenPermissionsValidity = (\n type: ApiTokenBody['type'],\n permissions: ApiTokenBody['permissions']\n) => {\n // Ensure non-custom tokens doesn't have permissions\n if (type !== constants.API_TOKEN_TYPE.CUSTOM && !isEmpty(permissions)) {\n throw new ValidationError('Non-custom tokens should not reference permissions');\n }\n\n // Custom type tokens should always have permissions attached to them\n if (type === constants.API_TOKEN_TYPE.CUSTOM && !isArray(permissions)) {\n throw new ValidationError('Missing permissions attribute for custom token');\n }\n\n // Permissions provided for a custom type token should be valid/registered permissions UID\n if (type === constants.API_TOKEN_TYPE.CUSTOM) {\n const validPermissions = strapi.contentAPI.permissions.providers.action.keys();\n const invalidPermissions = difference(permissions, validPermissions) as string[];\n\n if (!isEmpty(invalidPermissions)) {\n throw new ValidationError(`Unknown permissions provided: ${invalidPermissions.join(', ')}`);\n }\n }\n};\n\n/**\n * Check if a token's lifespan is valid\n */\nconst isValidLifespan = (lifespan: unknown) => {\n if (isNil(lifespan)) {\n return true;\n }\n\n if (!isNumber(lifespan) || !Object.values(constants.API_TOKEN_LIFESPANS).includes(lifespan)) {\n return false;\n }\n\n return true;\n};\n\n/**\n * Assert that a token's lifespan is valid\n */\nconst assertValidLifespan = (lifespan: unknown) => {\n if (!isValidLifespan(lifespan)) {\n throw new ValidationError(\n `lifespan must be one of the following values:\n ${Object.values(constants.API_TOKEN_LIFESPANS).join(', ')}`\n );\n }\n};\n\n/**\n * Flatten a token's database permissions objects to an array of strings\n */\nconst flattenTokenPermissions = (token: DBApiToken): ApiToken => {\n if (!token) {\n return token;\n }\n\n return {\n ...token,\n permissions: isArray(token.permissions) ? map('action', token.permissions) : token.permissions,\n };\n};\n\ntype WhereParams = {\n id?: string | number;\n name?: string;\n lastUsedAt?: number;\n description?: string;\n accessKey?: string;\n};\n\n/**\n * Get a token\n */\nconst getBy = async (whereParams: WhereParams = {}): Promise<ApiToken | null> => {\n if (Object.keys(whereParams).length === 0) {\n return null;\n }\n\n const token = await strapi.db\n .query('admin::api-token')\n .findOne({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: whereParams });\n\n if (!token) {\n return token;\n }\n\n return flattenTokenPermissions(token);\n};\n\n/**\n * Check if token exists\n */\nconst exists = async (whereParams: WhereParams = {}): Promise<boolean> => {\n const apiToken = await getBy(whereParams);\n\n return !!apiToken;\n};\n\n/**\n * Return a secure sha512 hash of an accessKey\n */\nconst hash = (accessKey: string) => {\n return crypto\n .createHmac('sha512', strapi.config.get('admin.apiToken.salt'))\n .update(accessKey)\n .digest('hex');\n};\n\nconst getExpirationFields = (lifespan: ApiTokenBody['lifespan']) => {\n // it must be nil or a finite number >= 0\n const isValidNumber = isNumber(lifespan) && Number.isFinite(lifespan) && lifespan > 0;\n if (!isValidNumber && !isNil(lifespan)) {\n throw new ValidationError('lifespan must be a positive number or null');\n }\n\n return {\n lifespan: lifespan || null,\n expiresAt: lifespan ? Date.now() + lifespan : null,\n };\n};\n\n/**\n * Create a token and its permissions\n */\nconst create = async (attributes: ApiTokenBody): Promise<ApiToken> => {\n const accessKey = crypto.randomBytes(128).toString('hex');\n\n assertCustomTokenPermissionsValidity(attributes.type, attributes.permissions);\n assertValidLifespan(attributes.lifespan);\n\n // Create the token\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').create({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n data: {\n ...omit('permissions', attributes),\n accessKey: hash(accessKey),\n ...getExpirationFields(attributes.lifespan),\n },\n });\n\n const result: ApiToken = { ...apiToken, accessKey };\n\n // If this is a custom type token, create and the related permissions\n if (attributes.type === constants.API_TOKEN_TYPE.CUSTOM) {\n // TODO: createMany doesn't seem to create relation properly, implement a better way rather than a ton of queries\n // const permissionsCount = await strapi.db.query('admin::api-token-permission').createMany({\n // populate: POPULATE_FIELDS,\n // data: attributes.permissions.map(action => ({ action, token: apiToken })),\n // });\n await Promise.all(\n uniq(attributes.permissions).map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: apiToken },\n })\n )\n );\n\n const currentPermissions = await strapi.db\n .query('admin::api-token')\n .load(apiToken, 'permissions');\n\n if (currentPermissions) {\n Object.assign(result, { permissions: map('action', currentPermissions) });\n }\n }\n\n return result;\n};\n\nconst regenerate = async (id: string | number): Promise<ApiToken> => {\n const accessKey = crypto.randomBytes(128).toString('hex');\n\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: ['id', 'accessKey'],\n where: { id },\n data: {\n accessKey: hash(accessKey),\n },\n });\n\n if (!apiToken) {\n throw new NotFoundError('The provided token id does not exist');\n }\n\n return {\n ...apiToken,\n accessKey,\n };\n};\n\nconst checkSaltIsDefined = () => {\n if (!strapi.config.get('admin.apiToken.salt')) {\n // TODO V5: stop reading API_TOKEN_SALT\n if (process.env.API_TOKEN_SALT) {\n process.emitWarning(`[deprecated] In future versions, Strapi will stop reading directly from the environment variable API_TOKEN_SALT. Please set apiToken.salt in config/admin.js instead.\nFor security reasons, keep storing the secret in an environment variable and use env() to read it in config/admin.js (ex: \\`apiToken: { salt: env('API_TOKEN_SALT') }\\`). See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`);\n\n strapi.config.set('admin.apiToken.salt', process.env.API_TOKEN_SALT);\n } else {\n throw new Error(\n `Missing apiToken.salt. Please set apiToken.salt in config/admin.js (ex: you can generate one using Node with \\`crypto.randomBytes(16).toString('base64')\\`).\nFor security reasons, prefer storing the secret in an environment variable and read it in config/admin.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`\n );\n }\n }\n};\n\n/**\n * Return a list of all tokens and their permissions\n */\nconst list = async (): Promise<Array<ApiToken>> => {\n const tokens: Array<DBApiToken> = await strapi.db.query('admin::api-token').findMany({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n orderBy: { name: 'ASC' },\n });\n\n if (!tokens) {\n return tokens;\n }\n\n return tokens.map((token) => flattenTokenPermissions(token));\n};\n\n/**\n * Revoke (delete) a token\n */\nconst revoke = async (id: string | number): Promise<ApiToken> => {\n return strapi.db\n .query('admin::api-token')\n .delete({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: { id } });\n};\n\n/**\n * Retrieve a token by id\n */\nconst getById = async (id: string | number) => {\n return getBy({ id });\n};\n\n/**\n * Retrieve a token by name\n */\nconst getByName = async (name: string) => {\n return getBy({ name });\n};\n\n/**\n * Update a token and its permissions\n */\nconst update = async (\n id: string | number,\n attributes: Update.Request['body']\n): Promise<ApiToken> => {\n // retrieve token without permissions\n const originalToken: DBApiToken = await strapi.db\n .query('admin::api-token')\n .findOne({ where: { id } });\n\n if (!originalToken) {\n throw new NotFoundError('Token not found');\n }\n\n const changingTypeToCustom =\n attributes.type === constants.API_TOKEN_TYPE.CUSTOM &&\n originalToken.type !== constants.API_TOKEN_TYPE.CUSTOM;\n\n // if we're updating the permissions on any token type, or changing from non-custom to custom, ensure they're still valid\n // if neither type nor permissions are changing, we don't need to validate again or else we can't allow partial update\n if (attributes.permissions || changingTypeToCustom) {\n assertCustomTokenPermissionsValidity(\n attributes.type || originalToken.type,\n attributes.permissions || originalToken.permissions\n );\n }\n\n assertValidLifespan(attributes.lifespan);\n\n const updatedToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: SELECT_FIELDS,\n where: { id },\n data: omit('permissions', attributes),\n });\n\n // custom tokens need to have their permissions updated as well\n if (updatedToken.type === constants.API_TOKEN_TYPE.CUSTOM && attributes.permissions) {\n const currentPermissionsResult = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n const currentPermissions = map('action', currentPermissionsResult || []);\n const newPermissions = uniq(attributes.permissions);\n\n const actionsToDelete = difference(currentPermissions, newPermissions);\n const actionsToAdd = difference(newPermissions, currentPermissions);\n\n // TODO: improve efficiency here\n // method using a loop -- works but very inefficient\n await Promise.all(\n actionsToDelete.map((action) =>\n strapi.db.query('admin::api-token-permission').delete({\n where: { action, token: id },\n })\n )\n );\n\n // TODO: improve efficiency here\n // using a loop -- works but very inefficient\n await Promise.all(\n actionsToAdd.map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: id },\n })\n )\n );\n }\n // if type is not custom, make sure any old permissions get removed\n else if (updatedToken.type !== constants.API_TOKEN_TYPE.CUSTOM) {\n await strapi.db.query('admin::api-token-permission').delete({\n where: { token: id },\n });\n }\n\n // retrieve permissions\n const permissionsFromDb = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n return {\n ...updatedToken,\n permissions: permissionsFromDb ? permissionsFromDb.map((p: any) => p.action) : undefined,\n };\n};\n\nconst count = async (where = {}): Promise<number> => {\n return strapi.db.query('admin::api-token').count({ where });\n};\n\nexport {\n create,\n count,\n regenerate,\n exists,\n checkSaltIsDefined,\n hash,\n list,\n revoke,\n getById,\n update,\n getByName,\n getBy,\n};\n"],"names":["ValidationError","NotFoundError","errors","SELECT_FIELDS","POPULATE_FIELDS","assertCustomTokenPermissionsValidity","type","permissions","constants","API_TOKEN_TYPE","CUSTOM","isEmpty","isArray","validPermissions","strapi","contentAPI","providers","action","keys","invalidPermissions","difference","join","isValidLifespan","lifespan","isNil","isNumber","Object","values","API_TOKEN_LIFESPANS","includes","assertValidLifespan","flattenTokenPermissions","token","map","getBy","whereParams","length","db","query","findOne","select","populate","where","exists","apiToken","hash","accessKey","crypto","createHmac","config","get","update","digest","getExpirationFields","isValidNumber","Number","isFinite","expiresAt","Date","now","create","attributes","randomBytes","toString","data","omit","result","Promise","all","uniq","currentPermissions","load","assign","regenerate","id","checkSaltIsDefined","process","env","API_TOKEN_SALT","emitWarning","set","Error","list","tokens","findMany","orderBy","name","revoke","delete","getById","getByName","originalToken","changingTypeToCustom","updatedToken","currentPermissionsResult","newPermissions","actionsToDelete","actionsToAdd","permissionsFromDb","p","undefined","count"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api-token.js","sources":["../../../../../server/src/services/api-token.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { omit, difference, isNil, isEmpty, map, isArray, uniq, isNumber } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport type { Update, ApiToken, ApiTokenBody } from '../../../shared/contracts/api-token';\nimport constants from './constants';\nimport { getService } from '../utils';\n\nconst { ValidationError, NotFoundError } = errors;\n\ntype ApiTokenPermission = {\n id: number | `${number}`;\n action: string;\n token: DBApiToken | number;\n};\n\ntype DBApiToken = ApiToken & {\n permissions: (number | ApiTokenPermission)[];\n};\n\nconst SELECT_FIELDS = [\n 'id',\n 'name',\n 'description',\n 'lastUsedAt',\n 'type',\n 'lifespan',\n 'expiresAt',\n 'createdAt',\n 'updatedAt',\n];\n\nconst POPULATE_FIELDS = ['permissions'];\n\n// TODO: we need to ensure the permissions are actually valid registered permissions!\n\n/**\n * Assert that a token's permissions attribute is valid for its type\n */\nconst assertCustomTokenPermissionsValidity = (\n type: ApiTokenBody['type'],\n permissions: ApiTokenBody['permissions']\n) => {\n // Ensure non-custom tokens doesn't have permissions\n if (type !== constants.API_TOKEN_TYPE.CUSTOM && !isEmpty(permissions)) {\n throw new ValidationError('Non-custom tokens should not reference permissions');\n }\n\n // Custom type tokens should always have permissions attached to them\n if (type === constants.API_TOKEN_TYPE.CUSTOM && !isArray(permissions)) {\n throw new ValidationError('Missing permissions attribute for custom token');\n }\n\n // Permissions provided for a custom type token should be valid/registered permissions UID\n if (type === constants.API_TOKEN_TYPE.CUSTOM) {\n const validPermissions = strapi.contentAPI.permissions.providers.action.keys();\n const invalidPermissions = difference(permissions, validPermissions) as string[];\n\n if (!isEmpty(invalidPermissions)) {\n throw new ValidationError(`Unknown permissions provided: ${invalidPermissions.join(', ')}`);\n }\n }\n};\n\n/**\n * Check if a token's lifespan is valid\n */\nconst isValidLifespan = (lifespan: unknown) => {\n if (isNil(lifespan)) {\n return true;\n }\n\n if (!isNumber(lifespan) || !Object.values(constants.API_TOKEN_LIFESPANS).includes(lifespan)) {\n return false;\n }\n\n return true;\n};\n\n/**\n * Assert that a token's lifespan is valid\n */\nconst assertValidLifespan = (lifespan: unknown) => {\n if (!isValidLifespan(lifespan)) {\n throw new ValidationError(\n `lifespan must be one of the following values:\n ${Object.values(constants.API_TOKEN_LIFESPANS).join(', ')}`\n );\n }\n};\n\n/**\n * Flatten a token's database permissions objects to an array of strings\n */\nconst flattenTokenPermissions = (token: DBApiToken): ApiToken => {\n if (!token) {\n return token;\n }\n\n return {\n ...token,\n permissions: isArray(token.permissions) ? map('action', token.permissions) : token.permissions,\n };\n};\n\ntype WhereParams = {\n id?: string | number;\n name?: string;\n lastUsedAt?: number;\n description?: string;\n accessKey?: string;\n};\n\n/**\n * Get a token\n */\nconst getBy = async (whereParams: WhereParams = {}): Promise<ApiToken | null> => {\n if (Object.keys(whereParams).length === 0) {\n return null;\n }\n\n const token = await strapi.db.query('admin::api-token').findOne({\n select: [...SELECT_FIELDS, 'encryptedKey'],\n populate: POPULATE_FIELDS,\n where: whereParams,\n });\n\n if (!token) {\n return token;\n }\n\n const { encryptedKey, ...rest } = token;\n\n if (!encryptedKey) {\n return flattenTokenPermissions(rest);\n }\n\n const accessKey = getService('encryption').decrypt(encryptedKey);\n\n return flattenTokenPermissions({\n ...rest,\n accessKey,\n });\n};\n\n/**\n * Check if token exists\n */\nconst exists = async (whereParams: WhereParams = {}): Promise<boolean> => {\n const apiToken = await getBy(whereParams);\n\n return !!apiToken;\n};\n\n/**\n * Return a secure sha512 hash of an accessKey\n */\nconst hash = (accessKey: string) => {\n return crypto\n .createHmac('sha512', strapi.config.get('admin.apiToken.salt'))\n .update(accessKey)\n .digest('hex');\n};\n\nconst getExpirationFields = (lifespan: ApiTokenBody['lifespan']) => {\n // it must be nil or a finite number >= 0\n const isValidNumber = isNumber(lifespan) && Number.isFinite(lifespan) && lifespan > 0;\n if (!isValidNumber && !isNil(lifespan)) {\n throw new ValidationError('lifespan must be a positive number or null');\n }\n\n return {\n lifespan: lifespan || null,\n expiresAt: lifespan ? Date.now() + lifespan : null,\n };\n};\n\n/**\n * Create a token and its permissions\n */\nconst create = async (attributes: ApiTokenBody): Promise<ApiToken> => {\n const encryptionService = getService('encryption');\n const accessKey = crypto.randomBytes(128).toString('hex');\n const encryptedKey = encryptionService.encrypt(accessKey);\n\n assertCustomTokenPermissionsValidity(attributes.type, attributes.permissions);\n assertValidLifespan(attributes.lifespan);\n\n // Create the token\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').create({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n data: {\n ...omit('permissions', attributes),\n accessKey: hash(accessKey),\n encryptedKey,\n ...getExpirationFields(attributes.lifespan),\n },\n });\n\n const result: ApiToken = { ...apiToken, accessKey };\n\n // If this is a custom type token, create and the related permissions\n if (attributes.type === constants.API_TOKEN_TYPE.CUSTOM) {\n // TODO: createMany doesn't seem to create relation properly, implement a better way rather than a ton of queries\n // const permissionsCount = await strapi.db.query('admin::api-token-permission').createMany({\n // populate: POPULATE_FIELDS,\n // data: attributes.permissions.map(action => ({ action, token: apiToken })),\n // });\n await Promise.all(\n uniq(attributes.permissions).map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: apiToken },\n })\n )\n );\n\n const currentPermissions = await strapi.db\n .query('admin::api-token')\n .load(apiToken, 'permissions');\n\n if (currentPermissions) {\n Object.assign(result, { permissions: map('action', currentPermissions) });\n }\n }\n\n return result;\n};\n\nconst regenerate = async (id: string | number): Promise<ApiToken> => {\n const accessKey = crypto.randomBytes(128).toString('hex');\n const encryptionService = getService('encryption');\n const encryptedKey = encryptionService.encrypt(accessKey);\n\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: ['id', 'accessKey'],\n where: { id },\n data: {\n accessKey: hash(accessKey),\n encryptedKey,\n },\n });\n\n if (!apiToken) {\n throw new NotFoundError('The provided token id does not exist');\n }\n\n return {\n ...apiToken,\n accessKey,\n };\n};\n\nconst checkSaltIsDefined = () => {\n if (!strapi.config.get('admin.apiToken.salt')) {\n // TODO V5: stop reading API_TOKEN_SALT\n if (process.env.API_TOKEN_SALT) {\n process.emitWarning(`[deprecated] In future versions, Strapi will stop reading directly from the environment variable API_TOKEN_SALT. Please set apiToken.salt in config/admin.js instead.\nFor security reasons, keep storing the secret in an environment variable and use env() to read it in config/admin.js (ex: \\`apiToken: { salt: env('API_TOKEN_SALT') }\\`). See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`);\n\n strapi.config.set('admin.apiToken.salt', process.env.API_TOKEN_SALT);\n } else {\n throw new Error(\n `Missing apiToken.salt. Please set apiToken.salt in config/admin.js (ex: you can generate one using Node with \\`crypto.randomBytes(16).toString('base64')\\`).\nFor security reasons, prefer storing the secret in an environment variable and read it in config/admin.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`\n );\n }\n }\n};\n\n/**\n * Return a list of all tokens and their permissions\n */\nconst list = async (): Promise<Array<ApiToken>> => {\n const tokens: Array<DBApiToken> = await strapi.db.query('admin::api-token').findMany({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n orderBy: { name: 'ASC' },\n });\n\n if (!tokens) {\n return tokens;\n }\n\n return tokens.map((token) => flattenTokenPermissions(token));\n};\n\n/**\n * Revoke (delete) a token\n */\nconst revoke = async (id: string | number): Promise<ApiToken> => {\n return strapi.db\n .query('admin::api-token')\n .delete({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: { id } });\n};\n\n/**\n * Retrieve a token by id\n */\nconst getById = async (id: string | number) => {\n return getBy({ id });\n};\n\n/**\n * Retrieve a token by name\n */\nconst getByName = async (name: string) => {\n return getBy({ name });\n};\n\n/**\n * Update a token and its permissions\n */\nconst update = async (\n id: string | number,\n attributes: Update.Request['body']\n): Promise<ApiToken> => {\n // retrieve token without permissions\n const originalToken: DBApiToken = await strapi.db\n .query('admin::api-token')\n .findOne({ where: { id } });\n\n if (!originalToken) {\n throw new NotFoundError('Token not found');\n }\n\n const changingTypeToCustom =\n attributes.type === constants.API_TOKEN_TYPE.CUSTOM &&\n originalToken.type !== constants.API_TOKEN_TYPE.CUSTOM;\n\n // if we're updating the permissions on any token type, or changing from non-custom to custom, ensure they're still valid\n // if neither type nor permissions are changing, we don't need to validate again or else we can't allow partial update\n if (attributes.permissions || changingTypeToCustom) {\n assertCustomTokenPermissionsValidity(\n attributes.type || originalToken.type,\n attributes.permissions || originalToken.permissions\n );\n }\n\n assertValidLifespan(attributes.lifespan);\n\n const updatedToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: SELECT_FIELDS,\n where: { id },\n data: omit('permissions', attributes),\n });\n\n // custom tokens need to have their permissions updated as well\n if (updatedToken.type === constants.API_TOKEN_TYPE.CUSTOM && attributes.permissions) {\n const currentPermissionsResult = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n const currentPermissions = map('action', currentPermissionsResult || []);\n const newPermissions = uniq(attributes.permissions);\n\n const actionsToDelete = difference(currentPermissions, newPermissions);\n const actionsToAdd = difference(newPermissions, currentPermissions);\n\n // TODO: improve efficiency here\n // method using a loop -- works but very inefficient\n await Promise.all(\n actionsToDelete.map((action) =>\n strapi.db.query('admin::api-token-permission').delete({\n where: { action, token: id },\n })\n )\n );\n\n // TODO: improve efficiency here\n // using a loop -- works but very inefficient\n await Promise.all(\n actionsToAdd.map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: id },\n })\n )\n );\n }\n // if type is not custom, make sure any old permissions get removed\n else if (updatedToken.type !== constants.API_TOKEN_TYPE.CUSTOM) {\n await strapi.db.query('admin::api-token-permission').delete({\n where: { token: id },\n });\n }\n\n // retrieve permissions\n const permissionsFromDb = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n return {\n ...updatedToken,\n permissions: permissionsFromDb ? permissionsFromDb.map((p: any) => p.action) : undefined,\n };\n};\n\nconst count = async (where = {}): Promise<number> => {\n return strapi.db.query('admin::api-token').count({ where });\n};\n\nexport {\n create,\n count,\n regenerate,\n exists,\n checkSaltIsDefined,\n hash,\n list,\n revoke,\n getById,\n update,\n getByName,\n getBy,\n};\n"],"names":["ValidationError","NotFoundError","errors","SELECT_FIELDS","POPULATE_FIELDS","assertCustomTokenPermissionsValidity","type","permissions","constants","API_TOKEN_TYPE","CUSTOM","isEmpty","isArray","validPermissions","strapi","contentAPI","providers","action","keys","invalidPermissions","difference","join","isValidLifespan","lifespan","isNil","isNumber","Object","values","API_TOKEN_LIFESPANS","includes","assertValidLifespan","flattenTokenPermissions","token","map","getBy","whereParams","length","db","query","findOne","select","populate","where","encryptedKey","rest","accessKey","getService","decrypt","exists","apiToken","hash","crypto","createHmac","config","get","update","digest","getExpirationFields","isValidNumber","Number","isFinite","expiresAt","Date","now","create","attributes","encryptionService","randomBytes","toString","encrypt","data","omit","result","Promise","all","uniq","currentPermissions","load","assign","regenerate","id","checkSaltIsDefined","process","env","API_TOKEN_SALT","emitWarning","set","Error","list","tokens","findMany","orderBy","name","revoke","delete","getById","getByName","originalToken","changingTypeToCustom","updatedToken","currentPermissionsResult","newPermissions","actionsToDelete","actionsToAdd","permissionsFromDb","p","undefined","count"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import crypto from 'crypto';
|
|
|
2
2
|
import { omit, uniq, map, difference, isEmpty, isArray, isNumber, isNil } from 'lodash/fp';
|
|
3
3
|
import { errors } from '@strapi/utils';
|
|
4
4
|
import constants from './constants.mjs';
|
|
5
|
+
import { getService } from '../utils/index.mjs';
|
|
5
6
|
|
|
6
7
|
const { ValidationError, NotFoundError } = errors;
|
|
7
8
|
const SELECT_FIELDS = [
|
|
@@ -76,14 +77,25 @@ const POPULATE_FIELDS = [
|
|
|
76
77
|
return null;
|
|
77
78
|
}
|
|
78
79
|
const token = await strapi.db.query('admin::api-token').findOne({
|
|
79
|
-
select:
|
|
80
|
+
select: [
|
|
81
|
+
...SELECT_FIELDS,
|
|
82
|
+
'encryptedKey'
|
|
83
|
+
],
|
|
80
84
|
populate: POPULATE_FIELDS,
|
|
81
85
|
where: whereParams
|
|
82
86
|
});
|
|
83
87
|
if (!token) {
|
|
84
88
|
return token;
|
|
85
89
|
}
|
|
86
|
-
|
|
90
|
+
const { encryptedKey, ...rest } = token;
|
|
91
|
+
if (!encryptedKey) {
|
|
92
|
+
return flattenTokenPermissions(rest);
|
|
93
|
+
}
|
|
94
|
+
const accessKey = getService('encryption').decrypt(encryptedKey);
|
|
95
|
+
return flattenTokenPermissions({
|
|
96
|
+
...rest,
|
|
97
|
+
accessKey
|
|
98
|
+
});
|
|
87
99
|
};
|
|
88
100
|
/**
|
|
89
101
|
* Check if token exists
|
|
@@ -110,7 +122,9 @@ const getExpirationFields = (lifespan)=>{
|
|
|
110
122
|
/**
|
|
111
123
|
* Create a token and its permissions
|
|
112
124
|
*/ const create = async (attributes)=>{
|
|
125
|
+
const encryptionService = getService('encryption');
|
|
113
126
|
const accessKey = crypto.randomBytes(128).toString('hex');
|
|
127
|
+
const encryptedKey = encryptionService.encrypt(accessKey);
|
|
114
128
|
assertCustomTokenPermissionsValidity(attributes.type, attributes.permissions);
|
|
115
129
|
assertValidLifespan(attributes.lifespan);
|
|
116
130
|
// Create the token
|
|
@@ -120,6 +134,7 @@ const getExpirationFields = (lifespan)=>{
|
|
|
120
134
|
data: {
|
|
121
135
|
...omit('permissions', attributes),
|
|
122
136
|
accessKey: hash(accessKey),
|
|
137
|
+
encryptedKey,
|
|
123
138
|
...getExpirationFields(attributes.lifespan)
|
|
124
139
|
}
|
|
125
140
|
});
|
|
@@ -151,6 +166,8 @@ const getExpirationFields = (lifespan)=>{
|
|
|
151
166
|
};
|
|
152
167
|
const regenerate = async (id)=>{
|
|
153
168
|
const accessKey = crypto.randomBytes(128).toString('hex');
|
|
169
|
+
const encryptionService = getService('encryption');
|
|
170
|
+
const encryptedKey = encryptionService.encrypt(accessKey);
|
|
154
171
|
const apiToken = await strapi.db.query('admin::api-token').update({
|
|
155
172
|
select: [
|
|
156
173
|
'id',
|
|
@@ -160,7 +177,8 @@ const regenerate = async (id)=>{
|
|
|
160
177
|
id
|
|
161
178
|
},
|
|
162
179
|
data: {
|
|
163
|
-
accessKey: hash(accessKey)
|
|
180
|
+
accessKey: hash(accessKey),
|
|
181
|
+
encryptedKey
|
|
164
182
|
}
|
|
165
183
|
});
|
|
166
184
|
if (!apiToken) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-token.mjs","sources":["../../../../../server/src/services/api-token.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { omit, difference, isNil, isEmpty, map, isArray, uniq, isNumber } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport type { Update, ApiToken, ApiTokenBody } from '../../../shared/contracts/api-token';\nimport constants from './constants';\n\nconst { ValidationError, NotFoundError } = errors;\n\ntype ApiTokenPermission = {\n id: number | `${number}`;\n action: string;\n token: DBApiToken | number;\n};\n\ntype DBApiToken = ApiToken & {\n permissions: (number | ApiTokenPermission)[];\n};\n\nconst SELECT_FIELDS = [\n 'id',\n 'name',\n 'description',\n 'lastUsedAt',\n 'type',\n 'lifespan',\n 'expiresAt',\n 'createdAt',\n 'updatedAt',\n];\n\nconst POPULATE_FIELDS = ['permissions'];\n\n// TODO: we need to ensure the permissions are actually valid registered permissions!\n\n/**\n * Assert that a token's permissions attribute is valid for its type\n */\nconst assertCustomTokenPermissionsValidity = (\n type: ApiTokenBody['type'],\n permissions: ApiTokenBody['permissions']\n) => {\n // Ensure non-custom tokens doesn't have permissions\n if (type !== constants.API_TOKEN_TYPE.CUSTOM && !isEmpty(permissions)) {\n throw new ValidationError('Non-custom tokens should not reference permissions');\n }\n\n // Custom type tokens should always have permissions attached to them\n if (type === constants.API_TOKEN_TYPE.CUSTOM && !isArray(permissions)) {\n throw new ValidationError('Missing permissions attribute for custom token');\n }\n\n // Permissions provided for a custom type token should be valid/registered permissions UID\n if (type === constants.API_TOKEN_TYPE.CUSTOM) {\n const validPermissions = strapi.contentAPI.permissions.providers.action.keys();\n const invalidPermissions = difference(permissions, validPermissions) as string[];\n\n if (!isEmpty(invalidPermissions)) {\n throw new ValidationError(`Unknown permissions provided: ${invalidPermissions.join(', ')}`);\n }\n }\n};\n\n/**\n * Check if a token's lifespan is valid\n */\nconst isValidLifespan = (lifespan: unknown) => {\n if (isNil(lifespan)) {\n return true;\n }\n\n if (!isNumber(lifespan) || !Object.values(constants.API_TOKEN_LIFESPANS).includes(lifespan)) {\n return false;\n }\n\n return true;\n};\n\n/**\n * Assert that a token's lifespan is valid\n */\nconst assertValidLifespan = (lifespan: unknown) => {\n if (!isValidLifespan(lifespan)) {\n throw new ValidationError(\n `lifespan must be one of the following values:\n ${Object.values(constants.API_TOKEN_LIFESPANS).join(', ')}`\n );\n }\n};\n\n/**\n * Flatten a token's database permissions objects to an array of strings\n */\nconst flattenTokenPermissions = (token: DBApiToken): ApiToken => {\n if (!token) {\n return token;\n }\n\n return {\n ...token,\n permissions: isArray(token.permissions) ? map('action', token.permissions) : token.permissions,\n };\n};\n\ntype WhereParams = {\n id?: string | number;\n name?: string;\n lastUsedAt?: number;\n description?: string;\n accessKey?: string;\n};\n\n/**\n * Get a token\n */\nconst getBy = async (whereParams: WhereParams = {}): Promise<ApiToken | null> => {\n if (Object.keys(whereParams).length === 0) {\n return null;\n }\n\n const token = await strapi.db\n .query('admin::api-token')\n .findOne({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: whereParams });\n\n if (!token) {\n return token;\n }\n\n return flattenTokenPermissions(token);\n};\n\n/**\n * Check if token exists\n */\nconst exists = async (whereParams: WhereParams = {}): Promise<boolean> => {\n const apiToken = await getBy(whereParams);\n\n return !!apiToken;\n};\n\n/**\n * Return a secure sha512 hash of an accessKey\n */\nconst hash = (accessKey: string) => {\n return crypto\n .createHmac('sha512', strapi.config.get('admin.apiToken.salt'))\n .update(accessKey)\n .digest('hex');\n};\n\nconst getExpirationFields = (lifespan: ApiTokenBody['lifespan']) => {\n // it must be nil or a finite number >= 0\n const isValidNumber = isNumber(lifespan) && Number.isFinite(lifespan) && lifespan > 0;\n if (!isValidNumber && !isNil(lifespan)) {\n throw new ValidationError('lifespan must be a positive number or null');\n }\n\n return {\n lifespan: lifespan || null,\n expiresAt: lifespan ? Date.now() + lifespan : null,\n };\n};\n\n/**\n * Create a token and its permissions\n */\nconst create = async (attributes: ApiTokenBody): Promise<ApiToken> => {\n const accessKey = crypto.randomBytes(128).toString('hex');\n\n assertCustomTokenPermissionsValidity(attributes.type, attributes.permissions);\n assertValidLifespan(attributes.lifespan);\n\n // Create the token\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').create({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n data: {\n ...omit('permissions', attributes),\n accessKey: hash(accessKey),\n ...getExpirationFields(attributes.lifespan),\n },\n });\n\n const result: ApiToken = { ...apiToken, accessKey };\n\n // If this is a custom type token, create and the related permissions\n if (attributes.type === constants.API_TOKEN_TYPE.CUSTOM) {\n // TODO: createMany doesn't seem to create relation properly, implement a better way rather than a ton of queries\n // const permissionsCount = await strapi.db.query('admin::api-token-permission').createMany({\n // populate: POPULATE_FIELDS,\n // data: attributes.permissions.map(action => ({ action, token: apiToken })),\n // });\n await Promise.all(\n uniq(attributes.permissions).map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: apiToken },\n })\n )\n );\n\n const currentPermissions = await strapi.db\n .query('admin::api-token')\n .load(apiToken, 'permissions');\n\n if (currentPermissions) {\n Object.assign(result, { permissions: map('action', currentPermissions) });\n }\n }\n\n return result;\n};\n\nconst regenerate = async (id: string | number): Promise<ApiToken> => {\n const accessKey = crypto.randomBytes(128).toString('hex');\n\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: ['id', 'accessKey'],\n where: { id },\n data: {\n accessKey: hash(accessKey),\n },\n });\n\n if (!apiToken) {\n throw new NotFoundError('The provided token id does not exist');\n }\n\n return {\n ...apiToken,\n accessKey,\n };\n};\n\nconst checkSaltIsDefined = () => {\n if (!strapi.config.get('admin.apiToken.salt')) {\n // TODO V5: stop reading API_TOKEN_SALT\n if (process.env.API_TOKEN_SALT) {\n process.emitWarning(`[deprecated] In future versions, Strapi will stop reading directly from the environment variable API_TOKEN_SALT. Please set apiToken.salt in config/admin.js instead.\nFor security reasons, keep storing the secret in an environment variable and use env() to read it in config/admin.js (ex: \\`apiToken: { salt: env('API_TOKEN_SALT') }\\`). See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`);\n\n strapi.config.set('admin.apiToken.salt', process.env.API_TOKEN_SALT);\n } else {\n throw new Error(\n `Missing apiToken.salt. Please set apiToken.salt in config/admin.js (ex: you can generate one using Node with \\`crypto.randomBytes(16).toString('base64')\\`).\nFor security reasons, prefer storing the secret in an environment variable and read it in config/admin.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`\n );\n }\n }\n};\n\n/**\n * Return a list of all tokens and their permissions\n */\nconst list = async (): Promise<Array<ApiToken>> => {\n const tokens: Array<DBApiToken> = await strapi.db.query('admin::api-token').findMany({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n orderBy: { name: 'ASC' },\n });\n\n if (!tokens) {\n return tokens;\n }\n\n return tokens.map((token) => flattenTokenPermissions(token));\n};\n\n/**\n * Revoke (delete) a token\n */\nconst revoke = async (id: string | number): Promise<ApiToken> => {\n return strapi.db\n .query('admin::api-token')\n .delete({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: { id } });\n};\n\n/**\n * Retrieve a token by id\n */\nconst getById = async (id: string | number) => {\n return getBy({ id });\n};\n\n/**\n * Retrieve a token by name\n */\nconst getByName = async (name: string) => {\n return getBy({ name });\n};\n\n/**\n * Update a token and its permissions\n */\nconst update = async (\n id: string | number,\n attributes: Update.Request['body']\n): Promise<ApiToken> => {\n // retrieve token without permissions\n const originalToken: DBApiToken = await strapi.db\n .query('admin::api-token')\n .findOne({ where: { id } });\n\n if (!originalToken) {\n throw new NotFoundError('Token not found');\n }\n\n const changingTypeToCustom =\n attributes.type === constants.API_TOKEN_TYPE.CUSTOM &&\n originalToken.type !== constants.API_TOKEN_TYPE.CUSTOM;\n\n // if we're updating the permissions on any token type, or changing from non-custom to custom, ensure they're still valid\n // if neither type nor permissions are changing, we don't need to validate again or else we can't allow partial update\n if (attributes.permissions || changingTypeToCustom) {\n assertCustomTokenPermissionsValidity(\n attributes.type || originalToken.type,\n attributes.permissions || originalToken.permissions\n );\n }\n\n assertValidLifespan(attributes.lifespan);\n\n const updatedToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: SELECT_FIELDS,\n where: { id },\n data: omit('permissions', attributes),\n });\n\n // custom tokens need to have their permissions updated as well\n if (updatedToken.type === constants.API_TOKEN_TYPE.CUSTOM && attributes.permissions) {\n const currentPermissionsResult = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n const currentPermissions = map('action', currentPermissionsResult || []);\n const newPermissions = uniq(attributes.permissions);\n\n const actionsToDelete = difference(currentPermissions, newPermissions);\n const actionsToAdd = difference(newPermissions, currentPermissions);\n\n // TODO: improve efficiency here\n // method using a loop -- works but very inefficient\n await Promise.all(\n actionsToDelete.map((action) =>\n strapi.db.query('admin::api-token-permission').delete({\n where: { action, token: id },\n })\n )\n );\n\n // TODO: improve efficiency here\n // using a loop -- works but very inefficient\n await Promise.all(\n actionsToAdd.map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: id },\n })\n )\n );\n }\n // if type is not custom, make sure any old permissions get removed\n else if (updatedToken.type !== constants.API_TOKEN_TYPE.CUSTOM) {\n await strapi.db.query('admin::api-token-permission').delete({\n where: { token: id },\n });\n }\n\n // retrieve permissions\n const permissionsFromDb = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n return {\n ...updatedToken,\n permissions: permissionsFromDb ? permissionsFromDb.map((p: any) => p.action) : undefined,\n };\n};\n\nconst count = async (where = {}): Promise<number> => {\n return strapi.db.query('admin::api-token').count({ where });\n};\n\nexport {\n create,\n count,\n regenerate,\n exists,\n checkSaltIsDefined,\n hash,\n list,\n revoke,\n getById,\n update,\n getByName,\n getBy,\n};\n"],"names":["ValidationError","NotFoundError","errors","SELECT_FIELDS","POPULATE_FIELDS","assertCustomTokenPermissionsValidity","type","permissions","constants","API_TOKEN_TYPE","CUSTOM","isEmpty","isArray","validPermissions","strapi","contentAPI","providers","action","keys","invalidPermissions","difference","join","isValidLifespan","lifespan","isNil","isNumber","Object","values","API_TOKEN_LIFESPANS","includes","assertValidLifespan","flattenTokenPermissions","token","map","getBy","whereParams","length","db","query","findOne","select","populate","where","exists","apiToken","hash","accessKey","crypto","createHmac","config","get","update","digest","getExpirationFields","isValidNumber","Number","isFinite","expiresAt","Date","now","create","attributes","randomBytes","toString","data","omit","result","Promise","all","uniq","currentPermissions","load","assign","regenerate","id","checkSaltIsDefined","process","env","API_TOKEN_SALT","emitWarning","set","Error","list","tokens","findMany","orderBy","name","revoke","delete","getById","getByName","originalToken","changingTypeToCustom","updatedToken","currentPermissionsResult","newPermissions","actionsToDelete","actionsToAdd","permissionsFromDb","p","undefined","count"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api-token.mjs","sources":["../../../../../server/src/services/api-token.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { omit, difference, isNil, isEmpty, map, isArray, uniq, isNumber } from 'lodash/fp';\nimport { errors } from '@strapi/utils';\nimport type { Update, ApiToken, ApiTokenBody } from '../../../shared/contracts/api-token';\nimport constants from './constants';\nimport { getService } from '../utils';\n\nconst { ValidationError, NotFoundError } = errors;\n\ntype ApiTokenPermission = {\n id: number | `${number}`;\n action: string;\n token: DBApiToken | number;\n};\n\ntype DBApiToken = ApiToken & {\n permissions: (number | ApiTokenPermission)[];\n};\n\nconst SELECT_FIELDS = [\n 'id',\n 'name',\n 'description',\n 'lastUsedAt',\n 'type',\n 'lifespan',\n 'expiresAt',\n 'createdAt',\n 'updatedAt',\n];\n\nconst POPULATE_FIELDS = ['permissions'];\n\n// TODO: we need to ensure the permissions are actually valid registered permissions!\n\n/**\n * Assert that a token's permissions attribute is valid for its type\n */\nconst assertCustomTokenPermissionsValidity = (\n type: ApiTokenBody['type'],\n permissions: ApiTokenBody['permissions']\n) => {\n // Ensure non-custom tokens doesn't have permissions\n if (type !== constants.API_TOKEN_TYPE.CUSTOM && !isEmpty(permissions)) {\n throw new ValidationError('Non-custom tokens should not reference permissions');\n }\n\n // Custom type tokens should always have permissions attached to them\n if (type === constants.API_TOKEN_TYPE.CUSTOM && !isArray(permissions)) {\n throw new ValidationError('Missing permissions attribute for custom token');\n }\n\n // Permissions provided for a custom type token should be valid/registered permissions UID\n if (type === constants.API_TOKEN_TYPE.CUSTOM) {\n const validPermissions = strapi.contentAPI.permissions.providers.action.keys();\n const invalidPermissions = difference(permissions, validPermissions) as string[];\n\n if (!isEmpty(invalidPermissions)) {\n throw new ValidationError(`Unknown permissions provided: ${invalidPermissions.join(', ')}`);\n }\n }\n};\n\n/**\n * Check if a token's lifespan is valid\n */\nconst isValidLifespan = (lifespan: unknown) => {\n if (isNil(lifespan)) {\n return true;\n }\n\n if (!isNumber(lifespan) || !Object.values(constants.API_TOKEN_LIFESPANS).includes(lifespan)) {\n return false;\n }\n\n return true;\n};\n\n/**\n * Assert that a token's lifespan is valid\n */\nconst assertValidLifespan = (lifespan: unknown) => {\n if (!isValidLifespan(lifespan)) {\n throw new ValidationError(\n `lifespan must be one of the following values:\n ${Object.values(constants.API_TOKEN_LIFESPANS).join(', ')}`\n );\n }\n};\n\n/**\n * Flatten a token's database permissions objects to an array of strings\n */\nconst flattenTokenPermissions = (token: DBApiToken): ApiToken => {\n if (!token) {\n return token;\n }\n\n return {\n ...token,\n permissions: isArray(token.permissions) ? map('action', token.permissions) : token.permissions,\n };\n};\n\ntype WhereParams = {\n id?: string | number;\n name?: string;\n lastUsedAt?: number;\n description?: string;\n accessKey?: string;\n};\n\n/**\n * Get a token\n */\nconst getBy = async (whereParams: WhereParams = {}): Promise<ApiToken | null> => {\n if (Object.keys(whereParams).length === 0) {\n return null;\n }\n\n const token = await strapi.db.query('admin::api-token').findOne({\n select: [...SELECT_FIELDS, 'encryptedKey'],\n populate: POPULATE_FIELDS,\n where: whereParams,\n });\n\n if (!token) {\n return token;\n }\n\n const { encryptedKey, ...rest } = token;\n\n if (!encryptedKey) {\n return flattenTokenPermissions(rest);\n }\n\n const accessKey = getService('encryption').decrypt(encryptedKey);\n\n return flattenTokenPermissions({\n ...rest,\n accessKey,\n });\n};\n\n/**\n * Check if token exists\n */\nconst exists = async (whereParams: WhereParams = {}): Promise<boolean> => {\n const apiToken = await getBy(whereParams);\n\n return !!apiToken;\n};\n\n/**\n * Return a secure sha512 hash of an accessKey\n */\nconst hash = (accessKey: string) => {\n return crypto\n .createHmac('sha512', strapi.config.get('admin.apiToken.salt'))\n .update(accessKey)\n .digest('hex');\n};\n\nconst getExpirationFields = (lifespan: ApiTokenBody['lifespan']) => {\n // it must be nil or a finite number >= 0\n const isValidNumber = isNumber(lifespan) && Number.isFinite(lifespan) && lifespan > 0;\n if (!isValidNumber && !isNil(lifespan)) {\n throw new ValidationError('lifespan must be a positive number or null');\n }\n\n return {\n lifespan: lifespan || null,\n expiresAt: lifespan ? Date.now() + lifespan : null,\n };\n};\n\n/**\n * Create a token and its permissions\n */\nconst create = async (attributes: ApiTokenBody): Promise<ApiToken> => {\n const encryptionService = getService('encryption');\n const accessKey = crypto.randomBytes(128).toString('hex');\n const encryptedKey = encryptionService.encrypt(accessKey);\n\n assertCustomTokenPermissionsValidity(attributes.type, attributes.permissions);\n assertValidLifespan(attributes.lifespan);\n\n // Create the token\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').create({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n data: {\n ...omit('permissions', attributes),\n accessKey: hash(accessKey),\n encryptedKey,\n ...getExpirationFields(attributes.lifespan),\n },\n });\n\n const result: ApiToken = { ...apiToken, accessKey };\n\n // If this is a custom type token, create and the related permissions\n if (attributes.type === constants.API_TOKEN_TYPE.CUSTOM) {\n // TODO: createMany doesn't seem to create relation properly, implement a better way rather than a ton of queries\n // const permissionsCount = await strapi.db.query('admin::api-token-permission').createMany({\n // populate: POPULATE_FIELDS,\n // data: attributes.permissions.map(action => ({ action, token: apiToken })),\n // });\n await Promise.all(\n uniq(attributes.permissions).map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: apiToken },\n })\n )\n );\n\n const currentPermissions = await strapi.db\n .query('admin::api-token')\n .load(apiToken, 'permissions');\n\n if (currentPermissions) {\n Object.assign(result, { permissions: map('action', currentPermissions) });\n }\n }\n\n return result;\n};\n\nconst regenerate = async (id: string | number): Promise<ApiToken> => {\n const accessKey = crypto.randomBytes(128).toString('hex');\n const encryptionService = getService('encryption');\n const encryptedKey = encryptionService.encrypt(accessKey);\n\n const apiToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: ['id', 'accessKey'],\n where: { id },\n data: {\n accessKey: hash(accessKey),\n encryptedKey,\n },\n });\n\n if (!apiToken) {\n throw new NotFoundError('The provided token id does not exist');\n }\n\n return {\n ...apiToken,\n accessKey,\n };\n};\n\nconst checkSaltIsDefined = () => {\n if (!strapi.config.get('admin.apiToken.salt')) {\n // TODO V5: stop reading API_TOKEN_SALT\n if (process.env.API_TOKEN_SALT) {\n process.emitWarning(`[deprecated] In future versions, Strapi will stop reading directly from the environment variable API_TOKEN_SALT. Please set apiToken.salt in config/admin.js instead.\nFor security reasons, keep storing the secret in an environment variable and use env() to read it in config/admin.js (ex: \\`apiToken: { salt: env('API_TOKEN_SALT') }\\`). See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`);\n\n strapi.config.set('admin.apiToken.salt', process.env.API_TOKEN_SALT);\n } else {\n throw new Error(\n `Missing apiToken.salt. Please set apiToken.salt in config/admin.js (ex: you can generate one using Node with \\`crypto.randomBytes(16).toString('base64')\\`).\nFor security reasons, prefer storing the secret in an environment variable and read it in config/admin.js. See https://docs.strapi.io/developer-docs/latest/setup-deployment-guides/configurations/optional/environment.html#configuration-using-environment-variables.`\n );\n }\n }\n};\n\n/**\n * Return a list of all tokens and their permissions\n */\nconst list = async (): Promise<Array<ApiToken>> => {\n const tokens: Array<DBApiToken> = await strapi.db.query('admin::api-token').findMany({\n select: SELECT_FIELDS,\n populate: POPULATE_FIELDS,\n orderBy: { name: 'ASC' },\n });\n\n if (!tokens) {\n return tokens;\n }\n\n return tokens.map((token) => flattenTokenPermissions(token));\n};\n\n/**\n * Revoke (delete) a token\n */\nconst revoke = async (id: string | number): Promise<ApiToken> => {\n return strapi.db\n .query('admin::api-token')\n .delete({ select: SELECT_FIELDS, populate: POPULATE_FIELDS, where: { id } });\n};\n\n/**\n * Retrieve a token by id\n */\nconst getById = async (id: string | number) => {\n return getBy({ id });\n};\n\n/**\n * Retrieve a token by name\n */\nconst getByName = async (name: string) => {\n return getBy({ name });\n};\n\n/**\n * Update a token and its permissions\n */\nconst update = async (\n id: string | number,\n attributes: Update.Request['body']\n): Promise<ApiToken> => {\n // retrieve token without permissions\n const originalToken: DBApiToken = await strapi.db\n .query('admin::api-token')\n .findOne({ where: { id } });\n\n if (!originalToken) {\n throw new NotFoundError('Token not found');\n }\n\n const changingTypeToCustom =\n attributes.type === constants.API_TOKEN_TYPE.CUSTOM &&\n originalToken.type !== constants.API_TOKEN_TYPE.CUSTOM;\n\n // if we're updating the permissions on any token type, or changing from non-custom to custom, ensure they're still valid\n // if neither type nor permissions are changing, we don't need to validate again or else we can't allow partial update\n if (attributes.permissions || changingTypeToCustom) {\n assertCustomTokenPermissionsValidity(\n attributes.type || originalToken.type,\n attributes.permissions || originalToken.permissions\n );\n }\n\n assertValidLifespan(attributes.lifespan);\n\n const updatedToken: ApiToken = await strapi.db.query('admin::api-token').update({\n select: SELECT_FIELDS,\n where: { id },\n data: omit('permissions', attributes),\n });\n\n // custom tokens need to have their permissions updated as well\n if (updatedToken.type === constants.API_TOKEN_TYPE.CUSTOM && attributes.permissions) {\n const currentPermissionsResult = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n const currentPermissions = map('action', currentPermissionsResult || []);\n const newPermissions = uniq(attributes.permissions);\n\n const actionsToDelete = difference(currentPermissions, newPermissions);\n const actionsToAdd = difference(newPermissions, currentPermissions);\n\n // TODO: improve efficiency here\n // method using a loop -- works but very inefficient\n await Promise.all(\n actionsToDelete.map((action) =>\n strapi.db.query('admin::api-token-permission').delete({\n where: { action, token: id },\n })\n )\n );\n\n // TODO: improve efficiency here\n // using a loop -- works but very inefficient\n await Promise.all(\n actionsToAdd.map((action) =>\n strapi.db.query('admin::api-token-permission').create({\n data: { action, token: id },\n })\n )\n );\n }\n // if type is not custom, make sure any old permissions get removed\n else if (updatedToken.type !== constants.API_TOKEN_TYPE.CUSTOM) {\n await strapi.db.query('admin::api-token-permission').delete({\n where: { token: id },\n });\n }\n\n // retrieve permissions\n const permissionsFromDb = await strapi.db\n .query('admin::api-token')\n .load(updatedToken, 'permissions');\n\n return {\n ...updatedToken,\n permissions: permissionsFromDb ? permissionsFromDb.map((p: any) => p.action) : undefined,\n };\n};\n\nconst count = async (where = {}): Promise<number> => {\n return strapi.db.query('admin::api-token').count({ where });\n};\n\nexport {\n create,\n count,\n regenerate,\n exists,\n checkSaltIsDefined,\n hash,\n list,\n revoke,\n getById,\n update,\n getByName,\n getBy,\n};\n"],"names":["ValidationError","NotFoundError","errors","SELECT_FIELDS","POPULATE_FIELDS","assertCustomTokenPermissionsValidity","type","permissions","constants","API_TOKEN_TYPE","CUSTOM","isEmpty","isArray","validPermissions","strapi","contentAPI","providers","action","keys","invalidPermissions","difference","join","isValidLifespan","lifespan","isNil","isNumber","Object","values","API_TOKEN_LIFESPANS","includes","assertValidLifespan","flattenTokenPermissions","token","map","getBy","whereParams","length","db","query","findOne","select","populate","where","encryptedKey","rest","accessKey","getService","decrypt","exists","apiToken","hash","crypto","createHmac","config","get","update","digest","getExpirationFields","isValidNumber","Number","isFinite","expiresAt","Date","now","create","attributes","encryptionService","randomBytes","toString","encrypt","data","omit","result","Promise","all","uniq","currentPermissions","load","assign","regenerate","id","checkSaltIsDefined","process","env","API_TOKEN_SALT","emitWarning","set","Error","list","tokens","findMany","orderBy","name","revoke","delete","getById","getByName","originalToken","changingTypeToCustom","updatedToken","currentPermissionsResult","newPermissions","actionsToDelete","actionsToAdd","permissionsFromDb","p","undefined","count"],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
const IV_LENGTH = 16; // 16 bytes for AES-GCM IV
|
|
6
|
+
const ENCRYPTION_VERSION = 'v1';
|
|
7
|
+
const getHashedKey = ()=>{
|
|
8
|
+
const rawKey = strapi.config.get('admin.secrets.encryptionKey');
|
|
9
|
+
if (!rawKey) {
|
|
10
|
+
strapi.log.warn('Encryption key is missing from config');
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return crypto.createHash('sha256').update(rawKey).digest(); // Always 32 bytes
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Encrypts a value string using AES-256-GCM.
|
|
17
|
+
* Returns a string prefixed with the encryption version and includes IV, encrypted content, and auth tag (all hex-encoded).
|
|
18
|
+
*/ const encrypt = (value)=>{
|
|
19
|
+
const key = getHashedKey();
|
|
20
|
+
if (!key) return null;
|
|
21
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
22
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
23
|
+
let encrypted = cipher.update(value, 'utf8', 'hex');
|
|
24
|
+
encrypted += cipher.final('hex');
|
|
25
|
+
const authTag = cipher.getAuthTag();
|
|
26
|
+
return `${ENCRYPTION_VERSION}:${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Decrypts a value encrypted by encrypt().
|
|
30
|
+
* Supports versioned formats like v1:iv:encrypted:authTag
|
|
31
|
+
*/ const decrypt = (encryptedValue)=>{
|
|
32
|
+
const [version, ...rest] = encryptedValue.split(':');
|
|
33
|
+
if (version !== ENCRYPTION_VERSION) {
|
|
34
|
+
throw new Error(`Unsupported encryption version: ${version}`);
|
|
35
|
+
}
|
|
36
|
+
const [ivHex, encryptedHex, tagHex] = rest;
|
|
37
|
+
if (!ivHex || !encryptedHex || !tagHex) {
|
|
38
|
+
throw new Error('Invalid encrypted value format');
|
|
39
|
+
}
|
|
40
|
+
const key = getHashedKey();
|
|
41
|
+
if (!key) return null;
|
|
42
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
43
|
+
const encryptedText = Buffer.from(encryptedHex, 'hex');
|
|
44
|
+
const authTag = Buffer.from(tagHex, 'hex');
|
|
45
|
+
try {
|
|
46
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
47
|
+
decipher.setAuthTag(authTag);
|
|
48
|
+
let decrypted = decipher.update(encryptedText, undefined, 'utf8');
|
|
49
|
+
decrypted += decipher.final('utf8');
|
|
50
|
+
return decrypted;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
strapi.log.warn('[decrypt] Unable to decrypt value — encryption key may have changed or data is corrupted.');
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var encryption = {
|
|
57
|
+
encrypt,
|
|
58
|
+
decrypt
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
module.exports = encryption;
|
|
62
|
+
//# sourceMappingURL=encryption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.js","sources":["../../../../../server/src/services/encryption.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst IV_LENGTH = 16; // 16 bytes for AES-GCM IV\nconst ENCRYPTION_VERSION = 'v1';\n\nconst getHashedKey = (): Buffer | null => {\n const rawKey: string = strapi.config.get('admin.secrets.encryptionKey');\n if (!rawKey) {\n strapi.log.warn('Encryption key is missing from config');\n return null;\n }\n\n return crypto.createHash('sha256').update(rawKey).digest(); // Always 32 bytes\n};\n\n/**\n * Encrypts a value string using AES-256-GCM.\n * Returns a string prefixed with the encryption version and includes IV, encrypted content, and auth tag (all hex-encoded).\n */\nconst encrypt = (value: string) => {\n const key = getHashedKey();\n if (!key) return null;\n\n const iv = crypto.randomBytes(IV_LENGTH);\n const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);\n\n let encrypted = cipher.update(value, 'utf8', 'hex');\n encrypted += cipher.final('hex');\n\n const authTag = cipher.getAuthTag();\n\n return `${ENCRYPTION_VERSION}:${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`;\n};\n\n/**\n * Decrypts a value encrypted by encrypt().\n * Supports versioned formats like v1:iv:encrypted:authTag\n */\nconst decrypt = (encryptedValue: string) => {\n const [version, ...rest] = encryptedValue.split(':');\n\n if (version !== ENCRYPTION_VERSION) {\n throw new Error(`Unsupported encryption version: ${version}`);\n }\n\n const [ivHex, encryptedHex, tagHex] = rest;\n if (!ivHex || !encryptedHex || !tagHex) {\n throw new Error('Invalid encrypted value format');\n }\n\n const key = getHashedKey();\n if (!key) return null;\n\n const iv = Buffer.from(ivHex, 'hex');\n const encryptedText = Buffer.from(encryptedHex, 'hex');\n const authTag = Buffer.from(tagHex, 'hex');\n\n try {\n const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(authTag);\n\n let decrypted = decipher.update(encryptedText, undefined, 'utf8');\n decrypted += decipher.final('utf8');\n\n return decrypted;\n } catch (err) {\n strapi.log.warn(\n '[decrypt] Unable to decrypt value — encryption key may have changed or data is corrupted.'\n );\n return null;\n }\n};\n\nexport default {\n encrypt,\n decrypt,\n};\n"],"names":["IV_LENGTH","ENCRYPTION_VERSION","getHashedKey","rawKey","strapi","config","get","log","warn","crypto","createHash","update","digest","encrypt","value","key","iv","randomBytes","cipher","createCipheriv","encrypted","final","authTag","getAuthTag","toString","decrypt","encryptedValue","version","rest","split","Error","ivHex","encryptedHex","tagHex","Buffer","from","encryptedText","decipher","createDecipheriv","setAuthTag","decrypted","undefined","err"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
const IV_LENGTH = 16; // 16 bytes for AES-GCM IV
|
|
4
|
+
const ENCRYPTION_VERSION = 'v1';
|
|
5
|
+
const getHashedKey = ()=>{
|
|
6
|
+
const rawKey = strapi.config.get('admin.secrets.encryptionKey');
|
|
7
|
+
if (!rawKey) {
|
|
8
|
+
strapi.log.warn('Encryption key is missing from config');
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return crypto.createHash('sha256').update(rawKey).digest(); // Always 32 bytes
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Encrypts a value string using AES-256-GCM.
|
|
15
|
+
* Returns a string prefixed with the encryption version and includes IV, encrypted content, and auth tag (all hex-encoded).
|
|
16
|
+
*/ const encrypt = (value)=>{
|
|
17
|
+
const key = getHashedKey();
|
|
18
|
+
if (!key) return null;
|
|
19
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
20
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
|
|
21
|
+
let encrypted = cipher.update(value, 'utf8', 'hex');
|
|
22
|
+
encrypted += cipher.final('hex');
|
|
23
|
+
const authTag = cipher.getAuthTag();
|
|
24
|
+
return `${ENCRYPTION_VERSION}:${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Decrypts a value encrypted by encrypt().
|
|
28
|
+
* Supports versioned formats like v1:iv:encrypted:authTag
|
|
29
|
+
*/ const decrypt = (encryptedValue)=>{
|
|
30
|
+
const [version, ...rest] = encryptedValue.split(':');
|
|
31
|
+
if (version !== ENCRYPTION_VERSION) {
|
|
32
|
+
throw new Error(`Unsupported encryption version: ${version}`);
|
|
33
|
+
}
|
|
34
|
+
const [ivHex, encryptedHex, tagHex] = rest;
|
|
35
|
+
if (!ivHex || !encryptedHex || !tagHex) {
|
|
36
|
+
throw new Error('Invalid encrypted value format');
|
|
37
|
+
}
|
|
38
|
+
const key = getHashedKey();
|
|
39
|
+
if (!key) return null;
|
|
40
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
41
|
+
const encryptedText = Buffer.from(encryptedHex, 'hex');
|
|
42
|
+
const authTag = Buffer.from(tagHex, 'hex');
|
|
43
|
+
try {
|
|
44
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
|
|
45
|
+
decipher.setAuthTag(authTag);
|
|
46
|
+
let decrypted = decipher.update(encryptedText, undefined, 'utf8');
|
|
47
|
+
decrypted += decipher.final('utf8');
|
|
48
|
+
return decrypted;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
strapi.log.warn('[decrypt] Unable to decrypt value — encryption key may have changed or data is corrupted.');
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var encryption = {
|
|
55
|
+
encrypt,
|
|
56
|
+
decrypt
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export { encryption as default };
|
|
60
|
+
//# sourceMappingURL=encryption.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.mjs","sources":["../../../../../server/src/services/encryption.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst IV_LENGTH = 16; // 16 bytes for AES-GCM IV\nconst ENCRYPTION_VERSION = 'v1';\n\nconst getHashedKey = (): Buffer | null => {\n const rawKey: string = strapi.config.get('admin.secrets.encryptionKey');\n if (!rawKey) {\n strapi.log.warn('Encryption key is missing from config');\n return null;\n }\n\n return crypto.createHash('sha256').update(rawKey).digest(); // Always 32 bytes\n};\n\n/**\n * Encrypts a value string using AES-256-GCM.\n * Returns a string prefixed with the encryption version and includes IV, encrypted content, and auth tag (all hex-encoded).\n */\nconst encrypt = (value: string) => {\n const key = getHashedKey();\n if (!key) return null;\n\n const iv = crypto.randomBytes(IV_LENGTH);\n const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);\n\n let encrypted = cipher.update(value, 'utf8', 'hex');\n encrypted += cipher.final('hex');\n\n const authTag = cipher.getAuthTag();\n\n return `${ENCRYPTION_VERSION}:${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`;\n};\n\n/**\n * Decrypts a value encrypted by encrypt().\n * Supports versioned formats like v1:iv:encrypted:authTag\n */\nconst decrypt = (encryptedValue: string) => {\n const [version, ...rest] = encryptedValue.split(':');\n\n if (version !== ENCRYPTION_VERSION) {\n throw new Error(`Unsupported encryption version: ${version}`);\n }\n\n const [ivHex, encryptedHex, tagHex] = rest;\n if (!ivHex || !encryptedHex || !tagHex) {\n throw new Error('Invalid encrypted value format');\n }\n\n const key = getHashedKey();\n if (!key) return null;\n\n const iv = Buffer.from(ivHex, 'hex');\n const encryptedText = Buffer.from(encryptedHex, 'hex');\n const authTag = Buffer.from(tagHex, 'hex');\n\n try {\n const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(authTag);\n\n let decrypted = decipher.update(encryptedText, undefined, 'utf8');\n decrypted += decipher.final('utf8');\n\n return decrypted;\n } catch (err) {\n strapi.log.warn(\n '[decrypt] Unable to decrypt value — encryption key may have changed or data is corrupted.'\n );\n return null;\n }\n};\n\nexport default {\n encrypt,\n decrypt,\n};\n"],"names":["IV_LENGTH","ENCRYPTION_VERSION","getHashedKey","rawKey","strapi","config","get","log","warn","crypto","createHash","update","digest","encrypt","value","key","iv","randomBytes","cipher","createCipheriv","encrypted","final","authTag","getAuthTag","toString","decrypt","encryptedValue","version","rest","split","Error","ivHex","encryptedHex","tagHex","Buffer","from","encryptedText","decipher","createDecipheriv","setAuthTag","decrypted","undefined","err"],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ var user = require('./user.js');
|
|
|
5
5
|
var role = require('./role.js');
|
|
6
6
|
var passport = require('./passport.js');
|
|
7
7
|
var metrics = require('./metrics.js');
|
|
8
|
+
var encryption = require('./encryption.js');
|
|
8
9
|
var token = require('./token.js');
|
|
9
10
|
var permission = require('./permission.js');
|
|
10
11
|
var contentType = require('./content-type.js');
|
|
@@ -35,7 +36,8 @@ var services = {
|
|
|
35
36
|
action,
|
|
36
37
|
'api-token': apiToken,
|
|
37
38
|
transfer: index,
|
|
38
|
-
'project-settings': projectSettings
|
|
39
|
+
'project-settings': projectSettings,
|
|
40
|
+
encryption
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
module.exports = services;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../../server/src/services/index.ts"],"sourcesContent":["// NOTE: Make sure to use default export for services overwritten in EE\nimport auth from './auth';\nimport user from './user';\nimport role from './role';\nimport passport from './passport';\nimport metrics from './metrics';\nimport * as token from './token';\nimport * as permission from './permission';\nimport * as contentType from './content-type';\nimport * as constants from './constants';\nimport * as condition from './condition';\nimport * as action from './action';\nimport * as apiToken from './api-token';\nimport * as transfer from './transfer';\nimport * as projectSettings from './project-settings';\n\n// TODO: TS - Export services one by one as this export is cjs\nexport default {\n auth,\n user,\n role,\n passport,\n token,\n permission,\n metrics,\n 'content-type': contentType,\n constants,\n condition,\n action,\n 'api-token': apiToken,\n transfer,\n 'project-settings': projectSettings,\n};\n"],"names":["auth","user","role","passport","token","permission","metrics","contentType","constants","condition","action","apiToken","transfer","projectSettings"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../server/src/services/index.ts"],"sourcesContent":["// NOTE: Make sure to use default export for services overwritten in EE\nimport auth from './auth';\nimport user from './user';\nimport role from './role';\nimport passport from './passport';\nimport metrics from './metrics';\nimport encryption from './encryption';\nimport * as token from './token';\nimport * as permission from './permission';\nimport * as contentType from './content-type';\nimport * as constants from './constants';\nimport * as condition from './condition';\nimport * as action from './action';\nimport * as apiToken from './api-token';\nimport * as transfer from './transfer';\nimport * as projectSettings from './project-settings';\n\n// TODO: TS - Export services one by one as this export is cjs\nexport default {\n auth,\n user,\n role,\n passport,\n token,\n permission,\n metrics,\n 'content-type': contentType,\n constants,\n condition,\n action,\n 'api-token': apiToken,\n transfer,\n 'project-settings': projectSettings,\n encryption,\n};\n"],"names":["auth","user","role","passport","token","permission","metrics","contentType","constants","condition","action","apiToken","transfer","projectSettings","encryption"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAiBA;AACA,eAAe;AACbA,IAAAA,IAAAA;AACAC,IAAAA,IAAAA;AACAC,IAAAA,IAAAA;AACAC,IAAAA,QAAAA;AACAC,IAAAA,KAAAA;AACAC,IAAAA,UAAAA;AACAC,IAAAA,OAAAA;IACA,cAAgBC,EAAAA,WAAAA;AAChBC,eAAAA,oBAAAA;AACAC,IAAAA,SAAAA;AACAC,IAAAA,MAAAA;IACA,WAAaC,EAAAA,QAAAA;AACbC,cAAAA,KAAAA;IACA,kBAAoBC,EAAAA,eAAAA;AACpBC,IAAAA;AACF,CAAE;;;;"}
|
|
@@ -3,6 +3,7 @@ import user from './user.mjs';
|
|
|
3
3
|
import role from './role.mjs';
|
|
4
4
|
import passport from './passport.mjs';
|
|
5
5
|
import metrics from './metrics.mjs';
|
|
6
|
+
import encryption from './encryption.mjs';
|
|
6
7
|
import * as token from './token.mjs';
|
|
7
8
|
import * as permission from './permission.mjs';
|
|
8
9
|
import * as contentType from './content-type.mjs';
|
|
@@ -29,7 +30,8 @@ var services = {
|
|
|
29
30
|
action,
|
|
30
31
|
'api-token': apiToken,
|
|
31
32
|
transfer: index,
|
|
32
|
-
'project-settings': projectSettings
|
|
33
|
+
'project-settings': projectSettings,
|
|
34
|
+
encryption
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
export { services as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../../../../server/src/services/index.ts"],"sourcesContent":["// NOTE: Make sure to use default export for services overwritten in EE\nimport auth from './auth';\nimport user from './user';\nimport role from './role';\nimport passport from './passport';\nimport metrics from './metrics';\nimport * as token from './token';\nimport * as permission from './permission';\nimport * as contentType from './content-type';\nimport * as constants from './constants';\nimport * as condition from './condition';\nimport * as action from './action';\nimport * as apiToken from './api-token';\nimport * as transfer from './transfer';\nimport * as projectSettings from './project-settings';\n\n// TODO: TS - Export services one by one as this export is cjs\nexport default {\n auth,\n user,\n role,\n passport,\n token,\n permission,\n metrics,\n 'content-type': contentType,\n constants,\n condition,\n action,\n 'api-token': apiToken,\n transfer,\n 'project-settings': projectSettings,\n};\n"],"names":["auth","user","role","passport","token","permission","metrics","contentType","constants","condition","action","apiToken","transfer","projectSettings"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../../../../server/src/services/index.ts"],"sourcesContent":["// NOTE: Make sure to use default export for services overwritten in EE\nimport auth from './auth';\nimport user from './user';\nimport role from './role';\nimport passport from './passport';\nimport metrics from './metrics';\nimport encryption from './encryption';\nimport * as token from './token';\nimport * as permission from './permission';\nimport * as contentType from './content-type';\nimport * as constants from './constants';\nimport * as condition from './condition';\nimport * as action from './action';\nimport * as apiToken from './api-token';\nimport * as transfer from './transfer';\nimport * as projectSettings from './project-settings';\n\n// TODO: TS - Export services one by one as this export is cjs\nexport default {\n auth,\n user,\n role,\n passport,\n token,\n permission,\n metrics,\n 'content-type': contentType,\n constants,\n condition,\n action,\n 'api-token': apiToken,\n transfer,\n 'project-settings': projectSettings,\n encryption,\n};\n"],"names":["auth","user","role","passport","token","permission","metrics","contentType","constants","condition","action","apiToken","transfer","projectSettings","encryption"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAiBA;AACA,eAAe;AACbA,IAAAA,IAAAA;AACAC,IAAAA,IAAAA;AACAC,IAAAA,IAAAA;AACAC,IAAAA,QAAAA;AACAC,IAAAA,KAAAA;AACAC,IAAAA,UAAAA;AACAC,IAAAA,OAAAA;IACA,cAAgBC,EAAAA,WAAAA;AAChBC,IAAAA,SAAAA;AACAC,IAAAA,SAAAA;AACAC,IAAAA,MAAAA;IACA,WAAaC,EAAAA,QAAAA;AACbC,cAAAA,KAAAA;IACA,kBAAoBC,EAAAA,eAAAA;AACpBC,IAAAA;AACF,CAAE;;;;"}
|
|
@@ -45,6 +45,13 @@ declare const _default: {
|
|
|
45
45
|
required: boolean;
|
|
46
46
|
searchable: boolean;
|
|
47
47
|
};
|
|
48
|
+
encryptedKey: {
|
|
49
|
+
type: string;
|
|
50
|
+
minLength: number;
|
|
51
|
+
configurable: boolean;
|
|
52
|
+
required: boolean;
|
|
53
|
+
searchable: boolean;
|
|
54
|
+
};
|
|
48
55
|
lastUsedAt: {
|
|
49
56
|
type: string;
|
|
50
57
|
configurable: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-token.d.ts","sourceRoot":"","sources":["../../../../server/src/content-types/api-token.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-token.d.ts","sourceRoot":"","sources":["../../../../server/src/content-types/api-token.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,wBA8EE"}
|
|
@@ -269,6 +269,13 @@ declare const _default: {
|
|
|
269
269
|
required: boolean;
|
|
270
270
|
searchable: boolean;
|
|
271
271
|
};
|
|
272
|
+
encryptedKey: {
|
|
273
|
+
type: string;
|
|
274
|
+
minLength: number;
|
|
275
|
+
configurable: boolean;
|
|
276
|
+
required: boolean;
|
|
277
|
+
searchable: boolean;
|
|
278
|
+
};
|
|
272
279
|
lastUsedAt: {
|
|
273
280
|
type: string;
|
|
274
281
|
configurable: boolean;
|