978 lines
36 KiB
C++
978 lines
36 KiB
C++
<% /*
|
|
$Revision$
|
|
$Id$
|
|
File: shared/login.inc
|
|
Description: Inlog functionaliteit
|
|
Parameters:
|
|
Context:
|
|
Note: LET OP: Dit bestand heeft een heel klein stukje VBScript in zich
|
|
Dat maakt dat je dit bestand *niet* meer overal (via common.inc
|
|
of zo) moet gaan includen
|
|
Note2: json2.js is nodig omdat user.options wordt gebruikt
|
|
Op zich moet je het dan maar aan diverse asp's toevoegen maar dan
|
|
moet je ook vele sso.asp's langs. Dan toch maar hier
|
|
|
|
*/
|
|
%>
|
|
<!-- #include file="../Shared/json2.js" -->
|
|
<%
|
|
// Elders is prs_key geauthenticeerd. Registreer die hier als de actieve gebruiker.
|
|
function doLogin(prs_key, params)
|
|
{
|
|
params = params || {};
|
|
// Paranoia mode
|
|
var sql = "SELECT prs_perslid_login"
|
|
+ " FROM prs_perslid"
|
|
+ " WHERE prs_perslid_verwijder IS NULL"
|
|
+ " AND prs_perslid_key = " + prs_key;
|
|
var oRs = Oracle.Execute(sql);
|
|
if (oRs.Eof)
|
|
{
|
|
__DoLog("Niet bestaande of verwijderde persoon " + prs_key + " geweigerd in doLogin.")
|
|
eval("INTERNAL_ERROR_INVALID_LOGIN_" + prs_key);
|
|
}
|
|
var first_login = (oRs("prs_perslid_login").Value == null);
|
|
oRs.Close();
|
|
|
|
// Alvast nieuwe user_key loggen zodat je ziet wie er inlogt.
|
|
if (typeof NO_ADDHEADER == "undefined" && user_key != prs_key && Request.Servervariables("HTTP_FCLT_VERSION").Count > 0)
|
|
{ // wordt opgepikt door FCLTAPI.DLL voor in de logging en daarna gestript. Niet in Fiddler te zien dus
|
|
Response.AddHeader ("FCLT_USERID", customerId + "\\" + String(prs_key));
|
|
}
|
|
|
|
/* global */ user_key = prs_key;
|
|
//user_lang = oRs(1).Value; // globale moet er nog uit!
|
|
if (typeof LCL_Disable == "undefined")
|
|
lcl.loadLCL();
|
|
|
|
setASPFIXATION();
|
|
|
|
Session.Contents.Remove("has_no_remote_res"); // Dat weten we nu nog niet zeker
|
|
|
|
if (!params.noFacSession) // AAEN infobord krijgt er anders 10 per minuut
|
|
{
|
|
// Nu maar eens oudere opruimen. Effectief blijft altijd
|
|
// de laatste sessie van een gebruiker staan.
|
|
// Mogelijk ruim je ook toch verlopen sessies van anderen op. Geen probleem.
|
|
var sql = "DELETE FROM fac_session"
|
|
+ " WHERE fac_session_expire < SYSDATE";
|
|
Oracle.Execute(sql);
|
|
|
|
var agent = String(Request.ServerVariables("HTTP_USER_AGENT"));
|
|
var ip = String(Request.ServerVariables("REMOTE_ADDR"));
|
|
|
|
// Welbeschouwd gebruiken we het volgende FAC_SESSION record nooit, we werken
|
|
// altijd met ASPSESSION==>IIS SESSION==>user_key
|
|
var sql = "INSERT INTO fac_session"
|
|
+ " (fac_session_sessionid_hash,"
|
|
+ " prs_perslid_key,"
|
|
+ " fac_session_expire,"
|
|
+ " fac_session_useragent,"
|
|
+ " fac_session_ip)"
|
|
+ " VALUES(fac.makehash(" + safe.quoted_sql(Session("FACSESSIONID")) + "), "
|
|
+ user_key + ","
|
|
+ " SYSDATE+1, " // 24 uur is genoeg
|
|
+ safe.quoted_sql(agent, 256) + ","
|
|
+ safe.quoted_sql(ip, 64) + ")";
|
|
Oracle.Execute(sql);
|
|
}
|
|
|
|
var registersql = "UPDATE prs_perslid"
|
|
+ " SET prs_perslid_login = SYSDATE"
|
|
+ " WHERE prs_perslid_key=" + user_key;
|
|
Oracle.Execute(registersql);
|
|
|
|
Session("user_key") = user_key; /* Nu ben je pas *echt* ingelogd
|
|
/* global */ user = new Perslid(user_key);
|
|
|
|
// FACFAC tracken we altijd
|
|
if (!params.noFacSession) // fac_scan_cust genereert er anders te veel
|
|
{
|
|
if (user.has("WEB_FACFAC"))
|
|
shared.trackaction("PRSLOG", user_key, L("lcl_logged_on").format(Session("ASPFIXATION").slice(-6)));
|
|
}
|
|
|
|
var autoopen = user.options("autoopen") || [];
|
|
if (first_login && S("fac_firstlogin_expire") > 0)
|
|
{
|
|
autoopen.push({ u: S("fac_firstlogin_url"), d: new Date, t: L("lcl_firstlogin_url"), activate: true });
|
|
user.options("autoopen", autoopen);
|
|
}
|
|
else
|
|
{
|
|
var expireDate = new Date();
|
|
expireDate.setDate(expireDate.getDate() - S("fac_firstlogin_expire"));
|
|
__Log("Autoopen expire date: " + expireDate);
|
|
for (var i = 0; i < autoopen.length; i++)
|
|
{
|
|
if (autoopen[i].u == S("fac_firstlogin_url"))
|
|
{
|
|
if (!autoopen[i].d)
|
|
{
|
|
autoopen[i].d = new Date();
|
|
user.options("autoopen", autoopen);
|
|
__Log("Autoopen expire date for welcome.asp set");
|
|
}
|
|
else
|
|
{
|
|
var d = myJSON.internal_parsedate(null, autoopen[i].d);
|
|
if (d < expireDate)
|
|
{
|
|
autoopen.splice(i, 1);
|
|
user.options("autoopen", autoopen);
|
|
__Log("Welcome.asp expired and removed");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var fac_lang = getQParamSafe("fac_lang", "").toUpperCase(); // overrule via param
|
|
Session("user_lang") = fac_lang || user.dblang();
|
|
// Liever geen session maar m_connections heeft dit al nodig voor zijn fac.initsession
|
|
// Bovendien voorkomen we zo dat een simpele user.lang al een _require_prs_perslid triggert
|
|
return true;
|
|
}
|
|
|
|
// Session.Abondon is gevaarlijk: dan verlies je ook CustomerID etc.
|
|
// Bovendien krijg je met IIS dan nog steeds geen nieuwe ASPSESSIONID
|
|
function doLogoff()
|
|
{
|
|
Session("no_sso") = 1; // Voorkom autosso
|
|
Session.Contents.Remove("user_key");
|
|
Session.Contents.Remove("ASPFIXATION");
|
|
Session.Contents.Remove("must_reset_password");
|
|
Session.Contents.Remove("login_by_fallback");
|
|
deleteSessionCookie("fcltid");
|
|
}
|
|
|
|
// Inloggen via een fcltid-cookie of een session die met QR-code is gescand
|
|
function setUserFromSession (p_session)
|
|
{
|
|
// TODO: Hier wil ik ooit de user_key eigenlijk ook hebben uit
|
|
// een cookie zodat deze query efficienter wordt.
|
|
var sql = "SELECT prs_perslid_key, fac_session_data "
|
|
+ " FROM fac_session "
|
|
+ " WHERE fac_session_expire > sysdate "
|
|
+ " AND fac_session_sessionid_hash = fac.makehash(" + safe.quoted_sql(p_session) + ")";
|
|
var oRs = Oracle.Execute( sql );
|
|
if (!oRs.eof)
|
|
{
|
|
doLogin(oRs("prs_perslid_key").Value);
|
|
var sessionData = oRs("fac_session_data").value;
|
|
|
|
// verwijder de huidige sessie
|
|
sql = "DELETE fac_session"
|
|
+ " WHERE prs_perslid_key = " + user_key // index-performance
|
|
+ " AND fac_session_sessionid_hash = fac.makehash(" + safe.quoted_sql(p_session) + ")";
|
|
Oracle.Execute(sql);
|
|
// makeSessionCookie(sessionData); aanroeper bepaalt maar of er een nieuwe sessie komt
|
|
}
|
|
oRs.Close();
|
|
}
|
|
|
|
function makeSessionCookie (sessionData)
|
|
{
|
|
var sessionId = shared.random(32);
|
|
|
|
// maak nieuwe sessie aan
|
|
var agent = String(Request.ServerVariables("HTTP_USER_AGENT"));
|
|
var ip = String(Request.ServerVariables("REMOTE_ADDR"));
|
|
|
|
sql = "INSERT INTO fac_session ( "
|
|
+ " fac_session_sessionid_hash, "
|
|
+ " prs_perslid_key, "
|
|
+ " fac_session_data, "
|
|
+ " fac_session_expire,"
|
|
+ " fac_session_useragent,"
|
|
+ " fac_session_ip) "
|
|
+ " VALUES ( "
|
|
+ "fac.makehash(" + safe.quoted_sql(sessionId) + "), "
|
|
+ user_key + ", "
|
|
+ safe.quoted_sql(sessionData) + ", "
|
|
+ " sysdate + " + S("login_remember_days") + ", " // sessie timeout op een half jaar.
|
|
+ safe.quoted_sql(agent, 256) + ","
|
|
+ safe.quoted_sql(ip, 64) + " )"
|
|
Oracle.Execute(sql);
|
|
|
|
// set de nieuwe sessionID als cookie.
|
|
Response.Cookies("fcltid")=sessionId;
|
|
Response.Cookies("fcltid").Path = rooturl + "/";
|
|
if (S("auto_https") & 2)
|
|
Response.Cookies("fcltid").Secure=true;
|
|
VBexpireCookie("fcltid", "d", S("login_remember_days"));
|
|
|
|
// fcltcust is niet per se nodig voor Facilitor maar wel handig bij interne ontwikkeling
|
|
Response.Cookies("fcltcust") = customerId;
|
|
Response.Cookies("fcltcust").Path = rooturl + "/";
|
|
if (S("auto_https") & 2)
|
|
Response.Cookies("fcltcust").Secure=true;
|
|
VBexpireCookie("fcltcust", "d", S("login_remember_days"));
|
|
}
|
|
|
|
function deleteSessionCookie (cookiename)
|
|
{
|
|
var session = String(Request.Cookies(cookiename));
|
|
if (session)
|
|
{
|
|
var sql = "DELETE fac_session"
|
|
+ " WHERE prs_perslid_key = " + user_key // index-performance
|
|
+ " AND fac_session_sessionid_hash = fac.makehash(" + safe.quoted_sql(session) + ")";
|
|
Oracle.Execute(sql);
|
|
}
|
|
|
|
// Cookie wissen
|
|
Response.Cookies(cookiename)="";
|
|
Response.Cookies(cookiename).Path = rooturl + "/";
|
|
if (S("auto_https") & 2)
|
|
Response.Cookies(cookiename).Secure=true;
|
|
VBexpireCookie(cookiename, "yyyy", -1);
|
|
}
|
|
|
|
function pbkdf2(wachtwoord, passsalt, workfactor, len)
|
|
{
|
|
if (workfactor < 1 ||
|
|
workfactor > 20) // workfactor 2^24 (16777216) duurt op FACWS001 zo'n 45 seconde
|
|
// Daarom voorlopig limiteren op 2^20 (1048576, zo'n 3 seconde)
|
|
{
|
|
__DoLog(workfactor);
|
|
INTERNAL_ERROR_BADWORKFACTOR;
|
|
}
|
|
len = len || S("prs_password_hash_length"); // default 20
|
|
// __Log("Wachtwoord: {0}, salt: {1}, workfactor: {2}, len: {3}".format(wachtwoord, passsalt, Math.pow(2, workfactor), len));
|
|
var oSLNKDWF = new ActiveXObject("SLNKDWF.About");
|
|
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
|
|
var usStart = oSLNKDWF.usTimer;
|
|
var is_hash = oCrypto.hex_pbkdf2(wachtwoord, passsalt, Math.pow(2, workfactor), len);
|
|
var tm = ((oSLNKDWF.usTimer - usStart)/1000).toFixed(1);
|
|
__Log("Calculating hash with workfactor {0} ({1}) took {2} ms".format(workfactor, Math.pow(2, workfactor), tm));
|
|
return is_hash;
|
|
}
|
|
|
|
function otpcodes(otpsecret)
|
|
{
|
|
if (!otpsecret)
|
|
return false;
|
|
var arr = otpsecret.split("$");
|
|
switch (arr[0])
|
|
{
|
|
case "1": // TOTP, formaat 1$30$size$offset$seed
|
|
if (arr.length != 5)
|
|
{
|
|
__Log(arr);
|
|
INTERNAL_ERROR_BADTOTP;
|
|
}
|
|
var result = { tokentime: new Date(),
|
|
otpstep: parseInt(arr[1], 10),
|
|
otpsize: parseInt(arr[2], 10),
|
|
otpoffset: parseInt(arr[3], 10),
|
|
otpseed: arr[4],
|
|
codes: [] };
|
|
//Response.Write("<p>Current time: {0}".format(toDateTimeString(result.tokentime, true)));
|
|
if (result.otpoffset != 0)
|
|
{
|
|
result.tokentime.setSeconds(result.tokentime.getSeconds() + result.otpoffset)
|
|
//Response.Write("</br>Token time: {0} ({1} s)".format(toDateTimeString(result.tokentime, true), result.otpoffset));
|
|
}
|
|
var ts = Math.round(result.tokentime.getTime() / 1000);
|
|
var cnt = Math.floor(ts / result.otpstep) ; // Google doet 30 seconde
|
|
result.tokenpassed = (ts / result.otpstep - cnt); // zo ver zijn we al in de periode gevorderd
|
|
__Log("OTP time {0} counter {1}".format(toDateTimeString(result.tokentime, true), cnt));
|
|
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
|
|
for (var i = -Math.floor(S("prs_password_otp_window") / result.otpstep); i <= Math.floor(S("prs_password_otp_window") / result.otpstep); i++)
|
|
{
|
|
var onecode = { otpshould : oCrypto.hotp(result.otpseed, cnt + i, result.otpsize),
|
|
dtsfrom: new Date(((cnt + i) * result.otpstep - result.otpoffset) * 1000),
|
|
dtsto: new Date(((cnt + i + 1) * result.otpstep - result.otpoffset)* 1000),
|
|
offset: i * result.otpstep,
|
|
counter: cnt + i
|
|
}
|
|
result.codes.push(onecode);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
INTERNAL_ERROR_BADOTP;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function testpassword(prs_key, wachtwoord, pmobile)
|
|
{
|
|
if (!wachtwoord)
|
|
return false;
|
|
|
|
var sql = " SELECT prs_perslid_key"
|
|
+ " , prs_perslid_flags"
|
|
+ " , prs_perslid_authenticatie"
|
|
+ " , prs_perslid_authenticatie_exp"
|
|
+ " , prs_perslid_salt"
|
|
+ " , prs_perslid_wachtwoord_hash"
|
|
+ " , prs_perslid_oslogin"
|
|
+ " , prs_perslid_apikey"
|
|
+ " FROM prs_perslid"
|
|
+ " WHERE prs_perslid_key = " + prs_key;
|
|
var oRs = Oracle.Execute(sql);
|
|
|
|
var passsalt = oRs("prs_perslid_salt").Value;
|
|
var passhash = oRs("prs_perslid_wachtwoord_hash").Value;
|
|
var mobauth = oRs("prs_perslid_authenticatie").Value;
|
|
var mobauthexp = new Date(oRs("prs_perslid_authenticatie_exp").Value);
|
|
var apikey = oRs("prs_perslid_apikey").Value;
|
|
var oslogin = oRs("prs_perslid_oslogin").Value;
|
|
oRs.Close();
|
|
|
|
if (pmobile==1) // Mobile 'verzonnen' wachtwoord
|
|
{
|
|
wachtwoord = wachtwoord.toLowerCase(); // wij sturen lowercase base32
|
|
wachtwoord = wachtwoord.replace(/0/i, 'o'); // 0 / 1 / 8 komen daar niet in voor
|
|
wachtwoord = wachtwoord.replace(/1/i, 'l');
|
|
wachtwoord = wachtwoord.replace(/8/i, 'b');
|
|
if (mobauth == wachtwoord && mobauthexp && new Date() <= mobauthexp)
|
|
{
|
|
return true; // Goed
|
|
}
|
|
__Log("Mobile token check failed");
|
|
// Wel doorgaan met gewoon wachtwoord controle, dat staan we ook toe
|
|
}
|
|
|
|
// geen wachtwoord
|
|
if (!passhash)
|
|
return false;
|
|
|
|
// APItoken wachtwoord 1452611295:0pBCO67Br4Cs7kkm+zszW0JhjlM
|
|
if (apikey && wachtwoord.match(/[0-9]{10}\:.*/))
|
|
{
|
|
if (protectHMAC.verify(oslogin, wachtwoord, { sleutel: apikey, expire: 60*24*30, relaxed: true })) // 30 dagen. TODO: S("fac_apitoken_auth_expire")
|
|
return true;
|
|
}
|
|
|
|
// gewoon wachtwoord
|
|
// Noot: we zouden hier kunnen testen op
|
|
// AND (prs_perslid_wachtwoord_exp IS NULL OR prs_perslid_wachtwoord_exp > SYSDATE)
|
|
// Met een verlopen wachtwoord mag je echter wel inloggen maar wordt je (via common.inc)
|
|
// wel gedwongen daarna je wachtwoord te wijzigen
|
|
var arr = passhash.split("$");
|
|
if (arr.length == 1) // Old style
|
|
{
|
|
var sql = "SELECT 1"
|
|
+ " FROM prs_perslid"
|
|
+ " WHERE prs.testpassword(prs_perslid_key, " + safe.quoted_sql(wachtwoord) + ") = 1"
|
|
+ " AND prs_perslid_key = " + prs_key
|
|
var oRs = Oracle.Execute(sql);
|
|
var found = !oRs.Eof;
|
|
oRs.Close();
|
|
if (!found)
|
|
return false;
|
|
// else TODO upgraden!
|
|
var workfactor = 0;
|
|
}
|
|
else // new style
|
|
{
|
|
switch (arr[0])
|
|
{
|
|
case "1": // PBKDF2, 1$workfactor$hash
|
|
var workfactor = parseInt(arr[1], 10);
|
|
var should_hash = arr[2];
|
|
if (arr.length != 3 ||
|
|
isNaN(workfactor) ||
|
|
should_hash.length < 40) // workfactor 2^24 (16777216) duurt op FACWS001 zo'n 45 seconde
|
|
// Daarom voorlopig limiteren op 2^20 (1048576, zo'n 3 seconde)
|
|
{
|
|
__DoLog(arr);
|
|
INTERNAL_ERROR_BADHASH;
|
|
}
|
|
var is_hash = pbkdf2(wachtwoord, passsalt, workfactor, should_hash.length / 2);
|
|
if (should_hash != is_hash)
|
|
{
|
|
__Log("Hash mismatch {0}<>{1}".format(should_hash, is_hash));
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
__DoLog(arr);
|
|
INTERNAL_ERROR_UNKNOWN_PASSHASH;
|
|
}
|
|
}
|
|
if (workfactor != S("prs_password_hash_factor"))
|
|
setpassword(prs_key, wachtwoord);
|
|
return true;
|
|
}
|
|
|
|
|
|
function setpassword(prs_key, wachtwoord)
|
|
{
|
|
if (S("prs_password_hash_factor") == 0 || !wachtwoord) // Old style
|
|
{
|
|
var sql = "BEGIN prs.setpassword(" + prs_key + ", " + safe.quoted_sql(wachtwoord) + "); END;";
|
|
Oracle.Execute(sql);
|
|
}
|
|
else
|
|
{
|
|
var passsalt = shared.random(32);
|
|
var workfactor = S("prs_password_hash_factor");
|
|
var is_hash = pbkdf2(wachtwoord, passsalt, workfactor);
|
|
var sql = "UPDATE prs_perslid"
|
|
+ " SET prs_perslid_wachtwoord_exp = NULL" // Niet meer expired. Ooit: SYSDATE + fac.getsetting ('prs_password_expiration') als die is gevuld
|
|
+ " , prs_perslid_salt = " + safe.quoted_sql(passsalt)
|
|
+ " , prs_perslid_wachtwoord_hash = " + safe.quoted_sql('1${0}${1}'.format(workfactor, is_hash))
|
|
+ " WHERE prs_perslid_key = " + prs_key;
|
|
Oracle.Execute(sql);
|
|
}
|
|
}
|
|
|
|
function testotp (prs_key, otprequest)
|
|
{
|
|
var sql = " SELECT prs_perslid_otpsecret"
|
|
+ " , prs_perslid_otpcounter"
|
|
+ " FROM prs_perslid"
|
|
+ " WHERE prs_perslid_key = " + prs_key;
|
|
|
|
var oRs = Oracle.Execute(sql);
|
|
|
|
var otpsecret = oRs("prs_perslid_otpsecret").Value;
|
|
var otpcounter = oRs("prs_perslid_otpcounter").Value || -1;
|
|
oRs.Close();
|
|
|
|
return verify_otp(prs_key, otprequest, otpsecret, otpcounter);
|
|
}
|
|
|
|
function verify_otp (prs_key, otprequest, otpsecret, otpcounter)
|
|
{
|
|
var otpresult = otpcodes(otpsecret);
|
|
|
|
__Log("Otprequest: " + otprequest);
|
|
var otp_oke = false;
|
|
if (otprequest.length == otpresult.otpsize && otprequest.match(/^[0-9]*$/)) // quick check exact 6 cijfers
|
|
{
|
|
for (var i = 0; i < otpresult.codes.length && !otp_oke; i++)
|
|
{
|
|
var code = otpresult.codes[i];
|
|
__Log(code);
|
|
if (code.counter > otpcounter) // Hij mag niet eerder toegepast zijn
|
|
{
|
|
var otpshould = code.otpshould;
|
|
if (otprequest == code.otpshould)
|
|
{
|
|
otp_oke = true;
|
|
otpcounter = code.counter; // TODO: We zouden moeten bijwerken
|
|
var sql = "UPDATE prs_perslid"
|
|
+ " SET prs_perslid_otpcounter = " + otpcounter
|
|
+ " WHERE prs_perslid_key = " + prs_key;
|
|
Oracle.Execute(sql);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!otp_oke)
|
|
{
|
|
__Log("OTP check failed");
|
|
__Log(otpresult);
|
|
return false;
|
|
}
|
|
return otp_oke;
|
|
}
|
|
|
|
//
|
|
// zet Session("user_key") als username en wachtwoord geldig zijn.
|
|
// Login na verzending via sms moet binnen 1 kwartier ingevuld zijn.
|
|
// resultaat: true bij succesvolle login, false bij niet succesvol
|
|
// drie username opties:
|
|
// - prs_perslid_oslogin
|
|
// - prs_perslid_oslogin2
|
|
// - upper(prs_perslid_email)
|
|
// drie wachtwoord opties
|
|
// - leeg (single-signon)
|
|
// - prs_perslid_wachtwoord (of eigenlijk: prs_perslid_salt en prs_perslid_wachtwoord_hash)
|
|
// - prs_perslid_authenticatie (en prs_perslid_authenticatie_exp > sysdate)
|
|
// de laatste wordt gebruikt voor mobile/SMS
|
|
|
|
/* global */ login_fail_reason = L("lcl_login_wrong");
|
|
/* global */ otp_user_key = -1;
|
|
|
|
function tryLogin(username, wachtwoord, params)
|
|
{
|
|
params = params || {};
|
|
Session.Contents.Remove("org_user_key");
|
|
|
|
if (!username || username == 'undefined')
|
|
return false;
|
|
|
|
var logins = [];
|
|
if (S("login_use_email"))
|
|
{
|
|
logins.push(" upper(prs_perslid_email) = " + safe.quoted_sql_upper(username));
|
|
}
|
|
else if (getQParam("API", ""))
|
|
{
|
|
logins.push(" prs_perslid_apikey = " + safe.quoted_sql_upper(username, 128));
|
|
wachtwoord = null;
|
|
}
|
|
else
|
|
{
|
|
logins.push(" prs_perslid_oslogin = " + safe.quoted_sql_upper(username, 30));
|
|
logins.push(" prs_perslid_oslogin2 = " + safe.quoted_sql_upper(username, 30));
|
|
}
|
|
var sql = " SELECT prs_perslid_key, "
|
|
+ " prs_perslid_flags"
|
|
+ " , prs_perslid_otpsecret"
|
|
+ " , prs_perslid_otpcounter"
|
|
+ " FROM prs_perslid"
|
|
+ " WHERE prs_perslid_verwijder IS NULL"
|
|
+ " AND (" + logins.join(" OR ") + ")"
|
|
+ " AND BITAND(prs_perslid_flags, 1+4+8) = 0"; // 2==unconfirmed staan we nog heel even toe
|
|
|
|
var oRs = Oracle.Execute(sql);
|
|
if (oRs.Eof)
|
|
return false; // Gebruikersnaam niet eens gevonden
|
|
|
|
var otpsecret = oRs("prs_perslid_otpsecret").Value;
|
|
var otpcounter = oRs("prs_perslid_otpcounter").Value || -1;
|
|
|
|
var found = false;
|
|
if (wachtwoord == null) // SSO
|
|
found = true; // En zijn we verder wel klaar
|
|
else
|
|
found = testpassword(oRs("prs_perslid_key").Value, wachtwoord, params.pmobile);
|
|
|
|
if (!found)
|
|
return false;
|
|
|
|
if ((oRs("prs_perslid_flags").Value & 2) == 2)
|
|
{
|
|
login_fail_reason = L("lcl_self_register_unconfirmed");
|
|
return false;
|
|
}
|
|
|
|
if (!otpsecret || !wachtwoord)
|
|
{
|
|
var deze = new Perslid(oRs("prs_perslid_key").Value);
|
|
if (wachtwoord && deze.has("WEB_FACFAC"))
|
|
{
|
|
login_fail_reason = L("lcl_login_needs_otpsecret");
|
|
return false;
|
|
}
|
|
doLogin(oRs("prs_perslid_key").Value, params);
|
|
}
|
|
else if (params.otpcode && testotp(oRs("prs_perslid_key").Value, params.otpcode))
|
|
doLogin(oRs("prs_perslid_key").Value, params);
|
|
else // Wordt opgepikt door login_save.asp
|
|
/* global */ otp_user_key = oRs("prs_perslid_key").Value;
|
|
|
|
oRs.Close();
|
|
return true;
|
|
}
|
|
|
|
// function SecureSSO
|
|
// Verzorgt de secure Single Signon communicatie protocol
|
|
//
|
|
// ssoProps
|
|
// strSharedKey: afgesproken shared key
|
|
// onSuccess: functie die aangeroepen wordt bij success
|
|
// We kunnen hier nog via twee routes komen: oude stijl (cust/xxxx/sso.asp)
|
|
// en nieuwe stijl (xxxx.facilitor.nl?sso=1)
|
|
// In het laatste geval zal ssoProps.sso ook 1 of 2 zijn
|
|
function SecureSSO(ssoProps)
|
|
{
|
|
var strAction, strReturnURL, strKey, strGUID, strCTID
|
|
var strUserName, strDecryptedCode, strControlID, strControlDecryptedCode, strLengthCode
|
|
//'* variables *******************************************************
|
|
//'*******************************************************************
|
|
Response.Buffer=true
|
|
%>
|
|
<HTML>
|
|
<HEAD>
|
|
<script type="text/javascript">
|
|
function fnSubmit() {
|
|
window.document.form.submit();
|
|
return;
|
|
}
|
|
</SCRIPT>
|
|
</HEAD>
|
|
<%
|
|
strReturnURL = getFParam("returnurl", "");
|
|
strReturnURL= strReturnURL.replace("<", "");
|
|
strReturnURL= strReturnURL.replace(">", "");
|
|
strAction = getFParam("action", "");
|
|
if (!strAction && ssoProps.ssoURL) // we zijn begonnen in Facilitor en moeten nog naar de klant
|
|
{
|
|
strReturnURL = ssoURL;
|
|
if (!strReturnURL)
|
|
{
|
|
__DoLog("Secure SSO login error 0");
|
|
Response.Write("Foute aanroep");
|
|
Response.End;
|
|
}
|
|
strAction = "requestid";
|
|
Session("SSO_QUERYSTRING") = String(Request.ServerVariables("QUERY_STRING")); // Deze onthouden we
|
|
Session("SSO_URL") = String(Request.ServerVariables("URL")); // Deze onthouden we
|
|
}
|
|
|
|
if (strAction == "requestid")
|
|
{
|
|
// * action = requestid *******************************************
|
|
%>
|
|
<BODY LANGUAGE="javascript" onload="return fnSubmit()">
|
|
<%
|
|
if (strReturnURL == "")
|
|
{
|
|
__DoLog("Secure SSO login error 1");
|
|
Response.write("Error: onvoldoende informatie ontvangen.")
|
|
Response.end
|
|
}
|
|
else
|
|
{
|
|
Response.write("Een moment aub..")
|
|
strGUID = GetGuid(64)
|
|
strCTID = GetGuid(strReturnURL.length)
|
|
// Save GUID
|
|
Session("GUID") = strGUID;
|
|
Session("CTID") = strCTID;
|
|
Session("GUIDEXPIRE") = (new Date()).valueOf()
|
|
+ (ssoProps.Timeout?ssoProps.Timeout:30)*1000;
|
|
if (Request.Form("Jumpto").Count>0) // Remember it (old style)
|
|
{
|
|
Session("FirstPage")=""+Request.Form("Jumpto")
|
|
}
|
|
%>
|
|
<form action='<%=strReturnURL%>' method="post" name="form" ID="Form1">
|
|
<input type="hidden" name="guid" value="<%=strGUID%>" ID="Hidden1">
|
|
<input type="hidden" name="ctid" value="<%=strCTID%>" ID="Hidden2">
|
|
<%
|
|
}
|
|
}
|
|
else if (strAction == "processcode")
|
|
{
|
|
// * action = processcode *****************************************
|
|
%>
|
|
<BODY>
|
|
<%
|
|
|
|
strUserName = String(Request.form("code"))
|
|
strControlID = String(Request.form("ctcode"))
|
|
strLengthCode = Request.form("ltcode")
|
|
strGUID = Session("GUID")
|
|
strCTID = Session("CTID")
|
|
var expire = Session("GUIDEXPIRE");
|
|
// Clean session memory
|
|
Session.Contents.Remove("GUID");
|
|
Session.Contents.Remove("CTID");
|
|
Session.Contents.Remove("GUIDEXPIRE");
|
|
if (typeof expire == "undefined" || !expire || (new Date()).valueOf() > expire ||
|
|
typeof strGUID == "undefined" || strUserName == "" || typeof strCTID == "undefined" || strControlID == "")
|
|
{
|
|
Session.Contents.Remove("FirstPage");
|
|
if (strReturnURL == "")
|
|
{
|
|
__DoLog("Secure SSO login error 2");
|
|
Response.write("Error: onvoldoende informatie ontvangen.")
|
|
Response.end
|
|
}
|
|
else
|
|
{
|
|
// FSN#25537 deze komt erg regelmatig voor maar oorzaak onbekend
|
|
__Log("Secure SSO login error 3");
|
|
Response.write("Error: onvoldoende informatie ontvangen.")
|
|
Response.End;
|
|
// Response.redirect(strReturnURL) kan oneindige loop geven
|
|
}
|
|
}
|
|
// Convert from ASC chars
|
|
strUserName = ConvertFromAsc(strUserName)
|
|
strControlID = ConvertFromAsc(strControlID)
|
|
// * decrypt ******************************************************
|
|
//First decoding phase
|
|
var strKey = (ssoProps.strSharedKey + strGUID).substr(0,strUserName.length);
|
|
strDecryptedCode = DeCrypt(strUserName)
|
|
//Second decoding phase
|
|
var strKey = strGUID.substr(0,strDecryptedCode.length);
|
|
strDecryptedCode = DeCrypt(strDecryptedCode)
|
|
// * decrypt Controlkey ********************************************
|
|
// First decoding phase
|
|
strKey = (ssoProps.strSharedKey + strCTID).substr(0,strControlID.length)
|
|
strControlDecryptedCode = DeCrypt(strControlID)
|
|
// Second decoding phase
|
|
strKey = strCTID.substr(0,strControlDecryptedCode.length)
|
|
strControlDecryptedCode = DeCrypt(strControlDecryptedCode)
|
|
// ltcode strLengthCode
|
|
if (strControlDecryptedCode == strReturnURL && parseInt(strLengthCode,10) == strDecryptedCode.length)
|
|
{
|
|
// For the ASP: User is authenticated, strDecryptedCode contains the validated Domain\Username
|
|
__Log("SSO Gebruikersnaam = " + strDecryptedCode)
|
|
if (ssoProps.fnparseName)
|
|
{
|
|
strDecryptedCode = ssoProps.fnparseName(strDecryptedCode)
|
|
//Response.write ("<p>Na fnparseName: " + strDecryptedCode)
|
|
}
|
|
if (tryLogin(strDecryptedCode, null, { noPassword: true }))
|
|
{
|
|
if (ssoProps.fnonSuccess)
|
|
ssoProps.fnonSuccess(user_key);
|
|
else // Alles goed!
|
|
{
|
|
var sso_qs = Session("SSO_QUERYSTRING")||"";
|
|
var sso_url = Session("SSO_URL")||"/default.asp";
|
|
Session.Contents.Remove("SSO_QUERYSTRING");
|
|
Session.Contents.Remove("SSO_URL");
|
|
Response.Redirect(sso_url + (sso_qs?"?":"") + sso_qs);
|
|
}
|
|
}
|
|
else
|
|
{ // Automatisch naar het inlogscherm
|
|
__DoLog("Secure SSO login niet gevonden binnen Facilitor: " + strDecryptedCode);
|
|
Response.Redirect(rooturl + "/default.asp");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (strReturnURL == "")
|
|
{
|
|
__DoLog("Secure SSO login error 4");
|
|
Response.write("Error: onvoldoende informatie ontvangen.")
|
|
Response.end
|
|
}
|
|
else
|
|
{
|
|
__Log("Secure SSO login error 5");
|
|
Response.Write("Decodeer fout");
|
|
Response.End;
|
|
Response.redirect(strReturnURL)
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
__Log("Secure SSO login error 6");
|
|
Response.Write("Foute aanroep");
|
|
Response.End;
|
|
}
|
|
// * Functions ********************************************************
|
|
function ConvertFromAsc(strAsc)
|
|
{
|
|
var iCount
|
|
var iChars
|
|
var sConvertFromAsc = ""
|
|
iCount = 0
|
|
do
|
|
{
|
|
iChars = parseInt(strAsc.substr(iCount,1))
|
|
iCount = iCount + 1
|
|
sConvertFromAsc = sConvertFromAsc + String.fromCharCode(parseInt(strAsc.substr(iCount,iChars)))
|
|
iCount = iCount + iChars
|
|
} while (iCount < strAsc.length);
|
|
return sConvertFromAsc;
|
|
}
|
|
|
|
function GetGuid(iDigits)
|
|
{
|
|
var lsGUID
|
|
var lsTemp
|
|
var TypeLib = Server.CreateObject("Scriptlet.TypeLib")
|
|
var lsTemp = ""
|
|
do
|
|
{
|
|
lsGUID = String(TypeLib.Guid).substr(0, 38)
|
|
lsTemp = lsTemp + lsGUID.substr(1,8) + lsGUID.substr(10,4) + lsGUID.substr(15,4) + lsGUID.substr(20,4) + lsGUID.substr(25,12)
|
|
} while (lsTemp.length < iDigits)
|
|
TypeLib = null
|
|
return lsTemp.substr(0,iDigits);
|
|
}
|
|
|
|
function DeCrypt(strEncrypted)
|
|
{
|
|
var strChar, iKeyChar, iStringChar, i
|
|
var strDecrypted = "";
|
|
for (i=0; i<strEncrypted.length; i++)
|
|
{
|
|
iKeyChar = strKey.charCodeAt(i);
|
|
iStringChar = strEncrypted.charCodeAt(i);
|
|
iDeCryptChar = iStringChar ^ iKeyChar
|
|
strDecrypted = strDecrypted + String.fromCharCode(iDeCryptChar);
|
|
}
|
|
return strDecrypted;
|
|
}
|
|
// *********************************************************************
|
|
%>
|
|
</form>
|
|
</BODY>
|
|
</HTML>
|
|
<%
|
|
Response.End;
|
|
}
|
|
|
|
var base64s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
function decode_b64(encStr) {
|
|
var bits, decOut = '', i = 0;
|
|
for(; i<encStr.length; i += 4){
|
|
bits =
|
|
(base64s.indexOf(encStr.charAt(i)) & 0xff) <<18 |
|
|
(base64s.indexOf(encStr.charAt(i +1)) & 0xff) <<12 |
|
|
(base64s.indexOf(encStr.charAt(i +2)) & 0xff) << 6 |
|
|
base64s.indexOf(encStr.charAt(i +3)) & 0xff;
|
|
decOut += String.fromCharCode(
|
|
(bits & 0xff0000) >>16, (bits & 0xff00) >>8, bits & 0xff);
|
|
}
|
|
if(encStr.charCodeAt(i -2) == 61)
|
|
undecOut=decOut.substring(0, decOut.length -2);
|
|
else if(encStr.charCodeAt(i -1) == 61)
|
|
undecOut=decOut.substring(0, decOut.length -1);
|
|
else undecOut=decOut;
|
|
|
|
return unescape(undecOut); //line add for chinese char
|
|
}
|
|
|
|
function SimpleSSO()
|
|
{
|
|
if (S("use_simple_sso") == 0)
|
|
return;
|
|
|
|
var username = String(Session("UID_DEC"));
|
|
__Log('User#1 = '+username);
|
|
// facilitorplace SSO decoded/descripted login?
|
|
// Bij decoded login moet de setting "S("use_simple_sso")" aan staan
|
|
if (username != '' && username!='undefined')
|
|
{
|
|
username = decode_b64(username);
|
|
Session.Contents.Remove("UID_DEC"); // nooit twee keer
|
|
__Log('User#2a = '+username);
|
|
}
|
|
if (username !='' && username!='undefined') {
|
|
// Strip domain name
|
|
while( (i = username.indexOf('\\')) >= 0 ) {
|
|
l = username.length;
|
|
if( i < l-1 ) username = username.substring(i+1,l);
|
|
}
|
|
tryLogin(username, null, { noPassword: true });
|
|
}
|
|
}
|
|
|
|
function IntegratedSSO()
|
|
{
|
|
var username = String( Request.ServerVariables("REMOTE_USER") ).toUpperCase();
|
|
__Log('REMOTE_USER = '+username);
|
|
if (username =='' || username=='UNDEFINED')
|
|
{
|
|
username = String( Request.ServerVariables("HTTP_USER") ).toUpperCase();
|
|
__Log('HTTP_USER = '+username);
|
|
if (username =='' || username=='UNDEFINED')
|
|
{
|
|
username = String( Request.ServerVariables("HTTP_LOGIN") ).toUpperCase();
|
|
__Log('HTTP_LOGIN = '+username);
|
|
if (username =='' || username=='UNDEFINED')
|
|
{
|
|
// HTTP_LOGIN, REMOTE_USER or HTTP_USER is not (yet) set. Forcing Windows Authentication
|
|
Response.Status = "401 Unauthorized";
|
|
shared.simpel_page("os_logon is set, trying 401 Unautorized<br>If this page stays, check IIS if integrated authentication is turned on.");
|
|
Response.End(); // Reloads current file
|
|
}
|
|
}
|
|
}
|
|
if (username !='' && username!='UNDEFINED')
|
|
{
|
|
// Strip domain name
|
|
while( (i = username.indexOf('\\')) >= 0 ) {
|
|
l = username.length;
|
|
if( i < l-1 ) username = username.substring(i+1,l);
|
|
}
|
|
tryLogin(username, null, { noPassword: true });
|
|
}
|
|
}
|
|
|
|
/* resultaat:
|
|
{ err: "Iets niet goed" }
|
|
of { header: header,
|
|
payload: payload (claim)
|
|
signature: signature
|
|
}
|
|
LET OP: signature is nog niet gevalideerd!
|
|
*/
|
|
function jwt_decode(token)
|
|
{
|
|
// check token
|
|
if (!token) {
|
|
return { err: 'No token supplied' };
|
|
}
|
|
// check segments
|
|
var segments = token.split('.');
|
|
if (segments.length !== 3) {
|
|
return { err: 'Not enough or too many segments' };
|
|
}
|
|
|
|
// All segment should be base64
|
|
var result = { headerSeg: segments[0],
|
|
payloadSeg: segments[1],
|
|
signature64: segments[2]
|
|
}
|
|
|
|
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
|
|
|
|
try
|
|
{
|
|
// base64 decode and parse JSON
|
|
result.header = JSON.parse(oCrypto.base64_decode(result.headerSeg));
|
|
result.payload = JSON.parse(oCrypto.base64_decode(result.payloadSeg));
|
|
}
|
|
catch (s)
|
|
{
|
|
return { err: "Invalid JSON: " + e.description };
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
function jwt_verify(decoded_jwt, secret, skew, duration)
|
|
{
|
|
skew = skew || 0;
|
|
duration = duration || 0;
|
|
if (decoded_jwt.header.alg != "HS256")
|
|
return { err: "Only HS256 is supported" };
|
|
|
|
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
|
|
var sig = oCrypto.hex_hmac_sha256(secret, decoded_jwt.headerSeg + "." + decoded_jwt.payloadSeg);
|
|
var sig64 = oCrypto.hex2base64(sig, false, true); // no padding, urlsafe
|
|
|
|
var now = new Date().getTime() / 1000;
|
|
if (claim.payload.iat)
|
|
{
|
|
// Support for nbf and exp claims.
|
|
// According to the RFC, they should be in seconds.
|
|
if (claim.payload.nbf && now + skew < claim.payload.nbf ) {
|
|
return { err: 'Token not yet active' };
|
|
}
|
|
|
|
if (claim.payload.exp && now > claim.payload.exp + skew) {
|
|
return { err: 'Token expired' };
|
|
}
|
|
|
|
// Onze eigen duration/expiration controleren we ook nog
|
|
if (claim.payload.iat + duration < now - skew) {
|
|
__DoLog("Token expired. Now is {0}, got {1}".format(toDateTimeString(new Date(now * 1000), true),
|
|
toDateTimeString(new Date(claim.payload.iat * 1000), true)));
|
|
return { err: 'Token expired' };
|
|
}
|
|
if (claim.payload.iat > now + skew) {
|
|
__DoLog("Token not yet active. Now is {0}, got {1}".format(toDateTimeString(new Date(now * 1000), true),
|
|
toDateTimeString(new Date(claim.payload.iat * 1000), true)));
|
|
return { err: 'Token not yet active' };
|
|
}
|
|
}
|
|
if (decoded_jwt.signature64 == sig64)
|
|
return { success: true }
|
|
|
|
return { err: "Token signature did not verify" };
|
|
}
|
|
|
|
%>
|
|
<script language="VBScript" runat="Server">
|
|
'' // Met de beste wil van de wereld kreeg ik dit niet werkend met JScript
|
|
'' // Op de SGF12 moest ik .Expires = '12/31/2012' doen
|
|
'' // Op mijn Vista PC moest ik .Expires = '31/12/2012' doen
|
|
'' // Daarom maar VBScript
|
|
Sub VBexpireCookie(cookiename, interval, number)
|
|
Dim datum
|
|
datum = DateAdd(interval, number, Now)
|
|
Response.Cookies(cookiename).Expires=datum
|
|
End Sub
|
|
</script>
|