Organize backend into product executable and shared Go modules
This commit is contained in:
1 parent
981e84a4b4
commit
f0720ec66b
382 files changed
+1944
-6989
No files matched your search
@@ -0,0 +1,122 @@
|
||||
package browser
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetRefreshCookieUsesHardenedHostOnlyPolicy(t *testing.T) {
|
||||
t.Setenv("API_ENDPOINT", "/api/v1")
|
||||
req := httptest.NewRequest(http.MethodPost, "http://app.sendico.io/api/v1/accounts/login", nil)
|
||||
req.Header.Set(TransportHeader, TransportCookie)
|
||||
req.Header.Set("X-Forwarded-Proto", "https")
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
SetRefreshCookie(recorder, req, "secret-refresh-token", time.Now().Add(time.Hour))
|
||||
|
||||
cookies := recorder.Result().Cookies()
|
||||
require.Len(t, cookies, 1)
|
||||
cookie := cookies[0]
|
||||
require.Equal(t, RefreshCookie, cookie.Name)
|
||||
require.Equal(t, "secret-refresh-token", cookie.Value)
|
||||
require.Equal(t, "/api/v1/accounts", cookie.Path)
|
||||
require.Empty(t, cookie.Domain, "an omitted Domain attribute keeps the cookie host-only")
|
||||
require.True(t, cookie.HttpOnly)
|
||||
require.True(t, cookie.Secure)
|
||||
require.Equal(t, http.SameSiteStrictMode, cookie.SameSite)
|
||||
require.Positive(t, cookie.MaxAge)
|
||||
}
|
||||
|
||||
func TestSetRefreshCookieUsesConfiguredAPIPathOutsideAccountsRouter(t *testing.T) {
|
||||
t.Setenv("API_ENDPOINT", "/gateway/v2/")
|
||||
req := httptest.NewRequest(http.MethodPost, "https://app.sendico.io/gateway/v2/verification/login", nil)
|
||||
req.Header.Set(TransportHeader, TransportCookie)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
SetRefreshCookie(recorder, req, "refresh", time.Now().Add(time.Hour))
|
||||
|
||||
require.Equal(t, "/gateway/v2/accounts", recorder.Result().Cookies()[0].Path)
|
||||
}
|
||||
|
||||
func TestRefreshCookieIsAlwaysSecure(t *testing.T) {
|
||||
t.Setenv("API_ENDPOINT", "/api/v1")
|
||||
tests := []struct {
|
||||
name string
|
||||
target string
|
||||
forwardedProto string
|
||||
}{
|
||||
{name: "public hostname", target: "http://app.sendico.io/api/v1/accounts/login"},
|
||||
{name: "localhost", target: "http://localhost:4000/api/v1/accounts/login"},
|
||||
{name: "loopback IP", target: "http://127.0.0.1:4000/api/v1/accounts/login"},
|
||||
{name: "private IP", target: "http://192.168.1.20:4000/api/v1/accounts/login"},
|
||||
{name: "forwarded HTTP", target: "http://app.sendico.io/api/v1/accounts/login", forwardedProto: "http"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, test.target, nil)
|
||||
req.Header.Set(TransportHeader, TransportCookie)
|
||||
if test.forwardedProto != "" {
|
||||
req.Header.Set("X-Forwarded-Proto", test.forwardedProto)
|
||||
}
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
SetRefreshCookie(recorder, req, "refresh", time.Now().Add(time.Hour))
|
||||
|
||||
require.True(t, recorder.Result().Cookies()[0].Secure)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearRefreshCookieUsesHardenedPolicy(t *testing.T) {
|
||||
t.Setenv("API_ENDPOINT", "/api/v1")
|
||||
req := httptest.NewRequest(http.MethodPost, "http://192.168.1.20:4000/api/v1/accounts/logout", nil)
|
||||
req.Header.Set(TransportHeader, TransportCookie)
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
ClearRefreshCookie(recorder, req)
|
||||
|
||||
cookies := recorder.Result().Cookies()
|
||||
require.Len(t, cookies, 1)
|
||||
cookie := cookies[0]
|
||||
require.Equal(t, RefreshCookie, cookie.Name)
|
||||
require.Empty(t, cookie.Value)
|
||||
require.Equal(t, -1, cookie.MaxAge)
|
||||
require.True(t, cookie.HttpOnly)
|
||||
require.True(t, cookie.Secure)
|
||||
require.Equal(t, http.SameSiteStrictMode, cookie.SameSite)
|
||||
}
|
||||
|
||||
func TestValidateCSRFRejectsCrossSiteAndMissingHeaderRequests(t *testing.T) {
|
||||
allowed := []string{"https://app.sendico.io"}
|
||||
|
||||
missingHeader := httptest.NewRequest(http.MethodPost, "https://app.sendico.io/api/v1/payments", nil)
|
||||
missingHeader.Header.Set(TransportHeader, TransportCookie)
|
||||
missingHeader.Header.Set("Origin", allowed[0])
|
||||
require.Error(t, ValidateCSRF(missingHeader, allowed))
|
||||
|
||||
crossSite := httptest.NewRequest(http.MethodPost, "https://app.sendico.io/api/v1/payments", nil)
|
||||
crossSite.Header.Set(TransportHeader, TransportCookie)
|
||||
crossSite.Header.Set(CSRFHeader, CSRFValue)
|
||||
crossSite.Header.Set("Origin", "https://attacker.example")
|
||||
require.Error(t, ValidateCSRF(crossSite, allowed))
|
||||
|
||||
valid := httptest.NewRequest(http.MethodPost, "https://app.sendico.io/api/v1/payments", nil)
|
||||
valid.Header.Set(TransportHeader, TransportCookie)
|
||||
valid.Header.Set(CSRFHeader, CSRFValue)
|
||||
valid.Header.Set("Origin", allowed[0])
|
||||
require.NoError(t, ValidateCSRF(valid, allowed))
|
||||
}
|
||||
|
||||
func TestRefreshCredentialNeverFallsBackForCookieTransport(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "https://app.sendico.io/api/v1/accounts/refresh", nil)
|
||||
req.Header.Set(TransportHeader, TransportCookie)
|
||||
|
||||
credential, err := RefreshCredential(req, "body-token")
|
||||
require.Error(t, err)
|
||||
require.Empty(t, credential)
|
||||
}
|
||||
Reference in new issue
Block a user