Version and install for library
[gssweb.git] / navigator.gssweb.js
1 console.log("Loading navigator.gssweb.js - #4");
2
3
4 var GSSWeb = (function () {
5   function GSSWeb(config) {
6     this.serverPath = config.serverPath;
7     this.credential = config.credential;
8     this.appTag = config.appTag || "GSSWeb-" + navigator.generateNonce();
9     this.error = config.error || function (err) {
10       console.warn(err);
11     };
12     this.success = config.success;
13
14     this.version = "0.0.1";
15     this.authenticationState = false;
16     this.context = "";
17     this.serverToken = "";
18     this.clientCred = "";
19     this.xhr = new XMLHttpRequest();
20
21     this.gss = new navigator.gssEap({
22       appTag: this.appTag,
23       error: this.handleGSSError.bind(this)
24     });
25   }
26   /* What to invoke when the underlying GSS library 
27    * has a problem.
28    */
29   GSSWeb.prototype.handleGSSError = 
30     function (major, minor, errMsg, appTag) {
31       this.error(errMsg, appTag);
32     };
33
34   /* The basic authenticate function.
35    * Takes no arguments, as all should be supplied
36    * on the base object.
37    */
38   GSSWeb.prototype.authenticate = function () {
39     /* Error checking */
40     // Ensure that the callbacks exist well
41     if ( "function" != typeof(this.error) )
42     {
43       // I can't even call my error function!  All that can be done
44       // is throw an error.
45       throw("Error function not supplied to navigator.gssweb object!");
46     }
47     if ( "function" != typeof(this.success) )
48     {
49       // OK, so we have an error function; use it.
50       this.error(
51         "Success function not supplied to navigator.gssweb object!"
52       );
53     }
54     if ( ! this.presentString(this.serverPath) )
55     {
56       this.error("Server path not supplied to navigator.gssweb object!");
57     }
58
59     
60     /* Setup */
61     this.nonce = navigator.generateNonce();
62
63     // Start off the cascade by getting the
64     // GSS name of the server
65     this.authGetServerName();
66   };
67
68   GSSWeb.prototype.authGetServerName = function () {
69     this.gss.import_name({
70       name: "HTTP@" + window.location.hostname,
71       success: this.authReceiveServerName.bind(this)
72     });
73   };
74   GSSWeb.prototype.authReceiveServerName = function (data, appTag) {
75     this.serverName = data.gss_name;
76
77     /* Either move on to acquire_cred because we have been
78     * supplied a credential, or move on to init_sec_context
79     * when we have not.
80     */
81     if ( this.presentString(this.credential) )
82       this.authGetClientName();
83     else
84       this.authInitSecContext();
85   };
86
87   GSSWeb.prototype.authGetClientName = function () {
88     this.gss.import_name({
89       name: this.credential,
90       success: this.authReceiveClientName.bind(this)
91     });
92   };
93   GSSWeb.prototype.authReceiveClientName = function (data, appTag) {
94     this.clientName = data.gss_name;
95
96     // Next up: Get the local credential
97     this.authAcquireCred();
98   };
99
100   GSSWeb.prototype.authAcquireCred = function () {
101     this.gss.acquire_cred({
102       desired_name: this.clientName,
103       cred_usage: 1,
104       success: this.authReceiveClientCred.bind(this)
105     });
106   };
107   GSSWeb.prototype.authReceiveClientCred =
108     function (cred, actual_mechs, lifetime_rec) {
109       this.clientCred = cred;
110
111       this.authInitSecContext();
112     };
113
114   GSSWeb.prototype.authInitSecContext = function () {
115     var params = {
116       target_name: this.serverName,
117       success: this.sendTokenToServer.bind(this)
118     };
119
120     if ("" != this.clientCred) {
121       params.cred_handle = this.clientCred;
122     }
123     if ("" != this.serverToken) {
124       params.input_token = this.serverToken;
125     }
126     if ("" != this.context) {
127       params.context_handle = this.context;
128     }
129
130     this.gss.init_sec_context(params);
131   };
132
133   GSSWeb.prototype.sendTokenToServer = 
134     function (data, 
135               app_tag) {
136     this.clientToken = data.output_token;
137     this.context = data.context_handle;
138
139     var msg = "nonce=" + this.nonce +
140                "&token=" + encodeURIComponent(this.clientToken);
141
142     this.xhr.open("POST", this.serverPath, true);
143
144     this.xhr.setRequestHeader(
145       'Content-Type', 
146       'application/x-www-form-urlencoded'
147     );
148     this.xhr.onreadystatechange = this.recvTokenFromServer.bind(this);
149
150     this.xhr.send(msg);
151     if (this.xhr.readyStatus <= 2)
152         console.log("Error sending POST, readyStatus =" + this.xhr.readyStatus
153                    + ", msg = '" + msg + "'");
154   };
155
156   GSSWeb.prototype.recvTokenFromServer = function () {
157     // Only care when we're ready
158     if (this.xhr.readyState != 4) {
159       return;
160     }
161
162     switch (this.xhr.status) {
163       case 200:
164         // Finished!
165         var serverResponse = JSON.parse(this.xhr.responseText);
166         var decoded = window.atob(serverResponse.application.data);
167         this.authenticationState = true;
168         this.success(
169           decoded,
170           serverResponse.application["content-type"],
171           this.appTag
172         );
173         break;
174       case 401:
175         // Continue needed
176         var serverResponse = JSON.parse(this.xhr.responseText);
177         this.serverToken = serverResponse.gssweb.token;
178         this.authInitSecContext();
179         break;
180       default:
181         // We have some server-reported error
182         this.error(
183           window.location.hostname + 
184           " reported an error; aborting",
185           this.appTag
186         );
187
188         // Destroy the GSS context.
189         this.context = undefined;
190         return;
191     }
192   };
193   
194   /*************************************
195    * Utility methods
196    *************************************/
197   // return true if the variable is a non-empty string
198   GSSWeb.prototype.presentString = function(str)
199   {
200     return(
201       "string" == typeof(str) &&
202       "" != str
203     );
204   }
205   
206   return GSSWeb;
207 })();
208
209 navigator.gssweb = GSSWeb;