mirror of
https://github.com/Foltik/Shimapan
synced 2025-01-06 00:08:25 -05:00
Refactor all base auth routes, allowing for case insensitivity and unicode usernames.
This commit is contained in:
parent
4b4d32ec9c
commit
26cc9bf6ef
@ -14,4 +14,8 @@ var InviteSchema = mongoose.Schema({
|
|||||||
exp: Date
|
exp: Date
|
||||||
});
|
});
|
||||||
|
|
||||||
|
InviteSchema.methods.use = function(canonicalname, cb) {
|
||||||
|
return this.model('Invite').updateOne({code: this.code}, {recipient: canonicalname, used: Date.now()}, cb);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = mongoose.model('Invite', InviteSchema);
|
module.exports = mongoose.model('Invite', InviteSchema);
|
@ -7,6 +7,11 @@ var UserSchema = mongoose.Schema({
|
|||||||
unique: true,
|
unique: true,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
canonicalname: {
|
||||||
|
type: String,
|
||||||
|
unique: true,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
scope: [String],
|
scope: [String],
|
||||||
uploadCount: {
|
uploadCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -19,6 +24,6 @@ var UserSchema = mongoose.Schema({
|
|||||||
date: Date
|
date: Date
|
||||||
});
|
});
|
||||||
|
|
||||||
UserSchema.plugin(passportLocalMongoose);
|
UserSchema.plugin(passportLocalMongoose, {usernameField: 'canonicalname'});
|
||||||
|
|
||||||
module.exports = mongoose.model('User', UserSchema);
|
module.exports = mongoose.model('User', UserSchema);
|
@ -6,57 +6,124 @@ var Invite = require('../models/Invite.js');
|
|||||||
|
|
||||||
var passport = require('passport');
|
var passport = require('passport');
|
||||||
|
|
||||||
function checkInvite(code, callback) {
|
var async = require('async');
|
||||||
|
|
||||||
|
// Normalizes, decomposes, and lowercases a unicode string
|
||||||
|
function canonicalize(username) {
|
||||||
|
return username.normalize('NFKD').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if an invite code is valid
|
||||||
|
// Returns the invite object if valid
|
||||||
|
function checkInvite(code, cb) {
|
||||||
Invite.findOne({code: code}, function (err, invite) {
|
Invite.findOne({code: code}, function (err, invite) {
|
||||||
if (err) return callback(err);
|
if (err)
|
||||||
if (!invite || invite.used || invite.exp < new Date())
|
cb(err);
|
||||||
callback(null, false);
|
else if (!invite)
|
||||||
|
cb('Invalid invite code.');
|
||||||
|
else if (invite.used)
|
||||||
|
cb('Invite already used.');
|
||||||
|
else if (invite.exp < Date.now())
|
||||||
|
cb('Invite expired.');
|
||||||
else
|
else
|
||||||
callback(null, true, invite);
|
cb(null, invite);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function useInvite(code, username) {
|
// Validates the username, then registers the user in the database using the given invite.
|
||||||
Invite.updateOne({code: code}, {recipient: username, used: new Date()}, function (err) {
|
function registerUser(username, password, invite, sanitizeFn, cb) {
|
||||||
if (err) throw err;
|
async.series([
|
||||||
|
function (cb) {
|
||||||
|
// Canonicalize and sanitize the username, checking for HTML
|
||||||
|
var canonicalName = canonicalize(username);
|
||||||
|
var sanitizedName = sanitizeFn(canonicalName);
|
||||||
|
|
||||||
|
if (sanitizedName !== canonicalName)
|
||||||
|
cb('Username failed sanitization check.');
|
||||||
|
else if (canonicalName.length > 36)
|
||||||
|
cb('Username too long.');
|
||||||
|
else
|
||||||
|
cb(null);
|
||||||
|
},
|
||||||
|
function (cb) {
|
||||||
|
User.register(new User({
|
||||||
|
username: username,
|
||||||
|
canonicalname: canonicalize(username),
|
||||||
|
scope: invite.scope,
|
||||||
|
date: Date.now()
|
||||||
|
}), password, cb);
|
||||||
|
},
|
||||||
|
function (cb) {
|
||||||
|
invite.use(canonicalize(username), cb);
|
||||||
|
}
|
||||||
|
], function (err) {
|
||||||
|
cb(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
router.post('/register', function (req, res, next) {
|
// Authenticates and creates the required session variables
|
||||||
// Validate the invite code, then hand off to passport
|
function setupSession(username, req, res, cb) {
|
||||||
checkInvite(req.body.invite, function (err, valid, invite) {
|
// Body needs to contain canonical name for proper authentication
|
||||||
if (valid) {
|
req.body.canonicalname = canonicalize(req.body.username);
|
||||||
User.register(
|
|
||||||
new User({username: req.body.username, scope: invite.scope, date: Date.now()}),
|
passport.authenticate('local')(req, res, function () {
|
||||||
req.body.password,
|
req.session.save(function (err) {
|
||||||
function (err) {
|
if (!err) {
|
||||||
if (err) return res.status(403).json({'message': err.message});
|
req.session.username = username;
|
||||||
passport.authenticate('local')(req, res, function () {
|
req.session.canonicalname = canonicalize(username);
|
||||||
req.session.save(function(err) {
|
}
|
||||||
if (err) return next(err);
|
cb(err);
|
||||||
useInvite(req.body.invite, req.body.username);
|
});
|
||||||
req.session.username = req.body.username;
|
});
|
||||||
res.status(200).json({'message': 'Registered.'});
|
}
|
||||||
});
|
|
||||||
});
|
router.post('/register', function (req, res) {
|
||||||
}
|
async.waterfall([
|
||||||
);
|
function (cb) {
|
||||||
|
checkInvite(req.body.invite, cb);
|
||||||
|
},
|
||||||
|
function (invite, cb) {
|
||||||
|
registerUser(req.body.username, req.body.password, invite, req.sanitize, cb);
|
||||||
|
},
|
||||||
|
function (cb) {
|
||||||
|
setupSession(req.body.username, req, res, cb);
|
||||||
|
}
|
||||||
|
], function (err) {
|
||||||
|
if (err) {
|
||||||
|
res.status(401).json({'message': err});
|
||||||
} else {
|
} else {
|
||||||
res.status(401).json({'message': 'Invalid invite code.'});
|
res.status(200).json({'message': 'Registration successful.'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/login', function (req, res, next) {
|
router.post('/login', function (req, res, next) {
|
||||||
passport.authenticate('local', function(err, user, info) {
|
// Take 'username' from the form and canonicalize it for authentication.
|
||||||
if (err) return next(err);
|
req.body.canonicalname = canonicalize(req.body.username);
|
||||||
if (!user) return res.status(401).json({'message': info});
|
|
||||||
req.logIn(user, function(err) {
|
async.waterfall([
|
||||||
if (err) return next(err);
|
function (cb) {
|
||||||
req.session.username = user;
|
passport.authenticate('local', function(err, user, info) {
|
||||||
res.status(200).json({'message': 'Logged in.'});
|
cb(err, user, info);
|
||||||
});
|
})(req, res, next);
|
||||||
})(req, res, next);
|
},
|
||||||
|
function (user, info, cb) {
|
||||||
|
if (!user)
|
||||||
|
cb(info);
|
||||||
|
else
|
||||||
|
req.logIn(user, cb);
|
||||||
|
},
|
||||||
|
function (cb) {
|
||||||
|
req.session.username = req.body.username;
|
||||||
|
req.session.canonicalname = canonicalize(req.body.username);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
], function (err) {
|
||||||
|
if (err)
|
||||||
|
res.status(401).json({'message': err});
|
||||||
|
else
|
||||||
|
res.status(200).json({'message': 'Login successful.'});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/logout', function (req, res) {
|
router.get('/logout', function (req, res) {
|
||||||
@ -64,17 +131,18 @@ router.get('/logout', function (req, res) {
|
|||||||
res.status(200).json({'message': 'Logged out.'});
|
res.status(200).json({'message': 'Logged out.'});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/session', function(req, res) {
|
router.get('/session', function (req, res) {
|
||||||
if (req.session.passport.user) {
|
if (req.session.passport.canonicalname) {
|
||||||
User.findOne({username: req.session.passport.user}, function(err, user) {
|
User.findOne({canonicalname: req.session.passport.canonicalname}, function (err, user) {
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
user: user.username,
|
username: user.username,
|
||||||
scope: user.scope
|
canonicalname: user.canonicalname,
|
||||||
});
|
scope: user.scope
|
||||||
});
|
});
|
||||||
} else {
|
});
|
||||||
res.status(401).json({'message': 'Unauthorized.'});
|
} else {
|
||||||
}
|
res.status(401).json({'message': 'Unauthorized.'});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
@ -50,7 +50,6 @@ app.use(bodyParser.urlencoded({ extended: true }));
|
|||||||
app.use(bodyParser.text());
|
app.use(bodyParser.text());
|
||||||
app.use(sanitizer());
|
app.use(sanitizer());
|
||||||
app.use(methodOverride('X-HTTP-Method-Override'));
|
app.use(methodOverride('X-HTTP-Method-Override'));
|
||||||
app.use(passport.initialize());
|
|
||||||
|
|
||||||
|
|
||||||
//app.use(favicon(__dirname + '/public/img/favicon.ico'));
|
//app.use(favicon(__dirname + '/public/img/favicon.ico'));
|
||||||
|
Loading…
Reference in New Issue
Block a user