diff --git a/lib/mysql.js b/lib/mysql.js index c31eeed..bb1857e 100644 --- a/lib/mysql.js +++ b/lib/mysql.js @@ -73,6 +73,7 @@ class Mysql { } whereConditions(query, params) { + let newQuery = query let paramsArray = [] if (Array.isArray(params)) { @@ -81,13 +82,13 @@ class Mysql { // Object to WHERE conditions let first = true for (const p in params) { - if (!first) query += ' AND' - query += ` ${p}=?` + if (!first) newQuery += ' AND' + newQuery += ` ${p}=?` paramsArray.push(params[p]) first = false } } - return [query, paramsArray] + return [newQuery, paramsArray] } async delete(query, params) { diff --git a/lib/permission.js b/lib/permission.js index a1ad40c..ff9729d 100644 --- a/lib/permission.js +++ b/lib/permission.js @@ -9,29 +9,6 @@ const permDbMap = { name: 'perm_name', } -const boolFields = [ - 'group_create', - 'group_delete', - 'group_write', - 'nameserver_create', - 'nameserver_delete', - 'nameserver_write', - 'self_write', - 'user_create', - 'user_delete', - 'user_write', - 'zone_create', - 'zone_delegate', - 'zone_delete', - 'zone_write', - 'zonerecord_create', - 'zonerecord_delegate', - 'zonerecord_delete', - 'zonerecord_write', - 'inherit', - 'deleted', -] - class Permission { constructor() { this.mysql = Mysql @@ -39,34 +16,49 @@ class Permission { async create(args) { if (args.id) { - const g = await this.get({ id: args.id }) - if (g.length) return g[0].id + const p = await this.get({ id: args.id }) + if (p) return p.id } return await Mysql.insert( `INSERT INTO nt_perm`, - mapToDbColumn(args, permDbMap), + mapToDbColumn(objectToDb(args), permDbMap), ) } async get(args) { - const rows = await Mysql.select( - `SELECT nt_perm_id AS id - , nt_user_id AS uid - , nt_group_id AS gid - , inherit_perm AS inherit - , perm_name AS name + const query = `SELECT p.nt_perm_id AS id + , p.nt_user_id AS uid + , p.nt_group_id AS gid + , p.inherit_perm AS inherit + , p.perm_name AS name ${getPermFields()} - , deleted - FROM nt_perm WHERE`, - mapToDbColumn(args, permDbMap), - ) - for (const r of rows) { - for (const b of boolFields) { - r[b] = r[b] === 1 - } + , p.deleted + FROM nt_perm p WHERE` + // Mysql.debug(1) + const rows = await Mysql.select(query, mapToDbColumn(args, permDbMap)) + if (rows.length === 0) return + if (rows.length > 1) { + throw new Error(`permissions.get found ${rows.length} rows for uid ${args.uid}`) } - return rows + return dbToObject(rows[0]) + } + + async getGroup(args) { + const query = `SELECT p.nt_perm_id AS id + , p.nt_user_id AS uid + , p.nt_group_id AS gid + , p.inherit_perm AS inherit + , p.perm_name AS name + ${getPermFields()} + , p.deleted + FROM nt_perm p + INNER JOIN nt_user u ON p.nt_group_id = u.nt_group_id + WHERE p.deleted=0 + AND u.deleted=0 + AND u.nt_user_id=?`; + const rows = await Mysql.select(query, [ args.uid ]) + return dbToObject(rows[0]) } async put(args) { @@ -83,23 +75,22 @@ class Permission { } async delete(args, val) { - const g = await this.get(args) - if (g.length !== 1) return false + const p = await this.get(args) + if (!p) return false await Mysql.execute(`UPDATE nt_perm SET deleted=? WHERE nt_perm_id=?`, [ val ?? 1, - g[0].id, + args.id, ]) return true } async destroy(args) { - const g = await this.get(args) - if (g.length === 1) { - await Mysql.delete( - `DELETE FROM nt_perm WHERE`, - mapToDbColumn(args, permDbMap), - ) - } + const p = await this.get(args) + if (!p) return false + return await Mysql.delete( + `DELETE FROM nt_perm WHERE`, + mapToDbColumn(args, permDbMap), + ) } } @@ -107,7 +98,7 @@ export default new Permission() function getPermFields() { return ( - `, nt_perm.` + + `, p.` + [ 'group_write', 'group_create', @@ -133,6 +124,115 @@ function getPermFields() { 'self_write', 'usable_ns', - ].join(`, nt_perm.`) + ].join(`, p.`) ) } + +/* the following two functions convert to and from: + +the SQL DB format: +{ + "id": 4096, + "uid": 4096, + "gid": 4096, + "inherit": 1, + "name": "Test Permission", + "group_write": 0, + "group_create": 0, + "group_delete": 0, + "zone_write": 1, + "zone_create": 1, + "zone_delegate": 1, + "zone_delete": 1, + "zonerecord_write": 0, + "zonerecord_create": 0, + "zonerecord_delegate": 0, + "zonerecord_delete": 0, + "user_write": 0, + "user_create": 0, + "user_delete": 0, + "nameserver_write": 0, + "nameserver_create": 0, + "nameserver_delete": 0, + "self_write": 0, + "usable_ns": "", + "deleted": 0 +} + +JSON object format: + +{ + "id": 4096, + "inherit": true, + "name": "Test Permission", + "self_write": false, + "usable_ns": "", + "deleted": false, + "group": { "id": 4096, "create": false, "write": false, "delete": false }, + "nameserver": { "create": false, "write": false, "delete": false }, + "zone": { "create": true, "write": true, "delete": true, "delegate": true }, + "zonerecord": { + "create": false, + "write": false, + "delete": false, + "delegate": false + }, + "user": { "id": 4096, "create": false, "write": false, "delete": false } +} +*/ + +const boolFields = [ + 'self_write', + 'inherit', + 'deleted', +] + +function dbToObject(row) { + const newRow = JSON.parse(JSON.stringify(row)) + for (const f of ['group', 'nameserver', 'zone', 'zonerecord', 'user']) { + for (const p of ['create','write','delete','delegate']) { + if (newRow[`${f}_${p}`] !== undefined) { + if (newRow[f] === undefined) newRow[f] = {} + newRow[f][p] = newRow[`${f}_${p}`] === 1 + delete newRow[`${f}_${p}`] + } + } + } + for (const b of boolFields) { + newRow[b] = newRow[b] === 1 + } + if (newRow.uid !== undefined) { + newRow.user.id = newRow.uid + delete newRow.uid + } + if (newRow.gid !== undefined) { + newRow.group.id = newRow.gid + delete newRow.gid + } + return newRow +} + +function objectToDb (row) { + const newRow = JSON.parse(JSON.stringify(row)) + if (newRow?.user?.id !== undefined) { + newRow.uid = newRow.user.id + delete newRow.user.id + } + if (newRow?.group?.id !== undefined) { + newRow.gid = newRow.group.id + delete newRow.group.id + } + for (const f of ['group', 'nameserver', 'zone', 'zonerecord', 'user']) { + for (const p of ['create','write','delete','delegate']) { + if (newRow[f] === undefined) continue + if (newRow[f][p] === undefined) continue + newRow[`${f}_${p}`] = newRow[f][p] === true ? 1 : 0 + delete newRow[f][p] + } + delete newRow[f] + } + for (const b of boolFields) { + newRow[b] = newRow[b] === true ? 1 : 0 + } + return newRow +} diff --git a/lib/permission.test.js b/lib/permission.test.js index bdc6f41..3d4186b 100644 --- a/lib/permission.test.js +++ b/lib/permission.test.js @@ -1,11 +1,17 @@ import assert from 'node:assert/strict' -import { describe, it, after } from 'node:test' +import { describe, it, after, before } from 'node:test' import Permission from './permission.js' +import User from './user.js' import permTestCase from './test/permission.json' with { type: 'json' } +import userTestCase from './test/user.json' with { type: 'json' } + +before(async () => { + await User.create(userTestCase) +}) after(async () => { - Permission.mysql.disconnect() + await Permission.mysql.disconnect() }) describe('permission', function () { @@ -13,25 +19,26 @@ describe('permission', function () { assert.ok(await Permission.create(permTestCase)) }) - it('gets permission by id', async () => { - const g = await Permission.get({ id: permTestCase.id }) - assert.deepEqual(g[0], permTestCase) + it('get: by id', async () => { + assert.deepEqual(await Permission.get({ id: permTestCase.id }), permTestCase) + }) + + it('get: by user id', async () => { + assert.deepEqual(await Permission.get({ uid: permTestCase.user.id }), permTestCase) }) - it('gets permission by user id', async () => { - const g = await Permission.get({ uid: permTestCase.uid }) - assert.deepEqual(g[0], permTestCase) + it('get: by group id', async () => { + assert.deepEqual(await Permission.get({ gid: permTestCase.group.id }), permTestCase) }) - it('gets permission by group id', async () => { - const g = await Permission.get({ uid: permTestCase.gid }) - assert.deepEqual(g[0], permTestCase) + it('getGroup: gets group permissions', async () => { + assert.deepEqual(await Permission.getGroup({ uid: permTestCase.user.id }), permTestCase) }) it('changes a permission', async () => { assert.ok(await Permission.put({ id: permTestCase.id, name: 'Changed' })) - const perms = await Permission.get({ id: permTestCase.id }) - assert.deepEqual(perms[0].name, 'Changed') + const perm = await Permission.get({ id: permTestCase.id }) + assert.deepEqual(perm.name, 'Changed') assert.ok( await Permission.put({ id: permTestCase.id, name: 'Test Permission' }), ) @@ -39,10 +46,17 @@ describe('permission', function () { it('deletes a permission', async () => { assert.ok(await Permission.delete({ id: permTestCase.id })) - let u = await Permission.get({ id: permTestCase.id }) - assert.equal(u[0].deleted, true) + let p = await Permission.get({ id: permTestCase.id }) + assert.equal(p.deleted, true) await Permission.delete({ id: permTestCase.id }, 0) // restore - u = await Permission.get({ id: permTestCase.id }) - assert.equal(u[0].deleted, false) + p = await Permission.get({ id: permTestCase.id }) + assert.equal(p.deleted, false) + }) + + it('destroys a permission', async () => { + const r = await Permission.destroy({ id: permTestCase.id }) + assert.equal(r.affectedRows, 1) + const p = await Permission.get({ id: permTestCase.id }) + assert.equal(p, undefined) }) }) diff --git a/lib/test/permission.json b/lib/test/permission.json index aa4a736..6267b91 100644 --- a/lib/test/permission.json +++ b/lib/test/permission.json @@ -1,27 +1,18 @@ { "id": 4096, - "uid": 4096, - "gid": 4096, "inherit": true, "name": "Test Permission", - "group_write": false, - "group_create": false, - "group_delete": false, - "zone_write": true, - "zone_create": true, - "zone_delegate": true, - "zone_delete": true, - "zonerecord_write": false, - "zonerecord_create": false, - "zonerecord_delegate": false, - "zonerecord_delete": false, - "user_write": false, - "user_create": false, - "user_delete": false, - "nameserver_write": false, - "nameserver_create": false, - "nameserver_delete": false, "self_write": false, "usable_ns": "", - "deleted": false -} + "deleted": false, + "group": { "id": 4096, "create": false, "write": false, "delete": false }, + "nameserver": { "create": false, "write": false, "delete": false }, + "zone": { "create": true, "write": true, "delete": true, "delegate": true }, + "zonerecord": { + "create": false, + "write": false, + "delete": false, + "delegate": false + }, + "user": { "id": 4096, "create": false, "write": false, "delete": false } +} \ No newline at end of file