-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add group management endpoints
- Loading branch information
1 parent
0014940
commit 4216346
Showing
4 changed files
with
268 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
const { APIError } = require("openai"); | ||
const configurable_auth = require("../middleware/configurable_auth"); | ||
const { Endpoint } = require("../util/expressutil"); | ||
const { whatis } = require("../util/langutil"); | ||
const BaseService = require("./BaseService"); | ||
|
||
class PermissionAPIService extends BaseService { | ||
static MODULES = { | ||
express: require('express'), | ||
}; | ||
|
||
async ['__on_install.routes'] () { | ||
const { app } = this.services.get('web-server'); | ||
|
||
app.use(require('../routers/auth/get-user-app-token')) | ||
app.use(require('../routers/auth/grant-user-app')) | ||
app.use(require('../routers/auth/revoke-user-app')) | ||
app.use(require('../routers/auth/grant-user-user')); | ||
app.use(require('../routers/auth/revoke-user-user')); | ||
app.use(require('../routers/auth/list-permissions')) | ||
|
||
// track: scoping iife | ||
const r_group = (() => { | ||
const require = this.require; | ||
const express = require('express'); | ||
return express.Router() | ||
})(); | ||
|
||
this.install_group_endpoints_({ router: r_group }); | ||
app.use('/group', r_group); | ||
} | ||
|
||
install_group_endpoints_ ({ router }) { | ||
Endpoint({ | ||
route: '/create', | ||
methods: ['POST'], | ||
mw: [configurable_auth()], | ||
handler: async (req, res) => { | ||
const owner_user_id = req.user.id; | ||
|
||
const extra = req.body.extra ?? {}; | ||
const metadata = req.body.metadata ?? {}; | ||
if ( whatis(extra) !== 'object' ) { | ||
throw APIError.create('field_invalid', null, { | ||
key: 'extra', | ||
expected: 'object', | ||
got: whatis(extra), | ||
}) | ||
} | ||
if ( whatis(metadata) !== 'object' ) { | ||
throw APIError.create('field_invalid', null, { | ||
key: 'metadata', | ||
expected: 'object', | ||
got: whatis(metadata), | ||
}) | ||
} | ||
|
||
const svc_group = this.services.get('group'); | ||
const uid = await svc_group.create({ | ||
owner_user_id, | ||
// TODO: allow specifying these in request | ||
extra: {}, | ||
metadata: {}, | ||
}); | ||
|
||
res.json({ uid }); | ||
} | ||
}).attach(router); | ||
|
||
Endpoint({ | ||
route: '/add-users', | ||
methods: ['POST'], | ||
mw: [configurable_auth()], | ||
handler: async (req, res) => { | ||
const svc_group = this.services.get('group') | ||
|
||
// TODO: validate string and uuid for request | ||
|
||
const group = await svc_group.get( | ||
{ uid: req.body.uid }); | ||
|
||
if ( ! group ) { | ||
throw APIError.create('entity_not_found', null, { | ||
identifier: req.body.uid, | ||
}) | ||
} | ||
|
||
if ( group.owner_user_id !== req.user.id ) { | ||
throw APIError.create('forbidden'); | ||
} | ||
|
||
if ( whatis(req.body.users) !== 'array' ) { | ||
throw APIError.create('field_invalid', null, { | ||
key: 'users', | ||
expected: 'array', | ||
got: whatis(req.body.users), | ||
}); | ||
} | ||
|
||
for ( let i=0 ; i < req.body.users.length ; i++ ) { | ||
const value = req.body.users[i]; | ||
if ( whatis(value) === 'string' ) continue; | ||
throw APIError.create('field_invalid', null, { | ||
key: `users[${i}]`, | ||
expected: 'string', | ||
got: whatis(value), | ||
}); | ||
} | ||
|
||
await svc_group.add_users({ | ||
uid: req.body.uid, | ||
users: req.body.users, | ||
}); | ||
|
||
res.json({}); | ||
} | ||
}).attach(router); | ||
|
||
// TODO: DRY: add-users is very similar | ||
Endpoint({ | ||
route: '/remove-users', | ||
methods: ['POST'], | ||
mw: [configurable_auth()], | ||
handler: async (req, res) => { | ||
const svc_group = this.services.get('group') | ||
|
||
// TODO: validate string and uuid for request | ||
|
||
const group = await svc_group.get( | ||
{ uid: req.body.uid }); | ||
|
||
if ( ! group ) { | ||
throw APIError.create('entity_not_found', null, { | ||
identifier: req.body.uid, | ||
}) | ||
} | ||
|
||
if ( group.owner_user_id !== req.user.id ) { | ||
throw APIError.create('forbidden'); | ||
} | ||
|
||
if ( whatis(req.body.users) !== 'array' ) { | ||
throw APIError.create('field_invalid', null, { | ||
key: 'users', | ||
expected: 'array', | ||
got: whatis(req.body.users), | ||
}); | ||
} | ||
|
||
for ( let i=0 ; i < req.body.users.length ; i++ ) { | ||
const value = req.body.users[i]; | ||
if ( whatis(value) === 'string' ) continue; | ||
throw APIError.create('field_invalid', null, { | ||
key: `users[${i}]`, | ||
expected: 'string', | ||
got: whatis(value), | ||
}); | ||
} | ||
|
||
await svc_group.remove_users({ | ||
uid: req.body.uid, | ||
users: req.body.users, | ||
}); | ||
|
||
res.json({}); | ||
} | ||
}).attach(router); | ||
} | ||
} | ||
|
||
module.exports = { | ||
PermissionAPIService, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
const BaseService = require("../BaseService"); | ||
const { DB_WRITE } = require("../database/consts"); | ||
|
||
class GroupService extends BaseService { | ||
static MODULES = { | ||
uuidv4: require('uuid').v4, | ||
}; | ||
|
||
_init () { | ||
this.db = this.services.get('database').get(DB_WRITE, 'permissions'); | ||
} | ||
|
||
async get({ uid }) { | ||
const [group] = | ||
await this.db.read('SELECT * FROM `group` WHERE uid=?', [uid]); | ||
if ( ! group ) return; | ||
group.extra = this.db.case({ | ||
mysql: () => group.extra, | ||
otherwise: () => JSON.parse(group.extra), | ||
})(); | ||
group.metadata = this.db.case({ | ||
mysql: () => group.metadata, | ||
otherwise: () => JSON.parse(group.metadata), | ||
})(); | ||
return group; | ||
} | ||
|
||
async create ({ owner_user_id, extra, metadata }) { | ||
extra = extra ?? {}; | ||
metadata = metadata ?? {}; | ||
|
||
const uid = this.modules.uuidv4(); | ||
|
||
await this.db.write( | ||
'INSERT INTO `group` ' + | ||
'(`uid`, `owner_user_id`, `extra`, `metadata`) ' + | ||
'VALUES (?, ?, ?, ?)', | ||
[ | ||
uid, owner_user_id, | ||
JSON.stringify(extra), | ||
JSON.stringify(metadata), | ||
] | ||
); | ||
|
||
return uid; | ||
} | ||
|
||
async add_users ({ uid, users }) { | ||
const question_marks = | ||
'(' + Array(users.length).fill('?').join(', ') + ')'; | ||
await this.db.write( | ||
'INSERT INTO `jct_user_group` ' + | ||
'(user_id, group_id) ' + | ||
'SELECT u.id, g.id FROM user u '+ | ||
'JOIN (SELECT id FROM `group` WHERE uid=?) g ON 1=1 ' + | ||
'WHERE u.username IN ' + | ||
question_marks, | ||
[uid, ...users], | ||
); | ||
} | ||
|
||
async remove_users ({ uid, users }) { | ||
const question_marks = | ||
'(' + Array(users.length).fill('?').join(', ') + ')'; | ||
/* | ||
DELETE FROM `jct_user_group` | ||
WHERE group_id = 1 | ||
AND user_id IN ( | ||
SELECT u.id | ||
FROM user u | ||
WHERE u.username IN ('user_that_shares', 'user_that_gets_shared_to') | ||
); | ||
*/ | ||
await this.db.write( | ||
'DELETE FROM `jct_user_group` ' + | ||
'WHERE group_id = (SELECT id FROM `group` WHERE uid=?) ' + | ||
'AND user_id IN (' + | ||
'SELECT u.id FROM user u ' + | ||
'WHERE u.username IN ' + | ||
question_marks + | ||
')', | ||
[uid, ...users], | ||
); | ||
} | ||
} | ||
|
||
module.exports = { | ||
GroupService, | ||
}; |