1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-24 07:39:44 +02:00

feat: Version 2

Closes #627, closes #1047
This commit is contained in:
Maksim Eltyshev 2025-05-10 02:09:06 +02:00
parent ad7fb51cfa
commit 2ee1166747
1557 changed files with 76832 additions and 47042 deletions

View file

@ -1,8 +1,13 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const bcrypt = require('bcrypt');
const validator = require('validator');
const { v4: uuid } = require('uuid');
const { getRemoteAddress } = require('../../../utils/remoteAddress');
const { isEmailOrUsername } = require('../../../utils/validators');
const { getRemoteAddress } = require('../../../utils/remote-address');
const Errors = {
INVALID_CREDENTIALS: {
@ -19,20 +24,17 @@ const Errors = {
},
};
const emailOrUsernameValidator = (value) =>
value.includes('@')
? validator.isEmail(value)
: value.length >= 3 && value.length <= 16 && /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/.test(value);
module.exports = {
inputs: {
emailOrUsername: {
type: 'string',
custom: emailOrUsernameValidator,
maxLength: 256,
custom: isEmailOrUsername,
required: true,
},
password: {
type: 'string',
maxLength: 256,
required: true,
},
withHttpOnlyToken: {
@ -62,7 +64,7 @@ module.exports = {
}
const remoteAddress = getRemoteAddress(this.req);
const user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
const user = await User.qm.getOneActiveByEmailOrUsername(inputs.emailOrUsername);
if (!user) {
sails.log.warn(
@ -74,11 +76,13 @@ module.exports = {
: Errors.INVALID_CREDENTIALS;
}
if (user.isSso) {
if (user.isSsoUser) {
throw Errors.USE_SINGLE_SIGN_ON;
}
if (!bcrypt.compareSync(inputs.password, user.password)) {
const isPasswordValid = await bcrypt.compare(inputs.password, user.password);
if (!isPasswordValid) {
sails.log.warn(`Invalid password! (IP: ${remoteAddress})`);
throw sails.config.custom.showDetailedAuthErrors
@ -92,7 +96,7 @@ module.exports = {
const httpOnlyToken = inputs.withHttpOnlyToken ? uuid() : null;
await Session.create({
await Session.qm.createOne({
accessToken,
httpOnlyToken,
remoteAddress,

View file

@ -1,13 +1,13 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
module.exports = {
async fn() {
const { currentSession } = this.req;
await Session.updateOne({
id: currentSession.id,
deletedAt: null,
}).set({
deletedAt: new Date().toISOString(),
});
await Session.qm.deleteOneById(currentSession.id);
sails.sockets.leaveAll(`@accessToken:${currentSession.accessToken}`);

View file

@ -1,10 +1,15 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
const { v4: uuid } = require('uuid');
const { getRemoteAddress } = require('../../../utils/remoteAddress');
const { getRemoteAddress } = require('../../../utils/remote-address');
const Errors = {
INVALID_OIDC_CONFIGURATION: {
invalidOIDCConfiguration: 'Invalid OIDC configuration',
invalidOidcConfiguration: 'Invalid OIDC configuration',
},
INVALID_CODE_OR_NONCE: {
invalidCodeOrNonce: 'Invalid code or nonce',
@ -18,6 +23,9 @@ const Errors = {
USERNAME_ALREADY_IN_USE: {
usernameAlreadyInUse: 'Username already in use',
},
ACTIVE_USERS_LIMIT_REACHED: {
activeUsersLimitReached: 'Active users limit reached',
},
MISSING_VALUES: {
missingValues: 'Unable to retrieve required values (email, name)',
},
@ -27,10 +35,12 @@ module.exports = {
inputs: {
code: {
type: 'string',
maxLength: 1024,
required: true,
},
nonce: {
type: 'string',
maxLength: 1024,
required: true,
},
withHttpOnlyToken: {
@ -40,7 +50,7 @@ module.exports = {
},
exits: {
invalidOIDCConfiguration: {
invalidOidcConfiguration: {
responseType: 'serverError',
},
invalidCodeOrNonce: {
@ -55,6 +65,9 @@ module.exports = {
usernameAlreadyInUse: {
responseType: 'conflict',
},
activeUsersLimitReached: {
responseType: 'conflict',
},
missingValues: {
responseType: 'unprocessableEntity',
},
@ -64,15 +77,16 @@ module.exports = {
const remoteAddress = getRemoteAddress(this.req);
const user = await sails.helpers.users
.getOrCreateOneUsingOidc(inputs.code, inputs.nonce)
.getOrCreateOneWithOidc(inputs.code, inputs.nonce)
.intercept('invalidOidcConfiguration', () => Errors.INVALID_OIDC_CONFIGURATION)
.intercept('invalidCodeOrNonce', () => {
sails.log.warn(`Invalid code or nonce! (IP: ${remoteAddress})`);
return Errors.INVALID_CODE_OR_NONCE;
})
.intercept('invalidOIDCConfiguration', () => Errors.INVALID_OIDC_CONFIGURATION)
.intercept('invalidUserinfoConfiguration', () => Errors.INVALID_USERINFO_CONFIGURATION)
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE)
.intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE)
.intercept('activeLimitReached', () => Errors.ACTIVE_USERS_LIMIT_REACHED)
.intercept('missingValues', () => Errors.MISSING_VALUES);
const { token: accessToken, payload: accessTokenPayload } = sails.helpers.utils.createJwtToken(
@ -81,7 +95,7 @@ module.exports = {
const httpOnlyToken = inputs.withHttpOnlyToken ? uuid() : null;
await Session.create({
await Session.qm.createOne({
accessToken,
httpOnlyToken,
remoteAddress,