mirror of
https://github.com/tale/headplane.git
synced 2026-08-12 14:46:52 +00:00
140 lines
3.6 KiB
TypeScript
140 lines
3.6 KiB
TypeScript
import { data } from 'react-router';
|
|
import { isDataWithApiError } from '~/server/headscale/api/error-client';
|
|
import { Capabilities } from '~/server/web/roles';
|
|
import type { Route } from './+types/overview';
|
|
|
|
// We only check capabilities here and assume it is writable
|
|
// If it isn't, it'll gracefully error anyways, since this means some
|
|
// fishy client manipulation is happening.
|
|
export async function aclAction({ request, context }: Route.ActionArgs) {
|
|
const session = await context.sessions.auth(request);
|
|
const check = await context.sessions.check(
|
|
request,
|
|
Capabilities.write_policy,
|
|
);
|
|
if (!check) {
|
|
throw data('You do not have permission to write to the ACL policy', {
|
|
status: 403,
|
|
});
|
|
}
|
|
|
|
// Try to write to the ACL policy via the API or via config file (TODO).
|
|
const formData = await request.formData();
|
|
const policyData = formData.get('policy')?.toString();
|
|
if (!policyData) {
|
|
throw data('Missing `policy` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const api = context.hsApi.getRuntimeClient(session.api_key);
|
|
try {
|
|
const { policy, updatedAt } = await api.setPolicy(policyData);
|
|
return data({
|
|
success: true,
|
|
error: undefined,
|
|
policy,
|
|
updatedAt,
|
|
});
|
|
} catch (error) {
|
|
if (isDataWithApiError(error)) {
|
|
const rawData = error.data.rawData;
|
|
// https://github.com/juanfont/headscale/blob/c4600346f9c29b514dc9725ac103efb9d0381f23/hscontrol/types/policy.go#L11
|
|
if (rawData.includes('update is disabled')) {
|
|
throw data('Policy is not writable', { status: 403 });
|
|
}
|
|
|
|
const message =
|
|
error.data.data != null &&
|
|
'message' in error.data.data &&
|
|
typeof error.data.data.message === 'string'
|
|
? error.data.data.message
|
|
: undefined;
|
|
|
|
if (message == null) {
|
|
throw error;
|
|
}
|
|
|
|
// Starting in Headscale 0.27.0 the ACLs parsing was changed meaning
|
|
// we need to reference other error messages based on API version.
|
|
if (context.hsApi.clientHelpers.isAtleast('0.27.0')) {
|
|
if (message.includes('parsing HuJSON:')) {
|
|
const cutIndex = message.indexOf('parsing HuJSON:');
|
|
const trimmed =
|
|
cutIndex > -1
|
|
? `Syntax error: ${message.slice(cutIndex + 16).trim()}`
|
|
: message;
|
|
|
|
return data(
|
|
{
|
|
success: false,
|
|
error: trimmed,
|
|
policy: undefined,
|
|
updatedAt: undefined,
|
|
},
|
|
400,
|
|
);
|
|
}
|
|
|
|
if (message.includes('parsing policy from bytes:')) {
|
|
const cutIndex = message.indexOf('parsing policy from bytes:');
|
|
const trimmed =
|
|
cutIndex > -1
|
|
? `Syntax error: ${message.slice(cutIndex + 26).trim()}`
|
|
: message;
|
|
|
|
return data(
|
|
{
|
|
success: false,
|
|
error: trimmed,
|
|
policy: undefined,
|
|
updatedAt: undefined,
|
|
},
|
|
400,
|
|
);
|
|
}
|
|
} else {
|
|
// Pre-0.27.0 error messages
|
|
if (message.includes('parsing hujson')) {
|
|
const cutIndex = message.indexOf('err: hujson:');
|
|
const trimmed =
|
|
cutIndex > -1
|
|
? `Syntax error: ${message.slice(cutIndex + 12)}`
|
|
: message;
|
|
|
|
return data(
|
|
{
|
|
success: false,
|
|
error: trimmed,
|
|
policy: undefined,
|
|
updatedAt: undefined,
|
|
},
|
|
400,
|
|
);
|
|
}
|
|
|
|
if (message.includes('unmarshalling policy')) {
|
|
const cutIndex = message.indexOf('err:');
|
|
const trimmed =
|
|
cutIndex > -1
|
|
? `Syntax error: ${message.slice(cutIndex + 5)}`
|
|
: message;
|
|
|
|
return data(
|
|
{
|
|
success: false,
|
|
error: trimmed,
|
|
policy: undefined,
|
|
updatedAt: undefined,
|
|
},
|
|
400,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise, this is a Headscale error that we can just propagate.
|
|
throw error;
|
|
}
|
|
}
|