feat(transaction): #87 add filter capabillities
All checks were successful
Build Docker Image / Build-Docker-Image (push) Successful in 4m30s
Build and Push Docker Image / Build-And-Push-Docker-Image (push) Successful in 4m6s

This commit was merged in pull request #106.
This commit is contained in:
2025-05-18 16:52:50 +02:00
parent 989a31afd1
commit 36e480f2ea
7 changed files with 198 additions and 151 deletions

View File

@@ -1,7 +1,6 @@
package middleware package middleware
import ( import (
"fmt"
"net/http" "net/http"
"strings" "strings"
@@ -29,8 +28,6 @@ func (rr *csrfResponseWriter) Write(data []byte) (int, error) {
dataStr := string(data) dataStr := string(data)
csrfToken, err := rr.auth.GetCsrfToken(rr.session) csrfToken, err := rr.auth.GetCsrfToken(rr.session)
if err == nil { if err == nil {
csrfInput := fmt.Sprintf(`<input type="hidden" name="csrf-token" value="%s" />`, csrfToken)
dataStr = strings.ReplaceAll(dataStr, "</form>", csrfInput+"</form>")
dataStr = strings.ReplaceAll(dataStr, "CSRF_TOKEN", csrfToken) dataStr = strings.ReplaceAll(dataStr, "CSRF_TOKEN", csrfToken)
} }
@@ -48,10 +45,8 @@ func CrossSiteRequestForgery(auth service.Auth) func(http.Handler) http.Handler
r.Method == http.MethodDelete || r.Method == http.MethodDelete ||
r.Method == http.MethodPatch { r.Method == http.MethodPatch {
csrfToken := r.FormValue("csrf-token") csrfToken := r.Header.Get("csrf-token")
if csrfToken == "" {
csrfToken = r.Header.Get("csrf-token")
}
if session == nil || csrfToken == "" || !auth.IsCsrfTokenValid(csrfToken, session.Id) { if session == nil || csrfToken == "" || !auth.IsCsrfTokenValid(csrfToken, session.Id) {
log.Info("CSRF-Token \"%s\" not correct", csrfToken) log.Info("CSRF-Token \"%s\" not correct", csrfToken)
if r.Header.Get("HX-Request") == "true" { if r.Header.Get("HX-Request") == "true" {

View File

@@ -50,7 +50,12 @@ func (h TransactionImpl) handleTransactionPage() http.HandlerFunc {
return return
} }
transactions, err := h.s.GetAll(user) filter := types.TransactionItemsFilter{
AccountId: r.URL.Query().Get("account-id"),
TreasureChestId: r.URL.Query().Get("treasure-chest-id"),
}
transactions, err := h.s.GetAll(user, filter)
if err != nil { if err != nil {
handleError(w, r, err) handleError(w, r, err)
return return
@@ -70,9 +75,14 @@ func (h TransactionImpl) handleTransactionPage() http.HandlerFunc {
accountMap, treasureChestMap := h.getTransactionData(accounts, treasureChests) accountMap, treasureChestMap := h.getTransactionData(accounts, treasureChests)
comp := t.Transaction(transactions, accountMap, treasureChestMap) items := t.TransactionItems(transactions, accountMap, treasureChestMap)
if utils.IsHtmx(r) {
h.r.Render(r, w, items)
} else {
comp := t.Transaction(items, filter, accounts, treasureChests)
h.r.RenderLayout(r, w, comp, user) h.r.RenderLayout(r, w, comp, user)
} }
}
} }
func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc { func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc {

View File

@@ -2,6 +2,7 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -157,12 +158,12 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"password"}, "password": {"password"},
"csrf-token": {"invalid-csrf-token"},
} }
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("csrf-token", "invalid-csrf-token")
resp, err := httpClient.Do(req) resp, err := httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode) assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
@@ -187,20 +188,20 @@ func TestIntegrationAuth(t *testing.T) {
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(t, html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", csrfToken)
session := findCookie(resp, "id") session := findCookie(resp, "id")
formData := url.Values{ formData := url.Values{
"email": {"invalid@mail.de"}, "email": {"invalid@mail.de"},
"password": {"password"}, "password": {"password"},
"csrf-token": {csrfToken},
} }
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="+session.Value) req.Header.Set("Cookie", "id="+session.Value)
req.Header.Set("csrf-token", csrfToken)
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)
@@ -227,14 +228,13 @@ func TestIntegrationAuth(t *testing.T) {
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(t, html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", csrfToken)
session := findCookie(resp, "id") session := findCookie(resp, "id")
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"invalid-password"}, "password": {"invalid-password"},
"csrf-token": {csrfToken},
} }
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()))
@@ -242,6 +242,7 @@ func TestIntegrationAuth(t *testing.T) {
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="+session.Value) req.Header.Set("Cookie", "id="+session.Value)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("csrf-token", csrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
@@ -267,7 +268,7 @@ func TestIntegrationAuth(t *testing.T) {
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(html) anonymousCsrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession := findCookie(resp, "id") anonymousSession := findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -275,12 +276,12 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"password"}, "password": {"password"},
"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("csrf-token", anonymousCsrfToken)
req.Header.Set("Cookie", "id="+anonymousSession.Value) req.Header.Set("Cookie", "id="+anonymousSession.Value)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
@@ -314,7 +315,7 @@ func TestIntegrationAuth(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
body, err := html.Parse(resp.Body) body, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(body) anonymousCsrfToken := findCsrfToken(t, body)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession := findCookie(resp, "id") anonymousSession := findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -322,13 +323,13 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"password"}, "password": {"password"},
"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)
req.Header.Set("csrf-token", anonymousCsrfToken)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
timeStart := time.Now() timeStart := time.Now()
@@ -348,7 +349,7 @@ func TestIntegrationAuth(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
body, err = html.Parse(resp.Body) body, err = html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken = findCsrfToken(body) anonymousCsrfToken = findCsrfToken(t, body)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession = findCookie(resp, "id") anonymousSession = findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -356,7 +357,6 @@ func TestIntegrationAuth(t *testing.T) {
formData = url.Values{ formData = url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"wrong-password"}, "password": {"wrong-password"},
"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()))
@@ -364,6 +364,7 @@ func TestIntegrationAuth(t *testing.T) {
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)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("csrf-token", anonymousCsrfToken)
timeStart = time.Now() timeStart = time.Now()
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
@@ -382,7 +383,7 @@ func TestIntegrationAuth(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
body, err = html.Parse(resp.Body) body, err = html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken = findCsrfToken(body) anonymousCsrfToken = findCsrfToken(t, body)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession = findCookie(resp, "id") anonymousSession = findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -390,7 +391,6 @@ func TestIntegrationAuth(t *testing.T) {
"email": {"invalid-mail@mail.de"}, "email": {"invalid-mail@mail.de"},
"password": {"password"}, "password": {"password"},
"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()))
@@ -398,6 +398,7 @@ func TestIntegrationAuth(t *testing.T) {
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)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("csrf-token", anonymousCsrfToken)
timeStart = time.Now() timeStart = time.Now()
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
@@ -427,7 +428,7 @@ func TestIntegrationAuth(t *testing.T) {
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(html) anonymousCsrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession := findCookie(resp, "id") anonymousSession := findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -435,7 +436,6 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"password"}, "password": {"password"},
"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()))
@@ -443,6 +443,7 @@ func TestIntegrationAuth(t *testing.T) {
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)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -493,12 +494,12 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"password"}, "password": {"password"},
"csrf-token": {"invalid-csrf-token"},
} }
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("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("csrf-token", "invalid-csrf-token")
resp, err := httpClient.Do(req) resp, err := httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -516,7 +517,7 @@ func TestIntegrationAuth(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
body, err := html.Parse(resp.Body) body, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(body) anonymousCsrfToken := findCsrfToken(t, body)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession := findCookie(resp, "id") anonymousSession := findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -524,13 +525,13 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"insecure-password"}, "password": {"insecure-password"},
"csrf-token": {anonymousCsrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signup", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signup", 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("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("Cookie", "id="+anonymousSession.Value) req.Header.Set("Cookie", "id="+anonymousSession.Value)
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -553,7 +554,7 @@ func TestIntegrationAuth(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
body, err := html.Parse(resp.Body) body, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(body) anonymousCsrfToken := findCsrfToken(t, body)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession := findCookie(resp, "id") anonymousSession := findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -561,13 +562,13 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"secure-Password!1"}, "password": {"secure-Password!1"},
"csrf-token": {anonymousCsrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signup", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signup", 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("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("Cookie", "id="+anonymousSession.Value) req.Header.Set("Cookie", "id="+anonymousSession.Value)
req.Header.Set("csrf-token", anonymousCsrfToken)
timeStart := time.Now() timeStart := time.Now()
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
timeEnd := time.Now() timeEnd := time.Now()
@@ -590,7 +591,7 @@ func TestIntegrationAuth(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
body, err := html.Parse(resp.Body) body, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(body) anonymousCsrfToken := findCsrfToken(t, body)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSession := findCookie(resp, "id") anonymousSession := findCookie(resp, "id")
assert.NotNil(t, anonymousSession) assert.NotNil(t, anonymousSession)
@@ -598,13 +599,13 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"password": {"secure-Password!1"}, "password": {"secure-Password!1"},
"csrf-token": {anonymousCsrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signup", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/signup", 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("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("Cookie", "id="+anonymousSession.Value) req.Header.Set("Cookie", "id="+anonymousSession.Value)
req.Header.Set("csrf-token", anonymousCsrfToken)
timeStart := time.Now() timeStart := time.Now()
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
timeEnd := time.Now() timeEnd := time.Now()
@@ -826,12 +827,11 @@ func TestIntegrationAuth(t *testing.T) {
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(t, html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", csrfToken)
formData := url.Values{ formData := url.Values{
"password": {"wrong-password"}, "password": {"wrong-password"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/delete-account", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/delete-account", strings.NewReader(formData.Encode()))
@@ -839,6 +839,7 @@ func TestIntegrationAuth(t *testing.T) {
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("csrf-token", csrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -864,7 +865,6 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"password": {"password"}, "password": {"password"},
"csrf-token": {"wrong-csrf-token"},
} }
req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/delete-account", strings.NewReader(formData.Encode())) req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/delete-account", strings.NewReader(formData.Encode()))
@@ -872,6 +872,7 @@ func TestIntegrationAuth(t *testing.T) {
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("csrf-token", "wrong-csrf-token")
resp, err := httpClient.Do(req) resp, err := httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -886,12 +887,12 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"name": {"Name"}, "name": {"Name"},
"csrf-token": {csrfToken},
} }
req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", strings.NewReader(formData.Encode())) req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", 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("csrf-token", csrfToken)
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)
@@ -899,13 +900,13 @@ func TestIntegrationAuth(t *testing.T) {
formData = url.Values{ formData = url.Values{
"name": {"Name"}, "name": {"Name"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/treasurechest/new", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/treasurechest/new", 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("csrf-token", csrfToken)
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)
@@ -913,26 +914,26 @@ func TestIntegrationAuth(t *testing.T) {
formData = url.Values{ formData = url.Values{
"timestamp": {"2006-01-02"}, "timestamp": {"2006-01-02"},
"value": {"100.00"}, "value": {"100.00"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/transaction/new", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/transaction/new", 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("csrf-token", csrfToken)
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)
formData = url.Values{ formData = url.Values{
"password": {"password"}, "password": {"password"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/delete-account", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/delete-account", 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("csrf-token", csrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -985,7 +986,7 @@ func TestIntegrationAuth(t *testing.T) {
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)
anonymousCsrfToken := findCsrfToken(html) anonymousCsrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
anonymousSessionId := findCookie(resp, "id").Value anonymousSessionId := findCookie(resp, "id").Value
assert.NotEqual(t, "", anonymousSessionId) assert.NotEqual(t, "", anonymousSessionId)
@@ -993,13 +994,13 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"current-password": {"password"}, "current-password": {"password"},
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"csrf-token": {anonymousCsrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-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="+anonymousSessionId) req.Header.Set("Cookie", "id="+anonymousSessionId)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1026,7 +1027,6 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"current-password": {"password"}, "current-password": {"password"},
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"csrf-token": {"invalid-csrf-token"},
} }
req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode())) req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode()))
@@ -1034,6 +1034,7 @@ func TestIntegrationAuth(t *testing.T) {
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("csrf-token", "invalid-csrf-token")
resp, err := httpClient.Do(req) resp, err := httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1069,19 +1070,19 @@ func TestIntegrationAuth(t *testing.T) {
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) csrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", csrfToken)
formData := url.Values{ formData := url.Values{
"current-password": {"wrong-password"}, "current-password": {"wrong-password"},
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-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")
req.Header.Set("csrf-token", csrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1117,19 +1118,19 @@ func TestIntegrationAuth(t *testing.T) {
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) csrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", csrfToken)
formData := url.Values{ formData := url.Values{
"current-password": {"password"}, "current-password": {"password"},
"new-password": {"insecure-password"}, "new-password": {"insecure-password"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-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")
req.Header.Set("csrf-token", csrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1176,13 +1177,12 @@ func TestIntegrationAuth(t *testing.T) {
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(t, html)
assert.NotEqual(t, "", csrfToken) assert.NotEqual(t, "", csrfToken)
formData := url.Values{ formData := url.Values{
"current-password": {"password"}, "current-password": {"password"},
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/api/auth/change-password", strings.NewReader(formData.Encode()))
@@ -1190,6 +1190,7 @@ func TestIntegrationAuth(t *testing.T) {
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("csrf-token", csrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1267,12 +1268,12 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"csrf-token": {"invalid-csrf-token"},
} }
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("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("csrf-token", "invalid-csrf-token")
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1297,17 +1298,17 @@ func TestIntegrationAuth(t *testing.T) {
assert.NotEqual(t, "", anonymousSessionId) assert.NotEqual(t, "", anonymousSessionId)
body, err := html.Parse(resp.Body) body, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(body) anonymousCsrfToken := findCsrfToken(t, body)
formData := url.Values{ formData := url.Values{
"email": {"non-existent@mail.de"}, "email": {"non-existent@mail.de"},
"csrf-token": {anonymousCsrfToken},
} }
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("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("Cookie", "id="+anonymousSessionId) req.Header.Set("Cookie", "id="+anonymousSessionId)
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1337,17 +1338,17 @@ func TestIntegrationAuth(t *testing.T) {
assert.NotEqual(t, "", anonymousSessionId) assert.NotEqual(t, "", anonymousSessionId)
body, err := html.Parse(resp.Body) body, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(body) anonymousCsrfToken := findCsrfToken(t, body)
formData := url.Values{ formData := url.Values{
"email": {"mail@mail.de"}, "email": {"mail@mail.de"},
"csrf-token": {anonymousCsrfToken},
} }
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("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("Cookie", "id="+anonymousSessionId) req.Header.Set("Cookie", "id="+anonymousSessionId)
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
@@ -1383,12 +1384,11 @@ func TestIntegrationAuth(t *testing.T) {
anonymousSessionId := findCookie(resp, "id").Value anonymousSessionId := findCookie(resp, "id").Value
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(html) anonymousCsrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
formData := url.Values{ formData := url.Values{
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"csrf-token": {anonymousCsrfToken},
} }
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)
@@ -1396,6 +1396,7 @@ func TestIntegrationAuth(t *testing.T) {
req.Header.Set("Cookie", "id="+anonymousSessionId) req.Header.Set("Cookie", "id="+anonymousSessionId)
req.Header.Set("HX-Request", "true") req.Header.Set("HX-Request", "true")
req.Header.Set("HX-Current-URL", basePath+"/auth/change-password?token=invalidToken") req.Header.Set("HX-Current-URL", basePath+"/auth/change-password?token=invalidToken")
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode) assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
@@ -1424,7 +1425,7 @@ func TestIntegrationAuth(t *testing.T) {
anonymousSessionId := findCookie(resp, "id").Value anonymousSessionId := findCookie(resp, "id").Value
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(html) anonymousCsrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
token := "password-reset-token" token := "password-reset-token"
@@ -1435,7 +1436,6 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"csrf-token": {anonymousCsrfToken},
} }
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)
@@ -1443,6 +1443,7 @@ func TestIntegrationAuth(t *testing.T) {
req.Header.Set("Cookie", "id="+anonymousSessionId) req.Header.Set("Cookie", "id="+anonymousSessionId)
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))
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode) assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
@@ -1471,7 +1472,7 @@ func TestIntegrationAuth(t *testing.T) {
anonymousSessionId := findCookie(resp, "id").Value anonymousSessionId := findCookie(resp, "id").Value
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
anonymousCsrfToken := findCsrfToken(html) anonymousCsrfToken := findCsrfToken(t, html)
assert.NotEqual(t, "", anonymousCsrfToken) assert.NotEqual(t, "", anonymousCsrfToken)
token := "password-reset-token" token := "password-reset-token"
@@ -1482,7 +1483,6 @@ func TestIntegrationAuth(t *testing.T) {
formData := url.Values{ formData := url.Values{
"new-password": {"insecure-password"}, "new-password": {"insecure-password"},
"csrf-token": {anonymousCsrfToken},
} }
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)
@@ -1490,6 +1490,7 @@ func TestIntegrationAuth(t *testing.T) {
req.Header.Set("Cookie", "id="+anonymousSessionId) req.Header.Set("Cookie", "id="+anonymousSessionId)
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))
req.Header.Set("csrf-token", anonymousCsrfToken)
resp, err = httpClient.Do(req) resp, err = httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, http.StatusBadRequest, resp.StatusCode) assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
@@ -1524,18 +1525,18 @@ func TestIntegrationAuth(t *testing.T) {
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(t, 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},
} }
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")
req.Header.Set("csrf-token", csrfToken)
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)
@@ -1546,13 +1547,13 @@ func TestIntegrationAuth(t *testing.T) {
formData = url.Values{ formData = url.Values{
"new-password": {"MyNewSecurePassword1!"}, "new-password": {"MyNewSecurePassword1!"},
"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("csrf-token", csrfToken)
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)
@@ -1668,12 +1669,12 @@ func TestIntegrationAccount(t *testing.T) {
formData := url.Values{ formData := url.Values{
"name": {"name"}, "name": {"name"},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/account/some-id", strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/account/some-id", 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("csrf-token", csrfToken)
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)
@@ -1699,12 +1700,12 @@ func TestIntegrationAccount(t *testing.T) {
expectedName := "My great Account" expectedName := "My great Account"
formData := url.Values{ formData := url.Values{
"name": {expectedName}, "name": {expectedName},
"csrf-token": {csrfToken},
} }
req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", strings.NewReader(formData.Encode())) req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", 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("csrf-token", csrfToken)
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)
@@ -1718,12 +1719,12 @@ func TestIntegrationAccount(t *testing.T) {
expectedNewName := "My new Account" expectedNewName := "My new Account"
formData = url.Values{ formData = url.Values{
"name": {expectedNewName}, "name": {expectedNewName},
"csrf-token": {csrfToken},
} }
req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/account/"+id.String(), strings.NewReader(formData.Encode())) req, err = http.NewRequestWithContext(ctx, "POST", basePath+"/account/"+id.String(), 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("csrf-token", csrfToken)
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)
@@ -1769,12 +1770,12 @@ func TestIntegrationAccount(t *testing.T) {
formData := url.Values{ formData := url.Values{
"name": {expectedName1}, "name": {expectedName1},
"csrf-token": {csrfToken1},
} }
req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", strings.NewReader(formData.Encode())) req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", 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="+sessionId1) req.Header.Set("Cookie", "id="+sessionId1)
req.Header.Set("csrf-token", csrfToken1)
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)
@@ -1810,13 +1811,13 @@ func TestIntegrationAccount(t *testing.T) {
formData := url.Values{ formData := url.Values{
"name": {name}, "name": {name},
"csrf-token": {csrfToken},
} }
req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", strings.NewReader(formData.Encode())) req, err := http.NewRequestWithContext(ctx, "POST", basePath+"/account/new", 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("csrf-token", csrfToken)
resp, err := httpClient.Do(req) resp, err := httpClient.Do(req)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, status, resp.StatusCode, "for name: "+name) assert.Equal(t, status, resp.StatusCode, "for name: "+name)
@@ -1859,7 +1860,7 @@ func createAnonymousSession(t *testing.T, ctx context.Context, basePath string)
html, err := html.Parse(resp.Body) html, err := html.Parse(resp.Body)
assert.Nil(t, err) assert.Nil(t, err)
return findCsrfToken(html), findCookie(resp, "id").Value return findCsrfToken(t, html), findCookie(resp, "id").Value
} }
func findCookie(resp *http.Response, name string) *http.Cookie { func findCookie(resp *http.Response, name string) *http.Cookie {
@@ -1958,19 +1959,19 @@ func waitForReady(
} }
} }
func findCsrfToken(data *html.Node) string { func findCsrfToken(t *testing.T, data *html.Node) string {
attr := getTokenAttribute(data) token := getTokenAttribute(t, data)
if attr != nil { if token != "" {
return attr.Val return token
} }
if data.FirstChild != nil { if data.FirstChild != nil {
if token := findCsrfToken(data.FirstChild); token != "" { if token = findCsrfToken(t, data.FirstChild); token != "" {
return token return token
} }
} }
if data.NextSibling != nil { if data.NextSibling != nil {
if token := findCsrfToken(data.NextSibling); token != "" { if token = findCsrfToken(t, data.NextSibling); token != "" {
return token return token
} }
} }
@@ -1978,25 +1979,16 @@ func findCsrfToken(data *html.Node) string {
return "" return ""
} }
func getTokenAttribute(data *html.Node) *html.Attribute { func getTokenAttribute(t *testing.T, data *html.Node) string {
returnValue := false
for _, attr := range data.Attr { for _, attr := range data.Attr {
if attr.Key == "name" && attr.Val == "csrf-token" { if attr.Key == "hx-headers" {
returnValue = true var data map[string]interface{}
err := json.Unmarshal([]byte(attr.Val), &data)
assert.Nil(t, err)
return data["csrf-token"].(string)
} }
} }
return ""
if !returnValue {
return nil
}
for _, attr := range data.Attr {
if attr.Key == "value" {
return &attr
}
}
return nil
} }
func readBody(t *testing.T, body io.ReadCloser) string { func readBody(t *testing.T, body io.ReadCloser) string {

View File

@@ -29,7 +29,7 @@ type Transaction interface {
Add(user *types.User, transaction types.TransactionInput) (*types.Transaction, error) Add(user *types.User, transaction types.TransactionInput) (*types.Transaction, error)
Update(user *types.User, transaction types.TransactionInput) (*types.Transaction, error) Update(user *types.User, transaction types.TransactionInput) (*types.Transaction, error)
Get(user *types.User, id string) (*types.Transaction, error) Get(user *types.User, id string) (*types.Transaction, error)
GetAll(user *types.User) ([]*types.Transaction, error) GetAll(user *types.User, filter types.TransactionItemsFilter) ([]*types.Transaction, error)
Delete(user *types.User, id string) error Delete(user *types.User, id string) error
RecalculateBalances(user *types.User) error RecalculateBalances(user *types.User) error
@@ -241,13 +241,20 @@ func (s TransactionImpl) Get(user *types.User, id string) (*types.Transaction, e
return &transaction, nil return &transaction, nil
} }
func (s TransactionImpl) GetAll(user *types.User) ([]*types.Transaction, error) { func (s TransactionImpl) GetAll(user *types.User, filter types.TransactionItemsFilter) ([]*types.Transaction, error) {
transactionMetric.WithLabelValues("get_all").Inc() transactionMetric.WithLabelValues("get_all").Inc()
if user == nil { if user == nil {
return nil, ErrUnauthorized return nil, ErrUnauthorized
} }
transactions := make([]*types.Transaction, 0) transactions := make([]*types.Transaction, 0)
err := s.db.Select(&transactions, `SELECT * FROM "transaction" WHERE user_id = ? ORDER BY timestamp DESC`, user.Id) err := s.db.Select(&transactions, `
SELECT *
FROM "transaction"
WHERE user_id = ?
AND (? = '' OR account_id = ?)
AND (? = '' OR treasure_chest_id = ?)
ORDER BY timestamp DESC`, user.Id, filter.AccountId, filter.AccountId, filter.TreasureChestId, filter.TreasureChestId)
err = db.TransformAndLogDbError("transaction GetAll", nil, err) err = db.TransformAndLogDbError("transaction GetAll", nil, err)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -6,24 +6,62 @@ import "spend-sparrow/template/svg"
import "spend-sparrow/types" import "spend-sparrow/types"
import "github.com/google/uuid" import "github.com/google/uuid"
templ Transaction(transactions []*types.Transaction, accounts, treasureChests map[uuid.UUID]string) { templ Transaction(items templ.Component, filter types.TransactionItemsFilter, accounts []*types.Account, treasureChests []*types.TreasureChest) {
{{ }}
<div class="max-w-6xl mt-10 mx-auto"> <div class="max-w-6xl mt-10 mx-auto">
<div class="flex items-center gap-4">
<form
hx-get="/transaction"
hx-target="#transaction-items"
hx-push-url="true"
hx-trigger="change"
>
<select name="account-id" class="bg-white input">
<option value="">- Filter Acount -</option>
for _, account := range accounts {
<option
value={ account.Id.String() }
selected?={ filter.AccountId == account.Id.String() }
>{ account.Name }</option>
}
</select>
<select name="treasure-chest-id" class="bg-white input">
<option value="">- Filter Treasure Chest -</option>
for _, parent := range treasureChests {
if parent.ParentId == uuid.Nil {
<optgroup label={ parent.Name }>
for _, child := range treasureChests {
if child.ParentId == parent.Id {
<option
value={ child.Id.String() }
selected?={ filter.TreasureChestId == child.Id.String() }
>{ child.Name }</option>
}
}
</optgroup>
}
}
</select>
</form>
<button <button
hx-get="/transaction/new" hx-get="/transaction/new"
hx-target="#transaction-items" hx-target="#transaction-items"
hx-swap="afterbegin" hx-swap="afterbegin"
class="ml-auto button button-primary px-2 flex-1 flex items-center gap-2 justify-center" class="button button-primary ml-auto px-2 flex items-center gap-2 justify-center"
> >
@svg.Plus() @svg.Plus()
<p>New Transaction</p> <p>New Transaction</p>
</button> </button>
</div>
@items
</div>
}
templ TransactionItems(transactions []*types.Transaction, accounts, treasureChests map[uuid.UUID]string) {
<div id="transaction-items" class="my-6"> <div id="transaction-items" class="my-6">
for _, transaction := range transactions { for _, transaction := range transactions {
@TransactionItem(transaction, accounts, treasureChests) @TransactionItem(transaction, accounts, treasureChests)
} }
</div> </div>
</div>
} }
templ EditTransaction(transaction *types.Transaction, accounts []*types.Account, treasureChests []*types.TreasureChest) { templ EditTransaction(transaction *types.Transaction, accounts []*types.Account, treasureChests []*types.TreasureChest) {

View File

@@ -45,3 +45,8 @@ type TransactionInput struct {
Party string Party string
Description string Description string
} }
type TransactionItemsFilter struct {
AccountId string
TreasureChestId string
}

View File

@@ -10,7 +10,7 @@ import (
) )
func TriggerToast(w http.ResponseWriter, r *http.Request, class string, message string) { func TriggerToast(w http.ResponseWriter, r *http.Request, class string, message string) {
if isHtmx(r) { if IsHtmx(r) {
w.Header().Set("HX-Trigger", fmt.Sprintf(`{"toast": "%v|%v"}`, class, strings.ReplaceAll(message, `"`, `\"`))) w.Header().Set("HX-Trigger", fmt.Sprintf(`{"toast": "%v|%v"}`, class, strings.ReplaceAll(message, `"`, `\"`)))
} else { } else {
log.Error("Trying to trigger toast in non-HTMX request") log.Error("Trying to trigger toast in non-HTMX request")
@@ -23,7 +23,7 @@ func TriggerToastWithStatus(w http.ResponseWriter, r *http.Request, class string
} }
func DoRedirect(w http.ResponseWriter, r *http.Request, url string) { func DoRedirect(w http.ResponseWriter, r *http.Request, url string) {
if isHtmx(r) { if IsHtmx(r) {
w.Header().Add("HX-Redirect", url) w.Header().Add("HX-Redirect", url)
} else { } else {
http.Redirect(w, r, url, http.StatusSeeOther) http.Redirect(w, r, url, http.StatusSeeOther)
@@ -37,6 +37,6 @@ func WaitMinimumTime[T interface{}](waitTime time.Duration, function func() (T,
return result, err return result, err
} }
func isHtmx(r *http.Request) bool { func IsHtmx(r *http.Request) bool {
return r.Header.Get("HX-Request") == "true" return r.Header.Get("HX-Request") == "true"
} }