module.exports = Self => { Self.getSynchronizer = async function() { return await Self.findOne({ fields: ['id', 'rolePrefix', 'userPrefix', 'userHost'] }); }; 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) { let mysqlUser = userName; if (this.dbType == 'MySQL') mysqlUser = this.userPrefix + mysqlUser; const [row] = await Self.rawSql( `SELECT COUNT(*) AS nRows FROM mysql.user WHERE User = ? AND Host = ?`, [mysqlUser, this.userHost] ); 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, this.userHost] ); const priv = row && JSON.parse(row.priv); isUpdatable = !row || (priv && priv.autogenerated); } 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, this.userHost, password]); userExists = true; } else { switch (this.dbType) { case 'MariaDB': await Self.rawSql('ALTER USER ?@? IDENTIFIED BY ?', [mysqlUser, this.userHost, password]); break; default: await Self.rawSql('SET PASSWORD FOR ?@? = PASSWORD(?)', [mysqlUser, this.userHost, password]); } } } if (userExists && this.dbType == 'MariaDB') { let role = `${this.rolePrefix}${info.user.role().name}`; try { await Self.rawSql('REVOKE ALL, GRANT OPTION FROM ?@?', [mysqlUser, this.userHost]); } 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, this.userHost]); if (role) { await Self.rawSql('SET DEFAULT ROLE ? FOR ?@?', [role, mysqlUser, this.userHost]); } else { await Self.rawSql('SET DEFAULT ROLE NONE FOR ?@?', [mysqlUser, this.userHost]); } } } else if (userExists) await Self.rawSql('DROP USER ?@?', [mysqlUser, this.userHost]); } }); };