@paroicms/server
Version:
The ParoiCMS server
218 lines • 10.1 kB
JavaScript
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