Skip to content
Snippets Groups Projects
Commit ba7af693 authored by Guillaume WANG's avatar Guillaume WANG
Browse files

reorganized all of middleware stack

parent a3a49b3e
No related branches found
No related tags found
No related merge requests found
...@@ -89,6 +89,10 @@ router.post('/avlogout', function (req, res) { ...@@ -89,6 +89,10 @@ router.post('/avlogout', function (req, res) {
res.redirect('/adminview'); res.redirect('/adminview');
}); });
// je pense qu'on ferait mieux d'utiliser ca // je pense qu'on ferait mieux d'utiliser ca
// https://expressjs.com/en/4x/api.html#router.route // https://expressjs.com/en/4x/api.html#router.route
router.get('/db?', function (req, res) { router.get('/db?', function (req, res) {
...@@ -130,6 +134,9 @@ router.get('/db/:table_name?', function (req, res) { ...@@ -130,6 +134,9 @@ router.get('/db/:table_name?', function (req, res) {
}); });
/** /**
* @function Error 404 catcher * @function Error 404 catcher
* @summary Catche les requêtes en dehors des URL acceptées * @summary Catche les requêtes en dehors des URL acceptées
......
...@@ -4,7 +4,7 @@ import fs from 'fs'; ...@@ -4,7 +4,7 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
/** /**
* @description Configuration de l'authentification * @description Configuration de passport pour utiliser l'authentification LDAP
* @author kadabra * @author kadabra
* *
* on a besoin d'authentification pour 2 trucs : * on a besoin d'authentification pour 2 trucs :
...@@ -23,6 +23,7 @@ import path from 'path'; ...@@ -23,6 +23,7 @@ import path from 'path';
const configPath = path.resolve('./', 'ldap_config.json'); const configPath = path.resolve('./', 'ldap_config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
// specifies options for 'ldapauth' strategy, to customize the behaviour of subsequent passport.authenticate('ldapauth') calls
passport.use(new LdapStrategy({ passport.use(new LdapStrategy({
server: { server: {
url: config.ldap.server, url: config.ldap.server,
...@@ -56,8 +57,7 @@ passport.use(new LdapStrategy({ ...@@ -56,8 +57,7 @@ passport.use(new LdapStrategy({
} }
} }
*/ */
}) }));
);
//toujours bon a savoir pour faire des tests: //toujours bon a savoir pour faire des tests:
......
/** /**
* @file Lance le serveur configuré dans {@link server.js} en y ajoutant le routeur d'URL. * @file Lance le serveur configuré dans {@link server.js}
* @author manifold * @author manifold
*/ */
import app from './server'; import app from './server';
import colors from 'colors'; import colors from 'colors';
import router from './routing/admin_router';
import passport from 'passport'; import passport from 'passport';
// setting up l'interface admin des BDD
app.use('/adminview',router); // catches and resolves HTTP requests to paths '/adminview/*'
app.get('/', // catches all other GET requests
((req, res, next) => res.redirect('/adminview'))
);
let port = process.env.PORT || 3000; let port = process.env.PORT || 3000;
app.listen(port, () => { app.listen(port, () => {
......
/**
* @file Ce fichier définit le routage d'URL au sein de l'interface du _backend_.
* Il définit la page de connexion `/`, le panneau administrateur `/admin` et l'API REST \(`/db/:table?`)
* permettant de consulter la base de donnée interne à Sigma, via des requêtes construites avec Knex.
* @author manifold
*
* Les res.redirect() sont censes supporter les paths relatifs (et donc pas besoin de repreciser /adminview/* a chaque fois)
* mais ca marche visiblement pas... Donc j'ai mis les paths absolus dans les res.redirect().
*/
import express from 'express';
import knex from '../../db/knex_router';
import passport from 'passport';
import { ensureLoggedIn } from 'connect-ensure-login';
const router = express.Router();
let port = process.env.PORT || 3000;
/**
* @description Le login se fait en POST. Faire un GET à la racine / renvoie sur
* /login ou sur /admin selon que l'utilisateur est connecté ou non.
*/
router.get('/', function (req, res) {
console.log("GET handler for /adminview route");
console.log('Connecting to ' + req.url);
console.log('Trying to go to admin page...');
res.redirect('/adminview/admin');
});
router.get('/avlogin', function (req, res) {
console.log('Connecting to ' + req.url);
res.render('login', { title: 'Login', port: port,
errorMessage: req.flash('error') }); //lets pug render src/views/login.pug with specified attributes
});
router.get('/admin',
ensureLoggedIn('/adminview/avlogin'),
function (req, res) {
console.log('Connecting to ' + req.url);
let userName;
// Une erreur a ce stade peut etre triggered si req.user n'existe pas
// mais pour autant on est assures que la personne est bien authentifiee
// donc on laisse passer sans déclencher d'erreur 500
try {
let user = req.user;
//let user = req.user;
console.log('Welcome,',user.uid);
userName = user.uid;
} catch (err) {
console.log("Warning: in admin_router router.get('/admin')");
console.log(err.message);
userName = "No one";
}
res.render('home', { title: 'Home', port: port, userName: userName });
}
);
router.post('/avlogin',
passport.authenticate('ldapauth', {
successRedirect: '/adminview/admin',
failureRedirect: '/adminview/avlogin',
failureFlash: true
}
// on a besoin de faire un callback apres le passport.authenticate car
// on souhaite garde l'information user.dn et body.password qq part.
// TODO: essayer de garder ces informations plus proprement...
// EDIT: en fait apparemment on a pas besoin de ces informations du tout
/*
function (req, res) {
req.session.dn = req.user.dn;
req.session.password = req.body.password;
if (req.session.returnTo !== undefined) {
res.redirect(req.session.returnTo); //TODO: <- euh ok ca marche mais c'est quoi ca?
} else {
res.redirect("/admin");
}
}
*/
)
);
router.post('/avlogout', function (req, res) {
req.logout();
res.redirect('/adminview');
});
// je pense qu'on ferait mieux d'utiliser ca
// https://expressjs.com/en/4x/api.html#router.route
router.get('/db?', function (req, res) {
let table_name = req.query.table;
let columns = req.query.columns;
res.redirect(`/adminview/db/${table_name}?columns=${columns}`);
});
/**
* @function Knex API: Get table
* @summary Effectue une requête pour une table dans la BDD
* @argument {string} table_name - La table voulue par l'utilisateur.
*/
router.get('/db/:table_name?', function (req, res) {
// get columns from query
let columns;
if (req.query.columns) {
columns = req.query.columns.split(',');
} else {
columns = null;
}
console.log(columns);
knex.select(columns).from(req.params.table_name).then(function (table) {
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify(table, null, 2));
res.end();
}, function () {
res.status(400);
res.render('error', {
status: res.statusCode,
error_message: "Bad request: can't find table " + req.params.table_name
});
res.end();
}
);
});
/**
* @function Error 404 catcher
* @summary Catche les requêtes en dehors des URL acceptées
*/
router.use((req, res, next) => {
let err = new Error('Not found');
err.status = 404;
next(err);
});
/**
* @function Error 404 handler
* @summary Gère les erreurs 404
*/
router.use((err, req, res, next) => {
console.log("Entering error handler");
res.locals.message = err.message;
console.log(err.message);
res.status(err.status || 500);
let error_message = res.statusCode == 404 ? 'Not found.' : 'Internal server error.';
res.render('error', {
status: res.statusCode,
error_message: error_message
});
});
export default router;
...@@ -3,108 +3,125 @@ ...@@ -3,108 +3,125 @@
* *
* La configuration inclut tout le _middleware_ définissant les API et les services * La configuration inclut tout le _middleware_ définissant les API et les services
* nécessaire utilisés, comme `express-session`, GraphiQL, GraphQL Voyager. * nécessaire utilisés, comme `express-session`, GraphiQL, GraphQL Voyager.
* @author manifold *
* TODO: changer cette description... ^
* TODD: qu'arrive-t-il aux requetes avec un cookie expire? elles ne sont traitees ni par passport.session() ni par passport.authenticate('ldapauth')...
*
* @author manifold, kadabra
*/ */
import express from 'express'; import express from 'express';
import cookieParser from 'cookie-parser'; import bodyParser from 'body-parser';
// packages pour graphql
import { express as graphqlVoyager } from 'graphql-voyager/middleware'; import { express as graphqlVoyager } from 'graphql-voyager/middleware';
import graphqlHTTP from 'express-graphql'; // new name of 'graphql-server-express'. cf npmjs.com import graphqlHTTP from 'express-graphql'; // new name of 'graphql-server-express' (mai 2018). cf npmjs.com.
import schema from './graphql/schema';
// packages pour adminview
import { ensureLoggedIn } from 'connect-ensure-login'; import { ensureLoggedIn } from 'connect-ensure-login';
import flash from 'connect-flash'; // utilise pour admin_view import flash from 'connect-flash';
import router from './admin_view/admin_router';
// packages pour l'authentification
import passport from 'passport'; import passport from 'passport';
import session from 'express-session'; import session from 'express-session';
import bodyParser from 'body-parser'; import cookieParser from 'cookie-parser';
import cors from 'cors';
// packages divers
import favicon from 'serve-favicon'; import favicon from 'serve-favicon';
import morgan from 'morgan'; import morgan from 'morgan';
// packages pour pouvoir importer depuis des fichiers de config
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import cors from 'cors';
import schema from './graphql/schema';
import './auth';
const app = express();
//The app object conventionally denotes the Express application
// Parse incoming HTTP request bodies, available under the req.body property const app = express(); // "The app object conventionally denotes the Express application" (https://expressjs.com/en/4x/api.html#app)
// cf www.npmjs.com/package/body-parser
// Parse incoming HTTP request bodies, available under the req.body property. cf www.npmjs.com/package/body-parser
app.use(bodyParser.json()); //parses bodies of media type "application/json" app.use(bodyParser.json()); //parses bodies of media type "application/json"
app.use(bodyParser.urlencoded({ //parses bodies of media type "application/x-www-form-urlencoded" app.use(bodyParser.urlencoded({ //parses bodies of media type "application/x-www-form-urlencoded"
extended: true //use qs library (quoi que ca veuille dire o.O) extended: true //use qs library (quoi que ca veuille dire o.O)
})); }));
app.use(cookieParser());
/**
* @desc TRUCS DIVERS
*/
// cache le fait que l'application tourne sous Express dans le header HTTP.
app.disable('x-powered-by');
// Morgan is middleware for logging requests
app.use(morgan('dev'));
// favicon: capital sigma symbol
app.use(favicon(path.resolve('./', 'assets', 'favicon.ico')));
// specifies path to static assets. ......je comprends pas ce que c'est. TODO
app.use('/assets', express.static(path.resolve('./', 'assets')));
/**
* FIN TRUCS DIVERS
*/
/**
* @desc AUTHENTIFICATION POUR LES REQUETES POSSEDANT UN COOKIE ET PROVENANT D'UN UTILISATEUR DEJA AUTHENTIFIE
* Remarque: introduit aussi les middlewares session et passport, qui sont aussi utiles pour l'authentification dans les autres cas.
*/
const configPath = path.resolve('./', 'ldap_config.json'); const configPath = path.resolve('./', 'ldap_config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
// WTF??? why is sessionSecret in ldap_config.json? it has nothing to do with ldap. TODO
// Config de passport : le "import './auth';" plus haut execute la configuration de l'objet passport. cf, donc, auth.js //app.use(cookieParser()); //parses Cookie header and populate req.cookies with an object keyed by the cookie names. was necessary for express-session before its v1.5.0. on peut probablement l'enlever desormais.
// Définit les paramètres de stockage des sessions. // defines parameters for *session store*. (adds field req.session and do some magic stuff)
// basically, searches for a session matching the received cookie and, if found, adds field req.blasomethingbla containing serialized object representing user (i.e. similar to what passport.serializeUser() could produce)
// TODO: it is important to configure this right!!! please check out https://www.npmjs.com/package/express-session and make sure you understand the way session is stored. (en vrai c'est vraiment important...)
app.use(session({ app.use(session({
secret: config.sessionSecret, secret: config.sessionSecret,
resave: true, resave: true,
saveUninitialized: false saveUninitialized: false,
//store: // TODO: change this. express-session doc warns that default value is ok to use for development only
})); }));
app.use(passport.initialize()); app.use(passport.initialize()); //initialize Passport. (adds hidden field req._passport and do some magic stuff)
app.use(passport.session()); app.use(passport.session()); //this is equivalent to app.use(passport.authenticate('session'))
// *aucun* effet sur les requetes n'ayant pas ete reconnues par app.use(session(...)) (e.g. les requetes sans cookie ou les requetes avec cookie expired). source: lecture directe du code passport/lib/strategies/session.js sur github... :/
/**
* FIN AUTHENTIFICATION POUR LES REQUETES POSSEDANT UN COOKIE ET PROVENANT D'UN UTILISATEUR DEJA AUTHENTIFIE
*/
// cache le fait que l'application tourne sous Express dans le header HTTP.
app.disable('x-powered-by');
// setting up view engine for pug
console.log("Running at",__dirname);
let viewpath = path.resolve(__dirname,'views');
app.set('views', viewpath);
app.set('view engine', 'pug');
// favicon: capital sigma symbol
app.use(favicon(path.resolve('./','assets','favicon.ico')));
// specifies path to static assets
app.use('/assets',express.static(path.resolve('./','assets')));
// Morgan is middleware for logging requests /**
app.use(morgan('dev')); * @desc AUTHENTIFICATION POUR LES REQUETES DE CONNEXION VIA LDAP VENANT DU FRONT
* i.e. endpoint for *frontend's* authentication requests (login through adminview/avlogin are caught by the router in admin_router.js)
* i.e. quand l'utilisateur submit le formulaire de login avec ses identifiants/mdp dans le front
* Remarque: configure aussi passport pour l'authentification ldap, ce qui est aussi utile pour les requetes de connexion via ldap venant de adminview
*/
const defaultUser = require('./../ldap_connexion_config.json'); const FRONTEND_SERVER_URL = 'change this to frontend server IP address';
const FRONTEND_SERVER_URL_LOCAL = 'http://localhost:8888';
// Options de configuration pour le _middleware_ `cors`. // Options de configuration pour le _middleware_ `cors`.
// CORS = Cross Origin Resource Sharing // CORS = Cross Origin Resource Sharing
const corsOptions = { const corsOptions = {
origin: 'http://localhost:8888', // Configures the Access-Control-Allow-Origin CORS header. i.e. specifies that sigma-back wants to make resources accessible to this site (and this site only) origin: FRONTEND_SERVER_URL_LOCAL, // Configures the Access-Control-Allow-Origin CORS header. i.e. specifies that sigma-back wants to make resources accessible to this site (and this site only)
credentials: true // Configures the Access-Control-Allow-Credentials CORS header. i.e. allows cookies to be included on cross-origin requests credentials: true // Configures the Access-Control-Allow-Credentials CORS header. i.e. allows cookies to be included on cross-origin requests
}; };
app.use(cors(corsOptions)); app.use(cors(corsOptions));
const SECRET_KEY = "azojgc;aegpfrihzcksdlmpqsqkx"; // Config de passport pour l'authentification ldap. Ne fait que *configurer* passport (aucun passport.authenticate() n'est appele, par exemple)
import './config_passport.js';
/*
const addUser = async (req, res, next) => {
const token = req.cookies.csrftoken;
if (!token) return next();
console.log(`Token is ${token}`);
try {
const { user } = jwt.verify(token, SECRET_KEY);
req.user = user;
} catch (err) {
console.log('Cookie error',err);
}
};
app.use(addUser);
*/
//endpoint for frontend's authentication requests
//with custom callback: //with custom callback:
//http://www.passportjs.org/docs/authenticate/#custom-callback //http://www.passportjs.org/docs/authenticate/#custom-callback
// http://toon.io/understanding-passportjs-authentication-flow/ // http://toon.io/understanding-passportjs-authentication-flow/
app.post('/login', (req, res, next) => { app.post('/login', (req, res, next) => {
passport.authenticate('ldapauth', (err, user, info) => { passport.authenticate('ldapauth', (err, user, info) => {
// If an exception occurred // If an exception occurred
...@@ -118,15 +135,15 @@ app.post('/login', (req, res, next) => { ...@@ -118,15 +135,15 @@ app.post('/login', (req, res, next) => {
} }
// If authentication failed, user will be set to false // If authentication failed, user will be set to false
if (!user) { if (!user) {
return res.status(401).json({ return res.status(401).json({
message: "Authentication failed: " + info.message, message: "Authentication failed: " + info.message,
authSucceeded: false authSucceeded: false
}); });
} }
req.login(user, (err) => { req.login(user, (err) => {
// If an exception occurred at login // If an exception occurred at login
if (err) { if (err) {
console.log(err); console.log(err);
return res.status(err.status).json({ return res.status(err.status).json({
message: "Exception raised in backend process during login: " + err, message: "Exception raised in backend process during login: " + err,
...@@ -160,6 +177,19 @@ app.post('/login', ...@@ -160,6 +177,19 @@ app.post('/login',
); );
*/ */
/**
* FIN AUTHENTIFICATION POUR LES REQUETES DE CONNEXION VIA LDAP VENANT DU FRONT
*/
/**
* @desc API GRAPHQL
*/
import { dn, passwd } from "../ldap_connexion_config.json"; // default user
app.use('/graphql', app.use('/graphql',
bodyParser.json(), // parse incoming HTTP request (req) as a JSON bodyParser.json(), // parse incoming HTTP request (req) as a JSON
...@@ -182,8 +212,8 @@ app.use('/graphql', ...@@ -182,8 +212,8 @@ app.use('/graphql',
} }
} else { } else {
// FOR DEVELOPMENT ONLY. for production, replace with a "publicUser" or "notLoggedInUser" or something. // FOR DEVELOPMENT ONLY. for production, replace with a "publicUser" or "notLoggedInUser" or something.
uid = defaultUser.dn.split("=")[1].split(",")[0]; uid = dn.split("=")[1].split(",")[0];
password = defaultUser.passwd; password = passwd;
} }
console.log("Cookies:",req.cookies); console.log("Cookies:",req.cookies);
...@@ -196,14 +226,50 @@ app.use('/graphql', ...@@ -196,14 +226,50 @@ app.use('/graphql',
}) })
); );
/**
* FIN API GRAPHQL
*/
/**
* @desc SETUP DE ADMINVIEW, L'INTERFACE ADMIN DES BDD
* Remarque: le graphiql est desormais integre a express-graphql (mai 2018), donc il n'est plus possible de le proteger par connect-ensure-login, ni donc de considerer qu'il fait partie de l'adminview.
* C'est relativement grave, car n'importe qui se connectant directement a [adresse_IP_de_roued]/graphql peut faire des requetes a la base de donnees de sigma
* Donc il faudra retirer cette fonctionnalite en production ; or elle est pratique, meme en production, pour des sanity checks.
* ...bref integrer graphiql a express-graphql etait completement con.
*/
// setting up view engine for pug, for adminview
console.log("Running at", __dirname);
let viewpath = path.resolve(__dirname, 'views');
app.set('views', viewpath);
app.set('view engine', 'pug');
// connect-flash is middleware for flashing messages, used in adminview
app.use(flash());
// GraphQL voyager affiche une représentation sous forme de graphe du schema GraphQL // GraphQL voyager affiche une représentation sous forme de graphe du schema GraphQL
// accessible depuis adminview
app.use('/voyager', app.use('/voyager',
/*ensureLoggedIn('/login'),*/ /*ensureLoggedIn('/login'),*/
graphqlVoyager({ endpointUrl: '/graphql' }) graphqlVoyager({ endpointUrl: '/graphql' })
); );
// connect-flash is middleware for flashing messages // on utilise un express.Router qui sert a creer un "sous-middleware stack".
// used in sigma-back's admin interface app.use('/adminview', router); // catches and resolves HTTP requests to paths '/adminview/*'
app.use(flash());
// catch all other GET requests.
// il est bien sur essentiel de mettre ceci a la toute fin du middleware stack !
app.get('/*',
((req, res, next) => res.redirect('/adminview'))
);
/**
* FIN SETUP DE ADMINVIEW, L'INTERFACE ADMIN DES BDD
*/
export default app; export default app;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment