1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-08-05 13:35:27 +02:00

feat: Add ldap authentication

This commit is contained in:
Erwan Boisard 2022-06-29 10:58:42 +02:00
parent ddf9a32ea9
commit 9cf7cbe0ca
18 changed files with 16581 additions and 7321 deletions

16551
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -63,16 +63,23 @@
}
},
"dependencies": {
"assert": "^2.0.0",
"buffer": "^6.0.3",
"classnames": "^2.2.6",
"connected-react-router": "^6.9.2",
"date-fns": "^2.28.0",
"dequal": "^2.0.2",
"dtrace-provider": "^0.8.8",
"express": "^4.18.1",
"history": "^4.10.1",
"i18next": "^21.6.16",
"i18next-browser-languagedetector": "^6.1.4",
"initials": "^3.1.2",
"js-cookie": "^3.0.1",
"ldap-authentication": "^2.2.9",
"ldapjs": "^2.3.2",
"lodash": "^4.17.20",
"net": "^1.0.2",
"node-sass": "^7.0.1",
"photoswipe": "^5.2.7",
"prop-types": "^15.8.1",
@ -99,7 +106,11 @@
"sails.io.js": "^1.2.1",
"semantic-ui-react": "^2.1.2",
"socket.io-client": "^2.3.1",
"stream-browserify": "^3.0.0",
"tls": "^0.0.1",
"url": "^0.11.0",
"validator": "^13.7.0",
"webcrypto": "^0.1.1",
"whatwg-fetch": "^3.5.0"
},
"devDependencies": {
@ -119,4 +130,4 @@
"prettier": "2.6.2",
"react-test-renderer": "^17.0.2"
}
}
}

View file

