Skip to content

Commit 1cdf65d

Browse files
authored
Sessions (#3)
* routes: return only JSON responses * route documentation for sessions
1 parent 99908d7 commit 1cdf65d

22 files changed

Lines changed: 410 additions & 195 deletions

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
[![Build Status](https://github.com/NicTool/api/actions/workflows/ci.yml/badge.svg)](https://github.com/NicTool/api/actions/workflows/ci.yml)
22
[![Coverage Status](https://coveralls.io/repos/github/NicTool/api/badge.svg)](https://coveralls.io/github/NicTool/api)
33

4-
# nt-api
5-
6-
nictool api v3
4+
# NicTool API v3
75

86

97
## Install
108

11-
1. Install [Node.js](https://nodejs.org/en/download/) on your system
9+
1. Install [Node.js](https://nodejs.org/en/download/)
1210
2. Download the NicTool v3 API
1311

1412

1513
```
16-
git clone https://github.com/NicTool/api.git nictool-api
17-
cd nictool-api
14+
mkdir nictool && cd nictool
15+
git clone https://github.com/NicTool/api.git
16+
cd api
1817
npm install
1918
```
2019

2120
## Configure
2221

23-
Edit the files in conf.d to reflect your local settings. Each config file has a default section which lists all available config settings. Below the `default` section are optional deployment environments such as `production`, `development`, and `test`. When a config file is loaded, the environment variable `NODE_ENV` is checked and if defined, any overrides in the matching deployment section are applied.
22+
Edit the files in conf.d to reflect your local settings.
23+
24+
Each config file has a default section which lists all available config settings. Below the `default` section are optional deployment environments such as `production`, `development`, and `test`. When a config file is loaded, the environment variable `NODE_ENV` is checked and if defined, any overrides in the matching deployment section are applied.
2425

2526
## Start the service
2627

lib/config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const fs = require('fs/promises')
22

33
const YAML = require('yaml')
44

5-
class config {
5+
class Config {
66
constructor(opts = {}) {
77
this.cfg = {}
88
this.debug = process.env.NODE_DEBUG ? true : false
@@ -16,7 +16,7 @@ class config {
1616

1717
const str = await fs.readFile(`./conf.d/${name}.yml`, 'utf8')
1818
const cfg = YAML.parse(str)
19-
// if (this.debug) console.log(cfg)
19+
if (this.debug) console.debug(cfg)
2020

2121
this.cfg[cacheKey] = applyDefaults(cfg[env ?? this.env], cfg.default)
2222
return this.cfg[cacheKey]
@@ -45,4 +45,4 @@ function applyDefaults(cfg = {}, defaults = {}) {
4545
return cfg
4646
}
4747

48-
module.exports = new config()
48+
module.exports = new Config()

test/config.js renamed to lib/config.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const assert = require('node:assert/strict')
22
const { describe, it } = require('node:test')
33

4-
const config = require('../lib/config')
4+
const config = require('./config')
55

66
describe('config', function () {
77
describe('get', function () {

lib/group.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,48 @@
1-
const mysql = require('./mysql')
1+
const Mysql = require('./mysql')
2+
const Util = require('./util')
23

3-
const validate = require('@nictool/nt-validate')
4+
const validate = require('@nictool/validate')
45

56
class Group {
67
constructor() {}
78

89
async create(args) {
9-
const { error } = validate.group.validate(args)
10+
const { error } = validate.group.v2.validate(args)
1011
if (error) console.error(error)
1112

1213
const g = await this.get({ nt_group_id: args.nt_group_id })
13-
if (g.length) {
14-
// console.log(g)
15-
return g[0].nt_group_id
16-
}
14+
if (g.length) return g[0].id
1715

18-
const groupId = await mysql.insert(`INSERT INTO nt_group`, args)
16+
const groupId = await Mysql.insert(`INSERT INTO nt_group`, args)
1917
return groupId
2018
}
2119

2220
async get(args) {
23-
return await mysql.select(`SELECT * FROM nt_group WHERE`, args)
21+
args = Util.mapToDbColumn(args, { id: 'nt_group_id' })
22+
return await Mysql.select(
23+
`SELECT nt_group_id AS id, name FROM nt_group WHERE`,
24+
args,
25+
)
26+
}
27+
28+
async getAdmin(args) {
29+
return await Mysql.select(
30+
`SELECT nt_group_id AS id
31+
, name
32+
, parent_group_id AS parent_gid
33+
, deleted
34+
FROM nt_group WHERE`,
35+
Util.mapToDbColumn(args, { id: 'nt_group_id' }),
36+
)
2437
}
2538

2639
async destroy(args) {
27-
const g = await this.get({ nt_group_id: args.nt_group_id })
28-
// console.log(g)
40+
const g = await this.getAdmin({ nt_group_id: args.nt_group_id })
2941
if (g.length === 1) {
30-
await mysql.execute(`DELETE FROM nt_group WHERE nt_group_id=?`, [
31-
g[0].nt_group_id,
32-
])
42+
await Mysql.execute(`DELETE FROM nt_group WHERE nt_group_id=?`, [g[0].id])
3343
}
3444
}
3545
}
3646

3747
module.exports = new Group()
38-
module.exports._mysql = mysql
48+
module.exports._mysql = Mysql

test/mysql.js renamed to lib/mysql.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const assert = require('node:assert/strict')
22
const { describe, it } = require('node:test')
33

4-
const mysql = require('../lib/mysql')
4+
const mysql = require('./mysql')
55

66
describe('mysql', () => {
77
it('connects', async () => {

lib/session.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const assert = require('node:assert/strict')
2+
const { describe, it, after } = require('node:test')
3+
4+
const session = require('./session')
5+
const userCase = require('../test/user.json')
6+
7+
after(async () => {
8+
session._mysql.disconnect()
9+
})
10+
11+
describe('session', function () {
12+
// session._mysql.debug(true)
13+
let sessionId
14+
15+
describe('create', () => {
16+
it('creates a login session', async () => {
17+
sessionId = await session.create({
18+
nt_user_id: userCase.nt_user_id,
19+
nt_user_session: '3.0.0',
20+
})
21+
assert.ok(sessionId)
22+
})
23+
})
24+
25+
describe('get', () => {
26+
it('finds a session by ID', async () => {
27+
const s = await session.get({ nt_user_session_id: sessionId })
28+
assert.ok(s.nt_user_session_id)
29+
})
30+
31+
it('finds a session by session', async () => {
32+
const s = await session.get({ nt_user_session: '3.0.0' })
33+
assert.ok(s.nt_user_session_id)
34+
})
35+
})
36+
37+
describe('delete', () => {
38+
it('deletes a session by ID', async () => {
39+
assert.ok(await session.delete({ nt_user_session_id: sessionId }))
40+
})
41+
42+
it('does not find a deleted session', async () => {
43+
assert.equal(
44+
await session.get({ nt_user_session_id: sessionId }),
45+
undefined,
46+
)
47+
})
48+
})
49+
})

lib/user.js

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
11
const crypto = require('node:crypto')
2-
const validate = require('@nictool/nt-validate')
2+
const validate = require('@nictool/validate')
33

4-
const Config = require('./config')
54
const Mysql = require('./mysql')
5+
const Util = require('./util')
6+
Util.setEnv()
7+
const Config = require('./config')
68

79
class User {
810
constructor(args) {
911
this.debug = args?.debug ?? false
12+
this.cfg = Config.getSync('session')
1013
}
1114

1215
async authenticate(authTry) {
1316
if (this.debug) console.log(authTry)
14-
let [username, group] = authTry.username.split('@')
15-
if (!group) {
16-
this.cfg = await Config.get('session')
17-
group = this.cfg.group ?? 'NicTool'
18-
}
17+
let [username, groupName] = authTry.username.split('@')
18+
if (!groupName) groupName = this.cfg.group ?? 'NicTool'
1919

20-
const query = `SELECT u.nt_user_id
20+
const query = `SELECT u.nt_user_id AS id
2121
, u.nt_group_id
2222
, u.first_name
2323
, u.last_name
2424
, u.username
2525
, u.password
2626
, u.pass_salt
2727
, u.email
28-
, u.is_admin
29-
, g.name AS groupname
28+
/*, u.is_admin */
29+
, g.name AS group_name
3030
FROM nt_user u, nt_group g
3131
WHERE u.nt_group_id = g.nt_group_id
3232
AND g.deleted=0
3333
AND u.deleted=0
3434
AND u.username = ?
3535
AND g.name = ?`
3636

37-
for (const u of await Mysql.execute(query, [username, group])) {
37+
for (const u of await Mysql.execute(query, [username, groupName])) {
3838
if (
3939
await this.validPassword(
4040
authTry.password,
@@ -46,13 +46,22 @@ class User {
4646
for (const f of ['password', 'pass_salt']) {
4747
delete u[f] // SECURITY: no longer needed
4848
}
49-
return u
49+
for (const b of ['is_admin']) {
50+
if (u[b] !== undefined) u[b] = u[b] === 1 // int to boolean
51+
}
52+
const g = {
53+
id: u.nt_group_id,
54+
name: groupName,
55+
}
56+
delete u.nt_group_id
57+
delete u.group_name
58+
return { user: u, group: g }
5059
}
5160
}
5261
}
5362

5463
async create(args) {
55-
const { error } = validate.user.validate(args)
64+
const { error } = validate.user.v2.validate(args)
5665
if (error) console.error(error)
5766

5867
const u = await this.get({
@@ -75,37 +84,57 @@ class User {
7584

7685
async get(args) {
7786
return await Mysql.select(
78-
`SELECT email, first_name, last_name, nt_group_id, nt_user_id, username, email, deleted
87+
`SELECT email
88+
, first_name
89+
, last_name
90+
, nt_group_id AS gid
91+
, nt_user_id AS id
92+
, username
93+
, email
7994
FROM nt_user WHERE`,
80-
args,
95+
Util.mapToDbColumn(args, { id: 'nt_user_id', gid: 'nt_group_id' }),
96+
)
97+
}
98+
99+
async getAdmin(args) {
100+
return await Mysql.select(
101+
`SELECT email
102+
, first_name
103+
, last_name
104+
, nt_group_id AS gid
105+
, nt_user_id AS id
106+
, username
107+
, password
108+
, email
109+
, deleted
110+
FROM nt_user WHERE`,
111+
Util.mapToDbColumn(args, { id: 'nt_user_id', gid: 'nt_group_id' }),
81112
)
82113
}
83114

84115
async delete(args, val) {
85-
const u = await this.get({ nt_user_id: args.nt_user_id })
116+
const u = await this.getAdmin({ nt_user_id: args.nt_user_id })
86117
if (u.length === 1) {
87118
await Mysql.execute(`UPDATE nt_user SET deleted=? WHERE nt_user_id=?`, [
88119
val ?? 1,
89-
u[0].nt_user_id,
120+
u[0].id,
90121
])
91122
}
92123
}
93124

94125
async destroy(args) {
95-
const u = await this.get({ nt_user_id: args.nt_user_id })
126+
const u = await this.getAdmin({ nt_user_id: args.nt_user_id })
96127
if (u.length === 1) {
97-
await Mysql.execute(`DELETE FROM nt_user WHERE nt_user_id=?`, [
98-
u[0].nt_user_id,
99-
])
128+
await Mysql.execute(`DELETE FROM nt_user WHERE nt_user_id=?`, [u[0].id])
100129
}
101130
}
102131

103132
// async get_perms(user_id) {
104133
// return await Mysql.execute(
105134
// `
106-
// SELECT ${getPermFields()} FROM nt_perm
107-
// WHERE deleted=0
108-
// AND nt_user_id = ?`,
135+
// SELECT ${getPermFields()} FROM nt_perm
136+
// WHERE deleted=0
137+
// AND nt_user_id = ?`,
109138
// [user_id],
110139
// )
111140
// }
@@ -189,4 +218,4 @@ function getPermFields() {
189218
].join(`, nt_perm.`)
190219
)
191220
}
192-
*/
221+
*/

0 commit comments

Comments
 (0)