diff --git a/server/internal/http/legacy/handler.go b/server/internal/http/legacy/handler.go index 172fef1a..edaf511d 100644 --- a/server/internal/http/legacy/handler.go +++ b/server/internal/http/legacy/handler.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "net" "net/http" "net/url" @@ -41,6 +42,8 @@ var ( type LegacyHandler struct { logger zerolog.Logger serverAddr string + bannedIPs map[string]struct{} + sessionIPs map[string]string } func New() *LegacyHandler { @@ -49,12 +52,14 @@ func New() *LegacyHandler { return &LegacyHandler{ logger: log.With().Str("module", "legacy").Logger(), serverAddr: "127.0.0.1:8080", + bannedIPs: make(map[string]struct{}), + sessionIPs: make(map[string]string), } } func (h *LegacyHandler) Route(r types.Router) { r.Get("/ws", func(w http.ResponseWriter, r *http.Request) error { - s := newSession(h.logger, h.serverAddr) + s := h.newSession(r) // create a new websocket connection connClient, err := DefaultUpgrader.Upgrade(w, r, nil) @@ -66,6 +71,14 @@ func (h *LegacyHandler) Route(r types.Router) { defer connClient.Close() s.connClient = connClient + if h.isBanned(r) { + s.toClient(&oldMessage.SystemMessage{ + Event: oldEvent.SYSTEM_DISCONNECT, + Title: "banned ip", + Message: "you are banned", + }) + } + // create a new session username := r.URL.Query().Get("username") password := r.URL.Query().Get("password") @@ -180,7 +193,11 @@ func (h *LegacyHandler) Route(r types.Router) { }) r.Get("/stats", func(w http.ResponseWriter, r *http.Request) error { - s := newSession(h.logger, h.serverAddr) + if h.isBanned(r) { + return utils.HttpForbidden("banned ip") + } + + s := h.newSession(r) // create a new session username := r.URL.Query().Get("usr") @@ -253,7 +270,11 @@ func (h *LegacyHandler) Route(r types.Router) { }) r.Get("/screenshot.jpg", func(w http.ResponseWriter, r *http.Request) error { - s := newSession(h.logger, h.serverAddr) + if h.isBanned(r) { + return utils.HttpForbidden("banned ip") + } + + s := h.newSession(r) // create a new session username := r.URL.Query().Get("usr") @@ -287,7 +308,11 @@ func (h *LegacyHandler) Route(r types.Router) { // allow downloading and uploading files r.Get("/file", func(w http.ResponseWriter, r *http.Request) error { - s := newSession(h.logger, h.serverAddr) + if h.isBanned(r) { + return utils.HttpForbidden("banned ip") + } + + s := h.newSession(r) // create a new session username := r.URL.Query().Get("usr") @@ -315,7 +340,11 @@ func (h *LegacyHandler) Route(r types.Router) { }) r.Post("/file", func(w http.ResponseWriter, r *http.Request) error { - s := newSession(h.logger, h.serverAddr) + if h.isBanned(r) { + return utils.HttpForbidden("banned ip") + } + + s := h.newSession(r) // create a new session username := r.URL.Query().Get("usr") @@ -341,3 +370,36 @@ func (h *LegacyHandler) Route(r types.Router) { return err }) } + +func (h *LegacyHandler) ban(sessionId string) error { + // find session by id + ip, ok := h.sessionIPs[sessionId] + if !ok { + return fmt.Errorf("session not found") + } + + h.bannedIPs[ip] = struct{}{} + return nil +} + +func (h *LegacyHandler) isBanned(r *http.Request) bool { + ipPort := r.RemoteAddr + ip, _, err := net.SplitHostPort(ipPort) + if err != nil { + h.logger.Error().Err(err).Msg("couldn't split host and port") + return false + } + + _, ok := h.bannedIPs[ip] + return ok +} + +func getIp(r *http.Request) string { + ipPort := r.RemoteAddr + ip, _, err := net.SplitHostPort(ipPort) + if err != nil { + return "" + } + + return ip +} diff --git a/server/internal/http/legacy/session.go b/server/internal/http/legacy/session.go index f81c064d..248b9bdb 100644 --- a/server/internal/http/legacy/session.go +++ b/server/internal/http/legacy/session.go @@ -29,10 +29,13 @@ type memberStruct struct { } type session struct { + r *http.Request + h *LegacyHandler + logger zerolog.Logger serverAddr string - id string + id, ip string token string name string isAdmin bool @@ -48,10 +51,12 @@ type session struct { connBackend *websocket.Conn } -func newSession(logger zerolog.Logger, serverAddr string) *session { +func (h *LegacyHandler) newSession(r *http.Request) *session { return &session{ - logger: logger, - serverAddr: serverAddr, + r: r, + h: h, + logger: h.logger, + serverAddr: h.serverAddr, client: http.DefaultClient, sessions: make(map[string]*memberStruct), } @@ -171,7 +176,8 @@ func (s *session) create(username, password string) error { return err } - s.id = data.ID + s.id, s.ip = data.ID, getIp(s.r) + s.h.sessionIPs[s.id] = s.ip // save session ip by id s.token = data.Token s.name = data.Profile.Name s.isAdmin = data.Profile.IsAdmin @@ -192,4 +198,7 @@ func (s *session) destroy() { if err != nil { s.logger.Error().Err(err).Msg("failed to logout") } + + // remove session id from ip map + delete(s.h.sessionIPs, s.id) } diff --git a/server/internal/http/legacy/wstobackend.go b/server/internal/http/legacy/wstobackend.go index 16b3a1d1..d53a9a31 100644 --- a/server/internal/http/legacy/wstobackend.go +++ b/server/internal/http/legacy/wstobackend.go @@ -329,8 +329,12 @@ func (s *session) wsToBackend(msg []byte) error { return err } - // TODO: No WS equivalent, call HTTP API. - return fmt.Errorf("event not implemented: %s", header.Event) + err = s.h.ban(request.ID) + if err != nil { + return err + } + + fallthrough // continue to kick case oldEvent.ADMIN_KICK: request := &oldMessage.Admin{} @@ -339,8 +343,6 @@ func (s *session) wsToBackend(msg []byte) error { return err } - // TODO: we need to send a message to the user before kicking them - // that they are being kicked so they will not automatically rejoin return s.apiReq(http.MethodPost, "/api/members/"+request.ID, map[string]any{ "can_login": false, }, nil)