Skip to content

Commit 4216346

Browse files
committed
feat: add group management endpoints
1 parent 0014940 commit 4216346

File tree

4 files changed

+268
-6
lines changed

4 files changed

+268
-6
lines changed

packages/backend/src/CoreModule.js

+6
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,12 @@ const install = async ({ services, app, useapi }) => {
286286

287287
const { ShareService } = require('./services/ShareService');
288288
services.registerService('share', ShareService);
289+
290+
const { GroupService } = require('./services/auth/GroupService');
291+
services.registerService('group', GroupService);
292+
293+
const { PermissionAPIService } = require('./services/PermissionAPIService');
294+
services.registerService('__permission-api', PermissionAPIService);
289295
}
290296

291297
const install_legacy = async ({ services }) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
const { APIError } = require("openai");
2+
const configurable_auth = require("../middleware/configurable_auth");
3+
const { Endpoint } = require("../util/expressutil");
4+
const { whatis } = require("../util/langutil");
5+
const BaseService = require("./BaseService");
6+
7+
class PermissionAPIService extends BaseService {
8+
static MODULES = {
9+
express: require('express'),
10+
};
11+
12+
async ['__on_install.routes'] () {
13+
const { app } = this.services.get('web-server');
14+
15+
app.use(require('../routers/auth/get-user-app-token'))
16+
app.use(require('../routers/auth/grant-user-app'))
17+
app.use(require('../routers/auth/revoke-user-app'))
18+
app.use(require('../routers/auth/grant-user-user'));
19+
app.use(require('../routers/auth/revoke-user-user'));
20+
app.use(require('../routers/auth/list-permissions'))
21+
22+
// track: scoping iife
23+
const r_group = (() => {
24+
const require = this.require;
25+
const express = require('express');
26+
return express.Router()
27+
})();
28+
29+
this.install_group_endpoints_({ router: r_group });
30+
app.use('/group', r_group);
31+
}
32+
33+
install_group_endpoints_ ({ router }) {
34+
Endpoint({
35+
route: '/create',
36+
methods: ['POST'],
37+
mw: [configurable_auth()],
38+
handler: async (req, res) => {
39+
const owner_user_id = req.user.id;
40+
41+
const extra = req.body.extra ?? {};
42+
const metadata = req.body.metadata ?? {};
43+
if ( whatis(extra) !== 'object' ) {
44+
throw APIError.create('field_invalid', null, {
45+
key: 'extra',
46+
expected: 'object',
47+
got: whatis(extra),
48+
})
49+
}
50+
if ( whatis(metadata) !== 'object' ) {
51+
throw APIError.create('field_invalid', null, {
52+
key: 'metadata',
53+
expected: 'object',
54+
got: whatis(metadata),
55+
})
56+
}
57+
58+
const svc_group = this.services.get('group');
59+
const uid = await svc_group.create({
60+
owner_user_id,
61+
// TODO: allow specifying these in request
62+
extra: {},
63+
metadata: {},
64+
});
65+
66+
res.json({ uid });
67+
}
68+
}).attach(router);
69+
70+
Endpoint({
71+
route: '/add-users',
72+
methods: ['POST'],
73+
mw: [configurable_auth()],
74+
handler: async (req, res) => {
75+
const svc_group = this.services.get('group')
76+
77+
// TODO: validate string and uuid for request
78+
79+
const group = await svc_group.get(
80+
{ uid: req.body.uid });
81+
82+
if ( ! group ) {
83+
throw APIError.create('entity_not_found', null, {
84+
identifier: req.body.uid,
85+
})
86+
}
87+
88+
if ( group.owner_user_id !== req.user.id ) {
89+
throw APIError.create('forbidden');
90+
}
91+
92+
if ( whatis(req.body.users) !== 'array' ) {
93+
throw APIError.create('field_invalid', null, {
94+
key: 'users',
95+
expected: 'array',
96+
got: whatis(req.body.users),
97+
});
98+
}
99+
100+
for ( let i=0 ; i < req.body.users.length ; i++ ) {
101+
const value = req.body.users[i];
102+
if ( whatis(value) === 'string' ) continue;
103+
throw APIError.create('field_invalid', null, {
104+
key: `users[${i}]`,
105+
expected: 'string',
106+
got: whatis(value),
107+
});
108+
}
109+
110+
await svc_group.add_users({
111+
uid: req.body.uid,
112+
users: req.body.users,
113+
});
114+
115+
res.json({});
116+
}
117+
}).attach(router);
118+
119+
// TODO: DRY: add-users is very similar
120+
Endpoint({
121+
route: '/remove-users',
122+
methods: ['POST'],
123+
mw: [configurable_auth()],
124+
handler: async (req, res) => {
125+
const svc_group = this.services.get('group')
126+
127+
// TODO: validate string and uuid for request
128+
129+
const group = await svc_group.get(
130+
{ uid: req.body.uid });
131+
132+
if ( ! group ) {
133+
throw APIError.create('entity_not_found', null, {
134+
identifier: req.body.uid,
135+
})
136+
}
137+
138+
if ( group.owner_user_id !== req.user.id ) {
139+
throw APIError.create('forbidden');
140+
}
141+
142+
if ( whatis(req.body.users) !== 'array' ) {
143+
throw APIError.create('field_invalid', null, {
144+
key: 'users',
145+
expected: 'array',
146+
got: whatis(req.body.users),
147+
});
148+
}
149+
150+
for ( let i=0 ; i < req.body.users.length ; i++ ) {
151+
const value = req.body.users[i];
152+
if ( whatis(value) === 'string' ) continue;
153+
throw APIError.create('field_invalid', null, {
154+
key: `users[${i}]`,
155+
expected: 'string',
156+
got: whatis(value),
157+
});
158+
}
159+
160+
await svc_group.remove_users({
161+
uid: req.body.uid,
162+
users: req.body.users,
163+
});
164+
165+
res.json({});
166+
}
167+
}).attach(router);
168+
}
169+
}
170+
171+
module.exports = {
172+
PermissionAPIService,
173+
};

