modules/cmd/initDB/initDB.js

/**
 * Create a database (schema) & a minimal set of DB object for a UnityBase ORM
 * Will first create a temporary config with authentication disabled and
 * @example
 *
     //from a command line
     >ub cmd/initDB -u admin -p admin -dba postgres -dbaPwd postgreDBAPassword

     //from a code
     var initDB = require('cmd/initDB');
     var options = {
        "host": "http://localhost:888",
        "user": "admin",
        "pwd": "admin",
        "clientIdentifier": 3,
        "dropDatabase": true,
        "createDatabase": true,
        "dba": "postgres",
        "dbaPwd": "postgreDBAPassword"
    };
    initDB(options);

 * @module cmd/initDB
 */

var cmdLineOpt = require('cmd/options');
/**
 * If DBA already create a database for you set both `dropDatabase` & `createDatabase` to false
 * @param {Object} options
 * @param {Number} [options.clientIdentifier=3] 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
 * @param {Boolean} [options.dropDatabase=false] Drop a database/schema first
 * @param {Boolean} [options.createDatabase=false] Create a new database/schema.
 * @param {String} [options.dba] A DBA name. Used in case `createDatabase=true`
 * @param {String} [options.dbaPwd] A DBA password. Used in case `createDatabase=true`
 */
module.exports = function initDB(options){
    var argv = require('cmd/argv');
    var fs = require('fs');
    if (!options){
        var opts = cmdLineOpt.describe('cmd/initDB', 'Prepare a new database for a UB ORM')
          .add(argv.establishConnectionFromCmdLineAttributes._cmdLineParams)
          .add({short: 'c',     long: 'clientIdentifier', param: 'clientIdentifier',defaultValue: 3,    searchInEnv: false, help: 'Identifier of the client'})
          .add({short: 'drop',  long: 'dropDatabase',     param: '',                defaultValue: false, searchInEnv: false, help: 'Drop a database/schema first'})
          .add({short: 'create',long: 'createDatabase',   param: '',                defaultValue: false, searchInEnv: false, help: 'Create a new database/schema'})
          .add({short: 'dba',   long: 'dba',              param: 'DBA_user_name',   defaultValue: '-', searchInEnv: false, help: 'A DBA name. Used in case `createDatabase=true`'})
          .add({short: 'dbaPwd',long: 'dbaPwd',           param: 'DBA_password',   defaultValue: '-', searchInEnv: false, help: 'A DBA password. Used in case `createDatabase=true`'});
        options = opts.parseVerbose({}, true);
    }
    if (!options) return;
    var session, conn, defaultDB, generator;

    var originalConfigFileName = argv.getConfigFileName();
    var config = argv.getServerConfiguration();
    options.host = argv.serverURLFromConfig(config);

    // database are slow :( Increase timeout to 2 minutes
    var http = require('http');
    http.setGlobalConnectionDefaults({receiveTimeout: 2 * 60 * 1000});

    if (argv.checkServerStarted(options.host)){
    //    throw new Error('Please, shutdown a server on ' + options.host + ' before run this command');
    }

    fs.renameSync(originalConfigFileName, originalConfigFileName + '.bak');
    try {
        defaultDB = createFakeConfig(config, originalConfigFileName);
        options.forceStartServer = true;
        session = argv.establishConnectionFromCmdLineAttributes(options);
        conn = session.connection;
        var dbDriverName = defaultDB.driver.toLowerCase();
        if (dbDriverName.startsWith('mssql')) {
            dbDriverName = 'mssql';
        }
        generator = require('./' + dbDriverName);
        if (options.dropDatabase) {
            console.info('Dropping a database...');
            generator.dropDatabase(session, defaultDB);
        }
        if (options.createDatabase) {
            console.info('Creating a database...');
            generator.createDatabase(conn, defaultDB);
        }
        console.info('Creating a minimal set of database objects...');
        generator.createMinSchema(conn, options.clientIdentifier, defaultDB);
        console.info('Creating a superuser..');
        createSuperUser(conn, dbDriverName);
        console.info('Database is ready. Run a `cmd/generateDDL` command to create a database tables for a domain');
    } finally {
        fs.renameSync(originalConfigFileName + '.bak', originalConfigFileName);
    }


    /**
     * Create a fake config with authentication disabled & empty domain.
     * Return a default database driver name
     */
    function createFakeConfig(){
        var defaultDB = _.find(config.application.connections, {isDefault: true}) || config.application.connections[0];
        var newConfig = _.cloneDeep(config);
        var dbaConn;

        newConfig.security = {};
        newConfig.application.domain = {};
        if (options.dropDatabase || options.createDatabase){
            dbaConn = _.cloneDeep(defaultDB);
            _.assign(dbaConn, {
                name: '__dba',
                userID: options.dba,
                password: options.dbaPwd,
                isDefault: false
            });
            if (dbaConn.driver.toLowerCase().startsWith('mssql')){
                dbaConn.databaseName = 'master';
            }
            newConfig.httpServer.threadPoolSize = 1;
            newConfig.application.connections.push(dbaConn);
        }
        fs.writeFileSync(originalConfigFileName, newConfig);
        // uncomment for debug purpose
        // fs.writeFileSync(originalConfigFileName + '.fake', JSON.stringify(newConfig, null, '\t'));
        return defaultDB;
    }
};


