<commit_before>'use strict';
var utils = require('../utils');
var InstanceResource = require('./InstanceResource');
var AuthRequestParser = require('../authc/AuthRequestParser');
var ApiKeyEncryptedOptions = require('../authc/ApiKeyEncryptedOptions');
var BasicApiAuthenticator = require('../authc/BasicApiAuthenticator');
var ApiAuthRequestError = require('../error/ApiAuthRequestError');
var OAuthBasicExchangeAuthenticator = require('../authc/OAuthBasicExchangeAuthenticator');
var OauthAccessTokenAuthenticator = require('../authc/OauthAccessTokenAuthenticator');
var AuthenticationResult = require('./AuthenticationResult');
var Account = require('./Account');
var errorMessages = require('../error/messages');

var uuid = require('node-uuid');
var url = require('url');
var jwt = require('jwt-simple');

function Application() {
  Application.super_.apply(this, arguments);
}
utils.inherits(Application, InstanceResource);

Application.prototype.createIdSiteUrl = function createIdSiteUrl(_options) {
  var self = this;
  var options = typeof _options === "object" ? _options : {};
  var p = url.parse(self.href);
  var base = p.protocol + '//' + p.host;
  var apiKey = self.dataStore.requestExecutor.options.apiKey;
  var nonce = uuid();
  var state = options.state || '';

  if(!options.callbackUri){
    throw new Error(errorMessages.ID_SITE_INVALID_CB_URI);
  }

  var token = jwt.encode({
    jti: nonce,
    iat: new Date().getTime()/1000,
    iss: apiKey.id,
    sub: self.href,
    state: state,
    path: options.path || '/',
    cb_uri: options.callbackUri
  },apiKey.secret,'HS256');

  var redirectUrl = base + '/sso'+(options.logout?'/logout':'')+'?jwtRequest=' + token;

  return redirectUrl;
};

Application.prototype._decodeJwt = function _decodeJwt(str,secret){
  var jwtObject;
  try{
    jwtObject = jwt.decode(str, secret);
    return jwtObject;
  }
  catch(e){
    return e;
  }
};

Application.prototype.handleIdSiteCallback = function handleIdSiteCallback(responseUri,callback) {
  var self = this;

  if(typeof responseUri !== 'string'){
    throw new Error('handleIdSiteCallback must be called with an uri string');
  }

  var cb = typeof callback === 'function' ? callback : utils.noop;

  var params = (url.parse(responseUri,true).query) || {};
  var token = params.jwtResponse || '';
  var secret = self.dataStore.requestExecutor.options.apiKey.secret;
  var apiKeyId = self.dataStore.requestExecutor.options.apiKey.id;

  var jwtObjectDecodedValue = self._decodeJwt(token,secret);

  if(jwtObjectDecodedValue instanceof Error){
    return cb(jwtObjectDecodedValue);
  }

  if(jwtObjectDecodedValue.aud !== apiKeyId){
    return cb(new Error(errorMessages.ID_SITE_JWT_INVALID_AUD));
  }

  if(!utils.isNumber(jwtObjectDecodedValue.exp) || (utils.nowEpochSeconds() > jwtObjectDecodedValue.exp)){
    return cb(new Error(errorMessages.ID_SITE_JWT_HAS_EXPIRED));
  }

  var nonce = jwtObjectDecodedValue.irt;
  var accountHref = jwtObjectDecodedValue.sub;

  self.dataStore.nonceStore.getNonce(nonce,function(err,value){
    if(err){
      cb(err);
    }else if(value){
      cb(new Error(errorMessages.ID_SITE_JWT_ALREADY_USED));
    }else{
      self.dataStore.nonceStore.putNonce(nonce,utils.noop);

      self.dataStore.getResource(accountHref,Account,function(err,account){
        if(err){
          cb(err);
        }else{
          cb(null,{
            account: account,
            state: jwtObjectDecodedValue.state,
            isNew: jwtObjectDecodedValue.isNewSub,
            status: jwtObjectDecodedValue.status
          });
        }
      });
    }
  });
};

