diff --git a/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php index 7c4fab9d22..67ffa20fcd 100644 --- a/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php +++ b/app/Http/Requests/Api/Client/Servers/Subusers/SubuserRequest.php @@ -67,7 +67,21 @@ protected function validatePermissionsCanBeAssigned(array $permissions) /** @var \Pterodactyl\Services\Servers\GetUserPermissionsService $service */ $service = $this->container->make(GetUserPermissionsService::class); - if (count(array_diff($permissions, $service->handle($server, $user))) > 0) { + $subuser = $this->route()->parameter('user'); + + $modifiedPermissions = $permissions; + + if (!is_null($subuser)) { + $currentPermissions = $service->handle($server, $subuser); + + $addedPermissions = array_diff($permissions, $currentPermissions); + $removedPermissions = array_diff($currentPermissions, $permissions); + + $modifiedPermissions = array_merge($addedPermissions, $removedPermissions); + } + + // Checks if user has all the permissions they are modifying on the Subuser + if (count(array_intersect($service->handle($server, $user), $modifiedPermissions)) !== count($modifiedPermissions)) { throw new HttpForbiddenException('Cannot assign permissions to a subuser that your account does not actively possess.'); } } diff --git a/resources/scripts/components/elements/Input.tsx b/resources/scripts/components/elements/Input.tsx index 677a5014d8..1164cbf76d 100644 --- a/resources/scripts/components/elements/Input.tsx +++ b/resources/scripts/components/elements/Input.tsx @@ -34,6 +34,10 @@ const checkboxStyle = css` ${tw`outline-none border-primary-300`}; box-shadow: 0 0 0 1px rgba(9, 103, 210, 0.25); } + + &:disabled { + ${tw`opacity-50 cursor-default border-transparent`}; + } `; const inputStyle = css` diff --git a/resources/scripts/components/server/users/EditSubuserModal.tsx b/resources/scripts/components/server/users/EditSubuserModal.tsx index c28d56f6c2..08995a23d5 100644 --- a/resources/scripts/components/server/users/EditSubuserModal.tsx +++ b/resources/scripts/components/server/users/EditSubuserModal.tsx @@ -144,6 +144,7 @@ const EditSubuserModal = ({ subuser }: Props) => { title={key} isEditable={canEditUser} permissions={Object.keys(permissions[key].keys).map((pkey) => `${key}.${pkey}`)} + editablePermissions={editablePermissions.filter((p) => p.startsWith(key))} css={index > 0 ? tw`mt-4` : undefined} >

{permissions[key].description}

diff --git a/resources/scripts/components/server/users/PermissionTitleBox.tsx b/resources/scripts/components/server/users/PermissionTitleBox.tsx index 1d678f2215..61a40b1924 100644 --- a/resources/scripts/components/server/users/PermissionTitleBox.tsx +++ b/resources/scripts/components/server/users/PermissionTitleBox.tsx @@ -9,18 +9,19 @@ interface Props { isEditable: boolean; title: string; permissions: string[]; + editablePermissions: string[]; className?: string; } -const PermissionTitleBox: React.FC = memo(({ isEditable, title, permissions, className, children }) => { +const PermissionTitleBox: React.FC = memo(({ isEditable, title, permissions, editablePermissions, className, children }) => { const [{ value }, , { setValue }] = useField('permissions'); const onCheckboxClicked = useCallback( (e: React.ChangeEvent) => { if (e.currentTarget.checked) { - setValue([...value, ...permissions.filter((p) => !value.includes(p))]); + setValue([...value, ...permissions.filter((p) => !value.includes(p) && editablePermissions.includes(p))]); } else { - setValue(value.filter((p) => !permissions.includes(p))); + setValue(value.filter((p) => !editablePermissions.includes(p))); } }, [permissions, value] @@ -34,8 +35,9 @@ const PermissionTitleBox: React.FC = memo(({ isEditable, title, permissio {isEditable && ( value.includes(p))} + checked={editablePermissions.every((p) => value.includes(p)) && value.find((p) => p.startsWith(title)) != null} onChange={onCheckboxClicked} + disabled={editablePermissions.filter((p) => p.startsWith(title)).length === 0} /> )}