package server import ( "fmt" "net/http" d "playback-device-server/data" m "playback-device-server/management" "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" ) type UsersApiHandler struct { router *echo.Echo userManager *m.UserManager } func (r *UsersApiHandler) Initialize(authenticator *Authenticator) { r.router.Use(authenticator.Authenticate("/api/users", []string{"/api/users/login"})) usersApi := r.router.Group("/api/users") usersApi.POST("/login", r.handleLogin) usersApi.POST("/logout", r.handleLogout) usersApi.GET("/session/info", r.handleGetSessionInfo) usersApi.POST("/newpassword", r.handleChangePassword) usersApi.PUT("/:id", r.handleUpdateConfig) usersApi.GET("", r.handleGetUsers) usersApi.POST("", r.handleCreateUser) usersApi.DELETE("/:id", r.handleDeleteUser) } func (r UsersApiHandler) handleLogin(context echo.Context) error { var loginData struct { Username string `json:"username"` Password string `json:"password"` } var errorResponse struct { Error string `json:"error"` } error := context.Bind(&loginData) if error != nil { log.Info().Msgf("failed to bind login data: %s", error) errorResponse.Error = fmt.Sprintf("%s", error) context.JSON(400, errorResponse) return error } token, error := r.userManager.Login(loginData.Username, loginData.Password) if error != nil { log.Info().Msgf("failed to login: %s", error) errorResponse.Error = fmt.Sprintf("%s", error) context.JSON(400, errorResponse) return error } thirdyDays := 30 * 24 * 60 * 60 cookie := &http.Cookie{ Name: "token", Value: token, Path: "/", HttpOnly: true, MaxAge: thirdyDays, } context.SetCookie(cookie) return context.JSON(200, "") } func (r UsersApiHandler) handleLogout(context echo.Context) error { r.removeTokenCookie(&context) context.JSON(200, "") authContext := context.(AuthContext) session := authContext.Session r.userManager.DeleteSession(session.Token) return nil } func (r UsersApiHandler) handleChangePassword(context echo.Context) error { authContext := context.(AuthContext) user := authContext.User var data struct { CurrentPassword string `json:"currentPassword"` NewPassword string `json:"newPassword"` } error := context.Bind(&data) if error != nil { SendError(500, context, fmt.Sprintf("Failed to change password: %s", error)) return error } error = r.userManager.UpdatePassword(data.CurrentPassword, data.NewPassword, user) if error != nil { SendError(500, context, fmt.Sprintf("Failed to change password: %s", error)) } return error } func (r UsersApiHandler) handleGetSessionInfo(context echo.Context) error { authContext := context.(AuthContext) user := authContext.User response := struct { UserId string `json:"user_id"` Username string `json:"username"` IsAdmin bool `json:"is_admin"` }{ UserId: user.ID, Username: user.Username, IsAdmin: user.IsAdmin, } return context.JSON(200, response) } func (r UsersApiHandler) handleGetUsers(context echo.Context) error { users, error := r.userManager.GetUsers() if error != nil { SendError(500, context, fmt.Sprintf("failed to get user list: %s", error)) } return context.JSON(200, users) } func (r UsersApiHandler) handleCreateUser(context echo.Context) error { authContext := context.(AuthContext) user := authContext.User if !user.IsAdmin { SendError(403, context, "permission denied") log.Info().Msg("failed to delete user: permission denied") return fmt.Errorf("permission denied") } var newUser d.User error := context.Bind(&newUser) if error != nil { SendError(500, context, fmt.Sprintf("Failed to create user: %s", error)) return error } id, error := r.userManager.CreateUser(&newUser) if error != nil { SendError(500, context, fmt.Sprintf("Failed to create user: %s", error)) return error } user.ID = id return context.JSON(200, user) } func (r UsersApiHandler) handleDeleteUser(context echo.Context) error { authContext := context.(AuthContext) user := authContext.User if !user.IsAdmin { SendError(403, context, "permission denied") log.Info().Msg("failed to delete user: permission denied") return fmt.Errorf("permission denied") } id := context.Param("id") exists, error := r.userManager.UserIdExists(id) if error != nil { SendError(500, context, fmt.Sprintf("Failed to delete user: %s", error)) return error } if !exists { SendError(404, context, fmt.Sprintf("Failed to delete user: Could not find user id %s", id)) return fmt.Errorf("not found") } error = r.userManager.DeleteUser(id) if error != nil { SendError(500, context, fmt.Sprintf("Failed to delete user: %s", error)) return error } return context.JSON(200, "") } func (r *UsersApiHandler) removeTokenCookie(context *echo.Context) { cookie := &http.Cookie{ Name: "token", Path: "/", HttpOnly: true, MaxAge: -1, } (*context).SetCookie(cookie) } func (r *UsersApiHandler) handleUpdateConfig(context echo.Context) error { authContext := context.(AuthContext) user := authContext.User if !user.IsAdmin { SendError(403, context, "permission denied") log.Info().Msg("failed to delete user: permission denied") return fmt.Errorf("permission denied") } id := context.Param("id") data := struct { IsAdmin int `json:"is_admin"` }{ IsAdmin: -1, } error := context.Bind(&data) if error != nil { SendError(500, context, fmt.Sprintf("Failed to update user config: %s", error)) return error } updatingUser, error := r.userManager.GetUserById(id) if error != nil { SendError(500, context, fmt.Sprintf("Failed to update user config: %s", error)) return error } if user.ID != id && data.IsAdmin != -1 { updatingUser.IsAdmin = data.IsAdmin == 1 } error = r.userManager.UpdateUser(updatingUser) if error != nil { SendError(500, context, fmt.Sprintf("Failed to update user config: %s", error)) return error } return context.JSON(200, updatingUser) } func (r *UsersApiHandler) SetRouter(router *echo.Echo) { r.router = router } func (r *UsersApiHandler) SetUserManager(userManager *m.UserManager) { r.userManager = userManager }