diff --git a/ldap_config.json b/ldap_config.json index f19e61a42b4b6ebcde0d7a86a60054e8b3ba9045..752a1d8dd50f1ff8242e436c1c8b2deb4e3599a4 100644 --- a/ldap_config.json +++ b/ldap_config.json @@ -71,5 +71,5 @@ "sa": { "attributs": "memberUid" }, - "sessionSecret":"change this" + "sessionSecret":"ozyNMHdT,WFTu|t" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5a7f9c667688d83d032a72a74ae63eafeb40061c..0cb39e42410ac656edf016405d88cd32a15346fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1515,6 +1515,11 @@ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", "dev": true }, + "base64url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" + }, "basic-auth": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", @@ -1721,6 +1726,11 @@ "isarray": "^1.0.0" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-writer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz", @@ -2757,6 +2767,15 @@ "stream-shift": "^1.0.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", + "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", + "requires": { + "base64url": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "editions": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", @@ -5642,6 +5661,30 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, + "jsonwebtoken": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.1.tgz", + "integrity": "sha512-l8rUBr0fqYYwPc8/ZGrue7GiW7vWdZtZqelxo4Sd5lMvuEeCK8/wS54sEo6tJhdZ6hqfutsj6COgC0d1XdbHGw==", + "requires": { + "jws": "^3.1.4", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "xtend": "^4.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -5651,6 +5694,27 @@ "promise": "^7.0.1" } }, + "jwa": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", + "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", + "requires": { + "base64url": "2.0.0", + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.9", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", + "requires": { + "base64url": "^2.0.0", + "jwa": "^1.1.4", + "safe-buffer": "^5.0.1" + } + }, "keyv": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", @@ -6198,6 +6262,41 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.7.tgz", "integrity": "sha512-jzqTi3vk4J5Dxq43cNjB0ekfCjPLHixoY2Sc0WHTo+0r928taLqe/VCt02vY5uQBvg0rdXgL3xWkK4X0MCmZcw==" }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", diff --git a/package.json b/package.json index e66886440735a2b63d893250d64ed0966bd8794c..21127a9bcbee49ab0f282f05abc8671ebee86cd9 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "graphql": "^0.13.2", "graphql-tools": "^2.24.0", "graphql-voyager": "^1.0.0-rc.15", + "jsonwebtoken": "^8.2.1", "knex": "^0.14.6", "ldap-escape": "^1.1.5", "ldapjs": "^1.0.2", diff --git a/src/auth.js b/src/auth.js new file mode 100644 index 0000000000000000000000000000000000000000..119826140a10a6f92ed9109f38a61339507191ba --- /dev/null +++ b/src/auth.js @@ -0,0 +1,71 @@ +import passport from 'passport'; +import LdapStrategy from 'passport-ldapauth'; +import fs from 'fs'; +import path from 'path'; + +/** + * @description Configuration de l'authentification + * @author guillaume.wang + * + * 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 et deserializeUser: passport s'attend a ce qu'on ait besoin d'avoir req.user disponible partout dans notre code + * En gros l'idee de passport c'est: serializeUser permet d'obtenir une cle identifiant chaque user + * et deserializeUser prend cette cle, fait une requete vers une BDD de users 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) + */ +const configPath = path.resolve('./', 'ldap_config.json'); +const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); + +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 pas sword is found, defaults to password + + // LdapStrategy has a default verify callback ! j'ai perdu plein de temps pour rien :'( + // cf. https://github.com/vesse/passport-ldapauth/blob/master/lib/passport-ldapauth/strategy.js, line 195 (` var verify = function() { ... } `) + /* + 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) { + 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) { + done(null, { uid: userUid }); +}); \ No newline at end of file diff --git a/src/server.js b/src/server.js index 512ea4cd4bbbd097cf98c6926b3ffa95cd0f11e1..dba035f832f7ad21e803245f6269e7630f837752 100644 --- a/src/server.js +++ b/src/server.js @@ -11,14 +11,14 @@ import { express as graphqlVoyager } from 'graphql-voyager/middleware'; import graphqlHTTP from 'express-graphql'; // new name of 'graphql-server-express'. cf npmjs.com import flash from 'connect-flash'; import { ensureLoggedIn } from 'connect-ensure-login'; +import './auth'; import passport from 'passport'; -import LdapStrategy from 'passport-ldapauth'; -import fs from 'fs'; import session from 'express-session'; import bodyParser from 'body-parser'; import favicon from 'serve-favicon'; import morgan from 'morgan'; import path from 'path'; +import fs from 'fs'; import cors from 'cors'; const server = express(); @@ -30,59 +30,8 @@ server.use(bodyParser.urlencoded({ //parses bodies of media type "application/x- extended: true //use qs library (quoi que ca veuille dire o.O) })); -/** - * @description Configuration de l'authentification - * @author guillaume.wang - * - * 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 et deserializeUser: passport s'attend a ce qu'on ait besoin d'avoir req.user disponible partout dans notre code - * En gros l'idee de passport c'est: serializeUser permet d'obtenir une cle identifiant chaque user - * et deserializeUser prend cette cle, fait une requete vers une BDD de users 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) - */ -let configPath = path.resolve('./', 'ldap_config.json'); -let config = JSON.parse(fs.readFileSync(configPath, 'utf8')); - -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 pas sword is found, defaults to password - - // LdapStrategy has a default verify callback ! j'ai perdu plein de temps pour rien :'( - // cf. https://github.com/vesse/passport-ldapauth/blob/master/lib/passport-ldapauth/strategy.js, line 195 (` var verify = function() { ... } `) - /* - 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); - } - } - */ -}) -); +const configPath = path.resolve('./', 'ldap_config.json'); +const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); // Définit les paramètres de stockage des sessions. server.use(session({ @@ -93,17 +42,7 @@ server.use(session({ server.use(passport.initialize()); server.use(passport.session()); -//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) { - 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) { - done(null, { uid: userUid }); -}); + /* fin de Configuration de l'authentification */ // cache le fait que l'application tourne sous Express dans le header HTTP. @@ -134,19 +73,6 @@ const corsOptions = { }; server.use(cors(corsOptions)); -/* -server.use('/graphql', bodyParser.json(), graphqlHTTP(async (req, res, params) => ({ - schema: schema, - graphiql: true, - context: { - user: { - uid: req.user ? req.user.uid : defaultUser.dn.split("=")[1].split(",")[0], - password: "mythe" - } - } -}))); -*/ - server.use('/graphql', bodyParser.json(), // parse incoming HTTP request (req) as a JSON graphqlHTTP(async (req, res, params) => { @@ -154,7 +80,7 @@ server.use('/graphql', let uid; let password; - if(req.isAuthenticated) { + if(req.isAuthenticated()) { try { uid = req.user.uid; password = "mythe";