/**
 * Create a Everyone & admin roles and a SuperUser named admin with password `admin`
 * @param {UBConnection} conn
 * @param {String} dbDriverName
 */
function createSuperUser(conn, dbDriverName) {
    var pwdHash = nsha256('salt' + 'admin');
    var initSecurity;

    if (dbDriverName === 'sqlite3'){
        var isoDate = "'" + new Date().toISOString().slice(0, -5) + "Z'";
        var auditTailFlds = 'mi_owner,mi_createdate,mi_createuser,mi_modifydate,mi_modifyuser';
        var auditTailVals = "10," + isoDate + ",10," + isoDate + ",10";
        initSecurity = [
            "PRAGMA foreign_keys = OFF",
            /* admins role */
            "insert into uba_subject (ID,name,sType,mi_unityentity) values(1,	'admins',		'R', 'UBA_SUBJECT')",
            "insert into uba_role (ID,name,description,sessionTimeout,allowedAppMethods," + auditTailFlds + ") values(1,'admins','UB application administrator',10,'*'," + auditTailVals + ")", /* user admin */
            "insert into uba_subject (ID,name,sType,mi_unityentity) values(10	,'admin',		'U', 'UBA_USER')",
            "insert into uba_user (id, name, description, upasswordhashhexa, disabled, udata," + auditTailFlds + ") values (10, 'admin', 'admin', '" + pwdHash + "', 0, ''," + auditTailVals + ")",
            /* Everyone pseudo-role */
            "insert into uba_subject (ID,name,sType,mi_unityentity) values(0,	'Everyone',		'R', 'UBA_SUBJECT')",
            "insert into uba_role (ID,name,description,sessionTimeout,allowedAppMethods," + auditTailFlds + ") values(0,'Everyone','Pseudo role. To use only in ELS rules',10000,'ubql,getDomainInfo,logout,getDocument,setDocument'," + auditTailVals + ")",
            /* allow all for admins role (with ID = 1) */
            "INSERT INTO uba_els (ID,	code,	description,	disabled,	entityMask,	methodMask,	ruleType,	ruleRole," + auditTailFlds + ") VALUES (200, 'UBA_ADMIN_ALL', 'Admins - enable all',	0,	'*',	'*',	'A',	1," + auditTailVals + ")",
            /* assign user admin to role admins */
            "insert into uba_userrole (ID,userID, roleID," + auditTailFlds + ") values(800,10,1," + auditTailVals + ");",
            "PRAGMA foreign_keys = ON"
        ];
    } else {
        initSecurity = [
            /* Everyone pseudo-role */
            "insert into uba_subject (ID,name,sType,mi_unityentity) values(0,	'Everyone',		'R', 'UBA_SUBJECT')",
            "insert into uba_role (ID,name,description,sessionTimeout,allowedAppMethods) values(0,'Everyone','Pseudo role. To use only in ELS rules',10000,'ubql,getDomainInfo,logout,getDocument,setDocument')",
            /* admins role */
            "insert into uba_subject (ID,name,sType,mi_unityentity) values(1,	'admins',		'R', 'UBA_SUBJECT')",
            "insert into uba_role (ID,name,description,sessionTimeout,allowedAppMethods) values(1,'admins','UB application administrator',10,'*')", /* user admin */
            "insert into uba_subject (ID,name,sType,mi_unityentity) values(10	,'admin',		'U', 'UBA_USER')",
            "insert into uba_user (id, name, description, upasswordhashhexa, disabled, udata) values (10, 'admin', 'admin', '" + pwdHash + "', 0, '')",
            /* allow all for admins role (with ID = 1) */
            "INSERT INTO uba_els (ID,	code,	description,	disabled,	entityMask,	methodMask,	ruleType,	ruleRole) VALUES (200, 'UBA_ADMIN_ALL', 'Admins - enable all',	0,	'*',	'*',	'A',	1)",
            /* assign user admin to role admins */
            "insert into uba_userrole (ID,userID, roleID) values(800,10,1);"
        ];
    }

    initSecurity.forEach(function(stmt){
        conn.xhr({
            endpoint: 'runSQL', URLParams: {CONNECTION: 'main'}, data: stmt
        })
    });
}