mirror of
https://github.com/HeyPuter/puter.git
synced 2026-01-07 21:50:25 -06:00
feat: add group management endpoints
This commit is contained in:
@@ -286,6 +286,12 @@ const install = async ({ services, app, useapi }) => {
|
||||
|
||||
const { ShareService } = require('./services/ShareService');
|
||||
services.registerService('share', ShareService);
|
||||
|
||||
const { GroupService } = require('./services/auth/GroupService');
|
||||
services.registerService('group', GroupService);
|
||||
|
||||
const { PermissionAPIService } = require('./services/PermissionAPIService');
|
||||
services.registerService('__permission-api', PermissionAPIService);
|
||||
}
|
||||
|
||||
const install_legacy = async ({ services }) => {
|
||||
|
||||
173
packages/backend/src/services/PermissionAPIService.js
Normal file
173
packages/backend/src/services/PermissionAPIService.js
Normal file
@@ -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,
|
||||
};
|
||||
@@ -26,12 +26,6 @@ class PuterAPIService extends BaseService {
|
||||
app.use(require('../routers/query/app'))
|
||||
app.use(require('../routers/change_username'))
|
||||
require('../routers/change_email')(app);
|
||||
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'))
|
||||
app.use(require('../routers/auth/list-sessions'))
|
||||
app.use(require('../routers/auth/revoke-session'))
|
||||
app.use(require('../routers/auth/check-app'))
|
||||
|
||||
89
packages/backend/src/services/auth/GroupService.js
Normal file
89
packages/backend/src/services/auth/GroupService.js
Normal file
@@ -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,
|
||||
};
|
||||
Reference in New Issue
Block a user