mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-23 13:46:11 +00:00
feat(api): add /lsmod
This commit is contained in:
@@ -317,6 +317,9 @@ const install = async ({ services, app, useapi }) => {
|
||||
|
||||
const { FeatureFlagService } = require('./services/FeatureFlagService');
|
||||
services.registerService('feature-flag', FeatureFlagService);
|
||||
|
||||
const { KernelInfoService } = require('./services/KernelInfoService');
|
||||
services.registerService('kernel-info', KernelInfoService);
|
||||
}
|
||||
|
||||
const install_legacy = async ({ services }) => {
|
||||
|
||||
@@ -136,6 +136,7 @@ class Kernel extends AdvancedBase {
|
||||
|
||||
// Internal modules
|
||||
for ( const module of this.modules ) {
|
||||
services.registerModule(module.constructor.name, module);
|
||||
await module.install(Context.get());
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ const hardcoded_user_group_permissions = {
|
||||
'driver': {},
|
||||
'service': {},
|
||||
'feature': {},
|
||||
'kernel-info': {},
|
||||
},
|
||||
'b7220104-7905-4985-b996-649fdcdb3c8f': {
|
||||
'service:hello-world:ii:hello-world': policy_perm('temp.es'),
|
||||
|
||||
@@ -29,7 +29,33 @@ class Container {
|
||||
this.instances_ = {};
|
||||
this.implementors_ = {};
|
||||
this.ready = new TeePromise();
|
||||
|
||||
this.modname_ = null;
|
||||
this.modules_ = {}
|
||||
}
|
||||
|
||||
registerModule (name, module) {
|
||||
this.modules_[name] = {
|
||||
services_l: [],
|
||||
services_m: {},
|
||||
module
|
||||
};
|
||||
this.setModuleName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the current module registering services.
|
||||
*
|
||||
* Note: this is an antipattern; it would be a bit better to
|
||||
* provide the module name while registering a service, but
|
||||
* this requires making an implementor of Container's interface
|
||||
* with this as a hidden variable so as not to break existing
|
||||
* modules.
|
||||
*/
|
||||
setModuleName (name) {
|
||||
this.modname_ = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* registerService registers a service with the servuces container.
|
||||
*
|
||||
@@ -44,6 +70,12 @@ class Container {
|
||||
: new cls({ services: this, config, my_config, name, args }) ;
|
||||
this.instances_[name] = instance;
|
||||
|
||||
if ( this.modname_ ) {
|
||||
const mod_entry = this.modules_[this.modname_];
|
||||
mod_entry.services_l.push(name);
|
||||
mod_entry.services_m[name] = true;
|
||||
}
|
||||
|
||||
if ( !(instance instanceof AdvancedBase) ) return;
|
||||
|
||||
const traits = instance.list_traits();
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
const configurable_auth = require("../middleware/configurable_auth");
|
||||
const { Context } = require("../util/context");
|
||||
const { Endpoint } = require("../util/expressutil");
|
||||
const BaseService = require("./BaseService");
|
||||
const { Interface } = require("./drivers/meta/Construct");
|
||||
|
||||
const PERM_SEE_ALL = 'kernel-info:see-all-services';
|
||||
const PERM_SEE_DRIVERS = 'kernel-info:see-all-drivers';
|
||||
|
||||
class KernelInfoService extends BaseService {
|
||||
async _init () {
|
||||
//
|
||||
}
|
||||
|
||||
['__on_install.routes'] (_, { app }) {
|
||||
const router = (() => {
|
||||
const require = this.require;
|
||||
const express = require('express');
|
||||
return express.Router();
|
||||
})();
|
||||
|
||||
app.use('/', router);
|
||||
|
||||
Endpoint({
|
||||
route: '/lsmod',
|
||||
methods: ['GET', 'POST'],
|
||||
mw: [
|
||||
configurable_auth(),
|
||||
],
|
||||
handler: async (req, res) => {
|
||||
const svc_permission = this.services.get('permission');
|
||||
|
||||
const actor = Context.get('actor');
|
||||
const can_see_all = actor &&
|
||||
await svc_permission.check(actor, PERM_SEE_ALL);
|
||||
const can_see_drivers = actor &&
|
||||
await svc_permission.check(actor, PERM_SEE_DRIVERS);
|
||||
|
||||
const interfaces = {};
|
||||
const svc_registry = this.services.get('registry');
|
||||
const col_interfaces = svc_registry.get('interfaces');
|
||||
for ( const interface_name of col_interfaces.keys() ) {
|
||||
const iface = col_interfaces.get(interface_name);
|
||||
console.log('-->', interface_name, iface);
|
||||
if ( iface === undefined ) continue;
|
||||
if ( iface.no_sdk ) continue;
|
||||
interfaces[interface_name] = {
|
||||
spec: (new Interface(
|
||||
iface,
|
||||
{ name: interface_name }
|
||||
)).serialize(),
|
||||
implementors: {}
|
||||
}
|
||||
}
|
||||
|
||||
const services = [];
|
||||
const modules = [];
|
||||
for ( const k in this.services.modules_ ) {
|
||||
const module_info = {
|
||||
name: k,
|
||||
services: []
|
||||
};
|
||||
|
||||
modules.push(module_info);
|
||||
|
||||
for ( const s_k of this.services.modules_[k].services_l ) {
|
||||
const service_info = {
|
||||
name: s_k,
|
||||
traits: []
|
||||
};
|
||||
services.push(service_info);
|
||||
|
||||
const service = this.services.get(s_k);
|
||||
if ( service.list_traits ) {
|
||||
const traits = service.list_traits();
|
||||
for ( const trait of traits ) {
|
||||
const corresponding_iface = interfaces[trait];
|
||||
if ( ! corresponding_iface ) continue;
|
||||
corresponding_iface.implementors[s_k] = {};
|
||||
}
|
||||
service_info.traits = service.list_traits();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If actor doesn't have permission to see all drivers,
|
||||
// (granted by either "can_see_all" or "can_see_drivers")
|
||||
if ( ! can_see_all && ! can_see_drivers ) {
|
||||
// only show interfaces with at least one implementation
|
||||
// that the actor has permission to use
|
||||
for ( const iface_name in interfaces ) {
|
||||
for ( const impl_name in interfaces[iface_name].implementors ) {
|
||||
const perm = `service:${impl_name}:ii:${iface_name}`;
|
||||
const can_see_this = actor &&
|
||||
await svc_permission.check(actor, perm);
|
||||
if ( ! can_see_this ) {
|
||||
delete interfaces[iface_name].implementors[impl_name];
|
||||
}
|
||||
}
|
||||
if ( Object.keys(interfaces[iface_name].implementors).length < 1 ) {
|
||||
delete interfaces[iface_name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
interfaces,
|
||||
...(can_see_all ? { services } : {})
|
||||
});
|
||||
}
|
||||
}).attach(router);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
KernelInfoService,
|
||||
};
|
||||
@@ -49,7 +49,8 @@ class MapCollection extends AdvancedBase {
|
||||
}
|
||||
|
||||
keys () {
|
||||
return this.kv.keys(`registry:map:${this.map_id}:*`);
|
||||
const keys = this.kv.keys(`registry:map:${this.map_id}:*`);
|
||||
return keys.map(k => k.slice(`registry:map:${this.map_id}:`.length));
|
||||
}
|
||||
|
||||
_mk_key (key) {
|
||||
|
||||
@@ -214,6 +214,14 @@ class PermissionService extends BaseService {
|
||||
}
|
||||
return permission;
|
||||
}
|
||||
|
||||
async check (actor, permission_options) {
|
||||
// TODO: optimized implementation for check instead of
|
||||
// delegating to the scan() method
|
||||
const reading = await this.scan(actor, permission_options);
|
||||
const options = PermissionUtil.reading_to_options(reading);
|
||||
return options.length > 0;
|
||||
}
|
||||
|
||||
async scan (actor, permission_options) {
|
||||
const reading = [];
|
||||
|
||||
Reference in New Issue
Block a user