Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add access request & granting #37

Open
wants to merge 35 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
fc5615d
feat(header): move header to layout & add new request page
NuttyShrimp Oct 11, 2024
a4a639d
feat(controller): add controls for resources.json
NuttyShrimp Oct 11, 2024
1c8b197
feat(loama): add toggle to allow access request for a resource
NuttyShrimp Oct 11, 2024
ecd1e57
feat(loama): add url to set webID for request access to pod resources
NuttyShrimp Oct 14, 2024
e918c0a
refactor(controller): make store more abstract for re-usability sake
NuttyShrimp Oct 14, 2024
bae1950
refactor(controller): move request logic to seperate class
NuttyShrimp Oct 14, 2024
ffd2523
fix: update loama to match changes of previous commit
NuttyShrimp Oct 18, 2024
0b0d84e
feat: get data as tree + transform into usable data for library
NuttyShrimp Oct 18, 2024
3d5c9b6
fix(lobutton): fix inconsistency in border setting
NuttyShrimp Oct 18, 2024
ff2718a
feat(accessRequest): add check if inbox exists when pod url is changed
NuttyShrimp Oct 18, 2024
0edc30d
refactor(controller): move store creation into controller & modules
NuttyShrimp Oct 21, 2024
678f753
feat(accessRequest): add inbox store
NuttyShrimp Oct 25, 2024
f0c8b52
fix(store): support resource in containers
NuttyShrimp Oct 25, 2024
3e02846
feat(accessRequest): send accessRequest to loama inbox
NuttyShrimp Oct 25, 2024
c193282
feat(accessRequest): use acl link from resource info in request
NuttyShrimp Oct 25, 2024
5205743
fix(accessRequest): also make sure resources.json is created before s…
NuttyShrimp Nov 4, 2024
f6c7377
refactor(inbox): add inbox specific type & constructor
NuttyShrimp Nov 4, 2024
4227386
fix(accessRequest): filter requested resource to prevent requests for…
NuttyShrimp Nov 4, 2024
c375c3c
fix(accessRequest): make directories requestable
NuttyShrimp Nov 4, 2024
bf363cd
fix(button): also add colormix to button border
NuttyShrimp Nov 4, 2024
f8a9a1f
feat(loama): add inbox view to visualize access requests
NuttyShrimp Nov 4, 2024
bce19b0
feat(accessRequest): introduce response logic to access request
NuttyShrimp Nov 8, 2024
099dcf4
feat(accessRequest): send more data in request response
NuttyShrimp Nov 8, 2024
8091a72
fix(inrupAccessRequest): await file removal
NuttyShrimp Nov 8, 2024
ba1d9f7
feat(controller): Add logic to retrieve access request responses
NuttyShrimp Nov 8, 2024
c733759
feat(loama): add inbox to view & remove access request responses
NuttyShrimp Nov 8, 2024
39a57cc
fix(controller): fix rebase issues
NuttyShrimp Nov 18, 2024
f401557
fix(controller): await setpodurl
NuttyShrimp Nov 22, 2024
6aa1e3a
feat(requestView): add extra information to inputs
NuttyShrimp Nov 25, 2024
335c04f
feat(login): redirct to corrent path after login
NuttyShrimp Dec 2, 2024
92e5284
fix(loama): label clarifications
NuttyShrimp Dec 2, 2024
7a53613
fix(controller): only add append permission to inbox resource
NuttyShrimp Dec 2, 2024
352538a
feat: add permission options to requestable files
NuttyShrimp Dec 2, 2024
760af37
feat(inbox): add confirmation for control permission
NuttyShrimp Dec 6, 2024
308361b
chore(types): add additional comments to interface functions
NuttyShrimp Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 52 additions & 25 deletions controller/src/classes/Controller.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { BaseSubject, IndexItem, Permission, ResourcePermissions, SubjectPermissions } from "../types";
import { IController, IStore, SubjectConfig, SubjectConfigs, SubjectKey, SubjectType } from "../types/modules";
import { BaseSubject, Index, IndexItem, Permission, ResourcePermissions, Resources, SubjectPermissions } from "../types";
import { IAccessRequest, IController, IInboxConstructor, IStore, IStoreConstructor, SubjectConfig, SubjectConfigs, SubjectKey, SubjectType } from "../types/modules";
import { AccessRequest } from "./accessRequests/AccessRequest";
import { InruptAccessRequest } from "./accessRequests/InruptAccessRequest";
import { Mutex } from "./utils/Mutex";

