mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-10 04:36:38 +00:00
server admin enforce 2fa per user
This commit is contained in:
@@ -34,7 +34,6 @@ export type UserRow = {
|
||||
status: string;
|
||||
role: string;
|
||||
isOwner: boolean;
|
||||
isTwoFactorEnabled: boolean;
|
||||
};
|
||||
|
||||
type UsersTableProps = {
|
||||
@@ -171,39 +170,6 @@ export default function UsersTable({ users: u }: UsersTableProps) {
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "isTwoFactorEnabled",
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === "asc")
|
||||
}
|
||||
>
|
||||
2FA Enabled
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const userRow = row.original;
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<span>{userRow.isTwoFactorEnabled && (
|
||||
<span className="text-green-500">
|
||||
{t('enabled')}
|
||||
</span>
|
||||
) || (
|
||||
<span className="text-white/50">
|
||||
{t('disabled')}
|
||||
</span>
|
||||
)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({ row }) => {
|
||||
|
||||
@@ -27,7 +27,6 @@ import { ListRolesResponse } from "@server/routers/role";
|
||||
import { userOrgUserContext } from "@app/hooks/useOrgUserContext";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { Checkbox } from "@app/components/ui/checkbox";
|
||||
import {
|
||||
SettingsContainer,
|
||||
SettingsSection,
|
||||
@@ -44,14 +43,14 @@ import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function AccessControlsPage() {
|
||||
const { orgUser: user, updateOrgUser } = userOrgUserContext();
|
||||
const { orgUser: user } = userOrgUserContext();
|
||||
|
||||
const api = createApiClient(useEnvContext());
|
||||
|
||||
const { orgId } = useParams();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [roles, setRoles] = useState<{ roleId: number; name: string }[]>([]);
|
||||
const [enable2FA, setEnable2FA] = useState(user.twoFactorEnabled || false);
|
||||
|
||||
const t = useTranslations();
|
||||
|
||||
@@ -97,8 +96,7 @@ export default function AccessControlsPage() {
|
||||
async function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
setLoading(true);
|
||||
|
||||
// Update user role
|
||||
const roleRes = await api
|
||||
const res = await api
|
||||
.post<
|
||||
AxiosResponse<InviteUserResponse>
|
||||
>(`/role/${values.roleId}/add/${user.userId}`)
|
||||
@@ -111,34 +109,9 @@ export default function AccessControlsPage() {
|
||||
t('accessRoleErrorAddDescription')
|
||||
)
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
// Update 2FA status if it changed
|
||||
if (enable2FA !== user.twoFactorEnabled) {
|
||||
const twoFARes = await api
|
||||
.patch(`/org/${orgId}/user/${user.userId}/2fa`, {
|
||||
twoFactorEnabled: enable2FA
|
||||
})
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Error updating 2FA",
|
||||
description: formatAxiosError(
|
||||
e,
|
||||
"Failed to update 2FA status"
|
||||
)
|
||||
});
|
||||
return null;
|
||||
});
|
||||
|
||||
if (twoFARes && twoFARes.status === 200) {
|
||||
// Update the user context with the new 2FA status
|
||||
updateOrgUser({ twoFactorEnabled: enable2FA });
|
||||
}
|
||||
}
|
||||
|
||||
if (roleRes && roleRes.status === 200) {
|
||||
if (res && res.status === 200) {
|
||||
toast({
|
||||
variant: "default",
|
||||
title: t('userSaved'),
|
||||
@@ -197,36 +170,6 @@ export default function AccessControlsPage() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="enable-2fa"
|
||||
checked={enable2FA}
|
||||
onCheckedChange={(
|
||||
e
|
||||
) =>
|
||||
setEnable2FA(
|
||||
e as boolean
|
||||
)
|
||||
}
|
||||
/>
|
||||
<label
|
||||
htmlFor="enable-2fa"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Enable 2FA for this user
|
||||
</label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground ml-6">
|
||||
When enabled, the user will be required to set up their authenticator app on their next login.
|
||||
{user.twoFactorEnabled && (
|
||||
<span className="text-primary"> This user currently has 2FA enabled.</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</Form>
|
||||
</SettingsSectionForm>
|
||||
@@ -243,8 +186,6 @@ export default function AccessControlsPage() {
|
||||
</Button>
|
||||
</SettingsSectionFooter>
|
||||
</SettingsSection>
|
||||
|
||||
|
||||
</SettingsContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,8 +81,7 @@ export default async function UsersPage(props: UsersPageProps) {
|
||||
idpName: user.idpName || t('idpNameInternal'),
|
||||
status: t('userConfirmed'),
|
||||
role: user.isOwner ? t('accessRoleOwner') : user.roleName || t('accessRoleMember'),
|
||||
isOwner: user.isOwner || false,
|
||||
isTwoFactorEnabled: user.twoFactorEnabled || false,
|
||||
isOwner: user.isOwner || false
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user