@ -28,6 +28,11 @@ const createMessage = (error) => {
type: 'error',
content: 'common.invalidPassword',
};
case 'Ldap authentication failed':
return {
type: 'error',
content: 'common.invalidLdap',
};
case 'Failed to fetch':
return {
type: 'warning',

View file

@ -27,6 +27,11 @@ const createMessage = (error) => {
type: 'error',
content: 'common.invalidCurrentPassword',
};
case 'Action not possible':
return {
type: 'error',
content: 'common.impossibleAction',
};
default:
return {
type: 'warning',

View file

@ -21,6 +21,11 @@ const createMessage = (error) => {
type: 'error',
content: 'common.invalidCurrentPassword',
};
case 'Action not possible':
return {
type: 'error',
content: 'common.impossibleAction',
};
default:
return {
type: 'warning',

View file

@ -88,6 +88,7 @@ export default {
fromComputer_title: 'From Computer',
general: 'General',
hours: 'Hours',
impossibleAction: 'Action not possible',
invalidCurrentPassword: 'Invalid current password',
labels: 'Labels',
leaveBoard_title: 'Leave Board',

View file

@ -3,6 +3,7 @@ export default {
common: {
emailOrUsername: 'E-mail or username',
invalidEmailOrUsername: 'Invalid e-mail or username',
invalidLdap: 'Ldap connection failure',
invalidPassword: 'Invalid password',
logInToPlanka: 'Log in to Planka',
noInternetConnection: 'No internet connection',

View file

@ -83,6 +83,7 @@ export default {
filterByMembers_title: 'Filtrer par membres',
fromComputer_title: "Depuis l'ordinateur",
hours: 'Les heures',
impossibleAction: 'Action impossible',
invalidCurrentPassword: 'Mot de passe actuel invalide',
labels: 'Étiquettes',
list: 'Lister',

View file

@ -3,6 +3,7 @@ export default {
common: {
emailOrUsername: "Email ou nom d'utilisateur",
invalidEmailOrUsername: "Email ou nom d'utilisateur invalide",
invalidLdap: "Connexion à l'AD échouée",
invalidPassword: 'Mot de passe invalide',
logInToPlanka: 'Se connecter à Planka',
noInternetConnection: 'Aucune connection internet',

4755
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -39,8 +39,18 @@
]
},
"dependencies": {
"buffer": "^6.0.3",
"concurrently": "^7.1.0",
"husky": "^7.0.4",
"lint-staged": "^12.3.8"
"knex": "^2.1.0",
"ldap-authentication": "^2.2.9",
"ldapjs": "^2.3.2",
"lint-staged": "^12.3.8",
"net": "^1.0.2",
"node": "^17.7.2",
"node-sass": "^7.0.1",
"path": "^0.12.7",
"stream-browserify": "^3.0.0",
"url": "^0.11.0"
}
}

View file

@ -2,3 +2,4 @@ TZ=UTC
BASE_URL=http://localhost:1337
DATABASE_URL=postgresql://postgres@localhost/planka
SECRET_KEY=notsecretkey
LDAP_SERVER=

View file

@ -1,5 +1,8 @@
const bcrypt = require('bcrypt');
const validator = require('validator');
const ldap = require('ldapjs');
const createUser = require('../users/create');
const { NULL } = require('node-sass');
const Errors = {
INVALID_EMAIL_OR_USERNAME: {
@ -8,6 +11,9 @@ const Errors = {
INVALID_PASSWORD: {
invalidPassword: 'Invalid password',
},
INVALID_LDAP: {
invalidLdap: 'Ldap authentication failed',
},
};
module.exports = {
@ -35,21 +41,102 @@ module.exports = {
invalidPassword: {
responseType: 'unauthorized',
},
invalidLdap: {
responseType: 'unauthorized',
},
},
async fn(inputs) {
const user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
if (!user) {
throw Errors.INVALID_EMAIL_OR_USERNAME;
}
if(process.env.LDAP_SERVER){
console.log('AUTH mode : LDAP');
if (!bcrypt.compareSync(inputs.password, user.password)) {
throw Errors.INVALID_PASSWORD;
}
const server = process.env.LDAP_SERVER;
const client = ldap.createClient({
url: `ldap://${server}`
});
var token_value = new Promise((resolve) => { client.bind(inputs.emailOrUsername, inputs.password, async (err) => {
var user;
var token;
if(!err){
console.log('AD connection success');
user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
if (!user) {
console.log('Non-existent Planka user: creation in progress');
await createUser.fn({
"email": inputs.emailOrUsername,
"password": inputs.password,
"isAdmin": false,
"name": inputs.emailOrUsername,
"subscribeToOwnCards": false,
"createdAt": "date",
"updatedAt": "date"
});
user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
}
token = await sails.helpers.utils.signToken(user.id);
resolve(token);
}if(err){
console.log('AD connection failure');
token = '';
resolve(token);
}
})});
// ADMIN CONNEXION
if (await token_value==''){
if(inputs.emailOrUsername=='admin' || inputs.emailOrUsername=='admin@admin.admin') {
console.log('ADMIN CONNEXION');
var user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
if (!user) {
throw Errors.INVALID_EMAIL_OR_USERNAME;
}
if (!bcrypt.compareSync(inputs.password, user.password)) {
throw Errors.INVALID_PASSWORD;
}
return {
item: sails.helpers.utils.signToken(user.id),
};
}
throw Errors.INVALID_LDAP;
}
return {
item: await token_value,
};
}else{ // NO LDAP AUTH in .env
console.log('AUTH mode : Normal DB');
const user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
if (!user) {
throw Errors.INVALID_EMAIL_OR_USERNAME;
}
if (!bcrypt.compareSync(inputs.password, user.password)) {
throw Errors.INVALID_PASSWORD;
}
return {
item: sails.helpers.utils.signToken(user.id),
};
}
return {
item: sails.helpers.utils.signToken(user.id),
};
},
};
};

View file

@ -10,6 +10,9 @@ const Errors = {
EMAIL_ALREADY_IN_USE: {
emailAlreadyInUse: 'Email already in use',
},
IMPOSSIBLE_ACTION: {
impossibleAction: 'Action not possible',
},
};
module.exports = {
@ -40,44 +43,52 @@ module.exports = {
emailAlreadyInUse: {
responseType: 'conflict',
},
impossibleAction: {
responseType: 'forbidden',
},
},
async fn(inputs) {
const { currentUser } = this.req;
if (inputs.id === currentUser.id) {
if (!inputs.currentPassword) {
if(!process.env.LDAP_SERVER){
const { currentUser } = this.req;
if (inputs.id === currentUser.id) {
if (!inputs.currentPassword) {
throw Errors.INVALID_CURRENT_PASSWORD;
}
} else if (!currentUser.isAdmin) {
throw Errors.USER_NOT_FOUND; // Forbidden
}
let user = await sails.helpers.users.getOne(inputs.id);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
if (
inputs.id === currentUser.id &&
!bcrypt.compareSync(inputs.currentPassword, user.password)
) {
throw Errors.INVALID_CURRENT_PASSWORD;
}
} else if (!currentUser.isAdmin) {
throw Errors.USER_NOT_FOUND; // Forbidden
const values = _.pick(inputs, ['email']);
user = await sails.helpers.users
.updateOne(user, values, this.req)
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
return {
item: user,
};
}
let user = await sails.helpers.users.getOne(inputs.id);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
if (
inputs.id === currentUser.id &&
!bcrypt.compareSync(inputs.currentPassword, user.password)
) {
throw Errors.INVALID_CURRENT_PASSWORD;
}
const values = _.pick(inputs, ['email']);
user = await sails.helpers.users
.updateOne(user, values, this.req)
.intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
return {
item: user,
};
throw Errors.IMPOSSIBLE_ACTION;
},
};

View file

@ -7,6 +7,9 @@ const Errors = {
INVALID_CURRENT_PASSWORD: {
invalidCurrentPassword: 'Invalid current password',
},
IMPOSSIBLE_ACTION: {
impossibleAction: 'Action not possible',
},
};
module.exports = {
@ -33,41 +36,47 @@ module.exports = {
invalidCurrentPassword: {
responseType: 'forbidden',
},
impossibleAction: {
responseType: 'forbidden',
},
},
async fn(inputs) {
const { currentUser } = this.req;
if(!process.env.LDAP_SERVER){
const { currentUser } = this.req;
if (inputs.id === currentUser.id) {
if (!inputs.currentPassword) {
if (inputs.id === currentUser.id) {
if (!inputs.currentPassword) {
throw Errors.INVALID_CURRENT_PASSWORD;
}
} else if (!currentUser.isAdmin) {
throw Errors.USER_NOT_FOUND; // Forbidden
}
let user = await sails.helpers.users.getOne(inputs.id);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
if (
inputs.id === currentUser.id &&
!bcrypt.compareSync(inputs.currentPassword, user.password)
) {
throw Errors.INVALID_CURRENT_PASSWORD;
}
} else if (!currentUser.isAdmin) {
throw Errors.USER_NOT_FOUND; // Forbidden
const values = _.pick(inputs, ['password']);
user = await sails.helpers.users.updateOne(user, values, this.req);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
return {
item: user,
};
}
let user = await sails.helpers.users.getOne(inputs.id);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
if (
inputs.id === currentUser.id &&
!bcrypt.compareSync(inputs.currentPassword, user.password)
) {
throw Errors.INVALID_CURRENT_PASSWORD;
}
const values = _.pick(inputs, ['password']);
user = await sails.helpers.users.updateOne(user, values, this.req);
if (!user) {
throw Errors.USER_NOT_FOUND;
}
return {
item: user,
};
throw Errors.IMPOSSIBLE_ACTION;
},
};

View file

@ -63,7 +63,8 @@ module.exports = {
if (
inputs.id === currentUser.id &&
!bcrypt.compareSync(inputs.currentPassword, user.password)
!bcrypt.compareSync(inputs.currentPassword, user.password) &&
!process.env.LDAP_SERVER
) {
throw Errors.INVALID_CURRENT_PASSWORD;
}

2293
server/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -44,6 +44,8 @@
"filenamify": "^4.3.0",
"jsonwebtoken": "^8.5.1",
"knex": "^1.0.7",
"ldap-authentication": "^2.2.9",
"ldapjs": "^2.3.2",
"lodash": "^4.17.21",
"moment": "^2.29.3",
"rimraf": "^3.0.2",
@ -71,4 +73,4 @@
"engines": {
"node": "^12.10"
}
}
}