Skip to content

Commit 32f0edb

Browse files
committed
feat(api): add /lsmod
1 parent d009cd0 commit 32f0edb

File tree

7 files changed

+164
-1
lines changed

7 files changed

+164
-1
lines changed

src/backend/src/CoreModule.js

+3
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ const install = async ({ services, app, useapi }) => {
317317

318318
const { FeatureFlagService } = require('./services/FeatureFlagService');
319319
services.registerService('feature-flag', FeatureFlagService);
320+
321+
const { KernelInfoService } = require('./services/KernelInfoService');
322+
services.registerService('kernel-info', KernelInfoService);
320323
}
321324

322325
const install_legacy = async ({ services }) => {

src/backend/src/Kernel.js

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class Kernel extends AdvancedBase {
136136

137137
// Internal modules
138138
for ( const module of this.modules ) {
139+
services.registerModule(module.constructor.name, module);
139140
await module.install(Context.get());
140141
}
141142

src/backend/src/data/hardcoded-permissions.js

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const hardcoded_user_group_permissions = {
7878
'driver': {},
7979
'service': {},
8080
'feature': {},
81+
'kernel-info': {},
8182
},
8283
'b7220104-7905-4985-b996-649fdcdb3c8f': {
8384
'service:hello-world:ii:hello-world': policy_perm('temp.es'),

src/backend/src/services/Container.js

+32
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,33 @@ class Container {
2929
this.instances_ = {};
3030
this.implementors_ = {};
3131
this.ready = new TeePromise();
32+
33+
this.modname_ = null;
34+
this.modules_ = {}
35+
}
36+
37+
registerModule (name, module) {
38+
this.modules_[name] = {
39+
services_l: [],
40+
services_m: {},
41+
module
42+
};
43+
this.setModuleName(name);
3244
}
45+
46+
/**
47+
* Sets the name of the current module registering services.
48+
*
49+
* Note: this is an antipattern; it would be a bit better to
50+
* provide the module name while registering a service, but
51+
* this requires making an implementor of Container's interface
52+
* with this as a hidden variable so as not to break existing
53+
* modules.
54+
*/
55+
setModuleName (name) {
56+
this.modname_ = name;
57+
}
58+
3359
/**
3460
* registerService registers a service with the servuces container.
3561
*
@@ -44,6 +70,12 @@ class Container {
4470
: new cls({ services: this, config, my_config, name, args }) ;
4571
this.instances_[name] = instance;
4672

73+
if ( this.modname_ ) {
74+
const mod_entry = this.modules_[this.modname_];
75+
mod_entry.services_l.push(name);
76+
mod_entry.services_m[name] = true;
77+
}
78+
4779
if ( !(instance instanceof AdvancedBase) ) return;
4880

4981
const traits = instance.list_traits();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
const configurable_auth = require("../middleware/configurable_auth");
2+
const { Context } = require("../util/context");
3+
const { Endpoint } = require("../util/expressutil");
4+
const BaseService = require("./BaseService");
5+
const { Interface } = require("./drivers/meta/Construct");
6+
7+
const PERM_SEE_ALL = 'kernel-info:see-all-services';
8+
const PERM_SEE_DRIVERS = 'kernel-info:see-all-drivers';
9+
10+
class KernelInfoService extends BaseService {
11+
async _init () {
12+
//
13+
}
14+
15+
['__on_install.routes'] (_, { app }) {
16+
const router = (() => {
17+
const require = this.require;
18+
const express = require('express');
19+
return express.Router();
20+
})();
21+
22+
app.use('/', router);
23+
24+
Endpoint({
25+
route: '/lsmod',
26+
methods: ['GET', 'POST'],
27+
mw: [
28+
configurable_auth(),
29+
],
30+
handler: async (req, res) => {
31+
const svc_permission = this.services.get('permission');
32+
33+
const actor = Context.get('actor');
34+
const can_see_all = actor &&
35+
await svc_permission.check(actor, PERM_SEE_ALL);
36+
const can_see_drivers = actor &&
37+
await svc_permission.check(actor, PERM_SEE_DRIVERS);
38+
39+
const interfaces = {};
40+
const svc_registry = this.services.get('registry');
41+
const col_interfaces = svc_registry.get('interfaces');
42+
for ( const interface_name of col_interfaces.keys() ) {
43+
const iface = col_interfaces.get(interface_name);
44+
console.log('-->', interface_name, iface);
45+
if ( iface === undefined ) continue;
46+
if ( iface.no_sdk ) continue;
47+
interfaces[interface_name] = {
48+
spec: (new Interface(
49+
iface,
50+
{ name: interface_name }
51+
)).serialize(),
52+
implementors: {}
53+
}
54+
}
55+
56+
const services = [];
57+
const modules = [];
58+
for ( const k in this.services.modules_ ) {
59+
const module_info = {
60+
name: k,
61+
services: []
62+
};
63+
64+
modules.push(module_info);
65+
66+
for ( const s_k of this.services.modules_[k].services_l ) {
67+
const service_info = {
68+
name: s_k,
69+
traits: []
70+
};
71+
services.push(service_info);
72+
73+
const service = this.services.get(s_k);
74+
if ( service.list_traits ) {
75+
const traits = service.list_traits();
76+
for ( const trait of traits ) {
77+
const corresponding_iface = interfaces[trait];
78+
if ( ! corresponding_iface ) continue;
79+
corresponding_iface.implementors[s_k] = {};
80+
}
81+
service_info.traits = service.list_traits();
82+
}
83+
}
84+
}
85+
86+
// If actor doesn't have permission to see all drivers,
87+
// (granted by either "can_see_all" or "can_see_drivers")
88+
if ( ! can_see_all && ! can_see_drivers ) {
89+
// only show interfaces with at least one implementation
90+
// that the actor has permission to use
91+
for ( const iface_name in interfaces ) {
92+
for ( const impl_name in interfaces[iface_name].implementors ) {
93+
const perm = `service:${impl_name}:ii:${iface_name}`;
94+
const can_see_this = actor &&
95+
await svc_permission.check(actor, perm);
96+
if ( ! can_see_this ) {
97+
delete interfaces[iface_name].implementors[impl_name];
98+
}
99+
}
100+
if ( Object.keys(interfaces[iface_name].implementors).length < 1 ) {
101+
delete interfaces[iface_name];
102+
}
103+
}
104+
}
105+
106+
res.json({
107+
interfaces,
108+
...(can_see_all ? { services } : {})
109+
});
110+
}
111+
}).attach(router);
112+
}
113+
}
114+
115+
module.exports = {
116+
KernelInfoService,
117+
};

src/backend/src/services/RegistryService.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class MapCollection extends AdvancedBase {
4949
}
5050

5151
keys () {
52-
return this.kv.keys(`registry:map:${this.map_id}:*`);
52+
const keys = this.kv.keys(`registry:map:${this.map_id}:*`);
53+
return keys.map(k => k.slice(`registry:map:${this.map_id}:`.length));
5354
}
5455

5556
_mk_key (key) {

src/backend/src/services/auth/PermissionService.js

+8
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ class PermissionService extends BaseService {
214214
}
215215
return permission;
216216
}
217+
218+
async check (actor, permission_options) {
219+
// TODO: optimized implementation for check instead of
220+
// delegating to the scan() method
221+
const reading = await this.scan(actor, permission_options);
222+
const options = PermissionUtil.reading_to_options(reading);
223+
return options.length > 0;
224+
}
217225

218226
async scan (actor, permission_options) {
219227
const reading = [];

0 commit comments

Comments
 (0)