Added UID hack.

This commit is contained in:
Steffo Weber 2017-05-17 17:56:38 +02:00
parent e55efb0699
commit 369c5290f7
3 changed files with 131 additions and 118 deletions

View file

@ -29,7 +29,7 @@ htmljs@1.0.11
http@1.2.12
id-map@1.0.9
jquery@1.11.10
local-test:steffo:meteor-accounts-saml@0.0.10
local-test:steffo:meteor-accounts-saml@0.0.11
localstorage@1.0.12
logging@1.1.17
meteor@1.6.1
@ -56,7 +56,7 @@ routepolicy@1.0.12
service-configuration@1.0.11
spacebars@1.0.12
spacebars-compiler@1.1.0
steffo:meteor-accounts-saml@0.0.10
steffo:meteor-accounts-saml@0.0.11
templating@1.1.14
templating-tools@1.1.1
tmeasday:test-reporter-helpers@0.2.1

View file

@ -1,7 +1,7 @@
Package.describe({
name:"steffo:meteor-accounts-saml",
summary: "SAML Login (SP) for Meteor. Works with OpenAM, OpenIDP and provides Single Logout.",
version: "0.0.10",
version: "0.0.11",
git: "https://github.com/steffow/meteor-accounts-saml.git"
});

View file

@ -8,12 +8,12 @@ var bodyParser = Npm.require('body-parser')
RoutePolicy.declare('/_saml/', 'network');
Meteor.methods({
samlLogout: function (provider) {
samlLogout: function(provider) {
// Make sure the user is logged in before initiate SAML SLO
if (!Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
var samlProvider = function (element) {
var samlProvider = function(element) {
return (element.provider == provider)
}
providerConfig = Meteor.settings.saml.filter(samlProvider)[0];
@ -63,13 +63,13 @@ Meteor.methods({
}
})
Accounts.registerLoginHandler(function (loginRequest) {
Accounts.registerLoginHandler(function(loginRequest) {
if (!loginRequest.saml || !loginRequest.credentialToken) {
return undefined;
}
var loginResult = Accounts.saml.retrieveCredential(loginRequest.credentialToken);
if (Meteor.settings.debug) {
console.log("RESULT :" + JSON.stringify(loginResult));
console.log("RESULT :" + JSON.stringify(loginResult));
}
if (loginResult && loginResult.profile && loginResult.profile.email) {
console.log("Profile: " + JSON.stringify(loginResult.profile.email));
@ -78,22 +78,22 @@ Accounts.registerLoginHandler(function (loginRequest) {
});
if (!user) {
if (Meteor.settings.saml[0].dynamicProfile) {
Accounts.createUser({
email: loginResult.profile.email,
password: "",
username: loginResult.profile.nameID,
profile: ""
});
user = Meteor.users.findOne({
"emails.address": loginResult.profile.email
});
if (Meteor.settings.debug) {
console.log("Created new user");
if (Meteor.settings.saml[0].dynamicProfile) {
Accounts.createUser({
email: loginResult.profile.email,
password: "",
username: loginResult.profile.nameID,
profile: ""
});
user = Meteor.users.findOne({
"emails.address": loginResult.profile.email
});
if (Meteor.settings.debug) {
console.log("Created new user");
}
} else {
throw new Error("Could not find an existing user with supplied email " + loginResult.profile.email);
}
} else {
throw new Error("Could not find an existing user with supplied email " + loginResult.profile.email);
}
}
@ -122,6 +122,17 @@ Accounts.registerLoginHandler(function (loginRequest) {
}
});
if (loginResult.profile.uid) {
Meteor.users.update({
_id: user._id
}, {
$set: {
// TBD this should be pushed, otherwise we're only able to SSO into a single IDP at a time
'uid': loginResult.profile.uid
}
});
}
//sending token along with the userId
var result = {
userId: user._id,
@ -137,11 +148,11 @@ Accounts.registerLoginHandler(function (loginRequest) {
Accounts.saml._loginResultForCredentialToken = {};
Accounts.saml.hasCredential = function (credentialToken) {
Accounts.saml.hasCredential = function(credentialToken) {
return _.has(Accounts.saml._loginResultForCredentialToken, credentialToken);
}
Accounts.saml.retrieveCredential = function (credentialToken) {
Accounts.saml.retrieveCredential = function(credentialToken) {
// The credentialToken in all these functions corresponds to SAMLs inResponseTo field and is mandatory to check.
var result = Accounts.saml._loginResultForCredentialToken[credentialToken];
delete Accounts.saml._loginResultForCredentialToken[credentialToken];
@ -151,15 +162,17 @@ Accounts.saml.retrieveCredential = function (credentialToken) {
// Listen to incoming SAML http requests
WebApp.connectHandlers.use(bodyParser.urlencoded({ extended: true })).use(function (req, res, next) {
WebApp.connectHandlers.use(bodyParser.urlencoded({
extended: true
})).use(function(req, res, next) {
// Need to create a Fiber since we're using synchronous http calls and nothing
// else is wrapping this in a fiber automatically
Fiber(function () {
Fiber(function() {
middleware(req, res, next);
}).run();
});
middleware = function (req, res, next) {
middleware = function(req, res, next) {
// Make sure to catch any exceptions because otherwise we'd crash
// the runner
try {
@ -172,7 +185,7 @@ middleware = function (req, res, next) {
if (!samlObject.actionName)
throw new Error("Missing SAML action");
var service = _.find(Meteor.settings.saml, function (samlSetting) {
var service = _.find(Meteor.settings.saml, function(samlSetting) {
return samlSetting.provider === samlObject.serviceName;
});
@ -180,102 +193,102 @@ middleware = function (req, res, next) {
if (!service)
throw new Error("Unexpected SAML service " + samlObject.serviceName);
switch (samlObject.actionName) {
case "metadata":
_saml = new SAML(service);
service.callbackUrl = Meteor.absoluteUrl("_saml/validate/" + service.provider);
res.writeHead(200);
res.write(_saml.generateServiceProviderMetadata(service.callbackUrl));
res.end();
//closePopup(res);
break;
case "logout":
// This is where we receive SAML LogoutResponse
_saml = new SAML(service);
_saml.validateLogoutResponse(req.query.SAMLResponse, function (err, result) {
if (!err) {
var logOutUser = function (inResponseTo) {
if (Meteor.settings.debug) {
console.log("Logging Out user via inResponseTo " + inResponseTo);
}
var loggedOutUser = Meteor.users.find({
'services.saml.inResponseTo': inResponseTo
}).fetch();
if (loggedOutUser.length == 1) {
case "metadata":
_saml = new SAML(service);
service.callbackUrl = Meteor.absoluteUrl("_saml/validate/" + service.provider);
res.writeHead(200);
res.write(_saml.generateServiceProviderMetadata(service.callbackUrl));
res.end();
//closePopup(res);
break;
case "logout":
// This is where we receive SAML LogoutResponse
_saml = new SAML(service);
_saml.validateLogoutResponse(req.query.SAMLResponse, function(err, result) {
if (!err) {
var logOutUser = function(inResponseTo) {
if (Meteor.settings.debug) {
console.log("Found user " + loggedOutUser[0]._id);
console.log("Logging Out user via inResponseTo " + inResponseTo);
}
Meteor.users.update({
_id: loggedOutUser[0]._id
}, {
$set: {
"services.resume.loginTokens": []
var loggedOutUser = Meteor.users.find({
'services.saml.inResponseTo': inResponseTo
}).fetch();
if (loggedOutUser.length == 1) {
if (Meteor.settings.debug) {
console.log("Found user " + loggedOutUser[0]._id);
}
});
Meteor.users.update({
_id: loggedOutUser[0]._id
}, {
$unset: {
"services.saml": ""
}
});
} else {
throw new Meteor.error("Found multiple users matching SAML inResponseTo fields");
Meteor.users.update({
_id: loggedOutUser[0]._id
}, {
$set: {
"services.resume.loginTokens": []
}
});
Meteor.users.update({
_id: loggedOutUser[0]._id
}, {
$unset: {
"services.saml": ""
}
});
} else {
throw new Meteor.error("Found multiple users matching SAML inResponseTo fields");
}
}
Fiber(function() {
logOutUser(result);
}).run();
res.writeHead(302, {
'Location': req.query.RelayState
});
res.end();
} else {
// TBD thinking of sth meaning full.
}
Fiber(function () {
logOutUser(result);
}).run();
res.writeHead(302, {
'Location': req.query.RelayState
});
res.end();
} else {
// TBD thinking of sth meaning full.
}
})
break;
case "sloRedirect":
var idpLogout = req.query.redirect
res.writeHead(302, {
// credentialToken here is the SAML LogOut Request that we'll send back to IDP
'Location': idpLogout
});
res.end();
break;
case "authorize":
service.callbackUrl = Meteor.absoluteUrl("_saml/validate/" + service.provider);
service.id = samlObject.credentialToken;
_saml = new SAML(service);
_saml.getAuthorizeUrl(req, function (err, url) {
if (err)
throw new Error("Unable to generate authorize url");
})
break;
case "sloRedirect":
var idpLogout = req.query.redirect
res.writeHead(302, {
'Location': url
// credentialToken here is the SAML LogOut Request that we'll send back to IDP
'Location': idpLogout
});
res.end();
});
break;
case "validate":
_saml = new SAML(service);
Accounts.saml.RelayState = req.body.RelayState;
_saml.validateResponse(req.body.SAMLResponse, req.body.RelayState, function (err, profile, loggedOut) {
if (err)
throw new Error("Unable to validate response url: " + err);
break;
case "authorize":
service.callbackUrl = Meteor.absoluteUrl("_saml/validate/" + service.provider);
service.id = samlObject.credentialToken;
_saml = new SAML(service);
_saml.getAuthorizeUrl(req, function(err, url) {
if (err)
throw new Error("Unable to generate authorize url");
res.writeHead(302, {
'Location': url
});
res.end();
});
break;
case "validate":
_saml = new SAML(service);
Accounts.saml.RelayState = req.body.RelayState;
_saml.validateResponse(req.body.SAMLResponse, req.body.RelayState, function(err, profile, loggedOut) {
if (err)
throw new Error("Unable to validate response url: " + err);
var credentialToken = profile.inResponseToId || profile.InResponseTo || samlObject.credentialToken;
if (!credentialToken)
throw new Error("Unable to determine credentialToken");
Accounts.saml._loginResultForCredentialToken[credentialToken] = {
profile: profile
};
closePopup(res);
});
break;
default:
throw new Error("Unexpected SAML action " + samlObject.actionName);
var credentialToken = profile.inResponseToId || profile.InResponseTo || samlObject.credentialToken;
if (!credentialToken)
throw new Error("Unable to determine credentialToken");
Accounts.saml._loginResultForCredentialToken[credentialToken] = {
profile: profile
};
closePopup(res);
});
break;
default:
throw new Error("Unexpected SAML action " + samlObject.actionName);
}
} catch (err) {
@ -283,7 +296,7 @@ middleware = function (req, res, next) {
}
};
var samlUrlToObject = function (url) {
var samlUrlToObject = function(url) {
// req.url will be "/_saml/<action>/<service name>/<credentialToken>"
if (!url)
return null;
@ -301,12 +314,12 @@ var samlUrlToObject = function (url) {
credentialToken: splitPath[4]
};
if (Meteor.settings.debug) {
console.log(result);
console.log(result);
}
return result;
};
var closePopup = function (res, err) {
var closePopup = function(res, err) {
res.writeHead(200, {
'Content-Type': 'text/html'
});