QRSlideoutView.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. *+------------------------------------------------------------------------+
  3. *| Licensed Materials - Property of IBM
  4. *| IBM Cognos Products: BI Content Explorer
  5. *| (C) Copyright IBM Corp. 2015, 2019
  6. *|
  7. *| US Government Users Restricted Rights - Use, duplication or disclosure
  8. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  9. *+------------------------------------------------------------------------+
  10. */
  11. define([
  12. 'bi/glass/app/ContentView',
  13. './nls/StringResource',
  14. 'jquery',
  15. 'underscore',
  16. 'doT',
  17. 'text!./templates/QRSlideoutViewTemplate.html',
  18. './lib/qrcode/build/qrcode'
  19. ], function (ContentView, StringResource, $, _, dot, CAMobileTemplate, QRCode) {
  20. 'use strict';
  21. var QRSlideoutView = ContentView.extend({
  22. init: function(options) {
  23. QRSlideoutView.inherited('init', this, arguments);
  24. _.extend(this, options);
  25. this.$el.addClass('caMobilePaneContainer');
  26. },
  27. open: function(context, options) {
  28. this.glassContext = __glassAppController.glassContext; // eslint-disable-line no-undef
  29. this.options = options;
  30. this.render();
  31. return Promise.resolve();
  32. },
  33. generateSlideout: function () {
  34. return dot.template(CAMobileTemplate)({
  35. 'strings': {
  36. 'getMobileApp': StringResource.get('getMobileApp').toUpperCase(),
  37. 'cognosAnalytics': StringResource.get('cognosAnalytics'),
  38. 'downloadMobileApp': StringResource.get('downloadMobileApp'),
  39. 'genQRCode': StringResource.get('genQRCode'),
  40. 'mobileTwoFactor': StringResource.get('mobileTwoFactor'),
  41. 'learnMore': StringResource.get('learnMore'),
  42. 'appIos': StringResource.get('appIos'),
  43. 'appAndroid': StringResource.get('appAndroid'),
  44. 'appUseMessage': StringResource.get('appUseMessage').toUpperCase(),
  45. 'getAppMessage': StringResource.get('getAppMessage'),
  46. 'appIcon': this.options.appIcon
  47. }
  48. });
  49. },
  50. addEventListeners: function () {
  51. $('#mobileGenQR').click(this.onGenerateQRClick.bind(this));
  52. $('.mobileTwoFactorMessage').click(this.onSecondFactorClick.bind(this));
  53. },
  54. /**
  55. * Render the mobile app property page view.
  56. */
  57. render: function() {
  58. var slideoutHtml = this.generateSlideout();
  59. this.$el.append(slideoutHtml);
  60. this.addEventListeners();
  61. return Promise.resolve(this.$el);
  62. },
  63. formatSecondFactorCountdownTime: function(startTime, expirationTime) {
  64. const differenceInTime = expirationTime - startTime;
  65. const minutes = Math.floor((differenceInTime % (1000 * 60 * 60)) / (1000 * 60));
  66. const seconds = Math.floor((differenceInTime % (1000 * 60)) / 1000);
  67. const timeAsString = this.padTimeNumber(minutes) + ':' + this.padTimeNumber(seconds);
  68. return timeAsString;
  69. },
  70. padTimeNumber: function(number) {
  71. number = String(number);
  72. while (number.length < 2) {number = '0' + number;}
  73. return number;
  74. },
  75. setCountdownInterval: function(secondFactor) {
  76. const displayElement = $('.mobileQRTwoFactorTimer');
  77. const finalTime = Date.parse(secondFactor.expires);
  78. var now;
  79. var timer = setInterval(function() {
  80. now = new Date().getTime();
  81. var differenceInTime = finalTime - now;
  82. displayElement.html(this.formatSecondFactorCountdownTime(now, finalTime));
  83. if (differenceInTime < 0) {
  84. clearInterval(timer);
  85. displayElement.html(StringResource.get('tokenExpired'));
  86. }
  87. }.bind(this), 1000);
  88. },
  89. generateQR: function (deepLink) {
  90. QRCode.toCanvas(document.getElementById('mobileQRCodeCanvas'), deepLink, function(err) {
  91. if(err) console.log(err);
  92. });
  93. this.hideGenerateQRDialog();
  94. },
  95. hideGenerateQRDialog: function () {
  96. $('#mobileQRCodeCanvas').style = 'width: 200px; height: 200px';
  97. $('#mobileGenQR').addClass('hidden');
  98. },
  99. displayTwoFactor: function (secondFactor) {
  100. const startTime = new Date().getTime();
  101. const expirationTime = Date.parse(secondFactor.expires);
  102. $('.mobileQRTwoFactor').removeClass('hidden');
  103. $('.mobileQRTwoFactorPin').html(secondFactor.value);
  104. $('.mobileQRTwoFactorTimer').html(this.formatSecondFactorCountdownTime(startTime, expirationTime));
  105. },
  106. /**
  107. * Generates a QR code and places it into the QRCodeCanvas element
  108. */
  109. onGenerateQRClick: function() {
  110. this.retrieveLoginToken().then(function(response) {
  111. const secondFactor = response.secondFactor;
  112. const deepLink = this.formatDeepLink(response);
  113. this.generateQR(deepLink);
  114. this.displayTwoFactor(secondFactor);
  115. this.setCountdownInterval(secondFactor);
  116. }.bind(this)).catch(function() {
  117. const deepLink = this.formatDeepLink();
  118. this.generateQR(deepLink);
  119. }.bind(this));
  120. },
  121. /**
  122. * Takes in the JSON response of v1/loginToken and outputs a deeplink to the app
  123. * Also removes the second factor from the response
  124. */
  125. formatDeepLink: function(response) {
  126. // Save the secondFactor locally and remove it from the json
  127. var deepLink = `camobile://?serverUrl=${encodeURIComponent(this.getServerURL())}`;
  128. if(response) {
  129. deepLink = deepLink +
  130. `&loginToken=${encodeURIComponent(JSON.stringify(response.loginToken))}` +
  131. `&x-ca-affinity=${encodeURIComponent(response.affinityHeader)}`;
  132. }
  133. return deepLink;
  134. },
  135. /**
  136. * Displays the second factor and sets a countdown
  137. */
  138. onSecondFactorClick: function() {
  139. if(!this.secondFactor) {
  140. return false;
  141. }
  142. $('.mobileTwoFactorPin').html(this.secondFactor.value);
  143. $('.mobileTwoFactor').removeClass('hidden');
  144. var expirationDate = Date.parse(this.secondFactor.expires);
  145. var now = new Date().getTime();
  146. $('.mobileTwoFactorTimer').html(this.formatSecondFactorCountdownTime(now, expirationDate));
  147. this._createCountdownInterval($('.mobileTwoFactorTimer'), expirationDate);
  148. },
  149. /**
  150. * Call user profile services to retrieve the loginToken
  151. */
  152. retrieveLoginToken: function() {
  153. var options = {
  154. 'url': 'v1/loginToken',
  155. 'dataType': 'json',
  156. 'type': 'POST'
  157. };
  158. return this.glassContext.getCoreSvc('.Ajax').ajax(options)
  159. .then(function(response) {
  160. const payload = response.data;
  161. payload.affinityHeader = response.jqXHR.getResponseHeader('x-ca-affinity');
  162. return Promise.resolve(payload);
  163. }.bind(this)).catch(this.handleLoginTokenError.bind(this));
  164. },
  165. handleLoginTokenError: function(err) {
  166. this.logger.error(err);
  167. return Promise.reject(err);
  168. },
  169. getServerURL: function() {
  170. return window.location.origin;
  171. }
  172. });
  173. return QRSlideoutView;
  174. });