add: logout handling
This commit is contained in:
@@ -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"`
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user