Merge branch 'cloudron-io-master'
This commit is contained in:
commit
3fcd389db8
8 changed files with 209 additions and 76 deletions
4
app.js
4
app.js
|
@ -135,6 +135,10 @@ passport.setup(app);
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
res.locals.flash = req.flash.bind(req);
|
res.locals.flash = req.flash.bind(req);
|
||||||
res.locals.user = req.user;
|
res.locals.user = req.user;
|
||||||
|
res.locals.ldap = {
|
||||||
|
enabled: config.ldap.enabled,
|
||||||
|
passwordresetlink: config.ldap.passwordresetlink
|
||||||
|
};
|
||||||
|
|
||||||
let menu = [{
|
let menu = [{
|
||||||
title: 'Home',
|
title: 'Home',
|
||||||
|
|
|
@ -87,3 +87,12 @@ host="0.0.0.0"
|
||||||
username="testuser"
|
username="testuser"
|
||||||
password="testpass"
|
password="testpass"
|
||||||
logger=false
|
logger=false
|
||||||
|
|
||||||
|
[ldap]
|
||||||
|
# enable to use ldap user backend
|
||||||
|
enabled=false
|
||||||
|
host="localhost"
|
||||||
|
port=3002
|
||||||
|
baseDN="ou=users,dc=company"
|
||||||
|
filter="(|(username={{username}})(mail={{username}}))"
|
||||||
|
passwordresetlink=""
|
||||||
|
|
|
@ -61,6 +61,52 @@ module.exports.findByAccessToken = (accessToken, callback) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.findByUsername = (username, callback) => {
|
||||||
|
db.getConnection((err, connection) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.query('SELECT `id`, `username`, `email`, `access_token` FROM `users` WHERE `username`=? LIMIT 1', [username], (err, rows) => {
|
||||||
|
connection.release();
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rows.length) {
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = tools.convertKeys(rows[0]);
|
||||||
|
return callback(null, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.add = (username, password, email, callback) => {
|
||||||
|
db.getConnection((err, connection) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.query('INSERT INTO `users` (`username`, `password`, `email`, `created`) VALUES (?, ?, ?, NOW())', [username, password, email], (err, result) => {
|
||||||
|
connection.release();
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = result && result.insertId;
|
||||||
|
if (!id) {
|
||||||
|
return callback(new Error('Could not store user row'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches user by username and password
|
* Fetches user by username and password
|
||||||
*
|
*
|
||||||
|
@ -70,6 +116,10 @@ module.exports.findByAccessToken = (accessToken, callback) => {
|
||||||
*/
|
*/
|
||||||
module.exports.authenticate = (username, password, callback) => {
|
module.exports.authenticate = (username, password, callback) => {
|
||||||
|
|
||||||
|
if (password === '') {
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
let login = (connection, callback) => {
|
let login = (connection, callback) => {
|
||||||
connection.query('SELECT `id`, `password`, `access_token` FROM `users` WHERE `username`=? OR email=? LIMIT 1', [username, username], (err, rows) => {
|
connection.query('SELECT `id`, `password`, `access_token` FROM `users` WHERE `username`=? OR email=? LIMIT 1', [username, username], (err, rows) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
let config = require('config');
|
let config = require('config');
|
||||||
|
let log = require('npmlog');
|
||||||
|
|
||||||
let passport = require('passport');
|
let passport = require('passport');
|
||||||
let LocalStrategy = require('passport-local').Strategy;
|
let LocalStrategy = require('passport-local').Strategy;
|
||||||
|
let LdapStrategy = require('passport-ldapjs').Strategy;
|
||||||
let csrf = require('csurf');
|
let csrf = require('csurf');
|
||||||
let bodyParser = require('body-parser');
|
let bodyParser = require('body-parser');
|
||||||
let users = require('./models/users');
|
let users = require('./models/users');
|
||||||
|
@ -30,7 +33,7 @@ module.exports.logout = (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.login = (req, res, next) => {
|
module.exports.login = (req, res, next) => {
|
||||||
passport.authenticate('local', (err, user, info) => {
|
passport.authenticate(config.ldap.enabled ? 'ldap' : 'local', (err, user, info) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
req.flash('danger', err.message);
|
req.flash('danger', err.message);
|
||||||
return next(err);
|
return next(err);
|
||||||
|
@ -58,21 +61,66 @@ module.exports.login = (req, res, next) => {
|
||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
passport.use(new LocalStrategy((username, password, done) => {
|
if (config.ldap.enabled) {
|
||||||
users.authenticate(username, password, (err, user) => {
|
log.info('Using LDAP auth');
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user) {
|
var opts = {
|
||||||
return done(null, false, {
|
server: {
|
||||||
message: 'Incorrect username or password'
|
url: 'ldap://' + config.ldap.host + ':' + config.ldap.port,
|
||||||
});
|
},
|
||||||
|
base: config.ldap.baseDN,
|
||||||
|
search: {
|
||||||
|
filter: config.ldap.filter,
|
||||||
|
attributes: ['username', 'mail'],
|
||||||
|
scope: 'sub'
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return done(null, user);
|
passport.use(new LdapStrategy(opts, function (profile, done) {
|
||||||
});
|
users.findByUsername(profile.username, (err, user) => {
|
||||||
}));
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
// password is empty for ldap
|
||||||
|
users.add(profile.username, '', profile.mail, (err, id) => {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return done(null, {
|
||||||
|
id: id,
|
||||||
|
username: profile.username
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return done(null, {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
log.info('Using local auth');
|
||||||
|
|
||||||
|
passport.use(new LocalStrategy((username, password, done) => {
|
||||||
|
users.authenticate(username, password, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return done(null, false, {
|
||||||
|
message: 'Incorrect username or password'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return done(null, user);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
passport.serializeUser((user, done) => {
|
passport.serializeUser((user, done) => {
|
||||||
done(null, user.id);
|
done(null, user.id);
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
"npmlog": "^4.0.0",
|
"npmlog": "^4.0.0",
|
||||||
"openpgp": "^2.3.3",
|
"openpgp": "^2.3.3",
|
||||||
"passport": "^0.3.2",
|
"passport": "^0.3.2",
|
||||||
|
"passport-ldapjs": "^1.0.2",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"request": "^2.74.0",
|
"request": "^2.74.0",
|
||||||
"serve-favicon": "^2.3.0",
|
"serve-favicon": "^2.3.0",
|
||||||
|
|
|
@ -7,58 +7,66 @@
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<form class="form-horizontal" method="post" action="/users/account">
|
{{#if ldap.enabled}}
|
||||||
|
<p>
|
||||||
|
This account is managed through LDAP.<br/>
|
||||||
|
<br/>
|
||||||
|
Associated Email Address: <a href="mailto:{{email}}">{{email}}</a>
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<form class="form-horizontal" method="post" action="/users/account">
|
||||||
|
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
General Settings
|
General Settings
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email" class="col-sm-2 control-label">Email Address</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="email" class="form-control" name="email" id="email" value="{{email}}" placeholder="Your e-mail address" required>
|
||||||
|
<span class="help-block">This address is used for account recovery in case you loose your password</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
Password change
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You only need to fill out this form if you want to change your current password
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="currentPassword" class="col-sm-2 control-label">Current Password</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="password" class="form-control" name="currentPassword" id="currentPassword" placeholder="Current Password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="col-sm-2 control-label">New Password</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="password" class="form-control" name="password" id="password" placeholder="New Password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password2" class="col-sm-2 control-label">Confirm Password</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="password" class="form-control" name="password2" id="password2" placeholder="Confirm New Password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email" class="col-sm-2 control-label">Email Address</label>
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="col-sm-10">
|
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
||||||
<input type="email" class="form-control" name="email" id="email" value="{{email}}" placeholder="Your e-mail address" required>
|
|
||||||
<span class="help-block">This address is used for account recovery in case you loose your password</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</fieldset>
|
</form>
|
||||||
<fieldset>
|
{{/if}}
|
||||||
<legend>
|
|
||||||
Password change
|
|
||||||
</legend>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
You only need to fill out this form if you want to change your current password
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="currentPassword" class="col-sm-2 control-label">Current Password</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input type="password" class="form-control" name="currentPassword" id="currentPassword" placeholder="Current Password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password" class="col-sm-2 control-label">New Password</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input type="password" class="form-control" name="password" id="password" placeholder="New Password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="password2" class="col-sm-2 control-label">Confirm Password</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input type="password" class="form-control" name="password2" id="password2" placeholder="Confirm New Password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
|
||||||
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-ok"></i> Update</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
|
@ -8,22 +8,30 @@
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<p>Please provide the username or email address that you used when you signed up for your Mailtrain account.</p>
|
{{#if ldap.enabled}}
|
||||||
<p>We will send you an email that will allow you to reset your password.</p>
|
<p>
|
||||||
|
Accounts are managed through LDAP.<br/>
|
||||||
|
<br/>
|
||||||
|
<a href="{{ldap.passwordresetlink}}">Reset Password</a>
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<p>Please provide the username or email address that you used when you signed up for your Mailtrain account.</p>
|
||||||
|
<p>We will send you an email that will allow you to reset your password.</p>
|
||||||
|
|
||||||
<form class="form-horizontal" role="login" method="post" action="/users/forgot">
|
<form class="form-horizontal" role="login" method="post" action="/users/forgot">
|
||||||
|
|
||||||
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="usernameMain" class="col-sm-2 control-label">Username</label>
|
<label for="usernameMain" class="col-sm-2 control-label">Username</label>
|
||||||
<div class="col-xs-4">
|
<div class="col-xs-4">
|
||||||
<input type="text" class="form-control" name="username" id="usernameMain" placeholder="Username or email address" autofocus required>
|
<input type="text" class="form-control" name="username" id="usernameMain" placeholder="Username or email address" autofocus required>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<button type="submit" class="btn btn-primary">Send verification email</button>
|
||||||
<button type="submit" class="btn btn-primary">Send verification email</button>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</form>
|
{{/if}}
|
||||||
|
|
|
@ -32,7 +32,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-xs-4">
|
<div class="col-sm-offset-2 col-xs-4">
|
||||||
<button type="submit" class="btn btn-primary">Sign in</button> or <a href="/users/forgot">Forgot password?</a>
|
<button type="submit" class="btn btn-primary">Sign in</button> or
|
||||||
|
{{#if ldap.enabled}}
|
||||||
|
<a href="{{ldap.passwordresetlink}}">Forgot password?</a>
|
||||||
|
{{else}}
|
||||||
|
<a href="/users/forgot">Forgot password?</a>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue