Skip to content

Commit

Permalink
Fixed MMW-MMS connection by re-adding token auth
Browse files Browse the repository at this point in the history
  • Loading branch information
JL102 committed Jan 15, 2022
1 parent b6cce68 commit 4464e49
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 45 deletions.
4 changes: 2 additions & 2 deletions lib/api/users.js
Expand Up @@ -12,7 +12,7 @@ const router = express.Router();

router.post('/login', async (req, res) => {

passport.authenticate('local', (err, user) => {
passport.authenticate('local', (err, {user, token}) => {

if (user) {

Expand All @@ -24,7 +24,7 @@ router.post('/login', async (req, res) => {

debug(user.name);
res.send({
token: req.session.id,
token: token,
user: safeUserInfo(req.user),
});
});
Expand Down
60 changes: 52 additions & 8 deletions lib/auth.js
Expand Up @@ -5,6 +5,8 @@

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const UniqueTokenStrategy = require('passport-unique-token').UniqueTokenStrategy;
const crypto = require('crypto');
const bcrypt = require('bcryptjs');
const logger = require('./logger');
const debug = require('debug')('httpserver:auth');
Expand Down Expand Up @@ -49,7 +51,7 @@ passport.deserializeUser(async function(id, done) {
done(null, user);
});

// Configures passport's authenticate (log-in) function.
// Log-in function via user and password. Creates a new session token & returns the token.
passport.use(new LocalStrategy(
{
usernameField: 'user',
Expand All @@ -60,22 +62,65 @@ passport.use(new LocalStrategy(
debug(`Login attempt for user ${username}`);

try {
var user = config.getUserByName(username);
let user = config.registry.getUserByName(username);

if (!user) return done('User could not be found');
//Check password
var comparison = await bcrypt.compare(password, user.password);
let comparison = await bcrypt.compare(password, user.password);
//debug(comparison);
if (comparison !== true) return done('Incorrect password.');

if (comparison === true) {
//var sessionToken = crypto.randomBytes(12).toString('base64');
let token = crypto.randomBytes(24).toString('base64');
let token_expire = Date.now() + config.getBasicConfig().sessionLength;

// Save token to db
config.registry.insertSession(token, token_expire, user._id);

logger.info(`User ${username} has logged in with new token: ${token}`);

// Append user's role to the object for easy authentication
user.role = config.registry.getRole(user.role_key);

logger.info(`User ${username} has logged in`);
//Return user object
done(null, user);
//Return user & auth token
done(null, {user, token});
}
}
catch (err) {
debug(err);
return done(err, false);
}
}
));

passport.use(new UniqueTokenStrategy(
{
tokenField: 'token', // body request
tokenQuery: 'token', // query string request
tokenHeader: 'Authorization', // header request
failOnMissing: false, // allow fallback to user prompt
},
(token, done) => {
try {
// for token format Authorization: Bearer IyFDQt9xGxj3zPPE6ZKjz6ntg5WVdto0
if (token.split(' ')[0] === 'Bearer') {
token = token.split(' ')[1];
}

// Find user based on session token
const user = config.registry.getUserFromSessionToken(token);
debug(`user=${user}, type=${typeof user}, token=${token}`);

if (!user) {
debug('User not found');
return done(null, false, {message: 'test'});
}

// Append user's role to the object for easy authentication
user.role = config.registry.getRole(user.role_key);

return done(null, user);
}
catch (err) {
debug(err);
return done(err);
Expand Down Expand Up @@ -135,7 +180,6 @@ class Auth {
const cfg = config.getBasicConfig();

if (cfg.extAccess || Auth.isLocalIP(req.ip)) {
debug('isRequestAllowed: true');
return true;
}
else {
Expand Down
30 changes: 8 additions & 22 deletions lib/configuration.js
Expand Up @@ -18,6 +18,7 @@ var config = {
httpPort: 10222,
httpsPort: 10223,
extHttpsPort: 10223,
sessionLength: 1000 * 3600 * 24 * 365 * 3, // Max session length in ms. Default: very long.
extAccess: false,
performNAT: false,
keyPemFile: '',
Expand Down Expand Up @@ -114,8 +115,8 @@ class Configuration {
/**
* @returns {import('./db/sqlRegistry')} registry
*/
getRegistry() {
// @ts-ignore
get registry() {
// 2022-01-15 JL: Made a bound getter for registry for easier coding
assert(_registryInitialized, 'Registry not initialized! , setRegistry() not called?');
return _registry;
}
Expand All @@ -133,7 +134,7 @@ class Configuration {

_saveToRegistry(config, callback) {
if (_registryInitialized)
this.getRegistry().putConfig(config, callback);
this.registry.putConfig(config, callback);
else
callback();
}
Expand Down Expand Up @@ -232,21 +233,6 @@ class Configuration {
return Path.join(os.tmpdir(), 'mms');
}

/**
* Gets user info from given username
* @param {String} username Username
* @returns {import('./DataStructures').User} User object
*/
getUserByName(username){

if (typeof username == 'string') {
return this.getRegistry().getUserByName(username);
}
else {
return null;
}
}

/**
* Gets user info from given ID
* @param {String} userId ID of user
Expand All @@ -258,7 +244,7 @@ class Configuration {

if (userCache[userId]) return userCache[userId];
else {
var user = this.getRegistry().getUserById(userId);
var user = this.registry.getUserById(userId);
//Add user's role to user object, for ease in other functions
if (user) user.role = this.getRole(user.role_key);
//Add user to memory cache
Expand All @@ -282,7 +268,7 @@ class Configuration {

if (typeof userId == 'string' && typeof update == 'object') {

var info = this.getRegistry().updateUser(userId, update);
var info = this.registry.updateUser(userId, update);
//Wipe userCache
userCache = {};

Expand All @@ -300,7 +286,7 @@ class Configuration {
getRole(key) {

if (typeof key == 'string') {
return this.getRegistry().getRole(key);
return this.registry.getRole(key);
}
else {
return null;
Expand All @@ -313,7 +299,7 @@ class Configuration {
*/
getRoles() {

return this.getRegistry().getRoles();
return this.registry.getRoles();
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/contentDirectoryService.js
Expand Up @@ -476,7 +476,7 @@ class ContentDirectoryService extends Service {
*
*/
initializeRegistry(callback) {
this._nodeRegistry = Configuration.getRegistry();
this._nodeRegistry = Configuration.registry;
this._nodeRegistry._service = this;
callback(null);
}
Expand Down
70 changes: 63 additions & 7 deletions lib/db/sqlRegistry.js
Expand Up @@ -54,6 +54,9 @@ class SQLRegistry extends MemoryRegistry {
getUserByName: 'SELECT * FROM users WHERE name = ?',
getUserById: 'SELECT * FROM users WHERE _id = ?',
updateUser: 'UPDATE users SET name = ?, display_name = ?, role_key = ?, password = ? WHERE _id = ?',
getUserFromSessionToken: 'SELECT * FROM users WHERE _id IN ( SELECT user_id FROM user_sessions WHERE token = ? AND token_expire > ? )',
insertSession: 'INSERT INTO user_sessions (token, token_expire, token_created, user_id, useragent, ip) VALUES (?,?,?,?,?,?)',
pruneOldSessions: 'DELETE FROM user_sessions WHERE token_expire < ?',
getRole: 'SELECT * FROM user_roles WHERE key=?',
getRoles: 'SELECT * FROM user_roles ORDER BY access_level',

Expand Down Expand Up @@ -371,6 +374,21 @@ class SQLRegistry extends MemoryRegistry {
});
queries.push('UPDATE db_info SET version = 7');
}
if (version < 8) {
queries.push(
'CREATE TABLE IF NOT EXISTS user_sessions (' +
'token TEXT NOT NULL UNIQUE,' +
'token_expire INTEGER NOT NULL,' + // Note: JS Date() format
'token_created INTEGER NOT NULL,' +
'user_id TEXT NOT NULL,' +
'useragent TEXT,' + // TODO: Configurable option to enable/disable useragent+IP session storage
'ip TEXT,' + // Purpose: Someone may wish to share their media server to the public & may wish to audit usage
'FOREIGN KEY(user_id) REFERENCES users(_id),' +
'PRIMARY KEY(token)' +
')'
);
queries.push('UPDATE db_info SET version = 8');
}
//execute queries in order
for (var query of queries) {
var stmt;
Expand Down Expand Up @@ -801,18 +819,24 @@ class SQLRegistry extends MemoryRegistry {

/**
* Get user by name
* @param {String} username Username
* @param {string} username Username
* @returns {import('../DataStructures').User} user data
*/
getUserByName(username) {
debug('getUserByName: ENTER');

return this.sql.getStatement('getUserByName').get(username);
if (typeof username == 'string') {
return this.sql.getStatement('getUserByName').get(username);
}
else {
debug('getUserByName: Username not provided');
return null;
}
}

/**
* Get user by ID
* @param {String} _id User ID
* @param {string} _id User ID
* @returns {import('../DataStructures').User} user data
*/
getUserById(_id) {
Expand Down Expand Up @@ -853,7 +877,7 @@ class SQLRegistry extends MemoryRegistry {

/**
* Update user with given data
* @param {String} userId userID
* @param {string} userId userID
* @param {Object} userData User data to update
* @returns {import('better-sqlite3').RunResult} Run Result
*/
Expand All @@ -872,9 +896,41 @@ class SQLRegistry extends MemoryRegistry {
return this.sql.getStatement('updateUser').run(name, display_name, role_key, password, userId);
}

/**
* Get user from session token
* @param {string} token Auth token
* @returns {import('../DataStructures').User} user data
*/
getUserFromSessionToken(token) {
debug('getUserFromSessionToken: ENTER');

return this.sql.getStatement('getUserFromSessionToken').get(token, Date.now());
}

/**
*
* @param {string} token New token
* @param {Date|Number} token_expire Expiration time
* @param {string} userId User ID
* @param {string} [useragent] Useragent, for logging
* @param {string} [ip] IP address of user, for logging
*/
insertSession(token, token_expire, userId, useragent, ip) {
debug('insertSession: ENTER');

token_expire = new Date(token_expire).valueOf();
assert(!isNaN(token_expire.valueOf()), 'token_expire is an invalid date.');
assert(typeof userId === 'string', 'userId must be string.');
assert(typeof token === 'string', 'token must be a string,');
if (!useragent) useragent = null;
if (!ip) ip = null;

return this.sql.getStatement('insertSession').run(token, token_expire, Date.now(), userId, useragent, ip);
}

/**
* Get role by key
* @param {String} key Role key
* @param {string} key Role key
* @returns {import('../DataStructures').Role} Role info
*/
getRole(key) {
Expand Down Expand Up @@ -956,8 +1012,8 @@ class SQLRegistry extends MemoryRegistry {

/**
* Validate FTS (search term?)
* @param {String} value Value to validate
* @returns {String} validated/filtered search query thing
* @param {string} value Value to validate
* @returns {string} validated/filtered search query thing
*/
validateFTS(value) {
debug('validateFTS: ENTER');
Expand Down
4 changes: 2 additions & 2 deletions lib/httpServer.js
Expand Up @@ -57,7 +57,7 @@ class HTTPServer {
this.httpServer = httpServer;
this.httpsServer = httpsServer;

var sqlDbDir = path.dirname(config.getRegistry().sql.path) + '\\';
var sqlDbDir = path.dirname(config.registry.sql.path) + '\\';
debug(`sqlDbDir=${sqlDbDir}`);

app.use(session({
Expand All @@ -74,7 +74,7 @@ class HTTPServer {
next();
});
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.authenticate('token'));
app.use('/api', restRouter);

app.use((req, res) => {
Expand Down

0 comments on commit 4464e49

Please sign in to comment.