From ba7af69338aad1927051d05c1d433a5ff090c6c8 Mon Sep 17 00:00:00 2001
From: Guillaume WANG <guillaume.wang@polytechnique.edu>
Date: Fri, 4 May 2018 00:50:31 +0200
Subject: [PATCH] reorganized all of middleware stack

---
 src/{routing => admin_view}/admin_router.js |   7 +
 src/{auth.js => config_passport.js}         |   6 +-
 src/index.js                                |  10 +-
 src/routing/admin_router_old.js             | 158 ----------------
 src/server.js                               | 194 +++++++++++++-------
 5 files changed, 141 insertions(+), 234 deletions(-)
 rename src/{routing => admin_view}/admin_router.js (99%)
 rename src/{auth.js => config_passport.js} (94%)
 delete mode 100644 src/routing/admin_router_old.js

diff --git a/src/routing/admin_router.js b/src/admin_view/admin_router.js
similarity index 99%
rename from src/routing/admin_router.js
rename to src/admin_view/admin_router.js
index cff8de3..9248f12 100644
--- a/src/routing/admin_router.js
+++ b/src/admin_view/admin_router.js
@@ -89,6 +89,10 @@ router.post('/avlogout', function (req, res) {
     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) {
@@ -130,6 +134,9 @@ router.get('/db/:table_name?', function (req, res) {
 });
 
 
+
+
+
 /**
  * @function Error 404 catcher
  * @summary Catche les requêtes en dehors des URL acceptées
diff --git a/src/auth.js b/src/config_passport.js
similarity index 94%
rename from src/auth.js
rename to src/config_passport.js
index 424bca8..6ba838b 100644
--- a/src/auth.js
+++ b/src/config_passport.js
@@ -4,7 +4,7 @@ import fs from 'fs';
 import path from 'path';
 
 /**
- * @description Configuration de l'authentification
+ * @description Configuration de passport pour utiliser l'authentification LDAP
  * @author kadabra
  *
  * on a besoin d'authentification pour 2 trucs :
@@ -23,6 +23,7 @@ import path from 'path';
 const configPath = path.resolve('./', 'ldap_config.json');
 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({
     server: {
         url: config.ldap.server,
@@ -56,8 +57,7 @@ passport.use(new LdapStrategy({
         }
     }
     */
-})
-);
+}));
 
 
 //toujours bon a savoir pour faire des tests:
diff --git a/src/index.js b/src/index.js
index 45114ba..ce145b2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,19 +1,11 @@
 /**
- * @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
  */
 import app from './server';
 import colors from 'colors';
-import router from './routing/admin_router';
 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;
 
 app.listen(port, () => {
diff --git a/src/routing/admin_router_old.js b/src/routing/admin_router_old.js
deleted file mode 100644
index 1c198e1..0000000
--- a/src/routing/admin_router_old.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/**
- * @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;
-
diff --git a/src/server.js b/src/server.js
index c68ea1c..61a71ed 100644
--- a/src/server.js
+++ b/src/server.js
@@ -3,108 +3,125 @@
  * 
  * La configuration inclut tout le _middleware_ définissant les API et les services
  * 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 cookieParser from 'cookie-parser';
+import bodyParser from 'body-parser';
+// packages pour graphql
 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 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 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 morgan from 'morgan';
+// packages pour pouvoir importer depuis des fichiers de config
 import path from 'path';
 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
-// cf www.npmjs.com/package/body-parser
+const app = express(); // "The app object conventionally denotes the Express application" (https://expressjs.com/en/4x/api.html#app)
+
+// 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.urlencoded({ //parses bodies of media type "application/x-www-form-urlencoded"
     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 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({
     secret: config.sessionSecret,
     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.session());
+app.use(passport.initialize()); //initialize Passport. (adds hidden field req._passport and do some magic stuff)
+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`.
 // CORS = Cross Origin Resource Sharing
 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
 };
 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:
 //http://www.passportjs.org/docs/authenticate/#custom-callback
 // http://toon.io/understanding-passportjs-authentication-flow/
-
 app.post('/login', (req, res, next) => {
     passport.authenticate('ldapauth', (err, user, info) => {
         // If an exception occurred
@@ -118,15 +135,15 @@ app.post('/login', (req, res, next) => {
         }
         // If authentication failed, user will be set to false
         if (!user) {
-            return res.status(401).json({ 
+            return res.status(401).json({
                 message: "Authentication failed: " + info.message,
                 authSucceeded: false
             });
         }
-        
+
         req.login(user, (err) => {
             // If an exception occurred at login
-            if (err) { 
+            if (err) {
                 console.log(err);
                 return res.status(err.status).json({
                     message: "Exception raised in backend process during login: " + err,
@@ -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', 
     bodyParser.json(), // parse incoming HTTP request (req) as a JSON
@@ -182,8 +212,8 @@ app.use('/graphql',
             }
         } else {
             // FOR DEVELOPMENT ONLY. for production, replace with a "publicUser" or "notLoggedInUser" or something.
-            uid = defaultUser.dn.split("=")[1].split(",")[0];
-            password = defaultUser.passwd;
+            uid = dn.split("=")[1].split(",")[0];
+            password = passwd;
         }
 
         console.log("Cookies:",req.cookies);
@@ -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
+// accessible depuis adminview
 app.use('/voyager',
     /*ensureLoggedIn('/login'),*/
     graphqlVoyager({ endpointUrl: '/graphql' })
 );
 
-// connect-flash is middleware for flashing messages
-// used in sigma-back's admin interface
-app.use(flash());
+// on utilise un express.Router qui sert a creer un "sous-middleware stack".
+app.use('/adminview', router); // catches and resolves HTTP requests to paths '/adminview/*'
+
+// 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;
-- 
GitLab