Fixed auth strategies when using with a second domain with a DNS (#4404)

Ylian Saint-Hilaire committed Aug 17, 2022 at 14:14 UTC 4092615c631efe3aab5dfb1f0e7936de707e5eaf
1 file changed +299 -215
webserver.js
+299 -215
@@ -6021,6 +6021,23 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6021 if ((parent.config.domains[i].dns == null) && (parent.config.domains[i].share != null)) { obj.app.use(parent.config.domains[i].url, obj.express.static(parent.config.domains[i].share)); }
6022 }
6023
6024 + // Setup all domain auth strategy passport.js
6025 + for (var i in parent.config.domains) {
6026 + if (typeof parent.config.domains[i].authstrategies == 'object') {
6027 + parent.config.domains[i].authstrategies.authStrategyFlags = 0;
6028 + const authStrategyFlags = setupDomainAuthStrategy(parent.config.domains[i]);
6029 + if (authStrategyFlags > 0) {
6030 + if (parent.config.domains[i].dns != null) {
6031 + if (typeof parent.config.domains[''].authstrategies != 'object') { parent.config.domains[''].authstrategies = { authStrategyFlags: 0 }; }
6032 + parent.config.domains[''].authstrategies.authStrategyFlags |= authStrategyFlags;
6033 + } else {
6034 + if (typeof parent.config.domains[i].authstrategies != 'object') { parent.config.domains[i].authstrategies = { authStrategyFlags: 0 }; }
6035 + parent.config.domains[i].authstrategies.authStrategyFlags |= authStrategyFlags;
6036 + }
6037 + }
6038 + }
6039 + }
6040 +
6041 // Setup all HTTP handlers
6042 if (parent.multiServer != null) { obj.app.ws('/meshserver.ashx', function (ws, req) { parent.multiServer.CreatePeerInServer(parent.multiServer, ws, req, obj.args.tlsoffload == null); }); }
6043 for (var i in parent.config.domains) {
@@ -6201,26 +6218,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6218
6219 // Setup auth strategies using passport if needed
6220 if (typeof domain.authstrategies == 'object') {
6204 - const passport = domain.passport = require('passport');
6205 - passport.serializeUser(function (user, done) { done(null, user.sid); });
6206 - passport.deserializeUser(function (sid, done) { done(null, { sid: sid }); });
6207 - obj.app.use(passport.initialize());
6208 - //obj.app.use(passport.session());
6209 -
6221 // Twitter
6211 - if ((typeof domain.authstrategies.twitter == 'object') && (typeof domain.authstrategies.twitter.clientid == 'string') && (typeof domain.authstrategies.twitter.clientsecret == 'string')) {
6212 - const TwitterStrategy = require('passport-twitter');
6213 - var options = { consumerKey: domain.authstrategies.twitter.clientid, consumerSecret: domain.authstrategies.twitter.clientsecret };
6214 - if (typeof domain.authstrategies.twitter.callbackurl == 'string') { options.callbackURL = domain.authstrategies.twitter.callbackurl; } else { options.callbackURL = url + 'auth-twitter-callback'; }
6215 - parent.debug('web', 'Adding Twitter SSO with options: ' + JSON.stringify(options));
6216 - passport.use('twitter-' + domain.id, new TwitterStrategy(options,
6217 - function (token, tokenSecret, profile, cb) {
6218 - parent.debug('web', 'Twitter profile: ' + JSON.stringify(profile));
6219 - var user = { sid: '~twitter:' + profile.id, name: profile.displayName, strategy: 'twitter' };
6220 - if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string')) { user.email = profile.emails[0].value; }
6221 - return cb(null, user);
6222 - }
6223 - ));
6222 + if ((domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.twitter) != 0) {
6223 obj.app.get(url + 'auth-twitter', function (req, res, next) {
6224 var domain = getDomain(req);
6225 if (domain.passport == null) { next(); return; }
@@ -6242,19 +6241,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6241 }
6242
6243 // Google
6245 - if ((typeof domain.authstrategies.google == 'object') && (typeof domain.authstrategies.google.clientid == 'string') && (typeof domain.authstrategies.google.clientsecret == 'string')) {
6246 - const GoogleStrategy = require('passport-google-oauth20');
6247 - var options = { clientID: domain.authstrategies.google.clientid, clientSecret: domain.authstrategies.google.clientsecret };
6248 - if (typeof domain.authstrategies.google.callbackurl == 'string') { options.callbackURL = domain.authstrategies.google.callbackurl; } else { options.callbackURL = url + 'auth-google-callback'; }
6249 - parent.debug('web', 'Adding Google SSO with options: ' + JSON.stringify(options));
6250 - passport.use('google-' + domain.id, new GoogleStrategy(options,
6251 - function (token, tokenSecret, profile, cb) {
6252 - parent.debug('web', 'Google profile: ' + JSON.stringify(profile));
6253 - var user = { sid: '~google:' + profile.id, name: profile.displayName, strategy: 'google' };
6254 - if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string') && (profile.emails[0].verified == true)) { user.email = profile.emails[0].value; }
6255 - return cb(null, user);
6256 - }
6257 - ));
6244 + if ((domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.google) != 0) {
6245 obj.app.get(url + 'auth-google', function (req, res, next) {
6246 var domain = getDomain(req);
6247 if (domain.passport == null) { next(); return; }
@@ -6267,20 +6254,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6254 }, handleStrategyLogin);
6255 }
6256
6270 - // Github
6271 - if ((typeof domain.authstrategies.github == 'object') && (typeof domain.authstrategies.github.clientid == 'string') && (typeof domain.authstrategies.github.clientsecret == 'string')) {
6272 - const GitHubStrategy = require('passport-github2');
6273 - var options = { clientID: domain.authstrategies.github.clientid, clientSecret: domain.authstrategies.github.clientsecret };
6274 - if (typeof domain.authstrategies.github.callbackurl == 'string') { options.callbackURL = domain.authstrategies.github.callbackurl; } else { options.callbackURL = url + 'auth-github-callback'; }
6275 - parent.debug('web', 'Adding Github SSO with options: ' + JSON.stringify(options));
6276 - passport.use('github-' + domain.id, new GitHubStrategy(options,
6277 - function (token, tokenSecret, profile, cb) {
6278 - parent.debug('web', 'Github profile: ' + JSON.stringify(profile));
6279 - var user = { sid: '~github:' + profile.id, name: profile.displayName, strategy: 'github' };
6280 - if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string')) { user.email = profile.emails[0].value; }
6281 - return cb(null, user);
6282 - }
6283 - ));
6257 + // GitHub
6258 + if ((domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.github) != 0) {
6259 obj.app.get(url + 'auth-github', function (req, res, next) {
6260 var domain = getDomain(req);
6261 if (domain.passport == null) { next(); return; }
@@ -6294,19 +6269,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6269 }
6270
6271 // Reddit
6297 - if ((typeof domain.authstrategies.reddit == 'object') && (typeof domain.authstrategies.reddit.clientid == 'string') && (typeof domain.authstrategies.reddit.clientsecret == 'string')) {
6298 - const RedditStrategy = require('passport-reddit');
6299 - var options = { clientID: domain.authstrategies.reddit.clientid, clientSecret: domain.authstrategies.reddit.clientsecret };
6300 - if (typeof domain.authstrategies.reddit.callbackurl == 'string') { options.callbackURL = domain.authstrategies.reddit.callbackurl; } else { options.callbackURL = url + 'auth-reddit-callback'; }
6301 - parent.debug('web', 'Adding Reddit SSO with options: ' + JSON.stringify(options));
6302 - passport.use('reddit-' + domain.id, new RedditStrategy.Strategy(options,
6303 - function (token, tokenSecret, profile, cb) {
6304 - parent.debug('web', 'Reddit profile: ' + JSON.stringify(profile));
6305 - var user = { sid: '~reddit:' + profile.id, name: profile.name, strategy: 'reddit' };
6306 - if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string')) { user.email = profile.emails[0].value; }
6307 - return cb(null, user);
6308 - }
6309 - ));
6272 + if ((domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.reddit) != 0) {
6273 obj.app.get(url + 'auth-reddit', function (req, res, next) {
6274 var domain = getDomain(req);
6275 if (domain.passport == null) { next(); return; }
@@ -6332,24 +6295,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6295 }
6296
6297 // Azure
6335 - if ((typeof domain.authstrategies.azure == 'object') && (typeof domain.authstrategies.azure.clientid == 'string') && (typeof domain.authstrategies.azure.clientsecret == 'string')) {
6336 - const AzureOAuth2Strategy = require('passport-azure-oauth2');
6337 - var options = { clientID: domain.authstrategies.azure.clientid, clientSecret: domain.authstrategies.azure.clientsecret, tenant: domain.authstrategies.azure.tenantid };
6338 - if (typeof domain.authstrategies.azure.callbackurl == 'string') { options.callbackURL = domain.authstrategies.azure.callbackurl; } else { options.callbackURL = url + 'auth-azure-callback'; }
6339 - parent.debug('web', 'Adding Azure SSO with options: ' + JSON.stringify(options));
6340 - passport.use('azure-' + domain.id, new AzureOAuth2Strategy(options,
6341 - function (accessToken, refreshtoken, params, profile, done) {
6342 - var userex = null;
6343 - try { userex = require('jwt-simple').decode(params.id_token, "", true); } catch (ex) { }
6344 - parent.debug('web', 'Azure profile: ' + JSON.stringify(userex));
6345 - var user = null;
6346 - if (userex != null) {
6347 - var user = { sid: '~azure:' + userex.unique_name, name: userex.name, strategy: 'azure' };
6348 - if (typeof userex.email == 'string') { user.email = userex.email; }
6349 - }
6350 - return done(null, user);
6351 - }
6352 - ));
6298 + if ((domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.azure) != 0) {
6299 obj.app.get(url + 'auth-azure', function (req, res, next) {
6300 var domain = getDomain(req);
6301 if (domain.passport == null) { next(); return; }
@@ -6374,155 +6320,52 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6320 }, handleStrategyLogin);
6321 }
6322
6377 - // Generic OpenID Connect
6378 - if ((typeof domain.authstrategies.oidc == 'object') && (typeof domain.authstrategies.oidc.clientid == 'string') && (typeof domain.authstrategies.oidc.clientsecret == 'string') && (typeof domain.authstrategies.oidc.issuer == 'string')) {
6379 - var options = {
6380 - authorizationURL: domain.authstrategies.oidc.authorizationurl,
6381 - callbackURL: domain.authstrategies.oidc.callbackurl,
6382 - clientID: domain.authstrategies.oidc.clientid,
6383 - clientSecret: domain.authstrategies.oidc.clientsecret,
6384 - issuer: domain.authstrategies.oidc.issuer,
6385 - tokenURL: domain.authstrategies.oidc.tokenurl,
6386 - userInfoURL: domain.authstrategies.oidc.userinfourl,
6387 - scope: ['openid profile email'],
6388 - responseMode: 'form_post',
6389 - state: true
6390 - };
6391 - const OIDCStrategy = require('@mstrhakr/passport-generic-oidc');
6392 - if (typeof domain.authstrategies.oidc.callbackurl == 'string') { options.callbackURL = domain.authstrategies.oidc.callbackurl; } else { options.callbackURL = url + 'oidc-callback'; }
6393 - parent.debug('web', 'Adding Generic OIDC SSO with options: ' + JSON.stringify(options));
6394 - passport.use('openidconnect', new OIDCStrategy.Strategy(options,
6395 - function verify(iss, sub, profile, cb) {
6396 - var user = { sid: '~oidc:' + profile.id, name: profile.displayName, email: profile.email, strategy: 'oidc' };
6397 - parent.debug('AUTH', 'OIDC: Configured user: ' + JSON.stringify(user));
6398 - return cb(null, user);
6399 - }
6400 - ));
6323 + // Generic OpenID
6324 + if (domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.openid != 0) {
6325 obj.app.get(url + 'auth-oidc', domain.passport.authenticate('openidconnect'));
6326 obj.app.get(url + 'oidc-callback', domain.passport.authenticate('openidconnect', { failureRedirect: '/login?failed-auth-attempt', failureFlash: true }), handleStrategyLogin);
6327 }
6328
6405 -
6329 // Generic SAML
6407 - if (typeof domain.authstrategies.saml == 'object') {
6408 - if ((typeof domain.authstrategies.saml.cert != 'string') || (typeof domain.authstrategies.saml.idpurl != 'string')) {
6409 - console.log('ERROR: Missing SAML configuration.');
6410 - } else {
6411 - const certPath = obj.common.joinPath(obj.parent.datapath, domain.authstrategies.saml.cert);
6412 - var cert = obj.fs.readFileSync(certPath);
6413 - if (cert == null) {
6414 - console.log('ERROR: Unable to read SAML IdP certificate: ' + domain.authstrategies.saml.cert);
6415 - } else {
6416 - var options = { entryPoint: domain.authstrategies.saml.idpurl, issuer: 'meshcentral' };
6417 - if (typeof domain.authstrategies.saml.callbackurl == 'string') { options.callbackUrl = domain.authstrategies.saml.callbackurl; } else { options.callbackUrl = url + 'auth-saml-callback'; }
6418 - if (domain.authstrategies.saml.disablerequestedauthncontext != null) { options.disableRequestedAuthnContext = domain.authstrategies.saml.disablerequestedauthncontext; }
6419 - if (typeof domain.authstrategies.saml.entityid == 'string') { options.issuer = domain.authstrategies.saml.entityid; }
6420 - parent.debug('web', 'Adding SAML SSO with options: ' + JSON.stringify(options));
6421 - options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
6422 - const SamlStrategy = require('passport-saml').Strategy;
6423 - passport.use('saml-' + domain.id, new SamlStrategy(options,
6424 - function (profile, done) {
6425 - parent.debug('web', 'SAML profile: ' + JSON.stringify(profile));
6426 - if (typeof profile.nameID != 'string') { return done(); }
6427 - var user = { sid: '~saml:' + profile.nameID, name: profile.nameID, strategy: 'saml' };
6428 - if ((typeof profile.firstname == 'string') && (typeof profile.lastname == 'string')) { user.name = profile.firstname + ' ' + profile.lastname; }
6429 - if (typeof profile.email == 'string') { user.email = profile.email; }
6430 - return done(null, user);
6431 - }
6432 - ));
6433 - obj.app.get(url + 'auth-saml', function (req, res, next) {
6434 - var domain = getDomain(req);
6435 - if (domain.passport == null) { next(); return; }
6436 - domain.passport.authenticate('saml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6437 - });
6438 - obj.app.post(url + 'auth-saml-callback', obj.bodyParser.urlencoded({ extended: false }), function (req, res, next) {
6439 - var domain = getDomain(req);
6440 - if (domain.passport == null) { next(); return; }
6441 - domain.passport.authenticate('saml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6442 - }, handleStrategyLogin);
6443 - }
6444 - }
6330 + if (domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.saml != 0) {
6331 + obj.app.get(url + 'auth-saml', function (req, res, next) {
6332 + var domain = getDomain(req);
6333 + if (domain.passport == null) { next(); return; }
6334 + domain.passport.authenticate('saml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6335 + });
6336 + obj.app.post(url + 'auth-saml-callback', obj.bodyParser.urlencoded({ extended: false }), function (req, res, next) {
6337 + var domain = getDomain(req);
6338 + if (domain.passport == null) { next(); return; }
6339 + domain.passport.authenticate('saml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6340 + }, handleStrategyLogin);
6341 }
6342
6343 // Intel SAML
6448 - if (typeof domain.authstrategies.intel == 'object') {
6449 - if ((typeof domain.authstrategies.intel.cert != 'string') || (typeof domain.authstrategies.intel.idpurl != 'string')) {
6450 - console.log('ERROR: Missing Intel SAML configuration.');
6451 - } else {
6452 - var cert = obj.fs.readFileSync(obj.common.joinPath(obj.parent.datapath, domain.authstrategies.intel.cert));
6453 - if (cert == null) {
6454 - console.log('ERROR: Unable to read Intel SAML IdP certificate: ' + domain.authstrategies.intel.cert);
6455 - } else {
6456 - var options = { entryPoint: domain.authstrategies.intel.idpurl, issuer: 'meshcentral' };
6457 - if (typeof domain.authstrategies.intel.callbackurl == 'string') { options.callbackUrl = domain.authstrategies.intel.callbackurl; } else { options.callbackUrl = url + 'auth-intel-callback'; }
6458 - if (domain.authstrategies.intel.disablerequestedauthncontext != null) { options.disableRequestedAuthnContext = domain.authstrategies.intel.disablerequestedauthncontext; }
6459 - if (typeof domain.authstrategies.intel.entityid == 'string') { options.issuer = domain.authstrategies.intel.entityid; }
6460 - parent.debug('web', 'Adding Intel SSO with options: ' + JSON.stringify(options));
6461 - options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
6462 - const SamlStrategy = require('passport-saml').Strategy;
6463 - passport.use('isaml-' + domain.id, new SamlStrategy(options,
6464 - function (profile, done) {
6465 - parent.debug('web', 'Intel profile: ' + JSON.stringify(profile));
6466 - if (typeof profile.nameID != 'string') { return done(); }
6467 - var user = { sid: '~intel:' + profile.nameID, name: profile.nameID, strategy: 'intel' };
6468 - if ((typeof profile.firstname == 'string') && (typeof profile.lastname == 'string')) { user.name = profile.firstname + ' ' + profile.lastname; }
6469 - else if ((typeof profile.FirstName == 'string') && (typeof profile.LastName == 'string')) { user.name = profile.FirstName + ' ' + profile.LastName; }
6470 - if (typeof profile.email == 'string') { user.email = profile.email; }
6471 - else if (typeof profile.EmailAddress == 'string') { user.email = profile.EmailAddress; }
6472 - return done(null, user);
6473 - }
6474 - ));
6475 - obj.app.get(url + 'auth-intel', function (req, res, next) {
6476 - var domain = getDomain(req);
6477 - if (domain.passport == null) { next(); return; }
6478 - domain.passport.authenticate('isaml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6479 - });
6480 - obj.app.post(url + 'auth-intel-callback', obj.bodyParser.urlencoded({ extended: false }), function (req, res, next) {
6481 - var domain = getDomain(req);
6482 - if (domain.passport == null) { next(); return; }
6483 - domain.passport.authenticate('isaml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6484 - }, handleStrategyLogin);
6485 - }
6486 - }
6344 + if (domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.intelSaml != 0) {
6345 + obj.app.get(url + 'auth-intel', function (req, res, next) {
6346 + var domain = getDomain(req);
6347 + if (domain.passport == null) { next(); return; }
6348 + domain.passport.authenticate('isaml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6349 + });
6350 + obj.app.post(url + 'auth-intel-callback', obj.bodyParser.urlencoded({ extended: false }), function (req, res, next) {
6351 + var domain = getDomain(req);
6352 + if (domain.passport == null) { next(); return; }
6353 + domain.passport.authenticate('isaml-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6354 + }, handleStrategyLogin);
6355 }
6356
6357 // JumpCloud SAML
6490 - if (typeof domain.authstrategies.jumpcloud == 'object') {
6491 - if ((typeof domain.authstrategies.jumpcloud.cert != 'string') || (typeof domain.authstrategies.jumpcloud.idpurl != 'string')) {
6492 - console.log('ERROR: Missing JumpCloud SAML configuration.');
6493 - } else {
6494 - var cert = obj.fs.readFileSync(obj.common.joinPath(obj.parent.datapath, domain.authstrategies.jumpcloud.cert));
6495 - if (cert == null) {
6496 - console.log('ERROR: Unable to read JumpCloud IdP certificate: ' + domain.authstrategies.jumpcloud.cert);
6497 - } else {
6498 - var options = { entryPoint: domain.authstrategies.jumpcloud.idpurl, issuer: 'meshcentral' };
6499 - if (typeof domain.authstrategies.jumpcloud.callbackurl == 'string') { options.callbackUrl = domain.authstrategies.jumpcloud.callbackurl; } else { options.callbackUrl = url + 'auth-jumpcloud-callback'; }
6500 - if (typeof domain.authstrategies.jumpcloud.entityid == 'string') { options.issuer = domain.authstrategies.jumpcloud.entityid; }
6501 - parent.debug('web', 'Adding JumpCloud SSO with options: ' + JSON.stringify(options));
6502 - options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
6503 - const SamlStrategy = require('passport-saml').Strategy;
6504 - passport.use('jumpcloud-' + domain.id, new SamlStrategy(options,
6505 - function (profile, done) {
6506 - parent.debug('web', 'JumpCloud profile: ' + JSON.stringify(profile));
6507 - if (typeof profile.nameID != 'string') { return done(); }
6508 - var user = { sid: '~jumpcloud:' + profile.nameID, name: profile.nameID, strategy: 'jumpcloud' };
6509 - if ((typeof profile.firstname == 'string') && (typeof profile.lastname == 'string')) { user.name = profile.firstname + ' ' + profile.lastname; }
6510 - if (typeof profile.email == 'string') { user.email = profile.email; }
6511 - return done(null, user);
6512 - }
6513 - ));
6514 - obj.app.get(url + 'auth-jumpcloud', function (req, res, next) {
6515 - var domain = getDomain(req);
6516 - if (domain.passport == null) { next(); return; }
6517 - domain.passport.authenticate('jumpcloud-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6518 - });
6519 - obj.app.post(url + 'auth-jumpcloud-callback', obj.bodyParser.urlencoded({ extended: false }), function (req, res, next) {
6520 - var domain = getDomain(req);
6521 - if (domain.passport == null) { next(); return; }
6522 - domain.passport.authenticate('jumpcloud-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6523 - }, handleStrategyLogin);
6524 - }
6525 - }
6358 + if (domain.authstrategies.authStrategyFlags & domainAuthStrategyConsts.jumpCloudSaml != 0) {
6359 + obj.app.get(url + 'auth-jumpcloud', function (req, res, next) {
6360 + var domain = getDomain(req);
6361 + if (domain.passport == null) { next(); return; }
6362 + domain.passport.authenticate('jumpcloud-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6363 + });
6364 + obj.app.post(url + 'auth-jumpcloud-callback', obj.bodyParser.urlencoded({ extended: false }), function (req, res, next) {
6365 + var domain = getDomain(req);
6366 + if (domain.passport == null) { next(); return; }
6367 + domain.passport.authenticate('jumpcloud-' + domain.id, { failureRedirect: '/', failureFlash: true })(req, res, next);
6368 + }, handleStrategyLogin);
6369 }
6370 }
6371
@@ -6793,6 +6636,247 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
6636 if (doneFunc) doneFunc();
6637 }
6638
6639 + // Auth strategy flags
6640 + const domainAuthStrategyConsts = {
6641 + twitter: 1,
6642 + google: 2,
6643 + github: 3,
6644 + reddit: 8,
6645 + azure: 16,
6646 + openid: 32,
6647 + saml: 64,
6648 + intelSaml: 128,
6649 + jumpCloudSaml: 256
6650 + }
6651 +
6652 + // Setup auth strategies for a domain
6653 + function setupDomainAuthStrategy(domain) {
6654 + // Return the auth strategies that have been setup
6655 + var authStrategyFlags = 0;
6656 +
6657 + // Setup auth strategies using passport if needed
6658 + if (typeof domain.authstrategies != 'object') return authStrategyFlags;
6659 +
6660 + const url = domain.url;
6661 + const passport = domain.passport = require('passport');
6662 + passport.serializeUser(function (user, done) { done(null, user.sid); });
6663 + passport.deserializeUser(function (sid, done) { done(null, { sid: sid }); });
6664 + obj.app.use(passport.initialize());
6665 +
6666 + // Twitter
6667 + if ((typeof domain.authstrategies.twitter == 'object') && (typeof domain.authstrategies.twitter.clientid == 'string') && (typeof domain.authstrategies.twitter.clientsecret == 'string')) {
6668 + const TwitterStrategy = require('passport-twitter');
6669 + var options = { consumerKey: domain.authstrategies.twitter.clientid, consumerSecret: domain.authstrategies.twitter.clientsecret };
6670 + if (typeof domain.authstrategies.twitter.callbackurl == 'string') { options.callbackURL = domain.authstrategies.twitter.callbackurl; } else { options.callbackURL = url + 'auth-twitter-callback'; }
6671 + parent.debug('web', 'Adding Twitter SSO with options: ' + JSON.stringify(options));
6672 + passport.use('twitter-' + domain.id, new TwitterStrategy(options,
6673 + function (token, tokenSecret, profile, cb) {
6674 + parent.debug('web', 'Twitter profile: ' + JSON.stringify(profile));
6675 + var user = { sid: '~twitter:' + profile.id, name: profile.displayName, strategy: 'twitter' };
6676 + if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string')) { user.email = profile.emails[0].value; }
6677 + return cb(null, user);
6678 + }
6679 + ));
6680 + authStrategyFlags |= domainAuthStrategyConsts.twitter;
6681 + }
6682 +
6683 + // Google
6684 + if ((typeof domain.authstrategies.google == 'object') && (typeof domain.authstrategies.google.clientid == 'string') && (typeof domain.authstrategies.google.clientsecret == 'string')) {
6685 + const GoogleStrategy = require('passport-google-oauth20');
6686 + var options = { clientID: domain.authstrategies.google.clientid, clientSecret: domain.authstrategies.google.clientsecret };
6687 + if (typeof domain.authstrategies.google.callbackurl == 'string') { options.callbackURL = domain.authstrategies.google.callbackurl; } else { options.callbackURL = url + 'auth-google-callback'; }
6688 + parent.debug('web', 'Adding Google SSO with options: ' + JSON.stringify(options));
6689 + passport.use('google-' + domain.id, new GoogleStrategy(options,
6690 + function (token, tokenSecret, profile, cb) {
6691 + parent.debug('web', 'Google profile: ' + JSON.stringify(profile));
6692 + var user = { sid: '~google:' + profile.id, name: profile.displayName, strategy: 'google' };
6693 + if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string') && (profile.emails[0].verified == true)) { user.email = profile.emails[0].value; }
6694 + return cb(null, user);
6695 + }
6696 + ));
6697 + authStrategyFlags |= domainAuthStrategyConsts.google;
6698 + }
6699 +
6700 + // Github
6701 + if ((typeof domain.authstrategies.github == 'object') && (typeof domain.authstrategies.github.clientid == 'string') && (typeof domain.authstrategies.github.clientsecret == 'string')) {
6702 + const GitHubStrategy = require('passport-github2');
6703 + var options = { clientID: domain.authstrategies.github.clientid, clientSecret: domain.authstrategies.github.clientsecret };
6704 + if (typeof domain.authstrategies.github.callbackurl == 'string') { options.callbackURL = domain.authstrategies.github.callbackurl; } else { options.callbackURL = url + 'auth-github-callback'; }
6705 + parent.debug('web', 'Adding Github SSO with options: ' + JSON.stringify(options));
6706 + passport.use('github-' + domain.id, new GitHubStrategy(options,
6707 + function (token, tokenSecret, profile, cb) {
6708 + parent.debug('web', 'Github profile: ' + JSON.stringify(profile));
6709 + var user = { sid: '~github:' + profile.id, name: profile.displayName, strategy: 'github' };
6710 + if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string')) { user.email = profile.emails[0].value; }
6711 + return cb(null, user);
6712 + }
6713 + ));
6714 + authStrategyFlags |= domainAuthStrategyConsts.github;
6715 + }
6716 +
6717 + // Reddit
6718 + if ((typeof domain.authstrategies.reddit == 'object') && (typeof domain.authstrategies.reddit.clientid == 'string') && (typeof domain.authstrategies.reddit.clientsecret == 'string')) {
6719 + const RedditStrategy = require('passport-reddit');
6720 + var options = { clientID: domain.authstrategies.reddit.clientid, clientSecret: domain.authstrategies.reddit.clientsecret };
6721 + if (typeof domain.authstrategies.reddit.callbackurl == 'string') { options.callbackURL = domain.authstrategies.reddit.callbackurl; } else { options.callbackURL = url + 'auth-reddit-callback'; }
6722 + parent.debug('web', 'Adding Reddit SSO with options: ' + JSON.stringify(options));
6723 + passport.use('reddit-' + domain.id, new RedditStrategy.Strategy(options,
6724 + function (token, tokenSecret, profile, cb) {
6725 + parent.debug('web', 'Reddit profile: ' + JSON.stringify(profile));
6726 + var user = { sid: '~reddit:' + profile.id, name: profile.name, strategy: 'reddit' };
6727 + if ((typeof profile.emails == 'object') && (profile.emails[0] != null) && (typeof profile.emails[0].value == 'string')) { user.email = profile.emails[0].value; }
6728 + return cb(null, user);
6729 + }
6730 + ));
6731 + authStrategyFlags |= domainAuthStrategyConsts.reddit;
6732 + }
6733 +
6734 + // Azure
6735 + if ((typeof domain.authstrategies.azure == 'object') && (typeof domain.authstrategies.azure.clientid == 'string') && (typeof domain.authstrategies.azure.clientsecret == 'string')) {
6736 + const AzureOAuth2Strategy = require('passport-azure-oauth2');
6737 + var options = { clientID: domain.authstrategies.azure.clientid, clientSecret: domain.authstrategies.azure.clientsecret, tenant: domain.authstrategies.azure.tenantid };
6738 + if (typeof domain.authstrategies.azure.callbackurl == 'string') { options.callbackURL = domain.authstrategies.azure.callbackurl; } else { options.callbackURL = url + 'auth-azure-callback'; }
6739 + parent.debug('web', 'Adding Azure SSO with options: ' + JSON.stringify(options));
6740 + passport.use('azure-' + domain.id, new AzureOAuth2Strategy(options,
6741 + function (accessToken, refreshtoken, params, profile, done) {
6742 + var userex = null;
6743 + try { userex = require('jwt-simple').decode(params.id_token, '', true); } catch (ex) { }
6744 + parent.debug('web', 'Azure profile: ' + JSON.stringify(userex));
6745 + var user = null;
6746 + if (userex != null) {
6747 + var user = { sid: '~azure:' + userex.unique_name, name: userex.name, strategy: 'azure' };
6748 + if (typeof userex.email == 'string') { user.email = userex.email; }
6749 + }
6750 + return done(null, user);
6751 + }
6752 + ));
6753 + authStrategyFlags |= domainAuthStrategyConsts.azure;
6754 + }
6755 +
6756 + // Generic OpenID Connect
6757 + if ((typeof domain.authstrategies.oidc == 'object') && (typeof domain.authstrategies.oidc.clientid == 'string') && (typeof domain.authstrategies.oidc.clientsecret == 'string') && (typeof domain.authstrategies.oidc.issuer == 'string')) {
6758 + var options = {
6759 + authorizationURL: domain.authstrategies.oidc.authorizationurl,
6760 + callbackURL: domain.authstrategies.oidc.callbackurl,
6761 + clientID: domain.authstrategies.oidc.clientid,
6762 + clientSecret: domain.authstrategies.oidc.clientsecret,
6763 + issuer: domain.authstrategies.oidc.issuer,
6764 + tokenURL: domain.authstrategies.oidc.tokenurl,
6765 + userInfoURL: domain.authstrategies.oidc.userinfourl,
6766 + scope: ['openid profile email'],
6767 + responseMode: 'form_post',
6768 + state: true
6769 + };
6770 + const OIDCStrategy = require('@mstrhakr/passport-generic-oidc');
6771 + if (typeof domain.authstrategies.oidc.callbackurl == 'string') { options.callbackURL = domain.authstrategies.oidc.callbackurl; } else { options.callbackURL = url + 'oidc-callback'; }
6772 + parent.debug('web', 'Adding Generic OIDC SSO with options: ' + JSON.stringify(options));
6773 + passport.use('openidconnect', new OIDCStrategy.Strategy(options,
6774 + function verify(iss, sub, profile, cb) {
6775 + var user = { sid: '~oidc:' + profile.id, name: profile.displayName, email: profile.email, strategy: 'oidc' };
6776 + parent.debug('AUTH', 'OIDC: Configured user: ' + JSON.stringify(user));
6777 + return cb(null, user);
6778 + }
6779 + ));
6780 + authStrategyFlags |= domainAuthStrategyConsts.openid;
6781 + }
6782 +
6783 + // Generic SAML
6784 + if (typeof domain.authstrategies.saml == 'object') {
6785 + if ((typeof domain.authstrategies.saml.cert != 'string') || (typeof domain.authstrategies.saml.idpurl != 'string')) {
6786 + console.log('ERROR: Missing SAML configuration.');
6787 + } else {
6788 + const certPath = obj.common.joinPath(obj.parent.datapath, domain.authstrategies.saml.cert);
6789 + var cert = obj.fs.readFileSync(certPath);
6790 + if (cert == null) {
6791 + console.log('ERROR: Unable to read SAML IdP certificate: ' + domain.authstrategies.saml.cert);
6792 + } else {
6793 + var options = { entryPoint: domain.authstrategies.saml.idpurl, issuer: 'meshcentral' };
6794 + if (typeof domain.authstrategies.saml.callbackurl == 'string') { options.callbackUrl = domain.authstrategies.saml.callbackurl; } else { options.callbackUrl = url + 'auth-saml-callback'; }
6795 + if (domain.authstrategies.saml.disablerequestedauthncontext != null) { options.disableRequestedAuthnContext = domain.authstrategies.saml.disablerequestedauthncontext; }
6796 + if (typeof domain.authstrategies.saml.entityid == 'string') { options.issuer = domain.authstrategies.saml.entityid; }
6797 + parent.debug('web', 'Adding SAML SSO with options: ' + JSON.stringify(options));
6798 + options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
6799 + const SamlStrategy = require('passport-saml').Strategy;
6800 + passport.use('saml-' + domain.id, new SamlStrategy(options,
6801 + function (profile, done) {
6802 + parent.debug('web', 'SAML profile: ' + JSON.stringify(profile));
6803 + if (typeof profile.nameID != 'string') { return done(); }
6804 + var user = { sid: '~saml:' + profile.nameID, name: profile.nameID, strategy: 'saml' };
6805 + if ((typeof profile.firstname == 'string') && (typeof profile.lastname == 'string')) { user.name = profile.firstname + ' ' + profile.lastname; }
6806 + if (typeof profile.email == 'string') { user.email = profile.email; }
6807 + return done(null, user);
6808 + }
6809 + ));
6810 + authStrategyFlags |= domainAuthStrategyConsts.saml;
6811 + }
6812 + }
6813 + }
6814 +
6815 + // Intel SAML
6816 + if (typeof domain.authstrategies.intel == 'object') {
6817 + if ((typeof domain.authstrategies.intel.cert != 'string') || (typeof domain.authstrategies.intel.idpurl != 'string')) {
6818 + console.log('ERROR: Missing Intel SAML configuration.');
6819 + } else {
6820 + var cert = obj.fs.readFileSync(obj.common.joinPath(obj.parent.datapath, domain.authstrategies.intel.cert));
6821 + if (cert == null) {
6822 + console.log('ERROR: Unable to read Intel SAML IdP certificate: ' + domain.authstrategies.intel.cert);
6823 + } else {
6824 + var options = { entryPoint: domain.authstrategies.intel.idpurl, issuer: 'meshcentral' };
6825 + if (typeof domain.authstrategies.intel.callbackurl == 'string') { options.callbackUrl = domain.authstrategies.intel.callbackurl; } else { options.callbackUrl = url + 'auth-intel-callback'; }
6826 + if (domain.authstrategies.intel.disablerequestedauthncontext != null) { options.disableRequestedAuthnContext = domain.authstrategies.intel.disablerequestedauthncontext; }
6827 + if (typeof domain.authstrategies.intel.entityid == 'string') { options.issuer = domain.authstrategies.intel.entityid; }
6828 + parent.debug('web', 'Adding Intel SSO with options: ' + JSON.stringify(options));
6829 + options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
6830 + const SamlStrategy = require('passport-saml').Strategy;
6831 + passport.use('isaml-' + domain.id, new SamlStrategy(options,
6832 + function (profile, done) {
6833 + parent.debug('web', 'Intel profile: ' + JSON.stringify(profile));
6834 + if (typeof profile.nameID != 'string') { return done(); }
6835 + var user = { sid: '~intel:' + profile.nameID, name: profile.nameID, strategy: 'intel' };
6836 + if ((typeof profile.firstname == 'string') && (typeof profile.lastname == 'string')) { user.name = profile.firstname + ' ' + profile.lastname; }
6837 + else if ((typeof profile.FirstName == 'string') && (typeof profile.LastName == 'string')) { user.name = profile.FirstName + ' ' + profile.LastName; }
6838 + if (typeof profile.email == 'string') { user.email = profile.email; }
6839 + else if (typeof profile.EmailAddress == 'string') { user.email = profile.EmailAddress; }
6840 + return done(null, user);
6841 + }
6842 + ));
6843 + authStrategyFlags |= domainAuthStrategyConsts.intelSaml;
6844 + }
6845 + }
6846 + }
6847 +
6848 + // JumpCloud SAML
6849 + if (typeof domain.authstrategies.jumpcloud == 'object') {
6850 + if ((typeof domain.authstrategies.jumpcloud.cert != 'string') || (typeof domain.authstrategies.jumpcloud.idpurl != 'string')) {
6851 + console.log('ERROR: Missing JumpCloud SAML configuration.');
6852 + } else {
6853 + var cert = obj.fs.readFileSync(obj.common.joinPath(obj.parent.datapath, domain.authstrategies.jumpcloud.cert));
6854 + if (cert == null) {
6855 + console.log('ERROR: Unable to read JumpCloud IdP certificate: ' + domain.authstrategies.jumpcloud.cert);
6856 + } else {
6857 + var options = { entryPoint: domain.authstrategies.jumpcloud.idpurl, issuer: 'meshcentral' };
6858 + if (typeof domain.authstrategies.jumpcloud.callbackurl == 'string') { options.callbackUrl = domain.authstrategies.jumpcloud.callbackurl; } else { options.callbackUrl = url + 'auth-jumpcloud-callback'; }
6859 + if (typeof domain.authstrategies.jumpcloud.entityid == 'string') { options.issuer = domain.authstrategies.jumpcloud.entityid; }
6860 + parent.debug('web', 'Adding JumpCloud SSO with options: ' + JSON.stringify(options));
6861 + options.cert = cert.toString().split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('');
6862 + const SamlStrategy = require('passport-saml').Strategy;
6863 + passport.use('jumpcloud-' + domain.id, new SamlStrategy(options,
6864 + function (profile, done) {
6865 + parent.debug('web', 'JumpCloud profile: ' + JSON.stringify(profile));
6866 + if (typeof profile.nameID != 'string') { return done(); }
6867 + var user = { sid: '~jumpcloud:' + profile.nameID, name: profile.nameID, strategy: 'jumpcloud' };
6868 + if ((typeof profile.firstname == 'string') && (typeof profile.lastname == 'string')) { user.name = profile.firstname + ' ' + profile.lastname; }
6869 + if (typeof profile.email == 'string') { user.email = profile.email; }
6870 + return done(null, user);
6871 + }
6872 + ));
6873 + authStrategyFlags |= domainAuthStrategyConsts.jumpCloudSaml;
6874 + }
6875 + }
6876 + }
6877 +
6878 + return authStrategyFlags;
6879 + }
6880
6881 // Handle an incoming request as a web relay
6882 function handleWebRelayRequest(req, res) {