Members
punycode: Object
The punycode
object.
sleep
Block thread for a specified number of milliseconds
console: Console
Put something to log with log levels depending on method
TubLoadContentBody
constant
Properties:
punycode
object.
Name | Type | Description |
---|---|---|
Default |
||
Yes |
||
No |
TubAttrDataType: Number readonly
Properties:
Name | Type | Description |
---|---|---|
Unknown |
Number | |
String |
Number | |
Int |
Number | |
BigInt |
Number | |
Float |
Number | |
Currency |
Number | |
Boolean |
Number | |
DateTime |
Number | |
Text |
Number | |
ID |
Number | |
Entity |
Number | |
Document |
Number | |
Many |
Number | |
TimeLog |
Number | |
Enum |
Number | |
BLOB |
Number | |
Date |
Number |
TubCacheType: Number readonly
Properties:
Name | Type | Description |
---|---|---|
None |
Number | |
SessionEntity |
Number | |
Entity |
Number | |
Session |
Number |
TubSQLDialect: Number readonly
Properties:
Name | Type | Description |
---|---|---|
AnsiSQL |
Number | |
Oracle |
Number | |
Oracle9 |
Number | |
Oracle10 |
Number | |
Oracle11 |
Number | |
MSSQL |
Number | |
MSSQL2008 |
Number | |
MSSQL2012 |
Number | |
SQLite3 |
Number | |
PostgreSQL |
Number | |
Firebird |
Number |
TubEntityDataSourceType: Number readonly
Properties:
Name | Type | Description |
---|---|---|
Normal |
Number | |
External |
Number | |
System |
Number | |
Virtual |
Number |
TubSQLExpressionType: Number readonly
Properties:
Name | Type | Description |
---|---|---|
Field |
Number | |
Expression |
Number |
_
models: App
models
endpoint. Responsible for return a static files content from a model publicPath folders
For example request GET http://host:port/models/modelName/fileName
will return a file from a public folder of a model modelName
Methods
spawn()
MPV - Fake implementation of nodejs child_process
Throw error on spawn
require(moduleName) → *
Load a module. Acts like a Node JS require, with 1 difference:
in case we run in production mode (!process.isDebug
) and minimized version of main module exists, it will be loaded.
By "minimized version" we mean package.json main
entry with .min.js
extension
In case you need to debug from there module is loaded set OS Environment variable
>SET NODE_DEBUG=modules
and restart server - require will put to debug log all information about how module are loaded. Do not do this on production, of course :)
Arguments:
-
moduleName
(String)
setImmediate(callback, …arg) → Number
This function is to be compatible with node.js
Arguments:
-
callback
(function)
-
...arg
(*)
removeCommentsFromJSON(JSONString) → String
Remove comments from JSON string (actually replace all comment content with ' ')
Arguments:
-
JSONString
(String)
 String to remove comments from
forceDirectories(pathToDir) → boolean
ForceDirectories ensures that all the directories in a specific path exist.
Any portion that does not already exist will be created. Function result
indicates success of the operation. The function can fail if the current
user does not have sufficient file access rights to create directories in
the given path.
Arguments:
-
pathToDir
(String)
removeDir(pathToDir) → boolean
Deletes an existing empty directory.
Call removeDir to remove the directory specified by the Dir parameter.
The return value is True if a new directory was successfully deleted, False if an error occurred.
The directory must be emptied before it can be successfully deleted
When working with symlinks, there are some special cases to consider because of how symlinks are implemented on different platforms.
On Windows, RemoveDir can only delete a symbolic link from a directory, regardless if the directory link is broken or not
Arguments:
-
pathToDir
(String)
createGuid() → string
Create GUID
gc()
Perform Garbage collection for current scripting context. Use it if you know WHAT you do!
ncrc32(initialValue, data) → number
Native CRC32 implementation. Much (x100) faster compared to JS implemenattion
Arguments:
-
initialValue
(Number)
 Must be 0 in case no initial value
-
data
(String|ArrayBuffer|ArrayBufferView)
 Data to calculate CRC32. In case of string will be transformed to UFT8 before calculation
nsha256(data) → String
Native SHA256 implementation. Much (x10) faster compared to JS implementation
Arguments:
-
data
(String|ArrayBuffer|ArrayBufferView)
 Data to calculate SHA256. In case of string will be transformed to UFT8 before calculation
