diff --git a/src/shell/commands/perms.ts b/src/shell/commands/perms.ts index c71c80c..d9f0498 100644 --- a/src/shell/commands/perms.ts +++ b/src/shell/commands/perms.ts @@ -2,11 +2,11 @@ import { Command } from 'commander'; import * as acl_perms from '../../commands/solid-perms_acl'; import { setPermission, listPermissions, IPermissionOperation, IPermissionListing } from '../../commands/solid-perms'; import authenticate from '../../authentication/authenticate'; -import { addEnvOptions, changeUrlPrefixes, getAndNormalizeURL } from '../../utils/shellutils'; -import { writeErrorString } from '../../utils/util'; +import { addEnvOptions, changeUrlPrefixes } from '../../utils/shellutils'; +import { discoverAccessMechanism, writeErrorString } from '../../utils/util'; import chalk from 'chalk'; import SolidCommand from './SolidCommand'; -import list from '../../commands/solid-list'; +import { acp_ess_2, hasAccessibleAcl, WithAcl } from "@inrupt/solid-client"; const Table = require('cli-table'); export default class PermsCommand extends SolidCommand { @@ -32,26 +32,36 @@ export default class PermsCommand extends SolidCommand { const authenticationInfo = await authenticate(programOpts) options.fetch = authenticationInfo.fetch url = await changeUrlPrefixes(authenticationInfo, url) - // try WAC - try { - const listings = await acl_perms.listPermissions(url, options) - if (listings?.access.agent || listings?.access.public) { - await formatACLPermissionListing(url, listings, options) - return; - } - } catch (e) { - if (options.verbose) writeErrorString('Unable to list permissions for WAC', e, options) + + + const { acp, acl } = await discoverAccessMechanism(url, options.fetch) + if ( !acp && !acl ) { + if (options.verbose) writeErrorString(`Could not list permissions for ${url}`, { message: "Could not find attached WAC or ACP management resource." }, options) + return; } - // try Universal - try { - const listings = await listPermissions(url, options) - if (listings?.access.agent || listings?.access.public) { - await formatPermissionListing(url, listings, options) - return; + if (acl) { + try { + const listings = await acl_perms.listPermissions(url, options) + if (listings?.access.agent || listings?.access.public) { + await formatACLPermissionListing(url, listings, options) + return; + } + } catch (e) { + if (options.verbose) writeErrorString('Unable to list permissions for WAC', e, options) } - } catch (e) { - if (options.verbose) writeErrorString('Unable to list permissions for ACP', e, options) } + if (acp) { + try { + const listings = await listPermissions(url, options) + if (listings?.access.agent || listings?.access.public) { + await formatPermissionListing(url, listings, options) + return; + } + } catch (e) { + if (options.verbose) writeErrorString('Unable to list permissions for ACP', e, options) + } + } + if (this.mayExit) process.exit(0) }) @@ -66,9 +76,8 @@ export default class PermsCommand extends SolidCommand { For the current authenticated user please set id to "u". For specific agents, set id to be the agent webid. `) - .option('--acl', 'Enables ACL specific operations --default and --group') - .option('--default', 'Set the defined permissions as default (only in --acl mode)') - .option('--group', 'Process identifier as a group identifier (only in --acl mode)') + .option('--default', 'Set the defined permissions as default (only for pods on a WAC-based solid server)') + .option('--group', 'Process identifier as a group identifier (only for pods on a WAC-based solid server)') .option('-v, --verbose', 'Log all operations') // Should this be default? .action( async (url: string, permissions: string[], options: any) => { @@ -109,18 +118,27 @@ export default class PermsCommand extends SolidCommand { return ({ type, id, read, write, append, control, default: def } as IPermissionOperation) }) for (let permission of parsedPermissions) { - try { - await acl_perms.changePermissions(url, [permission], options) - return; - } catch (e) { - if (options.verbose) writeErrorString(`Could not set permissions for ${permission.id} using WAC`, e, options) + const { acp, acl } = await discoverAccessMechanism(url, options.fetch) + if ( !acp && !acl ) { + if (options.verbose) writeErrorString(`Could not set permissions for ${permission.id}`, { message: "Could not find attached WAC or ACP management resource." }, options) + continue; } - try { - if (options.group || options.default) throw new Error("Cannot set WAC-specific options such as group and default for non-WAC environments ") - await setPermission(url, [permission], options) - return; - } catch (e) { - if (options.verbose) writeErrorString(`Could not set permissions for ${permission.id} using ACP`, e, options) + if (acp) { + try { + if (options.group || options.default) throw new Error("Cannot set WAC-specific options such as group and default for non-WAC environments ") + await setPermission(url, [permission], options) + return; + } catch (e) { + if (options.verbose) writeErrorString(`Could not set permissions for ${permission.id} using ACP`, e, options) + } + } + if (acl) { + try { + await acl_perms.changePermissions(url, [permission], options) + return; + } catch (e) { + if (options.verbose) writeErrorString(`Could not set permissions for ${permission.id} using WAC`, e, options) + } } } } diff --git a/src/utils/util.ts b/src/utils/util.ts index 8e155d9..9e42095 100644 --- a/src/utils/util.ts +++ b/src/utils/util.ts @@ -1,4 +1,4 @@ -import { getSolidDataset, getContainedResourceUrlAll, getUrl, getUrlAll, getThing, getThingAll, getDatetime, getInteger, SolidDataset } from '@inrupt/solid-client'; +import { getSolidDataset, getContainedResourceUrlAll, getUrl, getUrlAll, getThing, getThingAll, getDatetime, getInteger, SolidDataset, acp_ess_2, hasAccessibleAcl } from '@inrupt/solid-client'; import { requestUserIdp } from './userInteractions'; import type { Logger } from '../logger'; @@ -503,4 +503,18 @@ export async function isRDFResource(fileInfo: FileInfo, fetch: any) { if(!contentType) return false; else if (parseableExtensions.indexOf(mime.extension(contentType)) !== -1) return true; return false +} + +export async function discoverAccessMechanism(url: string, fetch: any) { + // We need to first check acp because Inrupt libs are kinda wack and reuse of acl rel header is confusing their own libs. + const acpInfo = await acp_ess_2.getResourceInfoWithAcr(url, { fetch }) + const acp = acp_ess_2.hasAccessibleAcr(acpInfo) + if (acp) return({ acp: true, acl: false }) + + // Now we check acl + const dataset = await acp_ess_2.getResourceInfoWithAccessDatasets(url, { fetch }) + const acl = hasAccessibleAcl(dataset) && !acp + if (acl) return({ acp: false, acl: true }) + + return ({ acp: false, acl: false }) } \ No newline at end of file