ubjs/packages/base/UBSession.js

/**
 * Internal class, returned as a result of {@link UBConnection#authorize UBConnection.authorize()}
 * @module @unitybase/base/UBSession
 */
/*
 @author pavel.mash
 */

/* global ncrc32 */

// ***********   !!!!WARNING!!!!! **********************
// Module shared between server and client code
/**
 * Internal class, returned as a result of {@link UBConnection#authorize UBConnection.authorize()}
 * The main method is {@link UBSession.signature UBSession.signature()}
 *
 * Developer never create this class directly.
 * @class
 * @protected
 */
function UBSession (authResponse, secretWord, authSchema) {
  let data = authResponse
  let hexa8ID = hexa8(data.result.split('+')[ 0 ])
  let userData = data.uData ? JSON.parse(data.uData) : { lang: 'en', login: 'anonymous' }
  let sessionWord = data.result
  let sessionPwdHash = secretWord || ''
  let sessionSaltCRC = (typeof ncrc32 !== 'undefined') ? ncrc32(0, sessionWord + sessionPwdHash) : null

  if (!userData.login) {
    userData.login = data.logonname
  }

  /** @property {String} sessionID user session id converted to {@link UBSession#hexa8}
   * @protected
   * @readonly
   */
  Object.defineProperty(this, 'sessionID', {enumerable: true, writable: false, value: hexa8ID})
  /**
   * User logon name. Better to access this value using {@link UBConnection#userLogin UBConnection.userLogin()} method.
   * @type {String}
   * @private
   * @readonly
   */
  this.logonname = data.logonname

  /** Contain custom user data. Usually filled inside **server** `onUserLogon` event handlers
   *
   * Do not use it directly, instead use helper method {@link UBConnection#userData UBConnection.userData()} instead.
   *
   * @type {Object}
   * @protected
   * @readonly
   */
  this.userData = userData

  /**
   * Name of authentication schema
   * @type {String}
   * @protected
   * @readonly
   */
  this.authSchema = authSchema || 'UB'

  /**
   * Session signature for authorized request. Can be added as LAST parameter in url, or to Authorization header (preferred way)
   *
   *      $App.connection.authorize().then(function(session){
   *          // for URL
   *          return 'session_signature=' + session.signature()
   *          //for header
   *          return {Authorization: session.authSchema + ' ' + session.signature()}
   *      });
   *
   * @returns {string}
   */
  this.signature = function () {
    switch (this.authSchema) {
      case 'None':
        return ''
      case 'UBIP':
        return this.logonname
      default:
        let timeStampI = Math.floor((new Date()).getTime() / 1000)
        let hexaTime = hexa8(timeStampI)
        return hexa8ID + hexaTime + hexa8((typeof ncrc32 !== 'undefined') ? ncrc32(sessionSaltCRC, hexaTime) : crc32(sessionWord + sessionPwdHash + hexaTime)) // + url?
    }
  }

  /**
   * Current session is anonymous session
   * @returns {boolean}
   */
  this.isAnonymous = function () {
    return (this.authSchema === 'None')
  }

  /**
   * Return authorization header
   *
   *      $App.connection.authorize().then(function(session){
   *          return {Authorization: session.authHeader()}
   *      });
   *
   * @returns {string}
   */
  this.authHeader = function () {
    return this.isAnonymous() ? '' : ((this.authSchema === 'Negotiate' ? 'UB' : this.authSchema) + ' ' + this.signature())
  }
}

/**
 * Return hexadecimal string of 8 character length from value
 * @param {String|Number} value
 * @returns {String}
 */
UBSession.prototype.hexa8 = function hexa8 (value) {
  let num = parseInt(value, 10)
  let res = isNaN(num) ? '00000000' : num.toString(16)
  while (res.length < 8) {
    res = '0' + res
  }
  return res
}
const hexa8 = UBSession.prototype.hexa8

const CRC32_POLYTABLES = {}
/* jslint bitwise: true */
/**
 * Calculate CRC32 checksum for string
 * @param {String} s string to calculate CRC32
 * @param {Number} [polynomial] polynomial basis. default to 0x04C11DB7
 * @param {Number} [initialValue] initial crc value. default to 0xFFFFFFFF
 * @param {Number} [finalXORValue] default to 0xFFFFFFFF
 * @returns {Number}
 */
UBSession.prototype.crc32 = function crc32 (s, polynomial, initialValue, finalXORValue) {
  s = String(s)
  polynomial = polynomial || 0x04C11DB7
  initialValue = initialValue || 0xFFFFFFFF
  finalXORValue = finalXORValue || 0xFFFFFFFF
  let crc = initialValue

  let table = CRC32_POLYTABLES[polynomial]
  if (!table) {
    table = CRC32_POLYTABLES[polynomial] = (function build () {
      let i, j, c
      let table = []
      let reverse = function (x, n) {
        let b = 0
        while (n) {
          b = b * 2 + x % 2
          x /= 2
          x -= x % 1
          n--
        }
        return b
      }
      for (i = 255; i >= 0; i--) {
        c = reverse(i, 32)

        for (j = 0; j < 8; j++) {
          c = ((c * 2) ^ (((c >>> 31) % 2) * polynomial)) >>> 0
        }

        table[i] = reverse(c, 32)
      }
      return table
    })()
  }

  for (let i = 0, l = s.length; i < l; i++) {
    let c = s.charCodeAt(i)
    if (c > 255) {
      throw new RangeError()
    }
    let j = (crc % 256) ^ c
    crc = ((crc / 256) ^ table[j]) >>> 0
  }
  return (crc ^ finalXORValue) >>> 0
}
const crc32 = UBSession.prototype.crc32

module.exports = UBSession