UNPKG

@paroicms/server

Version:
218 lines 10.1 kB
import { ApiError } from "@paroicms/public-server-lib"; import { siteReadyGuard } from "../../graphql/graphql.types.js"; import { authGuard, verifyAccessToken } from "../auth/auth.helper.js"; import { permissionGuard } from "../auth/authorization.helper.js"; import { loadAccountPermissions, loadAccountRoles } from "../auth/special-account.helper.js"; import { recordEvent } from "../event-log/event-log.service.js"; import { formatAccountType } from "./account.formatters.js"; import { changeOwnPassword, createAccount, deleteAccount, getAccount, getAllAccounts, getAuthenticatedAccount, isAccountReferenced, resetAccountPassword, setAccountPreferences, updateAccount, updateAccountActive, } from "./account.queries.js"; export const accountResolvers = { Query: { account: async (_parent, { id }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); await permissionGuard(siteContext, httpContext, "site.manageUsers"); const acccount = await getAccount(siteContext, id); return acccount; }, authAccount: async (_parent, _values, { siteContext, httpContext }) => { authGuard(httpContext); siteReadyGuard(siteContext); const token = httpContext.req.headers.authorization?.replace(/^Bearer\s+/, ""); if (!token) { throw new ApiError("authorization header not found", 404); } const payload = verifyAccessToken(token); if (!payload.id) { throw new ApiError("auth account id not found", 404); } return await getAuthenticatedAccount(siteContext, payload.id); }, allAccounts: async (_parent, _values, { siteContext, httpContext }) => { siteReadyGuard(siteContext); await permissionGuard(siteContext, httpContext, "site.manageUsers"); return await getAllAccounts(siteContext); }, }, Mutation: { createAccount: async (_parent, { values }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); const authorizedAccount = await permissionGuard(siteContext, httpContext, "site.manageUsers"); const newAccount = await createAccount(siteContext, { accountType: formatAccountType(values.accountType), email: values.email, language: values.language, name: values.name ?? undefined, roles: values.roles, }); recordEvent(siteContext, { eventType: "account.create", actorId: authorizedAccount.accountId, targetType: "account", targetId: newAccount.id, eventData: { accountId: newAccount.id, email: newAccount.email, name: newAccount.name, }, }); return newAccount; }, updateAccount: async (_parent, { accountId, values }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); const authorizedAccount = await permissionGuard(siteContext, httpContext, "site.manageUsers"); const accountToUpdate = await getAccount(siteContext, accountId); if (["localDev", "platformAdmin"].includes(accountToUpdate.loginMethod ?? "")) { throw new ApiError("Cannot update special accounts", 403); } if (!accountToUpdate.active) { throw new ApiError("Cannot update inactive accounts", 403); } const oldAccount = accountToUpdate; const changedFields = []; if (values.name !== undefined && values.name !== oldAccount.name) { changedFields.push("name"); } if (values.email !== undefined && values.email !== oldAccount.email) { changedFields.push("email"); } const updatedAccount = await updateAccount(siteContext, accountId, { email: values.email ?? undefined, name: values.name ?? undefined, }); if (changedFields.length > 0) { recordEvent(siteContext, { eventType: "account.update", actorId: authorizedAccount.accountId, targetType: "account", targetId: accountId, eventData: { accountId, email: updatedAccount.email, changedFields, }, }); } return updatedAccount; }, deleteAccount: async (_parent, { accountId }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); const authorizedAccount = await permissionGuard(siteContext, httpContext, "site.manageUsers"); const account = await getAccount(siteContext, accountId); if (!account.active) { throw new ApiError("Cannot delete inactive accounts", 403); } await deleteAccount(siteContext, accountId); recordEvent(siteContext, { eventType: "account.delete", actorId: authorizedAccount.accountId, targetType: "account", targetId: accountId, eventData: { accountId, email: account.email, name: account.name, }, }); return true; }, reactivateAccount: async (_parent, { accountId }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); const authorizedAccount = await permissionGuard(siteContext, httpContext, "site.manageUsers"); const account = await getAccount(siteContext, accountId); if (account.active) { throw new ApiError("Account is already active", 400); } await updateAccountActive(siteContext, accountId, true); recordEvent(siteContext, { eventType: "account.update", actorId: authorizedAccount.accountId, targetType: "account", targetId: accountId, eventData: { accountId, email: account.email, changedFields: ["active"], }, }); return true; }, resetAccountPassword: async (_parent, { accountId }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); const authorizedAccount = await permissionGuard(siteContext, httpContext, "site.manageUsers"); const account = await getAccount(siteContext, accountId); if (account.loginMethod && ["localDev", "platformAdmin"].includes(account.loginMethod)) { throw new ApiError("Cannot reset password for special accounts", 403); } await resetAccountPassword(siteContext, accountId); recordEvent(siteContext, { eventType: "account.update", actorId: authorizedAccount.accountId, targetType: "account", targetId: accountId, eventData: { accountId, email: account.email, changedFields: ["passwordReset"], }, }); return true; }, changeOwnPassword: async (_parent, { currentPassword, newPassword }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); const payload = authGuard(httpContext); if (payload.loginMethod !== "local") { throw new ApiError("Password change is only available for local accounts", 403); } await changeOwnPassword(siteContext, payload.id, currentPassword, newPassword); recordEvent(siteContext, { eventType: "account.update", actorId: payload.id, targetType: "account", targetId: payload.id, eventData: { accountId: payload.id, email: payload.email, changedFields: ["password"], }, }); return true; }, setAccountPreferences: async (_parent, { accountId, values }, { siteContext, httpContext }) => { siteReadyGuard(siteContext); const account = await permissionGuard(siteContext, httpContext, "document.edit"); if (accountId !== account.accountId && !account.permissions.includes("site.manageUsers")) { throw new ApiError("Cannot modify other users' preferences", 403); } await setAccountPreferences(siteContext, accountId, { language: values.language ?? undefined, currentAuthorNodeId: values.currentAuthorNodeId ?? undefined, }); return accountId; }, }, AuthenticatedAccount: { roles: async (parent, _args, { siteContext }) => { return await loadAccountRoles(siteContext, parent); }, permissions: async (parent, _args, { siteContext }) => { return await loadAccountPermissions(siteContext, parent); }, active: () => { return true; }, loginMethod: (parent) => { if (!parent.loginMethod) { throw new ApiError("loginMethod is required for authenticated accounts", 500); } return parent.loginMethod; }, }, Account: { canDelete: async (parent, _args, { siteContext }) => { siteReadyGuard(siteContext); const isReferenced = await isAccountReferenced(siteContext, parent.id); return !isReferenced; }, }, }; //# sourceMappingURL=account.resolver.js.map