add: logout handling

This commit is contained in:
$(pass /github/name)
2024-09-07 11:38:35 +02:00
parent c3944cb4aa
commit 4d6938de96
3 changed files with 82 additions and 8 deletions

View File

@@ -43,7 +43,13 @@ func (uc *UserController) CurrentUserHandler(c *gin.Context) {
c.JSON(http.StatusOK, user.Safe()) c.JSON(http.StatusOK, user.Safe())
} }
func (uc *UserController) LoginUser(c *gin.Context) { func (uc *UserController) LogoutHandler(c *gin.Context) {
// just clear the JWT cookie
c.SetCookie("jwt", "", -1, "/", "", true, true)
c.JSON(http.StatusOK, gin.H{"message": "Logged out successfully"})
}
func (uc *UserController) LoginHandler(c *gin.Context) {
var input struct { var input struct {
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"` Password string `json:"password"`

View File

@@ -70,11 +70,77 @@ func testUserController(t *testing.T) {
testCurrentUserHandler(t) testCurrentUserHandler(t)
} }
func testLoginUser(t *testing.T) (string, http.Cookie) { func testLogoutHandler(t *testing.T) {
loginCookie := testCurrentUserHandler(t)
tests := []struct {
name string
setupCookie func(*http.Request)
expectedStatus int
}{
{
name: "Logout with valid cookie",
setupCookie: func(req *http.Request) {
req.AddCookie(&loginCookie)
},
expectedStatus: http.StatusOK,
},
{
name: "Logout without cookie",
setupCookie: func(req *http.Request) {},
expectedStatus: http.StatusOK, // Logout should still succeed even without a cookie
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/logout", Uc.LogoutHandler)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/logout", nil)
tt.setupCookie(req)
router.ServeHTTP(w, req)
assert.Equal(t, tt.expectedStatus, w.Code)
var response map[string]string
err := json.Unmarshal(w.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, "Logged out successfully", response["message"])
// Check if the cookie has been cleared
var logoutCookie *http.Cookie
for _, cookie := range w.Result().Cookies() {
if cookie.Name == "jwt" {
logoutCookie = cookie
break
}
}
assert.NotNil(t, logoutCookie, "Logout should set a clearing cookie")
assert.Equal(t, "", logoutCookie.Value, "Logout cookie should have empty value")
assert.True(t, logoutCookie.Expires.Before(time.Now()), "Logout cookie should be expired")
// Verify that the user can no longer access protected routes
w = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/current-user", nil)
if logoutCookie != nil {
req.AddCookie(logoutCookie)
}
router.GET("/current-user", middlewares.AuthMiddleware(), Uc.CurrentUserHandler)
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusUnauthorized, w.Code, "User should not be able to access protected routes after logout")
})
}
}
func testLoginHandler(t *testing.T) (string, http.Cookie) {
// This test should run after the user registration test // This test should run after the user registration test
var loginCookie http.Cookie var loginCookie http.Cookie
var loginInput loginInput var loginInput loginInput
t.Run("LoginUser", func(t *testing.T) { t.Run("LoginHandler", func(t *testing.T) {
// Test cases // Test cases
tests := []struct { tests := []struct {
name string name string
@@ -120,7 +186,7 @@ func testLoginUser(t *testing.T) (string, http.Cookie) {
c, w, _ := GetMockedJSONContext([]byte(tt.input), "/login") c, w, _ := GetMockedJSONContext([]byte(tt.input), "/login")
// Execute // Execute
Uc.LoginUser(c) Uc.LoginHandler(c)
// Assert // Assert
assert.Equal(t, tt.wantStatusCode, w.Code) assert.Equal(t, tt.wantStatusCode, w.Code)
@@ -154,8 +220,8 @@ func testLoginUser(t *testing.T) (string, http.Cookie) {
return loginInput.Email, loginCookie return loginInput.Email, loginCookie
} }
func testCurrentUserHandler(t *testing.T) { func testCurrentUserHandler(t *testing.T) http.Cookie {
loginEmail, loginCookie := testLoginUser(t) loginEmail, loginCookie := testLoginHandler(t)
// This test should run after the user login test // This test should run after the user login test
invalidCookie := http.Cookie{ invalidCookie := http.Cookie{
Name: "jwt", Name: "jwt",
@@ -238,6 +304,7 @@ func testCurrentUserHandler(t *testing.T) {
}) })
} }
return loginCookie
} }
func validateUser(assert bool, wantDBData map[string]interface{}) error { func validateUser(assert bool, wantDBData map[string]interface{}) error {
@@ -460,7 +527,7 @@ func getBaseUser() models.User {
func customizeInput(customize func(models.User) models.User) *RegistrationData { func customizeInput(customize func(models.User) models.User) *RegistrationData {
user := getBaseUser() user := getBaseUser()
user = customize(user) // Apply the customization user = customize(user) // Apply the customization
return &RegistrationData{User: user, Password: user.Password} return &RegistrationData{User: user}
} }
func getTestUsers() []RegisterUserTest { func getTestUsers() []RegisterUserTest {

View File

@@ -12,7 +12,7 @@ func RegisterRoutes(router *gin.Engine, userController *controllers.UserControll
router.POST("/users/register", userController.RegisterUser) router.POST("/users/register", userController.RegisterUser)
router.POST("/users/contact", contactController.RelayContactRequest) router.POST("/users/contact", contactController.RelayContactRequest)
router.POST("/users/login", userController.LoginUser) router.POST("/users/login", userController.LoginHandler)
router.POST("/csp-report", middlewares.CSPReportHandling) router.POST("/csp-report", middlewares.CSPReportHandling)
// create subrouter for teh authenticated area /account // create subrouter for teh authenticated area /account
@@ -30,5 +30,6 @@ func RegisterRoutes(router *gin.Engine, userController *controllers.UserControll
authRouter.Use(middlewares.AuthMiddleware()) authRouter.Use(middlewares.AuthMiddleware())
{ {
authRouter.POST("/currentUser", userController.CurrentUserHandler) authRouter.POST("/currentUser", userController.CurrentUserHandler)
authRouter.POST("/logout", userController.LogoutHandler)
} }
} }