diff --git a/h/security/permission_map.py b/h/security/permission_map.py
index 1a6ecfe541d..0e93cc5c0ba 100644
--- a/h/security/permission_map.py
+++ b/h/security/permission_map.py
@@ -51,6 +51,7 @@
Permission.Group.EDIT: [
[p.group_matches_authenticated_client_authority],
[p.group_has_user_as_owner],
+ [p.group_has_user_as_admin],
],
Permission.Group.MEMBER_ADD: [[p.group_matches_authenticated_client_authority]],
Permission.Group.MEMBER_REMOVE: [[p.group_member_remove]],
diff --git a/h/security/policy/_api_cookie.py b/h/security/policy/_api_cookie.py
index 5bc3c3eab5a..626d50c25ce 100644
--- a/h/security/policy/_api_cookie.py
+++ b/h/security/policy/_api_cookie.py
@@ -9,6 +9,8 @@
("api.groups", "POST"), # Create a new group.
("api.group", "PATCH"), # Edit an existing group.
("api.group_members", "GET"), # Get a list of a group's members.
+ ("api.group_member", "DELETE"), # Remove a user from a group.
+ ("api.group_member", "PATCH"), # Change a user's role in a group.
]
diff --git a/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx b/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx
index 839823a2e17..08ef249ab5e 100644
--- a/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx
+++ b/h/static/scripts/group-forms/components/CreateEditGroupForm.tsx
@@ -4,6 +4,7 @@ import {
Button,
CancelIcon,
TrashIcon,
+ IconButton,
Input,
RadioGroup,
Table,
@@ -356,6 +357,80 @@ export default function CreateEditGroupForm() {
}
} ,[])
+ function uppercaseFirstLetter(str) {
+ return str[0].toUpperCase() + str.slice(1);
+ }
+
+ function selectOptions(member) {
+ const options = []
+ Object.keys(member.api.role).forEach((role) => {
+ options.push(
+
+ {uppercaseFirstLetter(role)}
+
+ );
+ });
+ return options;
+ }
+
+ async function onClickDelete(api) {
+ await callAPI(api.url, {
+ method: api.method,
+ headers: api.headers,
+ parseResponseBody: false,
+ });
+ getMembers();
+ }
+
+ async function onChangeRole(api, newRole) {
+ await callAPI(api[newRole].url, {
+ method: api[newRole].method,
+ headers: api[newRole].headers,
+ json: api[newRole].body
+ });
+ getMembers();
+ }
+
+ function tableRows(members: Array