packages/backend/src/services/PuterAPIService.js

-6
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@ class PuterAPIService extends BaseService {
2626
app.use(require('../routers/query/app'))
2727
app.use(require('../routers/change_username'))
2828
require('../routers/change_email')(app);
29-
app.use(require('../routers/auth/get-user-app-token'))
30-
app.use(require('../routers/auth/grant-user-app'))
31-
app.use(require('../routers/auth/revoke-user-app'))
32-
app.use(require('../routers/auth/grant-user-user'));
33-
app.use(require('../routers/auth/revoke-user-user'));
34-
app.use(require('../routers/auth/list-permissions'))
3529
app.use(require('../routers/auth/list-sessions'))
3630
app.use(require('../routers/auth/revoke-session'))
3731
app.use(require('../routers/auth/check-app'))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const BaseService = require("../BaseService");
2+
const { DB_WRITE } = require("../database/consts");
3+
4+
class GroupService extends BaseService {
5+
static MODULES = {
6+
uuidv4: require('uuid').v4,
7+
};
8+
9+
_init () {
10+
this.db = this.services.get('database').get(DB_WRITE, 'permissions');
11+
}
12+
13+
async get({ uid }) {
14+
const [group] =
15+
await this.db.read('SELECT * FROM `group` WHERE uid=?', [uid]);
16+
if ( ! group ) return;
17+
group.extra = this.db.case({
18+
mysql: () => group.extra,
19+
otherwise: () => JSON.parse(group.extra),
20+
})();
21+
group.metadata = this.db.case({
22+
mysql: () => group.metadata,
23+
otherwise: () => JSON.parse(group.metadata),
24+
})();
25+
return group;
26+
}
27+
28+
async create ({ owner_user_id, extra, metadata }) {
29+
extra = extra ?? {};
30+
metadata = metadata ?? {};
31+
32+
const uid = this.modules.uuidv4();
33+
34+
await this.db.write(
35+
'INSERT INTO `group` ' +
36+
'(`uid`, `owner_user_id`, `extra`, `metadata`) ' +
37+
'VALUES (?, ?, ?, ?)',
38+
[
39+
uid, owner_user_id,
40+
JSON.stringify(extra),
41+
JSON.stringify(metadata),
42+
]
43+
);
44+
45+
return uid;
46+
}
47+
48+
async add_users ({ uid, users }) {
49+
const question_marks =
50+
'(' + Array(users.length).fill('?').join(', ') + ')';
51+
await this.db.write(
52+
'INSERT INTO `jct_user_group` ' +
53+
'(user_id, group_id) ' +
54+
'SELECT u.id, g.id FROM user u '+
55+
'JOIN (SELECT id FROM `group` WHERE uid=?) g ON 1=1 ' +
56+
'WHERE u.username IN ' +
57+
question_marks,
58+
[uid, ...users],
59+
);
60+
}
61+
62+
async remove_users ({ uid, users }) {
63+
const question_marks =
64+
'(' + Array(users.length).fill('?').join(', ') + ')';
65+
/*
66+
DELETE FROM `jct_user_group`
67+
WHERE group_id = 1
68+
AND user_id IN (
69+
SELECT u.id
70+
FROM user u
71+
WHERE u.username IN ('user_that_shares', 'user_that_gets_shared_to')
72+
);
73+
*/
74+
await this.db.write(
75+
'DELETE FROM `jct_user_group` ' +
76+
'WHERE group_id = (SELECT id FROM `group` WHERE uid=?) ' +
77+
'AND user_id IN (' +
78+
'SELECT u.id FROM user u ' +
79+
'WHERE u.username IN ' +
80+
question_marks +
81+
')',
82+
[uid, ...users],
83+
);
84+
}
85+
}
86+
87+
module.exports = {
88+
GroupService,
89+
};

0 commit comments

Comments
 (0)