Forked from an inaccessible project.
-
Quentin CHEVALIER authoredQuentin CHEVALIER authored
app.ts 10.48 KiB
/**
* @file Initialise et configure le serveur Express sur lequel tourne le back.
*
* La configuration inclut tout le _middleware_ définissant les API et les services
* nécessaire utilisés, comme `express-session`, GraphiQL, GraphQL Voyager.
*
* @todo changer cette description... ^
* @todo 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 bodyParser from 'body-parser';
// packages pour graphql
import { express as graphqlVoyager } from 'graphql-voyager/middleware';
// replacement of express-graphql, which hasn't been updated in 6 months
import { ApolloServer } from 'apollo-server-express'; //hawkspar->manifold VSCode râle ici pr moi
// typeDefs and resolvers
import schema from './graphql/schema';
// packages pour adminview
import { ensureLoggedIn } from 'connect-ensure-login';
import flash from 'connect-flash';
import router from './routing/admin.router';
// packages pour l'authentification
import passport from 'passport';
import session from 'express-session';
import cookieParser from 'cookie-parser'; //hawkspar->manifold VSCode râle ici pr moi
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 { ldapConfig, credentialsConfig } from './ldap/config';
const { dn, passwd } = credentialsConfig;
// "The app object conventionally denotes the Express application"
// see https://expressjs.com/en/4x/api.html#app
const app = express();
// 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)
}));
//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.
app.use(cookieParser());
// cache le fait que l'application tourne sous Express dans le header HTTP.
app.disable('x-powered-by');
app.use(morgan('dev'));
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')));
/**
* @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.
*/
/**
* WTF??? why is sessionSecret in ldap_config.json? it has nothing to do with ldap.
* @todo FIX
*/
/**
/* 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 do this right, express-session docs warns that isn't for prod!
* 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: ldapConfig.sessionSecret,
resave: true,
saveUninitialized: false,
}));
app.use(passport.initialize());
//GHETTO
//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'))
//this is equivalent to app.use(passport.authenticate('session'))
app.use(passport.session(), (req, res, next)=>{
const user = req.user ? req.user.uid : "none";
console.log(
`passport.session: found user: ${user}, authenticated: ${req.isAuthenticated()}`);
next();
});
// *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... :/
// connect-flash is middleware for flashing messages, used in adminview
app.use(flash());
/**
* @desc SETUP DE ADMINVIEW
*/
// 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');
/**
* @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 FRONTEND_SERVER_URL = process.env.FRONTEND_SERVER_URL || 'http://localhost:8888';
// Options de configuration pour le _middleware_ `cors`.
// CORS = Cross Origin Resource Sharing
const corsOptions = {
origin: FRONTEND_SERVER_URL, // 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));
//GHETTO
// Config de passport pour l'authentification ldap. Ne fait que *configurer* passport (aucun passport.authenticate() n'est appele, par exemple)
import './config_passport';
//with custom callback:
//http://www.passportjs.org/docs/authenticate/#custom-callback
// http://toon.io/understanding-passportjs-authentication-flow/
app.post('/login', (req, res, next) => {
console.log("Received an authentication request to /login");
passport.authenticate('ldapauth', (err, user, info) => {
console.log("| Entering passport.authenticate('ldapauth', - ) callback");
// If an exception occurred
if (err) {
console.log("| Error when trying to passport.authenticate with ldapauth");
console.log(err);
return res.status(err.status).json({
message: "Exception raised in backend process during authentication: " + err,
authSucceeded: false
});
// return next(err); // handle error? or drop request and answer with res.json()?
}
// If authentication failed, user will be set to false
if (!user) {
console.log("| Authentication failed, passport.authenticate did not return a user. ");
return res.status(401).json({
message: "Authentication failed: " + info.message,
authSucceeded: false
});
}
req.login(user, (err) => {
// If an exception occurred at login
if (err) {
console.log("| Error when trying to req.login in callback in passport.authenticate('ldapauth', - )");
console.log(err);
return res.status(err.status).json({
message: "Exception raised in backend process during login: " + err,
authSucceeded: false
});
// return next(err); // handle error? or drop request and answer with res.json()?
}
// If all went well
console.log("| Authentication succeeded! :)");
// passport.authenticate automatically includes a Set-Cookie HTTP header in
// the response. The JSON body is just to signal the frontend that all went well
return res.status(200).json({
message: 'Authentication succeeded',
authSucceeded: true
});
});
})(req, res, next);
});
//without custom callback:
/*
// http://toon.io/understanding-passportjs-authentication-flow/
app.post('/login',
passport.authenticate('ldapauth'),
function (req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
console.log("Frontend authentication succeeded");
res.json({
message: 'Authentication succeeded',
authSucceeded: true
});
}
);
*/
/**
* @desc API GraphQL
*/
/**
* @desc Define GraphQL request context object, through a callback, with authorization.
* See: https://github.com/apollographql/apollo-server/blob/master/docs/source/best-practices/authentication.md
*
*/
const context = async ({ req }) => {
let uid;
let password;
console.log("Responding to graphql request...");
console.log(`
| User: ${req.user ? req.user.uid : "none"}
| Authorization: ${req.headers.authorization}
| Authenticated: ${req.isAuthenticated()}
`.trim());
if(req.isAuthenticated()) {
console.log("graphql API is receiving a request from an authenticated user! \\o/");
try {
uid = req.user.uid;
password = "mythe";
} catch (err) {
console.log("Error: req is authenticated, but pb when trying to extract uid from req.user. Probably user was either not serialized or not deserialized properly");
console.log(err);
}
} else {
// FOR DEVELOPMENT ONLY. for production, replace with a "publicUser" or "notLoggedInUser" or something.
uid = dn.split("=")[1].split(",")[0];
password = passwd;
}
return {
request: req,
user: { uid, password }
}
}
const server = new ApolloServer({
...schema,
context,
playground: {
settings: {
"editor.theme": "dark",
"editor.cursorShape": 'line'
}
}
});
server.applyMiddleware({ app });
// GraphQL voyager affiche une représentation sous forme de graphe du schema GraphQL
// accessible depuis adminview
app.use('/voyager',
/*ensureLoggedIn('/login'),*/
graphqlVoyager({ endpointUrl: '/graphql' })
);
// 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/*'
app.use('/', (req, res) => {
res.redirect('/adminview');
});
export default app;