From 285849b24c5e8ad2bc9451d20ef57a9ef1ed541b Mon Sep 17 00:00:00 2001 From: L4B0MB4 Date: Mon, 2 Dec 2024 19:52:30 +0100 Subject: [PATCH 1/3] Refactor event handling by introducing interfaces for improved modularity and adding websocket support --- go.mod | 1 + go.sum | 2 + pkg/eventpolling/event_handler.go | 7 --- pkg/eventpolling/polling.go | 5 +- pkg/interfaces/event_handler.go | 8 +++ pkg/interfaces/websocket_connector.go | 6 +++ pkg/websocket/auth_req.go | 9 ++++ pkg/websocket/websocket_connection.go | 70 +++++++++++++++++++++++++++ pkg/websocket/websocket_controller.go | 35 ++++++++++++++ 9 files changed, 134 insertions(+), 9 deletions(-) delete mode 100644 pkg/eventpolling/event_handler.go create mode 100644 pkg/interfaces/event_handler.go create mode 100644 pkg/interfaces/websocket_connector.go create mode 100644 pkg/websocket/auth_req.go create mode 100644 pkg/websocket/websocket_connection.go create mode 100644 pkg/websocket/websocket_controller.go diff --git a/go.mod b/go.mod index c17f9ad..71c2c51 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/goccy/go-json v0.10.3 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/leodido/go-urn v1.4.0 // indirect diff --git a/go.sum b/go.sum index a74f80d..49baffe 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= diff --git a/pkg/eventpolling/event_handler.go b/pkg/eventpolling/event_handler.go deleted file mode 100644 index b729347..0000000 --- a/pkg/eventpolling/event_handler.go +++ /dev/null @@ -1,7 +0,0 @@ -package eventpolling - -import "github.com/L4B0MB4/EVTSRC/pkg/models" - -type EventHanlder interface { - HandleEvent(event models.Event) error -} diff --git a/pkg/eventpolling/polling.go b/pkg/eventpolling/polling.go index e92b404..921352b 100644 --- a/pkg/eventpolling/polling.go +++ b/pkg/eventpolling/polling.go @@ -4,6 +4,7 @@ import ( "time" "github.com/L4B0MB4/EVTSRC/pkg/client" + "github.com/PRYVT/utils/pkg/interfaces" "github.com/PRYVT/utils/pkg/store/repository" "github.com/rs/zerolog/log" ) @@ -11,10 +12,10 @@ import ( type EventPolling struct { client *client.EventSourcingHttpClient eventRepo *repository.EventRepository - eventHandler EventHanlder + eventHandler interfaces.EventHandler } -func NewEventPolling(client *client.EventSourcingHttpClient, eventRepo *repository.EventRepository, eventHandler EventHanlder) *EventPolling { +func NewEventPolling(client *client.EventSourcingHttpClient, eventRepo *repository.EventRepository, eventHandler interfaces.EventHandler) *EventPolling { if client == nil || eventRepo == nil || eventHandler == nil { return nil } diff --git a/pkg/interfaces/event_handler.go b/pkg/interfaces/event_handler.go new file mode 100644 index 0000000..660ccbc --- /dev/null +++ b/pkg/interfaces/event_handler.go @@ -0,0 +1,8 @@ +package interfaces + +import "github.com/L4B0MB4/EVTSRC/pkg/models" + +type EventHandler interface { + HandleEvent(event models.Event) error + AddWebsocketConnection(conn WebsocketConnecter) +} diff --git a/pkg/interfaces/websocket_connector.go b/pkg/interfaces/websocket_connector.go new file mode 100644 index 0000000..ab8a3a7 --- /dev/null +++ b/pkg/interfaces/websocket_connector.go @@ -0,0 +1,6 @@ +package interfaces + +type WebsocketConnecter interface { + WriteJSON(v interface{}) error + ReadForDisconnect() +} diff --git a/pkg/websocket/auth_req.go b/pkg/websocket/auth_req.go new file mode 100644 index 0000000..41d45a2 --- /dev/null +++ b/pkg/websocket/auth_req.go @@ -0,0 +1,9 @@ +package websocket + +import "encoding/json" + +type AuthRequest struct { + Token string `json:"token"` + Type string `json:"type"` + Data json.RawMessage +} diff --git a/pkg/websocket/websocket_connection.go b/pkg/websocket/websocket_connection.go new file mode 100644 index 0000000..6b91fd7 --- /dev/null +++ b/pkg/websocket/websocket_connection.go @@ -0,0 +1,70 @@ +package websocket + +import ( + "fmt" + + "github.com/PRYVT/utils/pkg/auth" + "github.com/PRYVT/utils/pkg/interfaces" + "github.com/google/uuid" + "github.com/gorilla/websocket" + "github.com/rs/zerolog/log" +) + +type WebsocketConnection struct { + connection *websocket.Conn + IsConnected bool + IsAuthenticated bool + userUuid uuid.UUID +} + +func NewWebsocketConnection(conn *websocket.Conn) interfaces.WebsocketConnecter { + wC := &WebsocketConnection{connection: conn} + go wC.ReadForDisconnect() + return wC +} + +func (wC *WebsocketConnection) WriteJSON(v interface{}) error { + if !wC.IsAuthenticated { + return fmt.Errorf("WebsocketConnection is not connected or authenticated") + } + err := wC.connection.WriteJSON(v) + if err != nil { + log.Warn().Err(err).Msg("Error while writing WriteJSON") + } + return err +} + +func (wC *WebsocketConnection) ReadForDisconnect() { + wC.IsConnected = true + for { + authRequest := AuthRequest{} + err := wC.connection.ReadJSON(&authRequest) + if err != nil { + log.Debug().Err(err).Msg("Error while reading from websocket connection") + wC.IsAuthenticated = false + wC.connection.Close() + wC.IsConnected = false + break + } else { + log.Debug().Interface("authReq", authRequest).Msg("Received auth request") + _, err = auth.VerifyToken(authRequest.Token) + if err != nil { + log.Debug().Err(err).Msg("Error while verifying token") + wC.IsAuthenticated = false + wC.connection.Close() + wC.IsConnected = false + break + } + uuid, err := auth.GetUserUuidFromToken(authRequest.Token) + if err != nil { + log.Debug().Err(err).Msg("Error while getting user uuid from token") + wC.IsAuthenticated = false + wC.connection.Close() + wC.IsConnected = false + break + } + wC.userUuid = uuid + wC.IsAuthenticated = true + } + } +} diff --git a/pkg/websocket/websocket_controller.go b/pkg/websocket/websocket_controller.go new file mode 100644 index 0000000..1137d49 --- /dev/null +++ b/pkg/websocket/websocket_controller.go @@ -0,0 +1,35 @@ +package websocket + +import ( + "net/http" + + "github.com/PRYVT/utils/pkg/interfaces" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/rs/zerolog/log" +) + +type WSController struct { + eventHandler interfaces.EventHandler +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +func NewWsController(eventHandler interfaces.EventHandler) *WSController { + + upgrader.CheckOrigin = func(r *http.Request) bool { return true } + return &WSController{eventHandler: eventHandler} +} + +func (w *WSController) OnRequest(c *gin.Context) { + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + log.Warn().Err(err).Msg("Error while upgrading connection") + + } else { + w.eventHandler.AddWebsocketConnection(NewWebsocketConnection(conn)) + } +} From 1b11aa5da5089857438d518ae01bff2bff41def1 Mon Sep 17 00:00:00 2001 From: L4B0MB4 Date: Mon, 2 Dec 2024 20:00:43 +0100 Subject: [PATCH 2/3] Refactor websocket connection handling by introducing interface methods for authentication and connection status, improving encapsulation and code clarity --- pkg/interfaces/websocket_connector.go | 2 ++ pkg/websocket/websocket_connection.go | 35 ++++++++++++++++++--------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/pkg/interfaces/websocket_connector.go b/pkg/interfaces/websocket_connector.go index ab8a3a7..abae76e 100644 --- a/pkg/interfaces/websocket_connector.go +++ b/pkg/interfaces/websocket_connector.go @@ -3,4 +3,6 @@ package interfaces type WebsocketConnecter interface { WriteJSON(v interface{}) error ReadForDisconnect() + IsAuthenticated() bool + IsConnected() bool } diff --git a/pkg/websocket/websocket_connection.go b/pkg/websocket/websocket_connection.go index 6b91fd7..8e47a3f 100644 --- a/pkg/websocket/websocket_connection.go +++ b/pkg/websocket/websocket_connection.go @@ -10,10 +10,15 @@ import ( "github.com/rs/zerolog/log" ) +type WebsocketConnectionInterfacer interface { + WriteJSON(v interface{}) error + ReadForDisconnect() +} + type WebsocketConnection struct { connection *websocket.Conn - IsConnected bool - IsAuthenticated bool + isConnected bool + isAuthenticated bool userUuid uuid.UUID } @@ -23,8 +28,16 @@ func NewWebsocketConnection(conn *websocket.Conn) interfaces.WebsocketConnecter return wC } +func (wC *WebsocketConnection) IsConnected() bool { + return wC.isConnected +} + +func (wC *WebsocketConnection) IsAuthenticated() bool { + return wC.isAuthenticated +} + func (wC *WebsocketConnection) WriteJSON(v interface{}) error { - if !wC.IsAuthenticated { + if !wC.isAuthenticated { return fmt.Errorf("WebsocketConnection is not connected or authenticated") } err := wC.connection.WriteJSON(v) @@ -35,36 +48,36 @@ func (wC *WebsocketConnection) WriteJSON(v interface{}) error { } func (wC *WebsocketConnection) ReadForDisconnect() { - wC.IsConnected = true + wC.isConnected = true for { authRequest := AuthRequest{} err := wC.connection.ReadJSON(&authRequest) if err != nil { log.Debug().Err(err).Msg("Error while reading from websocket connection") - wC.IsAuthenticated = false + wC.isAuthenticated = false wC.connection.Close() - wC.IsConnected = false + wC.isConnected = false break } else { log.Debug().Interface("authReq", authRequest).Msg("Received auth request") _, err = auth.VerifyToken(authRequest.Token) if err != nil { log.Debug().Err(err).Msg("Error while verifying token") - wC.IsAuthenticated = false + wC.isAuthenticated = false wC.connection.Close() - wC.IsConnected = false + wC.isConnected = false break } uuid, err := auth.GetUserUuidFromToken(authRequest.Token) if err != nil { log.Debug().Err(err).Msg("Error while getting user uuid from token") - wC.IsAuthenticated = false + wC.isAuthenticated = false wC.connection.Close() - wC.IsConnected = false + wC.isConnected = false break } wC.userUuid = uuid - wC.IsAuthenticated = true + wC.isAuthenticated = true } } } From 5c0dbbd332ddd8d2762b43482431d36c1ad222c4 Mon Sep 17 00:00:00 2001 From: L4B0MB4 Date: Mon, 2 Dec 2024 20:07:42 +0100 Subject: [PATCH 3/3] Refactor CreateToken function to accept duration parameter for token expiration, enhancing flexibility in token management --- pkg/auth/token_manager.go | 4 ++-- pkg/auth/token_manager_test.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/auth/token_manager.go b/pkg/auth/token_manager.go index ff4991e..1e04789 100644 --- a/pkg/auth/token_manager.go +++ b/pkg/auth/token_manager.go @@ -42,7 +42,7 @@ func GetUserUuidFromToken(tokenString string) (uuid.UUID, error) { } -func CreateToken(userUuid uuid.UUID) (string, error) { +func CreateToken(userUuid uuid.UUID, duration time.Duration) (string, error) { signingSecret, err := getSigningSecret() if err != nil { @@ -53,7 +53,7 @@ func CreateToken(userUuid uuid.UUID) (string, error) { "sub": userUuid, "iss": "pryvt", "aud": "local-audience", - "exp": time.Now().Add(time.Minute * 30).Unix(), + "exp": time.Now().Add(duration).Unix(), "iat": time.Now().Unix(), }) diff --git a/pkg/auth/token_manager_test.go b/pkg/auth/token_manager_test.go index 8195080..81ca25a 100644 --- a/pkg/auth/token_manager_test.go +++ b/pkg/auth/token_manager_test.go @@ -3,6 +3,7 @@ package auth import ( "os" "testing" + "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -15,7 +16,7 @@ func TestGetUserUuidFromToken(t *testing.T) { // Create a test UUID testUuid := uuid.New() - tokenString, err := CreateToken(testUuid) + tokenString, err := CreateToken(testUuid, 1*time.Second) assert.NoError(t, err) returnedUuid, err := GetUserUuidFromToken(tokenString)