export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>> extends Mutex implements IController<T> {
private store: IStore<T>
private index: IStore<Index<T[keyof T & string]>>
private resources: IStore<Resources>
private accessRequest: AccessRequest;
private subjectConfigs: SubjectConfigs<T>

constructor(store: IStore<T>, subjects: SubjectConfigs<T>) {
// TODO : Find a better way of constructing the controller with all the different modules
constructor(storeConstructor: IStoreConstructor, inboxConstructor: IInboxConstructor, subjects: SubjectConfigs<T>) {
super();
this.store = store;
// There is currently no "easy" solution to get around the as IStore...
this.index = new storeConstructor("index.json", () => ({ id: "", items: [] })) as IStore<Index<T[keyof T & string]>>;
this.resources = new storeConstructor("resources.json", () => ({ id: "", items: [] })) as IStore<Resources>;;
this.accessRequest = new InruptAccessRequest(this as unknown as Controller<{}>, inboxConstructor, this.resources);
this.subjectConfigs = subjects;
}

Expand Down Expand Up @@ -56,12 +64,12 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
subject: subject,
}

const index = await this.store.getCurrentIndex();
const index = await this.index.getCurrent();
index.items.push(item);
}

if (!alwaysKeepItem && permissions.length === 0 && manager.shouldDeleteOnAllRevoked()) {
const index = await this.store.getCurrentIndex();
const index = await this.index.getCurrent();
const idx = index.items.findIndex(i => i.id === item.id);
index.items.splice(idx, 1);
} else {
Expand All @@ -78,19 +86,27 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
}
}

await this.store.saveToRemoteIndex();
await this.index.saveToRemote();
}

