import { type EntityData, type EntityJson, type EntityPatch, type SerializedEntityData } from '@nymphjs/nymph'; import AbleObject from './AbleObject.js'; import GroupClass from './Group.js'; import type { GroupData } from './Group.js'; export type EventType = 'checkUsername' | 'beforeRegister' | 'afterRegister' | 'beforeSave' | 'afterSave' | 'beforeLogin' | 'afterLogin' | 'beforeLogout' | 'afterLogout' | 'beforeDelete' | 'afterDelete'; /** * This is run when the user has entered an otherwise valid username into the * signup form. It should return a result, which when false will stop the * process and return the included message, disallowing the username. */ export type TilmeldCheckUsernameCallback = (user: User & UserData, data: { username: string; }) => Promise<{ result: boolean; message?: string; }>; /** * This is run before the user data checks, so the only checks before are * whether registration is allowed and whether the user is already registered. */ export type TilmeldBeforeRegisterCallback = (user: User & UserData, data: { password: string; additionalData?: { [k: string]: any; }; }) => Promise; /** * This is run before the transaction is committed, and you can perform * additional functions on the transaction, which is available in `user.$nymph`. */ export type TilmeldAfterRegisterCallback = (user: User & UserData, result: { loggedin: boolean; message: string; }) => Promise; /** * This is run just before the user is saved, so all checks have passed. * * The transaction is available in `user.$nymph`. */ export type TilmeldBeforeSaveCallback = (user: User & UserData) => Promise; /** * This is run before the transaction is committed, and you can perform * additional functions on the transaction, which is available in `user.$nymph`. * * This is run after a new email verification email has been sent, so throwing * an error will cause confusion for the user if they've changed their email. */ export type TilmeldAfterSaveCallback = (user: User & UserData) => Promise; /** * This is run after the authentication checks, but before the login action. */ export type TilmeldBeforeLoginCallback = (user: User & UserData, data: { username: string; password: string; additionalData?: { [k: string]: any; }; }) => Promise; export type TilmeldAfterLoginCallback = (user: User & UserData) => Promise; export type TilmeldBeforeLogoutCallback = (user: User & UserData) => Promise; export type TilmeldAfterLogoutCallback = (user: User & UserData) => Promise; /** * This is run before the user is logged out (if the current user is being * deleted). * * The transaction is available in `user.$nymph`. */ export type TilmeldBeforeDeleteCallback = (user: User & UserData) => Promise; /** * This is run before the transaction is committed, and you can perform * additional functions on the transaction, which is available in `user.$nymph`. */ export type TilmeldAfterDeleteCallback = (user: User & UserData) => Promise; export type UserData = { /** * The abilities granted to the user. */ abilities?: string[]; /** * The user's username. */ username?: string; /** * The user's first name. */ nameFirst?: string; /** * The user's middle name. */ nameMiddle?: string; /** * The user's last name. */ nameLast?: string; /** * The user's full name. This is generated from the first, middle, and last * names. */ name?: string; /** * The user's email address. */ email?: string; /** * The user's avatar URL. (Use $getAvatar() to support Gravatar.) */ avatar?: string; /** * The user's telephone number. */ phone?: string; /** * The user's primary group. */ group?: GroupClass & GroupData; /** * The user's secondary groups. */ groups?: (GroupClass & GroupData)[]; /** * Whether the user should inherit the abilities of his groups. */ inheritAbilities?: boolean; /** * Whether the user can log in. */ enabled?: boolean; /** * A verification secret. */ secret?: string; /** * The timestamp of when the email address was last changed. */ emailChangeDate?: number; /** * An email change proceed secret. */ newEmailSecret?: string; /** * If the user has changed their email address, this is the new one, awaiting * verification. */ newEmailAddress?: string; /** * An email change cancellation secret. */ cancelEmailSecret?: string; /** * The old email address. */ cancelEmailAddress?: string; /** * A recovery secret. */ recoverSecret?: string; /** * The timestamp of when the recovery secret was issued. */ recoverSecretDate?: number; /** * The password hash salt. */ salt?: string; /** * The password or password hash. */ password?: string; /** * Temporary storage for passwords. This will be hashed before going into the * database. */ passwordTemp?: string; /** * If set, this timestamp is the cutoff point for JWT issue dates. Any token * issued before this date will not authenticate the user. */ revokeTokenDate?: number; /** * Two factor auth secret. */ totpSecret?: string; }; /** * A user data model. * * Written by Hunter Perrin for SciActive. * * @author Hunter Perrin * @copyright SciActive Inc * @see http://nymph.io/ */ export default class User extends AbleObject { static ETYPE: string; static class: string; private static checkUsernameCallbacks; private static beforeRegisterCallbacks; private static afterRegisterCallbacks; private static beforeSaveCallbacks; private static afterSaveCallbacks; private static beforeLoginCallbacks; private static afterLoginCallbacks; private static beforeLogoutCallbacks; private static afterLogoutCallbacks; private static beforeDeleteCallbacks; private static afterDeleteCallbacks; private static DEFAULT_CLIENT_ENABLED_METHODS; private static DEFAULT_PRIVATE_DATA; private static DEFAULT_ALLOWLIST_DATA; protected $clientEnabledMethods: string[]; static clientEnabledStaticMethods: string[]; protected $privateData: string[]; static searchRestrictedData: string[]; protected $allowlistData?: string[] | undefined; protected $allowlistTags?: string[]; /** * Gatekeeper ability cache. * * Gatekeeper will cache the user's abilities that it calculates, so it can * check faster if that user has been checked before. */ private $gatekeeperCache?; /** * This should only be used by the backend. */ private $skipAcWhenSaving; /** * This should only be used by the backend. */ private $skipAcWhenDeleting; /** * The user's group descendants. */ private $descendantGroups?; /** * Used to save the current email address to send verification if it changes * from the frontend. * * If you are changing a user's email address and want to bypass email * verification, don't set this. */ $originalEmail?: string; /** * Used to save the current username for domain admin permissions. */ $originalUsername?: string; static getFTSText(name: string, value: any): string | null; static factoryUsername(username?: string): Promise; static current(returnObjectIfNotExist: true): User & UserData; static current(returnObjectIfNotExist?: false): (User & UserData) | null; /** * Send an account recovery link. * * @param data The input data from the client. * @returns An object with a boolean 'result' entry and a 'message' entry. */ static sendRecovery(data: { recoveryType: 'username' | 'password'; account: string; }): Promise<{ result: boolean; message: string; }>; /** * Recover account details. * * @param data The input data from the client. * @returns An object with a boolean 'result' entry and a 'message' entry. */ static recover(data: { username: string; secret: string; password: string; }): Promise<{ result: boolean; message: string; }>; static getClientConfig(): { regFields: string[]; userFields: string[]; emailUsernames: boolean; domainSupport: boolean; allowRegistration: boolean; allowUsernameChange: boolean; pwRecovery: boolean; verifyEmail: boolean; unverifiedAccess: boolean; }; static loginUser(data: { username: string; password: string; code?: string; additionalData?: { [k: string]: any; }; }): Promise<{ result: boolean; message: string; user?: User & UserData; }>; /** * Get the users in a domain. This is only accessible to the domain's admins. */ static getDomainUsers(domain: string, options?: { limit?: number; offset?: number; sort?: string; reverse?: boolean; }): Promise<(User & UserData)[]>; constructor(); $getUniques(): Promise; $login(data: { username: string; password: string; code?: string; additionalData?: { [k: string]: any; }; }): Promise<{ result: boolean; needTOTP: boolean; message: string; } | { needTOTP?: undefined; result: boolean; message: any; }>; $switchUser(data?: { additionalData?: { [k: string]: any; }; }): Promise<{ result: boolean; message: string; }>; /** * Log a user out of the system. * @returns An object with a boolean 'result' entry and a 'message' entry. */ $logout(): Promise<{ result: boolean; message: string; }>; $getAvatar(): string; $getGid(): string | null; $getGids(): string[] | null; /** * Get the user's group descendants. */ $getDescendantGroups(): Promise<(GroupClass & GroupData)[]>; $jsonAcceptData(input: EntityJson, allowConflict?: boolean): void; $jsonAcceptPatch(patch: EntityPatch, allowConflict?: boolean): void; private $checkIncomingAbilities; $putData(data: EntityData, sdata?: SerializedEntityData, source?: 'server'): void; /** * Update the data protection arrays for a user. * * @param givenUser User to update protection for. If undefined, will use the currently logged in user. */ $updateDataProtection(givenUser?: User & UserData): void; $updateGroupDataProtection(): void; /** * Check to see if a user has an ability. * * This function will check both user and group abilities, if the user is * marked to inherit the abilities of its group. * * If `ability` is undefined, it will check to see if the user is currently * logged in. * * If the user has the "system/admin" ability, this function will return true. * * @param ability The ability. * @returns True or false. */ $gatekeeper(ability?: string): Promise; /** * Build a gatekeeper cache object. */ $getGatekeeperCache(): Promise<{ [k: string]: true; }>; $clearCache(): void; /** * Send the user email verification/change/cancellation links. * * @returns True on success, false on failure. */ $sendEmailVerification(): Promise; /** * Add the user to a (secondary) group. * * @param group The group. * @returns True if the user is already in the group. The resulting array of groups if the user was not. */ $addGroup(group: GroupClass & GroupData): Promise; /** * Check the given password against the user's. * * @param password The password in question. * @returns True if the passwords match, otherwise false. */ $checkPassword(password: string): boolean; /** * Remove the user from a (secondary) group. * * @param group The group. * @returns True if the user wasn't in the group. The resulting array of groups if the user was. */ $delGroup(group: GroupClass & GroupData): Promise; /** * Check whether the user is in a (primary or secondary) group. * * @param group The group, or the group's GUID. * @returns True or false. */ $inGroup(group: (GroupClass & GroupData) | string): Promise; /** * Check whether the user is a descendant of a group. * * @param group The group, or the group's GUID. * @returns True or false. */ $isDescendant(group: (GroupClass & GroupData) | string): Promise; /** * A frontend accessible method to change the user's password. * * @param data The input data from the client. * @returns An object with a boolean 'result' entry and a 'message' entry. */ $changePassword(data: { newPassword: string; currentPassword: string; revokeCurrentTokens?: boolean; }): Promise<{ result: boolean; message: string; }>; /** * Change the user's password. * * @param password The new password. * @returns The resulting password or hash which is stored in the entity. */ $password(password: string): string; /** * A frontend accessible method to revoke all currently issued tokens. * * @param data The input data from the client. * @returns An object with a boolean 'result' entry and a 'message' entry. */ $revokeCurrentTokens(data: { password: string; }): Promise<{ result: boolean; message: string; }>; $hasTOTPSecret(): boolean; /** * Check the given code against the user's TOTP secret. * * @param code The code in question. * @returns True if the code is valid, otherwise false. */ $checkTOTPCode(code: string): boolean; /** * A frontend accessible method to generate a new TOTP secret. * * @returns An object with 'uri', 'qrcode', and 'secret'. */ $getNewTOTPSecret(): Promise<{ uri: string; qrcode: string; secret: string; }>; /** * A frontend accessible method to save a TOTP secret into the user's account. * * @param data The input data from the client. * @returns An object with a boolean 'result' entry and a 'message' entry. */ $saveTOTPSecret(data: { password: string; secret: string; code: string; }): Promise<{ result: boolean; message: string; }>; /** * A frontend accessible method to remove the TOTP secret from the user's * account. * * @param data The input data from the client. * @returns An object with a boolean 'result' entry and a 'message' entry. */ $removeTOTPSecret(data?: { password: string; code: string; }): Promise<{ result: boolean; message: string; }>; /** * Check that a username is valid. * * @returns An object with a boolean 'result' entry and a 'message' entry. */ $checkUsername(): Promise<{ result: boolean; message: string; }>; /** * Check that an email is unique. * * @returns An object with a boolean 'result' entry and a 'message' entry. */ $checkEmail(): Promise<{ result: boolean; message: string; }>; /** * Check that a phone number is unique. * * @returns An object with a boolean 'result' entry and a 'message' entry. */ $checkPhone(): Promise<{ result: boolean; message: string; }>; $register(data: { password: string; additionalData?: { [k: string]: any; }; }): Promise<{ result: boolean; loggedin: boolean; message: string; }>; $save(): Promise; /** * This should *never* be accessible on the client. */ $saveSkipAC(): Promise; $tilmeldSaveSkipAC(): boolean; $delete(): Promise; $deleteSkipAC(): Promise; $tilmeldDeleteSkipAC(): boolean; static on(event: T, callback: T extends 'checkUsername' ? TilmeldCheckUsernameCallback : T extends 'beforeRegister' ? TilmeldBeforeRegisterCallback : T extends 'afterRegister' ? TilmeldAfterRegisterCallback : T extends 'beforeSave' ? TilmeldBeforeSaveCallback : T extends 'afterSave' ? TilmeldAfterSaveCallback : T extends 'beforeLogin' ? TilmeldBeforeLoginCallback : T extends 'afterLogin' ? TilmeldAfterLoginCallback : T extends 'beforeLogout' ? TilmeldBeforeLogoutCallback : T extends 'afterLogout' ? TilmeldAfterLogoutCallback : T extends 'beforeDelete' ? TilmeldBeforeDeleteCallback : T extends 'afterDelete' ? TilmeldAfterDeleteCallback : never): () => boolean; static off(event: T, callback: T extends 'checkUsername' ? TilmeldCheckUsernameCallback : T extends 'beforeRegister' ? TilmeldBeforeRegisterCallback : T extends 'afterRegister' ? TilmeldAfterRegisterCallback : T extends 'beforeSave' ? TilmeldBeforeSaveCallback : T extends 'afterSave' ? TilmeldAfterSaveCallback : T extends 'beforeLogin' ? TilmeldBeforeLoginCallback : T extends 'afterLogin' ? TilmeldAfterLoginCallback : T extends 'beforeLogout' ? TilmeldBeforeLogoutCallback : T extends 'afterLogout' ? TilmeldAfterLogoutCallback : T extends 'beforeDelete' ? TilmeldBeforeDeleteCallback : T extends 'afterDelete' ? TilmeldAfterDeleteCallback : never): boolean; }