ShareTestService.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * Copyright (C) 2024-present 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. // TODO: accessing these imports directly from a mod is not really
  20. // the way mods are intended to work; this is temporary until
  21. // we have these things registered in "useapi".
  22. const {
  23. get_user,
  24. invalidate_cached_user,
  25. deleteUser,
  26. } = require('../../../src/backend/src/helpers.js');
  27. const { HLWrite } = require('../../../src/backend/src/filesystem/hl_operations/hl_write.js');
  28. const { LLRead } = require('../../../src/backend/src/filesystem/ll_operations/ll_read.js');
  29. const { Actor, UserActorType }
  30. = require('../../../src/backend/src/services/auth/Actor.js');
  31. const { DB_WRITE } = require('../../../src/backend/src/services/database/consts.js');
  32. const {
  33. RootNodeSelector,
  34. NodeChildSelector,
  35. NodePathSelector,
  36. } = require('../../../src/backend/src/filesystem/node/selectors.js');
  37. const { Context } = require('../../../src/backend/src/util/context.js');
  38. class ShareTestService extends use.Service {
  39. static MODULES = {
  40. uuidv4: require('uuid').v4,
  41. }
  42. async _init () {
  43. const svc_commands = this.services.get('commands');
  44. this._register_commands(svc_commands);
  45. this.scenarios = require('./data/sharetest_scenarios');
  46. const svc_db = this.services.get('database');
  47. this.db = svc_db.get(svc_db.DB_WRITE, 'share-test');
  48. }
  49. _register_commands (commands) {
  50. commands.registerCommands('share-test', [
  51. {
  52. id: 'start',
  53. description: '',
  54. handler: async (_, log) => {
  55. const results = await this.runit();
  56. for ( const result of results ) {
  57. log.log(`=== ${result.title} ===`);
  58. if ( ! result.report ) {
  59. log.log(`\x1B[32;1mSUCCESS\x1B[0m`);
  60. continue;
  61. }
  62. log.log(
  63. `\x1B[31;1mSTOPPED\x1B[0m at ` +
  64. `${result.report.step}: ` +
  65. result.report.report.message,
  66. );
  67. }
  68. }
  69. }
  70. ]);
  71. }
  72. async runit () {
  73. await this.teardown_();
  74. await this.setup_();
  75. const results = [];
  76. for ( const scenario of this.scenarios ) {
  77. if ( ! scenario.title ) {
  78. scenario.title = scenario.sequence.map(
  79. step => step.title).join('; ')
  80. }
  81. results.push({
  82. title: scenario.title,
  83. report: await this.run_scenario_(scenario)
  84. });
  85. }
  86. await this.teardown_();
  87. return results;
  88. }
  89. async setup_ () {
  90. await this.create_test_user_('testuser_eric');
  91. await this.create_test_user_('testuser_stan');
  92. await this.create_test_user_('testuser_kyle');
  93. await this.create_test_user_('testuser_kenny');
  94. }
  95. async run_scenario_ (scenario) {
  96. let error;
  97. // Run sequence
  98. for ( const step of scenario.sequence ) {
  99. const method = this[`__scenario:${step.call}`];
  100. const user = await get_user({ username: step.as })
  101. const actor = await Actor.create(UserActorType, { user });
  102. const generated = { user, actor };
  103. const report = await Context.get().sub({ user, actor })
  104. .arun(async () => {
  105. return await method.call(this, generated, step.with);
  106. });
  107. if ( report ) {
  108. error = { step: step.title, report };
  109. break;
  110. }
  111. }
  112. return error;
  113. }
  114. async teardown_ () {
  115. await this.delete_test_user_('testuser_eric');
  116. await this.delete_test_user_('testuser_stan');
  117. await this.delete_test_user_('testuser_kyle');
  118. await this.delete_test_user_('testuser_kenny');
  119. }
  120. async create_test_user_ (username) {
  121. await this.db.write(
  122. `
  123. INSERT INTO user (uuid, username, email, free_storage, password)
  124. VALUES (?, ?, ?, ?, ?)
  125. `,
  126. [
  127. this.modules.uuidv4(),
  128. username,
  129. username + '@example.com',
  130. 1024 * 1024 * 500, // 500 MiB
  131. this.modules.uuidv4(),
  132. ],
  133. );
  134. const user = await get_user({ username });
  135. const svc_user = this.services.get('user');
  136. await svc_user.generate_default_fsentries({ user });
  137. invalidate_cached_user(user);
  138. return user;
  139. }
  140. async delete_test_user_ (username) {
  141. const user = await get_user({ username });
  142. if ( ! user ) return;
  143. await deleteUser(user.id);
  144. }
  145. // API for scenarios
  146. async ['__scenario:create-example-file'] (
  147. { actor, user },
  148. { name, contents },
  149. ) {
  150. const svc_fs = this.services.get('filesystem');
  151. const parent = await svc_fs.node(new NodePathSelector(
  152. `/${user.username}/Desktop`
  153. ));
  154. console.log('test -> create-example-file',
  155. user, name, contents);
  156. const buffer = Buffer.from(contents);
  157. const file = {
  158. size: buffer.length,
  159. name: name,
  160. type: 'application/octet-stream',
  161. buffer,
  162. };
  163. const hl_write = new HLWrite();
  164. await hl_write.run({
  165. actor,
  166. user,
  167. destination_or_parent: parent,
  168. specified_name: name,
  169. file,
  170. });
  171. }
  172. async ['__scenario:assert-no-access'] (
  173. { actor, user },
  174. { path },
  175. ) {
  176. const svc_fs = this.services.get('filesystem');
  177. const node = await svc_fs.node(new NodePathSelector(path));
  178. const ll_read = new LLRead();
  179. let expected_e; try {
  180. const stream = await ll_read.run({
  181. fsNode: node,
  182. actor,
  183. })
  184. } catch (e) {
  185. expected_e = e;
  186. }
  187. if ( ! expected_e ) {
  188. return { message: 'expected error, got none' };
  189. }
  190. }
  191. async ['__scenario:grant'] (
  192. { actor, user },
  193. { to, permission },
  194. ) {
  195. const svc_permission = this.services.get('permission');
  196. await svc_permission.grant_user_user_permission(
  197. actor, to, permission, {}, {},
  198. );
  199. }
  200. async ['__scenario:assert-access'] (
  201. { actor, user },
  202. { path, level }
  203. ) {
  204. const svc_fs = this.services.get('filesystem');
  205. const svc_acl = this.services.get('acl');
  206. const node = await svc_fs.node(new NodePathSelector(path));
  207. const has_read = await svc_acl.check(actor, node, 'read');
  208. const has_write = await svc_acl.check(actor, node, 'write');
  209. if ( level !== 'write' && level !== 'read' ) {
  210. return {
  211. message: 'unexpected value for "level" parameter'
  212. };
  213. }
  214. if ( level === 'read' && has_write ) {
  215. return {
  216. message: 'expected read-only but actor can write'
  217. };
  218. }
  219. if ( level === 'read' && !has_read ) {
  220. return {
  221. message: 'expected read access but no read access'
  222. };
  223. }
  224. if ( level === 'write' && (!has_write || !has_read) ) {
  225. return {
  226. message: 'expected write access but no write access'
  227. };
  228. }
  229. }
  230. }
  231. module.exports = {
  232. ShareTestService,
  233. };