From 6d773ffb25a1a916a36f4aab238700984e393f67 Mon Sep 17 00:00:00 2001 From: Mark Donnelly Date: Wed, 22 Oct 2014 16:54:44 -0400 Subject: [PATCH] First pass at making a GSSWeb object --- navigator.gssweb.js | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 navigator.gssweb.js diff --git a/navigator.gssweb.js b/navigator.gssweb.js new file mode 100644 index 0000000..a72fa87 --- /dev/null +++ b/navigator.gssweb.js @@ -0,0 +1,204 @@ +console.log("Loading navigator.gssweb.js - #2"); + + +var GSSWeb = (function () { + function GSSWeb(config) { + this.serverPath = config.serverPath; + this.credential = config.credential; + this.appTag = config.appTag || "GSSWeb-" + navigator.generateNonce(); + this.error = config.error || function (err) { + console.warn(err); + }; + this.success = config.success; + + this.version = "0.0.1"; + this.authenticationState = false; + this.context = ""; + this.serverToken = ""; + this.clientCred = ""; + this.xhr = new XMLHttpRequest(); + this.xhr.open("POST", this.serverPath, true); + this.xhr.setRequestHeader( + 'Content-Type', + 'application/x-www-form-urlencoded' + ); + this.xhr.onreadystatechange = this.recvTokenFromServer.bind(this); + + this.gss = new navigator.gss_eap({ + appTag: this.appTag, + error: this.handleGSSError.bind(this) + }); + } + /* What to invoke when the underlying GSS library + * has a problem. + */ + GSSWeb.prototype.handleGSSError = + function (major, minor, errMsg, appTag) { + this.error(errMsg, appTag); + }; + + /* The basic authenticate function. + * Takes no arguments, as all should be supplied + * on the base object. + */ + GSSWeb.prototype.authenticate = function () { + /* Error checking */ + // Ensure that the callbacks exist well + if ( "function" != typeof(this.error) ) + { + // I can't even call my error function! All that can be done + // is throw an error. + throw("Error function not supplied to navigator.gssweb object!"); + } + if ( "function" != typeof(this.success) ) + { + // OK, so we have an error function; use it. + this.error( + "Success function not supplied to navigator.gssweb object!" + ); + } + if ( ! this.presentString(this.serverPath) ) + { + this.error("Server path not supplied to navigator.gssweb object!"); + } + + + /* Setup */ + this.nonce = navigator.generateNonce(); + + // Start off the cascade by getting the + // GSS name of the server + this.authGetServerName(); + }; + + GSSWeb.prototype.authGetServerName = function () { + this.gss.import_name({ + name: "HTTP@" + window.location.host, + success: this.authReceiveServerName.bind(this) + }); + }; + GSSWeb.prototype.authReceiveServerName = function (data, appTag) { + this.serverName = data.gss_name; + + /* Either move on to acquire_cred because we have been + * supplied a credential, or move on to init_sec_context + * when we have not. + */ + if ( this.presentString(this.credential) ) + this.authGetClientName(); + else + this.authInitSecContext(); + }; + + GSSWeb.prototype.authGetClientName = function () { + this.gss.import_name({ + name: this.credential, + success: this.authReceiveClientName.bind(this) + }); + }; + GSSWeb.prototype.authReceiveClientName = function (data, appTag) { + this.clientName = data.gss_name; + + // Next up: Get the local credential + this.authAcquireCred(); + }; + + GSSWeb.prototype.authAcquireCred = function () { + this.gss.acquire_cred({ + desired_name: this.clientName, + cred_usage: 1, + success: this.authReceiveClientCred.bind(this) + }); + }; + GSSWeb.prototype.authReceiveClientCred = + function (cred, actual_mechs, lifetime_rec) { + this.clientCred = cred; + + this.authInitSecContext(); + }; + + GSSWeb.prototype.authInitSecContext = function () { + var params = { + target_name: this.serverName, + success: this.sendTokenToServer.bind(this) + }; + + if ("" != this.clientCred) { + params.claimant_cred_handle = this.clientCred; + } + if ("" != this.serverToken) { + params.input_token = this.serverToken; + } + if ("" != this.context) { + params.context_handle = this.context; + } + + this.gss.init_sec_context(params); + }; + GSSWeb.prototype.sendTokenToServer = + function (ctxt, + mech_type, + output_token, + ret_flags, + time_rec, + app_tag) { + this.clientToken = output_token; + this.context = ctxt; + + var data = "nonce=" + this.nonce + + "&token=" + encodeURIComponent(this.clientToken); + this.xhr.send(data); + }; + GSSWeb.prototype.recvTokenFromServer = function () { + // Only care when we're ready + if (this.xhr.readyState != 4) { + return; + } + + var serverResponse = JSON.parse(this.xhr.responseText); + this.serverToken = serverResponse.gssweb.token; + switch (this.xhr.status) { + case 200: + // Finished! + var decoded = window.atob(serverResponse.application.data); + this.authenticationState = true; + this.success( + decoded, + serverResponse.application["content-type"], + this.appTag + ); + break; + case 401: + // Continue needed + this.authInitSecContext(); + break; + default: + // We have some server-reported error + this.error( + window.location.host + + " reported an error; aborting", + this.appTag + ); + + // Destroy the GSS context. + this.context = undefined; + return; + } + }; + + /************************************* + * Utility methods + *************************************/ + // return true if the variable is a non-empty string + GSSWeb.prototype.presentString = function(str) + { + return( + "string" == typeof(str) && + "" != str + ); + } + + return GSSWeb; +})(); + +navigator.gssweb = GSSWeb; -- 2.1.4