membership: api group membership endpoint (#104)
* added api group handler
* func name fixes
* changed json encoding
diff --git a/core/auth/memberships/main.go b/core/auth/memberships/main.go
index 3bb0948..0c1d104 100644
--- a/core/auth/memberships/main.go
+++ b/core/auth/memberships/main.go
@@ -3,16 +3,18 @@
import (
"database/sql"
"embed"
+ "encoding/json"
"flag"
"fmt"
"html/template"
"log"
"net/http"
- "strings"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
+
+ "github.com/gorilla/mux"
)
var port = flag.Int("port", 8080, "ort to listen on")
@@ -31,7 +33,7 @@
CreateGroup(owner string, group Group) error
AddChildGroup(parent, child string) error
GetGroupsOwnedBy(user string) ([]Group, error)
- GetMembershipGroups(user string) ([]Group, error)
+ GetGroupsUserBelongsTo(user string) ([]Group, error)
IsGroupOwner(user, group string) (bool, error)
AddGroupMember(user, group string) error
AddGroupOwner(user, group string) error
@@ -39,6 +41,8 @@
GetGroupMembers(group string) ([]string, error)
GetGroupDescription(group string) (string, error)
GetAvailableGroupsAsChild(group string) ([]string, error)
+ GetAllTransitiveGroupsForUser(user string) ([]string, error)
+ GetGroupsGroupBelongsTo(group string) ([]string, error)
}
type Server struct {
@@ -118,7 +122,7 @@
return s.queryGroups(query, user)
}
-func (s *SQLiteStore) GetMembershipGroups(user string) ([]Group, error) {
+func (s *SQLiteStore) GetGroupsUserBelongsTo(user string) ([]Group, error) {
query := `
SELECT groups.name, groups.description
FROM groups
@@ -311,6 +315,62 @@
return availableGroups, nil
}
+func (s *SQLiteStore) GetAllTransitiveGroupsForUser(user string) ([]string, error) {
+ directGroups, err := s.GetGroupsUserBelongsTo(user)
+ if err != nil {
+ return nil, err
+ }
+ allGroups := make(map[string]bool)
+ for _, group := range directGroups {
+ if err := s.getParentGroups(group.Name, allGroups); err != nil {
+ return nil, err
+ }
+ }
+ var result []string
+ for group := range allGroups {
+ result = append(result, group)
+ }
+ return result, nil
+}
+
+func (s *SQLiteStore) getParentGroups(group string, allGroups map[string]bool) error {
+ if allGroups[group] {
+ return nil
+ }
+ allGroups[group] = true
+ parentGroups, err := s.GetGroupsGroupBelongsTo(group)
+ if err != nil {
+ return err
+ }
+ for _, parentGroup := range parentGroups {
+ if err := s.getParentGroups(parentGroup, allGroups); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *SQLiteStore) GetGroupsGroupBelongsTo(group string) ([]string, error) {
+ query := "SELECT parent_group FROM group_to_group WHERE child_group = ?"
+ rows, err := s.db.Query(query, group)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var parentGroups []string
+ for rows.Next() {
+ var parentGroup string
+ if err := rows.Scan(&parentGroup); err != nil {
+ return nil, err
+ }
+ parentGroups = append(parentGroups, parentGroup)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return parentGroups, nil
+}
+
func getLoggedInUser(r *http.Request) (string, error) {
// TODO(dtabidze): should make a request to get loggedin user
return "tabo", nil
@@ -335,13 +395,15 @@
}
func (s *Server) Start() {
- http.Handle("/static/", http.FileServer(http.FS(staticResources)))
- http.HandleFunc("/", s.homePageHandler)
- http.HandleFunc("/group/", s.groupHandler)
- http.HandleFunc("/create-group", s.createGroupHandler)
- http.HandleFunc("/add-user", s.addUserHandler)
- http.HandleFunc("/add-child-group", s.addChildGroupHandler)
- log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
+ router := mux.NewRouter()
+ router.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticResources)))
+ router.HandleFunc("/group/{group-name}", s.groupHandler)
+ router.HandleFunc("/create-group", s.createGroupHandler)
+ router.HandleFunc("/add-user", s.addUserHandler)
+ router.HandleFunc("/add-child-group", s.addChildGroupHandler)
+ router.HandleFunc("/api/user/{username}", s.apiMemberOfHandler)
+ router.HandleFunc("/", s.homePageHandler)
+ log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), router))
}
type GroupData struct {
@@ -373,7 +435,7 @@
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- membershipGroups, err := s.store.GetMembershipGroups(loggedInUser)
+ membershipGroups, err := s.store.GetGroupsUserBelongsTo(loggedInUser)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -422,7 +484,9 @@
}
func (s *Server) groupHandler(w http.ResponseWriter, r *http.Request) {
- groupName := strings.TrimPrefix(r.URL.Path, "/group/")
+ // groupName := strings.TrimPrefix(r.URL.Path, "/group/")
+ vars := mux.Vars(r)
+ groupName := vars["group-name"]
tmpl, err := template.New("group").Parse(groupHTML)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -526,6 +590,29 @@
http.Redirect(w, r, "/group/"+parentGroup, http.StatusSeeOther)
}
+type UserInfo struct {
+ MemberOf []string `json:"memberOf"`
+}
+
+func (s *Server) apiMemberOfHandler(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ user, ok := vars["username"]
+ if !ok {
+ http.Error(w, "Username parameter is required", http.StatusBadRequest)
+ return
+ }
+ transitiveGroups, err := s.store.GetAllTransitiveGroupsForUser(user)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ w.Header().Set("Content-Type", "application/json")
+ if err := json.NewEncoder(w).Encode(UserInfo{transitiveGroups}); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
func main() {
flag.Parse()
db, err := NewSQLiteStore(*dbPath)