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:
105
src/init/SeededAccountInitializer.ts
Normal file
105
src/init/SeededAccountInitializer.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { readJson } from 'fs-extra';
|
||||
import { array, object, string } from 'yup';
|
||||
import { RepresentationMetadata } from '../http/representation/RepresentationMetadata';
|
||||
import type { JsonInteractionHandler } from '../identity/interaction/JsonInteractionHandler';
|
||||
import type { ResolveLoginHandler } from '../identity/interaction/login/ResolveLoginHandler';
|
||||
import { URL_SCHEMA } from '../identity/interaction/YupUtil';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { createErrorMessage } from '../util/errors/ErrorUtil';
|
||||
import { Initializer } from './Initializer';
|
||||
|
||||
const inSchema = array().of(object({
|
||||
email: string().trim().email().lowercase()
|
||||
.required(),
|
||||
password: string().trim().min(1).required(),
|
||||
pods: array().of(object({
|
||||
name: string().trim().min(1).required(),
|
||||
settings: object({
|
||||
webId: URL_SCHEMA,
|
||||
}).optional(),
|
||||
})).optional(),
|
||||
})).required();
|
||||
|
||||
export interface SeededAccountInitializerArgs {
|
||||
/**
|
||||
* Creates the accounts.
|
||||
*/
|
||||
accountHandler: ResolveLoginHandler;
|
||||
/**
|
||||
* Adds the login methods.
|
||||
*/
|
||||
passwordHandler: JsonInteractionHandler;
|
||||
/**
|
||||
* Creates the pods.
|
||||
*/
|
||||
podHandler: JsonInteractionHandler;
|
||||
/**
|
||||
* File path of the JSON describing the accounts to seed.
|
||||
*/
|
||||
configFilePath?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a set of accounts based on the input data.
|
||||
* These accounts have exactly 1 email/password login method, and 0 or more pods.
|
||||
* The pod settings that can be defined are identical to those of the {@link CreatePodHandler}.
|
||||
*/
|
||||
export class SeededAccountInitializer extends Initializer {
|
||||
protected readonly logger = getLoggerFor(this);
|
||||
|
||||
private readonly accountHandler: ResolveLoginHandler;
|
||||
private readonly passwordHandler: JsonInteractionHandler;
|
||||
private readonly podHandler: JsonInteractionHandler;
|
||||
private readonly configFilePath?: string;
|
||||
|
||||
public constructor(args: SeededAccountInitializerArgs) {
|
||||
super();
|
||||
this.accountHandler = args.accountHandler;
|
||||
this.passwordHandler = args.passwordHandler;
|
||||
this.podHandler = args.podHandler;
|
||||
this.configFilePath = args.configFilePath;
|
||||
}
|
||||
|
||||
public async handle(): Promise<void> {
|
||||
// This value being undefined means that the variable linking to the seed config is not defined
|
||||
// and this class should just do nothing.
|
||||
if (!this.configFilePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
let configuration: typeof inSchema.__outputType;
|
||||
try {
|
||||
configuration = await inSchema.validate(await readJson(this.configFilePath, 'utf8'));
|
||||
} catch (error: unknown) {
|
||||
const msg = `Invalid account seed file: ${createErrorMessage(error)}`;
|
||||
this.logger.error(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
// Dummy data for requests to all the handlers
|
||||
const method = 'POST';
|
||||
const target = { path: '' };
|
||||
const metadata = new RepresentationMetadata();
|
||||
|
||||
let accounts = 0;
|
||||
let pods = 0;
|
||||
for await (const input of configuration) {
|
||||
try {
|
||||
this.logger.info(`Creating account for ${input.email}`);
|
||||
const accountResult = await this.accountHandler.login({ method, target, metadata, json: {}});
|
||||
const { accountId } = accountResult.json;
|
||||
await this.passwordHandler.handleSafe({ method, target, metadata, accountId, json: input });
|
||||
accounts += 1;
|
||||
|
||||
for (const pod of input.pods ?? []) {
|
||||
this.logger.info(`Creating pod with name ${pod.name}`);
|
||||
await this.podHandler.handleSafe({ method, target, metadata, accountId, json: pod });
|
||||
pods += 1;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
this.logger.warn(`Error while initializing seeded account: ${createErrorMessage(error)}`);
|
||||
}
|
||||
}
|
||||
this.logger.info(`Initialized ${accounts} accounts and ${pods} pods.`);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { readJson } from 'fs-extra';
|
||||
import type { RegistrationManager } from '../identity/interaction/email-password/util/RegistrationManager';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { createErrorMessage } from '../util/errors/ErrorUtil';
|
||||
import { Initializer } from './Initializer';
|
||||
|
||||
/**
|
||||
* Uses a {@link RegistrationManager} to initialize accounts and pods
|
||||
* for all seeded pods. Reads the pod settings from seededPodConfigJson.
|
||||
*/
|
||||
export class SeededPodInitializer extends Initializer {
|
||||
protected readonly logger = getLoggerFor(this);
|
||||
|
||||
private readonly registrationManager: RegistrationManager;
|
||||
private readonly configFilePath: string | null;
|
||||
|
||||
public constructor(registrationManager: RegistrationManager, configFilePath: string | null) {
|
||||
super();
|
||||
this.registrationManager = registrationManager;
|
||||
this.configFilePath = configFilePath;
|
||||
}
|
||||
|
||||
public async handle(): Promise<void> {
|
||||
if (!this.configFilePath) {
|
||||
return;
|
||||
}
|
||||
const configuration = await readJson(this.configFilePath, 'utf8');
|
||||
|
||||
let count = 0;
|
||||
for await (const input of configuration) {
|
||||
const config = {
|
||||
confirmPassword: input.password,
|
||||
createPod: true,
|
||||
createWebId: true,
|
||||
register: true,
|
||||
...input,
|
||||
};
|
||||
|
||||
this.logger.info(`Initializing pod ${input.podName}`);
|
||||
|
||||
// Validate the input JSON
|
||||
const validated = this.registrationManager.validateInput(config, true);
|
||||
this.logger.debug(`Validated input: ${JSON.stringify(validated)}`);
|
||||
|
||||
// Register and/or create a pod as requested. Potentially does nothing if all booleans are false.
|
||||
try {
|
||||
await this.registrationManager.register(validated, true);
|
||||
this.logger.info(`Initialized seeded pod and account for "${input.podName}".`);
|
||||
count += 1;
|
||||
} catch (error: unknown) {
|
||||
this.logger.warn(`Error while initializing seeded pod: ${createErrorMessage(error)})}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.info(`Initialized ${count} seeded pods.`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user