setPodUrl(podUrl: string) {
this.store.setPodUrl(podUrl);
AccessRequest(): IAccessRequest {
return this.accessRequest;
}

async setPodUrl(podUrl: string) {
this.index.setPodUrl(podUrl);
this.resources.setPodUrl(podUrl);
await this.accessRequest.setPodUrl(podUrl)
}

unsetPodUrl() {
this.store.unsetPodUrl();
this.index.unsetPodUrl();
this.resources.unsetPodUrl();
this.accessRequest.unsetPodUrl();
}

async getOrCreateIndex() {
return this.store.getOrCreateIndex();
return this.index.getOrCreate();
}

getLabelForSubject<K extends SubjectKey<T>>(subject: T[K]): string {
Expand All @@ -101,7 +117,7 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
async getItem<K extends SubjectKey<T>>(resourceUrl: string, subject: SubjectType<T, K>): Promise<IndexItem<T[K]> | undefined> {
const { resolver } = this.getSubjectConfig<K>(subject);

const index = await this.store.getCurrentIndex<K>();
const index = await this.index.getCurrent() as Index<T[K]>;
return resolver.getItem(index, resourceUrl, subject.selector)
}

Expand Down Expand Up @@ -130,7 +146,7 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
await this.updateItem(resourceUrl, subject, []);

const subjectConfig = this.getSubjectConfig(subject);
const index = await this.store.getCurrentIndex<K>();
const index = await this.index.getCurrent() as Index<T[K]>;

const item = subjectConfig.resolver.getItem(index, resourceUrl, subject.selector);
if (!item) return;
Expand All @@ -140,7 +156,7 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
const idx = index.items.findIndex(i => subjectConfig.resolver.checkMatch(i.subject, subject));
index.items.splice(idx, 1);

await this.store.saveToRemoteIndex();
await this.index.saveToRemote();
}

async removePermission<K extends SubjectKey<T>>(resourceUrl: string, removedPermission: Permission, subject: SubjectType<T, K>) {
Expand Down Expand Up @@ -174,7 +190,7 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
await manager.createPermissions(resource, subject, item.permissions);

item.isEnabled = true;
await this.store.saveToRemoteIndex()
await this.index.saveToRemote()
}

async disablePermissions<K extends SubjectKey<T>>(resourceUrl: string, subject: SubjectType<T, K>) {
Expand All @@ -188,13 +204,13 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>

item.isEnabled = false;

await this.store.saveToRemoteIndex()
await this.index.saveToRemote()
}

async getContainerPermissionList(containerUrl: string): Promise<ResourcePermissions<T[keyof T]>[]> {
const configs: SubjectConfig<T>[] = Object.values(this.subjectConfigs);

const index = await this.store.getCurrentIndex();
const index = await this.index.getCurrent();
const resourcesToSkip = index.items.filter(i => {
if (!i.resource.includes(containerUrl)) {
return false;
Expand Down Expand Up @@ -247,46 +263,49 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
}
}));

await this.store.saveToRemoteIndex();
await this.index.saveToRemote();

return index.items.reduce<ResourcePermissions<T[keyof T]>[]>((arr, v) => {
return await index.items.reduce<Promise<ResourcePermissions<T[keyof T]>[]>>(async (arr, v) => {
const resourcePath = v.resource.replace(containerUrl, "");
const amountOfSlashes = resourcePath.replace(/[^\/]/g, "").length;
if ((amountOfSlashes == 1 && !resourcePath.endsWith("/")) || resourcePath.startsWith("/") || amountOfSlashes > 1) {
return arr;
}
let existingInfo = arr.find((info) => info.resourceUrl === v.resource);
let indexItems = await arr;
let existingInfo = indexItems.find((info) => info.resourceUrl === v.resource);
if (existingInfo) {
existingInfo.permissionsPerSubject.push({
permissions: v.permissions,
subject: v.subject,
isEnabled: v.isEnabled,
})
} else {
arr.push({
indexItems.push({
resourceUrl: v.resource,
canRequestAccess: await this.accessRequest.canRequestAccessToResource(v.resource),
permissionsPerSubject: [{
permissions: v.permissions,
subject: v.subject,
isEnabled: v.isEnabled,
}]
})
}
return arr;
}, []);
return indexItems;
}, Promise.resolve([]));
}

// NOTE: Do we want to force this to only use the index stored in the store?
async getResourcePermissionList(resourceUrl: string) {
// Need to put it in a variable because the type declaration vanishes
const configs: SubjectConfig<T>[] = Object.values(this.subjectConfigs);
const index = await this.store.getCurrentIndex<keyof T & string>();
const index = await this.index.getCurrent();
const results = await Promise.allSettled(configs.map(c => c.manager.getRemotePermissions<keyof T & string>(resourceUrl)))

let permissionsPerSubject = index.items.filter(i => i.resource === resourceUrl)

return {
resourceUrl,
canRequestAccess: await this.accessRequest.canRequestAccessToResource(resourceUrl),
permissionsPerSubject: results.reduce<SubjectPermissions<T[keyof T & string]>[]>((arr, v) => {
if (v.status === "fulfilled") {
v.value.forEach(remotePps => {
Expand All @@ -306,4 +325,12 @@ export class Controller<T extends Record<keyof T, BaseSubject<keyof T & string>>
}, permissionsPerSubject)
}
}

isSubjectSupported<K extends string, B extends BaseSubject<K>>(subject: BaseSubject<K>): IController<Record<K, B>> {
if (!this.subjectConfigs[subject.type as unknown as keyof T]) {

throw new Error(`Subject type ${subject.type} is not supported`);
}
return this as unknown as IController<Record<K, B>>
}
}
Loading