Skip to content
Snippets Groups Projects
Commit e0064876 authored by Wilson JALLET's avatar Wilson JALLET :money_with_wings:
Browse files

Merge branch 'master' into authentication

parents c5de7f00 ba7af693
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
......
import passport from 'passport';
import LdapStrategy from 'passport-ldapauth';
import fs from 'fs';
import path from 'path';
/**
* @description Configuration de passport pour utiliser l'authentification LDAP
* @author kadabra
*
* on a besoin d'authentification pour 2 trucs :
* - l'acces a l'interface admin (admin_view) du back, definie dans admin_router.js
* - le contexte graphQL
*
* serializeUser permet d'obtenir une cle identifiant chaque user
* deserializeUser fait une requete vers une BDD de users en utilisant cette cle, et met dans l'objet JS req.user toutes les infos issues de la BDD
* Cette repartition permet de ne stocker dans la session (i.e. en memoire sur le serveur) que la cle des utilisateurs connectes et de ne "charger en memoire" toutes les infos de la BDD que lorsque necessaire
* cf https://stackoverflow.com/questions/27637609/understanding-passport-serialize-deserialize#27637668
* et http://toon.io/understanding-passportjs-authentication-flow/
*
* Mais en fait dans notre cas c'est graphql qui communique avec la BDD, donc on s'en fiche! On peut se contenter de dire a serializeUser et deserializeUser de ne s'occuper que du champ uid)
* (on pourrait penser que passer par deserializeUser permettrait de reduire le nombre d'interactions avec la BDD, mais en fait non car deserializeUser est appele *a chaque requete*)
*/
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,
//bindDn: '.............',
//bindCredentials: '..........',
searchBase: config.ldap.searchBase,
searchFilter: config.ldap.searchFilter,
//searchAttributes: ['givenName', 'sn'],
//tlsOptions: '..........',
},
//usernameField: 'username', // Field name where the username is found, defaults to username
//passwordField: 'password', // Field name where the password is found, defaults to password
// given how LdapStrategy is coded, it is not necessary to do a verify callback
// https://github.com/vesse/passport-ldapauth/blob/master/lib/passport-ldapauth/strategy.js#L230
// (note that LdapStrategy has no default verify callback, the "verify" function (L105) is actually the "done" function that is called by the verify callback if we choose to make one)
// we leave this commented out as a template for future use
/*
function (user, done) {
// "verify callback", called after each passport.authenticate(...),
// unless missing credentials (in which case a 400 Error is returned)
// "The purpose of a verify callback is to find the user that possesses a set of credentials" (from passport doc)
// i.e. we query the database (in our case the LDAP) to get user's data
console.log("Entering passport's verify callback");
if (user){
//if user exists
console.log("Successfully authenticated " + user.uid);
}
}
*/
}));
//toujours bon a savoir pour faire des tests:
//The result of the serializeUser method is attached to the session as req.session.passport.user
passport.serializeUser(function (user, done) {
console.log(`passport.serializeUser(): serializing user ${user.uid}`); // DEBUG
done(null, user.uid);
});
//The first argument of deserializeUser corresponds to the key of the user object that was given to the done function in serializeUser
//The fetched object is attached to the request object as req.user (available in all subsequent middleware)
passport.deserializeUser(function (userUid, done) {
console.log(`passport.deserializeUser(): deserializing user ${userUid}`); // DEBUG
done(null, { uid: userUid });
});
\ No newline at end of file
/** /**
* @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, () => {
......
...@@ -3,108 +3,126 @@ ...@@ -3,108 +3,126 @@
* *
* 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/auth'; import './auth/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 +136,15 @@ app.post('/login', (req, res, next) => { ...@@ -118,15 +136,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 +178,19 @@ app.post('/login', ...@@ -160,6 +178,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',
graphqlHTTP(async (req, res, params) => { graphqlHTTP(async (req, res, params) => {
...@@ -181,8 +212,8 @@ app.use('/graphql', ...@@ -181,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);
...@@ -195,14 +226,50 @@ app.use('/graphql', ...@@ -195,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