From ab156297a746c0754145c2abdb2c99bb1b30651a Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Fri, 19 Jul 2024 15:19:04 -0400 Subject: [PATCH] feat: add system user --- .../database/SqliteDatabaseAccessService.js | 33 +++++++++- .../sqlite_setup/0025_system-user.dbmig.js | 63 +++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 src/backend/src/services/database/sqlite_setup/0025_system-user.dbmig.js diff --git a/src/backend/src/services/database/SqliteDatabaseAccessService.js b/src/backend/src/services/database/SqliteDatabaseAccessService.js index c99ba115..7de3982b 100644 --- a/src/backend/src/services/database/SqliteDatabaseAccessService.js +++ b/src/backend/src/services/database/SqliteDatabaseAccessService.js @@ -42,7 +42,7 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { this.db = new Database(this.config.path); // Database upgrade logic - const TARGET_VERSION = 22; + const TARGET_VERSION = 23; if ( do_setup ) { this.log.noticeme(`SETUP: creating database at ${this.config.path}`); @@ -71,6 +71,7 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { '0022_dev-center-max.sql', '0023_fix-kv.sql', '0024_default-groups.sql', + '0025_system-user.dbmig.js' ].map(p => path_.join(__dirname, 'sqlite_setup', p)); const fs = require('fs'); for ( const filename of sql_files ) { @@ -175,6 +176,10 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { upgrade_files.push('0024_default-groups.sql'); } + if ( user_version <= 22 ) { + upgrade_files.push('0025_system-user.dbmig.js'); + } + if ( upgrade_files.length > 0 ) { this.log.noticeme(`Database out of date: ${this.config.path}`); this.log.noticeme(`UPGRADING DATABASE: ${user_version} -> ${TARGET_VERSION}`); @@ -188,7 +193,20 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { const basename = path_.basename(filename); this.log.noticeme(`applying ${basename}`); const contents = fs.readFileSync(filename, 'utf8'); - this.db.exec(contents); + switch ( path_.extname(filename) ) { + case '.sql': + this.db.exec(contents); + break; + case '.js': + await this.run_js_migration_({ + filename, contents, + }); + break; + default: + throw new Error( + `unrecognized migration type: ${filename}` + ); + } } // Update version number @@ -273,6 +291,17 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { return p; }); } + + async run_js_migration_ ({ filename, contents }) { + contents = `(async () => {${contents}})()`; + const vm = require('vm'); + const context = vm.createContext({ + read: this.read.bind(this), + write: this.write.bind(this), + log: this.log, + }); + await vm.runInContext(contents, context); + } _register_commands (commands) { commands.registerCommands('sqlite', [ diff --git a/src/backend/src/services/database/sqlite_setup/0025_system-user.dbmig.js b/src/backend/src/services/database/sqlite_setup/0025_system-user.dbmig.js new file mode 100644 index 00000000..4b12a727 --- /dev/null +++ b/src/backend/src/services/database/sqlite_setup/0025_system-user.dbmig.js @@ -0,0 +1,63 @@ +/* +Add a user called `system`. + +If a user called `system` already exists, first rename the existing +user to the first username in this sequence: + system_, system_0, system_1, system_2, ... +*/ + +let existing_user; + +;[existing_user] = await read( + "SELECT username FROM `user` WHERE username='system'", +); + +if ( existing_user ) { + let replace_num = 0; + let replace_name = 'system_'; + + for (;;) { + ;[existing_user] = await read( + 'SELECT username FROM `user` WHERE username=?', + [replace_name] + ); + if ( ! existing_user ) break; + replace_name = 'system_' + (replace_num++); + } + + log.noticeme('updating existing user called system', { + replace_num, + replace_name, + }); + + await write( + `UPDATE \`user\` SET username=? WHERE username='system' LIMIT 1`, + [replace_name] + ); +} + +const { insertId: system_user_id } = await write( + 'INSERT INTO `user` (`uuid`, `username`) VALUES (?, ?)', + [ + '5d4adce0-a381-4982-9c02-6e2540026238', + 'system', + ] +); + +const [{id: system_group_id}] = await read( + 'SELECT id FROM `group` WHERE uid=?', + ['26bfb1fb-421f-45bc-9aa4-d81ea569e7a5'] +); + +const [{id: admin_group_id}] = await read( + 'SELECT id FROM `group` WHERE uid=?', + ['ca342a5e-b13d-4dee-9048-58b11a57cc55'] +); + +// admin group has unlimited access to all drivers +await write( + 'INSERT INTO `user_to_group_permissions` ' + + '(`user_id`, `group_id`, `permission`, `extra`) ' + + 'VALUES (?, ?, ?, ?)', + [system_user_id, admin_group_id, 'driver', '{}'] +);