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 */