authproxy.js

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