Name

global.PwdVAEnrollmentManager

Description

No description available

Script

var PwdVAEnrollmentManager = Class.create();
PwdVAEnrollmentManager.prototype = {
  PARM_NUM_ENROLL: 'num_enroll',
  CREATE_MODE: "create",
  UPDATE_MODE: "update",
  IN_PROGRESS: "0",
  ACTIVE: "1",
  INACTIVE: "2",
  SUCCESS: "Success",
  ERROR: "Error",
  OTHER: "Other",
  CODE_LENGTH: 6,
  manager: new PwdEnrollmentManager(),
  INVALID_PHONE_NUMBER: -4,
  INVALID_REQUEST_RESPONSE: -3,
  PAUSE_WINDOW_RESPONSE: -2,
  PER_DAY_LIMIT_RESPONSE: -1,
  GENERIC_FAIL_RESPONSE: 0,
  SUCCESS_RESPONSE: 1,
  PWD_MESSAGE: '7cd0c421bf200100710071a7bf0739bd',
  UNSUBSCRIBE: 'c1bfa4040a0a0b8b001eeb0f3f5ee961',
  SMS: 'SMS',
  EMAIL: 'Email',
  initialize: function() {
      this.helper = new PwdNotificationHelper();
  },

  //General
  getEntitledPasswordResetProcesses: function(userId) {
      var processes = {};
      var processIds = new global.PasswordResetScopedUtil().getProcessIdsByUserId(userId);
      for (var i = 0; i < processIds.length; i++) {
          var processId = processIds[i];
          var process = new SNC.PwdProcess(processId);
          if (process.getId() == null || !process.isPublic() || !process.isActive() || (!process.isResetPwd() && !process.isUnlockAccount()))
              continue;
          var verificationList = new SNC.PwdVerificationManager().getVerificationIds();
          var processVerificationGr = new GlideRecord('pwd_map_proc_to_verification');
          processVerificationGr.addQuery('process', processId);
          processVerificationGr.addQuery('verification', 'IN', verificationList);
          processVerificationGr.query();
          if (processVerificationGr.next()) {
              processes[process.getId()] = process.getLabel();
          }
      }
      var otherAppVerifications = new global.PasswordResetScopedUtil().getVerificationsFromOtherApplications();
      if (otherAppVerifications.size() > 0)
          processes["Other"] = this.OTHER;
      return JSON.stringify(processes);
  },

  getEnrollmentVerificationsForProcess: function(userId, processId) {
      var processMgr = new SNC.PwdProcessManager();
      var enrollmentMgr = new SNC.PwdEnrollmentManager();
      var result = {
          "verificationIds": {},
          "verificationTypeIds": {},
          "info": ""
      };
      var verificationIds = {};
      var verificationTypeIds = {};
      var pwdVericationMgr = new SNC.PwdVerificationManager();
      var info = "";
      if (processId == this.OTHER) {
          var infoHeader = gs.getMessage("For your selection,");
          var enrolledVerifications = [];
          var nonEnrolledVerifications = [];
          var otherAppVerifications = new global.PasswordResetScopedUtil().getVerificationsFromOtherApplications();
          var index = 0;
          while (index != otherAppVerifications.size()) {
              var verificationId = otherAppVerifications.get(index);
              var verLabel = pwdVericationMgr.getVerificationLabelByVerificationId(verificationId);
              var verificationTypeId = pwdVericationMgr.getVerificationTypeIdByVerificationId(verificationId);
              verificationIds[verificationId] = verLabel;
              verificationTypeIds[verificationId] = verificationTypeId;
              if (enrollmentMgr.isUserEnrolledByVerificationId(userId, verificationId))
                  enrolledVerifications.push(verLabel);
              else
                  nonEnrolledVerifications.push(verLabel);
              index++;
          }
          info = infoHeader + "\n";
          if (enrolledVerifications.length > 0) {
              var enrolledInfo = gs.getMessage("You have completed {0} enrollment(s):", enrolledVerifications.length + "");
              var enrolledListInfo = enrolledInfo + "\n";
              for (var i = 0; i < enrolledVerifications.length; i++) {
                  enrolledListInfo = enrolledListInfo + "- " + enrolledVerifications[i] + "\n";
              }
              info = info + enrolledListInfo;
          }
          if (nonEnrolledVerifications.length > 0) {
              var nonEnrolledInfo = nonEnrolledVerifications.length > 1 ? gs.getMessage("You have {0} additional enrollment(s) to choose from:", nonEnrolledVerifications.length + "") : gs.getMessage("You have 1 pending enrollment:");
              var nonEnrolledListInfo = nonEnrolledInfo + "\n";
              for (var i = 0; i < nonEnrolledVerifications.length; i++) {
                  nonEnrolledListInfo = nonEnrolledListInfo + "- " + nonEnrolledVerifications[i] + "\n";
              }
              info = info + nonEnrolledListInfo;
          }
          result.info = info;
          result.verificationIds = verificationIds;
          result.verificationTypeIds = verificationTypeIds;
      } else {
          var processMgr = new SNC.PwdProcessManager();
          var process = new SNC.PwdProcess(processId);
          var minVerificationsAtProcess = process.getMinVerifications();
          var infoHeader = process.getLabel() + ",";
          var enrolledVerifications = [];
          var nonEnrolledVerifications = [];
          var autoEnrollVerifications = {};
          var mandatoryVerifications = processMgr.getProcessVerificationIdsByMandatoryFlag(processId, true);
          for (var i = 0; i < mandatoryVerifications.size(); i++) {
              autoEnrollVerifications[mandatoryVerifications.get(i)] = true;
          }
          var nonEnrolledMandatory = 0;
          var verificationList = new SNC.PwdVerificationManager().getVerificationIds();
          var processVerificationGr = new GlideRecord('pwd_map_proc_to_verification');
          processVerificationGr.addQuery('process', processId);
          processVerificationGr.addQuery('verification', 'IN', verificationList);
          processVerificationGr.query();
          while (processVerificationGr.next()) {
              var verificationId = processVerificationGr.getValue('verification');
              autoEnrollVerifications[verificationId] = false;
              var verLabel = pwdVericationMgr.getVerificationLabelByVerificationId(verificationId);
              var verificationTypeId = pwdVericationMgr.getVerificationTypeIdByVerificationId(verificationId);
              verificationIds[verificationId] = verLabel;
              verificationTypeIds[verificationId] = verificationTypeId;
              if (enrollmentMgr.isUserEnrolledByVerificationId(userId, verificationId))
                  enrolledVerifications.push(verLabel);
              else {
                  nonEnrolledVerifications.push(verLabel);
                  if (mandatoryVerifications.indexOf(verificationId) > -1)
                      nonEnrolledMandatory++;
              }
          }
          var autoEnrollMandatoryVerificationsCount = 0;
          for (var key in autoEnrollVerifications) {
              if (autoEnrollVerifications[key])
                  autoEnrollMandatoryVerificationsCount = autoEnrollMandatoryVerificationsCount + 1;
          }
          info = infoHeader + "\n";
          if (enrolledVerifications.length > 0) {
              var enrolledInfo = gs.getMessage("You have completed {0} enrollment(s):", enrolledVerifications.length + "");
              var enrolledListInfo = enrolledInfo + "\n";
              for (var i = 0; i < enrolledVerifications.length; i++) {
                  enrolledListInfo = enrolledListInfo + "- " + enrolledVerifications[i] + "\n";
              }
              info = info + enrolledListInfo;
          }
          if (nonEnrolledVerifications.length > 0) {
              var enrolledVerificationsCount = enrolledVerifications.length + autoEnrollMandatoryVerificationsCount;
              if (nonEnrolledMandatory > 0 || (minVerificationsAtProcess > enrolledVerificationsCount)) {
                  reqVerificationsCount = (nonEnrolledMandatory + enrolledVerificationsCount) < minVerificationsAtProcess ? minVerificationsAtProcess - enrolledVerificationsCount : nonEnrolledMandatory;
                  var minVerificationInfo = gs.getMessage("You must enroll for at least {0} more verification(s) to complete your enrollment", reqVerificationsCount + "");
                  info = info + minVerificationInfo;
              }
          }
          result.info = info;
          result.verificationIds = verificationIds;
          result.verificationTypeIds = verificationTypeIds;
      }
      return JSON.stringify(result);
  },

  //Auth Enrollment
  enableMFAVerification: function(userId, verificationId) {
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      // Enable google auth for pwd reset
      this._updateEnrollmentRecord(userId, this.IN_PROGRESS, verificationId);
      return JSON.stringify({
          "status": this.SUCCESS,
          "message": gs.getMessage("Enabled Multi-factor authentication successfully")
      });
  },
  isMFAEnabledForUser: function(userId, defaultVal, verificationId) {
      var gr = new GlideRecord('pwd_enrollment');
      gr.addQuery('user', userId);
      gr.addQuery('verification', verificationId);
      gr.query();
      if (gr.next()) {
          return gr.getValue("status") != this.INACTIVE; // NOT Inactive
      }
      return defaultVal;
  },
  getMFAContent: function() {
      var contentMap = new Packages.java.util.HashMap();
      var result = {};
      contentMap = SNC.MultifactorAuthUtil.loadPopupContent();
      if (contentMap != null) {
          result = {
              "validated": contentMap.get("validated").toString(),
              "qrCodeURL": contentMap.get("qrCodeURL").toString(),
              "qrCodeText": contentMap.get("qrCodeText").toString(),
              "canDisable": contentMap.get("canDisable").toString(),
              "registrationTime": contentMap.get("registrationTime").toString()
          };
      }
      return JSON.stringify(result);
  },
  resetMFACode: function(userId, verificationId) {
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      this._updateEnrollmentRecord(userId, this.IN_PROGRESS, verificationId);
      SNC.MultifactorAuthUtil.reset(false);
      return this.getMFAContent();
  },
  disableMFA: function(userId, verificationId) {
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      // Disable google auth for pwd reset
      this._updateEnrollmentRecord(userId, this.INACTIVE, verificationId);
      if (!this._isMFAEnabledInUserProfile(userId))
          SNC.MultifactorAuthUtil.reset(true);
      return JSON.stringify({
          "status": this.SUCCESS,
          "message": gs.getMessage("Multi-factor authentication has been disabled.")
      });
  },
  getHTMLContentForQRCode: function(qrCodeURL, qrCodeText) {
      var header = gs.getMessage("Open the app and scan the QR code below to pair your mobile device");
      var textPrefix = gs.getMessage("Or type in: {0}", qrCodeText);
      var hint = "<p>" + header + "</p><img src=\"" + qrCodeURL + "\"/><p>" + textPrefix + "</p>";
      return hint;
  },
  validateAndEnrollMFA: function(input, userId, verificationId) {
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      if (input.length != this.CODE_LENGTH || gs.nil(input)) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("The Authenticator App code that you entered is invalid. Try again.")
          });
      }
      var previouslyEnabled = this._isMFAEnabledInUserProfile(userId);
      var valid = SNC.MultifactorAuthUtil.isResponseValid(input);
      if (valid) {
          // Update pwd_enrollment as a flag of pwd google auth being enabled for the user
          this._updateEnrollmentRecord(userId, this.ACTIVE, verificationId);
      }
      //calling validateResponse will enable the mfa flag if it was previously disabled
      //for password reset we disable it again after validating the resposne
      if (!previouslyEnabled) {
          var userGr = new GlideRecord("sys_user");
          userGr.get(userId);
          userGr.setValue("enable_multifactor_authn", false);
          userGr.update();
      }
      return JSON.stringify({
          "status": valid ? this.SUCCESS : this.ERROR,
          "message": valid ? gs.getMessage("You have successfully enrolled for Authenticator App Verification") : gs.getMessage("The Authenticator App code that you entered is invalid. Try again.")
      });
  },
  _isMFAEnabledInUserProfile: function(userId) {
      var userGr = new GlideRecord("sys_user");
      userGr.get(userId);
      return userGr.getValue("enable_multifactor_authn") == this.ACTIVE;
  },
  _updateEnrollmentRecord: function(userId, status, verificationId) {
  	if (gs.nil(userId) || gs.nil(verificationId))
          return;
      var gr = new GlideRecord('pwd_enrollment');
      gr.addQuery('user', userId);
      gr.addQuery('verification', verificationId);
      gr.query();
      if (gr.next())
          new SNC.PwdEnrollmentManager().updateEnrollment(userId, verificationId, status);
      else
          new SNC.PwdEnrollmentManager().createEnrollment(userId, verificationId, status);
          
      new global.PwdEnrollSnapshotUtil().createOrUpdateEnrollmentSnapshot(verificationId, userId, status);
  },
  _isAuthorized: function(userId) {
      if (userId == gs.getUserID())
          return true;
      return false;
  },
  //Security Questions Enrollment
  isUserEnrolledForSecurityQuestions: function(userId, verificationId) {
      var wasPreviouslyEnrolled = false;
      var enrollmentGr = new GlideRecord("pwd_enrollment");
      enrollmentGr.addQuery("user", userId);
      enrollmentGr.addQuery("verification", verificationId);
      enrollmentGr.query();
      if (enrollmentGr.next()) {
          wasPreviouslyEnrolled = true;
      }
      return wasPreviouslyEnrolled;
  },
  createOrUpdateSecurityQuestions: function(data, mode, userId, verificationId) {
      var parsedData = JSON.parse(data);
      var verificationTypeId = new SNC.PwdVerificationManager().getVerificationTypeIdByVerificationId(verificationId);
      var enrollmentProcessor = new SNC.PwdVerificationType(verificationTypeId).getEnrollmentProcessor();
      var result = this.manager.initializeByScriptNameAndCategory(enrollmentProcessor, "password_reset.extension.enrollment_form_processor");
      if (!this.manager.extensionScriptFound()) {
          gs.error('Cannot find enrollment processor extension: {0}', enrollmentProcessor);
          res.isError = true;
          res.errorMessage = gs.getMessage('Cannot find enrollment processor extension: {0}', enrollmentProcessor);
          return res;
      }
      var dataMap = {
          "can_submit": "true",
          "from_VA": "true"
      };
      var storedAnswerSysIds = this.getEnrolledAnswerIds(userId, verificationId);
      if (mode == this.CREATE_MODE) {
          var i = 1;
          for (var key in parsedData) {
              dataMap["question_" + i] = key;
              dataMap["answer_" + i] = parsedData[key]["answer"];
              dataMap["normalized_answer_" + i] = parsedData[key]["normalizedAnswer"];
              dataMap["changed_" + i] = "true";
              dataMap["stored_sys_id_" + i] = storedAnswerSysIds.size() > 0 ? storedAnswerSysIds.get(i - 1) : "";
              i++;
          }
          dataMap["mode"] = this.CREATE_MODE;
      } else if (mode == this.UPDATE_MODE) {
          var storedQuestions = this.getEnrolledQuestions(userId, verificationId);
          var questionAnswers = {};
          for (var i = 0; i < storedQuestions.length; i++) {
              questionAnswers[storedQuestions[i]] = {
                  "answer": parsedData[storedQuestions[i]] ? parsedData[storedQuestions[i]]["answer"] : "*******************",
                  "normalizedAnswer": parsedData[storedQuestions[i]] ? parsedData[storedQuestions[i]]["normalizedAnswer"] : "*******************",
                  "answerId": storedAnswerSysIds.get(i),
                  "isChanged": parsedData[storedQuestions[i]] ? "true" : "false"
              };
          }
          for (var key in parsedData) {
              if (!questionAnswers[key])
                  questionAnswers[key] = {
                      "answer": parsedData[key]["answer"],
                      "normalizedAnswer": parsedData[key]["normalizedAnswer"],
                      "answerId": "",
                      "isChanged": "true"
                  };
          }
          var i = 1;
          for (var entry in questionAnswers) {
              dataMap["question_" + i] = entry;
              dataMap["answer_" + i] = questionAnswers[entry]["answer"];
              dataMap["normalized_answer_" + i] = questionAnswers[entry]["normalizedAnswer"];
              dataMap["stored_sys_id_" + i] = questionAnswers[entry]["answerId"];
              dataMap["changed_" + i] = questionAnswers[entry]["isChanged"];
              i++;
          }
          dataMap["mode"] = this.UPDATE_MODE;
      }
      dataMap["min_length_for_qa"] = gs.getProperty('password_reset.qa.ans_min_len');
      dataMap["count"] = this.getNumOfQuestionsToEnroll(verificationId);
      dataMap["verification_id"] = verificationId;
      dataMap["mandatory"] = "true";
      return this.manager.createNew(dataMap, verificationId, userId);
  },
  getEnrolledQuestionsAnswerIds: function(userId, verificationId) {
      var questions = this.getEnrolledQuestions(userId, verificationId);
      var answerIds = this.getEnrolledAnswerIds(userId, verificationId);
      var res = {};
      for (var i = 0; i < questions.length; i++) {
          res[questions[i]] = answerIds.get(i);
      }
      return JSON.stringify(res);
  },
  isQAConfigurationChanged: function(userId, verificationId) {
      var storedQuestions = this.getEnrolledQuestions(userId, verificationId);
      if (parseInt(this.getNumOfQuestionsToEnroll(verificationId)) > storedQuestions.length)
          return true;
      return false;
  },
  getEnrolledQuestions: function(userId, verificationId) {
      var questions = [];
      var qaManager = new SNC.PwdQAManager();
      var storedAnswerIds = this.getEnrolledAnswerIds(userId, verificationId);
      for (var i = 0; i < storedAnswerIds.size(); i++) {
          questions.push(qaManager.getStoredQuestionByAnswerId(storedAnswerIds.get(i)));
      }
      return questions;
  },
  getEnrolledQuestionIds: function(userId, verificationId) {
      var questions = {};
      var qaManager = new SNC.PwdQAManager();
      var storedAnswerIds = qaManager.getStoredAnswerIdsByVerificationId(userId, verificationId);
      for (var i = 0; i < storedAnswerIds.size(); i++) {
          var question = qaManager.getStoredQuestionByAnswerId(storedAnswerIds.get(i));
          var gr = new GlideRecord("pwd_question");
          gr.addQuery("question_translated", question);
          gr.query();
          if (gr.next())
              questions[gr.getValue("sys_id")] = question;
      }
      return JSON.stringify(questions);
  },
  getEnrolledAnswerIds: function(userId, verificationId) {
      var QAmanager = new SNC.PwdQAManager();
      return QAmanager.getStoredAnswerIdsByVerificationId(userId, verificationId);
  },
  validateQuestionAnswers: function(answer, newAnswers, enrolledData) {
      var res = {
          "isError": false,
          "errorMessage": ""
      };
      var newAnswer = new PasswordResetUtil().decryptWithKMFModule(answer);
      var MIN_LENGTH = gs.getProperty('password_reset.qa.ans_min_len');
      var trimmedAnswer = newAnswer.trim() + "";
      if (trimmedAnswer == '') {
          res.isError = true;
          res.errorMessage = gs.getMessage("All questions should be answered");
          return JSON.stringify(res);
      } else if (trimmedAnswer.length < MIN_LENGTH) {
          res.isError = true;
          res.errorMessage = gs.getMessage("Answer text must be at least {0} characters", MIN_LENGTH);
          return JSON.stringify(res);
      }
      var enrolledAnswerIds = JSON.parse(enrolledData);
      var data = JSON.parse(newAnswers);
      if (Object.keys(enrolledAnswerIds).length == 0) {
          if (Object.keys(data).length > 0) {
              for (var key in data) {
                  var normalizedAnswer = data[key]["normalizedAnswer"];
                  var isSameAnswer = new SNC.PwdQAManager().compareEncryptedAnswers(trimmedAnswer, normalizedAnswer);
                  if (isSameAnswer) {
                      res.isError = true;
                      res.errorMessage = gs.getMessage("All answers must be unique");
                      return JSON.stringify(res);
                  }
              }
          }
      } else {
          for (var answerId in enrolledAnswerIds) {
              if (!data[answerId]) {
                  var isSameAnswer = new SNC.PwdQAManager().compareAnswers(enrolledAnswerIds[answerId], trimmedAnswer, false);
                  if (isSameAnswer) {
                      res.isError = true;
                      res.errorMessage = gs.getMessage("All answers must be unique");
                      return JSON.stringify(res);
                  }
              }
          }
          for (var key in data) {
              var normalizedAnswer = data[key]["normalizedAnswer"];
              var isSameAnswer = new SNC.PwdQAManager().compareEncryptedAnswers(trimmedAnswer, normalizedAnswer);
              if (isSameAnswer) {
                  res.isError = true;
                  res.errorMessage = gs.getMessage("All answers must be unique");
                  return JSON.stringify(res);
              }
          }
      }
      return JSON.stringify(res);
  },
  getNumOfQuestionsToEnroll: function(verificationid) {
      var pwdVerificationManager = new SNC.PwdVerificationManager();
      return pwdVerificationManager.getVerificationParamValue(verificationid, this.PARM_NUM_ENROLL);
  },
  getSecurityQuestionsExcept: function(selectedQuestions) {
      var selected = JSON.parse(selectedQuestions);
      var options = [];
      var gr = new GlideRecord("pwd_question");
      gr.addQuery("sys_id", "NOT IN", Object.keys(selected));
      gr.query();
      while (gr.next()) {
          options.push({
              'value': gr.getValue("sys_id"),
              'label': gr.getValue("question_translated")
          });
      }
      return options;
  },
  //SoftPin Enrollment
  createOrUpdateSoftPin: function(pin, verificationid, userId) {
      var decryptPin = new PasswordResetUtil().decryptWithKMFModule(pin);
      var verificationTypeId = new SNC.PwdVerificationManager().getVerificationTypeIdByVerificationId(verificationid);
      var enrollmentProcessor = new SNC.PwdVerificationType(verificationTypeId).getEnrollmentProcessor();
      var res = this.manager.initializeByScriptNameAndCategory(enrollmentProcessor, "password_reset.extension.enrollment_form_processor");
      if (!this.manager.extensionScriptFound()) {
          gs.error('Cannot find enrollment processor extension: {0}', enrollmentProcessor);
      }
      var dataMap = {
          'softpin_input': decryptPin,
          'pin_changed': 'true',
          'mandatory': 'true'
      };
      return this.manager.createNew(dataMap, verificationid, userId);
  },
  validateSoftPin: function(pin, verificationId) {
      var decryptPin = new PasswordResetUtil().decryptWithKMFModule(pin);
      var res = {
          "error": false,
          "errorMessage": ""
      };
      var pwdSoftpinMgr = new SNC.PwdSoftPINManager();
      var charRepetitionThreshold = pwdSoftpinMgr.getPINRepetitionThreshold(verificationId);
      if (!charRepetitionThreshold)
          charRepetitionThreshold = gs.getProperty('password_reset.softpin.repetition_threshold', 2);
      var charSequenceThreshold = pwdSoftpinMgr.getPINSequenceThreshold(verificationId);
      if (!charSequenceThreshold)
          charSequenceThreshold = gs.getProperty('password_reset.softpin.sequence_threshold', 2);
      charSequenceThreshold = parseInt(charSequenceThreshold);
      var softPinLength = pwdSoftpinMgr.getPINLength(verificationId);
      softPinLength = parseInt(softPinLength);
      var providedSoftPIN = decryptPin;
      var digitPattern = new RegExp("^\\d{" + softPinLength + "}$");
      if (!digitPattern.test(providedSoftPIN)) {
          res.error = true;
          res.errorMessage = gs.getMessage('Soft PIN must be atleast {0} digits', softPinLength + "");
      }
      var repetitionsPattern = "(\\d+)";
      for (var i = 0; i < charRepetitionThreshold; i++)
          repetitionsPattern += "\\1";
      var repetitionsPatternRegX = new RegExp(repetitionsPattern);
      if (repetitionsPatternRegX.test(providedSoftPIN)) {
          res.error = true;
          res.errorMessage = gs.getMessage("Soft PIN cannot have more than {0} repeated digits/pattern", charRepetitionThreshold + "");
      }
      if (this.checkForSequence(providedSoftPIN, charSequenceThreshold)) {
          res.error = true;
          res.errorMessage = gs.getMessage("Soft PIN cannot have more than {0} digits in sequence", charSequenceThreshold + "");
      }
      return res;
  },
  checkForSequence: function(data, threshold) {
      // Check for sequential numerical characters
      var increasingSequence = '0123456789';
      var decreasingSequence = '9876543210';
      data = data + "";
      for (var i = 0; i < data.length - threshold; i++) {
          var subData = data.substring(i, i + threshold + 1);
          if ((increasingSequence.indexOf(subData) > -1) ||
              (decreasingSequence.indexOf(subData) > -1)) {
              return true;
          }
      }
      return false;
  },
  getSoftPinRules: function(verificationId) {
      var rules = {};
      var pwdSoftpinMgr = new SNC.PwdSoftPINManager();
      var pinLength = pwdSoftpinMgr.getPINLength(verificationId);
      pinLength = parseInt(pinLength);
      var repetitionThreshold = pwdSoftpinMgr.getPINRepetitionThreshold(verificationId);
      if (!repetitionThreshold)
          repetitionThreshold = gs.getProperty('password_reset.softpin.repetition_threshold', 2);
      var sequenceThreshold = pwdSoftpinMgr.getPINSequenceThreshold(verificationId);
      if (!sequenceThreshold)
          sequenceThreshold = gs.getProperty('password_reset.softpin.sequence_threshold', 2);
      rules = {
          "pinLength": pinLength,
          "repetitionThreshold": repetitionThreshold,
          "sequenceThreshold": sequenceThreshold
      };
      return JSON.stringify(rules);
  },

  //SMS Enrollment
  getSMSDevices: function(userId, maskPhoneNumber) {
      var dev = {};
      var gr = new GlideRecord('cmn_notif_device');
      gr.addActiveQuery();
      gr.addQuery('user', userId);
      gr.addQuery('type', this.SMS);
      gr.orderBy('name');
      gr.query();
      while (gr.next()) {
          var isSubscribed = false;
          var grNM = GlideRecord('cmn_notif_message');
          grNM.addQuery('device', gr.getValue('sys_id'));
          grNM.addActiveQuery();
          grNM.query();
          while (grNM.next()) {
              var filter = grNM.getValue('notification_filter'); // unsunscribe = c1bfa4040a0a0b8b001eeb0f3f5ee961
              var notif = grNM.getValue('notification');
              // subscribed, Good goto go
              if (filter == null && notif == this.PWD_MESSAGE)
                  isSubscribed = true;
          }
          var eachDev = {};
          eachDev["name"] = gr.getValue('name');
          var phone_number = gr.getValue('phone_number');
          if (maskPhoneNumber) {
              phone_number = PwdMaskHelper.maskPhone(phone_number);
          }
          eachDev["phone"] = phone_number;
          eachDev["isSubscribed"] = isSubscribed;
          eachDev["sys_id"] = gr.getValue('sys_id');
          // add provider name.
          var prov = new GlideRecord("cmn_notif_service_provider");
          if (prov.get(gr.getValue("service_provider"))) {
              eachDev["provider"] = prov.getValue('name');
          }
          // check if device is verified and add country name and code
          dev.setAttribute("isVerified", false);
          var dvc = GlideRecord('pwd_device');
          dvc.addQuery('device', gr.getUniqueValue());
          dvc.query();
          if (dvc.next()) {
              eachDev["isVerified"] = (dvc.status == 1) ? true : false;
              //set default country code if does no exists
              if (!dvc.country_name) {
                  dvc.setValue('country_name', 'United States');
              }
              if (!dvc.country_code) {
                  dvc.setValue('country_code', '+1');
              }
              dvc.update();
              eachDev["countryCode"] = dvc.country_code;
              eachDev["countryName"] = dvc.country_name;
          }
          dev[gr.getValue('sys_id')] = eachDev;
      }
      return JSON.stringify(dev);
  },
  isNotifyEnabled: function() {
      var resetUtil = new global.PasswordResetUtil();
      return resetUtil.useNotifyInPasswordReset();
  },
  getCountryCodes: function() {
      var countryCodes = {};
      var list = PwdCountryCode.CountryCodeList;
      for (var i = 0; i < list.length; i++) {
          countryCodes[list[i].name] = list[i].code;
      }
      return JSON.stringify(countryCodes);
  },
  _validateSMSDeviceDetails: function(useNotify, deviceName, countryName, countryCode, provider, phoneNumber) {
      var res = {
          "status": this.SUCCESS,
          "message": ""
      };
      if (deviceName == "") {
          res.status = this.ERROR;
          res.message = gs.getMessage("You must specify a valid name to associate with your device");
          return res;
      }
      if (useNotify == 'true' && (countryName == "" || countryCode == "")) {
          res.status = this.ERROR;
          res.message = gs.getMessage("You must specify a country code");
          return res;
      }
      if (phoneNumber == "") {
          res.status = this.ERROR;
          res.message = gs.getMessage("You must specify a phone number");
          return res;
      }
      var phoneBare = phoneNumber.toString();
      phoneBare = phoneBare.replace(/\D/g, ''); // strip non-digits
      var validLengthRegX = new RegExp("^[0-9]{6,14}$");
      var validLength = validLengthRegX.test(phoneBare); // 6 - 14 digits
      //Must end with a digit, can begin with a digit or (, and may contain (, ), digit, - or space
      var validPhoneRegX = /^[\d|\(][\d|\s|\(|\)|-]*[\d]$/;
      var validPhone = validPhoneRegX.test(phoneNumber);
      if (!validPhone || !validLength) {
          res.status = this.ERROR;
          res.message = gs.getMessage("Invalid phone number");
          return res;
      }
      if (useNotify != "true" && provider == "") {
          res.status = this.ERROR;
          res.message = gs.getMessage("You must specify a provider");
          return res;
      }
      return res;
  },
  addNewSMSDevice: function(userId, verificationId, useNotify, deviceName, countryName, countryCode, provider, phoneNumber) {
      var res = this._validateSMSDeviceDetails(useNotify, deviceName, countryName, countryCode, provider, phoneNumber);
      if (res.status == this.ERROR)
          return JSON.stringify(res);
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      var phone = phoneNumber.toString();
      phone = phone.replace(/\D/g, '');
      if (useNotify == "true") {
          return this._addDeviceWithCountryCode(userId, phone, deviceName, countryCode, countryName);
      } else {
          return this._addDeviceWithProvider(userId, phone, deviceName, provider);
      }
  },
  _addDeviceWithCountryCode: function(userId, phone, deviceName, countryCode, countryName) {
      if (this.helper.deviceWithCountryCodeExists(userId, phone, countryCode, this.SMS)) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Device already exists: ({0}) {1}", [countryCode, phone])
          });
      }
      var newDev = this.helper.createDeviceWithCountryCode(userId, phone, countryCode, countryName, deviceName);
      if (newDev == null) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Could not add device: ({0}) {1}", [countryCode, phone])
          });
      } else {
          return JSON.stringify({
              "status": this.SUCCESS,
              "message": gs.getMessage("Added the device successfully.")
          });
      }
  },
  _addDeviceWithProvider: function(userId, phone, name, provider) {
      if (this.helper.deviceExists(userId, phone, provider, this.SMS)) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Device already exists: {0}", phone)
          });
      }
      var newDev = this.helper.createDevice(userId, phone, provider, name);
      if (newDev == null) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Could not add device: {0}", phone)
          });
      } else {
          return JSON.stringify({
              "status": this.SUCCESS,
              "message": gs.getMessage("Added the device successfully.")
          });
      }
  },
  sendEnrollmentCode: function(deviceId, verificationId) {
      var response = new PwdSMSNotificationManager().sendEnrollmentCode(deviceId, verificationId);
      return this._handleResponse(response, verificationId);
  },

  _handleResponse: function(response, verificationId) {
      if (response == this.SUCCESS_RESPONSE) {
          var expiry = new SNC.PwdSMSManager().getExpiryByVerificationId(verificationId);
          var msgKey = "Verification Code Sent Message";
          var responseMsg = gs.getMessage(msgKey, expiry); // I18N_OK 08-04-16
          return JSON.stringify({
              "status": this.SUCCESS,
              "message": responseMsg
          });
      } else {
          var msg;
          switch (response) {
              case this.PER_DAY_LIMIT_RESPONSE:
                  var per_day_limit = this._getVerificationParam(verificationId, "max_per_day", "password_reset.sms.max_per_day");
                  msg = gs.getMessage('Cannot send more than {0} verification codes in a day', [per_day_limit]);
                  break;
              case this.PAUSE_WINDOW_RESPONSE:
                  var pause_window = this._getVerificationParam(verificationId, "pause_window", "password_reset.sms.pause_window");
                  msg = gs.getMessage('You can send a new verification code after {0} minutes', pause_window);
                  break;
              case this.INVALID_REQUEST_RESPONSE:
                  msg = gs.getMessage('Your password reset request is no longer valid. Submit another request.');
                  break;
              case this.INVALID_PHONE_NUMBER:
                  msg = gs.getMessage('Could not deliver the code via SMS text. The number is not a valid phone number (not E.164 compliant).');
                  break;
              default:
                  msg = gs.getMessage('Could not generate a verification code');
          }
          return JSON.stringify({
              "status": this.ERROR,
              "message": msg
          });
      }
  },

  verifyEnrollmentCode: function(userId, deviceId, verificationId, code) {
      var response = new PwdSMSNotificationManager().verifyEnrollmentCode(deviceId, verificationId, code);
      if (response) {
          this.helper.subscribeDevice(deviceId, userId);
          this._updateEnrollmentRecord(userId, this.ACTIVE, verificationId);
          var msg = gs.getMessage('The device has been authorized');
          return JSON.stringify({
              "status": this.SUCCESS,
              "message": msg
          });
      } else {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("The verification code does not match")
          });
      }
  },
  getDeviceId: function(userId, phone) {
      return this.helper.deviceExistsForPhone(userId, phone);
  },
  removeSMSDevice: function(userId, verificationId, deviceId) {
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action.")
          });
      if (this.helper.deleteDevice(deviceId, userId)) {
      	if (!gs.nil(verificationId)) {
              var isEnrolled = new SNC.PwdEnrollmentManager().isUserEnrolledByVerificationId(userId, verificationId);
              if (!isEnrolled) {
              	this._updateEnrollmentRecord(userId, this.INACTIVE, verificationId);
              }
          }
          return JSON.stringify({
              "status": this.SUCCESS,
              "message": gs.getMessage("Deleted your mobile device.")
          });
      } else {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Could not delete device.")
          });
      }
  },
  updateSubscriptionOfDevice: function(userId, verificationId, deviceId, deviceName, isSubscribed) {
      var filter = (isSubscribed.toString() == "true") ? this.UNSUBSCRIBE : '';
      var update = this.helper.updateDeviceSubscription(deviceId, userId, filter);
      if (update == null) {
          if (filter == '')
              return JSON.stringify({
                  "status": this.ERROR,
                  "message": gs.getMessage("Could not authorize device: {0}", deviceName)
              });
          else
              return JSON.stringify({
                  "status": this.ERROR,
                  "message": gs.getMessage("Could not unauthorize device: {0}", deviceName)
              });
      } else {
          if (filter == '') {
          	this._updateEnrollmentRecord(userId, this.ACTIVE, verificationId);
          	return JSON.stringify({
                  "status": this.SUCCESS,
                  "message": gs.getMessage("Device {0} has been authorized successfully", deviceName)
              });
          } else {
          	if (!gs.nil(verificationId)) {
          		var isEnrolled = new SNC.PwdEnrollmentManager().isUserEnrolledByVerificationId(userId, verificationId);
          		if (!isEnrolled) {
          		   this._updateEnrollmentRecord(userId, this.INACTIVE, verificationId);
         		}
      	}	
  		
          	return JSON.stringify({
                  "status": this.SUCCESS,
                  "message": gs.getMessage("Device {0} has been unauthorized", deviceName)
              });
          } 
      }
  },

  //Email Enrollment

  getEmails: function(userId, maskEmailAddr) {
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      var emails = {};
      var userGr = new GlideRecord('sys_user');
      userGr.get(userId);
      var userProfEmail = userGr.getValue('email');
      var gr = new GlideRecord('cmn_notif_device');
      gr.addActiveQuery();
      gr.addQuery('user', userId);
      gr.addQuery('type', this.EMAIL);
      gr.addQuery('email_address', '!=', '');
      gr.orderBy('name');
      gr.query();
      while (gr.next()) {
          var isSubscribed = false;

          var grPD = GlideRecord('cmn_notif_message');
          grPD.addQuery('device', gr.getValue('sys_id'));
          grPD.addQuery('notification_filter', '');
          grPD.addActiveQuery();
          grPD.query();
          if (grPD.next()) {
              isSubscribed = true;
          }

          var eachEmail = {};
          eachEmail["name"] = gr.getValue('name');
          var email_addr = gr.getValue('email_address');
          if (maskEmailAddr) {
              email_addr = PwdMaskHelper.maskEmail(email_addr);
          }
          eachEmail["email"] = email_addr;
          eachEmail["isSubscribed"] = isSubscribed;
          eachEmail["sys_id"] = gr.getValue('sys_id');

          // check if email is verified
          eachEmail["isVerified"] = false;
          var dvc = GlideRecord('pwd_device');
          dvc.addQuery('device', gr.getUniqueValue());
          dvc.query();
          if (dvc.next()) {
              eachEmail["isVerified"] = (dvc.status == 1) ? true : false;
              dvc.update();
          }
          eachEmail["isUserProfileEmail"] = false;
          if (email_addr == userProfEmail)
              eachEmail["isUserProfileEmail"] = true;
          emails[gr.getValue('sys_id')] = eachEmail;
      }
      return JSON.stringify(emails);
  },

  _validateEmailDeviceDetails: function(emailAddress, emailName) {
      var res = {
          "status": this.SUCCESS,
          "message": ""
      };
      if (emailName == "") {
          res.status = this.ERROR;
          res.message = gs.getMessage("You must specify a valid name to associate with the email address");
          return res;
      }
      var validEmailRegX = new RegExp("^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$");
      var validEmail = validEmailRegX.test(emailAddress);
      if (!validEmail) {
          res.status = this.ERROR;
          res.message = gs.getMessage("Invalid Email address");
          return res;
      }
      return res;
  },

  addNewEmailDevice: function(userId, emailAddress, emailName) {
      var res = this._validateEmailDeviceDetails(emailAddress, emailName);
      if (res.status == this.ERROR)
          return JSON.stringify(res);
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      if (this.helper.emailExists(userId, emailAddress, this.EMAIL)) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Email already exists: {0}", emailAddress)
          });
      }

      var newDev = this.helper.createEmail(userId, emailAddress, emailName);
      if (newDev == null) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Could not add the email: {0}", emailAddress)
          });
      } else {
          return JSON.stringify({
              "status": this.SUCCESS,
              "message": gs.getMessage("Added the email successfully.")
          });
      }
  },

  getEmailDeviceId: function(userId, emailAddress) {
      return this.helper.emailEntryExists(userId, emailAddress);
  },

  sendEmailEnrollmentCode: function(deviceId, verificationId) {
      var response = new PwdEmailNotificationManager().sendEnrollmentCode(deviceId, verificationId);
      return this._handleResponse(response, verificationId);
  },

  verifyEmailEnrollmentCode: function(userId, deviceId, verificationId, code) {
      var response = new PwdEmailNotificationManager().verifyEnrollmentCode(deviceId, verificationId, code);
      if (response) {
          this.helper.subscribeDevice(deviceId, userId);
          this._updateEnrollmentRecord(userId, this.ACTIVE, verificationId);
          var msg = gs.getMessage('The email has been authorized');
          return JSON.stringify({
              "status": this.SUCCESS,
              "message": msg
          });
      } else {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("The verification code does not match")
          });
      }
  },

  addPrimaryEmail: function(userId, verificationId) {
      if (!this._isAuthorized(userId))
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("You are not authorized to perform that action")
          });
      var userGr = new GlideRecord('sys_user');
      userGr.get(userId);
      var email = userGr.getValue('email');
      // If the email already exists it may just be inactive. Either activate it or throw an error 
      if (this.helper.emailExists(userId, email, this.EMAIL)) {
          var dev = new GlideRecord('cmn_notif_device');
          dev.addQuery('user', userId);
          dev.addQuery('type', 'Email');
          dev.addQuery('email_address', email);
          dev.addQuery('active', false);
          dev.query();

          if (dev.next()) {
              dev.setValue('active', true);
              dev.update();
              this._updateEnrollmentRecord(userId, this.ACTIVE, verificationId);
              return JSON.stringify({
                  "status": this.SUCCESS,
                  "message": gs.getMessage("Added the email from your user profile.")
              });
          } else { // This shouldn't happen 			
              return JSON.stringify({
                  "status": this.ERROR,
                  "message": gs.getMessage("Email already exists: {0}", email)
              });
          }
      }
      var name = gs.getMessage('User Profile Email');
      var newDev = this.helper.createEmail(userId, email, name);
      if (newDev == null) {
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Could not add the email: {0}", email)
          });
      }
      // Usually the pwd_device is created when enrollment code is generated, but we're bypassing that step so do it here
      var devGr = new GlideRecord('pwd_device');
      devGr.setValue('status', '1'); // verified
      devGr.setValue('device', newDev);
      devGr.insert();

      var update = this.helper.updateDeviceSubscription(newDev, userId, '');
      if (update == null)
          return JSON.stringify({
              "status": this.ERROR,
              "message": gs.getMessage("Could not add the email")
          });
      else {
      	this._updateEnrollmentRecord(userId, this.ACTIVE, verificationId);
      	return JSON.stringify({
              "status": this.SUCCESS,
              "message": gs.getMessage("Added the email from your user profile.")
          });
      }
  },

  _getVerificationParam: function(verificationId, parameter, property) {
      var value = new SNC.PwdVerificationManager().getVerificationParamValue(verificationId, parameter);
      if (value == null) {
          value = GlideProperties.get(property, '0');
      }
      return value;
  },

  type: 'PwdVAEnrollmentManager'
};

Sys ID

91e1dd4c439e11102695d6085bb8f26f

Offical Documentation

Official Docs: