diff --git a/APPL/MGT/fac_idp.asp b/APPL/MGT/fac_idp.asp index 5eb36fe586..4cb0a98ae3 100644 --- a/APPL/MGT/fac_idp.asp +++ b/APPL/MGT/fac_idp.asp @@ -30,6 +30,7 @@ scaffolding(this_model, "list": { "columns": [ "id", + "code", "name", "type", "remote_loginurl" diff --git a/APPL/Shared/Shared.inc b/APPL/Shared/Shared.inc index 70d3d4942d..cb52bf56dc 100644 --- a/APPL/Shared/Shared.inc +++ b/APPL/Shared/Shared.inc @@ -1578,4 +1578,53 @@ HTTP = return sitenoroot; } } + +// Alleen nog maar ipv4 +// http://stackoverflow.com/a/503238/569090 +IP = +{ + IPnumber: function(IPaddress) { + var ip = IPaddress.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); + if(ip) { + return (+ip[1]<<24) + (+ip[2]<<16) + (+ip[3]<<8) + (+ip[4]); + } + // else ... ? + __DoLog("Invalid IPAddress: " + IPaddress); + INTERNAL_ERROR_INVALID_IP; + }, + + IPmask: function (maskSize) { + return -1<<(32-maskSize) + }, + + inSubnet: function(ip, subnet) + { + var long_ip = IP.IPnumber(ip); + var bits = 32; + if (subnet.indexOf("/") > 0) + { + bits = parseInt(subnet.split("/")[1], 10); + subnet = subnet.split("/")[0]; + } + var mask = IP.IPmask(bits); + var long_subnet = IP.IPnumber(subnet); + + return (long_ip & mask) == (long_subnet & mask); + }, + + // subnets gescheiden door komma's + inAnySubnet: function(ip, subnets) + { + if (!subnets) + INTERNAL_ERROR_NOSUBNETS; // 'k weet niet of default veilig of onveilig gewenst is + var arr = String(subnets).split(","); + for (var i = 0; i < arr.length; i++) + { + if (IP.inSubnet(ip, arr[i])) + return true; + } + return false; + } + +} %> diff --git a/APPL/Shared/loginTry.asp b/APPL/Shared/loginTry.asp index d66ec45cb6..fed35f01d7 100644 --- a/APPL/Shared/loginTry.asp +++ b/APPL/Shared/loginTry.asp @@ -89,6 +89,7 @@ if (user_key < 0) // jwt claim // TODO: altijd/ ook als user_key > 0? +// TODO ook uit form POST ondersteunen? var jwt = getQParam("jwt", ""); if (user_key < 0 && !jwt) { @@ -101,42 +102,102 @@ if (user_key < 0 && !jwt) Session.Abandon(); } } -if (user_key < 0 && jwt) // TODO of uit http-header authorization: Bearer of uit form POST +if (user_key < 0 && jwt) { var claim = jwt_decode(jwt); - // __DoLog(claim); + __Log(claim); if (claim.err) - abort_with_warning("Invalid JWT: " + claim.err); + shared.internal_error("Invalid JWT: " + claim.err); + + // We staan nooit twee keer dezelfde signature toe. Voorkomt alle replay-aanvallen + // Iets verderop onthouden we signature + var sql = "SELECT prs_perslid_key, fac_session_data " + + " FROM fac_session " + + " WHERE fac_session_expire > sysdate " + + " AND fac_session_sessionid_hash = " + safe.quoted_sql(claim.signature64); + var oRs = Oracle.Execute( sql ); + if (!oRs.eof) + shared.internal_error("Invalid JWT: it has been used before."); + oRs.Close(); var sql = "SELECT *" + " FROM fac_idp" + " WHERE fac_idp_issuer = " + safe.quoted_sql(claim.payload.iss); var oRs = Oracle.Execute(sql); if (oRs.Eof) - abort_with_warning("Unknown JWT issuer: " + claim.payload.iss); + shared.internal_error("Unknown JWT issuer: " + claim.payload.iss); var verify = jwt_verify(claim, oRs("fac_idp_secret").Value, oRs("fac_idp_clockskew").Value); if (verify.err) - abort_with_warning("Invalid JWT: " + verify.err); - // TODO: claim.payload.iss gebruiken om FAC_IDP_CODE te zoeken - // en jwt_verify doen + shared.internal_error("Invalid JWT: " + verify.err); + // en claim.jit registreren/ controleren in fac_session + var isFACFACinternal = oRs("fac_idp_internal").Value != 0; + if (isFACFACinternal && !claim.payload.fclt_realuser) + { + shared.internal_error("Missing fclt_realuser in claim. It is required for IDP_internal."); + } - Session.Contents.Remove("ASPFIXATION"); // Niet moeilijk doen - settings.overrule_setting("login_use_email", 0); // We hebben altijd login gescanned namelijk - if (claim.payload.username) - tryLogin(claim.payload.username, null, { noPassword: true, noFacSession: by_bearer }); + // Ok, de claim is geldig. Nu kijken of we er iets mee kunnen + if (claim.payload.username) // je mag username meegeven + { + settings.overrule_setting("login_use_email", 0); + tryLogin(claim.payload.username, null, { noPassword: true, noFacSession: by_bearer, isFACFACinternal: isFACFACinternal }); + } + if (user_key < 0 && claim.payload.email) // je mag email meegeven + { + settings.overrule_setting("login_use_email", 1); + tryLogin(claim.payload.email, null, { noPassword: true, noFacSession: by_bearer, isFACFACinternal: isFACFACinternal }); + } if (user_key < 0 && claim.perslid_key > 0) - doLogin(claim.payload.perslid_key, null, { noFacSession: by_bearer }); // je mag ook key meegeven - Session("idp_key") = oRs("fac_idp_key").Value; + doLogin(claim.payload.perslid_key, { noFacSession: by_bearer, isFACFACinternal: isFACFACinternal }); // je mag ook key meegeven - var realuser = Session("sso_sgf_realuser"); - if (user_key > 0 && realuser) - shared.trackaction("PRSLOG", user_key, L("lcl_logged_on_sso").format(realuser, Request.ServerVariables("REMOTE_ADDR"))); - Session.Contents.Remove("sso_sgf_realuser"); + if (user_key > 0) + { + // SSO naar een FACFAC gebruiker mag alleen als fac_idp_internal aan staat + if (user.has("WEB_FACFAC") && !isFACFACinternal) + { + __DoLog("Illegal login WEB_FACFAC"); + doLogoff(); + shared.internal_error("This IDP cannot be used for users with WEB_FACFAC."); + } + // Als fac_idp_internal aan staat mag alleen SSO naar een FACFAC gebruiker + // Tenzij S("idp_internal_anyuser") true is, dan mag je naar iedereen + // Dat doen we op OTA via custenc.wsc, dat doen we niet in PROD + if (isFACFACinternal && !S("idp_internal_anyuser") && !user.has("WEB_FACFAC")) + { + doLogoff(); + shared.internal_error("This IDP can only be used for users with WEB_FACFAC."); + } - if (!by_bearer) // bearer is stateless/ single request dus geen redirect + // Ongeldige lukken de volgende keer ook niet. Geldige wil ik niet weer zien + // De expiretijd gaat er alleen over wanneer ik mag opruimen. Neem daarbij uur speling + var agent = String(Request.ServerVariables("HTTP_USER_AGENT")); + var ip = String(Request.ServerVariables("REMOTE_ADDR")); + var sql = "INSERT INTO fac_session" + + " (fac_session_sessionid_hash," + + " fac_session_data," + + " prs_perslid_key," + + " fac_session_expire," + + " fac_session_useragent," + + " fac_session_ip)" + + " VALUES(" + safe.quoted_sql(claim.signature64) + ", " + + " 'JWT replay preventer'," + + user_key + "," + + " SYSDATE + 1/24 + 1/24/60/60 * " + oRs("fac_idp_clockskew").Value + ", " + + safe.quoted_sql(agent, 256) + "," + + safe.quoted_sql(ip, 64) + ")"; + Oracle.Execute(sql); + + // Onthouden hoe je bent binnengekomen zodat logout naar logout_url kan leiden + Session("idp_key") = oRs("fac_idp_key").Value; + } + + if (user_key > 0 && claim.payload.fclt_realuser) + shared.trackaction("PRSLOG", user_key, L("lcl_logged_on_sso").format(claim.payload.fclt_realuser, Request.ServerVariables("REMOTE_ADDR"))); + + if (!by_bearer && user_key > 0) // bearer is stateless/ single request dus geen redirect { var return_to = getQParam("return_to", "/") || "/"; // validate: enkele / voor root, /? voor root met params @@ -163,19 +224,22 @@ if (user_key < 0 && getQParam("sso", "")) + " WHERE fac_idp_code = " + safe.quoted_sql(sso); var oRs = Oracle.Execute(sql); if (oRs.Eof) - abort_with_warning("Identity provider '{0}' is not configured".format(sso)); + shared.internal_error("Identity provider '{0}' is not configured".format(sso)); + var isFACFACinternal = oRs("fac_idp_internal").Value != 0; var ip_restrict = oRs("fac_idp_ipfilter").Value; + if (isFACFACinternal && S("idp_internal_anyuser")) + ip_restrict = ""; // dan niet al te moeilijk doen var ip_ok = true; - if (ip_restrict) + if (ip_restrict) // Application("otap_environment") != "O") { var ip = String(Request.ServerVariables("REMOTE_ADDR")); - ip_ok = new RegExp(ip_restrict.replace(/\./ig, "\\."), "ig").test(ip); + ip_ok = IP.inAnySubnet(ip, ip_restrict); __Log("SSO IP-restrictie {0} versus remote {1}: {2}".format(ip_restrict, ip, ip_ok)); } if (!ip_ok) - abort_with_warning("IP {0} not allowed".format(ip)); // TODO of 400 code forbidden? + shared.internal_error("IP {0} not allowed".format(ip)); // TODO of 400 code forbidden? if (oRs("fac_idp_type").Value == 3) // die doet het verder zelf {