feat: disable role changing on unmanaged users

This commit is contained in:
Aarnav Tale 2025-04-24 18:58:37 -04:00
parent abd0d39aeb
commit f87065f58f
No known key found for this signature in database
4 changed files with 34 additions and 4 deletions

View File

@ -14,6 +14,7 @@
- Machine tags now show states when waiting for subnet or exit node approval and when expiry is disabled. - Machine tags now show states when waiting for subnet or exit node approval and when expiry is disabled.
- Expiry status on the UI was incorrectly showing as never due to changes in the Headscale API. - Expiry status on the UI was incorrectly showing as never due to changes in the Headscale API.
- Added validation for machine renaming to prevent invalid submissions (closes [#192](https://github.com/tale/headplane/issues/192)). - Added validation for machine renaming to prevent invalid submissions (closes [#192](https://github.com/tale/headplane/issues/192)).
- Unmanaged (non-OIDC) users cannot have a role assigned to them so the menu option was disabled.
### 0.5.10 (April 4, 2025) ### 0.5.10 (April 4, 2025)
- Fix an issue where other preferences to skip onboarding affected every user. - Fix an issue where other preferences to skip onboarding affected every user.

View File

@ -61,7 +61,7 @@ export default function ManageBanner({ oidc, isDisabled }: ManageBannerProps) {
: 'You can add, remove, and rename users here.'} : 'You can add, remove, and rename users here.'}
</p> </p>
<div className="flex items-center gap-2 mt-4"> <div className="flex items-center gap-2 mt-4">
<CreateUser isDisabled={isDisabled} /> <CreateUser isOidc={oidc !== undefined} isDisabled={isDisabled} />
</div> </div>
</div> </div>
</div> </div>

View File

@ -48,7 +48,7 @@ export default function UserMenu({ user }: MenuProps) {
/> />
)} )}
<Menu disabledKeys={user.provider === 'oidc' ? ['rename'] : []}> <Menu disabledKeys={user.provider === 'oidc' ? ['rename'] : ['reassign']}>
<Menu.IconButton <Menu.IconButton
label="Machine Options" label="Machine Options"
className={cn( className={cn(

View File

@ -2,11 +2,12 @@ import Dialog from '~/components/Dialog';
import Input from '~/components/Input'; import Input from '~/components/Input';
interface CreateUserProps { interface CreateUserProps {
isOidc?: boolean;
isDisabled?: boolean; isDisabled?: boolean;
} }
// TODO: Support image upload for user avatars // TODO: Support image upload for user avatars
export default function CreateUser({ isDisabled }: CreateUserProps) { export default function CreateUser({ isOidc, isDisabled }: CreateUserProps) {
return ( return (
<Dialog> <Dialog>
<Dialog.Button isDisabled={isDisabled}>Add a new user</Dialog.Button> <Dialog.Button isDisabled={isDisabled}>Add a new user</Dialog.Button>
@ -15,21 +16,49 @@ export default function CreateUser({ isDisabled }: CreateUserProps) {
<Dialog.Text className="mb-6"> <Dialog.Text className="mb-6">
Enter a username to create a new user. Usernames can be addressed when Enter a username to create a new user. Usernames can be addressed when
managing ACL policies. managing ACL policies.
{isOidc ? (
<>
{' '}
Manually created users are given administrative access to
Headplane unless they become linked to an OIDC user in Headscale.
</>
) : undefined}
</Dialog.Text> </Dialog.Text>
<input type="hidden" name="action_id" value="create_user" /> <input type="hidden" name="action_id" value="create_user" />
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<Input <Input
isRequired isRequired
name="username" name="username"
type="text"
label="Username" label="Username"
placeholder="my-new-user" placeholder="my-new-user"
validationBehavior="native"
validate={(value) => {
if (value.trim().length === 0) {
return 'Username is required';
}
if (value.includes(' ')) {
return 'Usernames cannot contain spaces';
}
return true;
}}
/> />
<Input <Input
name="display_name" name="display_name"
type="text"
label="Display Name" label="Display Name"
placeholder="John Doe" placeholder="John Doe"
validationBehavior="native"
/>
<Input
name="email"
type="email"
label="Email"
placeholder="name@example.com"
validationBehavior="native"
/> />
<Input name="email" label="Email" placeholder="name@example.com" />
</div> </div>
</Dialog.Panel> </Dialog.Panel>
</Dialog> </Dialog>