1 var utils = sap.logon.Utils;
2 var TIMEOUT = 2000;
3
4 var _oLogonCore;
5 var _oLogonView;
6 var _hasLogonSuccessEventFired = false;
7
8 var _providedContext;
9 var flowqueue;
10
11 var init = function (successCallback, errorCallback, applicationId, context, customView) {
12
13 document.addEventListener("resume",
14 function(){
15 resume(
16 function() { fireEvent('onSapResumeSuccess', arguments);},
17 function() { fireEvent('onSapResumeError', arguments);}
18 );
19 },
20 false);
21
22 // The success callback used for the call to _oLogonCore.initLogon(...)
23 var initSuccess = function(){
24 utils.log('LogonController: LogonCore successfully initialized.');
25
26 // Now that Logon is initialized, registerOrUnlock is automatically called.
27 registerOrUnlock( successCallback, errorCallback );
28 }
29
30 var initError = function(error){
31 // If a parameter describing the error is given, pass it along.
32 // Otherwise, construct something to call the error callback with.
33 if( error ) {
34 errorCallback( error );
35 } else {
36 errorCallback( utils.Error('ERR_INIT_FAILED') );
37 }
38 }
39
40
41 utils.log('LogonController.init enter');
42 utils.log(applicationId);
43 module.exports.applicationId = applicationId;
44
45 // Make note of the context given (if any)
46 if( context ){
47 _providedContext = context;
48 }
49
50 _oLogonView = customView;
51 if (!_oLogonView) {
52 _oLogonView = sap.logon.IabUi;
53 }
54
55 flowqueue = new FlowRunnerQueue();
56
57 //coLogonCore.cordova.require("com.sap.mp.cordova.plugins.logon.LogonCore");
58 _oLogonCore = sap.logon.Core;
59 _oLogonCore.initLogon(initSuccess, initError, applicationId);
60
61 //update exports definition
62 module.exports.core = _oLogonCore;
63 }
64
65 var fireEvent = function (eventId, args) {
66 if (typeof eventId === 'string') {
67 //var event = document.createEvent('Events');
68 //event.initEvent(eventId, false, false);
69
70 if (!window.CustomEvent) {
71 window.CustomEvent = function(type, eventInitDict) {
72 var newEvent = document.createEvent('CustomEvent');
73 newEvent.initCustomEvent(
74 type,
75 !!(eventInitDict && eventInitDict.bubbles),
76 !!(eventInitDict && eventInitDict.cancelable),
77 (eventInitDict ? eventInitDict.detail : null));
78 return newEvent;
79 };
80 }
81
82 var event = new CustomEvent(eventId, { 'detail':{ 'id': eventId, 'args': args }});
83
84 setTimeout(function() {
85 document.dispatchEvent(event);
86 }, 0);
87 } else {
88 throw 'Invalid eventId: ' + JSON.stringify(event);
89 }
90 }
91
92 var FlowRunner = function(callbacks, pLogonView, pLogonCore, flowClass) {
93
94 var onFlowSuccess;
95 var onFlowError;
96 var onFlowCancel;
97
98 var logonView;
99 var logonCore;
100 var flow;
101
102 var onsuccess = callbacks.onsuccess;
103 var onerror = callbacks.onerror;
104
105
106
107 logonView = pLogonView;
108 logonCore = pLogonCore;
109
110 onFlowSuccess = function onFlowSuccess() {
111 utils.logJSON('onFlowSuccess');
112 logonView.close();
113 onsuccess.apply(this, arguments);
114 }
115
116 onFlowError = function onFlowError() {
117 utils.logJSON('onFlowError');
118 logonView.close();
119 onerror.apply(this, arguments);
120 }
121
122 onFlowCancel = function onFlowCancel(){
123 utils.logJSON('onFlowCancel');
124 //logonView.close();
125 onFlowError(new utils.Error('ERR_USER_CANCELLED'));
126 }
127
128 var handleCoreStateOnly = function(currentState){
129 handleCoreResult(null, currentState);
130 }
131
132 var handleCoreResult = function (currentContext, currentState) {
133 if (typeof currentContext === undefined) currentContext = null;
134
135 //workaround for defaultPasscodeAllowed
136 if (currentState) {
137 if (currentContext && currentContext.policyContext && currentContext.policyContext.defaultAllowed){
138 currentState.defaultPasscodeAllowed = true;
139 }
140 else {
141 currentState.defaultPasscodeAllowed = false;
142 }
143 }
144
145 utils.logJSON(currentContext, 'handleCoreResult currentContext');
146 utils.logJSON(currentState, 'handleCoreResult currentState');
147
148
149 utils.logJSON(flow.name);
150 var matchFound = false;
151 var rules = flow.stateTransitions;
152
153
154 ruleMatching:
155 for (key in rules){
156
157 var rule = flow.stateTransitions[key];
158 //utils.logJSON(rule, 'rule');
159
160 //utils.logJSON(rule.condition, 'rule.condition');
161 if (typeof rule.condition === 'undefined') {
162 throw 'undefined condition in state transition rule';
163 }
164
165
166 if (rule.condition.state === null) {
167 if (currentState)
168 {
169 continue ruleMatching; // non-null state (and rule) mismatch
170 }
171 //else {
172 // // match:
173 // // rule.condition.state === null &&
174 // // (typeof currentState === 'undefined') // null or undefined
175 //}
176 }
177 else if (rule.condition.state !== 'undefined' && currentState){
178 utils.log('stateMatching');
179
180 stateMatching:
181 for (field in rule.condition.state) {
182 utils.log(field);
183 if (rule.condition.state[field] === currentState[field])
184 {
185 utils.log('field matching ' + field);
186 continue stateMatching; // state field match
187 }
188 else {
189 utils.log('field mismatching ' + field);
190 continue ruleMatching; // state field (and rule) mismatch
191 };
192 }
193 }
194
195 if (rule.condition.context === null) {
196 if (currentContext)
197 {
198 continue ruleMatching; // non-null context (and rule) mismatch
199 }
200 //else {
201 // // match:
202 // // rule.condition.context === null &&
203 // // (typeof currentContext === 'undefined') // null or undefined
204 //}
205 }
206 else if (rule.condition.context !== 'undefined' && currentContext){
207
208 utils.log('contextMatching');
209 contextMatching:
210 for (field in rule.condition.context) {
211 utils.log(field);
212 if (rule.condition.context[field] === currentContext[field])
213 {
214 utils.log('field matching ' + field);
215 continue contextMatching; // context field match
216 }
217 else {
218 utils.log('field mismatching ' + field);
219 continue ruleMatching; // context field (and rule) mismatch
220 };
221 }
222 }
223 utils.log('match found');
224 utils.logJSON(rule, 'rule');
225
226 if (typeof rule.action === 'function') {
227 rule.action(currentContext);
228 }
229 else if (typeof rule.action === 'string') {
230 // the action is a screenId
231 var screenId = rule.action;
232 utils.log('handleCoreResult: ' + screenId);
233 utils.logKeys(flow.screenEvents[screenId]);
234 if(!currentContext){
235 currentContext = {};
236 }
237
238 if( !currentContext.registrationContext && _providedContext ){
239 // The current registrationContext is null, and we have been given a context when initialized,
240 // so use the one we were given.
241 currentContext.registrationContext = _providedContext;
242 } else if (currentContext.registrationContext && _providedContext && !currentContext.registrationReadOnly && !(currentState.stateAfaria=='initializationSuccessful')){
243 for (key in _providedContext) {
244 //if (!currentContext.registrationContext[key]){
245 currentContext.registrationContext[key] = _providedContext[key];
246 //}
247 }
248 }
249
250 logonView.showScreen(screenId, flow.screenEvents[screenId], currentContext);
251
252 }
253 else {
254 onFlowError(new utils.Error('ERR_INVALID_ACTION'));
255 }
256
257 matchFound = true;
258 break ruleMatching;
259 }
260
261 if (!matchFound) {
262 onFlowError(new utils.Error('ERR_INVALID_STATE'));
263 }
264 }
265
266 flow = new flowClass(logonCore, logonView, handleCoreResult, onFlowSuccess, onFlowError, onFlowCancel);
267
268 this.run = function() {
269 utils.log('FlowRunner.run ' + flowClass.name);
270 utils.logKeys(flow , 'new flow ');
271 logonCore.getState(handleCoreStateOnly, onFlowError);
272 }
273 }
274
275 var FlowRunnerQueue = function() {
276 var isRunning = false;
277 var innerQueue = [];
278
279 this.add = function(flowRunner) {
280 innerQueue.push(flowRunner);
281 if (isRunning == false) {
282 isRunning = true;
283 process();
284 }
285 }
286
287 this.runNextFlow = function() {
288 innerQueue.shift();
289 if (innerQueue.length == 0) {
290 isRunning = false;
291 }
292 else {
293 process();
294 }
295 }
296
297 var process = function() {
298 if (innerQueue.length > 0) {
299 var flowRunner = innerQueue[0];
300 flowRunner.run();
301 }
302 else {
303 isRunning = false;
304 }
305 }
306 }
307
308
309 var MockFlow = function MockFlow(logonCore, logonView, onCoreResult, onFlowSuccess, onFlowError, onFlowCancel) {
310 //wrapped into a function to defer evaluation of the references to flow callbacks
311 //var flow = {};
312
313 this.name = 'mockFlowBuilder';
314
315 this.stateTransitions = [
316 {
317 condition: {
318 state: {
319 secureStoreOpen: false,
320 }
321 },
322 action: 'SCR_MOCKSCREEN'
323 },
324 {
325 condition: {
326 state: {
327 secureStoreOpen: true,
328 }
329 },
330 action: 'SCR_MOCKSCREEN'
331 },
332
333 ];
334
335 this.screenEvents = {
336 'SCR_TURN_PASSCODE_ON': {
337 onsubmit: onFlowSuccess,
338 oncancel: onFlowCancel,
339 onerror: onFlowError,
340 }
341 };
342
343 utils.log('flow constructor return');
344 //return flow;
345 }
346
347 var RegistrationFlow = function RegistrationFlow(logonCore, logonView, onCoreResult, onFlowSuccess, onFlowError, onFlowCancel) {
348 //wrapped into a function to defer evaluation of the references to flow callbacks
349
350 this.name = 'registrationFlowBuilder';
351
352 var registrationInProgress = false;
353
354 var onCancelSSOPin = function() {
355 onFlowError(errorWithDomainCodeDescription("MAFLogon","0","SSO Passcode set screen was cancelled"));
356 }
357
358 var onCancelRegistration = function() {
359 onFlowError(errorWithDomainCodeDescription("MAFLogon","1","Registration screen was cancelled"));
360 }
361
362 // internal methods
363 var showScreen = function(screenId) {
364 return function(coreContext) {
365 logonView.showScreen(screenId, this.screenEvents[screenId], coreContext);
366 }.bind(this);
367 }.bind(this);
368
369 var onUnlockSubmit = function(context){
370 utils.logJSON(context, 'logonCore.unlockSecureStore');
371 logonCore.unlockSecureStore(onCoreResult, onUnlockError, context)
372 }
373
374 var onUnlockError = function(error) {
375 utils.logJSON("onUnlockError: " + JSON.stringify(error));
376
377 // TODO switch case according to the error codes
378 if (error
379 && error.errorDomain && error.errorDomain === "MAFSecureStoreManagerErrorDomain"
380 && error.errorCode && error.errorCode === "16") {
381 // Too many attempts --> DV deleted
382 logonView.showNotification("ERR_TOO_MANY_ATTEMPTS_APP_PASSCODE")
383 }
384 else {
385 logonView.showNotification("ERR_UNLOCK_FAILED");
386 }
387
388 }
389
390 var onSetAfariaCredentialError = function(error) {
391 utils.logJSON("onSetAfariaCredentialError: " + JSON.stringify(error));
392
393 logonView.showNotification("ERR_SET_AFARIA_CREDENTIAL_FAILED");
394 }
395
396 var noOp = function() { }
397
398 var onErrorAck = function(ack) {
399 if (ack.key === 'ERR_TOO_MANY_ATTEMPTS_APP_PASSCODE') {
400 onFlowError(new utils.Error('ERR_TOO_MANY_ATTEMPTS_APP_PASSCODE'));
401 }
402 }
403
404
405 var onRegistrationBackButton = function() {
406 if (registrationInProgress == true) {
407 utils.log('back button pushed, no operation is required as registration is running');
408 }
409 else {
410 onCancelRegistration();
411 }
412 }
413
414 var onUnlockVaultWithDefaultPasscode = function(){
415 utils.log('logonCore.unlockSecureStore - default passcode');
416 var unlockContext = {"unlockPasscode":null};
417 logonCore.unlockSecureStore(onCoreResult, onFlowError, unlockContext)
418 }
419
420 var onRegSucceeded = function(context, state) {
421 onCoreResult(context, state);
422 registrationInProgress = false;
423 }
424
425 var onRegError = function(error){
426 utils.logJSON(error, 'registration failed');
427 logonView.showNotification(getRegistrationErrorText(error));
428 registrationInProgress = false;
429 }
430
431 var onRegSubmit = function(context){
432 utils.logJSON(context, 'logonCore.startRegistration');
433 registrationInProgress = true;
434 logonCore.startRegistration(onRegSucceeded, onRegError, context)
435 }
436
437 var onCreatePasscodeSubmit = function(context){
438 utils.logJSON(context, 'logonCore.persistRegistration');
439 logonCore.persistRegistration(onCoreResult, onCreatePasscodeError, context);
440 }
441
442 var onCancelRegistrationError = function(error){
443 utils.logJSON("onCancelRegistrationError: " + JSON.stringify(error));
444 logonView.showNotification(getRegistrationCancelError(error));
445 }
446
447 var onCreatePasscodeError = function(error) {
448 utils.logJSON("onCreatePasscodeError: " + JSON.stringify(error));
449 logonView.showNotification(getSecureStoreErrorText(error));
450 }
451
452 var onSSOPasscodeSetError = function(error) {
453 utils.logJSON("onSSOPasscodeSetError: " + JSON.stringify(error));
454 logonView.showNotification(getSSOPasscodeSetErrorText(error));
455 }
456
457 var callGetContext = function(){
458 utils.log('logonCore.getContext');
459 logonCore.getContext(onCoreResult, onFlowError);
460 }
461
462 var onFullRegistered = function()
463 {
464 var getContextSuccessCallback = function(result){
465
466 if(!_hasLogonSuccessEventFired) {
467 fireEvent("onSapLogonSuccess", arguments);
468 _hasLogonSuccessEventFired = true;
469 }
470
471 onFlowSuccess(result);
472 }
473 utils.log('logonCore.getContext');
474 logonCore.getContext(getContextSuccessCallback, onFlowError);
475 }
476
477 var onForgotAppPasscode = function(){
478 utils.log('logonCore.deleteRegistration');
479 logonCore.deleteRegistration(onFlowError, onFlowError);
480 }
481
482 var onForgotSsoPin = function(){
483 utils.log('forgotSSOPin');
484 logonView.showNotification("ERR_FORGOT_SSO_PIN");
485 }
486
487 var onSkipSsoPin = function(){
488 utils.logJSON('logonCore.skipClientHub');
489 logonCore.skipClientHub(onCoreResult, onFlowError);
490 }
491
492 var callPersistWithDefaultPasscode = function(context){
493 utils.logJSON(context, 'logonCore.persistRegistration');
494 context.passcode = null;
495 logonCore.persistRegistration(
496 onCoreResult,
497 onFlowError,
498 context)
499 }
500
501 // exported properties
502 this.stateTransitions = [
503 {
504 condition: {
505 state: {
506 secureStoreOpen: false,
507 status: 'fullRegistered',
508 defaultPasscodeUsed: true
509 }
510 },
511 action: onUnlockVaultWithDefaultPasscode
512 },
513
514 {
515 condition: {
516 state: {
517 secureStoreOpen: false,
518 status: 'fullRegistered'
519 }
520 },
521 action: 'SCR_UNLOCK'
522 },
523
524
525 {
526 condition: {
527 state: {
528 //secureStoreOpen: false, //TODO clarify
529 status: 'fullRegistered',
530 stateClientHub: 'availableNoSSOPin'
531 }
532 },
533 action: 'SCR_SSOPIN_SET'
534 },
535 {
536 condition: {
537 state: {
538 status: 'new'
539 },
540 context: null
541 },
542 action: callGetContext
543 },
544
545 {
546 condition: {
547 state: {
548 status: 'new',
549 stateClientHub: 'availableNoSSOPin'
550 }
551 },
552 action: 'SCR_SSOPIN_SET'
553 },
554
555 {
556 condition: {
557 state: {
558 status: 'new',
559 stateClientHub: 'availableInvalidSSOPin'
560 }
561 },
562 action: 'SCR_SSOPIN_SET'
563 },
564 {
565 condition: {
566 state: {
567 status: 'new',
568 stateClientHub: 'availableValidSSOPin',
569 stateAfaria: 'credentialNeeded'
570 },
571 context : {
572 afariaRegistration: 'certificate'
573 }
574 },
575 action: 'SCR_ENTER_AFARIA_CREDENTIAL'
576 },
577 {
578 condition: {
579 state: {
580 status: 'new',
581 stateClientHub: 'notAvailable',
582 stateAfaria: 'credentialNeeded'
583 }
584 },
585 action: 'SCR_ENTER_AFARIA_CREDENTIAL'
586 },
587 {
588 condition: {
589 state: {
590 status: 'new',
591 isAfariaCredentialsProvided: false
592 },
593 context : {
594 afariaRegistration: 'certificate'
595 }
596 },
597 action: 'SCR_ENTER_AFARIA_CREDENTIAL'
598 },
599 {
600 condition: {
601 state: {
602 status: 'new',
603 stateClientHub: 'availableValidSSOPin'
604 },
605 context : {
606 credentialsByClientHub : true,
607 registrationReadOnly : true
608 }
609 },
610 action: function(context){
611 utils.logJSON(context, 'logonCore.startRegistration');
612 logonCore.startRegistration(onCoreResult, onRegError, context.registrationContext);
613 }
614 },
615 {
616 condition: {
617 state: {
618 status: 'new',
619 stateClientHub: 'availableValidSSOPin',
620 isAfariaCredentialsProvided: true
621 },
622 context : {
623 afariaRegistration: 'certificate',
624 registrationReadOnly : true
625 }
626 },
627 action: function(context){
628 utils.logJSON(context, 'logonCore.startRegistration');
629 logonCore.startRegistration(onCoreResult, onRegError, context.registrationContext);
630 }
631 },
632 {
633 condition: {
634 state: {
635 status: 'new',
636 stateClientHub: 'availableValidSSOPin',
637 stateAfaria: 'initializationSuccessful'
638 },
639 context : {
640 registrationReadOnly : true,
641 afariaRegistration: 'certificate'
642 }
643 },
644 action: function(context){
645 utils.logJSON(context, 'logonCore.startRegistration');
646 logonCore.startRegistration(onCoreResult, onRegError, context.registrationContext);
647 }
648 },
649 {
650 condition: {
651 state: {
652 status: 'new',
653 stateClientHub: 'notAvailable',
654 stateAfaria: 'initializationSuccessful'
655 },
656 context : {
657 afariaRegistration: 'certificate'
658 }
659 },
660 action: function(context){
661 utils.logJSON(context, 'logonCore.startRegistration');
662 logonCore.startRegistration(onCoreResult, onRegError, context.registrationContext);
663 }
664 },
665 {
666 condition: {
667 state: {
668 status: 'new',
669 stateAfaria: 'initializationSuccessful'
670 }
671 },
672 action: 'SCR_ENTER_CREDENTIALS'
673 },
674 {
675 condition: {
676 state: {
677 status: 'new',
678 stateClientHub: 'availableValidSSOPin'
679 },
680 context : {
681 registrationReadOnly :true,
682 credentialsByClientHub : false
683 }
684 },
685 action: 'SCR_ENTER_CREDENTIALS'
686 },
687
688
689 {
690 condition: {
691 state: {
692 status: 'new',
693 //stateClientHub: 'notAvailable' | 'availableValidSSOPin' | 'skipped' | 'error'
694 stateAfaria: 'initializationFailed'
695 }
696 },
697 action: 'SCR_REGISTRATION'
698 },
699
700 {
701 condition: {
702 state: {
703 status: 'new',
704 //stateClientHub: 'notAvailable' | 'availableValidSSOPin' | 'skipped' | 'error'
705 }
706 },
707 action: 'SCR_REGISTRATION'
708 },
709
710 {
711 condition: {
712 state: {
713 status: 'new',
714 //stateClientHub: 'notAvailable' | 'availableValidSSOPin' | 'skipped' | 'error'
715 stateAfaria: 'initializationFailed'
716 }
717 },
718 action: 'SCR_REGISTRATION'
719 },
720
721 {
722 condition: {
723 state: {
724 secureStoreOpen: false,
725 status: 'registered',
726 defaultPasscodeUsed: true,
727 // defaultPasscodeAllowed: true,
728 }
729 },
730 action: 'SCR_SET_PASSCODE_OPT_OFF'
731 },
732 {
733 condition: {
734 state: {
735 secureStoreOpen: false,
736 status: 'registered',
737 defaultPasscodeUsed: false,
738 defaultPasscodeAllowed: true,
739 }
740 },
741 action: 'SCR_SET_PASSCODE_OPT_ON'
742 },
743 {
744 condition: {
745 state: {
746 secureStoreOpen: false,
747 status: 'registered',
748 // defaultPasscodeAllowed: false,
749 }
750 },
751 action: 'SCR_SET_PASSCODE_MANDATORY'
752 },
753
754
755 {
756 condition: {
757 state: {
758 //secureStoreOpen: false, //TODO clarify
759 status: 'fullRegistered',
760 stateClientHub: 'availableInvalidSSOPin'
761 }
762 },
763 action: 'SCR_SSOPIN_CHANGE'
764 },
765 {
766 condition: {
767 state: {
768 secureStoreOpen: true,
769 status: 'fullRegistered',
770 stateClientHub: 'notAvailable'
771 }
772 },
773 action: onFullRegistered
774 },
775 {
776 condition: {
777 state: {
778 secureStoreOpen: true,
779 status: 'fullRegistered',
780 stateClientHub: 'availableValidSSOPin'
781 }
782 },
783 action: onFullRegistered
784 },
785 {
786 condition: {
787 state: {
788 secureStoreOpen: true,
789 status: 'fullRegistered',
790 stateClientHub: 'skipped'
791 }
792 },
793 action: onFullRegistered
794 },
795
796
797
798 ];
799
800 this.screenEvents = {
801 'SCR_SSOPIN_SET': {
802 onsubmit: function(context){
803 utils.logJSON(context, 'logonCore.setSSOPasscode');
804 logonCore.setSSOPasscode(onCoreResult, onSSOPasscodeSetError, context);
805 },
806 oncancel: onCancelSSOPin,
807 onerror: onFlowError,
808 onforgot: onForgotSsoPin,
809 onskip: onSkipSsoPin
810 },
811
812 'SCR_ENTER_AFARIA_CREDENTIAL' : {
813 onsubmit: function(context){
814 utils.logJSON(context, 'logonCore.setAfariaCredential');
815 logonCore.setAfariaCredential(onCoreResult, onSetAfariaCredentialError, context);
816 }
817 },
818
819 'SCR_SSOPIN_CHANGE': {
820 onsubmit: function(context){
821 utils.logJSON(context, 'logonCore.setSSOPasscode');
822 logonCore.setSSOPasscode(onCoreResult, onSSOPasscodeSetError, context);
823 },
824 oncancel: onSkipSsoPin,
825 onerror: onFlowError,
826 onforgot: onForgotSsoPin
827 },
828
829 'SCR_UNLOCK': {
830 onsubmit: onUnlockSubmit,
831 oncancel: noOp,
832 onerror: onFlowError,
833 onforgot: onForgotAppPasscode,
834 onerrorack: onErrorAck
835 },
836
837 'SCR_REGISTRATION': {
838 onsubmit: onRegSubmit,
839 oncancel: onCancelRegistration,
840 onerror: onFlowError,
841 onbackbutton: onRegistrationBackButton
842 },
843
844 'SCR_ENTER_CREDENTIALS' : {
845 onsubmit: onRegSubmit,
846 oncancel: onCancelRegistration,
847 onerror: onFlowError
848 },
849 'SCR_SET_PASSCODE_OPT_ON': {
850 onsubmit: onCreatePasscodeSubmit,
851 oncancel: noOp,
852 onerror: onFlowError,
853 ondisable: showScreen('SCR_SET_PASSCODE_OPT_OFF'),
854 onerrorack: noOp
855 },
856 'SCR_SET_PASSCODE_OPT_OFF': {
857 onsubmit: callPersistWithDefaultPasscode,
858 oncancel: noOp,
859 onerror: onFlowError,
860 onenable: showScreen('SCR_SET_PASSCODE_OPT_ON'),
861 onerrorack: noOp
862 },
863 'SCR_SET_PASSCODE_MANDATORY': {
864 onsubmit: onCreatePasscodeSubmit,
865 oncancel: noOp,
866 onerror: onFlowError,
867 onerrorack: noOp
868 },
869
870
871
872 };
873
874
875 utils.log('flow constructor return');
876 }
877
878
879
880 var ChangePasswordFlow = function ChangePasswordFlow(logonCore, logonView, onCoreResult, onFlowSuccess, onFlowError, onFlowCancel) {
881 //wrapped into a function to defer evaluation of the references to flow callbacks
882
883 this.name = 'changePasswordFlowBuilder';
884
885
886 // internal methods
887
888 var callUnlockFlow = function(){
889 utils.log(this.name + ' triggered unlock');
890 registerOrUnlock(onCoreResult,onFlowError);
891 }
892
893 var onChangePasswordSubmit = function(context){
894 utils.logJSON(context, 'logonCore.changePassword');
895 // this logonCore call does not return with context
896 logonCore.changePassword(onPasswordChanged, onFlowError, context);
897 }
898
899
900 var onPasswordChanged = function(){
901 utils.log('onPasswordChanged');
902 logonCore.getContext(onFlowSuccess, onFlowError);
903 }
904
905 // exported properties
906 this.stateTransitions = [
907 {
908 condition: {
909 state: {
910 secureStoreOpen: false,
911 }
912 },
913 action: callUnlockFlow,
914 },
915 {
916 condition: {
917 state: {
918 secureStoreOpen: true,
919 }
920 },
921 action: 'SCR_CHANGE_PASSWORD'
922 },
923
924 ];
925
926 this.screenEvents = {
927 'SCR_CHANGE_PASSWORD': {
928 onsubmit: onChangePasswordSubmit,
929 oncancel: onFlowCancel,
930 onerror: onFlowError
931 }
932 };
933
934
935 utils.log('flow constructor return');
936 }
937
938 var ManagePasscodeFlow = function ManagePasscodeFlow(logonCore, logonView, onCoreResult, onFlowSuccess, onFlowError, onFlowCancel) {
939 //wrapped into a function to defer evaluation of the references to flow callbacks
940
941 this.name = 'managePasscodeFlowBuilder';
942
943 // internal methods
944 var showScreen = function(screenId) {
945 return function(coreContext) {
946 logonView.showScreen(screenId, this.screenEvents[screenId], coreContext);
947 }.bind(this);
948 }.bind(this);
949
950
951 var callSetPasscode = function(context){
952 utils.logJSON(context, 'logonCore.changePasscode');
953 logonCore.changePasscode(
954 onCoreResult,
955 onChangePasscodeError,
956 context)
957 }
958
959 var callChangePasscode = function(context){
960 utils.logJSON(context, 'logonCore.changePasscode');
961 context.passcode = context.newPasscode;
962 logonCore.changePasscode(
963 onCoreResult,
964 onChangePasscodeError,
965 context)
966 }
967
968 var onChangePasscodeError = function(error) {
969 utils.logJSON("onChangePasscodeError: " + JSON.stringify(error));
970 logonView.showNotification(getSecureStoreErrorText(error));
971 }
972
973 var noOp = function() { }
974
975 var callDisablePasscode = function(context){
976 utils.logJSON(context, 'logonCore.disablePasscode');
977 context.passcode = null;
978 logonCore.changePasscode(
979 onCoreResult,
980 onFlowError,
981 context)
982 }
983
984 var callGetContext = function(){
985 utils.log('logonCore.getContext');
986 logonCore.getContext(onCoreResult, onFlowError);
987 }
988
989 var onPasscodeEnable = function(context){
990 utils.logJSON(context, this.name + ' onPasscodeEnable: ');
991 //logonCore.changePasscode(onFlowSuccess, onFlowError, context);
992 onFlowError();
993 }
994
995 // exported properties
996 this.stateTransitions = [
997 {
998 condition: {
999 state: {
1000 secureStoreOpen: true,
1001 },
1002 context: null
1003 },
1004 action: callGetContext
1005 },
1006 {
1007 condition: {
1008 state: {
1009 secureStoreOpen: false,
1010 }
1011 },
1012 action: onFlowError
1013 },
1014 {
1015 condition: {
1016 state: {
1017 secureStoreOpen: true,
1018 defaultPasscodeUsed: true,
1019 // defaultPasscodeAllowed: true,
1020 }
1021 },
1022 action: 'SCR_MANAGE_PASSCODE_OPT_OFF'
1023 },
1024 {
1025 condition: {
1026 state: {
1027 secureStoreOpen: true,
1028 defaultPasscodeUsed: false,
1029 defaultPasscodeAllowed: true,
1030 }
1031 },
1032 action: 'SCR_MANAGE_PASSCODE_OPT_ON'
1033 },
1034 {
1035 condition: {
1036 state: {
1037 secureStoreOpen: true,
1038 //defaultPasscodeUsed: [DONTCARE],
1039 defaultPasscodeAllowed: false,
1040 }
1041 },
1042 action: 'SCR_MANAGE_PASSCODE_MANDATORY'
1043 },
1044
1045
1046 ];
1047
1048 this.screenEvents = {
1049 'SCR_MANAGE_PASSCODE_OPT_ON': {
1050 onsubmit: onFlowSuccess,
1051 oncancel: onFlowSuccess,
1052 onerror: onFlowError,
1053 ondisable: showScreen('SCR_CHANGE_PASSCODE_OPT_OFF'),
1054 onchange: showScreen('SCR_CHANGE_PASSCODE_OPT_ON')
1055 },
1056 'SCR_MANAGE_PASSCODE_OPT_OFF': {
1057 onsubmit: onFlowSuccess,
1058 oncancel: onFlowSuccess,
1059 onerror: onFlowError,
1060 onenable: showScreen('SCR_SET_PASSCODE_OPT_ON')
1061 },
1062 'SCR_MANAGE_PASSCODE_MANDATORY': {
1063 onsubmit: onFlowSuccess,
1064 oncancel: onFlowSuccess,
1065 onerror: onFlowError,
1066 onchange: showScreen('SCR_CHANGE_PASSCODE_MANDATORY')
1067 },
1068
1069
1070 'SCR_SET_PASSCODE_OPT_ON': {
1071 onsubmit: callSetPasscode,
1072 oncancel: onFlowCancel,
1073 onerror: onFlowError,
1074 ondisable: showScreen('SCR_SET_PASSCODE_OPT_OFF'),
1075 onerrorack: noOp
1076 },
1077 'SCR_SET_PASSCODE_OPT_OFF': {
1078 onsubmit: callDisablePasscode,
1079 oncancel: onFlowCancel,
1080 onerror: onFlowError,
1081 onenable: showScreen('SCR_SET_PASSCODE_OPT_ON'),
1082 onerrorack: noOp
1083 },
1084 'SCR_CHANGE_PASSCODE_OPT_ON': {
1085 onsubmit: callChangePasscode,
1086 oncancel: onFlowCancel,
1087 onerror: onFlowError,
1088 ondisable: showScreen('SCR_CHANGE_PASSCODE_OPT_OFF'),
1089 onerrorack: noOp
1090 },
1091 'SCR_CHANGE_PASSCODE_OPT_OFF': {
1092 onsubmit: callDisablePasscode,
1093 oncancel: onFlowCancel,
1094 onerror: onFlowError,
1095 onenable: showScreen('SCR_CHANGE_PASSCODE_OPT_ON'),
1096 onerrorack: noOp
1097 },
1098 'SCR_CHANGE_PASSCODE_MANDATORY': {
1099 onsubmit: callChangePasscode,
1100 oncancel: onFlowCancel,
1101 onerror: onFlowError,
1102 onerrorack: noOp
1103 },
1104
1105 };
1106
1107
1108 utils.log('flow constructor return');
1109 }
1110
1111 var ShowRegistrationFlow = function ShowRegistrationFlow(logonCore, logonView, onCoreResult, onFlowSuccess, onFlowError, onFlowCancel) {
1112 //wrapped into a function to defer evaluation of the references to flow callbacks
1113
1114 this.name = 'showRegistrationFlowBuilder';
1115
1116 var showRegistrationInfo = function(context) {
1117 logonView.showScreen('SCR_SHOW_REGISTRATION', this.screenEvents['SCR_SHOW_REGISTRATION'], context);
1118 }.bind(this);
1119
1120 var callGetContext = function(){
1121 utils.log('logonCore.getContext');
1122 logonCore.getContext(onCoreResult, onFlowError);
1123 }
1124
1125 // exported properties
1126 this.stateTransitions = [
1127 {
1128 condition: {
1129 state: {
1130 secureStoreOpen: true,
1131
1132 },
1133 context: null
1134 },
1135 action: callGetContext
1136 },
1137 {
1138 condition: {
1139 secureStoreOpen: true,
1140 },
1141 action: showRegistrationInfo
1142 }
1143
1144 ];
1145
1146 this.screenEvents = {
1147 'SCR_SHOW_REGISTRATION': {
1148 oncancel: onFlowSuccess,
1149 onerror: onFlowError
1150 }
1151 };
1152
1153
1154 utils.log('flow constructor return');
1155 }
1156
1157 // === flow launcher methods =====================================
1158
1159
1160 var resume = function (onsuccess, onerror) {
1161
1162 if(!_oLogonCore) {
1163 utils.log('FlowRunner.run MAFLogon is not initialized');
1164 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1165 return;
1166 }
1167
1168 var onUnlockSuccess = function(){
1169 _oLogonCore.onEvent(onsuccess, onerror, 'RESUME');
1170 }
1171
1172 var onGetStateSuccess = function(state) {
1173 //call registration flow only if the status is fullregistered in case of resume, so logon screen will not loose its input values
1174 if (state.status == 'fullRegistered') {
1175 registerOrUnlock(onUnlockSuccess, onerror);
1176 }
1177 }
1178
1179 getState(onGetStateSuccess, onerror);
1180 }
1181
1182
1183 var get = function (onsuccess, onerror, key) {
1184
1185 if(!_oLogonCore) {
1186 utils.log('FlowRunner.run MAFLogon is not initialized');
1187 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1188 return;
1189 }
1190
1191 var onUnlockSuccess = function(){
1192 _oLogonCore.getSecureStoreObject(onsuccess, onerror, key);
1193 }
1194
1195 registerOrUnlock(onUnlockSuccess, onerror);
1196 }
1197
1198
1199
1200 var set = function (onsuccess, onerror, key, value) {
1201
1202 if(!_oLogonCore) {
1203 utils.log('FlowRunner.run MAFLogon is not initialized');
1204 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1205 return;
1206 }
1207
1208 var onUnlockSuccess = function(){
1209 _oLogonCore.setSecureStoreObject(onsuccess, onerror, key, value);
1210 }
1211
1212 registerOrUnlock(onUnlockSuccess, onerror);
1213 }
1214
1215
1216
1217 var lock = function (onsuccess, onerror) {
1218 if(!_oLogonCore) {
1219 utils.log('FlowRunner.run MAFLogon is not initialized');
1220 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1221 return;
1222 }
1223
1224 _oLogonCore.lockSecureStore(onsuccess, onerror);
1225 }
1226
1227 var getState = function (onsuccess, onerror) {
1228 if(!_oLogonCore) {
1229 utils.log('FlowRunner.run MAFLogon is not initialized');
1230 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1231 return;
1232 }
1233
1234 _oLogonCore.getState(onsuccess, onerror);
1235 }
1236
1237 var wrapCallbackWithQueueNext = function(callback) {
1238 return function() {
1239 callback.apply(this, arguments);
1240 if (flowqueue) {
1241 flowqueue.runNextFlow();
1242 }
1243 }
1244 }
1245
1246
1247 var registerOrUnlock = function(onsuccess, onerror) {
1248 if(!_oLogonCore) {
1249 utils.log('FlowRunner.run MAFLogon is not initialized');
1250 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1251 return;
1252 }
1253
1254 var callbacks = {
1255 "onsuccess" : wrapCallbackWithQueueNext(onsuccess),
1256 "onerror" : wrapCallbackWithQueueNext(onerror)
1257 }
1258 var flowRunner = new FlowRunner(callbacks, _oLogonView, _oLogonCore, RegistrationFlow);
1259
1260
1261 if (flowqueue) {
1262 flowqueue.add(flowRunner);
1263 }
1264 else {
1265 flowRunner.run();
1266 }
1267 }
1268
1269 var changePassword = function(onsuccess, onerror) {
1270 if(!_oLogonCore) {
1271 utils.log('FlowRunner.run MAFLogon is not initialized');
1272 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1273 return;
1274 }
1275
1276 var onUnlockSuccess = function(){
1277 var callbacks = {
1278 "onsuccess" : wrapCallbackWithQueueNext(onsuccess),
1279 "onerror" : wrapCallbackWithQueueNext(onerror)
1280 }
1281 var innerFlowRunner = new FlowRunner(callbacks, _oLogonView, _oLogonCore, ChangePasswordFlow);
1282
1283 if (flowqueue) {
1284 flowqueue.add(innerFlowRunner);
1285 }
1286 else {
1287 innerFlowRunner.run();
1288 }
1289 }
1290
1291 registerOrUnlock(onUnlockSuccess, onerror);
1292 }
1293
1294
1295 var forgottenPasscode = function(onsuccess, onerror) {
1296 if(!_oLogonCore) {
1297 utils.log('FlowRunner.run MAFLogon is not initialized');
1298 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1299 return;
1300 }
1301
1302 var onUnlockSuccess = function(){
1303 var callbacks = {
1304 "onsuccess" : wrapCallbackWithQueueNext(onsuccess),
1305 "onerror" : wrapCallbackWithQueueNext(onerror)
1306 }
1307 var innerFlowRunner = new FlowRunner(callbacks, _oLogonView, _oLogonCore, MockFlow);
1308 if (flowqueue) {
1309 flowqueue.add(innerFlowRunner);
1310 }
1311 else {
1312 innerFlowRunner.run();
1313 }
1314 }
1315
1316 registerOrUnlock(onUnlockSuccess, onerror);
1317 }
1318
1319 var managePasscode = function(onsuccess, onerror) {
1320 if(!_oLogonCore) {
1321 utils.log('FlowRunner.run MAFLogon is not initialized');
1322 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1323 return;
1324 }
1325
1326 var onUnlockSuccess = function(){
1327 var callbacks = {
1328 "onsuccess" : wrapCallbackWithQueueNext(onsuccess),
1329 "onerror" : wrapCallbackWithQueueNext(onerror)
1330 }
1331 var innerFlowRunner = new FlowRunner(callbacks, _oLogonView, _oLogonCore, ManagePasscodeFlow);
1332 if (flowqueue) {
1333 flowqueue.add(innerFlowRunner);
1334 }
1335 else {
1336 innerFlowRunner.run();
1337 }
1338 }
1339
1340 registerOrUnlock(onUnlockSuccess, onerror);
1341 }
1342
1343 var showRegistrationData = function(onsuccess, onerror) {
1344 if(!_oLogonCore) {
1345 utils.log('FlowRunner.run MAFLogon is not initialized');
1346 onerror(errorWithDomainCodeDescription("MAFLogon","2","MAFLogon is not initialized"));
1347 return;
1348 }
1349
1350 var onUnlockSuccess = function(){
1351 var callbacks = {
1352 "onsuccess" : wrapCallbackWithQueueNext(onsuccess),
1353 "onerror" : wrapCallbackWithQueueNext(onerror)
1354 }
1355 var innerFlowRunner = new FlowRunner(callbacks, _oLogonView, _oLogonCore, ShowRegistrationFlow);
1356 if (flowqueue) {
1357 flowqueue.add(innerFlowRunner);
1358 }
1359 else {
1360 innerFlowRunner.run();
1361 }
1362 }
1363
1364 registerOrUnlock(onUnlockSuccess, onerror);
1365 }
1366
1367 var getSecureStoreErrorText = function(error) {
1368 utils.logJSON('LogonController.getSecureStoreErrorText: ' + JSON.stringify(error));
1369
1370 var errorText;
1371
1372 if(error.errorCode === '14' && error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1373 errorText = "ERR_PASSCODE_TOO_SHORT";
1374 else if(error.errorCode === '10' && error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1375 errorText = "ERR_PASSCODE_REQUIRES_DIGIT";
1376 else if(error.errorCode === '13' && error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1377 errorText = "ERR_PASSCODE_REQUIRES_UPPER";
1378 else if(error.errorCode === '11' && error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1379 errorText = "ERR_PASSCODE_REQUIRES_LOWER";
1380 else if(error.errorCode === '12' && error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1381 errorText = "ERR_PASSCODE_REQUIRES_SPECIAL";
1382 else if(error.errorCode === '15' && error.errorDomain === 'MAFSecureStoreManagerErrorDomain')
1383 errorText = "ERR_PASSCODE_UNDER_MIN_UNIQUE_CHARS";
1384 else {
1385 errorText = "ERR_SETPASSCODE_FAILED";
1386 }
1387
1388 return errorText;
1389 }
1390
1391 var getSSOPasscodeSetErrorText = function(error) {
1392 utils.logJSON('LogonController.getSSOPasscodeSetErrorText: ' + JSON.stringify(error));
1393
1394 var errorText;
1395
1396 if (error.errorDomain === 'MAFLogonCoreErrorDomain') {
1397 if (error.errorCode === '16') {
1398 errorText = "ERR_SSO_PASSCODE_SET_ERROR";
1399 }
1400 }
1401
1402 return errorText;
1403 }
1404
1405 var getRegistrationErrorText = function(error) {
1406 utils.logJSON('LogonController.getRegistrationErrorText: ' + JSON.stringify(error));
1407
1408 var errorText;
1409
1410 if (error.errorDomain === 'MAFLogonCoreErrorDomain') {
1411 if (error.errorCode === '80003') {
1412 errorText = "ERR_REG_FAILED_WRONG_SERVER";
1413 }
1414 //in case of wrong application id
1415 else if (error.errorCode === '404') {
1416 errorText = "ERR_REG_FAILED";
1417 }
1418 else if (error.errorCode === '401') {
1419 errorText = "ERR_REG_FAILED_UNATHORIZED";
1420 }
1421 else {
1422 errorText = "ERR_REG_FAILED";
1423 }
1424 }
1425
1426 return errorText;
1427 }
1428
1429 var getRegistrationCancelError = function(error) {
1430 utils.logJSON('LogonController.getRegistrationCancelError: ' + JSON.stringify(error));
1431
1432 var errorText;
1433
1434 errorText = "ERR_REGISTRATION_CANCEL";
1435
1436 return errorText;
1437 }
1438
1439 var errorWithDomainCodeDescription = function(domain, code, description) {
1440 var error = {
1441 errorDomain: domain,
1442 errorCode: code,
1443 errorMessage: description
1444 };
1445
1446 return error;
1447 }
1448
1449
1450 // =================== exported (public) members ====================
1451
1452 /**
1453 * The Logon plugin provides screen flows to register an app with an SAP Mobile Platform server.<br/>
1454 * <br/>
1455 * The logon plugin is a component of the SAP Mobile Application Framework (MAF), exposed as a Cordova plugin. The basic
1456 * idea is that it provides screen flows where the user can enter the values needed to connect to an SAP Mobile Platform 3.0 server and
1457 * stores those values in its own secure data vault. This data vault is separate from the one provided with the
1458 * encrypted storage plugin. In an OData based SAP Mobile Platform 3.0 application, a client must onboard or register with the SAP Mobile Platform 3.0
1459 * server to receive an application connection ID for a particular app. The application connection ID must be sent
1460 * along with each request that is proxied through the SAP Mobile Platform 3.0 server to the OData producer.<br/>
1461 * <br/>
1462 * <b>Adding and Removing the Logon Plugin</b><br/>
1463 * The Logon plugin is added and removed using the
1464 * <a href="http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface">Cordova CLI</a>.<br/>
1465 * <br/>
1466 * To add the Logon plugin to your project, use the following command:<br/>
1467 * cordova plugin add <full path to directory containing Kapsel plugins>\logon<br/>
1468 * <br/>
1469 * To remove the Logon plugin from your project, use the following command:<br/>
1470 * cordova plugin rm com.sap.mp.cordova.plugins.logon
1471 *
1472 * @namespace
1473 * @alias Logon
1474 * @memberof sap
1475 */
1476 module.exports = {
1477
1478 /**
1479 * Initialization method to set up the Logon plugin. This will register the application with the SMP server and also authenticate the user
1480 * with servers on the network. This step must be done first prior to any attempt to communicate with the SMP server.
1481 *
1482 * @method
1483 * @param {sap.Logon~successCallback} successCallback The function that is invoked if initialization is successful. The current
1484 * context is passed to this function as the parameter.
1485 * @param {sap.Logon~errorCallback} errorCallback The function that is invoked in case of an error.
1486 * @param {string} applicationId The unique ID of the application. Must match the application ID on the SAP Mobile Platform server.
1487 * @param {object} [context] The context with default values for application registration. See {@link sap.Logon~successCallback} for the structure
1488 * of the context object. Note that all properties of the context object are optional, and you only need to specify the properties
1489 * for which you want to provide default values for. The values will be presented to the application users during the registration process and given them
1490 * a chance to override these values during runtime.
1491 * @param {string} [logonView="com/sap/mp/logon/iabui"] The cordova module ID of a custom renderer for the logon,
1492 * implementing the [showScreen(), close()] interface. Please use the defaul module unless you are absolutely sure that you can provide your own
1493 * custom implementation. Please refer to JavaScript files inside your Kapsel project's plugins\logon\www\common\modules\ folder as example.
1494 * @example
1495 * // a custom UI can be loaded here
1496 * var logonView = sap.logon.IabUi;
1497 *
1498 * // The app ID
1499 * var applicationId = "someAppID";
1500 *
1501 * // You only need to specify the fields for which you want to set the default. These values are optional because they will be
1502 * // used to prefill the fields on Logon's UI screen.
1503 * var defaultContext = {
1504 * "serverHost" : "defaultServerHost.com"
1505 * "https" : false,
1506 * "serverPort" : "8080",
1507 * "user" : "user1",
1508 * "password" : "Zzzzzz123",
1509 * "communicatorId" : "REST",
1510 * "securityConfig" : "sec1",
1511 * "passcode" : "Aaaaaa123",
1512 * "unlockPasscode" : "Aaaaaa123"
1513 * };
1514 *
1515 * var app_context;
1516 *
1517 * var successCallback = function(context){
1518 * app_context = context;
1519 * }
1520 *
1521 * var errorCallback = function(errorInfo){
1522 * alert("error: " + JSON.stringify(errorInfo));
1523 * }
1524 * sap.Logon.init(successCallback, errorCallback, applicationId, defaultContext, logonView);
1525 */
1526 init: init,
1527
1528 /**
1529 * The application ID with which {@link sap.Logon.init} was called. It is available here so it is easy to access later.
1530 * @example
1531 * // After calling the init function
1532 * alert("The app ID for this app is: " + sap.Logon.applicationId);
1533 */
1534 applicationId: null,
1535 /**
1536 * Direct reference to the logon core object used by the Logon plugin. This is needed to perform more complex operations that
1537 * are not generally needed by applications. <br/>
1538 * There are several functions that can be accessed on the core object:<br/>
1539 * getState(successCallback,errorCallback) returns the state object of the application to the success callback in the form of a JavaScript object.<br/>
1540 * getContext(successCallback,errorCallback) returns the context object of the application to the success callback in the form of a JavaScript object.<br/>
1541 * deleteRegistration(successCallback,errorCallback) deletes the application's registration from the SAP Mobile Platform server and removes<br/>
1542 * application data on device.<br/>
1543 * @example
1544 * var successCallback = function(result){
1545 * alert("Result: " + JSON.stringify(result));
1546 * }
1547 * var errorCallback = function(errorInfo){
1548 * alert("Error: " + JSON.stringify(errorInfo));
1549 * }
1550 * sap.Logon.core.getState(successCallback,errorCallback);
1551 * sap.Logon.core.getContext(successCallback,errorCallback);
1552 * sap.Logon.core.deleteRegistration(successCallback,errorCallback);
1553 */
1554 core: _oLogonCore, //Must be updated after init
1555
1556 /**
1557 * Get an (JSON serializable) object from the DataVault for a given key.
1558 * @method
1559 * @param {sap.Logon~getSuccessCallback} onsuccess The function that is invoked
1560 * upon success. It is called with the resulting object as a single parameter.
1561 * This can be null or undefined, if no object is defined for the given key.
1562 * @param {sap.Logon~errorCallback} onerror The function to invoke in case of error.
1563 * @param {string} key The key with which to query the DataVault.
1564 * @example
1565 * var errorCallback = function(errorInfo){
1566 * alert("Error: " + JSON.stringify(errorInfo));
1567 * }
1568 * var getSuccess = function(value){
1569 * alert("value retrieved from the store: " + JSON.stringify(value));
1570 * }
1571 * var setSuccess = function(){
1572 * sap.Logon.get(getSuccess,errorCallback,'someKey');
1573 * }
1574 * sap.Logon.set(setSuccess,errorCallback,'someKey', 'some string (could also be an object).');
1575 */
1576 get: get,
1577
1578 /**
1579 * Set an (JSON serializable) object in the DataVault.
1580 * @method
1581 * @param {sap.Logon~successCallbackNoParameters} onsuccess The function to invoke upon success.
1582 * onsuccess will be called without parameters for this method.
1583 * @param {sap.Logon~errorCallback} onerror The function to invoke in case of error.
1584 * @param {string} key The key to store the provided object on.
1585 * @param {object} value The object to be set on the given key. Must be JSON serializable (ie:
1586 * cannot contain circular references).
1587 * @example
1588 * var errorCallback = function(errorInfo){
1589 * alert("Error: " + JSON.stringify(errorInfo));
1590 * }
1591 * var getSuccess = function(value){
1592 * alert("value retrieved from the store: " + JSON.stringify(value));
1593 * }
1594 * var setSuccess = function(){
1595 * sap.Logon.get(getSuccess,errorCallback,'someKey');
1596 * }
1597 * sap.Logon.set(setSuccess,errorCallback,'someKey', 'some string (could also be an object).');
1598 */
1599 set: set,
1600
1601 /**
1602 * Locks the Logon plugin's secure data vault.
1603 * @method
1604 * @param {sap.Logon~successCallbackNoParameters} onsuccess The function to invoke upon success.
1605 * @param {sap.Logon~errorCallback} onerror The function to invoke in case of error.
1606 * @example
1607 * var errorCallback = function(errorInfo){
1608 * alert("Error: " + JSON.stringify(errorInfo));
1609 * }
1610 * var successCallback = function(){
1611 * alert("Locked!");
1612 * }
1613 * sap.Logon.lock(successCallback,errorCallback);
1614 */
1615 lock: lock,
1616
1617 /**
1618 * Unlock the Logon plugin's secure data vault if it has been locked (due to being inactive, or
1619 * {@link sap.Logon.lock} being called), then the user is prompted for the passcode to unlock the
1620 * application.<br/>
1621 * If the application is already unlocked, then nothing will be done.<br/>
1622 * If the application has passcode disabled, then passcode prompt will not be necessary.
1623 * In all cases if an error does not occur, the success callback is invoked with the current logon context
1624 * as the parameter.
1625 * @method
1626 * @param {sap.Logon~successCallback} onsuccess - The callback to call if the screen flow succeeds.
1627 * onsuccess will be called with the current logon context as a single parameter.
1628 * @param {sap.Logon~errorCallback} onerror - The callback to call if the screen flow fails.
1629 * @example
1630 * var errorCallback = function(errorInfo){
1631 * alert("Error: " + JSON.stringify(errorInfo));
1632 * }
1633 * var successCallback = function(context){
1634 * alert("Registered and unlocked. Context: " + JSON.stringify(context));
1635 * }
1636 * sap.Logon.unlock(successCallback,errorCallback);
1637 */
1638 unlock: registerOrUnlock,
1639
1640 /**
1641 * This is an alias for registerOrUnlock. Calling this function is equivalent
1642 * to calling {@link sap.Logon.unlock} since both of them are alias to registerOrUnlock.
1643 * @method
1644 * @private
1645 */
1646 registerUser: registerOrUnlock,
1647
1648 /**
1649 * This function registers the user and creates a new unlocked DataVault to store the registration
1650 * information.<br/>
1651 * If the user has already been registered, but the application is locked (due to being inactive, or
1652 * {@link sap.Logon.lock} being called), then the user is prompted for the passcode to unlock the
1653 * application.<br/>
1654 * If the application is already unlocked, then nothing will be done.<br/>
1655 * In all cases if an error does not occur, the success callback is invoked with the current logon context
1656 * as the parameter.
1657 * @method
1658 * @param {sap.Logon~successCallback} onsuccess - The callback to call if the screen flow succeeds.
1659 * onsuccess will be called with the current logon context as a single parameter.
1660 * @param {sap.Logon~errorCallback} onerror - The callback to call if the screen flow fails.
1661 * @example
1662 * var errorCallback = function(errorInfo){
1663 * alert("Error: " + JSON.stringify(errorInfo));
1664 * }
1665 * var successCallback = function(context){
1666 * alert("Registered and unlocked. Context: " + JSON.stringify(context));
1667 * }
1668 * sap.Logon.registerOrUnlock(successCallback,errorCallback);
1669 * @private
1670 */
1671 registerOrUnlock: registerOrUnlock,
1672
1673 /**
1674 * This method will launch the UI screen for application users to manage and update the data vault passcode or,
1675 * if the SMP server's Client Passcode Policy allows it, enable or disable the passcode to the data vault.
1676 *
1677 * @method
1678 * @param {sap.Logon~successCallbackNoParameters} onsuccess - The function to invoke upon success.
1679 * @param {sap.Logon~errorCallback} onerror - The function to invoke in case of error.
1680 * @example
1681 * var errorCallback = function(errorInfo){
1682 * alert("Error: " + JSON.stringify(errorInfo));
1683 * }
1684 * var successCallback = function(context){
1685 * alert("Passcode successfully managed.");
1686 * }
1687 * sap.Logon.managePasscode(successCallback,errorCallback);
1688 */
1689 managePasscode: managePasscode,
1690
1691 /**
1692 * This method will launch the UI screen for application users to manage and update the back-end passcode that Logon stores in the
1693 * data vault that is used to authenticate the client to the server.
1694 *
1695 * @method
1696 * @param {sap.Logon~successCallbackNoParameters} onsuccess - The callback to call if the screen flow succeeds.
1697 * onsuccess will be called without parameters for this method.
1698 * @param {sap.Logon~errorCallback} onerror The function that is invoked in case of an error.
1699 * @example
1700 * var errorCallback = function(errorInfo){
1701 * alert("Error: " + JSON.stringify(errorInfo));
1702 * }
1703 * var successCallback = function(context){
1704 * alert("Password successfully changed.");
1705 * }
1706 * sap.Logon.changePassword(successCallback,errorCallback);
1707 */
1708 changePassword: changePassword,
1709
1710 /**
1711 * Calling this method will show a screen which displays the current registration settings for the application.
1712 * @method
1713 * @param {sap.Logon~successCallbackNoParameters} onsuccess - The callback to call if the screen flow succeeds.
1714 * onsuccess will be called without parameters for this method.
1715 * @param {sap.Logon~errorCallback} onerror The function that is invoked in case of an error.
1716 * @example
1717 * var errorCallback = function(errorInfo){
1718 * alert("Error: " + JSON.stringify(errorInfo));
1719 * }
1720 * var successCallback = function(context){
1721 * alert("The showRegistrationData screenflow was successful.");
1722 * }
1723 * sap.Logon.showRegistrationData(successCallback,errorCallback);
1724 */
1725 showRegistrationData: showRegistrationData
1726 };
1727
1728 /**
1729 * Callback function that is invoked in case of an error.
1730 *
1731 * @callback sap.Logon~errorCallback
1732 *
1733 * @param {Object} errorObject Depending on the origin of the error the object can take several forms.
1734 * (Unfortunately the error object structure and content is not uniform among the platforms, this will
1735 * probably change in the future.)
1736 *
1737 * Errors originating from the logon plugin have only an 'errorKey' property.
1738 * The possible values for 'errorKey':
1739 *
1740 * ERR_CHANGE_TIMEOUT_FAILED
1741 * ERR_FORGOT_SSO_PIN
1742 * ERR_INIT_FAILED
1743 * ERR_INVALID_ACTION
1744 * ERR_INVALID_STATE
1745 * ERR_PASSCODE_REQUIRES_DIGIT
1746 * ERR_PASSCODE_REQUIRES_LOWER
1747 * ERR_PASSCODE_REQUIRES_SPECIAL
1748 * ERR_PASSCODE_REQUIRES_UPPER
1749 * ERR_PASSCODE_TOO_SHORT
1750 * ERR_PASSCODE_UNDER_MIN_UNIQUE_CHARS
1751 * ERR_REGISTRATION_CANCEL
1752 * ERR_REG_FAILED
1753 * ERR_REG_FAILED_UNATHORIZED
1754 * ERR_REG_FAILED_WRONG_SERVER
1755 * ERR_SETPASSCODE_FAILED
1756 * ERR_SET_AFARIA_CREDENTIAL_FAILED
1757 * ERR_SSO_PASSCODE_SET_ERROR
1758 * ERR_UNKNOWN_SCREEN_ID
1759 * ERR_UNLOCK_FAILED
1760 * ERR_USER_CANCELLED
1761 *
1762 * Errors originating in the logon core (either iOS or Android) have the following properties: 'errorCode',
1763 * 'errorMessage', and 'errorDomain'.
1764 * The 'errorCode' is just a number uniquely identifying the error. The 'errorMessage'
1765 * property is a string with more detailed information of what went wrong. The 'errorDomain' property specifies
1766 * the domain that the error occurred in.
1767 *
1768 * On iOS the 'errorDomain' property of the core errors can take the following values: MAFLogonCoreErrorDomain, MAFSecureStoreManagerErrorDomain, and MAFLogonCoreCDVPluginErrorDomain.
1769 *
1770 * In the MAFLogonCoreErrorDomain the following errors are thrown (throwing methods in paren):
1771 *
1772 * 3 errMAFLogonErrorCommunicationManagerError (register, update settings, delete, change backend password)
1773 * 9 errMAFLogonErrorCouldNotDecideCommunicator (register)
1774 * 11 errMAFLogonErrorOperationNotAllowed (all)
1775 * 12 errMAFLogonErrorInvalidServerHost (register)
1776 * 13 errMAFLogonErrorInvalidBackendPassword (changeBackendPassword)
1777 * 15 errMAFLogonErrorUploadTraceFailed (uploadTrace)
1778 * 16 errMAFLogonErrorInvalidMCIMSSOPin (setMCIMSSOPin)
1779 * 18 errMAFLogonErrorCertificateKeyError (register)
1780 * 19 errMAFLogonErrorCertificateError (register)
1781 * 20 errMAFLogonErrorAfariaInvalidCredentials (setAfariaCredentialWithUser)
1782 *
1783 * In the MAFSecureStoreManagerErrorDomain the following errors are thrown (throwing methods in paren):
1784 *
1785 * 0 errMAFSecureStoreManagerErrorUnknown (persist, unlock, changePasscode, delete, getContext)
1786 * 1 errMAFSecureStoreManagerErrorAlreadyExists (persist)
1787 * 2 errMAFSecureStoreManagerErrorDataTypeError (unlock, getContext)
1788 * 3 errMAFSecureStoreManagerErrorDoesNotExist (unlock, persist, getContext)
1789 * 4 errMAFSecureStoreManagerErrorInvalidArg unlock, (persist, getContext)
1790 * 5 errMAFSecureStoreManagerErrorInvalidPassword (unlock)
1791 * 6 errMAFSecureStoreManagerErrorLocked (getContext)
1792 * 7 errMAFSecureStoreManagerErrorOutOfMemory (persist, unlock, changePasscode, delete, getContext)
1793 * 8 errMAFSecureStoreManagerErrorPasswordExpired (unlock, getContext)
1794 * 9 errMAFSecureStoreManagerErrorPasswordRequired (persist, changePasscode)
1795 * 10 errMAFSecureStoreManagerErrorPasswordRequiresDigit (persist, changePasscode)
1796 * 11 errMAFSecureStoreManagerErrorPasswordRequiresLower (persist, changePasscode)
1797 * 12 errMAFSecureStoreManagerErrorPasswordRequiresSpecial (persist, changePasscode)
1798 * 13 errMAFSecureStoreManagerErrorPasswordRequiresUpper (persist, changePasscode)
1799 * 14 errMAFSecureStoreManagerErrorPasswordUnderMinLength (persist, changePasscode)
1800 * 15 errMAFSecureStoreManagerErrorPasswordUnderMinUniqueChars (persist, changePasscode)
1801 * 16 errMAFSecureStoreManagerErrorDeleted (unlock)
1802 *
1803 * In the MAFLogonCoreCDVPluginErrorDomain the following errors are thrown:
1804 *
1805 * 1 (init failed)
1806 * 2 (plugin not initialized)
1807 * 3 (no input provided)
1808 *
1809 * On Android the 'errorDomain' property of the core errors can take the following values: MAFLogonCoreErrorDomain and MAFLogonCoreCDVPluginErrorDomain.
1810 * There are no logon specific error codes, the 'errorCode' property only wraps the error values from the underlying libraries.
1811 */
1812
1813 /**
1814 * Callback function that is invoked upon successfully registering or unlocking or retrieving the context.
1815 *
1816 * @callback sap.Logon~successCallback
1817 *
1818 * @param {Object} context An object containing the current logon context. Two properties of particular importance
1819 * are applicationEndpointURL, and applicationConnectionId.
1820 * The context object contains the following properties:<br/>
1821 * "registrationContext": {<br/>
1822 * "serverHost": Host of the server.<br/>
1823 * "domain": Domain for server. Can be used in case of SAP Mobile Platform communication.<br/>
1824 * "resourcePath": Resource path on the server. The path is used mainly for path based reverse proxy but can contain a custom relay server path as well.<br/>
1825 * "https": Marks whether the server should be accessed in a secure way.<br/>
1826 * "serverPort": Port of the server.<br/>
1827 * "user": Username in the backend.<br/>
1828 * "password": Password for the backend user.<br/>
1829 * "farmId": FarmId of the server. Can be nil. Used in case of Relay server or SiteMinder.<br/>
1830 * "communicatorId": Id of the communicator manager that will be used for performing the logon. Possible values: IMO / GATEWAY / REST<br/>
1831 * "securityConfig": Security configuration. If nil, the default configuration is used.<br/>
1832 * "mobileUser": Mobile User. Used in case of IMO manual user creation.<br/>
1833 * "activationCode": Activation Code. Used in case of IMO manual user creation.<br/>
1834 * "gatewayClient": The key string that identifies the client on the gateway. Used in Gateway only registration mode. The value will be used as adding the parameter: sap-client=<gateway client><br/>
1835 * "gatewayPingPath": The custom path of the ping URL on the gateway. Used in case of Gateway only registration mode.<br/>
1836 * }<br/>
1837 * "applicationEndpointURL": Contains the application endpoint URL after a successful registration.<br/>
1838 * "applicationConnectionId": ID to get after a successful SUP REST registration. Needs to be set in the download request header with key X-SUP-APPCID<br/>
1839 * "afariaRegistration": manual / automatic / certificate<br/>
1840 * "policyContext": Contains the password policy for the secure store {<br/>
1841 * "alwaysOn":<br/>
1842 * "alwaysOff":<br/>
1843 * "defaultOn":<br/>
1844 * "hasDigits":<br/>
1845 * "hasLowerCaseLetters":<br/>
1846 * "hasSpecialLetters":<br/>
1847 * "hasUpperCaseLetters":<br/>
1848 * "defaultAllowed":<br/>
1849 * "expirationDays":<br/>
1850 * "lockTimeout":<br/>
1851 * "minLength":<br/>
1852 * "minUniqueChars":<br/>
1853 * "retryLimit":<br/>
1854 * }<br/>
1855 * "registrationReadOnly": specifies whether context values are coming from clientHub / afaria<br/>
1856 * "policyReadOnly": specifies whether passcode policy is coming from afaria<br/>
1857 * "credentialsByClientHub": specifies whether credentials are coming from clientHub
1858 */
1859
1860 /**
1861 * Callback function that will be invoked with no parameters.
1862 *
1863 * @callback sap.Logon~successCallbackNoParameters
1864 */
1865
1866 /**
1867 * Callback function that is invoked upon successfully retrieving an object from the DataVault.
1868 *
1869 * @callback sap.Logon~getSuccessCallback
1870 *
1871 * @param {Object} value The object that was stored with the given key. Can be null or undefined if no object was stored
1872 * with the given key.
1873 */
1874
1875