diff --git a/src/backend/src/routers/auth/revoke-user-app.js b/src/backend/src/routers/auth/revoke-user-app.js
index fd455f8ef..7a4b58880 100644
--- a/src/backend/src/routers/auth/revoke-user-app.js
+++ b/src/backend/src/routers/auth/revoke-user-app.js
@@ -16,16 +16,16 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-import eggspress from '../../api/eggspress.js';
-import { UserActorType } from '../../services/auth/Actor.js';
-import { Context } from '../../util/context.js';
-import { APIError } from '../../api/APIError.js';
+const eggspress = require('../../api/eggspress');
+const { UserActorType } = require('../../services/auth/Actor');
+const { Context } = require('../../util/context');
+const APIError = require('../../api/APIError');
-export const revokeUserAppPermsRoute = eggspress('/auth/revoke-user-app', {
+module.exports = eggspress('/auth/revoke-user-app', {
subdomain: 'api',
auth2: true,
allowedMethods: ['POST'],
-}, async (req, res) => {
+}, async (req, res, next) => {
const x = Context.get();
const svc_permission = x.get('services').get('permission');
diff --git a/src/backend/src/services/DynamoKVStore/DynamoKVStore.ts b/src/backend/src/services/DynamoKVStore/DynamoKVStore.ts
index 18daa0a78..056157d5b 100644
--- a/src/backend/src/services/DynamoKVStore/DynamoKVStore.ts
+++ b/src/backend/src/services/DynamoKVStore/DynamoKVStore.ts
@@ -382,7 +382,7 @@ export class DynamoKVStore {
}
@Span('kv:flush')
- async flush ({ optConfig}: { optConfig?: { appUuid?: string } }) {
+ async flush ({ optConfig }: { optConfig?: { appUuid?: string } } = {}) {
const actor = Context.get('actor');
const app = actor.type.app ?? undefined;
diff --git a/src/backend/src/services/PermissionAPIService.js b/src/backend/src/services/PermissionAPIService.js
index 22721f898..94eb6d7f1 100644
--- a/src/backend/src/services/PermissionAPIService.js
+++ b/src/backend/src/services/PermissionAPIService.js
@@ -16,11 +16,10 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-const { APIError } = require('../api/APIError.js');
+const { APIError } = require('openai');
const configurable_auth = require('../middleware/configurable_auth');
const { Endpoint } = require('../util/expressutil');
-const { BaseService } = require('./BaseService');
-const { revokeUserAppPermsRoute } = require('../routers/auth/revoke-user-app');
+const BaseService = require('./BaseService');
/**
* @class PermissionAPIService
@@ -45,7 +44,7 @@ class PermissionAPIService extends BaseService {
async '__on_install.routes' (_, { app }) {
app.use(require('../routers/auth/get-user-app-token'));
app.use(require('../routers/auth/grant-user-app'));
- app.use(revokeUserAppPermsRoute);
+ app.use(require('../routers/auth/revoke-user-app'));
app.use(require('../routers/auth/grant-dev-app'));
app.use(require('../routers/auth/revoke-dev-app'));
app.use(require('../routers/auth/grant-user-user'));
diff --git a/src/backend/src/services/auth/PermissionService.js b/src/backend/src/services/auth/PermissionService.js
index 599b02d71..363f0c678 100644
--- a/src/backend/src/services/auth/PermissionService.js
+++ b/src/backend/src/services/auth/PermissionService.js
@@ -16,25 +16,35 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
-import { trace } from '@opentelemetry/api';
-import { APIError } from '../../api/APIError.js';
-import { setRedisCacheValue } from '../../clients/redis/cacheUpdate.js';
-import { deleteRedisKeys } from '../../clients/redis/deleteRedisKeys.js';
-import { redisClient } from '../../clients/redis/redisSingleton.js';
-import { hardcoded_user_group_permissions } from '../../data/hardcoded-permissions.js';
-import { ECMAP } from '../../filesystem/ECMAP.js';
-import { get_app, get_user } from '../../helpers.js';
-import scanSequence from '../../structured/sequence/scan-permission.mjs';
-import { reading_has_terminal } from '../../unstructured/permission-scan-lib.js';
-import { Context } from '../../util/context.js';
-import { spanify } from '../../util/otelutil.js';
-import { BaseService } from '../BaseService.js';
-import { Actor, UserActorType } from './Actor.js';
-import { MANAGE_PERM_PREFIX, PERM_KEY_PREFIX } from './permissionConts.mjs';
-import { PermissionScanRedisCacheSpace } from './PermissionScanRedisCacheSpace.js';
-import { PermissionExploder, PermissionImplicator, PermissionRewriter, PermissionUtil } from './permissionUtils.mjs';
+const APIError = require('../../api/APIError');
+const { hardcoded_user_group_permissions } = require('../../data/hardcoded-permissions.js');
+const { ECMAP } = require('../../filesystem/ECMAP');
+const { get_user, get_app } = require('../../helpers');
+const { reading_has_terminal } = require('../../unstructured/permission-scan-lib');
+const { trace } = require('@opentelemetry/api');
+const BaseService = require('../BaseService');
+const { DB_WRITE } = require('../database/consts');
+const { UserActorType, Actor, AppUnderUserActorType } = require('./Actor');
+const { PERM_KEY_PREFIX, MANAGE_PERM_PREFIX } = require('./permissionConts.mjs');
+const { PermissionUtil, PermissionExploder, PermissionImplicator, PermissionRewriter } = require('./permissionUtils.mjs');
+const { spanify } = require('../../util/otelutil');
+const { deleteRedisKeys } = require('../../clients/redis/deleteRedisKeys.js');
+const { setRedisCacheValue } = require('../../clients/redis/cacheUpdate.js');
+const { redisClient } = require('../../clients/redis/redisSingleton');
+const { PermissionScanRedisCacheSpace } = require('./PermissionScanRedisCacheSpace.js');
+const { Context } = require('../../util/context');
-export class PermissionService extends BaseService {
+/**
+* @class PermissionService
+* @extends BaseService
+* @description
+* The PermissionService class manages and enforces permissions within the application. It provides methods to:
+* - Check, grant, and revoke permissions for users and applications.
+* - Scan for existing permissions.
+* - Handle permission implications, rewriting, and explosion to support complex permission hierarchies.
+* This service interacts with the database to manage permissions and logs actions for auditing purposes.
+*/
+class PermissionService extends BaseService {
static CONCERN = 'permissions';
/**
* Initializes the PermissionService by setting up internal arrays for permission handling.
@@ -56,25 +66,45 @@ export class PermissionService extends BaseService {
* @throws {Error} If the provided exploder is not an instance of PermissionExploder.
*/
async _init () {
-
- this.kvService = this.services.get('puter-kvstore');
- this.db = this.services.get('database');
+ /**
+ * @type {import('../../modules/kvstore/KVStoreInterfaceService.js').KVStoreInterface} db
+ */
+ this.kvService = this.services.get('puter-kvstore').as('puter-kvstore');
+ this.db = this.services.get('database').get(DB_WRITE, 'permissions');
+ this._register_commands(this.services.get('commands'));
+ this.kvAvgTimes = { count: 0, avg: 0, max: 0 };
+ this.dbAvgTimes = { count: 0, avg: 0, max: 0 };
}
async '__on_boot.consolidation' () {
const svc_event = this.services.get('event');
// Event to allow extensions to add permissions
-
- const event = {};
- event.grant_to_everyone = permission => {
- hardcoded_user_group_permissions.system[this.global_config.default_temp_group][permission] = {};
- hardcoded_user_group_permissions.system[this.global_config.default_user_group][permission] = {};
- };
- event.grant_to_users = permission => {
- hardcoded_user_group_permissions[this.global_config.default_user_group][permission] = {};
- };
- svc_event.emit('create.permissions', event);
-
+ {
+ const event = {};
+ event.grant_to_everyone = permission => {
+ /* eslint-disable */
+ hardcoded_user_group_permissions
+ .system
+ [this.global_config.default_temp_group]
+ [permission]
+ = {};
+ hardcoded_user_group_permissions
+ .system
+ [this.global_config.default_user_group]
+ [permission]
+ = {};
+ /* eslint-enable */
+ };
+ event.grant_to_users = permission => {
+ /* eslint-disable */
+ hardcoded_user_group_permissions
+ [this.global_config.default_user_group]
+ [permission]
+ = {};
+ /* eslint-enable */
+ };
+ svc_event.emit('create.permissions', event);
+ }
}
/**
@@ -191,12 +221,13 @@ export class PermissionService extends BaseService {
// cylog(l, 'ACT & PERM:', actor.uid, permission_options);
const start_ts = Date.now();
- await scanSequence.call(this, {
- actor,
- permission_options,
- reading,
- state,
- });
+ await require('../../structured/sequence/scan-permission.mjs').default
+ .call(this, {
+ actor,
+ permission_options,
+ reading,
+ state,
+ });
const end_ts = Date.now();
// TODO: command to enable these logs
@@ -1241,4 +1272,64 @@ export class PermissionService extends BaseService {
this._permission_exploders.push(exploder);
}
-}
\ No newline at end of file
+
+ _register_commands (commands) {
+ commands.registerCommands('perms', [
+ {
+ id: 'grant-user-app',
+ handler: async (args, _log) => {
+ const [username, app_uid, permission, extra] = args;
+
+ // actor from username
+ const actor = new Actor({
+ type: new UserActorType({
+ user: await get_user({ username }),
+ }),
+ });
+
+ await this.grant_user_app_permission(actor, app_uid, permission, extra);
+ },
+ },
+ {
+ id: 'scan',
+ handler: async (args, ctx) => {
+ const [username, permission] = args;
+
+ // actor from username
+ const actor = new Actor({
+ type: new UserActorType({
+ user: await get_user({ username }),
+ }),
+ });
+
+ let reading = await this.scan(actor, permission);
+ // reading = PermissionUtil.reading_to_options(reading);
+ ctx.log(JSON.stringify(reading, undefined, ' '));
+ },
+ },
+ {
+ id: 'scan-app',
+ handler: async (args, ctx) => {
+ const [username, app_name, permission] = args;
+ const app = await get_app({ name: app_name });
+
+ // actor from username
+ const actor = new Actor({
+ type: new AppUnderUserActorType({
+ app,
+ user: await get_user({ username }),
+ }),
+ });
+
+ const reading = await this.scan(actor, permission);
+ // reading = PermissionUtil.reading_to_options(reading);
+ ctx.log(JSON.stringify(reading, undefined, ' '));
+ },
+ },
+ ]);
+ }
+}
+
+module.exports = {
+ PermissionService,
+};