Added local authentication (AFLOW-32)
This commit is contained in:
parent
347979bb10
commit
af896a6688
53
src/index.ts
53
src/index.ts
@ -5,6 +5,8 @@ import fileUpload from 'express-fileupload';
|
|||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from '@prisma/client';
|
||||||
import * as eta from 'eta';
|
import * as eta from 'eta';
|
||||||
import bodyParser from 'body-parser';
|
import bodyParser from 'body-parser';
|
||||||
|
import session from 'express-session';
|
||||||
|
import passport from 'passport';
|
||||||
|
|
||||||
// Sentry
|
// Sentry
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
@ -28,6 +30,7 @@ export const log = {
|
|||||||
core: coreLogger,
|
core: coreLogger,
|
||||||
db: coreLogger.scope('DB'),
|
db: coreLogger.scope('DB'),
|
||||||
web: coreLogger.scope('WEB'),
|
web: coreLogger.scope('WEB'),
|
||||||
|
auth: coreLogger.scope('AUTH'),
|
||||||
helper: coreLogger.scope('HELPER')
|
helper: coreLogger.scope('HELPER')
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,9 +40,22 @@ export const config = new ConfigHandler(__path + '/config.json', {
|
|||||||
http_listen_address: '127.0.0.1',
|
http_listen_address: '127.0.0.1',
|
||||||
http_port: 3000,
|
http_port: 3000,
|
||||||
sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
|
sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
|
||||||
debug: false
|
debug: false,
|
||||||
|
auth: {
|
||||||
|
local: {
|
||||||
|
active: true,
|
||||||
|
users: {
|
||||||
|
user: 'password',
|
||||||
|
user1: 'password'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
oidc: {
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: Add errorhandling with some sort of message.
|
||||||
export const prisma = new PrismaClient({
|
export const prisma = new PrismaClient({
|
||||||
datasources: {
|
datasources: {
|
||||||
db: {
|
db: {
|
||||||
@ -67,16 +83,16 @@ Sentry.init({
|
|||||||
environment: config.global.debug ? 'development' : 'production'
|
environment: config.global.debug ? 'development' : 'production'
|
||||||
});
|
});
|
||||||
|
|
||||||
app.locals.versionRevLong = require('child_process')
|
// TODO: Version check need to be rewritten.
|
||||||
.execSync('git rev-parse HEAD')
|
app.locals.versionRevLong = require('child_process').execSync('git rev-parse HEAD').toString().trim();
|
||||||
.toString().trim()
|
app.locals.versionRev = require('child_process').execSync('git rev-parse --short HEAD').toString().trim();
|
||||||
app.locals.versionRev = require('child_process')
|
app.locals.versionRevLatest = require('child_process').execSync('git ls-remote --refs -q').toString().trim().split('\t')[0];
|
||||||
.execSync('git rev-parse --short HEAD')
|
|
||||||
.toString().trim()
|
if (app.locals.versionRevLong === app.locals.versionRevLatest) {
|
||||||
app.locals.versionRevLatest = require('child_process')
|
log.core.info(`Running Latest Version (${app.locals.versionRevLong})`);
|
||||||
.execSync('git ls-remote --refs -q')
|
} else {
|
||||||
.toString().trim().split("\t")[0]
|
log.core.info(`Running Version: ${app.locals.versionRevLong} (Latest: ${app.locals.versionRevLatest})`);
|
||||||
log.core.info(`Running revision ${app.locals.versionRevLong} (${app.locals.versionRevLatest} latest)`);
|
}
|
||||||
|
|
||||||
// RequestHandler creates a separate execution context using domains, so that every
|
// RequestHandler creates a separate execution context using domains, so that every
|
||||||
// transaction/span/breadcrumb is attached to its own Hub instance
|
// transaction/span/breadcrumb is attached to its own Hub instance
|
||||||
@ -93,14 +109,23 @@ app.use(bodyParser.urlencoded({ extended: false }));
|
|||||||
// Using bodyParser to parse JSON bodies into JS objects
|
// Using bodyParser to parse JSON bodies into JS objects
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
// Session store
|
||||||
|
// TODO: Move secret to config -> Autogenerate.
|
||||||
|
app.use(
|
||||||
|
session({
|
||||||
|
secret: 'keyboard cat',
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: false,
|
||||||
|
cookie: { secure: false }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
app.use(passport.authenticate('session'));
|
||||||
|
|
||||||
app.use(fileUpload());
|
app.use(fileUpload());
|
||||||
app.use(express.static(__path + '/static'));
|
app.use(express.static(__path + '/static'));
|
||||||
|
|
||||||
app.use(routes);
|
app.use(routes);
|
||||||
|
|
||||||
// The error handler must be before any other error middleware and after all controllers
|
|
||||||
app.use(Sentry.Handlers.errorHandler());
|
|
||||||
|
|
||||||
app.listen(config.global.http_port, config.global.http_listen_address, () => {
|
app.listen(config.global.http_port, config.global.http_listen_address, () => {
|
||||||
log.web.info(`Listening at http://${config.global.http_listen_address}:${config.global.http_port}`);
|
log.web.info(`Listening at http://${config.global.http_listen_address}:${config.global.http_port}`);
|
||||||
});
|
});
|
||||||
|
23
src/middleware/auth.mw.ts
Normal file
23
src/middleware/auth.mw.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
function checkAuthentication(req: any, res: any, next: Function) {
|
||||||
|
if (req.isAuthenticated()) {
|
||||||
|
//req.isAuthenticated() will return true if user is logged in
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
res.redirect('/auth/login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkIsInRole = (...roles) => (req, res, next) => {
|
||||||
|
if (!req.user) {
|
||||||
|
return res.redirect('/login')
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasRole = roles.find(role => req.user.role === role)
|
||||||
|
if (!hasRole) {
|
||||||
|
return res.redirect('/login')
|
||||||
|
}
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
*/
|
@ -1,4 +1,5 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
import passport from 'passport';
|
||||||
|
|
||||||
// Route imports
|
// Route imports
|
||||||
import testRoute from './test.js';
|
import testRoute from './test.js';
|
||||||
|
96
src/routes/auth/index.ts
Normal file
96
src/routes/auth/index.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import passport from 'passport';
|
||||||
|
import { Strategy as LocalStrategy } from 'passport-local';
|
||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
import { prisma, __path, log, config, app } from '../../index.js';
|
||||||
|
|
||||||
|
/* Configure password authentication strategy.
|
||||||
|
*
|
||||||
|
* The `LocalStrategy` authenticates users by verifying a username and password.
|
||||||
|
* The strategy parses the username and password from the request and calls the
|
||||||
|
* `verify` function.
|
||||||
|
*
|
||||||
|
* The `verify` function queries the database for the user record and verifies
|
||||||
|
* the password by hashing the password supplied by the user and comparing it to
|
||||||
|
* the hashed password stored in the database. If the comparison succeeds, the
|
||||||
|
* user is authenticated; otherwise, not.
|
||||||
|
*/
|
||||||
|
passport.use(
|
||||||
|
new LocalStrategy(function verify(username, password, cb) {
|
||||||
|
//log.auth.debug('LocalStrategy:', username, password);
|
||||||
|
|
||||||
|
for (const [user, pass] of Object.entries(config.global.auth.local.users)) {
|
||||||
|
//log.auth.debug('Loop(REQ):', username, password);
|
||||||
|
//log.auth.debug('Loop(CFG):', user, pass);
|
||||||
|
|
||||||
|
if (user === username && pass === password) {
|
||||||
|
log.auth.debug('LocalStrategy: success');
|
||||||
|
return cb(null, { username: username }); // This is the user object.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.auth.debug('LocalStrategy: failed');
|
||||||
|
return cb(null, false, { message: 'Incorrect username or password.' });
|
||||||
|
})
|
||||||
|
/*
|
||||||
|
1. If the user not found in DB,
|
||||||
|
done (null, false)
|
||||||
|
|
||||||
|
2. If the user found in DB, but password does not match,
|
||||||
|
done (null, false)
|
||||||
|
|
||||||
|
3. If user found in DB and password match,
|
||||||
|
done (null, {authenticated_user})
|
||||||
|
*/
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Configure session management.
|
||||||
|
*
|
||||||
|
* When a login session is established, information about the user will be
|
||||||
|
* stored in the session. This information is supplied by the `serializeUser`
|
||||||
|
* function, which is yielding the user ID and username.
|
||||||
|
*
|
||||||
|
* As the user interacts with the app, subsequent requests will be authenticated
|
||||||
|
* by verifying the session. The same user information that was serialized at
|
||||||
|
* session establishment will be restored when the session is authenticated by
|
||||||
|
* the `deserializeUser` function.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
passport.serializeUser(function (user: any, cb) {
|
||||||
|
process.nextTick(function () {
|
||||||
|
log.auth.debug('Called seriealizeUser');
|
||||||
|
log.auth.debug('user:', user);
|
||||||
|
return cb(null, {
|
||||||
|
username: user.username
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.deserializeUser(function (user, cb) {
|
||||||
|
process.nextTick(function () {
|
||||||
|
log.auth.debug('Called deseriealizeUser');
|
||||||
|
return cb(null, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Route imports
|
||||||
|
import testRoute from './test.js';
|
||||||
|
import loginRoute from './login.js';
|
||||||
|
//import logoutRoute from './login.js'
|
||||||
|
|
||||||
|
// Router base is '/auth'
|
||||||
|
const Router = express.Router({ strict: false });
|
||||||
|
|
||||||
|
Router.route('/login').get(loginRoute.get);
|
||||||
|
Router.route('/login').post(passport.authenticate('local', { successRedirect: '/', failureRedirect: '/auth/login?failed' }));
|
||||||
|
|
||||||
|
Router.route('/test').get(checkAuthentication, testRoute.get);
|
||||||
|
|
||||||
|
export default Router;
|
||||||
|
|
||||||
|
function checkAuthentication(req: Request, res: Response, next: Function) {
|
||||||
|
if (req.isAuthenticated()) {
|
||||||
|
//req.isAuthenticated() will return true if user is logged in
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
res.redirect('/auth/login');
|
||||||
|
}
|
||||||
|
}
|
10
src/routes/auth/login.ts
Normal file
10
src/routes/auth/login.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import passport from 'passport';
|
||||||
|
|
||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
import { prisma, __path, log } from '../../index.js';
|
||||||
|
|
||||||
|
function get(req: Request, res: Response) {
|
||||||
|
res.render(__path + '/src/frontend/auth/login.eta.html'); //, { items: items });
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { get };
|
7
src/routes/auth/test.ts
Normal file
7
src/routes/auth/test.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
|
||||||
|
function get(req: Request, res: Response) {
|
||||||
|
res.status(200).send('Auth Test Successful!');
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { get };
|
@ -1,17 +1,23 @@
|
|||||||
import express, { Express } from 'express';
|
import express, { Express } from 'express';
|
||||||
import { __path, prisma } from '../index.js';
|
import { __path, prisma } from '../index.js';
|
||||||
|
import * as Sentry from '@sentry/node';
|
||||||
|
|
||||||
// Route imports
|
// Route imports
|
||||||
import frontend_routes from './frontend/index.js';
|
import frontend_routes from './frontend/index.js';
|
||||||
import static_routes from './static/index.js';
|
import static_routes from './static/index.js';
|
||||||
import api_routes from './api/index.js';
|
import api_routes from './api/index.js';
|
||||||
|
import auth_routes from './auth/index.js';
|
||||||
|
|
||||||
const Router = express.Router({ strict: false });
|
const Router = express.Router({ strict: false });
|
||||||
|
|
||||||
Router.use('/static', static_routes);
|
Router.use('/static', static_routes);
|
||||||
Router.use('/api', api_routes);
|
Router.use('/api', api_routes);
|
||||||
|
Router.use('/auth', auth_routes);
|
||||||
Router.use('/', frontend_routes);
|
Router.use('/', frontend_routes);
|
||||||
|
|
||||||
|
// The error handler must be before any other error middleware and after all controllers
|
||||||
|
Router.use(Sentry.Handlers.errorHandler());
|
||||||
|
|
||||||
// Default route.
|
// Default route.
|
||||||
Router.all('*', function (req, res) {
|
Router.all('*', function (req, res) {
|
||||||
// TODO: Respond based on content-type (with req.is('application/json'))
|
// TODO: Respond based on content-type (with req.is('application/json'))
|
||||||
@ -23,3 +29,4 @@ Router.all('*', function (req, res) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default Router;
|
export default Router;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user