diff --git a/messages/en-US.json b/messages/en-US.json
index 75760164..c2e1a6f1 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -975,7 +975,6 @@
"actionUpdateOrg": "Update Organization",
"actionUpdateUser": "Update User",
"actionGetUser": "Get User",
- "actionResetUserPassword": "Reset User Password",
"actionGetOrgUser": "Get Organization User",
"actionListOrgDomains": "List Organization Domains",
"actionCreateSite": "Create Site",
@@ -1155,25 +1154,9 @@
"initialSetupDescription": "Create the intial server admin account. Only one server admin can exist. You can always change these credentials later.",
"createAdminAccount": "Create Admin Account",
"setupErrorCreateAdmin": "An error occurred while creating the server admin account.",
-"passwordReset": "Password Reset",
- "passwordResetAdminInstructions": "Clicking the button below will send a password reset email to the user. They will be able to set a new password using the link provided.",
- "passwordResetSent": "Password Reset Sent",
- "passwordResetSentDescription": "A password reset email has been sent to {email}.",
- "passwordResetError": "Failed to Send Reset",
- "passwordResetErrorDescription": "Unable to send password reset email. Please try again.",
- "passwordResetSending": "Sending...",
- "passwordResetSendEmail": "Send Reset Email",
- "passwordResetUnavailable": "Password Reset Unavailable",
- "passwordResetExternalUserDescription": "Password reset is only available for internal users. External users authenticate through their identity provider.",
+
"adminSettings": "Admin Settings",
"adminSettingsDescription": "Configure server-wide administration settings",
- "security": "Security",
- "tokenExpiration": "Token Expiration",
- "tokenExpirationDescription": "Configure how long password reset tokens remain valid before expiring",
- "passwordResetExpireLimit": "Password Reset Token Expiry",
- "passwordResetExpireLimitDescription": "Set how many hours password reset tokens remain valid. Default is 1 hour.",
- "hours": "hours",
- "securitySettingsSaved": "Security Settings Saved",
"securitySettingsSavedDescription": "Your security settings have been updated successfully",
"securitySettingsUpdated": "Security Settings Updated",
"securitySettingsUpdatedDescription": "Your security settings have been updated successfully",
diff --git a/server/auth/actions.ts b/server/auth/actions.ts
index 7fd92f74..ee2c5dac 100644
--- a/server/auth/actions.ts
+++ b/server/auth/actions.ts
@@ -93,7 +93,6 @@ export enum ActionsEnum {
listApiKeyActions = "listApiKeyActions",
listApiKeys = "listApiKeys",
getApiKey = "getApiKey",
- resetUserPassword = "resetUserPassword",
createOrgDomain = "createOrgDomain",
deleteOrgDomain = "deleteOrgDomain",
restartOrgDomain = "restartOrgDomain"
diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts
index aabebbbe..37a9aa60 100644
--- a/server/routers/auth/signup.ts
+++ b/server/routers/auth/signup.ts
@@ -23,7 +23,6 @@ import { passwordSchema } from "@server/auth/passwordSchema";
import { UserType } from "@server/types/UserTypes";
export const signupBodySchema = z.object({
- name: z.string().optional(),
email: z
.string()
.toLowerCase()
@@ -55,7 +54,7 @@ export async function signup(
);
}
- const { name, email, password, inviteToken, inviteId } = parsedBody.data;
+ const { email, password, inviteToken, inviteId } = parsedBody.data;
const passwordHash = await hashPassword(password);
const userId = generateId(15);
@@ -165,7 +164,6 @@ export async function signup(
userId: userId,
type: UserType.Internal,
username: email,
- name: name,
email: email,
passwordHash,
dateCreated: moment().toISOString()
diff --git a/server/routers/external.ts b/server/routers/external.ts
index 11ce07b2..8c90170a 100644
--- a/server/routers/external.ts
+++ b/server/routers/external.ts
@@ -552,11 +552,6 @@ authenticated.delete(
verifyUserIsServerAdmin,
user.adminRemoveUser
);
-authenticated.post(
- "/admin/user/:userId/password",
- verifyUserIsServerAdmin,
- user.adminResetUserPassword
-);
authenticated.post(
"/admin/user/:userId",
diff --git a/server/routers/integration.ts b/server/routers/integration.ts
index 48e5bfef..51604a11 100644
--- a/server/routers/integration.ts
+++ b/server/routers/integration.ts
@@ -388,13 +388,6 @@ authenticated.post(
user.updateUser2FA
);
-authenticated.post(
- "/user/:userId/password",
- verifyApiKeyIsRoot,
- verifyApiKeyHasAction(ActionsEnum.resetUserPassword),
- user.adminResetUserPassword
-);
-
authenticated.get(
"/user/:userId",
verifyApiKeyIsRoot,
diff --git a/server/routers/user/adminGetUser.ts b/server/routers/user/adminGetUser.ts
index 0a961bec..0a260a08 100644
--- a/server/routers/user/adminGetUser.ts
+++ b/server/routers/user/adminGetUser.ts
@@ -32,7 +32,6 @@ async function queryUser(userId: string) {
userId: users.userId,
email: users.email,
username: users.username,
- name: users.name,
type: users.type,
twoFactorEnabled: users.twoFactorEnabled,
twoFactorSetupRequested: users.twoFactorSetupRequested,
diff --git a/server/routers/user/adminListUsers.ts b/server/routers/user/adminListUsers.ts
index 308b9def..dae6405f 100644
--- a/server/routers/user/adminListUsers.ts
+++ b/server/routers/user/adminListUsers.ts
@@ -32,7 +32,6 @@ async function queryUsers(limit: number, offset: number) {
id: users.userId,
email: users.email,
username: users.username,
- name: users.name,
dateCreated: users.dateCreated,
serverAdmin: users.serverAdmin,
type: users.type,
diff --git a/server/routers/user/adminResetUserPassword.ts b/server/routers/user/adminResetUserPassword.ts
deleted file mode 100644
index a6784156..00000000
--- a/server/routers/user/adminResetUserPassword.ts
+++ /dev/null
@@ -1,212 +0,0 @@
-import { NextFunction, Request, Response } from "express";
-import { z } from "zod";
-import { fromError } from "zod-validation-error";
-import createHttpError from "http-errors";
-import { db } from "@server/db";
-import { passwordResetTokens, users } from "@server/db";
-import { eq } from "drizzle-orm";
-import { alphabet, generateRandomString } from "oslo/crypto";
-import { createDate, TimeSpan } from "oslo";
-import { hashPassword } from "@server/auth/password";
-import { sendEmail } from "@server/emails";
-import ResetPasswordCode from "@server/emails/templates/ResetPasswordCode";
-import config from "@server/lib/config";
-import logger from "@server/logger";
-import response from "@server/lib/response";
-import HttpCode from "@server/types/HttpCode";
-import { OpenAPITags, registry } from "@server/openApi";
-import { UserType } from "@server/types/UserTypes";
-
-const adminResetUserPasswordParamsSchema = z
- .object({
- userId: z.string()
- })
- .strict();
-
-const adminResetUserPasswordBodySchema = z
- .object({
- sendEmail: z.boolean().optional().default(true),
- expirationHours: z.number().int().positive().optional().default(24)
- })
- .strict();
-
-export type AdminResetUserPasswordBody = z.infer;
-export type AdminResetUserPasswordResponse = {
- resetLink?: string;
- emailSent: boolean;
-};
-
-registry.registerPath({
- method: "post",
- path: "/admin/user/{userId}/password",
- description: "Generate a password reset link for a user (server admin only).",
- tags: [OpenAPITags.User],
- request: {
- params: adminResetUserPasswordParamsSchema,
- body: {
- content: {
- "application/json": {
- schema: adminResetUserPasswordBodySchema
- }
- }
- }
- },
- responses: {}
-});
-
-export async function adminResetUserPassword(
- req: Request,
- res: Response,
- next: NextFunction
-): Promise {
- const parsedParams = adminResetUserPasswordParamsSchema.safeParse(req.params);
-
- if (!parsedParams.success) {
- return next(
- createHttpError(
- HttpCode.BAD_REQUEST,
- fromError(parsedParams.error).toString()
- )
- );
- }
-
- const parsedBody = adminResetUserPasswordBodySchema.safeParse(req.body);
-
- if (!parsedBody.success) {
- return next(
- createHttpError(
- HttpCode.BAD_REQUEST,
- fromError(parsedBody.error).toString()
- )
- );
- }
-
- const { userId } = parsedParams.data;
- const { sendEmail: shouldSendEmail, expirationHours } = parsedBody.data;
-
- try {
- // Get the target user
- const targetUser = await db
- .select()
- .from(users)
- .where(eq(users.userId, userId));
-
- if (!targetUser || !targetUser.length) {
- return next(
- createHttpError(
- HttpCode.NOT_FOUND,
- "User not found"
- )
- );
- }
-
- const user = targetUser[0];
-
- // Only allow resetting passwords for internal users
- if (user.type !== UserType.Internal) {
- return next(
- createHttpError(
- HttpCode.BAD_REQUEST,
- "Password reset is only available for internal users"
- )
- );
- }
-
- if (!user.email) {
- return next(
- createHttpError(
- HttpCode.BAD_REQUEST,
- "User does not have an email address"
- )
- );
- }
-
- // Generate reset token
- const token = generateRandomString(16, alphabet("0-9", "A-Z", "a-z"));
- const tokenHash = await hashPassword(token);
-
- // Store reset token in database
- await db.transaction(async (trx) => {
- // Delete any existing reset tokens for this user
- await trx
- .delete(passwordResetTokens)
- .where(eq(passwordResetTokens.userId, userId));
-
- // Insert new reset token
- await trx.insert(passwordResetTokens).values({
- userId: userId,
- email: user.email!,
- tokenHash,
- expiresAt: createDate(new TimeSpan(expirationHours, "h")).getTime()
- });
- });
-
- const resetUrl = `${config.getRawConfig().app.dashboard_url}/auth/reset-password?email=${encodeURIComponent(user.email!)}&token=${token}`;
-
- let emailSent = false;
-
- // Get the admin identifier (either user ID or API key ID)
- const adminId = req.user?.userId || req.apiKey?.apiKeyId || 'unknown';
-
- // Send email if requested
- if (shouldSendEmail) {
- // Check if email is configured
- if (!config.getRawConfig().email) {
- logger.info(
- `Server admin ${adminId} generated password reset link for user ${userId}. Email not configured, no email sent. Token expires in ${expirationHours} hours.`
- );
- emailSent = false;
- } else {
- try {
- await sendEmail(
- ResetPasswordCode({
- email: user.email!,
- code: token,
- link: resetUrl
- }),
- {
- from: config.getNoReplyEmail(),
- to: user.email!,
- subject: "Password Reset - Initiated by Server Administrator"
- }
- );
- emailSent = true;
-
- logger.info(
- `Server admin ${adminId} initiated password reset for user ${userId}. Email sent to ${user.email}. Token expires in ${expirationHours} hours.`
- );
- } catch (e) {
- logger.error("Failed to send server admin-initiated password reset email", e);
- // Don't fail the request if email fails, just log it
- emailSent = false;
- }
- }
- } else {
- logger.info(
- `Server admin ${adminId} generated password reset link for user ${userId}. No email sent. Token expires in ${expirationHours} hours.`
- );
- }
-
- return response(res, {
- data: {
- resetLink: resetUrl,
- emailSent
- },
- success: true,
- error: false,
- message: emailSent
- ? `Password reset email sent to ${user.email}`
- : "Password reset link generated successfully",
- status: HttpCode.OK
- });
-
- } catch (e) {
- logger.error("Failed to generate server admin password reset", e);
- return next(
- createHttpError(
- HttpCode.INTERNAL_SERVER_ERROR,
- "Failed to generate password reset"
- )
- );
- }
-}
\ No newline at end of file
diff --git a/server/routers/user/getOrgUser.ts b/server/routers/user/getOrgUser.ts
index 05e231c9..fbc9740d 100644
--- a/server/routers/user/getOrgUser.ts
+++ b/server/routers/user/getOrgUser.ts
@@ -18,7 +18,6 @@ async function queryUser(orgId: string, userId: string) {
userId: users.userId,
email: users.email,
username: users.username,
- name: users.name,
type: users.type,
roleId: userOrgs.roleId,
roleName: roles.name,
diff --git a/server/routers/user/getUser.ts b/server/routers/user/getUser.ts
index e33daab6..98c586cd 100644
--- a/server/routers/user/getUser.ts
+++ b/server/routers/user/getUser.ts
@@ -14,7 +14,6 @@ async function queryUser(userId: string) {
userId: users.userId,
email: users.email,
username: users.username,
- name: users.name,
type: users.type,
twoFactorEnabled: users.twoFactorEnabled,
emailVerified: users.emailVerified,
diff --git a/server/routers/user/index.ts b/server/routers/user/index.ts
index 2abcde07..85b2474a 100644
--- a/server/routers/user/index.ts
+++ b/server/routers/user/index.ts
@@ -13,6 +13,4 @@ export * from "./removeInvitation";
export * from "./createOrgUser";
export { updateUser } from "./updateUser";
export { adminUpdateUser } from "./adminUpdateUser";
-export { adminResetUserPassword } from "./adminResetUserPassword";
-export type { AdminResetUserPasswordBody, AdminResetUserPasswordResponse } from "./adminResetUserPassword";
export * from "./adminUpdateUser2FA";
\ No newline at end of file
diff --git a/server/routers/user/listUsers.ts b/server/routers/user/listUsers.ts
index 83c1e492..c02eeae9 100644
--- a/server/routers/user/listUsers.ts
+++ b/server/routers/user/listUsers.ts
@@ -43,7 +43,6 @@ async function queryUsers(orgId: string, limit: number, offset: number) {
dateCreated: users.dateCreated,
orgId: userOrgs.orgId,
username: users.username,
- name: users.name,
type: users.type,
roleId: userOrgs.roleId,
roleName: roles.name,
diff --git a/server/routers/user/updateUser.ts b/server/routers/user/updateUser.ts
index 09a98c33..4e979766 100644
--- a/server/routers/user/updateUser.ts
+++ b/server/routers/user/updateUser.ts
@@ -20,7 +20,6 @@ const updateUserParamsSchema = z
const updateUserBodySchema = z
.object({
- name: z.string().min(1).max(255).optional(),
email: z.string().email().optional()
})
.strict()
@@ -30,7 +29,6 @@ const updateUserBodySchema = z
export type UpdateUserResponse = {
userId: string;
- name: string | null;
email: string | null;
username: string;
};
@@ -129,7 +127,6 @@ export async function updateUser(
.where(eq(users.userId, userId))
.returning({
userId: users.userId,
- name: users.name,
email: users.email,
username: users.username
});
diff --git a/src/app/[orgId]/settings/access/users/UsersTable.tsx b/src/app/[orgId]/settings/access/users/UsersTable.tsx
index 44e606f1..5721e59c 100644
--- a/src/app/[orgId]/settings/access/users/UsersTable.tsx
+++ b/src/app/[orgId]/settings/access/users/UsersTable.tsx
@@ -27,7 +27,6 @@ export type UserRow = {
email: string | null;
displayUsername: string | null;
username: string;
- name: string | null;
idpId: number | null;
idpName: string;
type: string;
@@ -248,7 +247,6 @@ export default function UsersTable({ users: u }: UsersTableProps) {
{t("userQuestionOrgRemove", {
email:
selectedUser?.email ||
- selectedUser?.name ||
selectedUser?.username ||
""
})}
@@ -263,7 +261,6 @@ export default function UsersTable({ users: u }: UsersTableProps) {
onConfirm={removeUser}
string={
selectedUser?.email ||
- selectedUser?.name ||
selectedUser?.username ||
""
}
diff --git a/src/app/[orgId]/settings/access/users/page.tsx b/src/app/[orgId]/settings/access/users/page.tsx
index 665b3436..f34eb2bb 100644
--- a/src/app/[orgId]/settings/access/users/page.tsx
+++ b/src/app/[orgId]/settings/access/users/page.tsx
@@ -74,7 +74,6 @@ export default async function UsersPage(props: UsersPageProps) {
id: user.id,
username: user.username,
displayUsername: user.email || user.username,
- name: user.name,
email: user.email,
type: user.type,
idpId: user.idpId,
diff --git a/src/app/admin/users/AdminUserManagement.tsx b/src/app/admin/users/AdminUserManagement.tsx
index ec3e12d0..b0473a84 100644
--- a/src/app/admin/users/AdminUserManagement.tsx
+++ b/src/app/admin/users/AdminUserManagement.tsx
@@ -60,13 +60,13 @@ export default function AdminUserManagement({
// Form schema for user details
const formSchema = z.object({
- name: z.string().min(1, { message: t('nameRequired') }).max(255)
+ // Name field removed - no longer needed
});
const form = useForm>({
resolver: zodResolver(formSchema),
defaultValues: {
- name: userName || ""
+ // No default values needed
}
});
@@ -74,16 +74,11 @@ export default function AdminUserManagement({
setLoading(true);
try {
- const res = await api.post(`/admin/user/${userId}`, {
- name: values.name
+ // No update needed since name field is removed
+ toast({
+ title: t('userUpdated'),
+ description: t('userUpdatedDescription'),
});
-
- if (res.status === 200) {
- toast({
- title: t('userUpdated'),
- description: t('userUpdatedDescription'),
- });
- }
} catch (e) {
toast({
variant: "destructive",
@@ -124,24 +119,8 @@ export default function AdminUserManagement({
@@ -257,7 +255,7 @@ export default function UsersTable({ users }: Props) {
buttonText={t("userDeleteConfirm")}
onConfirm={async () => deleteUser(selected!.id)}
string={
- selected.email || selected.name || selected.username
+ selected.email || selected.username
}
title={t("userDeleteServer")}
/>
diff --git a/src/app/admin/users/[userId]/general/page.tsx b/src/app/admin/users/[userId]/general/page.tsx
index 6117a540..99aac1fb 100644
--- a/src/app/admin/users/[userId]/general/page.tsx
+++ b/src/app/admin/users/[userId]/general/page.tsx
@@ -19,7 +19,7 @@ import {
SettingsSectionForm
} from "@app/components/Settings";
import { UserType } from "@server/types/UserTypes";
-import AdminPasswordReset from "@app/components/AdminPasswordReset";
+
export default function GeneralPage() {
const { userId } = useParams();
@@ -31,7 +31,6 @@ export default function GeneralPage() {
const [twoFactorEnabled, setTwoFactorEnabled] = useState(false);
const [userType, setUserType] = useState(null);
const [userEmail, setUserEmail] = useState("");
- const [userName, setUserName] = useState("");
useEffect(() => {
// Fetch current user 2FA status
@@ -47,7 +46,6 @@ export default function GeneralPage() {
);
setUserType(userData.type);
setUserEmail(userData.email || "");
- setUserName(userData.name || userData.username || "");
}
} catch (error) {
console.error("Failed to fetch user data:", error);
@@ -123,29 +121,7 @@ export default function GeneralPage() {
-
-
-
- {t("passwordReset")}
-
-
- {t("passwordResetAdminInstructions")}
-
-
-
-
-
-
-
-
diff --git a/src/app/admin/users/[userId]/layout.tsx b/src/app/admin/users/[userId]/layout.tsx
index 062b40d8..78ea59c5 100644
--- a/src/app/admin/users/[userId]/layout.tsx
+++ b/src/app/admin/users/[userId]/layout.tsx
@@ -44,7 +44,7 @@ export default async function UserLayoutProps(props: UserLayoutProps) {
return (
<>
diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx
index e9673374..21282ab7 100644
--- a/src/app/admin/users/page.tsx
+++ b/src/app/admin/users/page.tsx
@@ -32,7 +32,6 @@ export default async function UsersPage(props: PageProps) {
return {
id: row.id,
email: row.email,
- name: row.name,
username: row.username,
type: row.type,
idpId: row.idpId,
diff --git a/src/components/AdminPasswordReset.tsx b/src/components/AdminPasswordReset.tsx
deleted file mode 100644
index 2b0acdf4..00000000
--- a/src/components/AdminPasswordReset.tsx
+++ /dev/null
@@ -1,232 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import { Button } from "@app/components/ui/button";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@app/components/ui/dialog";
-import { Input } from "@app/components/ui/input";
-import { Checkbox } from "@app/components/ui/checkbox";
-import { Label } from "@app/components/ui/label";
-import { toast } from "@app/hooks/useToast";
-import { formatAxiosError } from "@app/lib/api";
-import { createApiClient } from "@app/lib/api";
-import { useEnvContext } from "@app/hooks/useEnvContext";
-import { useTranslations } from "next-intl";
-import { Key, Mail, Copy, Link } from "lucide-react";
-import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
-
-type AdminPasswordResetProps = {
- userId: string;
- userEmail: string;
- userName: string;
- userType: string;
-};
-
-export default function AdminPasswordReset({
- userId,
- userEmail,
- userName,
- userType,
-}: AdminPasswordResetProps) {
- const api = createApiClient(useEnvContext());
- const { env } = useEnvContext();
- const t = useTranslations();
-
- const [open, setOpen] = useState(false);
- const [passwordLoading, setPasswordLoading] = useState(false);
- const [sendEmail, setSendEmail] = useState(env.email.emailEnabled);
- const [resetLink, setResetLink] = useState();
-
- const isExternalUser = userType !== "internal";
-
- // Don't render the button for external users
- if (isExternalUser) {
- return null;
- }
-
- const handleResetPassword = async () => {
- setPasswordLoading(true);
-
- try {
- const response = await api.post(`/admin/user/${userId}/password`, {
- sendEmail,
- expirationHours: 24
- });
-
- const data = response.data.data;
-
- if (data.resetLink) {
- setResetLink(data.resetLink);
- }
-
- toast({
- title: t('passwordResetSuccess'),
- description: data.emailSent
- ? `Password reset email sent to ${userEmail}`
- : data.message,
- });
-
- if (env.email.emailEnabled && sendEmail && data.emailSent) {
- setOpen(false);
- }
- } catch (error) {
- toast({
- variant: "destructive",
- title: t('passwordResetError'),
- description: formatAxiosError(error, t('passwordResetErrorDescription')),
- });
- } finally {
- setPasswordLoading(false);
- }
- };
-
- const copyToClipboard = async (text: string) => {
- try {
- await navigator.clipboard.writeText(text);
- toast({
- title: t('linkCopied'),
- description: t('linkCopiedDescription'),
- });
- } catch (e) {
- toast({
- variant: "destructive",
- title: "Copy failed",
- description: "Failed to copy to clipboard. Please copy manually.",
- });
- }
- };
-
- const handleClose = () => {
- setOpen(false);
- setResetLink(undefined);
- setSendEmail(true);
- };
-
- return (
- <>
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/components/PermissionsSelectBox.tsx b/src/components/PermissionsSelectBox.tsx
index 17bc8f3d..6f11d98e 100644
--- a/src/components/PermissionsSelectBox.tsx
+++ b/src/components/PermissionsSelectBox.tsx
@@ -113,8 +113,7 @@ function getActionsCategories(root: boolean) {
actionsByCategory["User"] = {
[t('actionUpdateUser')]: "updateUser",
- [t('actionGetUser')]: "getUser",
- [t('actionResetUserPassword')]: "resetUserPassword"
+ [t('actionGetUser')]: "getUser"
};
}
diff --git a/src/components/ProfileIcon.tsx b/src/components/ProfileIcon.tsx
index 0348058f..031086fe 100644
--- a/src/components/ProfileIcon.tsx
+++ b/src/components/ProfileIcon.tsx
@@ -45,7 +45,7 @@ export default function ProfileIcon() {
const t = useTranslations();
function getInitials() {
- return (user.name || user.email || user.username)
+ return (user.email || user.username)
.substring(0, 1)
.toUpperCase();
}
@@ -96,7 +96,7 @@ export default function ProfileIcon() {
{t("signingAs")}
- {user.email || user.name || user.username}
+ {user.email || user.username}
{user.serverAdmin ? (