Add an option to completely disable users and authentication

Bug: Flood-UI/flood#240
Cherry-Pick: Flood-UI/flood#880
Signed-off-by: Jesse Chan <jc@linux.com>
This commit is contained in:
Valentin Tolmer
2020-07-28 20:02:15 +02:00
committed by Jesse Chan
parent 52535e1eab
commit 5c4a22f0fd
6 changed files with 142 additions and 81 deletions
+1
View File
@@ -15,6 +15,7 @@ const CONFIG = {
ssl: process.env.FLOOD_ENABLE_SSL === 'true' || process.env.FLOOD_ENABLE_SSL === true,
sslKey: '/data/flood_ssl.key',
sslCert: '/data/flood_ssl.cert',
disableUsersAndAuth: false,
};
module.exports = CONFIG;
+17
View File
@@ -19,6 +19,23 @@ const CONFIG = {
dbCleanInterval: 1000 * 60 * 60,
// Where to store the local nedb database.
dbPath: './server/db/',
// If this is true, there will be no users and no attempt to
// authenticate or password-protect any page. In that case,
// instead of per-user config, the following configUser settings
// will be used.
disableUsersAndAuth: false,
// Settings for the no-user configuration.
configUser: {
// How to connect to rTorrent. If this is true, it will connect
// to the unix socket defined in socketPath.
// Otherwise, it will connect via SCGI with the host and port.
socket: true,
// Path to the rTorrent unix socket.
socketPath: '/path/to/rtorrent.sock',
// SCGI host and port to connect to rTorrent.
host: 'localhost',
port: 5000,
},
// The host that Flood should listen for web connections on.
// If you want to connect to Flood from hosts other that the one it is running
// on, you should change this value.
+3
View File
@@ -89,6 +89,9 @@ const startWebServer = () => {
const address = chalk.underline(`${useSSL ? 'https' : 'http'}://${host}:${port}`);
console.log(chalk.green(`Flood server starting on ${address}.\n`));
if (config.disableUsersAndAuth) {
console.log(chalk.yellow('Starting in insecure mode without authentication\n'));
}
};
module.exports = {startWebServer};
+16
View File
@@ -155,7 +155,23 @@ class Users {
});
}
getConfigUser() {
const {socket} = config.configUser;
return {
_id: '_config',
username: '_config',
host: socket ? null : config.configUser.host,
port: socket ? null : config.configUser.port,
socketPath: socket ? config.configUser.socketPath : null,
password: '',
isAdmin: true,
};
}
listUsers(callback) {
if (config.disableUsersAndAuth) {
return callback([this.getConfigUser()]);
}
this.db.find({}, (err, users) => {
if (err) {
return callback(null, err);
+10 -1
View File
@@ -12,8 +12,17 @@ const eventStream = require('../middleware/eventStream');
const Filesystem = require('../models/Filesystem');
const mediainfo = require('../util/mediainfo');
const settings = require('../models/settings');
const config = require('../../config');
const Users = require('../models/Users');
router.use('/', passport.authenticate('jwt', {session: false}), appendUserServices);
if (config.disableUsersAndAuth) {
router.use('/', (req, res, next) => {
req.user = Users.getConfigUser();
appendUserServices(req, res, next);
});
} else {
router.use('/', passport.authenticate('jwt', {session: false}), appendUserServices);
}
router.use('/client', clientRoutes);
+95 -80
View File
@@ -42,6 +42,9 @@ const authValidation = joi.object().keys({
});
router.use('/', (req, res, next) => {
if (config.disableUsersAndAuth) {
req.user = Users.getConfigUser();
}
const validation = joi.validate(req.body, authValidation);
if (!validation.error) {
@@ -54,9 +57,14 @@ router.use('/', (req, res, next) => {
}
});
router.use('/users', passport.authenticate('jwt', {session: false}), requireAdmin);
if (!config.disableUsersAndAuth) {
router.use('/users', passport.authenticate('jwt', {session: false}), requireAdmin);
}
router.post('/authenticate', (req, res) => {
if (config.disableUsersAndAuth) {
return setAuthToken(res, req.user._id, true);
}
const credentials = {
password: req.body.password,
username: req.body.username,
@@ -74,44 +82,49 @@ router.post('/authenticate', (req, res) => {
});
});
// Allow unauthenticated registration if no users are currently registered.
router.use('/register', (req, res, next) => {
Users.initialUserGate({
handleInitialUser: () => {
next();
},
handleSubsequentUser: () => {
passport.authenticate('jwt', {session: false}, (passportReq, passportRes) => {
passportRes.json({username: req.username});
});
},
if (!config.disableUsersAndAuth) {
// Allow unauthenticated registration if no users are currently registered.
router.use('/register', (req, res, next) => {
Users.initialUserGate({
handleInitialUser: () => {
next();
},
handleSubsequentUser: () => {
passport.authenticate('jwt', {session: false}, (passportReq, passportRes) => {
passportRes.json({username: req.username});
});
},
});
});
});
router.post('/register', (req, res) => {
// Attempt to save the user
Users.createUser(
{
username: req.body.username,
password: req.body.password,
host: req.body.host,
port: req.body.port,
socketPath: req.body.socketPath,
isAdmin: true,
},
(createUserResponse, createUserError) => {
if (createUserError) {
ajaxUtil.getResponseFn(res)(createUserResponse, createUserError);
return;
}
router.post('/register', (req, res) => {
// Attempt to save the user
Users.createUser(
{
username: req.body.username,
password: req.body.password,
host: req.body.host,
port: req.body.port,
socketPath: req.body.socketPath,
isAdmin: true,
},
(createUserResponse, createUserError) => {
if (createUserError) {
ajaxUtil.getResponseFn(res)(createUserResponse, createUserError);
return;
}
setAuthToken(res, req.body.username, true);
},
);
});
setAuthToken(res, req.body.username, true);
},
);
});
}
// Allow unauthenticated verification if no users are currently registered.
router.use('/verify', (req, res, next) => {
if (config.disableUsersAndAuth) {
return next();
}
Users.initialUserGate({
handleInitialUser: () => {
req.initialUser = true;
@@ -132,63 +145,65 @@ router.get('/verify', (req, res) => {
});
});
// All subsequent routes are protected.
router.use('/', passport.authenticate('jwt', {session: false}));
if (!config.disableUsersAndAuth) {
// All subsequent routes are protected.
router.use('/', passport.authenticate('jwt', {session: false}));
router.get('/logout', (req, res) => {
res.clearCookie('jwt').send();
});
router.get('/logout', (req, res) => {
res.clearCookie('jwt').send();
});
router.use('/users', (req, res, next) => {
if (req.user && req.user.isAdmin) {
next();
return;
}
router.use('/users', (req, res, next) => {
if (req.user && req.user.isAdmin) {
next();
return;
}
res.status(401).send('Not authorized');
});
res.status(401).send('Not authorized');
});
router.get('/users', (req, res) => {
Users.listUsers(ajaxUtil.getResponseFn(res));
});
router.get('/users', (req, res) => {
Users.listUsers(ajaxUtil.getResponseFn(res));
});
router.delete('/users/:username', (req, res) => {
Users.removeUser(req.params.username, ajaxUtil.getResponseFn(res));
services.destroyUserServices(req.user);
});
router.delete('/users/:username', (req, res) => {
Users.removeUser(req.params.username, ajaxUtil.getResponseFn(res));
services.destroyUserServices(req.user);
});
router.patch('/users/:username', (req, res) => {
const {username} = req.params;
const userPatch = req.body;
router.patch('/users/:username', (req, res) => {
const {username} = req.params;
const userPatch = req.body;
if (!userPatch.socketPath) {
userPatch.socketPath = null;
} else {
userPatch.host = null;
userPatch.port = null;
}
if (!userPatch.socketPath) {
userPatch.socketPath = null;
} else {
userPatch.host = null;
userPatch.port = null;
}
Users.updateUser(username, userPatch, () => {
Users.lookupUser({username}, (err, user) => {
if (err) return req.status(500).json({error: err});
services.updateUserServices(user);
res.send();
Users.updateUser(username, userPatch, () => {
Users.lookupUser({username}, (err, user) => {
if (err) return req.status(500).json({error: err});
services.updateUserServices(user);
res.send();
});
});
});
});
router.put('/users', (req, res) => {
Users.createUser(
{
username: req.body.username,
password: req.body.password,
host: req.body.host,
port: req.body.port,
socketPath: req.body.socketPath,
isAdmin: req.body.isAdmin,
},
ajaxUtil.getResponseFn(res),
);
});
router.put('/users', (req, res) => {
Users.createUser(
{
username: req.body.username,
password: req.body.password,
host: req.body.host,
port: req.body.port,
socketPath: req.body.socketPath,
isAdmin: req.body.isAdmin,
},
ajaxUtil.getResponseFn(res),
);
});
}
module.exports = router;