Application.prototype.authenticateAccount = function authenticateApplicationAccount(authcRequest, callback) {
  var _this = this,
    username = authcRequest.username,
    password = <PASSWORD>,
    type = authcRequest.type || 'basic';
  var accountStore = ('string' === typeof authcRequest.accountStore) ?
    {href: authcRequest.accountStore} :
    authcRequest.accountStore;

  var loginAttempt = {
    type: type,
    value: utils.base64.encode(username + ":" + password)
  };

  if (authcRequest.accountStore){
    loginAttempt.accountStore = accountStore;
  }

  _this.dataStore.createResource(
    _this.loginAttempts.href,
    {expand: 'account'},
    loginAttempt,
    AuthenticationResult,
    function(err,authenticationResult){
      if(err){
        callback(err);
      }
      else{
        authenticationResult.application = _this;
        callback(null,authenticationResult);
      }
  });
};

Application.prototype.sendPasswordResetEmail = function sendApplicationPasswordResetEmail(emailOrUsername, callback) {
  var self = this;
  return self.dataStore.createResource(self.passwordResetTokens.href, {email: emailOrUsername}, callback);
};

Application.prototype.resendVerificationEmail = function resendVerificationEmail(/* [options,] callback */) {
  var self = this;
  var args = Array.prototype.slice.call(arguments);
  var callback = args.pop();
  var options = (args.length > 0) ? args.shift() : null;
  return self.dataStore.createResource(self.verificationEmails.href, options, callback);
};

Application.prototype.verifyPasswordResetToken = function verifyApplicationPasswordResetToken(token, callback) {
  var self = this;
  var href = self.passwordResetTokens.href + "/" + token;
  return self.dataStore.getResource(href, callback);
};

Application.prototype.resetPassword = function resetApplicationPassword(token, password, callback) {
  var self = this;
  var href = self.passwordResetTokens.href + "/" + token;
  return self.dataStore.createResource(href, {expand: 'account'}, { password: password }, callback);
};

