diff --git a/server/server_admin.go b/server/server_admin.go index b724d4b7..514cfcdd 100644 --- a/server/server_admin.go +++ b/server/server_admin.go @@ -24,15 +24,17 @@ func (s *Server) handleUsersGet(w http.ResponseWriter, r *http.Request, v *visit userGrants := make([]*apiUserGrantResponse, len(grants[u.ID])) for i, g := range grants[u.ID] { userGrants[i] = &apiUserGrantResponse{ - Topic: g.TopicPattern, - Permission: g.Permission.String(), + Topic: g.TopicPattern, + Permission: g.Permission.String(), + Provisioned: g.Provisioned, } } usersResponse[i] = &apiUserResponse{ - Username: u.Name, - Role: string(u.Role), - Tier: tier, - Grants: userGrants, + Username: u.Name, + Role: string(u.Role), + Tier: tier, + Grants: userGrants, + Provisioned: u.Provisioned, } } return s.writeJSON(w, usersResponse) diff --git a/server/types.go b/server/types.go index d9519b94..0ca53c7f 100644 --- a/server/types.go +++ b/server/types.go @@ -308,15 +308,17 @@ type apiUserAddOrUpdateRequest struct { } type apiUserResponse struct { - Username string `json:"username"` - Role string `json:"role"` - Tier string `json:"tier,omitempty"` - Grants []*apiUserGrantResponse `json:"grants,omitempty"` + Username string `json:"username"` + Role string `json:"role"` + Tier string `json:"tier,omitempty"` + Grants []*apiUserGrantResponse `json:"grants,omitempty"` + Provisioned bool `json:"provisioned,omitempty"` } type apiUserGrantResponse struct { - Topic string `json:"topic"` // This may be a pattern - Permission string `json:"permission"` + Topic string `json:"topic"` // This may be a pattern + Permission string `json:"permission"` + Provisioned bool `json:"provisioned,omitempty"` } type apiUserDeleteRequest struct { diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json index 5bebf2fa..cd05e7bf 100644 --- a/web/public/static/langs/en.json +++ b/web/public/static/langs/en.json @@ -415,10 +415,13 @@ "admin_users_table_grants_header": "Access grants", "admin_users_table_actions_header": "Actions", "admin_users_table_grant_tooltip": "Permission: {{permission}}", + "admin_users_table_grant_provisioned_tooltip": "Permission: {{permission}} (provisioned, cannot be changed)", "admin_users_table_add_access_tooltip": "Add access grant", "admin_users_table_edit_tooltip": "Edit user", "admin_users_table_delete_tooltip": "Delete user", "admin_users_table_admin_no_actions": "Cannot modify admin users", + "admin_users_provisioned_tooltip": "Provisioned user (defined in server config)", + "admin_users_provisioned_cannot_edit": "Provisioned users cannot be edited or deleted", "admin_users_role_admin": "Admin", "admin_users_role_user": "User", "admin_users_add_button": "Add user", diff --git a/web/src/components/Admin.jsx b/web/src/components/Admin.jsx index cf900dc2..1ff317ab 100644 --- a/web/src/components/Admin.jsx +++ b/web/src/components/Admin.jsx @@ -35,6 +35,7 @@ import { useTranslation } from "react-i18next"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import AddIcon from "@mui/icons-material/Add"; import CloseIcon from "@mui/icons-material/Close"; +import LockIcon from "@mui/icons-material/Lock"; import routes from "./routes"; import { AccountContext } from "./App"; import DialogFooter from "./DialogFooter"; @@ -206,7 +207,14 @@ const UsersTable = (props) => { {users.map((user) => ( - {user.username} + + {user.username} + {user.provisioned && ( + + + + )} + @@ -215,23 +223,31 @@ const UsersTable = (props) => { {user.grants && user.grants.length > 0 ? ( - {user.grants.map((grant, idx) => ( - - handleDeleteAccessClick(user, grant) : undefined} - /> - - ))} + {user.grants.map((grant, idx) => { + const canDelete = user.role !== "admin" && !grant.provisioned; + const tooltipText = grant.provisioned + ? t("admin_users_table_grant_provisioned_tooltip", { permission: grant.permission }) + : t("admin_users_table_grant_tooltip", { permission: grant.permission }); + return ( + + : undefined} + onDelete={canDelete ? () => handleDeleteAccessClick(user, grant) : undefined} + /> + + ); + })} ) : ( "-" )} - {user.role !== "admin" ? ( + {user.role !== "admin" && !user.provisioned ? ( <> handleAddAccessClick(user)} size="small"> @@ -249,6 +265,24 @@ const UsersTable = (props) => { + ) : user.role !== "admin" && user.provisioned ? ( + <> + + handleAddAccessClick(user)} size="small"> + + + + + + + + + + + + + + ) : (