feat: Add a cache to the AgentGroupAccessChecker

This commit is contained in:
Joachim Van Herwegen
2021-08-19 15:37:51 +02:00
parent 401923b792
commit ff200e22a9
9 changed files with 102 additions and 32 deletions

View File

@@ -1,6 +1,8 @@
import type { Term } from 'n3';
import type { Store, Term } from 'n3';
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
import type { RepresentationConverter } from '../../storage/conversion/RepresentationConverter';
import type { ExpiringStorage } from '../../storage/keyvalue/ExpiringStorage';
import { fetchDataset } from '../../util/FetchUtil';
import { promiseSome } from '../../util/PromiseUtil';
import { readableToQuads } from '../../util/StreamUtil';
@@ -11,13 +13,23 @@ import { AccessChecker } from './AccessChecker';
/**
* Checks if the given WebID belongs to a group that has access.
* Implements the behaviour of groups from the WAC specification.
*
* Fetched results will be stored in an ExpiringStorage.
*
* Requires a storage that can store JS objects.
* `expiration` parameter is how long entries in the cache should be stored in seconds, defaults to 3600.
*/
export class AgentGroupAccessChecker extends AccessChecker {
private readonly converter: RepresentationConverter;
private readonly cache: ExpiringStorage<string, Promise<Store>>;
private readonly expiration: number;
public constructor(converter: RepresentationConverter) {
public constructor(converter: RepresentationConverter, cache: ExpiringStorage<string, Promise<Store>>,
expiration = 3600) {
super();
this.converter = converter;
this.cache = cache;
this.expiration = expiration * 1000;
}
public async handle({ acl, rule, credentials }: AccessCheckerArgs): Promise<boolean> {
@@ -42,9 +54,24 @@ export class AgentGroupAccessChecker extends AccessChecker {
const groupDocument: ResourceIdentifier = { path: /^[^#]*/u.exec(group.value)![0] };
// Fetch the required vCard group file
const dataset = await fetchDataset(groupDocument.path, this.converter);
const quads = await readableToQuads(dataset.data);
const quads = await this.fetchCachedQuads(groupDocument.path);
return quads.countQuads(group, VCARD.terms.hasMember, webId, null) !== 0;
}
/**
* Fetches quads from the given URL.
* Will cache the values for later re-use.
*/
private async fetchCachedQuads(url: string): Promise<Store> {
let result = await this.cache.get(url);
if (!result) {
const prom = (async(): Promise<Store> => {
const representation = await fetchDataset(url, this.converter);
return readableToQuads(representation.data);
})();
await this.cache.set(url, prom, this.expiration);
result = await prom;
}
return result;
}
}