mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Full rework of account management
Complete rewrite of the account management and related systems. Makes the architecture more modular, allowing for easier extensions and configurations.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import type { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import type { KeyValueStorage } from '../storage/keyvalue/KeyValueStorage';
|
||||
import type { ResourceStore } from '../storage/ResourceStore';
|
||||
@@ -40,15 +39,15 @@ export class ConfigPodManager implements PodManager {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public async createPod(identifier: ResourceIdentifier, settings: PodSettings): Promise<void> {
|
||||
this.logger.info(`Creating pod ${identifier.path}`);
|
||||
public async createPod(settings: PodSettings): Promise<void> {
|
||||
this.logger.info(`Creating pod ${settings.base.path}`);
|
||||
|
||||
// Will error in case there already is a store for the given identifier
|
||||
const store = await this.podGenerator.generate(identifier, settings);
|
||||
const store = await this.podGenerator.generate(settings);
|
||||
|
||||
await this.routingStorage.set(identifier.path, store);
|
||||
const count = await addGeneratedResources(identifier, settings, this.resourcesGenerator, this.store);
|
||||
await this.routingStorage.set(settings.base.path, store);
|
||||
const count = await addGeneratedResources(settings, this.resourcesGenerator, this.store);
|
||||
|
||||
this.logger.info(`Added ${count} resources to ${identifier.path}`);
|
||||
this.logger.info(`Added ${count} resources to ${settings.base.path}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import type { ResourceStore } from '../storage/ResourceStore';
|
||||
import { ConflictHttpError } from '../util/errors/ConflictHttpError';
|
||||
@@ -26,13 +25,13 @@ export class GeneratedPodManager implements PodManager {
|
||||
* Creates a new pod, pre-populating it with the resources created by the data generator.
|
||||
* Will throw an error if the given identifier already has a resource.
|
||||
*/
|
||||
public async createPod(identifier: ResourceIdentifier, settings: PodSettings, overwrite: boolean): Promise<void> {
|
||||
this.logger.info(`Creating pod ${identifier.path}`);
|
||||
if (!overwrite && await this.store.hasResource(identifier)) {
|
||||
throw new ConflictHttpError(`There already is a resource at ${identifier.path}`);
|
||||
public async createPod(settings: PodSettings, overwrite: boolean): Promise<void> {
|
||||
this.logger.info(`Creating pod ${settings.base.path}`);
|
||||
if (!overwrite && await this.store.hasResource(settings.base)) {
|
||||
throw new ConflictHttpError(`There already is a resource at ${settings.base.path}`);
|
||||
}
|
||||
|
||||
const count = await addGeneratedResources(identifier, settings, this.resourcesGenerator, this.store);
|
||||
this.logger.info(`Added ${count} resources to ${identifier.path}`);
|
||||
const count = await addGeneratedResources(settings, this.resourcesGenerator, this.store);
|
||||
this.logger.info(`Added ${count} resources to ${settings.base.path}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
|
||||
import type { PodSettings } from './settings/PodSettings';
|
||||
|
||||
/**
|
||||
@@ -8,9 +7,8 @@ import type { PodSettings } from './settings/PodSettings';
|
||||
export interface PodManager {
|
||||
/**
|
||||
* Creates a pod for the given settings.
|
||||
* @param identifier - Root identifier indicating where the pod should be created.
|
||||
* @param settings - Settings describing the pod.
|
||||
* @param overwrite - If the creation should proceed if there already is a resource there.
|
||||
*/
|
||||
createPod: (identifier: ResourceIdentifier, settings: PodSettings, overwrite: boolean) => Promise<void>;
|
||||
createPod: (settings: PodSettings, overwrite: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export class BaseResourcesGenerator implements TemplatedResourcesGenerator {
|
||||
this.store = args.store;
|
||||
}
|
||||
|
||||
public async* generate(templateFolder: string, location: ResourceIdentifier, options: Dict<string>):
|
||||
public async* generate(templateFolder: string, location: ResourceIdentifier, options: Dict<unknown>):
|
||||
AsyncIterable<Resource> {
|
||||
templateFolder = resolveAssetPath(templateFolder);
|
||||
|
||||
@@ -111,7 +111,7 @@ export class BaseResourcesGenerator implements TemplatedResourcesGenerator {
|
||||
/**
|
||||
* Generates results for all entries in the given folder, including the folder itself.
|
||||
*/
|
||||
private async* processFolder(folderLink: TemplateResourceLink, mapper: FileIdentifierMapper, options: Dict<string>):
|
||||
private async* processFolder(folderLink: TemplateResourceLink, mapper: FileIdentifierMapper, options: Dict<unknown>):
|
||||
AsyncIterable<Resource> {
|
||||
// Group resource links with their corresponding metadata links
|
||||
const links = await this.groupLinks(folderLink.filePath, mapper);
|
||||
@@ -175,7 +175,7 @@ export class BaseResourcesGenerator implements TemplatedResourcesGenerator {
|
||||
* If a ResourceLink of metadata is provided the corresponding metadata resource
|
||||
* will be yielded as a separate resource.
|
||||
*/
|
||||
private async* generateResource(link: TemplateResourceLink, options: Dict<string>, metaLink?: TemplateResourceLink):
|
||||
private async* generateResource(link: TemplateResourceLink, options: Dict<unknown>, metaLink?: TemplateResourceLink):
|
||||
AsyncIterable<Resource> {
|
||||
let data: Guarded<Readable> | undefined;
|
||||
const metadata = new RepresentationMetadata(link.identifier);
|
||||
@@ -211,7 +211,7 @@ export class BaseResourcesGenerator implements TemplatedResourcesGenerator {
|
||||
/**
|
||||
* Generates a RepresentationMetadata using the given template.
|
||||
*/
|
||||
private async generateMetadata(metaLink: TemplateResourceLink, options: Dict<string>):
|
||||
private async generateMetadata(metaLink: TemplateResourceLink, options: Dict<unknown>):
|
||||
Promise<RepresentationMetadata> {
|
||||
const metadata = new RepresentationMetadata(metaLink.identifier);
|
||||
|
||||
@@ -226,7 +226,7 @@ export class BaseResourcesGenerator implements TemplatedResourcesGenerator {
|
||||
/**
|
||||
* Creates a read stream from the file and applies the template if necessary.
|
||||
*/
|
||||
private async processFile(link: TemplateResourceLink, contents: Dict<string>): Promise<Guarded<Readable>> {
|
||||
private async processFile(link: TemplateResourceLink, contents: Dict<unknown>): Promise<Guarded<Readable>> {
|
||||
if (link.isTemplate) {
|
||||
const rendered = await this.templateEngine.handleSafe({ contents, template: { templateFile: link.filePath }});
|
||||
return guardedStreamFrom(rendered);
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import type { ResourceStore } from '../../storage/ResourceStore';
|
||||
import type { PodSettings } from '../settings/PodSettings';
|
||||
import type { ResourcesGenerator } from './ResourcesGenerator';
|
||||
|
||||
/**
|
||||
* Generates resources with the given generator and adds them to the given store.
|
||||
* @param identifier - Identifier of the pod.
|
||||
* @param settings - Settings from which the pod is being created.
|
||||
* @param generator - Generator to be used.
|
||||
* @param store - Store to be updated.
|
||||
*
|
||||
* @returns The amount of resources that were added.
|
||||
*/
|
||||
export async function addGeneratedResources(identifier: ResourceIdentifier, settings: NodeJS.Dict<string>,
|
||||
generator: ResourcesGenerator, store: ResourceStore): Promise<number> {
|
||||
const resources = generator.generate(identifier, settings);
|
||||
export async function addGeneratedResources(settings: PodSettings, generator: ResourcesGenerator,
|
||||
store: ResourceStore): Promise<number> {
|
||||
const resources = generator.generate(settings.base, settings);
|
||||
let count = 0;
|
||||
for await (const { identifier: resourceId, representation } of resources) {
|
||||
await store.setRepresentation(resourceId, representation);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import type { ResourceStore } from '../../storage/ResourceStore';
|
||||
import type { PodSettings } from '../settings/PodSettings';
|
||||
|
||||
@@ -12,10 +11,9 @@ export interface PodGenerator {
|
||||
* Creates a ResourceStore based on the given input.
|
||||
* Should error if there already is a store for the given identifier.
|
||||
*
|
||||
* @param identifier - Identifier of the new pod.
|
||||
* @param settings - Parameters to be used for the new pod.
|
||||
*
|
||||
* @returns A new ResourceStore to be used for the new pod.
|
||||
*/
|
||||
generate: (identifier: ResourceIdentifier, settings: PodSettings) => Promise<ResourceStore>;
|
||||
generate: (settings: PodSettings) => Promise<ResourceStore>;
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@ export interface ResourcesGenerator {
|
||||
*
|
||||
* @returns A map where the keys are the identifiers and the values the corresponding representations to store.
|
||||
*/
|
||||
generate: (location: ResourceIdentifier, options: Dict<string>) => AsyncIterable<Resource>;
|
||||
generate: (location: ResourceIdentifier, options: Dict<unknown>) => AsyncIterable<Resource>;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class StaticFolderGenerator implements ResourcesGenerator {
|
||||
this.templateFolder = templateFolder;
|
||||
}
|
||||
|
||||
public generate(location: ResourceIdentifier, options: Dict<string>): AsyncIterable<Resource> {
|
||||
public generate(location: ResourceIdentifier, options: Dict<unknown>): AsyncIterable<Resource> {
|
||||
return this.resourcesGenerator.generate(this.templateFolder, location, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class SubfolderResourcesGenerator implements TemplatedResourcesGenerator
|
||||
this.subfolders = subfolders;
|
||||
}
|
||||
|
||||
public async* generate(templateFolder: string, location: ResourceIdentifier, options: Dict<string>):
|
||||
public async* generate(templateFolder: string, location: ResourceIdentifier, options: Dict<unknown>):
|
||||
AsyncIterable<Resource> {
|
||||
const root = resolveAssetPath(templateFolder);
|
||||
const templateSubfolders = this.subfolders.map((subfolder): string => joinFilePath(root, subfolder));
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import type { KeyValueStorage } from '../../storage/keyvalue/KeyValueStorage';
|
||||
import type { ResourceStore } from '../../storage/ResourceStore';
|
||||
@@ -48,7 +47,8 @@ export class TemplatedPodGenerator implements PodGenerator {
|
||||
this.configTemplatePath = configTemplatePath ?? DEFAULT_CONFIG_PATH;
|
||||
}
|
||||
|
||||
public async generate(identifier: ResourceIdentifier, settings: PodSettings): Promise<ResourceStore> {
|
||||
public async generate(settings: PodSettings): Promise<ResourceStore> {
|
||||
const identifier = settings.base;
|
||||
if (!settings.template) {
|
||||
throw new BadRequestHttpError('Settings require template field.');
|
||||
}
|
||||
@@ -61,7 +61,7 @@ export class TemplatedPodGenerator implements PodGenerator {
|
||||
await this.variableHandler.handleSafe({ identifier, settings });
|
||||
|
||||
// Filter out irrelevant data in the agent
|
||||
const variables: NodeJS.Dict<string> = {};
|
||||
const variables: NodeJS.Dict<unknown> = {};
|
||||
for (const key of Object.keys(settings)) {
|
||||
if (isValidVariable(key)) {
|
||||
variables[key] = settings[key];
|
||||
@@ -78,7 +78,7 @@ export class TemplatedPodGenerator implements PodGenerator {
|
||||
|
||||
const store: ResourceStore =
|
||||
await this.storeFactory.generate(
|
||||
variables[TEMPLATE_VARIABLE.templateConfig]!,
|
||||
variables[TEMPLATE_VARIABLE.templateConfig] as string,
|
||||
TEMPLATE.ResourceStore,
|
||||
{ ...variables, 'urn:solid-server:default:variable:baseUrl': this.baseUrl },
|
||||
);
|
||||
|
||||
@@ -17,5 +17,5 @@ export interface TemplatedResourcesGenerator {
|
||||
*
|
||||
* @returns A map where the keys are the identifiers and the values the corresponding representations to store.
|
||||
*/
|
||||
generate: (templateFolder: string, location: ResourceIdentifier, options: Dict<string>) => AsyncIterable<Resource>;
|
||||
generate: (templateFolder: string, location: ResourceIdentifier, options: Dict<unknown>) => AsyncIterable<Resource>;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
|
||||
/**
|
||||
* Metadata related to pod generation.
|
||||
* Although the optional fields are not that relevant since this extends Dict,
|
||||
* they give an indication of what is sometimes expected.
|
||||
*/
|
||||
export interface PodSettings extends NodeJS.Dict<string> {
|
||||
export interface PodSettings extends NodeJS.Dict<unknown> {
|
||||
/**
|
||||
* The root of the pod. Determines where the pod will be created.
|
||||
*/
|
||||
base: ResourceIdentifier;
|
||||
/**
|
||||
* The WebId of the owner of this pod.
|
||||
*/
|
||||
@@ -22,7 +26,7 @@ export interface PodSettings extends NodeJS.Dict<string> {
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* The OIDC issuer of the owner's WebId.
|
||||
* The OIDC issuer of the owner's WebId. Necessary if the WebID in the pod is registered with the IDP.
|
||||
*/
|
||||
oidcIssuer?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user