mirror of
https://github.com/tale/headplane.git
synced 2026-08-12 06:46:51 +00:00
116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
import { data } from 'react-router';
|
|
import { Capabilities, Roles } from '~/server/web/roles';
|
|
import type { Route } from './+types/overview';
|
|
|
|
export async function userAction({ request, context }: Route.ActionArgs) {
|
|
const session = await context.sessions.auth(request);
|
|
const check = await context.sessions.check(request, Capabilities.write_users);
|
|
if (!check) {
|
|
throw data('You do not have permission to update users', {
|
|
status: 403,
|
|
});
|
|
}
|
|
|
|
const formData = await request.formData();
|
|
const action = formData.get('action_id')?.toString();
|
|
if (!action) {
|
|
throw data('Missing `action_id` in the form data.', {
|
|
status: 404,
|
|
});
|
|
}
|
|
|
|
const api = context.hsApi.getRuntimeClient(session.api_key);
|
|
switch (action) {
|
|
case 'create_user': {
|
|
const name = formData.get('username')?.toString();
|
|
const displayName = formData.get('display_name')?.toString();
|
|
const email = formData.get('email')?.toString();
|
|
|
|
if (!name) {
|
|
throw data('Missing `username` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
await api.createUser(name, email, displayName);
|
|
return { message: 'User created successfully' };
|
|
}
|
|
case 'delete_user': {
|
|
const userId = formData.get('user_id')?.toString();
|
|
if (!userId) {
|
|
throw data('Missing `user_id` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
await api.deleteUser(userId);
|
|
return { message: 'User deleted successfully' };
|
|
}
|
|
case 'rename_user': {
|
|
const userId = formData.get('user_id')?.toString();
|
|
const newName = formData.get('new_name')?.toString();
|
|
if (!userId || !newName) {
|
|
return data({ success: false }, 400);
|
|
}
|
|
|
|
const users = await api.getUsers(userId);
|
|
const user = users.find((user) => user.id === userId);
|
|
if (!user) {
|
|
throw data(`No user found with id: ${userId}`, { status: 400 });
|
|
}
|
|
|
|
if (user.provider === 'oidc') {
|
|
// OIDC users cannot be renamed via this endpoint, return an error
|
|
throw data('Users managed by OIDC cannot be renamed', {
|
|
status: 403,
|
|
});
|
|
}
|
|
|
|
await api.renameUser(userId, newName);
|
|
return { message: 'User renamed successfully' };
|
|
}
|
|
case 'reassign_user': {
|
|
const userId = formData.get('user_id')?.toString();
|
|
const newRole = formData.get('new_role')?.toString();
|
|
if (!userId || !newRole) {
|
|
throw data('Missing `user_id` or `new_role` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const users = await api.getUsers(userId);
|
|
const user = users.find((user) => user.id === userId);
|
|
if (!user?.providerId) {
|
|
throw data('Specified user is not an OIDC user', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
// For some reason, headscale makes providerID a url where the
|
|
// last component is the subject, so we need to strip that out
|
|
const subject = user.providerId?.split('/').pop();
|
|
if (!subject) {
|
|
throw data(
|
|
'Malformed `providerId` for the specified user. Cannot find subject.',
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
|
|
const result = await context.sessions.reassignSubject(
|
|
subject,
|
|
newRole as keyof typeof Roles,
|
|
);
|
|
|
|
if (!result) {
|
|
throw data('Failed to reassign user role.', { status: 500 });
|
|
}
|
|
|
|
return { message: 'User reassigned successfully' };
|
|
}
|
|
default:
|
|
throw data('Invalid `action_id` provided.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
}
|