Rework user types: create AuthenticatedUser and AnonymousUser class
Both are subclasses of an abstract User class which contains almost everything interesting.
This commit is contained in:
@@ -51,39 +51,15 @@ const defaultRolePermissions: RolePermissionMap = new Map([
|
||||
["user", ["users:read"]],
|
||||
]);
|
||||
|
||||
export class User {
|
||||
private readonly data: UserData;
|
||||
private rolePermissions: RolePermissionMap;
|
||||
export abstract class User {
|
||||
protected readonly data: UserData;
|
||||
protected rolePermissions: RolePermissionMap;
|
||||
|
||||
constructor(data: UserData, rolePermissions?: RolePermissionMap) {
|
||||
this.data = userDataParser.parse(data);
|
||||
this.rolePermissions = rolePermissions ?? defaultRolePermissions;
|
||||
}
|
||||
|
||||
// Factory for creating new users with sensible defaults
|
||||
static create(
|
||||
email: string,
|
||||
options?: {
|
||||
id?: string;
|
||||
displayName?: string;
|
||||
status?: UserStatus;
|
||||
roles?: Role[];
|
||||
permissions?: Permission[];
|
||||
},
|
||||
): User {
|
||||
const now = new Date();
|
||||
return new User({
|
||||
id: options?.id ?? crypto.randomUUID(),
|
||||
email,
|
||||
displayName: options?.displayName,
|
||||
status: options?.status ?? "active",
|
||||
roles: options?.roles ?? [],
|
||||
permissions: options?.permissions ?? [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
// Identity
|
||||
get id(): UserId {
|
||||
return this.data.id as UserId;
|
||||
@@ -185,15 +161,72 @@ export class User {
|
||||
toString(): string {
|
||||
return `User(id ${this.id})`;
|
||||
}
|
||||
|
||||
abstract isAnonymous(): boolean;
|
||||
}
|
||||
|
||||
export class AuthenticatedUser extends User {
|
||||
// Factory for creating new users with sensible defaults
|
||||
static create(
|
||||
email: string,
|
||||
options?: {
|
||||
id?: string;
|
||||
displayName?: string;
|
||||
status?: UserStatus;
|
||||
roles?: Role[];
|
||||
permissions?: Permission[];
|
||||
},
|
||||
): User {
|
||||
const now = new Date();
|
||||
return new AuthenticatedUser({
|
||||
id: options?.id ?? crypto.randomUUID(),
|
||||
email,
|
||||
displayName: options?.displayName,
|
||||
status: options?.status ?? "active",
|
||||
roles: options?.roles ?? [],
|
||||
permissions: options?.permissions ?? [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
isAnonymous(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// For representing "no user" in contexts where user is optional
|
||||
export const AnonymousUser = Symbol("AnonymousUser");
|
||||
export class AnonymousUser extends User {
|
||||
// FIXME: this is C&Ped with only minimal changes. No bueno.
|
||||
static create(
|
||||
email: string,
|
||||
options?: {
|
||||
id?: string;
|
||||
displayName?: string;
|
||||
status?: UserStatus;
|
||||
roles?: Role[];
|
||||
permissions?: Permission[];
|
||||
},
|
||||
): AnonymousUser {
|
||||
const now = new Date(0);
|
||||
return new AnonymousUser({
|
||||
id: options?.id ?? crypto.randomUUID(),
|
||||
email,
|
||||
displayName: options?.displayName,
|
||||
status: options?.status ?? "active",
|
||||
roles: options?.roles ?? [],
|
||||
permissions: options?.permissions ?? [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
|
||||
export const anonymousUser = User.create("anonymous@example.com", {
|
||||
isAnonymous(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export const anonymousUser = AnonymousUser.create("anonymous@example.com", {
|
||||
id: "-1",
|
||||
displayName: "Anonymous User",
|
||||
// FIXME: set createdAt and updatedAt to start of epoch
|
||||
});
|
||||
|
||||
export type MaybeUser = User | typeof AnonymousUser;
|
||||
|
||||
Reference in New Issue
Block a user