From b082257c1e6bff0c7140292db8d0172b705dfb63 Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Sat, 5 Mar 2022 18:13:25 +0100 Subject: [PATCH] Added checks for occupying and leaving a seat based on permissions and user identity --- server/auth/auth.go | 10 +++-- server/db/database.go | 10 +++++ server/server.go | 89 ++++++++++++++++++++++++++----------------- 3 files changed, 72 insertions(+), 37 deletions(-) diff --git a/server/auth/auth.go b/server/auth/auth.go index 0af2fe5..829f8f6 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -32,10 +32,12 @@ type Authenticator interface { // LoggedMiddleware accepts all logged users without checking for specific permissions LoggedMiddleware() func(http.Handler) http.Handler - // UserFromSession returns the userID for this cookie session token - UserFromSession(r *http.Request) (string, error) + // RequestUser returns the userID for this cookie session token + RequestUser(r *http.Request) (string, error) } +var _ Authenticator = &AuthService{} + type AuthService struct { // CheckUserPassword CheckUserPassword func(userID string, password string) error @@ -157,6 +159,7 @@ func (auth *AuthService) Middleware(config *AuthMiddlewareConfig) func(http.Hand } } +// LoggedMiddleware is a shortcut for "Middleware(*AuthMiddlewareConfig)" that checks if a user is logged, no extra permissions are checked func (auth *AuthService) LoggedMiddleware() func(http.Handler) http.Handler { return auth.Middleware(&AuthMiddlewareConfig{ RequireLogged: true, @@ -164,7 +167,8 @@ func (auth *AuthService) LoggedMiddleware() func(http.Handler) http.Handler { }) } -func (auth *AuthService) UserFromSession(r *http.Request) (string, error) { +// RequestUser retrives the "userID" from the given request based on the cookie session token +func (auth *AuthService) RequestUser(r *http.Request) (string, error) { cookie, err := r.Cookie(SessionCookieName) if err != nil { return "", err diff --git a/server/db/database.go b/server/db/database.go index 056c492..a27412f 100644 --- a/server/db/database.go +++ b/server/db/database.go @@ -15,6 +15,16 @@ type User struct { Permissions []string `json:"permissions"` } +func (user User) HasPermission(neededPerm string) bool { + for _, perm := range user.Permissions { + if perm == neededPerm { + return true + } + } + + return false +} + type Room struct { ID string `json:"id"` SeatIDs []string `json:"seatIds"` diff --git a/server/server.go b/server/server.go index 3128df2..0b79113 100644 --- a/server/server.go +++ b/server/server.go @@ -89,11 +89,7 @@ func NewServer() *Server { roomIDs, _ := database.GetRooms() for _, roomID := range roomIDs { - server.roomServerEventStreams[roomID] = serverevents.New(&serverevents.Config{ - Connected: func(client chan string) { - server.ConnectedToRoom(roomID, client) - }, - }) + server.roomServerEventStreams[roomID] = serverevents.New(&serverevents.Config{}) } server.setupRoutes() @@ -104,6 +100,7 @@ func NewServer() *Server { func (server *Server) setupRoutes() { api := server.ApiRoute database := server.Database + auth := server.authService // Authenticated Routes @@ -118,16 +115,16 @@ func (server *Server) setupRoutes() { return } - server.authService.Login(w, r, credentials.Username, credentials.Password) + auth.Login(w, r, credentials.Username, credentials.Password) }) - api.With(server.authService.LoggedMiddleware()). + api.With(auth.LoggedMiddleware()). Post("/logout", func(w http.ResponseWriter, r *http.Request) { - server.authService.Logout(w) + auth.Logout(w) }) api.Get("/user", func(w http.ResponseWriter, r *http.Request) { - userID, err := server.authService.UserFromSession(r) + userID, err := auth.RequestUser(r) if err != nil { httputil.WriteJSON(w, nil) return @@ -177,14 +174,20 @@ func (server *Server) setupRoutes() { server.roomServerEventStreams[roomID[0]].ServeHTTP(w, r) }) - api.With(server.authService.LoggedMiddleware()). + api.With(auth.LoggedMiddleware()). Post("/seat/occupy", func(w http.ResponseWriter, r *http.Request) { - userID, err := server.authService.UserFromSession(r) + userID, err := auth.RequestUser(r) if err != nil { http.Error(w, err.Error(), http.StatusUnauthorized) return } + user, err := database.GetUser(userID) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + seatID, ok := r.URL.Query()["id"] if !ok { http.Error(w, `missing seat id`, http.StatusBadRequest) @@ -193,22 +196,37 @@ func (server *Server) setupRoutes() { seat, err := database.GetSeat(seatID[0]) if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) + http.Error(w, err.Error(), http.StatusInternalServerError) return } - repr.Println(userID, seatID) + if len(seat.OccupiedBy) == 0 { + if err := database.OccupySeat(seatID[0], user.ID); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + server.roomServerEventStreams[seat.RoomID].Broadcast("refresh") + httputil.WriteJSON(w, "ok") + } else { + http.Error(w, `seat already occupied`, http.StatusUnprocessableEntity) + } + }) + + api.With(auth.LoggedMiddleware()). + Post("/seat/leave", func(w http.ResponseWriter, r *http.Request) { + userID, err := auth.RequestUser(r) + if err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } - if err := database.OccupySeat(seatID[0], userID); err != nil { + user, err := database.GetUser(userID) + if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - server.roomServerEventStreams[seat.RoomID].Broadcast("refresh") - httputil.WriteJSON(w, "ok") - }) - api.With(server.authService.LoggedMiddleware()). - Post("/seat/leave", func(w http.ResponseWriter, r *http.Request) { seatID, ok := r.URL.Query()["id"] if !ok { http.Error(w, `missing seat id`, http.StatusBadRequest) @@ -217,25 +235,28 @@ func (server *Server) setupRoutes() { seat, err := database.GetSeat(seatID[0]) if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - - if err := database.FreeSeat(seatID[0]); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - server.roomServerEventStreams[seat.RoomID].Broadcast("refresh") - httputil.WriteJSON(w, "ok") + // Check permissions + if len(seat.OccupiedBy) > 0 { + if user.ID == seat.OccupiedBy[0] || user.HasPermission("admin") || user.HasPermission("moderator") { + + if err := database.FreeSeat(seatID[0]); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + server.roomServerEventStreams[seat.RoomID].Broadcast("refresh") + httputil.WriteJSON(w, "ok") + } else { + http.Error(w, `seat can be freed only by its owner, a moderator or an admin`, http.StatusUnauthorized) + } + } else { + http.Error(w, `seat already empty`, http.StatusUnprocessableEntity) + } }) } -func (server *Server) ConnectedToRoom(roomID string, client chan string) { - // go func() { - // for { - // client <- "hi from server!" - // time.Sleep(1 * time.Second) - // } - // }() -} +// func (server *Server) ConnectedToRoom(roomID string, client chan string)