PermissionAPIService.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * Copyright (C) 2024 Puter Technologies Inc.
  3. *
  4. * This file is part of Puter.
  5. *
  6. * Puter is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. const { APIError } = require("openai");
  20. const configurable_auth = require("../middleware/configurable_auth");
  21. const { Endpoint } = require("../util/expressutil");
  22. const { whatis } = require("../util/langutil");
  23. const BaseService = require("./BaseService");
  24. class PermissionAPIService extends BaseService {
  25. static MODULES = {
  26. express: require('express'),
  27. };
  28. async ['__on_install.routes'] (_, { 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/grant-user-group'));
  35. app.use(require('../routers/auth/revoke-user-group'));
  36. app.use(require('../routers/auth/list-permissions'))
  37. // track: scoping iife
  38. const r_group = (() => {
  39. const require = this.require;
  40. const express = require('express');
  41. return express.Router()
  42. })();
  43. this.install_group_endpoints_({ router: r_group });
  44. app.use('/group', r_group);
  45. }
  46. install_group_endpoints_ ({ router }) {
  47. Endpoint({
  48. route: '/create',
  49. methods: ['POST'],
  50. mw: [configurable_auth()],
  51. handler: async (req, res) => {
  52. const owner_user_id = req.user.id;
  53. const extra = req.body.extra ?? {};
  54. const metadata = req.body.metadata ?? {};
  55. if ( whatis(extra) !== 'object' ) {
  56. throw APIError.create('field_invalid', null, {
  57. key: 'extra',
  58. expected: 'object',
  59. got: whatis(extra),
  60. })
  61. }
  62. if ( whatis(metadata) !== 'object' ) {
  63. throw APIError.create('field_invalid', null, {
  64. key: 'metadata',
  65. expected: 'object',
  66. got: whatis(metadata),
  67. })
  68. }
  69. const svc_group = this.services.get('group');
  70. const uid = await svc_group.create({
  71. owner_user_id,
  72. // TODO: allow specifying these in request
  73. extra: {},
  74. metadata: {},
  75. });
  76. res.json({ uid });
  77. }
  78. }).attach(router);
  79. Endpoint({
  80. route: '/add-users',
  81. methods: ['POST'],
  82. mw: [configurable_auth()],
  83. handler: async (req, res) => {
  84. const svc_group = this.services.get('group')
  85. // TODO: validate string and uuid for request
  86. const group = await svc_group.get(
  87. { uid: req.body.uid });
  88. if ( ! group ) {
  89. throw APIError.create('entity_not_found', null, {
  90. identifier: req.body.uid,
  91. })
  92. }
  93. if ( group.owner_user_id !== req.user.id ) {
  94. throw APIError.create('forbidden');
  95. }
  96. if ( whatis(req.body.users) !== 'array' ) {
  97. throw APIError.create('field_invalid', null, {
  98. key: 'users',
  99. expected: 'array',
  100. got: whatis(req.body.users),
  101. });
  102. }
  103. for ( let i=0 ; i < req.body.users.length ; i++ ) {
  104. const value = req.body.users[i];
  105. if ( whatis(value) === 'string' ) continue;
  106. throw APIError.create('field_invalid', null, {
  107. key: `users[${i}]`,
  108. expected: 'string',
  109. got: whatis(value),
  110. });
  111. }
  112. await svc_group.add_users({
  113. uid: req.body.uid,
  114. users: req.body.users,
  115. });
  116. res.json({});
  117. }
  118. }).attach(router);
  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. // TODO: validate string and uuid for request
  127. const group = await svc_group.get(
  128. { uid: req.body.uid });
  129. if ( ! group ) {
  130. throw APIError.create('entity_not_found', null, {
  131. identifier: req.body.uid,
  132. })
  133. }
  134. if ( group.owner_user_id !== req.user.id ) {
  135. throw APIError.create('forbidden');
  136. }
  137. if ( whatis(req.body.users) !== 'array' ) {
  138. throw APIError.create('field_invalid', null, {
  139. key: 'users',
  140. expected: 'array',
  141. got: whatis(req.body.users),
  142. });
  143. }
  144. for ( let i=0 ; i < req.body.users.length ; i++ ) {
  145. const value = req.body.users[i];
  146. if ( whatis(value) === 'string' ) continue;
  147. throw APIError.create('field_invalid', null, {
  148. key: `users[${i}]`,
  149. expected: 'string',
  150. got: whatis(value),
  151. });
  152. }
  153. await svc_group.remove_users({
  154. uid: req.body.uid,
  155. users: req.body.users,
  156. });
  157. res.json({});
  158. }
  159. }).attach(router);
  160. Endpoint({
  161. route: '/list',
  162. methods: ['GET'],
  163. mw: [configurable_auth()],
  164. handler: async (req, res) => {
  165. const svc_group = this.services.get('group');
  166. // TODO: validate string and uuid for request
  167. const owned_groups = await svc_group.list_groups_with_owner(
  168. { owner_user_id: req.user.id });
  169. const in_groups = await svc_group.list_groups_with_member(
  170. { user_id: req.user.id });
  171. res.json({
  172. owned_groups: await Promise.all(owned_groups.map(
  173. g => g.get_client_value())),
  174. in_groups: await Promise.all(in_groups.map(
  175. g => g.get_client_value())),
  176. });
  177. }
  178. }).attach(router);
  179. }
  180. }
  181. module.exports = {
  182. PermissionAPIService,
  183. };