|
@@ -23,6 +23,7 @@ const { TypedValue } = require("./meta/Runtime");
|
|
|
const BaseService = require("../BaseService");
|
|
|
const { Driver } = require("../../definitions/Driver");
|
|
|
const { PermissionUtil } = require("../auth/PermissionService");
|
|
|
+const { PolicyEnforcer } = require("./PolicyEnforcer");
|
|
|
|
|
|
/**
|
|
|
* DriverService provides the functionality of Puter drivers.
|
|
@@ -129,17 +130,87 @@ class DriverService extends BaseService {
|
|
|
const service = this.services.get(driver);
|
|
|
const reading = await svc_permission.scan(
|
|
|
actor,
|
|
|
- PermissionUtil.join('driver', driver, 'ii', iface),
|
|
|
+ PermissionUtil.join('service', driver, 'ii', iface),
|
|
|
);
|
|
|
+ console.log({
|
|
|
+ perm: PermissionUtil.join('service', driver, 'ii', iface),
|
|
|
+ reading,
|
|
|
+ });
|
|
|
const options = PermissionUtil.reading_to_options(reading);
|
|
|
if ( options.length > 0 ) {
|
|
|
- return await this.call_new_({
|
|
|
- service_name: driver,
|
|
|
- service,
|
|
|
- method,
|
|
|
- args: processed_args,
|
|
|
- iface,
|
|
|
+ const option = await this.select_best_option_(options);
|
|
|
+ const policies = await this.get_policies_for_option_(option);
|
|
|
+ console.log('SLA', JSON.stringify(policies, undefined, ' '));
|
|
|
+
|
|
|
+ // NOT FINAL: For now we apply monthly usage logic
|
|
|
+ // to the first holder of the permission. Later this
|
|
|
+ // will be changed so monthly usage can cascade across
|
|
|
+ // multiple actors. I decided not to implement this
|
|
|
+ // immediately because it's a hefty time sink and it's
|
|
|
+ // going to be some time before we can offer this feature
|
|
|
+ // to the end-user either way.
|
|
|
+
|
|
|
+ let effective_policy = null;
|
|
|
+ for ( const policy of policies ) {
|
|
|
+ if ( policy.holder ) {
|
|
|
+ effective_policy = policy;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( ! effective_policy ) {
|
|
|
+ throw new Error(
|
|
|
+ 'policies with no effective user are not yet ' +
|
|
|
+ 'supported'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // NOT FINAL: this will be handled by 'get_policies_for_option_'
|
|
|
+ // when cascading monthly usage is implemented.
|
|
|
+ const svc_systemData = this.services.get('system-data');
|
|
|
+ const svc_su = this.services.get('su');
|
|
|
+ effective_policy = await svc_su.sudo(async () => {
|
|
|
+ return await svc_systemData.interpret(effective_policy.data);
|
|
|
});
|
|
|
+
|
|
|
+ effective_policy = effective_policy.policy;
|
|
|
+
|
|
|
+ console.log('EFFECTIVE',
|
|
|
+ JSON.stringify(effective_policy, undefined, ' '));
|
|
|
+
|
|
|
+ const policy_enforcer = new PolicyEnforcer({
|
|
|
+ services: this.services,
|
|
|
+ actor,
|
|
|
+ policy: effective_policy,
|
|
|
+ driver, method,
|
|
|
+ });
|
|
|
+
|
|
|
+ try {
|
|
|
+ await policy_enforcer.check();
|
|
|
+ const result = await this.call_new_({
|
|
|
+ service_name: driver,
|
|
|
+ service,
|
|
|
+ method,
|
|
|
+ args: processed_args,
|
|
|
+ iface,
|
|
|
+ });
|
|
|
+ await policy_enforcer.on_success();
|
|
|
+ return result;
|
|
|
+ } catch (e) {
|
|
|
+ policy_enforcer.on_fail();
|
|
|
+ console.error(e);
|
|
|
+ let for_user = (e instanceof APIError) || (e instanceof DriverError);
|
|
|
+ if ( ! for_user ) this.errors.report(`driver:${iface}:${method}`, {
|
|
|
+ source: e,
|
|
|
+ trace: true,
|
|
|
+ // TODO: alarm will not be suitable for all errors.
|
|
|
+ alarm: true,
|
|
|
+ extra: {
|
|
|
+ args,
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return this._driver_response_from_error(e, meta);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -196,6 +267,32 @@ class DriverService extends BaseService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ async get_policies_for_option_ (option) {
|
|
|
+ // NOT FINAL: before implementing cascading monthly usage,
|
|
|
+ // this return will be removed and the code below it will
|
|
|
+ // be uncommented
|
|
|
+ return option.path;
|
|
|
+ /*
|
|
|
+ const svc_systemData = this.services.get('system-data');
|
|
|
+ const svc_su = this.services.get('su');
|
|
|
+
|
|
|
+ const policies = await Promise.all(option.path.map(async path_node => {
|
|
|
+ const policy = await svc_su.sudo(async () => {
|
|
|
+ return await svc_systemData.interpret(option.data);
|
|
|
+ });
|
|
|
+ return {
|
|
|
+ ...path_node,
|
|
|
+ policy,
|
|
|
+ };
|
|
|
+ }));
|
|
|
+ return policies;
|
|
|
+ */
|
|
|
+ }
|
|
|
+
|
|
|
+ async select_best_option_ (options) {
|
|
|
+ return options[0];
|
|
|
+ }
|
|
|
+
|
|
|
async call_new_ ({
|
|
|
service_name,
|
|
|
service, method, args,
|