/
solid-perms.ts
176 lines (160 loc) · 6.42 KB
/
solid-perms.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import {
getAgentAccessAll,
AgentAccess,
Access,
getPublicAccess,
getResourceAcl,
getGroupAccessAll,
getAgentDefaultAccessAll,
getPublicDefaultAccess,
getGroupDefaultAccessAll,
getAgentResourceAccessAll,
getGroupResourceAccessAll,
getPublicResourceAccess,
getResourceInfoWithAcl,
setAgentDefaultAccess,
setGroupDefaultAccess,
setPublicDefaultAccess,
setAgentResourceAccess,
setGroupResourceAccess,
setPublicResourceAccess,
hasAccessibleAcl,
getFallbackAcl,
AclDataset,
saveAclFor,
WithAccessibleAcl,
deleteAclFor,
hasResourceAcl,
createAclFromFallbackAcl,
hasFallbackAcl,
createAcl,
} from '@inrupt/solid-client';
import { writeErrorString } from '../utils/util';
export type QueryOptions = {
fetch: any
verbose?: boolean
}
type Record<K extends keyof any, T> = {
[P in K]: T;
};
export type PermissionListing = {
access: {
agent?: null | Record<string, Access>,
group?: null | Record<string, Access>,
public?: null | Access
},
default?: {
agent?: null | AgentAccess,
group?: null | Record<string, Access>,
public?: null | Access
}
resource?: {
agent?: null | AgentAccess,
group?: null | Record<string, Access>,
public?: null | Access
}
}
export async function listPermissions(resourceUrl: string, options: QueryOptions) {
let permissions : PermissionListing = { access: {} }
try {
const resourceInfo = await getResourceInfoWithAcl(resourceUrl, { fetch: options.fetch })
permissions.access.agent = await getAgentAccessAll(resourceInfo)
permissions.access.group = await getGroupAccessAll(resourceInfo)
permissions.access.public = await getPublicAccess(resourceInfo)
let aclDataset = getResourceAcl(resourceInfo);
if (aclDataset) {
permissions.default = {};
permissions.default.agent = await getAgentDefaultAccessAll(aclDataset)
permissions.default.group = await getGroupDefaultAccessAll(aclDataset)
permissions.default.public = await getPublicDefaultAccess(aclDataset)
permissions.resource = {};
permissions.resource.agent = await getAgentResourceAccessAll(aclDataset)
permissions.resource.group = await getGroupResourceAccessAll(aclDataset)
permissions.resource.public = await getPublicResourceAccess(aclDataset)
}
return permissions
} catch (e) {
if (options.verbose) writeErrorString(`Could not retrieve permissions for ${resourceUrl}`, e)
}
}
export type PermissionOperation = {
type: "agent" | 'group' | 'public',
id?: string,
read?: boolean,
write?: boolean,
append?: boolean,
control?: boolean,
default?: boolean,
}
export async function changePermissions(resourceUrl: string, operations: PermissionOperation[], options: QueryOptions) {
const resourceInfo = await getResourceInfoWithAcl(resourceUrl, { fetch: options.fetch })
let aclDataset : AclDataset | null;
if (await hasResourceAcl(resourceInfo)) {
aclDataset = await getResourceAcl(resourceInfo);
} else {
try {
if (hasFallbackAcl(resourceInfo) && hasAccessibleAcl(resourceInfo)) {
aclDataset = await createAclFromFallbackAcl(resourceInfo)
} else if (hasAccessibleAcl(resourceInfo)) {
aclDataset = await createAcl(resourceInfo)
} else {
throw new Error('No acl found in path to root. This tool requires at least a root acl to be set.2');
}
} catch (e) {
throw new Error(`Could not find fallback ACL file to initialize permissions for ${resourceUrl}: ${(<Error>e).message}`)
}
}
if (!aclDataset) {
throw new Error(`You do not have the permissions to edit the ACL file for ${resourceUrl}`)
}
for (let operation of operations) {
if (operation.type === 'agent') {
// Update access rights
if (!operation.id) { throw new Error('Please specify agent id in the passed operation.')}
let access = { read: false, write: false, append: false, control: false }
access = updateAccess(access, operation)
// Update local acl for agent with new rights
if (operation.default) aclDataset = await setAgentDefaultAccess(aclDataset, operation.id, access)
aclDataset = await setAgentResourceAccess(aclDataset, operation.id, access)
} else if (operation.type === 'group') {
// Update access rights
if (!operation.id) { throw new Error('Please specify group id in the passed operation.')}
let access = { read: false, write: false, append: false, control: false }
access = updateAccess(access, operation)
// Update local acl for group with new rights
if (operation.default) aclDataset = await setGroupDefaultAccess(aclDataset, operation.id, access)
aclDataset = await setGroupResourceAccess(aclDataset, operation.id, access)
} else if (operation.type === 'public') {
// Update access rights
if (!operation.id) { throw new Error('Please specify agent id in the passed operation.')}
let access = { read: false, write: false, append: false, control: false }
access = updateAccess(access, operation)
// Update local acl for agent with new rights
if (operation.default) aclDataset = await setPublicDefaultAccess(aclDataset, access)
aclDataset = await setPublicResourceAccess(aclDataset, access)
} else {
if (options.verbose) writeErrorString("Incorrect operation type", 'Please provide an operation type of agent, group or public.')
}
}
// Post updated acl to pod
if (aclDataset && await hasAccessibleAcl(resourceInfo)) {
await saveAclFor(resourceInfo as WithAccessibleAcl, aclDataset, {fetch: options.fetch})
if (options.verbose) console.log(`Updated permissions for: ${resourceUrl}`)
}
}
export async function deletePermissions(resourceUrl: string, options: QueryOptions) {
let resourceInfo = await getResourceInfoWithAcl(resourceUrl, {fetch: options.fetch})
if (hasAccessibleAcl(resourceInfo)) {
await deleteAclFor(resourceInfo, {fetch: options.fetch})
if (options.verbose) console.log(`Deleted resource at ${resourceUrl}`)
} else {
throw Error(`Resource at ${resourceUrl} does not have an accessible ACL resource`)
}
}
function updateAccess(access: Access, operation: PermissionOperation) {
if (operation.read !== undefined) access.read = operation.read
if (operation.write !== undefined) access.write = operation.write
if (operation.append !== undefined) access.append = operation.append
if (operation.control !== undefined) access.control = operation.control
return access
}