mirror of
https://github.com/zoriya/flood.git
synced 2026-05-31 02:15:12 +00:00
Implement JWT authentication
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
import AppDispatcher from '../dispatcher/AppDispatcher';
|
||||
|
||||
const AuthActions = {
|
||||
authenticate: (credentials) => {
|
||||
return axios.post('/auth/authenticate', credentials)
|
||||
.then((json = {}) => {
|
||||
return json.data;
|
||||
})
|
||||
.then((data) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_LOGIN_SUCCESS,
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_LOGIN_ERROR,
|
||||
error: error.data.message
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
createUser: (credentials) => {
|
||||
return axios.put('/auth/users', credentials)
|
||||
.then((json = {}) => {
|
||||
return json.data;
|
||||
})
|
||||
.then((data) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_CREATE_USER_SUCCESS,
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_CREATE_USER_ERROR,
|
||||
error
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
deleteUser: (username) => {
|
||||
return axios.delete(`/auth/users/${username}`)
|
||||
.then((json = {}) => {
|
||||
return json.data;
|
||||
})
|
||||
.then((data) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_DELETE_USER_SUCCESS,
|
||||
data: {
|
||||
username,
|
||||
...data
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_DELETE_USER_ERROR,
|
||||
error: {
|
||||
username,
|
||||
...error
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
fetchUsers: () => {
|
||||
return axios.get('/auth/users')
|
||||
.then((json = {}) => {
|
||||
return json.data;
|
||||
})
|
||||
.then((data) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_LIST_USERS_SUCCESS,
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_LIST_USERS_ERROR,
|
||||
error
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
register: (credentials) => {
|
||||
return axios.post('/auth/register', credentials)
|
||||
.then((json = {}) => {
|
||||
return json.data;
|
||||
})
|
||||
.then((data) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_REGISTER_SUCCESS,
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_REGISTER_ERROR,
|
||||
error
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
verify: () => {
|
||||
// We need to prevent caching this endpoint.
|
||||
return axios.get(`/auth/verify?${Date.now()}`)
|
||||
.then((json = {}) => {
|
||||
return json.data;
|
||||
})
|
||||
.then((data) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_VERIFY_SUCCESS,
|
||||
data
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
AppDispatcher.dispatchServerAction({
|
||||
type: ActionTypes.AUTH_VERIFY_ERROR,
|
||||
error
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default AuthActions;
|
||||
@@ -0,0 +1,105 @@
|
||||
import classnames from'classnames';
|
||||
import React from'react';
|
||||
|
||||
import EventTypes from '../../constants/EventTypes';
|
||||
import FloodActions from '../../actions/FloodActions';
|
||||
import AuthStore from '../../stores/AuthStore';
|
||||
|
||||
const METHODS_TO_BIND = ['handleAuthError', 'handleSubmitClick'];
|
||||
|
||||
export default class AuthForm extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {error: null}
|
||||
|
||||
METHODS_TO_BIND.forEach((method) => {
|
||||
this[method] = this[method].bind(this);
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
AuthStore.listen(EventTypes.AUTH_LOGIN_ERROR, this.handleAuthError);
|
||||
AuthStore.listen(EventTypes.AUTH_REGISTER_ERROR, this.handleAuthError);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
AuthStore.unlisten(EventTypes.AUTH_LOGIN_ERROR, this.handleAuthError);
|
||||
AuthStore.unlisten(EventTypes.AUTH_REGISTER_ERROR, this.handleAuthError);
|
||||
}
|
||||
|
||||
getValue(fieldName) {
|
||||
return this.state[fieldName];
|
||||
}
|
||||
|
||||
handleSubmitClick() {
|
||||
if (this.props.mode === 'login') {
|
||||
AuthStore.authenticate({
|
||||
username: this.refs.username.value,
|
||||
password: this.refs.password.value
|
||||
});
|
||||
} else {
|
||||
AuthStore.register({
|
||||
username: this.refs.username.value,
|
||||
password: this.refs.password.value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleAuthError(error) {
|
||||
this.setState({error});
|
||||
}
|
||||
|
||||
render() {
|
||||
let actionText = null;
|
||||
let error = null;
|
||||
let headerText = null;
|
||||
|
||||
if (this.props.mode === 'login') {
|
||||
actionText = 'Log In';
|
||||
headerText = 'Login';
|
||||
} else {
|
||||
actionText = 'Create Account';
|
||||
headerText = 'Create an Account';
|
||||
}
|
||||
|
||||
if (!!this.state.error) {
|
||||
error = (
|
||||
<div className="form__row form__row--error">
|
||||
<div className="form__column">
|
||||
{this.state.error}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form form--authentication">
|
||||
<div className="form__wrapper">
|
||||
<div className="form__row form__header">
|
||||
<h1>{headerText}</h1>
|
||||
</div>
|
||||
<div className="form__row">
|
||||
<div className="form__column">
|
||||
<input className="textbox textbox--open" placeholder="Username"
|
||||
ref="username" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="form__row">
|
||||
<div className="form__column">
|
||||
<input className="textbox textbox--open" placeholder="Password"
|
||||
ref="password" type="password" />
|
||||
</div>
|
||||
</div>
|
||||
{error}
|
||||
</div>
|
||||
<div className="form__actions">
|
||||
<button className="button button--primary"
|
||||
onClick={this.handleSubmitClick}>
|
||||
{actionText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import classnames from'classnames';
|
||||
import React from'react';
|
||||
|
||||
import AuthForm from './AuthForm';
|
||||
|
||||
export default class LoginForm extends React.Component {
|
||||
render() {
|
||||
return <AuthForm mode="login" />;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import classnames from'classnames';
|
||||
import React from'react';
|
||||
|
||||
import AuthForm from './AuthForm';
|
||||
|
||||
export default class RegistrationForm extends React.Component {
|
||||
render() {
|
||||
return <AuthForm mode="register" />;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
const ActionTypes = {
|
||||
AUTH_CREATE_USER_ERROR: 'AUTH_CREATE_USER_ERROR',
|
||||
AUTH_CREATE_USER_SUCCESS: 'AUTH_CREATE_USER_SUCCESS',
|
||||
AUTH_DELETE_USER_ERROR: 'AUTH_DELETE_USER_ERROR',
|
||||
AUTH_DELETE_USER_SUCCESS: 'AUTH_DELETE_USER_SUCCESS',
|
||||
AUTH_LIST_USERS_ERROR: 'AUTH_LIST_USERS_ERROR',
|
||||
AUTH_LIST_USERS_SUCCESS: 'AUTH_LIST_USERS_SUCCESS',
|
||||
AUTH_LOGIN_ERROR: 'AUTH_LOGIN_ERROR',
|
||||
AUTH_LOGIN_SUCCESS: 'AUTH_LOGIN_SUCCESS',
|
||||
AUTH_REGISTER_ERROR: 'AUTH_REGISTER_ERROR',
|
||||
AUTH_REGISTER_SUCCESS: 'AUTH_REGISTER_SUCCESS',
|
||||
AUTH_VERIFY_ERROR: 'AUTH_VERIFY_ERROR',
|
||||
AUTH_VERIFY_SUCCESS: 'AUTH_VERIFY_SUCCESS',
|
||||
CLIENT_ADD_TORRENT_ERROR: 'CLIENT_ADD_TORRENT_ERROR',
|
||||
CLIENT_ADD_TORRENT_SUCCESS: 'CLIENT_ADD_TORRENT_SUCCESS',
|
||||
CLIENT_CHECK_HASH_ERROR: 'CLIENT_CHECK_HASH_ERROR',
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
const EventTypes = {
|
||||
AUTH_CREATE_USER_ERROR: 'AUTH_CREATE_USER_ERROR',
|
||||
AUTH_CREATE_USER_SUCCESS: 'AUTH_CREATE_USER_SUCCESS',
|
||||
AUTH_DELETE_USER_ERROR: 'AUTH_DELETE_USER_ERROR',
|
||||
AUTH_DELETE_USER_SUCCESS: 'AUTH_DELETE_USER_SUCCESS',
|
||||
AUTH_LIST_USERS_ERROR: 'AUTH_LIST_USERS_ERROR',
|
||||
AUTH_LIST_USERS_SUCCESS: 'AUTH_LIST_USERS_SUCCESS',
|
||||
AUTH_LOGIN_ERROR: 'AUTH_LOGIN_ERROR',
|
||||
AUTH_LOGIN_SUCCESS: 'AUTH_LOGIN_SUCCESS',
|
||||
AUTH_REGISTER_ERROR: 'AUTH_REGISTER_ERROR',
|
||||
AUTH_REGISTER_SUCCESS: 'AUTH_REGISTER_SUCCESS',
|
||||
AUTH_VERIFY_ERROR: 'AUTH_VERIFY_ERROR',
|
||||
AUTH_VERIFY_SUCCESS: 'AUTH_VERIFY_SUCCESS',
|
||||
CLIENT_ADD_TORRENT_ERROR: 'CLIENT_ADD_TORRENT_ERROR',
|
||||
CLIENT_ADD_TORRENT_SUCCESS: 'CLIENT_ADD_TORRENT_SUCCESS',
|
||||
CLIENT_SET_THROTTLE_ERROR: 'CLIENT_SET_THROTTLE_ERROR',
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import AuthActions from '../actions/AuthActions';
|
||||
import ActionTypes from '../constants/ActionTypes';
|
||||
import AppDispatcher from '../dispatcher/AppDispatcher';
|
||||
import BaseStore from './BaseStore';
|
||||
import EventTypes from '../constants/EventTypes';
|
||||
|
||||
class AuthStoreClass extends BaseStore {
|
||||
constructor() {
|
||||
super();
|
||||
this.token = null;
|
||||
this.users = [];
|
||||
}
|
||||
|
||||
authenticate(credentials) {
|
||||
AuthActions.authenticate({
|
||||
username: credentials.username,
|
||||
password: credentials.password
|
||||
});
|
||||
}
|
||||
|
||||
createUser(credentials) {
|
||||
AuthActions.createUser(credentials);
|
||||
}
|
||||
|
||||
deleteUser(username) {
|
||||
AuthActions.deleteUser(username);
|
||||
}
|
||||
|
||||
fetchUserList() {
|
||||
AuthActions.fetchUsers();
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
getUsers() {
|
||||
return this.users;
|
||||
}
|
||||
|
||||
handleCreateUserError(error) {
|
||||
this.emit(EventTypes.AUTH_CREATE_USER_ERROR, error);
|
||||
}
|
||||
|
||||
handleCreateUserSuccess(data) {
|
||||
this.emit(EventTypes.AUTH_CREATE_USER_SUCCESS);
|
||||
}
|
||||
|
||||
handleDeleteUserError(error) {
|
||||
this.emit(EventTypes.AUTH_DELETE_USER_ERROR, error.username);
|
||||
}
|
||||
|
||||
handleDeleteUserSuccess(data) {
|
||||
this.emit(EventTypes.AUTH_DELETE_USER_SUCCESS, data.username);
|
||||
}
|
||||
|
||||
handleListUsersError(error) {
|
||||
this.emit(EventTypes.AUTH_LIST_USERS_ERROR);
|
||||
}
|
||||
|
||||
handleListUsersSuccess(data) {
|
||||
this.users = data;
|
||||
this.emit(EventTypes.AUTH_LIST_USERS_SUCCESS);
|
||||
}
|
||||
|
||||
handleLoginSuccess(data) {
|
||||
this.emit(EventTypes.AUTH_LOGIN_SUCCESS);
|
||||
this.token = data.token;
|
||||
}
|
||||
|
||||
handleLoginError(error) {
|
||||
this.token = null;
|
||||
this.emit(EventTypes.AUTH_LOGIN_ERROR, error);
|
||||
}
|
||||
|
||||
handleRegisterSuccess(data) {
|
||||
this.emit(EventTypes.AUTH_REGISTER_SUCCESS, data);
|
||||
}
|
||||
|
||||
handleRegisterError(error) {
|
||||
this.emit(EventTypes.AUTH_REGISTER_ERROR, error);
|
||||
}
|
||||
|
||||
register(credentials) {
|
||||
AuthActions.register({
|
||||
username: credentials.username,
|
||||
password: credentials.password
|
||||
});
|
||||
}
|
||||
|
||||
verify() {
|
||||
AuthActions.verify();
|
||||
}
|
||||
}
|
||||
|
||||
let AuthStore = new AuthStoreClass();
|
||||
|
||||
AuthStore.dispatcherID = AppDispatcher.register((payload) => {
|
||||
const {action, source} = payload;
|
||||
|
||||
switch (action.type) {
|
||||
case ActionTypes.AUTH_LOGIN_SUCCESS:
|
||||
AuthStore.handleLoginSuccess(action.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_LOGIN_ERROR:
|
||||
AuthStore.handleLoginError(action.error);
|
||||
break;
|
||||
case ActionTypes.AUTH_LIST_USERS_SUCCESS:
|
||||
AuthStore.handleListUsersSuccess(action.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_LIST_USERS_ERROR:
|
||||
AuthStore.handleListUsersError(action.error);
|
||||
break;
|
||||
case ActionTypes.AUTH_CREATE_USER_SUCCESS:
|
||||
AuthStore.handleCreateUserSuccess(action.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_CREATE_USER_ERROR:
|
||||
AuthStore.handleCreateUserError(action.error.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_DELETE_USER_SUCCESS:
|
||||
AuthStore.handleDeleteUserSuccess(action.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_DELETE_USER_ERROR:
|
||||
AuthStore.handleDeleteUserError(action.error);
|
||||
break;
|
||||
case ActionTypes.AUTH_REGISTER_SUCCESS:
|
||||
AuthStore.handleRegisterSuccess(action.data);
|
||||
AuthStore.emit(EventTypes.AUTH_REGISTER_SUCCESS,
|
||||
action.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_REGISTER_ERROR:
|
||||
AuthStore.handleRegisterError(action.error.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_VERIFY_SUCCESS:
|
||||
AuthStore.emit(EventTypes.AUTH_VERIFY_SUCCESS,
|
||||
action.data);
|
||||
break;
|
||||
case ActionTypes.AUTH_VERIFY_ERROR:
|
||||
AuthStore.emit(EventTypes.AUTH_VERIFY_ERROR,
|
||||
action.error);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
export default AuthStore;
|
||||
@@ -10,6 +10,13 @@ export default class BaseStore extends EventEmitter {
|
||||
this.setMaxListeners(20);
|
||||
}
|
||||
|
||||
emit(eventName) {
|
||||
super.emit(...arguments);
|
||||
if (eventName == null) {
|
||||
console.warn('Event is undefined!');
|
||||
}
|
||||
}
|
||||
|
||||
beginRequest(id) {
|
||||
this.requests[id] = true;
|
||||
}
|
||||
@@ -19,7 +26,7 @@ export default class BaseStore extends EventEmitter {
|
||||
}
|
||||
|
||||
isRequestPending(id) {
|
||||
if (this.requests[id] == null || this.requests[id] === false) {
|
||||
if (this.requests[id] == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -31,7 +38,7 @@ export default class BaseStore extends EventEmitter {
|
||||
}
|
||||
|
||||
resolveRequest(id) {
|
||||
this.requests[id] = false;
|
||||
delete this.requests[id];
|
||||
}
|
||||
|
||||
unlisten(event, callback) {
|
||||
|
||||
@@ -178,7 +178,7 @@ class TorrentStoreClass extends BaseStore {
|
||||
}
|
||||
|
||||
handleFetchTorrentsError(error) {
|
||||
console.trace(error);
|
||||
this.resolveRequest('fetch-torrents');
|
||||
}
|
||||
|
||||
handleFetchTorrentsSuccess(torrents) {
|
||||
|
||||
@@ -59,11 +59,11 @@ class TransferDataStoreClass extends BaseStore {
|
||||
|
||||
handleSetThrottleSuccess(data) {
|
||||
this.fetchTransferData();
|
||||
this.emit(EventTypes.CLIENT_SET_THROTTLE_SUCCESS);
|
||||
// this.emit(EventTypes.CLIENT_SET_THROTTLE_SUCCESS);
|
||||
}
|
||||
|
||||
handleSetThrottleError(error) {
|
||||
this.emit(EventTypes.CLIENT_SET_THROTTLE_ERROR);
|
||||
// this.emit(EventTypes.CLIENT_SET_THROTTLE_ERROR);
|
||||
}
|
||||
|
||||
handleTransferDataSuccess(transferData) {
|
||||
|
||||
@@ -3,6 +3,7 @@ const config = {
|
||||
dbPath: './server/db/',
|
||||
maxHistoryStates: 30,
|
||||
pollInterval: 1000 * 5,
|
||||
secret: 'flood',
|
||||
scgi: {
|
||||
host: 'localhost',
|
||||
port: 5000,
|
||||
|
||||
+5
-2
@@ -9,7 +9,8 @@
|
||||
"dependencies": {
|
||||
"axios": "^0.7.0",
|
||||
"babel-polyfill": "^6.9.1",
|
||||
"body-parser": "~1.12.0",
|
||||
"bcrypt-nodejs": "0.0.3",
|
||||
"body-parser": "^1.12.4",
|
||||
"classnames": "^2.1.5",
|
||||
"compression": "^1.6.1",
|
||||
"cookie-parser": "~1.3.4",
|
||||
@@ -28,9 +29,10 @@
|
||||
"inuit-page": "^0.2.1",
|
||||
"inuit-reset": "^0.1.1",
|
||||
"isomorphic-fetch": "^2.1.1",
|
||||
"jsonwebtoken": "^7.0.1",
|
||||
"keymirror": "^0.1.1",
|
||||
"lodash": "^4.3.0",
|
||||
"morgan": "~1.5.1",
|
||||
"morgan": "^1.5.3",
|
||||
"multer": "^1.1.0",
|
||||
"mv": "^2.1.1",
|
||||
"nedb": "^1.7.2",
|
||||
@@ -38,6 +40,7 @@
|
||||
"object-assign": "^2.0.0",
|
||||
"passport": "^0.3.2",
|
||||
"passport-http": "^0.3.0",
|
||||
"passport-jwt": "^2.1.0",
|
||||
"pug": "^2.0.0-beta3",
|
||||
"q": "^1.2.0",
|
||||
"react": "^15.0.2",
|
||||
|
||||
+33
-42
@@ -1,54 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
require('events').EventEmitter.defaultMaxListeners = Infinity;
|
||||
|
||||
var bodyParser = require('body-parser');
|
||||
var compression = require('compression');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var express = require('express');
|
||||
var favicon = require('serve-favicon');
|
||||
var logger = require('morgan');
|
||||
var path = require('path');
|
||||
let bodyParser = require('body-parser');
|
||||
let compression = require('compression');
|
||||
let cookieParser = require('cookie-parser');
|
||||
let express = require('express');
|
||||
let favicon = require('serve-favicon');
|
||||
let morgan = require('morgan');
|
||||
let passport = require('passport');
|
||||
let path = require('path');
|
||||
|
||||
var clientRoutes = require('./routes/client');
|
||||
var mainRoutes = require('./routes/index');
|
||||
|
||||
var app = express();
|
||||
let app = express();
|
||||
let apiRoutes = require('./routes/api');
|
||||
let authRoutes = require('./routes/auth');
|
||||
let Users = require('./models/Users');
|
||||
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'pug');
|
||||
|
||||
// uncomment after placing your favicon in /assets
|
||||
//app.use(favicon(__dirname + '/assets/favicon.ico'));
|
||||
app.use(logger('dev'));
|
||||
// TODO: Add favicon...
|
||||
// app.use(favicon(__dirname + '/assets/favicon.ico'));
|
||||
app.use(morgan('dev'));
|
||||
app.use(passport.initialize());
|
||||
app.use(compression());
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(bodyParser.urlencoded({extended: false}));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, 'assets')));
|
||||
|
||||
app.use((req, res, next) => {
|
||||
req.socket.on("error", (err) => {
|
||||
console.trace(err);
|
||||
});
|
||||
res.socket.on("error", (err) => {
|
||||
console.trace(err);
|
||||
});
|
||||
next();
|
||||
});
|
||||
require('./config/passport')(passport);
|
||||
|
||||
app.use('/', mainRoutes);
|
||||
app.use('/client', clientRoutes);
|
||||
app.use('/auth', authRoutes);
|
||||
app.use('/api', apiRoutes);
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
// Catch 404 and forward to error handler.
|
||||
app.use((req, res, next) => {
|
||||
var err = new Error('Not Found');
|
||||
err.status = 404;
|
||||
next(err);
|
||||
});
|
||||
|
||||
// error handlers
|
||||
|
||||
// development error handler
|
||||
// will print stacktrace
|
||||
// Development error handler, will print stacktrace.
|
||||
if (app.get('env') === 'development') {
|
||||
app.use((err, req, res, next) => {
|
||||
res.status(err.status || 500);
|
||||
@@ -57,17 +50,15 @@ if (app.get('env') === 'development') {
|
||||
error: err
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Production error handler, no stacktraces leaked to user.
|
||||
app.use((err, req, res, next) => {
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: {}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// production error handler
|
||||
// no stacktraces leaked to user
|
||||
app.use((err, req, res, next) => {
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: {}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
module.exports = app;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
let extractJWT = require('passport-jwt').ExtractJwt;
|
||||
let jwtStrategy = require('passport-jwt').Strategy;
|
||||
|
||||
let config = require('../../config');
|
||||
let Users = require('../models/Users');
|
||||
|
||||
// Setup work and export for the JWT passport strategy
|
||||
module.exports = (passport) => {
|
||||
let options = {
|
||||
jwtFromRequest: extractJWT.fromAuthHeader(),
|
||||
secretOrKey: config.secret
|
||||
};
|
||||
|
||||
passport.use(new jwtStrategy(options, (jwtPayload, callback) => {
|
||||
Users.lookupUser({username: jwtPayload.username}, (err, user) => {
|
||||
if (err) {
|
||||
return callback(err, false);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return callback(null, user);
|
||||
}
|
||||
|
||||
return callback(null, false);
|
||||
});
|
||||
}));
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
'use strict';
|
||||
|
||||
let Datastore = require('nedb');
|
||||
|
||||
let bcrypt = require('bcrypt-nodejs');
|
||||
let config = require('../../config');
|
||||
|
||||
class Users {
|
||||
constructor() {
|
||||
this.ready = false;
|
||||
this.db = this.loadDatabase();
|
||||
}
|
||||
|
||||
comparePassword(credentials, callback) {
|
||||
this.db.findOne({username: credentials.username}).exec((err, user) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Username not found.
|
||||
if (user == null) {
|
||||
return callback(null, user);
|
||||
}
|
||||
|
||||
bcrypt.compare(credentials.password, user.password, (err, isMatch) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
return callback(null, isMatch);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createUser(credentials, callback) {
|
||||
if (!this.ready) {
|
||||
callback({message: 'Users database is not ready.'});
|
||||
}
|
||||
|
||||
bcrypt.genSalt(10, (err, salt) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let username = credentials.username;
|
||||
|
||||
bcrypt.hash(credentials.password, salt, null, (err, hash) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
this.db.insert({username: username, password: hash}, (err, user) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {username: credentials.username});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initialUserGate(handlers) {
|
||||
this.db.find({}, (err, users) => {
|
||||
if (users && users.length > 0) {
|
||||
return handlers.handleSubsequentUser();
|
||||
}
|
||||
|
||||
return handlers.handleInitialUser();
|
||||
});
|
||||
}
|
||||
|
||||
loadDatabase() {
|
||||
let db = new Datastore({
|
||||
autoload: true,
|
||||
filename: `${config.dbPath}authUsers.db`
|
||||
});
|
||||
|
||||
db.ensureIndex({fieldName: 'username', unique: true});
|
||||
|
||||
this.ready = true;
|
||||
return db;
|
||||
}
|
||||
|
||||
lookupUser(credentials, callback) {
|
||||
this.db.findOne({username: credentials.username}, (err, user) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
return callback(null, user);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Users();
|
||||
@@ -1,34 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
var express = require('express');
|
||||
var passport = require('passport');
|
||||
var router = express.Router();
|
||||
var Strategy = require('passport-http').BasicStrategy;
|
||||
let express = require('express');
|
||||
|
||||
let ajaxUtil = require('../util/ajaxUtil');
|
||||
let client = require('../models/client');
|
||||
let clientRoutes = require('./client');
|
||||
let history = require('../models/history');
|
||||
let passport = require('passport');
|
||||
let router = express.Router();
|
||||
let settings = require('../models/settings');
|
||||
var users = require('../db/users');
|
||||
|
||||
history.startPolling();
|
||||
|
||||
passport.use(new Strategy(
|
||||
(username, password, callback) => {
|
||||
users.findByUsername(username, (err, user) => {
|
||||
if (err) { return callback(err); }
|
||||
if (!user) { return callback(null, false); }
|
||||
if (user.password != password) { return callback(null, false); }
|
||||
return callback(null, user);
|
||||
});
|
||||
}
|
||||
));
|
||||
router.use(passport.authenticate('jwt', {session: false}));
|
||||
|
||||
router.get('/', passport.authenticate('basic', { session: false }),
|
||||
(req, res) => {
|
||||
res.render('index', { title: 'Flood' });
|
||||
}
|
||||
);
|
||||
router.use('/client', clientRoutes);
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
res.render('index', {title: 'Flood'});
|
||||
});
|
||||
|
||||
router.get('/history', function(req, res, next) {
|
||||
history.get(req.query, ajaxUtil.getResponseFn(res));
|
||||
@@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
let express = require('express');
|
||||
let jwt = require('jsonwebtoken');
|
||||
let multer = require('multer');
|
||||
let passport = require('passport');
|
||||
|
||||
let config = require('../../config');
|
||||
let router = express.Router();
|
||||
let Users = require('../models/Users');
|
||||
|
||||
// Allow unauthenticated registration if no users are currently registered.
|
||||
router.use('/register', (req, res, next) => {
|
||||
Users.initialUserGate({
|
||||
handleInitialUser: next.bind(this),
|
||||
handleSubsequentUser: passport.authenticate('jwt', {session: false}).bind(this, req, res, next)
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/register', (req, res) => {
|
||||
if(!req.body.username || !req.body.password) {
|
||||
return res.json({success: false, message: 'Please enter username and password.'});
|
||||
} else {
|
||||
// Attempt to save the user
|
||||
Users.createUser({
|
||||
username: req.body.username,
|
||||
password: req.body.password
|
||||
}, (err, user) => {
|
||||
if (err) {
|
||||
return res.json({success: false, message: 'That username already exists.'});
|
||||
}
|
||||
|
||||
return res.json({success: true, message: `Successfully created new user, ${user.username}.`});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/authenticate', (req, res) => {
|
||||
let credentials = {
|
||||
password: req.body.password,
|
||||
username: req.body.username
|
||||
};
|
||||
|
||||
Users.comparePassword(credentials, function(err, isMatch) {
|
||||
if (isMatch == null) {
|
||||
return res.send({success: false, message: 'Username not found.'});
|
||||
}
|
||||
|
||||
if (isMatch && !err) {
|
||||
// Create token if the password matched and no error was thrown.
|
||||
let token = jwt.sign(credentials, config.secret, {
|
||||
expiresIn: 60 * 60 * 24 * 7 // one week
|
||||
});
|
||||
return res.json({success: true, token: 'JWT ' + token});
|
||||
} else {
|
||||
return res.send({success: false, message: 'Authentication failed. Passwords did not match.'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
let express = require('express');
|
||||
let multer = require('multer');
|
||||
let router = express.Router();
|
||||
|
||||
let ajaxUtil = require('../util/ajaxUtil');
|
||||
let client = require('../models/client');
|
||||
let clientUtil = require('../util/clientUtil');
|
||||
let history = require('../models/history');
|
||||
let router = express.Router();
|
||||
let settings = require('../models/settings');
|
||||
|
||||
let upload = multer({
|
||||
|
||||
Reference in New Issue
Block a user