clientRequire(req, resp)
The clientRequire
endpoint. Used by client side loaders (SystemJS for example) to emulate commonJS require
Security note: It is very important to prevent loading a server-side logic to the client - server-side logic MUST be hidden from clients
To do this clientRequire
endpoint:
- allow access only to modules inside application
node_modules
folder
- in case requested module is a UnityBase model (present in ubConfig.json) then restrict access to non-public part of such model
So developer MUST listen all modules what contains a sensitive server-side business logic in the application
config and set a moduleName
parameter correctly for such models
Arguments:
-
req
(THTTPRequest)
-
resp
(THTTPResponse)
getAppInfo(req, resp)
The getAppInfo
endpoint. Responsible for return a information about application required for a
initial client side connectivity and UI setup
Arguments:
-
req
(THTTPRequest)
-
resp
(THTTPResponse)
getDomainInfo(req, resp)
The getDomainInfo
endpoint.
Return JSON representation of application Domain according to caller rights
Arguments:
-
req
(THTTPRequest)
-
resp
(THTTPResponse)
module:@unitybase/ubcli/initDB(cfg)
Arguments:
-
cfg
(Object)
Properties
-
[clientIdentifier=3]
(Number)
 Identifier of the client.
Must be between 2 and 8999. Number 1 is for UnityBase developer, 3 for test.
Numbers > 100 is for real installations
-
[dropDatabase=false]
(Boolean)
 Drop a database/schema first
-
[createDatabase=false]
(Boolean)
 Create a new database/schema.
-
[dba]
(String)
 A DBA name. Used in case createDatabase=true
-
[dbaPwd]
(String)
 A DBA password. Used in case createDatabase=true
Type Definitions
parseSQLResult
Properties:
Load a module. Acts like a Node JS require, with 1 difference:
in case we run in production mode (
!process.isDebug
) and minimized version of main module exists, it will be loaded. By "minimized version" we mean package.jsonmain
entry with.min.js
extensionIn case you need to debug from there module is loaded set OS Environment variable
>SET NODE_DEBUG=modules
and restart server - require will put to debug log all information about how module are loaded. Do not do this on production, of course :)
moduleName
(String)
callback
(function)
...arg
(*)
JSONString
(String)
 String to remove comments from
pathToDir
(String)
pathToDir
(String)
initialValue
(Number)
 Must be 0 in case no initial value
data
(String|ArrayBuffer|ArrayBufferView)
 Data to calculate CRC32. In case of string will be transformed to UFT8 before calculation
data
(String|ArrayBuffer|ArrayBufferView)
 Data to calculate SHA256. In case of string will be transformed to UFT8 before calculation
clientRequire
endpoint. Used by client side loaders (SystemJS for example) to emulate commonJS require
Security note: It is very important to prevent loading a server-side logic to the client - server-side logic MUST be hidden from clients
To do this clientRequire
endpoint:
- allow access only to modules inside application
node_modules
folder - in case requested module is a UnityBase model (present in ubConfig.json) then restrict access to non-public part of such model
So developer MUST listen all modules what contains a sensitive server-side business logic in the application
config and set a moduleName
parameter correctly for such models
req
(THTTPRequest)
resp
(THTTPResponse)
getAppInfo
endpoint. Responsible for return a information about application required for a
initial client side connectivity and UI setup
req
(THTTPRequest)
resp
(THTTPResponse)
getDomainInfo
endpoint.
Return JSON representation of application Domain according to caller rights
req
(THTTPRequest)
resp
(THTTPResponse)
cfg
(Object)
Properties
-
[clientIdentifier=3]
(Number)
 Identifier of the client. Must be between 2 and 8999. Number 1 is for UnityBase developer, 3 for test. Numbers > 100 is for real installations -
[dropDatabase=false]
(Boolean)
 Drop a database/schema first -
[createDatabase=false]
(Boolean)
 Create a new database/schema. -
[dba]
(String)
 A DBA name. Used in casecreateDatabase=true
-
[dbaPwd]
(String)
 A DBA password. Used in casecreateDatabase=true
