/**
* Exports UBSession class, returned as a result of [UB|Sync]Connection.authorize
*
* @module UBSession
* @memberOf module:@unitybase/cs-shared
* @author pavel.mash
*/
module.exports = UBSession
/* global ncrc32 */
// *********** !!!!WARNING!!!!! **********************
// Module shared between server and client code
/**
* Internal class, returned as a result of [UB|Sync]Connection.authorize()
* The main method is {@link UBSession.signature UBSession.signature()}
*
* Developer never create this class directly
*
* @class
* @protected
*/
function UBSession (authResponse, secretWord, authSchema) {
const data = authResponse
const hexa8ID = hexa8(data.result.split('+')[0])
const userData = data.uData ? JSON.parse(data.uData) : { lang: 'en', login: 'anonymous' }
const sessionWord = data.result
const secret = secretWord || ''
const sessionSaltCRC = (typeof ncrc32 !== 'undefined') ? ncrc32(0, sessionWord + secret) : 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 SyncConnection#userLogin SyncConnection.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 SyncConnection#userData SyncConnection.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)
*
* @example
$App.connection.authorize().then(function(session) {
// for URL
return 'session_signature=' + session.signature()
//for header
return { Authorization: session.authSchema + ' ' + session.signature() }
})
* @param {boolean} [authMock]
* @returns {string}
*/
this.signature = function (authMock) {
let hexaTime
switch (this.authSchema) {
case 'None':
return ''
case 'UBIP':
return this.logonname
case 'ROOT':
return process.rootOTP()
default:
hexaTime = hexa8(authMock ? 1 : Math.floor(Date.now() / 1000))
return authMock
? hexa8ID + hexaTime + hexa8(1)
: hexa8ID + hexaTime + hexa8((typeof ncrc32 !== 'undefined') ? ncrc32(sessionSaltCRC, hexaTime) : crc32(sessionWord + secret + hexaTime)) // + url?
}
}
/**
* Current session is anonymous session
*
* @returns {boolean}
*/
this.isAnonymous = function () {
return (this.authSchema === 'None')
}
/**
* Return authorization header
*
* @example
$App.connection.authorize().then(function(session){
return {Authorization: session.authHeader()}
})
* @param {boolean} [authMock=false]
* @returns {string}
*/
this.authHeader = function (authMock) {
return this.isAnonymous() ? '' : ((this.authSchema === 'Negotiate' ? 'UB' : this.authSchema) + ' ' + this.signature(authMock))
}
}
/**
* Return hexadecimal string of 8 character length from value
*
* @param {string|number} value
* @returns {string}
*/
UBSession.prototype.hexa8 = function hexa8 (value) {
const num = parseInt(value, 10)
return isNaN(num) ? '00000000' : num.toString(16).padStart(8, '0')
}
const hexa8 = UBSession.prototype.hexa8
const CRC32_POLYTABLES = {}
let TE // TextEncoder instance
/* jslint bitwise: true */
/**
* Calculate CRC32 checksum for UTF8 string representation
*
* @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) {
polynomial = polynomial || 0x04C11DB7
initialValue = initialValue || 0xFFFFFFFF
finalXORValue = finalXORValue || 0xFFFFFFFF
let crc = initialValue
let table = CRC32_POLYTABLES[polynomial]
if (!table) {
TE = new TextEncoder()
table = CRC32_POLYTABLES[polynomial] = (function build () {
let i, j, c
const table = []
const 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
})()
}
// allow non english chars by encoding passed string to UTF8
const utf8Arr = TE.encode(s)
for (let i = 0, L = utf8Arr.length; i < L; i++) {
const c = utf8Arr[i]
const j = (crc % 256) ^ c
crc = ((crc / 256) ^ table[j]) >>> 0
}
return (crc ^ finalXORValue) >>> 0
}
const crc32 = UBSession.prototype.crc32