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:
parent
ddf9a32ea9
commit
9cf7cbe0ca
18 changed files with 16581 additions and 7321 deletions
16551
client/package-lock.json
generated
16551
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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": {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
4755
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,3 +2,4 @@ TZ=UTC
|
|||
BASE_URL=http://localhost:1337
|
||||
DATABASE_URL=postgresql://postgres@localhost/planka
|
||||
SECRET_KEY=notsecretkey
|
||||
LDAP_SERVER=
|
||||
|
|
|
@ -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');
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (!bcrypt.compareSync(inputs.password, user.password)) {
|
||||
throw Errors.INVALID_PASSWORD;
|
||||
}
|
||||
|
||||
return {
|
||||
item: sails.helpers.utils.signToken(user.id),
|
||||
};
|
||||
|
||||
},
|
||||
};
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
throw Errors.IMPOSSIBLE_ACTION;
|
||||
|
||||
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,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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
2293
server/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue