Wizard: Add group input to User step
This adds a group input to the User step. The input is implemented as a `LabelInput`, when Administrator checkbox is checked, `wheel` group gets added and it's removal automatically unchecks the Administrator checkbox again.
This commit is contained in:
parent
47b5eb8392
commit
7a225e4146
3 changed files with 67 additions and 1 deletions
|
|
@ -15,10 +15,16 @@ import {
|
|||
setUserSshKeyByIndex,
|
||||
setUserAdministratorByIndex,
|
||||
removeUser,
|
||||
selectUserGroupsByIndex,
|
||||
addUserGroupByIndex,
|
||||
removeUserGroupByIndex,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
import LabelInput from '../../../LabelInput';
|
||||
import { PasswordValidatedInput } from '../../../utilities/PasswordValidatedInput';
|
||||
import { useUsersValidation } from '../../../utilities/useValidation';
|
||||
import { ValidatedInputAndTextArea } from '../../../ValidatedInput';
|
||||
import { isUserGroupValid } from '../../../validators';
|
||||
|
||||
const UserInfo = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const index = 0;
|
||||
|
|
@ -30,6 +36,8 @@ const UserInfo = () => {
|
|||
const userSshKey = useAppSelector(userSshKeySelector);
|
||||
const userIsAdministratorSelector = selectUserAdministrator(index);
|
||||
const userIsAdministrator = useAppSelector(userIsAdministratorSelector);
|
||||
const userGroupsSelector = selectUserGroupsByIndex(index);
|
||||
const userGroups = useAppSelector(userGroupsSelector);
|
||||
|
||||
const handleNameChange = (
|
||||
_e: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||
|
|
@ -111,13 +119,30 @@ const UserInfo = () => {
|
|||
<FormGroup>
|
||||
<Checkbox
|
||||
label="Administrator"
|
||||
isChecked={userIsAdministrator}
|
||||
isChecked={userIsAdministrator || userGroups.includes('wheel')}
|
||||
onChange={(_e, value) => handleCheckboxChange(_e, value)}
|
||||
aria-label="Administrator"
|
||||
id="user Administrator"
|
||||
name="user Administrator"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup label="Groups">
|
||||
<LabelInput
|
||||
ariaLabel="Add user group"
|
||||
placeholder="Add user group"
|
||||
validator={isUserGroupValid}
|
||||
list={userGroups}
|
||||
item="Group"
|
||||
addAction={(value) =>
|
||||
addUserGroupByIndex({ index: index, group: value })
|
||||
}
|
||||
removeAction={(value) =>
|
||||
removeUserGroupByIndex({ index: index, group: value })
|
||||
}
|
||||
stepValidation={stepValidation}
|
||||
fieldName="groups"
|
||||
/>
|
||||
</FormGroup>
|
||||
<Tooltip position="top-start" content={'Remove user'}>
|
||||
<FormGroup>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -67,6 +67,15 @@ export const isUserNameValid = (userName: string) => {
|
|||
return isLengthValid && isNotNumericOnly && isPatternValid;
|
||||
};
|
||||
|
||||
export const isUserGroupValid = (group: string) => {
|
||||
// see `man groupadd` for the exact specification
|
||||
return (
|
||||
group.length <= 32 &&
|
||||
/^[a-zA-Z0-9_][a-zA-Z0-9_-]*(\$)?$/.test(group) &&
|
||||
/[a-zA-Z]+/.test(group) // contains at least one letter
|
||||
);
|
||||
};
|
||||
|
||||
export const isSshKeyValid = (sshKey: string) => {
|
||||
// 1. Key types: ssh-rsa, ssh-dss, ssh-ed25519, or ecdsa-sha2-nistp(256|384|521).
|
||||
// 2. Base64-encoded key material.
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ type UserAdministratorPayload = {
|
|||
isAdministrator: boolean;
|
||||
};
|
||||
|
||||
type UserGroupPayload = {
|
||||
index: number;
|
||||
group: string;
|
||||
};
|
||||
|
||||
export type wizardState = {
|
||||
env: {
|
||||
serverUrl: string;
|
||||
|
|
@ -411,6 +416,11 @@ export const selectUserAdministrator =
|
|||
return state.wizard.users[userIndex]?.isAdministrator;
|
||||
};
|
||||
|
||||
export const selectUserGroupsByIndex =
|
||||
(userIndex: number) => (state: RootState) => {
|
||||
return state.wizard.users[userIndex]?.groups;
|
||||
};
|
||||
|
||||
export const selectKernel = (state: RootState) => {
|
||||
return state.wizard.kernel;
|
||||
};
|
||||
|
|
@ -1022,6 +1032,26 @@ export const wizardSlice = createSlice({
|
|||
user.groups = user.groups.filter((group) => group !== 'wheel');
|
||||
}
|
||||
},
|
||||
addUserGroupByIndex: (state, action: PayloadAction<UserGroupPayload>) => {
|
||||
if (
|
||||
!state.users[action.payload.index].groups.some(
|
||||
(group) => group === action.payload.group
|
||||
)
|
||||
) {
|
||||
state.users[action.payload.index].groups.push(action.payload.group);
|
||||
}
|
||||
},
|
||||
removeUserGroupByIndex: (
|
||||
state,
|
||||
action: PayloadAction<UserGroupPayload>
|
||||
) => {
|
||||
const groupIndex = state.users[action.payload.index].groups.findIndex(
|
||||
(group) => group === action.payload.group
|
||||
);
|
||||
if (groupIndex !== -1) {
|
||||
state.users[action.payload.index].groups.splice(groupIndex, 1);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -1112,5 +1142,7 @@ export const {
|
|||
setUserPasswordByIndex,
|
||||
setUserSshKeyByIndex,
|
||||
setUserAdministratorByIndex,
|
||||
addUserGroupByIndex,
|
||||
removeUserGroupByIndex,
|
||||
} = wizardSlice.actions;
|
||||
export default wizardSlice.reducer;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue