diff --git a/db/docker.js b/db/docker.js index c9a0b88a2..ea9fe8ed1 100644 --- a/db/docker.js +++ b/db/docker.js @@ -137,7 +137,8 @@ module.exports = class Docker { user: this.dbConf.username, password: this.dbConf.password, host: this.dbConf.host, - port: this.dbConf.port + port: this.dbConf.port, + connectTimeout: maxInterval }; log('Waiting for MySQL init process...'); diff --git a/modules/account/back/model-config.json b/modules/account/back/model-config.json index 96372f427..c697bd3b9 100644 --- a/modules/account/back/model-config.json +++ b/modules/account/back/model-config.json @@ -20,6 +20,9 @@ "MailForward": { "dataSource": "vn" }, + "RoleConfig": { + "dataSource": "vn" + }, "RoleInherit": { "dataSource": "vn" }, diff --git a/modules/account/back/models/account-config.js b/modules/account/back/models/account-config.js index 83fe3332f..49e120a08 100644 --- a/modules/account/back/models/account-config.js +++ b/modules/account/back/models/account-config.js @@ -114,17 +114,22 @@ module.exports = Self => { 'bcryptPassword', 'updated' ], - include: { - relation: 'roles', - scope: { - include: { - relation: 'inherits', - scope: { - fields: ['name'] + include: [ + { + relation: 'roles', + scope: { + include: { + relation: 'inherits', + scope: { + fields: ['name'] + } } } + }, { + relation: 'role', + fields: ['name'] } - } + ] }); let info = { diff --git a/modules/account/back/models/role-config.js b/modules/account/back/models/role-config.js new file mode 100644 index 000000000..b5cfb7b83 --- /dev/null +++ b/modules/account/back/models/role-config.js @@ -0,0 +1,103 @@ + +module.exports = Self => { + Self.getSynchronizer = async function() { + return await Self.findOne({fields: ['id']}); + }; + + Object.assign(Self.prototype, { + async init() { + const [row] = await Self.rawSql('SELECT VERSION() AS `version`'); + if (row.version.includes('MariaDB')) + this.dbType = 'MariaDB'; + else + this.dbType = 'MySQL'; + }, + + async syncUser(userName, info, password) { + const mysqlHost = '%'; + + let mysqlUser = userName; + if (this.dbType == 'MySQL') mysqlUser = `!${mysqlUser}`; + + const [row] = await Self.rawSql( + `SELECT COUNT(*) AS nRows + FROM mysql.user + WHERE User = ? + AND Host = ?`, + [mysqlUser, mysqlHost] + ); + let userExists = row.nRows > 0; + + let isUpdatable = true; + if (this.dbType == 'MariaDB') { + const [row] = await Self.rawSql( + `SELECT Priv AS priv + FROM mysql.global_priv + WHERE User = ? + AND Host = ?`, + [mysqlUser, mysqlHost] + ); + const priv = row && JSON.parse(row.priv); + const role = priv && priv.default_role; + isUpdatable = !row || (role && role.startsWith('z-')); + } + + if (!isUpdatable) { + console.warn(`RoleConfig.syncUser(): User '${userName}' cannot be updated, not managed by me`); + return; + } + + if (info.hasAccount) { + if (password) { + if (!userExists) { + await Self.rawSql('CREATE USER ?@? IDENTIFIED BY ?', + [mysqlUser, mysqlHost, password] + ); + userExists = true; + } else { + switch (this.dbType) { + case 'MariaDB': + await Self.rawSql('ALTER USER ?@? IDENTIFIED BY ?', + [mysqlUser, mysqlHost, password] + ); + break; + default: + await Self.rawSql('SET PASSWORD FOR ?@? = PASSWORD(?)', + [mysqlUser, mysqlHost, password] + ); + } + } + } + + if (userExists && this.dbType == 'MariaDB') { + let role = `z-${info.user.role().name}`; + + try { + await Self.rawSql('REVOKE ALL, GRANT OPTION FROM ?@?', + [mysqlUser, mysqlHost] + ); + } catch (err) { + if (err.code == 'ER_REVOKE_GRANTS') + console.warn(`${err.code}: ${err.sqlMessage}: ${err.sql}`); + else + throw err; + } + await Self.rawSql('GRANT ? TO ?@?', + [role, mysqlUser, mysqlHost] + ); + + if (role) { + await Self.rawSql('SET DEFAULT ROLE ? FOR ?@?', + [role, mysqlUser, mysqlHost] + ); + } else { + await Self.rawSql('SET DEFAULT ROLE NONE FOR ?@?', + [mysqlUser, mysqlHost] + ); + } + } + } else if (userExists) + await Self.rawSql('DROP USER ?@?', [mysqlUser, mysqlHost]); + } + }); +}; diff --git a/modules/account/back/models/role-config.json b/modules/account/back/models/role-config.json new file mode 100644 index 000000000..c2abfcc38 --- /dev/null +++ b/modules/account/back/models/role-config.json @@ -0,0 +1,21 @@ +{ + "name": "RoleConfig", + "base": "VnModel", + "options": { + "mysql": { + "table": "account.roleConfig" + } + }, + "mixins": { + "AccountSynchronizer": {} + }, + "properties": { + "id": { + "type": "number", + "id": true + }, + "mysqlPassword": { + "type": "string" + } + } +}