diff --git a/config/default.json b/config/default.json index 1a509087939daca3bf09b1f1fdfdd6c33692925d..cd012ee147ca9726b86ab669c2270ca95a6fac18 100644 --- a/config/default.json +++ b/config/default.json @@ -29,5 +29,9 @@ "mail_smtp_secure": true, "mail_smtp_require_tls": true, "mail_smtp_user": "your.smtp.user", - "mail_smtp_pass": "your.secret.smtp.password" + "mail_smtp_pass": "your.secret.smtp.password", + + "path" : "http://localhost:9666/saml/SSO", + "entryPoint" : "https://m4lab.hft-stuttgart.de/idp/saml2/idp/SSOService.php", + "issuer" : "spacedeck.m4lab.hft-stuttgart.de" } diff --git a/package.json b/package.json index d53987bab436aad7230a524b52c0f2349ccf3756..d3991ee6d1c6ed41250d55b4fd6727be7da2978a 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,12 @@ "node-phantom-simple": "2.2.4", "node-server-screenshot": "^0.2.1", "nodemailer": "^4.6.7", + "passport": "^0.4.1", + "passport-saml": "^1.3.5", "phantomjs-prebuilt": "^2.1.16", "read-chunk": "^2.1.0", "request": "^2.88.0", + "saml2js": "^0.1.2", "sanitize-html": "^1.11.1", "sequelize": "^4.37.6", "serve-favicon": "~2.4.2", diff --git a/routes/api/sessions.js b/routes/api/sessions.js index 17c7be9afccac16aa2fec61fa5b5ce80e38778c3..93c9d714a8933aa04abf9b20d807923b4991027a 100644 --- a/routes/api/sessions.js +++ b/routes/api/sessions.js @@ -57,6 +57,7 @@ router.post('/', function(req, res) { }); }); + router.delete('/current', function(req, res, next) { if (req.user) { var token = req.cookies['sdsession']; diff --git a/routes/root.js b/routes/root.js index f2e18564f48e01afd6557584fa762e78219dd84c..9cd43aa911d9b549aba393fb35dea353ef996b28 100644 --- a/routes/root.js +++ b/routes/root.js @@ -9,11 +9,87 @@ const router = express.Router(); const mailer = require('../helpers/mailer'); const _ = require('underscore'); +const fs = require('fs') +const SamlStrategy = require('passport-saml').Strategy +const passport = require('passport') +const Saml2js = require('saml2js'); + const db = require('../models/db'); const Sequelize = require('sequelize'); const Op = Sequelize.Op; const uuidv4 = require('uuid/v4'); + +// =========== PASSPORT ======= + passport.serializeUser(function (user, done) { + done(null, user); + }); + + passport.deserializeUser(function (user, done) { + done(null, user); + }); + + var samlStrategy = new SamlStrategy({ + // URL that goes from the Identity Provider -> Service Provider + callbackUrl: config.path, + + entryPoint: config.entryPoint, + issuer: config.issuer, + identifierFormat: null, + + validateInResponseTo: false, + disableRequestedAuthnContext: true + }, + function (profile, done) { + return done(null, { + id: profile.nameID, + idFormat: profile.nameIDFormat, + email: profile.email, + firstName: profile.givenName, + lastName: profile.sn + }); + }); + + passport.use(samlStrategy); + + // to generate Service Provider's XML metadata + router.get('/saml/metadata', + function(req, res) { + res.type('application/xml'); + var spMetadata = samlStrategy.generateServiceProviderMetadata(fs.readFileSync('/cert/certificate.pem', 'utf8')); + res.status(200).send(spMetadata); + } + ); + +router.post('/saml/SSO', passport.authenticate('saml', { failureRedirect: '/login', failureFlash: true}), function(req, res){ + const xmlResponse = req.body.SAMLResponse; + const parser = new Saml2js(xmlResponse); + const userid = parser.get('email'); + crypto.randomBytes(48, function(ex, buf) { + var token = buf.toString('hex'); + + var session = { + user_id: userid, + token: token, + ip: req.ip, + device: "web", + created_at: new Date() + }; + + db.Session.create(session) + .error(err => { + console.error("Error creating Session:",err); + res.sendStatus(500); + }) + .then(() => { + var domain = (process.env.NODE_ENV == "production") ? new URL(config.get('endpoint')).hostname : req.headers.hostname; + res.cookie('sdsession', token, { domain: domain, httpOnly: true }); + res.status(201).json(session); + }); + res.redirect("/"); + }); +}); + router.get('/', (req, res) => { res.render('index', { config:config, user:req.user }); }); @@ -90,9 +166,16 @@ router.get('/account', (req, res) => { res.render('spacedeck'); }); -router.get('/login', (req, res) => { - res.render('spacedeck', { config:config, user:req.user }); -}); +router.get('/login', passport.authenticate('saml', + { + successRedirect: '/', + failureRedirect: '/login' + }) +); + + +// res.render('spacedeck', { config:config, user:req.user }); +//}); router.get('/logout', (req, res) => { res.render('spacedeck', { config:config, user:req.user }); @@ -134,4 +217,4 @@ router.get('/spaces/:id', (req, res) => { res.render('spacedeck', { config:config, user:req.user }); }); -module.exports = router; +module.exports = {router: router, passport:passport}; diff --git a/spacedeck.js b/spacedeck.js index 233e21e9be5b6117f959472a77035c4d5e4cbbc1..6ac9dcbf4322bacba925999ba25ad19235666c34 100644 --- a/spacedeck.js +++ b/spacedeck.js @@ -22,7 +22,6 @@ const helmet = require('helmet'); const express = require('express'); const app = express(); const serveStatic = require('serve-static'); - const isProduction = app.get('env') === 'production'; // workaround for libssl_conf.so error triggered by phantomjs @@ -92,7 +91,7 @@ spaceRouter.use('/:id', require('./routes/api/space_exports')); app.use('/api/sessions', require('./routes/api/sessions')); //app.use('/api/webgrabber', require('./routes/api/webgrabber')); -app.use('/', require('./routes/root')); + if (config.get('storage_local_path')) { app.use('/storage', serveStatic(config.get('storage_local_path')+"/"+config.get('storage_bucket'), { @@ -108,6 +107,13 @@ if (app.get('env') == 'development') { app.use(require('./middlewares/500')); } +const root = require('./routes/root'); + +const passport = root.passport; +app.use(passport.initialize()); +app.use(passport.session()); +app.use('/', root.router); + module.exports = app; // CONNECT TO DATABASE