diff --git a/src/backend/src/data/hardcoded-permissions.js b/src/backend/src/data/hardcoded-permissions.js new file mode 100644 index 00000000..16d28987 --- /dev/null +++ b/src/backend/src/data/hardcoded-permissions.js @@ -0,0 +1,68 @@ +const default_implicit_user_app_permissions = { + 'driver:helloworld:greet': {}, + 'driver:puter-kvstore': {}, + 'driver:puter-ocr:recognize': {}, + 'driver:puter-chat-completion': {}, + 'driver:puter-image-generation': {}, + 'driver:puter-tts': {}, + 'driver:puter-apps': {}, + 'driver:puter-subdomains': {}, + 'driver:temp-email': {}, +}; + +const implicit_user_app_permissions = [ + { + id: 'builtin-apps', + apps: [ + 'app-0bef044f-918f-4cbf-a0c0-b4a17ee81085', // about + 'app-838dfbc4-bf8b-48c2-b47b-c4adc77fab58', // editor + 'app-58282b08-990a-4906-95f7-fa37ff92452b', // draw + 'app-0087b701-da09-4f49-a37d-2d6bcabc81ee', // minipaint + 'app-3fea7529-266e-47d9-8776-31649cd06557', // terminal + 'app-5584fbf7-ed69-41fc-99cd-85da21b1ef51', // camera + 'app-7bdca1a4-6373-4c98-ad97-03ff2d608ca1', // recorder + 'app-240a43f4-43b1-49bc-b9fc-c8ae719dab77', // dev-center + 'app-a2ae72a4-1ba3-4a29-b5c0-6de1be5cf178', // app-center + 'app-74378e84-b9cd-5910-bcb1-3c50fa96d6e7', // https://nj.puter.site + 'app-13a38aeb-f9f6-54f0-9bd3-9d4dd655ccfe', // https://cdpn.io + 'app-dce8f797-82b0-5d95-a2f8-ebe4d71b9c54', // https://null.jsbin.com + 'app-93005ce0-80d1-50d9-9b1e-9c453c375d56', // https://markus.puter.com + ], + permissions: { + 'driver:helloworld:greet': {}, + 'driver:puter-ocr:recognize': {}, + 'driver:puter-kvstore:get': {}, + 'driver:puter-kvstore:set': {}, + 'driver:puter-kvstore:del': {}, + 'driver:puter-kvstore:list': {}, + 'driver:puter-kvstore:flush': {}, + 'driver:puter-chat-completion:complete': {}, + 'driver:puter-image-generation:generate': {}, + 'driver:puter-analytics:create_trace': {}, + 'driver:puter-analytics:record': {}, + }, + }, + { + id: 'local-testing', + apps: [ + 'app-a392f3e5-35ca-5dac-ae10-785696cc7dec', // https://localhost + 'app-a6263561-6a84-5d52-9891-02956f9fac65', // https://127.0.0.1 + 'app-26149f0b-8304-5228-b995-772dadcf410e', // http://localhost + 'app-c2e27728-66d9-54dd-87cd-6f4e9b92e3e3', // http://127.0.0.1 + ], + permissions: { + 'driver:helloworld:greet': {}, + 'driver:puter-ocr:recognize': {}, + 'driver:puter-kvstore:get': {}, + 'driver:puter-kvstore:set': {}, + 'driver:puter-kvstore:del': {}, + 'driver:puter-kvstore:list': {}, + 'driver:puter-kvstore:flush': {}, + }, + }, +]; + +module.exports = { + implicit_user_app_permissions, + default_implicit_user_app_permissions, +}; diff --git a/src/backend/src/services/auth/PermissionService.js b/src/backend/src/services/auth/PermissionService.js index bd3fc358..1b8bc6b8 100644 --- a/src/backend/src/services/auth/PermissionService.js +++ b/src/backend/src/services/auth/PermissionService.js @@ -16,6 +16,11 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +const { + implicit_user_app_permissions, + default_implicit_user_app_permissions +} = require("../../data/hardcoded-permissions"); + const { get_user, get_app } = require("../../helpers"); const { AssignableMethodsFeature } = require("../../traits/AssignableMethodsFeature"); const { Context } = require("../../util/context"); @@ -23,70 +28,6 @@ const BaseService = require("../BaseService"); const { DB_WRITE } = require("../database/consts"); const { UserActorType, Actor, AppUnderUserActorType, AccessTokenActorType, SiteActorType } = require("./Actor"); -const default_implicit_user_app_permissions = { - 'driver:helloworld:greet': {}, - 'driver:puter-kvstore': {}, - 'driver:puter-ocr:recognize': {}, - 'driver:puter-chat-completion': {}, - 'driver:puter-image-generation': {}, - 'driver:puter-tts': {}, - 'driver:puter-apps': {}, - 'driver:puter-subdomains': {}, - 'driver:temp-email': {}, -}; - -const implicit_user_app_permissions = [ - { - id: 'builtin-apps', - apps: [ - 'app-0bef044f-918f-4cbf-a0c0-b4a17ee81085', // about - 'app-838dfbc4-bf8b-48c2-b47b-c4adc77fab58', // editor - 'app-58282b08-990a-4906-95f7-fa37ff92452b', // draw - 'app-0087b701-da09-4f49-a37d-2d6bcabc81ee', // minipaint - 'app-3fea7529-266e-47d9-8776-31649cd06557', // terminal - 'app-5584fbf7-ed69-41fc-99cd-85da21b1ef51', // camera - 'app-7bdca1a4-6373-4c98-ad97-03ff2d608ca1', // recorder - 'app-240a43f4-43b1-49bc-b9fc-c8ae719dab77', // dev-center - 'app-a2ae72a4-1ba3-4a29-b5c0-6de1be5cf178', // app-center - 'app-74378e84-b9cd-5910-bcb1-3c50fa96d6e7', // https://nj.puter.site - 'app-13a38aeb-f9f6-54f0-9bd3-9d4dd655ccfe', // https://cdpn.io - 'app-dce8f797-82b0-5d95-a2f8-ebe4d71b9c54', // https://null.jsbin.com - 'app-93005ce0-80d1-50d9-9b1e-9c453c375d56', // https://markus.puter.com - ], - permissions: { - 'driver:helloworld:greet': {}, - 'driver:puter-ocr:recognize': {}, - 'driver:puter-kvstore:get': {}, - 'driver:puter-kvstore:set': {}, - 'driver:puter-kvstore:del': {}, - 'driver:puter-kvstore:list': {}, - 'driver:puter-kvstore:flush': {}, - 'driver:puter-chat-completion:complete': {}, - 'driver:puter-image-generation:generate': {}, - 'driver:puter-analytics:create_trace': {}, - 'driver:puter-analytics:record': {}, - }, - }, - { - id: 'local-testing', - apps: [ - 'app-a392f3e5-35ca-5dac-ae10-785696cc7dec', // https://localhost - 'app-a6263561-6a84-5d52-9891-02956f9fac65', // https://127.0.0.1 - 'app-26149f0b-8304-5228-b995-772dadcf410e', // http://localhost - 'app-c2e27728-66d9-54dd-87cd-6f4e9b92e3e3', // http://127.0.0.1 - ], - permissions: { - 'driver:helloworld:greet': {}, - 'driver:puter-ocr:recognize': {}, - 'driver:puter-kvstore:get': {}, - 'driver:puter-kvstore:set': {}, - 'driver:puter-kvstore:del': {}, - 'driver:puter-kvstore:list': {}, - 'driver:puter-kvstore:flush': {}, - }, - }, -]; - const implicit_user_permissions = { // 'driver': {}, }; @@ -895,6 +836,25 @@ class PermissionService extends BaseService { }), }) + const reading = await this.scan(actor, permission); + const util = require('node:util'); + 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); const util = require('node:util'); ctx.log(JSON.stringify(reading, undefined, ' ')); diff --git a/src/backend/src/structured/sequence/scan-user-permission.js b/src/backend/src/structured/sequence/scan-user-permission.js index ebcabe35..3dbffc81 100644 --- a/src/backend/src/structured/sequence/scan-user-permission.js +++ b/src/backend/src/structured/sequence/scan-user-permission.js @@ -25,6 +25,9 @@ module.exports = new Sequence([ async function grant_if_system (a) { const reading = a.get('reading'); const { actor } = a.values(); + if ( !(actor.type instanceof UserActorType) ) { + return; + } if ( actor.type.user.username === 'system' ) { reading.push({ $: 'option', diff --git a/src/backend/src/unstructured/permission-scanners.js b/src/backend/src/unstructured/permission-scanners.js index 334b7f8d..c0d3deb0 100644 --- a/src/backend/src/unstructured/permission-scanners.js +++ b/src/backend/src/unstructured/permission-scanners.js @@ -1,5 +1,9 @@ +const { + default_implicit_user_app_permissions, + implicit_user_app_permissions, +} = require("../data/hardcoded-permissions"); const { get_user } = require("../helpers"); -const { Actor, UserActorType } = require("../services/auth/Actor"); +const { Actor, UserActorType, AppUnderUserActorType } = require("../services/auth/Actor"); const PERMISSION_SCANNERS = [ { @@ -21,6 +25,7 @@ const PERMISSION_SCANNERS = [ reading.push({ $: 'option', source: 'implied', + by: `implicator:${implicator.id}`, data: implied, }); } @@ -30,9 +35,11 @@ const PERMISSION_SCANNERS = [ { name: 'user-user', async scan (a) { - const reading = a.get('reading'); + const { reading, actor, permission_options } = a.values(); + if ( !(actor.type instanceof UserActorType) ) { + return; + } const db = a.iget('db'); - const { actor, permission_options } = a.values(); let sql_perm = permission_options.map(perm => { return `\`permission\` = ?` @@ -77,8 +84,10 @@ const PERMISSION_SCANNERS = [ { name: 'user-group-user', async scan (a) { - const reading = a.get('reading'); - const { actor, permission_options } = a.values(); + const { reading, actor, permission_options } = a.values(); + if ( !(actor.type instanceof UserActorType) ) { + return; + } const db = a.iget('db'); let sql_perm = permission_options.map((perm) => @@ -116,7 +125,76 @@ const PERMISSION_SCANNERS = [ }); } } - } + }, + { + name: 'user-app', + async scan (a) { + const { reading, actor, permission_options } = a.values(); + if ( !(actor.type instanceof AppUnderUserActorType) ) { + return; + } + const db = a.iget('db'); + + const app_uid = actor.type.app.uid; + + for ( const permission of permission_options ) { + { + const implied = default_implicit_user_app_permissions[permission]; + if ( implied ) { + reading.push({ + $: 'option', + source: 'implied', + by: 'user-app-hc-1', + data: implied, + }); + } + } { + const implicit_permissions = {}; + for ( const implicit_permission of implicit_user_app_permissions ) { + if ( implicit_permission.apps.includes(app_uid) ) { + implicit_permissions[permission] = implicit_permission.permissions[permission]; + } + } + if ( implicit_permissions[permission] ) { + reading.push({ + $: 'option', + source: 'implied', + by: 'user-app-hc-2', + data: implicit_permissions[permission], + }); + } + } + } + + let sql_perm = permission_options.map(() => + `\`permission\` = ?`).join(' OR '); + if ( permission_options.length > 1 ) sql_perm = '(' + sql_perm + ')'; + + // SELECT permission + const rows = await db.read( + 'SELECT * FROM `user_to_app_permissions` ' + + 'WHERE `user_id` = ? AND `app_id` = ? AND ' + + sql_perm, + [ + actor.type.user.id, + actor.type.app.id, + ...permission_options, + ] + ); + + if ( rows[0] ) { + const row = rows[0]; + const issuer_actor = actor.get_related_actor(UserActorType); + const issuer_reading = await a.icall('scan', issuer_actor, row.permission); + reading.push({ + $: 'path', + via: 'user-app', + issuer_username: actor.type.user.username, + reading: issuer_reading, + }); + } + } + }, ]; module.exports = {