memberships: remove owner, member and child group (#128)
* remove child group
* remove group as a child fixed
* remove group as a child fixed
* remove owner and member from group
* removed table names from html, changed api url approach
* changed handler url
diff --git a/core/auth/memberships/group.html b/core/auth/memberships/group.html
index f923232..a9cbb66 100644
--- a/core/auth/memberships/group.html
+++ b/core/auth/memberships/group.html
@@ -8,6 +8,7 @@
<link rel="stylesheet" href="/static/main.css">
</head>
<body class="container">
+ {{- $parentGroupName := .GroupName }}
<div>
<h2 class="headline">{{ .GroupName }} Group Management</h2>
<p class="description">{{ .Description }}</p>
@@ -44,7 +45,11 @@
{{- range .Owners }}
<tr>
<td><a href="/user/{{ . }}">{{ . }}</a></td>
- <td>Delete</td>
+ <td>
+ <form action="/remove-group-owner/{{ $parentGroupName }}/{{ . }}" method="post" onsubmit="return confirm('Are you sure you want to revoke user {{ . }} ownership of the {{ $parentGroupName }} group?')">
+ <button type="submit" class="button">Remove</button>
+ </form>
+ </td>
</tr>
{{- end }}
</table>
@@ -57,7 +62,11 @@
{{- range .Members }}
<tr>
<td><a href="/user/{{ . }}">{{ . }}</a></td>
- <td>Delete</td>
+ <td>
+ <form action="/remove-group-member/{{ $parentGroupName }}/{{ . }}" method="post" onsubmit="return confirm('Are you sure you want to remove user {{ . }} user from {{ $parentGroupName }} group?')">
+ <button type="submit" class="button">Remove</button>
+ </form>
+ </td>
</tr>
{{- end }}
</table>
@@ -79,11 +88,17 @@
<tr>
<th>Group Name</th>
<th>Description</th>
+ <th>Action</th>
</tr>
{{- range .ChildGroups }}
<tr>
<td><a href="/group/{{ .Name }}">{{ .Name }}</a></td>
<td>{{ .Description }}</td>
+ <td>
+ <form action="/remove-child-group/{{ $parentGroupName }}/{{ .Name }}" method="post" onsubmit="return confirm('Are you sure you want to remove group {{ .Name }} as a child of the group {{ $parentGroupName }}?')">
+ <button type="submit" class="button">Remove</button>
+ </form>
+ </td>
</tr>
{{- end }}
</table>
diff --git a/core/auth/memberships/main.go b/core/auth/memberships/main.go
index a645777..8df9356 100644
--- a/core/auth/memberships/main.go
+++ b/core/auth/memberships/main.go
@@ -51,6 +51,8 @@
GetGroupsGroupBelongsTo(group string) ([]Group, error)
GetDirectChildrenGroups(group string) ([]Group, error)
GetAllTransitiveGroupsForGroup(group string) ([]Group, error)
+ RemoveFromGroupToGroup(parent, child string) error
+ RemoveUserFromTable(username, groupName, tableName string) error
}
type Server struct {
@@ -463,6 +465,47 @@
return childrenGroups, nil
}
+func (s *SQLiteStore) RemoveFromGroupToGroup(parent, child string) error {
+ query := `DELETE FROM group_to_group WHERE parent_group = ? AND child_group = ?`
+ rowDeleted, err := s.db.Exec(query, parent, child)
+ if err != nil {
+ return err
+ }
+ rowDeletedNumber, err := rowDeleted.RowsAffected()
+ if err != nil {
+ return err
+ }
+ if rowDeletedNumber == 0 {
+ return fmt.Errorf("pair of parent '%s' and child '%s' groups not found", parent, child)
+ }
+ return nil
+}
+
+func (s *SQLiteStore) RemoveUserFromTable(username, groupName, tableName string) error {
+ if tableName == "owners" {
+ owners, err := s.GetGroupOwners(groupName)
+ if err != nil {
+ return err
+ }
+ if len(owners) == 1 {
+ return fmt.Errorf("cannot remove the last owner of the group")
+ }
+ }
+ query := fmt.Sprintf("DELETE FROM %s WHERE username = ? AND group_name = ?", tableName)
+ rowDeleted, err := s.db.Exec(query, username, groupName)
+ if err != nil {
+ return err
+ }
+ rowDeletedNumber, err := rowDeleted.RowsAffected()
+ if err != nil {
+ return err
+ }
+ if rowDeletedNumber == 0 {
+ return fmt.Errorf("pair of group '%s' and user '%s' not found", groupName, username)
+ }
+ return nil
+}
+
func getLoggedInUser(r *http.Request) (string, error) {
if user := r.Header.Get("X-User"); user != "" {
return user, nil
@@ -483,6 +526,8 @@
go func() {
r := mux.NewRouter()
r.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticResources)))
+ r.HandleFunc("/remove-child-group/{parent-group}/{child-group}", s.removeChildGroupHandler)
+ r.HandleFunc("/remove-{action}/{group-name}/{username}", s.removeUserFromGroupHandler)
r.HandleFunc("/group/{group-name}", s.groupHandler)
r.HandleFunc("/user/{username}", s.userHandler)
r.HandleFunc("/create-group", s.createGroupHandler)
@@ -512,8 +557,7 @@
return false, err
}
if !isOwner {
- http.Error(w, fmt.Sprintf("You are not the owner of the group %s", group), http.StatusUnauthorized)
- return false, nil
+ return false, fmt.Errorf("you are not the owner of the group %s", group)
}
return true, nil
}
@@ -681,6 +725,75 @@
}
}
+func (s *Server) removeChildGroupHandler(w http.ResponseWriter, r *http.Request) {
+ loggedInUser, err := getLoggedInUser(r)
+ if err != nil {
+ http.Error(w, "User Not Logged In", http.StatusUnauthorized)
+ return
+ }
+ if r.Method == http.MethodPost {
+ vars := mux.Vars(r)
+ parentGroup := vars["parent-group"]
+ childGroup := vars["child-group"]
+ if err := isValidGroupName(parentGroup); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ if err := isValidGroupName(childGroup); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ if _, err := s.checkIsOwner(w, loggedInUser, parentGroup); err != nil {
+ http.Error(w, err.Error(), http.StatusUnauthorized)
+ return
+ }
+ err := s.store.RemoveFromGroupToGroup(parentGroup, childGroup)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, "/group/"+parentGroup, http.StatusSeeOther)
+ }
+}
+
+func (s *Server) removeUserFromGroupHandler(w http.ResponseWriter, r *http.Request) {
+ loggedInUser, err := getLoggedInUser(r)
+ if err != nil {
+ http.Error(w, "User Not Logged In", http.StatusUnauthorized)
+ return
+ }
+ if r.Method == http.MethodPost {
+ vars := mux.Vars(r)
+ username := vars["username"]
+ groupName := vars["group-name"]
+ action := vars["action"]
+ var tableName string
+ switch action {
+ case "group-owner":
+ tableName = "owners"
+ case "group-member":
+ tableName = "user_to_group"
+ default:
+ http.Error(w, "action not found", http.StatusBadRequest)
+ return
+ }
+ if err := isValidGroupName(groupName); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ if _, err := s.checkIsOwner(w, loggedInUser, groupName); err != nil {
+ http.Error(w, err.Error(), http.StatusUnauthorized)
+ return
+ }
+ err := s.store.RemoveUserFromTable(username, groupName, tableName)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, "/group/"+groupName, http.StatusSeeOther)
+ }
+}
+
func (s *Server) addUserHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
@@ -707,6 +820,7 @@
return
}
if _, err := s.checkIsOwner(w, loggedInUser, groupName); err != nil {
+ http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
switch status {
@@ -747,6 +861,7 @@
return
}
if _, err := s.checkIsOwner(w, loggedInUser, parentGroup); err != nil {
+ http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
if err := s.store.AddChildGroup(parentGroup, childGroup); err != nil {