feat: Add API key authentication (#1254)

Closes #945
This commit is contained in:
Samuel
2025-11-06 20:56:48 +01:00
committed by GitHub
parent 5a2564f575
commit b4cbd32bf2
75 changed files with 1501 additions and 94 deletions

View File

@@ -23,8 +23,10 @@ module.exports = {
..._.omit(inputs.record, [
'password',
'avatar',
'apiKeyHash',
'termsSignature',
'passwordChangedAt',
'apiKeyCreatedAt',
'termsAcceptedAt',
]),
avatar: inputs.record.avatar && {

View File

@@ -38,13 +38,23 @@ module.exports = {
values.email = values.email.toLowerCase();
}
let isOnlyEmailChange = false;
let isOnlyPasswordChange = false;
if (_.isNull(values.apiKey)) {
Object.assign(values, {
apiKeyPrefix: null,
apiKeyHash: null,
apiKeyCreatedAt: null,
});
delete values.apiKey;
}
let isOnlyPrivateFieldsChange = false;
let isOnlyPersonalFieldsChange = false;
let isOnlyPasswordChange = false;
let isDeactivatedChangeToTrue = false;
if (!_.isUndefined(values.email) && Object.keys(values).length === 1) {
isOnlyEmailChange = true;
if (_.difference(Object.keys(values), User.PRIVATE_FIELD_NAMES).length === 0) {
isOnlyPrivateFieldsChange = true;
}
if (_.difference(Object.keys(values), User.PERSONAL_FIELD_NAMES).length === 0) {
@@ -64,6 +74,10 @@ module.exports = {
values.username = values.username.toLowerCase();
}
if (values.apiKeyHash) {
values.apiKeyCreatedAt = new Date().toISOString();
}
if (values.isDeactivated && values.isDeactivated !== inputs.record.isDeactivated) {
isDeactivatedChangeToTrue = true;
}
@@ -154,7 +168,7 @@ module.exports = {
);
});
if (!isOnlyEmailChange) {
if (!isOnlyPrivateFieldsChange) {
if (inputs.record.role === User.Roles.ADMIN && user.role !== User.Roles.ADMIN) {
const managerProjectIds = await sails.helpers.users.getManagerProjectIds(user.id);

View File

@@ -0,0 +1,19 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
module.exports = {
sync: true,
fn() {
const prefix = sails.helpers.utils.generateRandomString(8);
const secret = sails.helpers.utils.generateRandomString(32);
const key = `${prefix}_${secret}`;
return {
key,
prefix,
};
},
};

View File

@@ -0,0 +1,30 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const crypto = require('crypto');
const CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
module.exports = {
sync: true,
inputs: {
size: {
type: 'number',
required: true,
},
},
fn(inputs) {
const bytes = crypto.randomBytes(inputs.size);
let result = '';
for (let i = 0; i < inputs.size; i += 1) {
result += CHARS[bytes[i] % 62];
}
return result;
},
};

View File

@@ -0,0 +1,21 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const crypto = require('crypto');
module.exports = {
sync: true,
inputs: {
data: {
type: 'ref',
required: true,
},
},
fn(inputs) {
return crypto.createHash('sha256').update(inputs.data).digest('hex');
},
};