chore(auth): #331 unify existing tests
All checks were successful
Build Docker Image / Build-Docker-Image (push) Successful in 47s
Build and Push Docker Image / Build-And-Push-Docker-Image (push) Successful in 52s

This commit was merged in pull request #341.
This commit is contained in:
2024-12-20 22:21:38 +01:00
parent 143662fff0
commit ea653f0087

View File

@@ -112,184 +112,193 @@ func TestIntegrationSecurityHeader(t *testing.T) {
func TestIntegrationAuth(t *testing.T) { func TestIntegrationAuth(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("should return secure cookie on signin with generated csrf-token and session-id", func(t *testing.T) { t.Run("SignIn", func(t *testing.T) {
t.Parallel() t.Run("should return secure cookie with NEW session-id", func(t *testing.T) {
t.Parallel()
db, basePath, ctx := setupIntegrationTest(t) db, basePath, ctx := setupIntegrationTest(t)
pass := service.GetHashPassword("password", []byte("salt")) pass := service.GetHashPassword("password", []byte("salt"))
_, err := db.Exec(` _, err := db.Exec(`
INSERT INTO user (user_id, email, email_verified, is_admin, password, salt, created_at) INSERT INTO user (user_id, email, email_verified, is_admin, password, salt, created_at)
VALUES (?, "mail@mail.de", FALSE, FALSE, ?, ?, datetime())`, uuid.New(), pass, []byte("salt")) VALUES (?, "mail@mail.de", FALSE, FALSE, ?, ?, datetime())`, uuid.New(), pass, []byte("salt"))
assert.Nil(t, err) assert.Nil(t, err)
req, err := http.NewRequestWithContext(ctx, "GET", basePath+"/auth/signin", nil) req, err := http.NewRequestWithContext(ctx, "GET", basePath+"/auth/signin", nil)
assert.Nil(t, err) assert.Nil(t, err)
resp, err := httpClient.Do(req) resp, err := httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
csrfToken := findCsrfToken(html) anonymousCsrfToken := findCsrfToken(html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession := findCookie(resp, "id") anonymousSession := findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"password"}, "password": {"password"},
"csrf-token": {csrfToken}, "csrf-token": {anonymousCsrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signin", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signin", strings.NewReader(formData.Encode()))
assert.Nil(t, err) assert.Nil(t, err)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "id="+anonymousSession.Value) req.Header.Set("Cookie", "id="+anonymousSession.Value)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusSeeOther, resp.StatusCode) assert.Equal(t, http.StatusSeeOther, resp.StatusCode)
cookie := findCookie(resp, "id") cookie := findCookie(resp, "id")
assert.NotNil(t, cookie) assert.NotNil(t, cookie)
assert.Equal(t, http.SameSiteStrictMode, cookie.SameSite, "Cookie is not secure") assert.Equal(t, http.SameSiteStrictMode, cookie.SameSite, "Cookie is not secure")
assert.True(t, cookie.HttpOnly, "Cookie is not secure") assert.True(t, cookie.HttpOnly, "Cookie is not secure")
assert.True(t, cookie.Secure, "Cookie is not secure") assert.True(t, cookie.Secure, "Cookie is not secure")
assert.NotEqual(t, anonymousSession.Value, cookie.Value, "Session ID did not change")
})
}) })
t.Run("should change password and invalidate other sessions from user", func(t *testing.T) { t.Run("ChangePassword", func(t *testing.T) {
t.Parallel() t.Run("should change password and invalidate all other user sessions", func(t *testing.T) {
t.Parallel()
db, basePath, ctx := setupIntegrationTest(t) db, basePath, ctx := setupIntegrationTest(t)
userId := uuid.New() userId := uuid.New()
userIdOther := uuid.New() userIdOther := uuid.New()
pass := service.GetHashPassword("password", []byte("salt")) pass := service.GetHashPassword("password", []byte("salt"))
_, err := db.Exec(` _, err := db.Exec(`
INSERT INTO user (user_id, email, email_verified, is_admin, password, salt, created_at) INSERT INTO user (user_id, email, email_verified, is_admin, password, salt, created_at)
VALUES (?, "mail@mail.de", FALSE, FALSE, ?, ?, datetime())`, userId, pass, []byte("salt")) VALUES (?, "mail@mail.de", FALSE, FALSE, ?, ?, datetime())`, userId, pass, []byte("salt"))
sessionId := "session-id" sessionId := "session-id"
assert.Nil(t, err) assert.Nil(t, err)
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO session (session_id, user_id, created_at, expires_at) INSERT INTO session (session_id, user_id, created_at, expires_at)
VALUES (?, ?, datetime(), datetime("now", "+1 day"))`, sessionId, userId) VALUES (?, ?, datetime(), datetime("now", "+1 day"))`, sessionId, userId)
assert.Nil(t, err) assert.Nil(t, err)
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO session (session_id, user_id, created_at, expires_at) INSERT INTO session (session_id, user_id, created_at, expires_at)
VALUES ("second", ?, datetime(), datetime("now", "+1 day"))`, userId) VALUES ("second", ?, datetime(), datetime("now", "+1 day"))`, userId)
assert.Nil(t, err) assert.Nil(t, err)
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO session (session_id, user_id, created_at, expires_at) INSERT INTO session (session_id, user_id, created_at, expires_at)
VALUES ("other", ?, datetime(), datetime("now", "+1 day"))`, userIdOther) VALUES ("other", ?, datetime(), datetime("now", "+1 day"))`, userIdOther)
assert.Nil(t, err)
req, err := http.NewRequestWithContext(ctx, "GET", basePath+"/auth/change-password", nil)
assert.Nil(t, err)
req.Header.Set("Cookie", "id="+sessionId)
resp, err := httpClient.Do(req)
assert.Nil(t, err)
html, err := html.Parse(resp.Body)
assert.Nil(t, err)
csrfToken := findCsrfToken(html)
assert.NotEqual(t, "", csrfToken)
formData := url.Values{
"current-password": {"password"},
"new-password": {"MyNewSecurePassword1!"},
"csrf-token": {csrfToken},
}
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode()))
assert.Nil(t, err)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "id="+sessionId)
req.Header.Set("HX-Request", "true")
resp, err = httpClient.Do(req)
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var sessionIds []string
sessions, err := db.Query(`SELECT session_id FROM session WHERE NOT user_id = ? ORDER BY session_id`, uuid.Nil)
assert.Nil(t, err)
for sessions.Next() {
var sessionId string
err = sessions.Scan(&sessionId)
assert.Nil(t, err) assert.Nil(t, err)
sessionIds = append(sessionIds, sessionId)
}
assert.Equal(t, 2, len(sessionIds)) req, err := http.NewRequestWithContext(ctx, "GET", basePath+"/auth/change-password", nil)
assert.Equal(t, "other", sessionIds[0]) assert.Nil(t, err)
assert.Equal(t, "session-id", sessionIds[1]) req.Header.Set("Cookie", "id="+sessionId)
resp, err := httpClient.Do(req)
assert.Nil(t, err)
html, err := html.Parse(resp.Body)
assert.Nil(t, err)
csrfToken := findCsrfToken(html)
assert.NotEqual(t, "", csrfToken)
formData := url.Values{
"current-password": {"password"},
"new-password": {"MyNewSecurePassword1!"},
"csrf-token": {csrfToken},
}
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode()))
assert.Nil(t, err)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "id="+sessionId)
req.Header.Set("HX-Request", "true")
resp, err = httpClient.Do(req)
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var sessionIds []string
sessions, err := db.Query(`SELECT session_id FROM session WHERE NOT user_id = ? ORDER BY session_id`, uuid.Nil)
assert.Nil(t, err)
for sessions.Next() {
var sessionId string
err = sessions.Scan(&sessionId)
assert.Nil(t, err)
sessionIds = append(sessionIds, sessionId)
}
assert.Equal(t, 2, len(sessionIds))
assert.Equal(t, "other", sessionIds[0])
assert.Equal(t, "session-id", sessionIds[1])
})
}) })
t.Run("should forget password and invalidate all user sessions", func(t *testing.T) {
t.Parallel()
d, basePath, ctx := setupIntegrationTest(t) t.Run("ForgotPassword", func(t *testing.T) {
userId := uuid.New() t.Run("should change password and invalidate ALL sessions", func(t *testing.T) {
t.Parallel()
pass := service.GetHashPassword("password", []byte("salt")) d, basePath, ctx := setupIntegrationTest(t)
_, err := d.Exec(` userId := uuid.New()
pass := service.GetHashPassword("password", []byte("salt"))
_, err := d.Exec(`
INSERT INTO user (user_id, email, email_verified, is_admin, password, salt, created_at) INSERT INTO user (user_id, email, email_verified, is_admin, password, salt, created_at)
VALUES (?, "mail@mail.de", FALSE, FALSE, ?, ?, datetime())`, userId, pass, []byte("salt")) VALUES (?, "mail@mail.de", FALSE, FALSE, ?, ?, datetime())`, userId, pass, []byte("salt"))
assert.Nil(t, err) assert.Nil(t, err)
_, err = d.Exec(` _, err = d.Exec(`
INSERT INTO session (session_id, user_id, created_at, expires_at) INSERT INTO session (session_id, user_id, created_at, expires_at)
VALUES ("session-id", ?, datetime(), datetime("now", "+1 day"))`, userId) VALUES ("session-id", ?, datetime(), datetime("now", "+1 day"))`, userId)
assert.Nil(t, err) assert.Nil(t, err)
req, err := http.NewRequestWithContext(ctx, "GET", basePath+"/auth/forgot-password", nil) req, err := http.NewRequestWithContext(ctx, "GET", basePath+"/auth/forgot-password", nil)
assert.Nil(t, err) assert.Nil(t, err)
resp, err := httpClient.Do(req) resp, err := httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
sessionId := findCookie(resp, "id").Value sessionId := findCookie(resp, "id").Value
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
csrfToken := findCsrfToken(html) csrfToken := findCsrfToken(html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", csrfToken)
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"csrf-token": {csrfToken}, "csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/forgot-password", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/forgot-password", strings.NewReader(formData.Encode()))
assert.Nil(t, err) assert.Nil(t, err)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "id="+sessionId) req.Header.Set("Cookie", "id="+sessionId)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, http.StatusOK, resp.StatusCode)
var token string var token string
err = d.QueryRow("SELECT token FROM token WHERE type = ?", types.TokenTypePasswordReset).Scan(&token) err = d.QueryRow("SELECT token FROM token WHERE type = ?", types.TokenTypePasswordReset).Scan(&token)
assert.Nil(t, err) assert.Nil(t, err)
formData = url.Values{ formData = url.Values{
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"csrf-token": {csrfToken}, "csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/forgot-password-actual", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/forgot-password-actual", strings.NewReader(formData.Encode()))
assert.Nil(t, err) assert.Nil(t, err)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "id="+sessionId) req.Header.Set("Cookie", "id="+sessionId)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("HX-Current-URL", basePath+"/auth/change-password?token="+url.QueryEscape(token)) req.Header.Set("HX-Current-URL", basePath+"/auth/change-password?token="+url.QueryEscape(token))
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, http.StatusOK, resp.StatusCode)
sessions, err := d.Query("SELECT session_id FROM session WHERE user_id = ?", userId) sessions, err := d.Query("SELECT session_id FROM session WHERE user_id = ?", userId)
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, sessions.Next()) assert.False(t, sessions.Next())
})
}) })
} }