Name | Type | Description |
---|---|---|
parsedSql |
string | |
parsedParams |
Array |
Events
exit
Fires for the process
instance when application stop working for each Working Thread
process.on('exit', function(){
console.log('thread is terminated');
});
domainIsLoaded
Fires for a App
just after all domain entites (all *.meta) is in server memory, and all server-side js is evaluated.
On this stage you can subscruibe/add cross-model handles.
App.once('domainIsLoaded', function(){
var
domain = App.domain,
ettCnt = domain.count,
entityMeta, entityObj;
// all models are evaluated into memory, so we can enumerate enitties here
for (var i = 0; i < ettCnt; i++) {
entityMeta = domain.items[i];
if (entityMeta.attributes.byName('mi_fedUnit')) {
entityObj = global[entityMeta.name];
entityObj.on('insert:before', fedBeforeInsert); // add before insert handler
}
}
})
login
Fires just after user successfully logged-in but before auth response is written to client.
Inside models initialization script you can subscribe to this event and add some data to Session.uData.
No parameter is passed to this event handler. Example below add someCustomProperty
to Session.uData
and this value is accessible on client via $App.connection.userData(someCustomProperty
):
// @param {THTTPRequest} req
Session.on('login', function (req) {
var uData = Session.uData
uData.someCustomProperty = 'Hello!'
})
See real life example inside \models\ORG\org.js
.
registration
Fires in case new user registered in system and authentication schema support
"registration" feature.
Currently only CERT and UB schemas support this feature
For CERT schema user registered means auth
endpoint is called with registration=1 parameter.
For UB schema user registered means 'publicRegistration' endpoint has been called and user confirmed
registration by email otp
Inside event handler server-side Session object is in INCONSISTENT state and you must not use it!!
Only parameter (stringified object), passed to event is valid user-relative information.
For CERT schema parameter is look like
{
"authType": 'CERT',
"id_cert": '',
"user_name": '',
"additional": '',
"certification_b64": ''
}
For UB schema parameter is look like
{
"authType": 'UB',
"publicRegistration": true,
userID,
userOtpData
}
Each AUTH schema can pass his own object as a event parameter, but all schema add authType
.
Below is a sample code for CERT schema:
Session.on('registration', function(registrationParams){
}
newUserRegistration
Fires in case new user registered in system and authentication schema support
"registration" feature.
Currently only CERT schemas
For CERT schema user registered means auth
endpoint is called with registration=1 parameter.
Called before start event "registration" and before starting check the user. You can create new user inside this event.
Parameter is look like
{
"authType": 'CERT',
"serialSign": '<serialSign>',
"name": '<user name>',
"additional": '',
"issuer": '<issuer>',
"serial": '<serial>',
"certification_b64": '<certification_b64>'
}
Below is a sample code for CERT schema:
var iitCrypto = require('iitCrypto');
iitCrypto.init();
Session.on('newUserRegistration', function(registrationParams){
var params = JSON.parse(registrationParams);
Session.runAsAdmin(function () {
var
storeCert, certData, certInfo,
certID, userID, request, certJson, handler,
certParams, connectionName, roleStore,
storeUser = UB.Repository('uba_user')
.attrs(['ID','name','mi_modifyDate'])
.where('name', '=', params.name).select(),
userExist, certExist;
userExist = !storeUser.eof;
if (userExist) {
userID = storeUser.get('ID');
}
storeCert = UB.Repository('uba_usercertificate').attrs(['ID', 'userID.name', 'disabled', 'revoked'])
.where('serial', '=', params.serialSign);
try {
if (!App.serverConfig.security.dstu.findCertificateBySerial) {
storeCert = storeCert.where('issuer_serial', '=', params.issuer);
}
storeCert = storeCert.select();
certExist = !storeCert.eof;
if (certExist && ((storeCert.get('disabled') === 1) || (storeCert.get('revoked') === 1))) {
throw new Error('Certificate is disabled');
}
if (!userExist && certExist) {
throw new Error('Certificate already registered by another user');
}
if (certExist) {
throw new Error('Certificate already registered');
//throw new Error('User ' + params.name + ' already registred');
}
} finally {
storeUser.freeNative();
storeCert.freeNative();
}
certData = Buffer.from(params.certificationB64, 'base64');
certInfo = iitCrypto.parseCertificate(certData.buffer);
if (!userExist) {
storeUser = new TubDataStore('uba_user');
storeUser.run('insert', {
fieldList: ['ID'],
execParams: {
name: params.name,
email: certInfo.SubjEMail,
disabled: 0,
isPending: 0,
// random
uPasswordHashHexa: (new Date()).getTime().toString(27) + Math.round(Math.random() * 10000000000000000).toString(28),
//phone
//description:
firstName: certInfo.SubjFullName //certInfo.SubjOrg
}
}
);
userID = storeUser.get('ID');
roleStore = UB.Repository('uba_role')
.attrs(['ID','name'])
.where('name', 'in', ['Admin']).select();
while (!roleStore.eof){
storeUser.run('insert', {
entity: 'uba_userrole',
execParams: {
userID: userID,
roleID: roleStore.get('ID')
}
}
);
roleStore.next();
}
}
storeCert = new TubDataStore('uba_usercertificate');
certID = storeCert.generateID();
certParams = new TubList();
certParams.ID = certID;
certParams.userID = userID;
certParams.issuer_serial = params.issuer;
certParams.serial = params.serialSign;
certParams.setBLOBValue('certificate', params.certificationB64);
//issuer_cn: certInfo.issuerCapt,
certParams.disabled = 0;
certParams.revoked = 0;
storeCert.run('insert', {
fieldList: ['ID'],
execParams: certParams
}
);
connectionName = App.domain.byName('uba_user').connectionName;
if (App.dbInTransaction(connectionName)) {
App.dbCommit(connectionName);
}
throw new Error('<UBInformation><<<Регистрация прошла успешно.>>>');
});
});
loginFailed
Fires in case auth
endpoint is called with authentication schema UB and userName is founded in database,
but password is incorrect.
If wrong passord is entered more than UBA.passwordPolicy.maxInvalidAttempts
(from ubs_settings) times
user will be locked
2 parameters passes to this event userID(Number) and isUserLocked(Boolean)
Session.on('loginFailed', function(userID, isLocked){
if (isLocked)
console.log('User with id ', userID, 'entered wrong password and locked');
else
console.log('User with id ', userID, 'entered wrong password');
})
securityViolation
Fires in case of any security violation:
- user is blocked or not exists (in uba_user)
- user provide wrong credential (password, domain, encripted secret key, certificate etc)
- for 2-factor auth schemas - too many sessions in pending state (max is 128)
- access to endpoint "%" deny for user (endpoint name not present in uba_role.allowedAppMethods for eny user roles)
- password for user is expired (see ubs_settings UBA.passwordPolicy.maxDurationDays key)
- entity method access deny by ELS (see rules in uba_els)
1 parameter passes to this event reason: string
Session.on('securityViolation', function(reason){
console.log('Security violation for user with ID', Session.userID, 'from', Session.callerIP, 'reason', reason);
})
Fires for the process
instance when application stop working for each Working Thread
process.on('exit', function(){
console.log('thread is terminated');
});
Fires for a App
just after all domain entites (all *.meta) is in server memory, and all server-side js is evaluated.
On this stage you can subscruibe/add cross-model handles.
App.once('domainIsLoaded', function(){
var
domain = App.domain,
ettCnt = domain.count,
entityMeta, entityObj;
// all models are evaluated into memory, so we can enumerate enitties here
for (var i = 0; i < ettCnt; i++) {
entityMeta = domain.items[i];
if (entityMeta.attributes.byName('mi_fedUnit')) {
entityObj = global[entityMeta.name];
entityObj.on('insert:before', fedBeforeInsert); // add before insert handler
}
}
})
someCustomProperty
to Session.uData
and this value is accessible on client via $App.connection.userData(someCustomProperty
):
// @param {THTTPRequest} req
Session.on('login', function (req) {
var uData = Session.uData
uData.someCustomProperty = 'Hello!'
})
See real life example inside \models\ORG\org.js
.
Fires in case new user registered in system and authentication schema support "registration" feature.
Currently only CERT and UB schemas support this feature
For CERT schema user registered means auth
endpoint is called with registration=1 parameter.
For UB schema user registered means 'publicRegistration' endpoint has been called and user confirmed registration by email otp
Inside event handler server-side Session object is in INCONSISTENT state and you must not use it!! Only parameter (stringified object), passed to event is valid user-relative information.
For CERT schema parameter is look like
{
"authType": 'CERT',
"id_cert": '
For UB schema parameter is look like { "authType": 'UB', "publicRegistration": true, userID, userOtpData }
Each AUTH schema can pass his own object as a event parameter, but all schema add authType
.
Below is a sample code for CERT schema:
Session.on('registration', function(registrationParams){
}
Fires in case new user registered in system and authentication schema support "registration" feature.
Currently only CERT schemas
For CERT schema user registered means auth
endpoint is called with registration=1 parameter.
Called before start event "registration" and before starting check the user. You can create new user inside this event.
Parameter is look like
{
"authType": 'CERT',
"serialSign": '<serialSign>',
"name": '<user name>',
"additional": '',
"issuer": '<issuer>',
"serial": '<serial>',
"certification_b64": '<certification_b64>'
}
Below is a sample code for CERT schema:
var iitCrypto = require('iitCrypto');
iitCrypto.init();
Session.on('newUserRegistration', function(registrationParams){
var params = JSON.parse(registrationParams);
Session.runAsAdmin(function () {
var
storeCert, certData, certInfo,
certID, userID, request, certJson, handler,
certParams, connectionName, roleStore,
storeUser = UB.Repository('uba_user')
.attrs(['ID','name','mi_modifyDate'])
.where('name', '=', params.name).select(),
userExist, certExist;
userExist = !storeUser.eof;
if (userExist) {
userID = storeUser.get('ID');
}
storeCert = UB.Repository('uba_usercertificate').attrs(['ID', 'userID.name', 'disabled', 'revoked'])
.where('serial', '=', params.serialSign);
try {
if (!App.serverConfig.security.dstu.findCertificateBySerial) {
storeCert = storeCert.where('issuer_serial', '=', params.issuer);
}
storeCert = storeCert.select();
certExist = !storeCert.eof;
if (certExist && ((storeCert.get('disabled') === 1) || (storeCert.get('revoked') === 1))) {
throw new Error('Certificate is disabled');
}
if (!userExist && certExist) {
throw new Error('Certificate already registered by another user');
}
if (certExist) {
throw new Error('Certificate already registered');
//throw new Error('User ' + params.name + ' already registred');
}
} finally {
storeUser.freeNative();
storeCert.freeNative();
}
certData = Buffer.from(params.certificationB64, 'base64');
certInfo = iitCrypto.parseCertificate(certData.buffer);
if (!userExist) {
storeUser = new TubDataStore('uba_user');
storeUser.run('insert', {
fieldList: ['ID'],
execParams: {
name: params.name,
email: certInfo.SubjEMail,
disabled: 0,
isPending: 0,
// random
uPasswordHashHexa: (new Date()).getTime().toString(27) + Math.round(Math.random() * 10000000000000000).toString(28),
//phone
//description:
firstName: certInfo.SubjFullName //certInfo.SubjOrg
}
}
);
userID = storeUser.get('ID');
roleStore = UB.Repository('uba_role')
.attrs(['ID','name'])
.where('name', 'in', ['Admin']).select();
while (!roleStore.eof){
storeUser.run('insert', {
entity: 'uba_userrole',
execParams: {
userID: userID,
roleID: roleStore.get('ID')
}
}
);
roleStore.next();
}
}
storeCert = new TubDataStore('uba_usercertificate');
certID = storeCert.generateID();
certParams = new TubList();
certParams.ID = certID;
certParams.userID = userID;
certParams.issuer_serial = params.issuer;
certParams.serial = params.serialSign;
certParams.setBLOBValue('certificate', params.certificationB64);
//issuer_cn: certInfo.issuerCapt,
certParams.disabled = 0;
certParams.revoked = 0;
storeCert.run('insert', {
fieldList: ['ID'],
execParams: certParams
}
);
connectionName = App.domain.byName('uba_user').connectionName;
if (App.dbInTransaction(connectionName)) {
App.dbCommit(connectionName);
}
throw new Error('<UBInformation><<<Регистрация прошла успешно.>>>');
});
});
Fires in case auth
endpoint is called with authentication schema UB and userName is founded in database,
but password is incorrect.
If wrong passord is entered more than UBA.passwordPolicy.maxInvalidAttempts
(from ubs_settings) times
user will be locked
2 parameters passes to this event userID(Number) and isUserLocked(Boolean)
Session.on('loginFailed', function(userID, isLocked){
if (isLocked)
console.log('User with id ', userID, 'entered wrong password and locked');
else
console.log('User with id ', userID, 'entered wrong password');
})
Fires in case of any security violation:
- user is blocked or not exists (in uba_user)
- user provide wrong credential (password, domain, encripted secret key, certificate etc)
- for 2-factor auth schemas - too many sessions in pending state (max is 128)
- access to endpoint "%" deny for user (endpoint name not present in uba_role.allowedAppMethods for eny user roles)
- password for user is expired (see ubs_settings UBA.passwordPolicy.maxDurationDays key)
- entity method access deny by ELS (see rules in uba_els)
1 parameter passes to this event reason: string
Session.on('securityViolation', function(reason){
console.log('Security violation for user with ID', Session.userID, 'from', Session.callerIP, 'reason', reason);
})