authproxy.js

1       // 3.0.2-SNAPSHOT
2       var exec = require('cordova/exec');
3       
4       /**
5        * The AuthProxy plugin provides the ability to make HTTPS requests with mutual authentication.<br/>
6        * <br/>
7        * The regular XMLHttpRequest does not
8        * support mutual authentication.  The AuthProxy plugin allows you to specify a certificate to include in an HTTPS request
9        * to identify the client to the server.  This allows the server to verify the identity of the client.  An example of where you
10       * might need mutual authenticaion is the onboarding process to register with an application, or, to access an
11       * OData producer. This occurs mostly in Business to Business (B2B) applications. This is different from most business to
12       * consumer (B2C) web sites where it is only the server that authenticates itself to the client with a certificate.<br/>
13       * <br/>
14       * <b>Adding and Removing the AuthProxy Plugin</b><br/>
15       * The AuthProxy plugin is added and removed using the
16       * <a href="http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface">Cordova CLI</a>.<br/>
17       * <br/>
18       * To add the AuthProxy plugin to your project, use the following command:<br/>
19       * cordova plugin add <path to directory containing Kapsel plugins>\authproxy<br/>
20       * <br/>
21       * To remove the AuthProxy plugin from your project, use the following command:<br/>
22       * cordova plugin rm com.sap.mp.cordova.plugins.authproxy
23       * @namespace
24       * @alias AuthProxy
25       * @memberof sap
26       */
27      var AuthProxy = function () {};
28      
29      
30      /**
31       * Constant definitions for registration methods
32       */
33      
34      /**
35       * Constant indicating the operation failed with unknown error. Used as a possible value for the
36       * errorCode in {@link sap.AuthProxy~errorCallback}.
37       * @constant
38       * @type number 
39       */
40      AuthProxy.prototype.ERR_UNKNOWN = -1;
41      
42      /**
43       * Constant indicating the operation failed due to an invalid parameter (for example, a string was passed where a number was
44       * required). Used as a possible value for the errorCode in {@link sap.AuthProxy~errorCallback}.
45       * @constant 
46       * @type number 
47       */
48      AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE = -2;
49      
50      /**
51       * Constant indicating the operation failed because of a missing parameter. Used as a possible value for the
52       * errorCode in {@link sap.AuthProxy~errorCallback}.
53       * @constant
54       * @type number 
55       */
56      AuthProxy.prototype.ERR_MISSING_PARAMETER = -3;
57      
58      /**
59       * Constant indicating there is no such Cordova action for the current service.  When a Cordova plugin calls into native
60       * code it specifies an action to perform.  If the action provided by the JavaScript is unknown to the native code this
61       * error occurs.  This error should not occur as long as authproxy.js is unmodified. Used as a possible
62       * value for the errorCode in {@link sap.AuthProxy~errorCallback}.
63       * @constant
64       * @type number 
65       */
66      AuthProxy.prototype.ERR_NO_SUCH_ACTION = -100;
67      
68      /**
69       * Constant indicating the certificate from file is not supported on the current platform. Used as a possible value for the
70       * errorCode in {@link sap.AuthProxy~errorCallback}.
71       * @constant
72       * @type number 
73       */
74      AuthProxy.prototype.ERR_FILE_CERTIFICATE_SOURCE_UNSUPPORTED = -101;
75      
76      /**
77       * Constant indicating the certificate from the system keystore is not supported on the current platform. Used as a possible value
78       * for the errorCode in {@link sap.AuthProxy~errorCallback}.
79       * @constant
80       * @type number 
81       */
82      AuthProxy.prototype.ERR_SYSTEM_CERTIFICATE_SOURCE_UNSUPPORTED = -102;
83      
84      /**
85       * Constant indicating the certificate with the given alias could not be found. Used as a possible value for the
86       * errorCode in {@link sap.AuthProxy~errorCallback}.
87       * @constant
88       * @type number 
89       */
90      AuthProxy.prototype.ERR_CERTIFICATE_ALIAS_NOT_FOUND = -104;
91      
92      /**
93       * Constant indicating the certificate file could not be found. Used as a possible value for the
94       * errorCode in {@link sap.AuthProxy~errorCallback}.
95       * @constant
96       * @type number 
97       */
98      AuthProxy.prototype.ERR_CERTIFICATE_FILE_NOT_EXIST = -105;
99      
100     /**
101      * Constant indicating incorrect certificate file format.  Used as a possible value for the
102      * errorCode in {@link sap.AuthProxy~errorCallback}.
103      * @constant
104      * @type number 
105      */
106     AuthProxy.prototype.ERR_CERTIFICATE_INVALID_FILE_FORMAT = -106;
107     
108     /**
109      * Constant indicating failure in getting the certificate. Used as a possible value for the
110      * errorCode in {@link sap.AuthProxy~errorCallback}.
111      * @constant
112      * @type number 
113      */
114     AuthProxy.prototype.ERR_GET_CERTIFICATE_FAILED = -107;
115     
116     /**
117      * Constant indicating the provided certificate failed validation on the server side. Used as a possible value for the
118      * errorCode in {@link sap.AuthProxy~errorCallback}.
119      * @constant
120      * @type number 
121      */
122     AuthProxy.prototype.ERR_CLIENT_CERTIFICATE_VALIDATION = -108;
123     
124     /**
125      * Constant indicating the server certificate failed validation on the client side.  This is likely because the server certificate
126      * is self-signed, or not signed by a well-known certificate authority.  This constant is used as a possible value for the
127      * errorCode in {@link sap.AuthProxy~errorCallback}.
128      * @constant
129      * @type number 
130      */
131     AuthProxy.prototype.ERR_SERVER_CERTIFICATE_VALIDATION = -109;
132     
133     /**
134      * Constant indicating the server request failed. Used as a possible value for the
135      * errorCode in {@link sap.AuthProxy~errorCallback}.
136      * @constant
137      * @type number 
138      */
139     AuthProxy.prototype.ERR_SERVER_REQUEST_FAILED = -110;
140     
141     /**
142      * Constant indicating the logon manager core library is not available.  Getting this error code means you tried
143      * to use Logon plugin features (for example, a certificate from Logon) without adding the Logon plugin to the app.
144      * A possible value for the errorCode in {@link sap.AuthProxy~errorCallback}.
145      * @constant
146      * @type number
147      */
148     AuthProxy.prototype.ERR_LOGON_MANAGER_CORE_NOT_AVAILABLE = -111;
149     
150     /**
151      * Constant indicating the logon manager certifciate method is not available. Used as a possible value for the
152      * errorCode in {@link sap.AuthProxy~errorCallback}.
153      * @constant
154      * @type number
155      */
156     AuthProxy.prototype.ERR_LOGON_MANAGER_CERTIFICATE_METHOD_NOT_AVAILABLE = -112;
157     
158     /**
159      * Constant indicating timeout error while connecting to the server. Used as a possible value for the
160      * errorCode in {@link sap.AuthProxy~errorCallback}.
161      * @constant
162      * @type number 
163      */
164     AuthProxy.prototype.ERR_HTTP_TIMEOUT = -120;
165     
166     /**
167      * Constant indicating cordova domain whitelist rejection error while sending request to server. Used as a possible value for the
168      * errorCode in {@link sap.AuthProxy~errorCallback}.
169      * @constant
170      * @type number 
171      */
172     AuthProxy.prototype.ERR_DOMAIN_WHITELIST_REJECTION = -121;
173     
174     /**
175      * Constant indicating a missing required parameter message.  Used as a possible value for the description
176      * in (@link sap.AuthProxy~errorCallback}.
177      * @constant
178      * @type string 
179      * @private
180      */
181     AuthProxy.prototype.MSG_MISSING_PARAMETER = "Missing a required parameter: ";
182     
183     /**
184      * Constant indicating invalid parameter value message.  Used as a possible value for the description
185      * in (@link sap.AuthProxy~errorCallback}.
186      * @constant
187      * @type string 
188      * @private
189      */
190     AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE = "Invalid Parameter Value for parameter: ";
191     
192     /**
193      * Create certificate source description object for a certificate from a keystore file.  The keystore file must be of type PKCS12
194      * (usually a .p12 extention) since that is the only certificate file type that can contain a private key (a private key is needed
195      * to authenticate the client to the server).  You might want to use this method if you know the desired certificate resides in a
196      * file on the filesystem.
197      * @class
198      * @param {string} Path The Path of the keystore file.<br/>For iOS clients, it first tries to load the 
199      *                 relative file path from the application's Documents folder. If it fails, it then tries
200      *                 to load the file path from application's main bundle. In addition, before trying 
201      *                 to load the certificate from the file system, the iOS client first checks whether the 
202      *                 specified certificate key already exists in the key store. If it does, it loads 
203      *                 the existing certificate from key store, instead of loading the certificate from 
204      *                 file system.<br/>
205      *                 For Android clients, the filepath is first treated as an absolute path. If the certificate
206      *                 is not found, then the filepath is treated as relative to the root of the sdcard.
207      * @param {string} Password The password of the keystore.
208      * @param {string} CertificateKey A unique key (aka: alias) that is used to locate the certificate. 
209      * @example
210      * // Create the certificate source description object.
211      * var fileCert = new sap.AuthProxy.CertificateFromFile("directory/certificateName.p12", "certificatePassword", "certificateKey");
212      * // callbacks
213      * var successCB = function(serverResponse){
214      *     alert("Status: " + JSON.stringify(serverResponse.status));
215      *     alert("Headers: " + JSON.stringify(serverResponse.headers));
216      *     alert("Response: " + JSON.stringify(serverResponse.response));
217      * }
218      * var errorCB = function(errorObject){
219      *     alert("Error making request: " + JSON.stringify(errorObject));
220      * }
221      * // Make the request with the certificate source description object.
222      * sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS IS THE BODY", successCB, errorCB, null, null, 0, fileCert);
223      * 
224      */
225     AuthProxy.prototype.CertificateFromFile = function (Path, Password, CertificateKey) {
226         this.Source = "FILE";
227         this.Path = Path;
228         this.Password = Password;
229         this.CertificateKey = CertificateKey;
230     };
231     
232     /**
233      * Create a certificate source description object for certificates from the system keystore.  You might want to use a certificate
234      * from the system keystore if you know the user's device will have the desired certificate installed on it.<br/>
235      * On Android, sending a request with a certificate from the system store results in UI being shown for the user to pick
236      * the certificate to use (the certificate with the alias matching the given CertificateKey is pre-selected).
237      * @class
238      * @param {string} CertificateKey A unique key (aka: alias) that is used to locate the certificate.
239      * @example
240      * // Create the certificate source description object.
241      * var systemCert = new sap.AuthProxy.CertificateFromStore("certificatekey");
242      * // callbacks
243      * var successCB = function(serverResponse){
244      *     alert("Status: " + JSON.stringify(serverResponse.status));
245      *     alert("Headers: " + JSON.stringify(serverResponse.headers));
246      *     alert("Response: " + JSON.stringify(serverResponse.response));
247      * }
248      * var errorCB = function(errorObject){
249      *     alert("Error making request: " + JSON.stringify(errorObject));
250      * }
251      * // Make the request with the certificate source description object.
252      * sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS IS THE BODY", successCB, errorCB, null, null, 0, systemCert);
253      */
254     AuthProxy.prototype.CertificateFromStore = function (CertificateKey) {
255         this.Source = "SYSTEM";
256         this.CertificateKey = CertificateKey;
257     };
258     
259     
260     /**
261      * Create a certificate source description object for certificates from logon manager.  Using the resulting certificate source description
262      * object on subsequent calls to AuthProxy.sendRequest or AuthProxy.get will cause AuthProxy to retrieve a certificate from Logon Manager
263      * to use for client authentication. The appID parameter is used to indicate which application's certificate to use.<br/>
264      * Note that to use a certificate from Logon Manager, the application must have already registered with the server using a certificate from Afaria.
265      * @class
266      * @param {string} appID application identifier
267      * @example
268      * // Create the certificate source description object.
269      * var logonCert = new sap.AuthProxy.CertificateFromLogonManager("applicationID");
270      * // callbacks
271      * var successCB = function(serverResponse){
272      *     alert("Status: " + JSON.stringify(serverResponse.status));
273      *     alert("Headers: " + JSON.stringify(serverResponse.headers));
274      *     alert("Response: " + JSON.stringify(serverResponse.response));
275      * }
276      * var errorCB = function(errorObject){
277      *     alert("Error making request: " + JSON.stringify(errorObject));
278      * }
279      * // Make the request with the certificate source description object.
280      * sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS IS THE BODY", successCB, errorCB, null, null, 0, logonCert);
281      */
282     AuthProxy.prototype.CertificateFromLogonManager = function (appID) {
283         this.Source = "LOGON";
284         this.AppID = appID;
285     };
286     
287     
288     /**
289      * Verifies that a certificate source description object (created with {@link sap.AuthProxy#CertificateFromFile},
290      * {@link sap.AuthProxy#CertificateFromStore}, or {@link sap.AuthProxy#CertificateFromLogonManager}) has all the required fields and that the values
291      * for those fields are the correct type.  This function verifies only the certificate description object, not the certificate itself.  So, for example,
292      * if the certificate source description object was created with {@link sap.AuthProxy#CertificateFromFile} and has a String for the filepath and a
293      * String for the key/alias, <b>this function considers it valid even if no certificate actually exists on the filesystem</b>.  If the certificate
294      * source description object is valid but the certificate itself is not, then an error occurs during the call to {@link sap.AuthProxy#get} or
295      * {@link sap.AuthProxy#sendRequest}.
296      * @param {object} certSource The certificate source object.
297      * @param {sap.AuthProxy~errorCallback} errorCB The error callback invoked if the certificate source is not valid.  Will have an object with 'errorCode'
298      * and 'description' properties.
299      * @example
300      * var notValidCert = {};
301      * var errorCallback = function(error){
302      *     alert("certificate not valid!\nError code: " + error.errorCode + "\ndescription: " + error.description);
303      * }
304      * var isCertValid = sap.AuthProxy.validateCertSource(notValidCert, errorCallback);
305      * if( isCertValid ){
306      *     // do stuff with the valid certificate source description object
307      * } else {
308      *     // at this point we know the cert is not valid, and the error callback is invoked with extra information.
309      * }
310      *
311      *
312      * Developers are not expected to call this function.
313      * @private
314      */
315     AuthProxy.prototype.validateCertSource = function (certSource, errorCB) {
316         if (!certSource) {
317             // The certificate is not present, so just ignore it.
318             return true;
319         }
320     
321         // errorCB required.
322         // First check this one. We may need it to return errors
323         if (errorCB && (typeof errorCB !== "function")) {
324             console.log("AuthProxy Error: errorCB is not a function");
325             return false;
326         }
327     
328         try {
329             // First check whether it is an object
330             if (typeof certSource !== "object") {
331                 errorCB({
332                     errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
333                     description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certSource"
334                 });
335                 return false;
336             }
337     
338             if (certSource.Source === "FILE") {
339                 if (!certSource.Path) {
340                     errorCB({
341                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
342                         description: AuthProxy.prototype.MSG_MISSING_PARAMETER + "keystore path"
343                     });
344                     return false;
345                 }
346     
347                 if (typeof certSource.Path !== "string") {
348                     errorCB({
349                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
350                         description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "keystore path"
351                     });
352                     return false;
353                 }
354     
355                 if (!certSource.CertificateKey) {
356                     errorCB({
357                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
358                         description: AuthProxy.prototype.MSG_MISSING_PARAMETER + "certificate key"
359                     });
360                     return false;
361                 }
362     
363                 if (typeof certSource.CertificateKey !== "string") {
364                     errorCB({
365                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
366                         description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certificate key"
367                     });
368                     return false;
369                 }
370             } else if (certSource.Source === "SYSTEM") {
371                 if (!certSource.CertificateKey) {
372                     errorCB({
373                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
374                         description: AuthProxy.prototype.MSG_MISSING_PARAMETER + "certificate key"
375                     });
376                     return false;
377                 }
378     
379                 if (typeof certSource.CertificateKey !== "string") {
380                     errorCB({
381                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
382                         description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certificate key"
383                     });
384                     return false;
385                 }
386             } else if (certSource.Source === "LOGON") {
387                 if (!certSource.AppID) {
388                     errorCB({
389                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
390                         description: AuthProxy.prototype.MSG_MISSING_PARAMETER + "AppID"
391                     });
392                     return false;
393                 }
394     
395                 if (typeof certSource.AppID !== "string") {
396                     errorCB({
397                         errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
398                         description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "AppID"
399                     });
400                     return false;
401                 }
402             } else {
403                 errorCB({
404                     errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
405                     description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certSource"
406                 });
407                 return false;
408             }
409     
410             return true;
411         } catch (ex) {
412             errorCB({
413                 errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
414                 description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "certSource"
415             });
416         }
417     };
418     
419     
420     /**
421      * Send an HTTP(S) request to a remote server.  This function is the centerpiece of the AuthProxy plugin.  It will handle
422      * mutual authentication if a certificate source is provided.
423      * The success callback is invoked upon any response from the server.  Even responses not generally considered to be
424      * successful (such as 404 or 500 status codes) will result in the success callback being invoked.  The error callback
425      * is reserved for problems that prevent the AuthProxy from creating the request or contacting the server.  It is therefore
426      * important to always check the status property on the object given to the success callback.
427      * @param {string} method Standard HTTP request method name.
428      * @param {string} url The HTTP URL with format http(s)://[user:password]@hostname[:port]/path.
429      * @param {Object} header HTTP header to send to the server. This is an Object. Can be null.
430      * @param {string} requestBody Data to send to the server with the request. Can be null.
431      * @param {sap.AuthProxy~successCallback} successCB Callback method invoked upon a response from the server.
432      * @param {sap.AuthProxy~errorCallback} errorCB Callback method invoked in case of failure.
433      * @param {string} [user] User ID for basic authentication.
434      * @param {string} [password] User password for basic authentication.
435      * @param {number} [timeout] Timeout setting in seconds.  Default timeout is 60 seconds.  A value of 0 means there is no timeout.
436      * @param {Object} [certSource] Certificate description object. It can be one of {@link sap.AuthProxy#CertificateFromFile},
437      * {@link sap.AuthProxy#CertificateFromStore}, or {@link sap.AuthProxy#CertificateFromLogonManager}.
438      * @return {function} A JavaScript function object to abort the operation.  Calling the abort function results in neither the success or error
439      * callback being invoked for the original request (excepting the case where the success or error callback was invoked before calling the
440      * abort function).  Note that the request itself cannot be unsent, and the server will still receive the request - the JavaScript will just
441      * not know the results of that request.
442      * @example
443      * // callbacks
444      * var successCB = function(serverResponse){
445      *     alert("Status: " + JSON.stringify(serverResponse.status));
446      *     alert("Headers: " + JSON.stringify(serverResponse.headers));
447      *     alert("Response: " + JSON.stringify(serverResponse.response));
448      * }
449      * var errorCB = function(errorObject){
450      *     alert("Error making request: " + JSON.stringify(errorObject));
451      * }
452      * // To send a post request to the server, call the method
453      * var abortFunction = sap.AuthProxy.sendRequest("POST", "http://www.google.com", null, "THIS IS THE BODY", successCB, errorCB);
454      * // An example of aborting the request
455      * abortFunction();
456      *
457      * // To send a post request to the server with headers, call the method
458      * sap.AuthProxy.sendRequest("POST", url, {HeaderName : "Header value"}, "THIS IS THE BODY", successCB, errorCB);
459      *
460      * // To send a post request to the server with basic authentication, call the method
461      * sap.AuthProxy.sendRequest("POST", url, headers, "THIS IS THE BODY", successCB, errorCB, "username", "password");
462      *
463      * // To send a post request to the server with mutual authentication, call the method
464      * sap.AuthProxy.sendRequest("POST", "https://hostname", headers, "THIS IS THE BODY", successCB, errorCB, null, 
465      *     null, 0, new sap.AuthProxy.CertificateFromLogonManager("theAppId"));
466      */
467     AuthProxy.prototype.sendRequest = function (method, url, header, requestBody, successCB, errorCB, user, password, timeout, certSource) {
468     
469         // errorCB required.
470         // First check this one. We may need it to return errors
471         if (!errorCB || (typeof errorCB !== "function")) {
472             console.log("AuthProxy Error: errorCB is not a function");
473             // if error callback is invalid, throw an exception to notify the caller
474             throw new Error("AuthProxy Error: errorCB is not a function");
475         }
476     
477         // method required
478         if (!method) {
479             console.log("AuthProxy Error: method is required");
480             errorCB({
481                 errorCode: AuthProxy.prototype.ERR_MISSING_PARAMETER,
482                 description: AuthProxy.prototype.MSG_MISSING_PARAMETER + "method"
483             });
484             return;
485         }
486     
487     
488         // We only support GET, POST, HEAD, PUT, DELETE, PATCH method
489         if (method !== "GET" && method !== "POST" && method !== "HEAD" && method !== "PUT" && method !== "DELETE" && method !== "PATCH") {
490             console.log("Invalid Parameter Value for parameter: " + method);
491             errorCB({
492                 errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
493                 description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "method"
494             });
495             return;
496         }
497     
498     
499         // url required
500         if (!url) {
501             console.log("AuthProxy Error: url is required");
502             errorCB({
503                 errorCode: AuthProxy.prototype.ERR_MISSING_PARAMETER,
504                 description: AuthProxy.prototype.MSG_MISSING_PARAMETER + "url"
505             });
506             return;
507         }
508     
509     
510         // successCB required
511         if (!successCB) {
512             console.log("AuthProxy Error: successCB is required");
513             errorCB({
514                 errorCode: AuthProxy.prototype.ERR_MISSING_PARAMETER,
515                 description: AuthProxy.prototype.MSG_MISSING_PARAMETER + "successCB"
516             });
517             return;
518         }
519     
520     
521         if (typeof successCB !== "function") {
522             console.log("AuthProxy Error: successCB is not a function");
523             errorCB({
524                 errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
525                 description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "successCB"
526             });
527             return;
528         }
529     
530     
531         if (user && typeof user !== "string") {
532             errorCB({
533                 errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
534                 description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "user"
535             });
536             return;
537         }
538     
539     
540         if (password && typeof password !== "string") {
541             errorCB({
542                 errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
543                 description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "password"
544             });
545             return;
546         }
547     
548     
549         if (timeout && typeof timeout !== "number") {
550             errorCB({
551                 errorCode: AuthProxy.prototype.ERR_INVALID_PARAMETER_VALUE,
552                 description: AuthProxy.prototype.MSG_INVALID_PARAMETER_VALUE + "timeout"
553             });
554             return;
555         }
556     
557         if (!this.validateCertSource(certSource, errorCB)) {
558             return;
559         }
560     
561     
562         try {
563             var client = new Client(method, url, header, requestBody, successCB, errorCB, user, password, timeout, certSource);
564             return client.send();
565         } catch (ex) {
566             errorCB({
567                 errorCode: AuthProxy.prototype.ERR_UNKNOWN,
568                 description: ex.message
569             });
570         }
571     
572     };
573     
574     /**
575      * Send an HTTP(S) GET request to a remote server.  This is a convenience function that simply calls {@link sap.AuthProxy#sendRequest}
576      * with "GET" as the method and null for the request body.  All given parameters are passed as-is to sap.AuthProxy.sendRequest.
577      * The success callback is invoked upon any response from the server.  Even responses not generally considered to be
578      * successful (such as 404 or 500 status codes) will result in the success callback being invoked. The error callback
579      * is reserved for problems that prevent the AuthProxy from creating the request or contacting the server.  It is, therefore,
580      * important to always check the status property on the object given to the success callback.
581      * @param {string} url The URL against which to make the request.
582      * @param {Object} header HTTP header to send to the server. This is an Object. Can be null.
583      * @param {sap.AuthProxy~successCallback} successCB Callback method invoked upon a response from the server.
584      * @param {sap.AuthProxy~errorCallback} errorCB Callback method invoked in case of failure.
585      * @param {string} [user] User ID for basic authentication.
586      * @param {string} [password] User password for basic authentication.
587      * @param {number} [timeout] Timeout setting in seconds.  Default timeout is 60 seconds.  A value of 0 means there is no timeout.
588      * @param {Object} [certSource] Certificate description object. It can be one of {@link sap.AuthProxy#CertificateFromFile},
589      * {@link sap.AuthProxy#CertificateFromStore}, or {@link sap.AuthProxy#CertificateFromLogonManager}.
590      * @return {function} A JavaScript function object to abort the operation.  Calling the abort function results in neither the success or error
591      * callback being invoked for the original request (excepting the case where the success or error callback was invoked before calling the
592      * abort functino).  Note that the request itself cannot be unsent, and the server will still receive the request - the JavaScript will just
593      * not know the results of that request.
594      * @example
595      * var successCB = function(serverResponse){
596      *     alert("Status: " + JSON.stringify(serverResponse.status));
597      *     alert("Headers: " + JSON.stringify(serverResponse.headers));
598      *     if (serverResponse.responseText){
599      *         alert("Response: " + JSON.stringify(serverResponse.responseText));
600      *     }
601      * }
602      * var errorCB = function(errorObject){
603      *     alert("Error making request: " + JSON.stringify(errorObject));
604      * }
605      * // To send a GET request to server, call the method
606      * var abortFunction = sap.AuthProxy.get("http://www.example.com", null, successCB, errorCB);
607      * // An example of aborting the request
608      * abortFunction();
609      * // To send a GET request to the server with headers, call the method
610      * sap.AuthProxy.get("http://www.example.com", {HeaderName : "Header value"}, successCB, errorCB);
611      * // To send a GET request to the server with basic authentication, call the method
612      * sap.AuthProxy.get("https://www.example.com", headers, successCB, errorCB, "username", "password");
613      * // To send a GET request to the server with mutual authentication, call the method
614      * sap.AuthProxy.get("https://www.example.com", headers, successCB, errorCB, null, null, 0, 
615      *     new sap.AuthProxy.CertificateFromLogonManager("theAppId"));
616      */
617     AuthProxy.prototype.get = function (url, header, successCB, errorCB, user, password, timeout, certSource) {
618         return this.sendRequest("GET", url, header, null, successCB, errorCB, user, password, timeout, certSource);
619     };
620     
621     /**
622      * Delete a cached certificate from the keychain. iOS clients always checks the cached certificate first to see if it is available before 
623      * loading the certificate from the file system. If the cached certificate is no longer valid, use this method to delete it from the keychain.
624      * <br/><b>Only supported on iOS platform, NOT Android.</b> 
625      * @param {sap.AuthProxy~deleteCertificateSuccessCallback} successCB Callback method upon success.
626      * @param {sap.AuthProxy~errorCallback} [errorCB] Callback method upon failure.
627      * @param {string} certificateKey The key of the certificate to be deleted.
628      * @example
629      * var successCB = function(){
630      *     alert("certificate successfully deleted.");
631      * }
632      * var errorCB = function(error){
633      *     alert("error deleting certificate: " + JSON.stringify(error));
634      * }
635      * sap.AuthProxy.deleteCertificateFromStore(successCB, errorCB, "certificateKeyToDelete");
636      */
637     AuthProxy.prototype.deleteCertificateFromStore = function (successCB, errorCB, certificateKey) {
638         cordova.exec(successCB, errorCB, "AuthProxy", "deleteCertificateFromStore", [certificateKey]);
639     };
640     
641     /**
642      * @private
643      */
644     var Client = function (method, url, header, requestBody, successCB, errorCB, user, password, timeout, certSource) {
645     
646         //ios plugin parameter does not support object type, convert Header and CertSource to JSON string
647         if (device.platform === "iOS" || (device.platform && device.platform.indexOf("iP") === 0)) {
648             if (header) {
649                 header = JSON.stringify(header);
650             }
651             if (certSource) {
652                 certSource = JSON.stringify(certSource);
653             }
654         }
655     
656         this.Method = method;
657         this.Url = url;
658         this.Header = header;
659         this.RequestBody = requestBody;
660         this.SuccessCB = successCB;
661         this.ErrorCB = errorCB;
662         this.User = user;
663         this.Password = password;
664         this.Timeout = timeout;
665         this.CertSource = certSource;
666         this.IsAbort = false;
667     
668         this.abort = function () {
669             this.IsAbort = true;
670         };
671     
672     
673         this.send = function () {
674     
675             var args = [this.Method, this.Url, this.Header, this.RequestBody, this.User, this.Password, this.Timeout, this.CertSource];
676     
677             var me = this;
678     
679             var successCallBack = function (data) {
680                 if (me.IsAbort === true) {
681                     return;
682                 }
683     
684                 successCB(data);
685             };
686     
687             var errorCallBack = function (data) {
688                 if (me.IsAbort === true) {
689                     return;
690                 }
691     
692                 errorCB(data);
693             };
694     
695             exec(successCallBack, errorCallBack, "AuthProxy", "sendRequest", args);
696     
697             return this.abort;
698         };
699     };
700     
701     /**
702      * Generates an OData client that uses the AuthProxy plugin to make requests.  This is useful if you are using Datajs, but want
703      * to make use of the certificate features of AuthProxy.  Datajs is a javascript library useful for accessing OData services.
704      * Datajs has a concept of an HttpClient, which does the work of making the request.  This function generates an HttpClient that
705      * you can specify to Datajs so you can provide client certificates for requests.  If you want to use the generated HTTP client
706      * for all future Datajs requests, you can do that by setting the OData.defaultHttpClient property to the return value of this
707      * function.  Once that is done, then doing OData stuff with Datajs is almost exactly the same, but you can add a
708      * certificateSource to a request.
709      * @example
710      * OData.defaultHttpClient = sap.AuthProxy.generateODataHttpClient();
711      *
712      * // Using a certificate from file, for example.
713      * fileCert = new sap.AuthProxy.CertificateFromFile("mnt/sdcard/cert.p12", "password", "certKey");
714      *
715      * // This is the same request object you would have created if you were just using Datajs, but now
716      * // you can add the extra 'certificateSource' property.
717      * var createRequest = {
718      *     requestUri: "http://www.example.com/stuff/etc/example.svc",
719      *     certificateSource : fileCert,
720      *     user : "username",
721      *     password : "password",
722      *     method : "POST",
723      *     data:
724      *     {
725      *          Description: "Created Record",
726      *          CategoryName: "Created Category"
727      *     }
728      * }
729      *
730      * // Use Datajs to send the request.
731      * OData.request( createRequest, successCallback, failureCallback );
732      * 
733      */
734     AuthProxy.prototype.generateODataHttpClient = function () {
735        var httpClient = {
736             request: function (request, success, error) {
737                 var url, requestHeaders, requestBody, statusCode, statusText, responseHeaders;
738                 var responseBody, requestTimeout, requestUserName, requestPassword, requestCertificate;
739                 var client, result;
740     
741                 url = request.requestUri;
742                 requestHeaders = request.headers;
743                 requestBody = request.body;
744     
745                 var successCB = function (data) {
746                     var response = {
747                         requestUri: url,
748                         statusCode: data.status,
749                         statusText: data.status,
750                         headers: data.headers,
751                         body: (data.responseText ? data.responseText : data.responseBase64)
752                     };
753     
754                     if (response.statusCode >= 200 && response.statusCode <= 299) {
755                         if (success) {
756                             success(response);
757                         }
758                     } else {
759                         if (error) {
760                             error({
761                                 message: "HTTP request failed",
762                                 request: request,
763                                 response: response
764                             });
765                         }
766                     }
767                 };
768     
769                 var errorCB = function (data) {
770                     if (error) {
771                         error({
772                             message: data
773                         });
774                     }
775                 };
776     
777                 if (request.timeoutMS) {
778                     requestTimeout = request.timeoutMS / 1000;
779                 }
780     
781                 if (request.certificateSource) {
782                     requestCertificate = request.certificateSource;
783                 }
784     
785                 if (request.user) {
786                     requestUserName = request.user;
787                 }
788     
789                 if (request.password) {
790                     requestPassword = request.password;
791                 }
792     
793                 client = AuthProxy.prototype.sendRequest(request.method || "GET", url, requestHeaders, requestBody, successCB, errorCB, requestUserName, requestPassword, requestTimeout, requestCertificate);
794     
795                 result = {};
796                 result.abort = function () {
797                     client.abort();
798     
799                     if (error) {
800                         error({
801                             message: "Request aborted"
802                         });
803                     }
804                 };
805                 return result;
806             }
807         };
808         return httpClient;
809     };
810     
811     var AuthProxyPlugin = new AuthProxy();
812     
813     module.exports = AuthProxyPlugin;
814     
815     
816     /**
817      * Callback function that is invoked in case of an error.
818      *
819      * @callback sap.AuthProxy~errorCallback
820      *
821      * @param {Object} errorObject An object containing two properties: 'errorCode' and 'description.'
822      * The 'errorCode' property corresponds to one of the {@link sap.AuthProxy} constants.  The 'description'
823      * property is a string with more detailed information of what went wrong.
824      *
825      * @example
826      * function errorCallback(errCode) {
827      *    //Set the default error message. Used if an invalid code is passed to the
828      *    //function (just in case) but also to cover the
829      *    //sap.AuthProxy.ERR_UNKNOWN case as well.
830      *    var msg = "Unkown Error";
831      *    switch (errCode) {
832      *       case sap.AuthProxy.ERR_INVALID_PARAMETER_VALUE:
833      *          msg = "Invalid parameter passed to method";
834      *          break;
835      *       case sap.AuthProxy.ERR_MISSING_PARAMETER:
836      *          msg = "A required parameter was missing";
837      *          break;
838      *       case sap.AuthProxy.ERR_HTTP_TIMEOUT:
839      *          msg = "The request timed out";
840      *          break;
841      *    };
842      *    //Write the error to the log
843      *    console.error(msg);
844      *    //Let the user know what happened
845      *    navigator.notification.alert(msg, null, "AuthProxy Error", "OK");
846      * };
847      */
848     
849     /**
850      * Callback function that is invoked upon a response from the server.
851      *
852      * @callback sap.AuthProxy~successCallback
853      *
854      * @param {Object} serverResponse An object containing the response from the server.  Contains a 'headers' property,
855      * a 'status' property, and a 'responseText' property.<br/>
856      * 'headers' is an object containing all the headers in the response.<br/>
857      * 'status' is an integer corresponding to the HTTP status code of the response.  It is important to check the status of
858      * the response, since <b>this success callback is invoked upon any response from the server</b> - including responses that are
859      * not normally thought of as successes (for example, the status code could be 404 or 500).<br/>
860      * 'responseText' is a string containing the body of the response.
861      */
862     
863     /**
864      * Callback function that is invoked upon successfully deleting a certificate from the store.
865      *
866      * @callback sap.AuthProxy~deleteCertificateSuccessCallback
867      */