Application.prototype.getAccounts = function getApplicationAccounts(/* [options,] callback */) {
  var self = this;
  var args = Array.prototype.slice.call(arguments);
  var callback = args.pop();
  var options = (args.length > 0) ? args.shift() : null;
<commit_msg>accept account store option for password reset<commit_after>'use strict';
var utils = require('../utils');
var InstanceResource = require('./InstanceResource');
var AuthRequestParser = require('../authc/AuthRequestParser');
var ApiKeyEncryptedOptions = require('../authc/ApiKeyEncryptedOptions');
var BasicApiAuthenticator = require('../authc/BasicApiAuthenticator');
var ApiAuthRequestError = require('../error/ApiAuthRequestError');
var OAuthBasicExchangeAuthenticator = require('../authc/OAuthBasicExchangeAuthenticator');
var OauthAccessTokenAuthenticator = require('../authc/OauthAccessTokenAuthenticator');
var AuthenticationResult = require('./AuthenticationResult');
var Account = require('./Account');
var errorMessages = require('../error/messages');

var uuid = require('node-uuid');
var url = require('url');
var jwt = require('jwt-simple');

function Application() {
  Application.super_.apply(this, arguments);
}
utils.inherits(Application, InstanceResource);

Application.prototype.createIdSiteUrl = function createIdSiteUrl(_options) {
  var self = this;
  var options = typeof _options === "object" ? _options : {};
  var p = url.parse(self.href);
  var base = p.protocol + '//' + p.host;
  var apiKey = self.dataStore.requestExecutor.options.apiKey;
  var nonce = uuid();
  var state = options.state || '';

  if(!options.callbackUri){
    throw new Error(errorMessages.ID_SITE_INVALID_CB_URI);
  }

  var token = jwt.encode({
    jti: nonce,
    iat: new Date().getTime()/1000,
    iss: apiKey.id,
    sub: self.href,
    state: state,
    path: options.path || '/',
    cb_uri: options.callbackUri
  },apiKey.secret,'HS256');

  var redirectUrl = base + '/sso'+(options.logout?'/logout':'')+'?jwtRequest=' + token;

  return redirectUrl;
};

Application.prototype._decodeJwt = function _decodeJwt(str,secret){
  var jwtObject;
  try{
    jwtObject = jwt.decode(str, secret);
    return jwtObject;
  }
  catch(e){
    return e;
  }
};

Application.prototype.handleIdSiteCallback = function handleIdSiteCallback(responseUri,callback) {
  var self = this;

  if(typeof responseUri !== 'string'){
    throw new Error('handleIdSiteCallback must be called with an uri string');
  }

  var cb = typeof callback === 'function' ? callback : utils.noop;

  var params = (url.parse(responseUri,true).query) || {};
  var token = params.jwtResponse || '';
  var secret = self.dataStore.requestExecutor.options.apiKey.secret;
  var apiKeyId = self.dataStore.requestExecutor.options.apiKey.id;

  var jwtObjectDecodedValue = self._decodeJwt(token,secret);

  if(jwtObjectDecodedValue instanceof Error){
    return cb(jwtObjectDecodedValue);
  }

  if(jwtObjectDecodedValue.aud !== apiKeyId){
    return cb(new Error(errorMessages.ID_SITE_JWT_INVALID_AUD));
  }

  if(!utils.isNumber(jwtObjectDecodedValue.exp) || (utils.nowEpochSeconds() > jwtObjectDecodedValue.exp)){
    return cb(new Error(errorMessages.ID_SITE_JWT_HAS_EXPIRED));
  }

  var nonce = jwtObjectDecodedValue.irt;
  var accountHref = jwtObjectDecodedValue.sub;

  self.dataStore.nonceStore.getNonce(nonce,function(err,value){
    if(err){
      cb(err);
    }else if(value){
      cb(new Error(errorMessages.ID_SITE_JWT_ALREADY_USED));
    }else{
      self.dataStore.nonceStore.putNonce(nonce,utils.noop);

      self.dataStore.getResource(accountHref,Account,function(err,account){
        if(err){
          cb(err);
        }else{
          cb(null,{
            account: account,
            state: jwtObjectDecodedValue.state,
            isNew: jwtObjectDecodedValue.isNewSub,
            status: jwtObjectDecodedValue.status
          });
        }
      });
    }
  });
};

Application.prototype.authenticateAccount = function authenticateApplicationAccount(authcRequest, callback) {
  var _this = this,
    username = authcRequest.username,
    password = <PASSWORD>,
    type = authcRequest.type || 'basic';
  var accountStore = ('string' === typeof authcRequest.accountStore) ?
    {href: authcRequest.accountStore} :
    authcRequest.accountStore;

  var loginAttempt = {
    type: type,
    value: utils.base64.encode(username + ":" + password)
  };

  if (authcRequest.accountStore){
    loginAttempt.accountStore = accountStore;
  }

  _this.dataStore.createResource(
    _this.loginAttempts.href,
    {expand: 'account'},
    loginAttempt,
    AuthenticationResult,
    function(err,authenticationResult){
      if(err){
        callback(err);
      }
      else{
        authenticationResult.application = _this;
        callback(null,authenticationResult);
      }
  });
};

Application.prototype.sendPasswordResetEmail = function sendApplicationPasswordResetEmail(emailOrUsernameOrOptions, callback) {
  var self = this;
  var options = typeof emailOrUsernameOrOptions === 'string' ? {
    email: emailOrUsernameOrOptions
  } : emailOrUsernameOrOptions;
  return self.dataStore.createResource(self.passwordResetTokens.href, options, callback);
};

Application.prototype.resendVerificationEmail = function resendVerificationEmail(/* [options,] callback */) {
  var self = this;
  var args = Array.prototype.slice.call(arguments);
  var callback = args.pop();
  var options = (args.length > 0) ? args.shift() : null;
  return self.dataStore.createResource(self.verificationEmails.href, options, callback);
};

Application.prototype.verifyPasswordResetToken = function verifyApplicationPasswordResetToken(token, callback) {
  var self = this;
  var href = self.passwordResetTokens.href + "/" + token;
  return self.dataStore.getResource(href, callback);
};

Application.prototype.resetPassword = function resetApplicationPassword(token, password, callback) {
  var self = this;
  var href = self.passwordResetTokens.href + "/" + token;
  return self.dataStore.createResource(href, {expand: 'account'}, { password: password }, callback);
};

Application.prototype.getAccounts = function getApplicationAccounts(/* [options,] callback */) {
  var self = this;
  var args = Array.prototype.slice.call(arguments);
  var callback = args.pop();
  var options = (args.length > 0) ? args.shift() : null;

{'fraction_non_alphanumeric': 0.12966355762824783, 'fraction_numerical': 0.0018321119253830779, 'mean_word_length': 4.290308370044053, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4768501', 'n_tokens_mistral': 3662, 'n_tokens_neox': 3605, 'n_words': 697}