<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var userAgent = require('../userAgent')
var ONE_HOUR = 60 * 60 * 1000

// Browsers ask for 6 hour duration on certificate sign,
// so don't bother updating device info any more frequently than that.
var TOKEN_FRESHNESS_THRESHOLD = 6 * ONE_HOUR

module.exports = function (log, inherits, Token) {

  function SessionToken(keys, details) {
    Token.call(this, keys, details)
    this.forceUpdate(details)
    this.email = details.email || null
    this.emailCode = details.emailCode || null
    this.emailVerified = !!details.emailVerified
    this.verifierSetAt = details.verifierSetAt
    this.locale = details.locale || null
    this.accountCreatedAt = details.createdAt
  }
  inherits(SessionToken, Token)

  SessionToken.tokenTypeID = 'sessionToken'

  SessionToken.create = function (details) {
    log.trace({ op: 'SessionToken.create', uid: details && details.uid })
    return Token.createNewToken(SessionToken, details || {})
  }

  SessionToken.fromHex = function (string, details) {
    log.trace({ op: 'SessionToken.fromHex' })
    return Token.createTokenFromHexData(SessionToken, string, details || {})
  }

  SessionToken.prototype.lastAuthAt = function () {
    return Math.floor(this.createdAt / 1000)
  }

  // Parse the user agent string, then check the result to see whether
  // the session token needs updating.
  //
  // If the session token has not changed, allowing up to an hour of
  // leeway for lastAccessTime, return false.
  //
  // Otherwise, update properties on this then return true.
  //
  // It is the caller's responsibility to update the database.
  SessionToken.prototype.update = function (userAgentString) {
    log.trace({ op: 'SessionToken.update', uid: this.uid })

    var freshData = userAgent.call({
      lastAccessTime: Date.now()
    }, userAgentString)

    if (this.isFresh(freshData)) {
      return false
    }

    this.forceUpdate(freshData)

    return true
  }

  SessionToken.prototype.isFresh = function (freshData) {
    var result = this.uaBrowser === freshData.uaBrowser &&
      this.uaBrowserVersion === freshData.uaBrowserVersion &&
      this.uaOS === freshData.uaOS &&
      this.uaOSVersion === freshData.uaOSVersion &&
      this.uaDeviceType === freshData.uaDeviceType &&
      this.lastAccessTime + TOKEN_FRESHNESS_THRESHOLD > freshData.lastAccessTime

    log.info({
      op: 'SessionToken.isFresh',
      uid: this.uid,
      tokenAge: freshData.lastAccessTime - this.lastAccessTime,
      fresh: result
    })

    return result
  }

  SessionToken.prototype.forceUpdate = function (data) {
    this.uaBrowser = data.uaBrowser
    this.uaBrowserVersion = data.uaBrowserVersion
    this.uaOS = data.uaOS
    this.uaOSVersion = data.uaOSVersion
    this.uaDeviceType = data.uaDeviceType
    this.lastAccessTime = data.lastAccessTime
  }

  return SessionToken
}
<commit_msg>Export constants from session_token.js for use in the tests.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var userAgent = require('../userAgent')
var ONE_HOUR = 60 * 60 * 1000

// Browsers ask for 6 hour duration on certificate sign,
// so don't bother updating device info any more frequently than that.
var TOKEN_FRESHNESS_THRESHOLD = 6 * ONE_HOUR

module.exports = function (log, inherits, Token) {

  function SessionToken(keys, details) {
    Token.call(this, keys, details)
    this.forceUpdate(details)
    this.email = details.email || null
    this.emailCode = details.emailCode || null
    this.emailVerified = !!details.emailVerified
    this.verifierSetAt = details.verifierSetAt
    this.locale = details.locale || null
    this.accountCreatedAt = details.createdAt
  }
  inherits(SessionToken, Token)

  SessionToken.tokenTypeID = 'sessionToken'

  SessionToken.create = function (details) {
    log.trace({ op: 'SessionToken.create', uid: details && details.uid })
    return Token.createNewToken(SessionToken, details || {})
  }

  SessionToken.fromHex = function (string, details) {
    log.trace({ op: 'SessionToken.fromHex' })
    return Token.createTokenFromHexData(SessionToken, string, details || {})
  }

  SessionToken.prototype.lastAuthAt = function () {
    return Math.floor(this.createdAt / 1000)
  }

  // Parse the user agent string, then check the result to see whether
  // the session token needs updating.
  //
  // If the session token has not changed, allowing up to an hour of
  // leeway for lastAccessTime, return false.
  //
  // Otherwise, update properties on this then return true.
  //
  // It is the caller's responsibility to update the database.
  SessionToken.prototype.update = function (userAgentString) {
    log.trace({ op: 'SessionToken.update', uid: this.uid })

    var freshData = userAgent.call({
      lastAccessTime: Date.now()
    }, userAgentString)

    if (this.isFresh(freshData)) {
      return false
    }

    this.forceUpdate(freshData)

    return true
  }

  SessionToken.prototype.isFresh = function (freshData) {
    var result = this.uaBrowser === freshData.uaBrowser &&
      this.uaBrowserVersion === freshData.uaBrowserVersion &&
      this.uaOS === freshData.uaOS &&
      this.uaOSVersion === freshData.uaOSVersion &&
      this.uaDeviceType === freshData.uaDeviceType &&
      this.lastAccessTime + TOKEN_FRESHNESS_THRESHOLD > freshData.lastAccessTime

    log.info({
      op: 'SessionToken.isFresh',
      uid: this.uid,
      tokenAge: freshData.lastAccessTime - this.lastAccessTime,
      fresh: result
    })

    return result
  }

  SessionToken.prototype.forceUpdate = function (data) {
    this.uaBrowser = data.uaBrowser
    this.uaBrowserVersion = data.uaBrowserVersion
    this.uaOS = data.uaOS
    this.uaOSVersion = data.uaOSVersion
    this.uaDeviceType = data.uaDeviceType
    this.lastAccessTime = data.lastAccessTime
  }

  return SessionToken
}

// For use by the tests.
module.exports.TOKEN_FRESHNESS_THRESHOLD = TOKEN_FRESHNESS_THRESHOLD

{'fraction_non_alphanumeric': 0.10315186246418338, 'fraction_numerical': 0.0057306590257879654, 'mean_word_length': 3.357142857142857, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '4122282', 'n_tokens_mistral': 1930, 'n_tokens_neox': 1839, 'n_words': 507}