From 0358fbbe8fe8659a0a3af48697be180cb9ce539f Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Tue, 23 Nov 2021 21:58:12 +0100 Subject: [PATCH] Test IndieAuth --- indieAuthServer_test.go | 144 ++++++++++++++++++++++++++++++++++++++++ indieAuth_test.go | 78 ++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 indieAuthServer_test.go create mode 100644 indieAuth_test.go diff --git a/indieAuthServer_test.go b/indieAuthServer_test.go new file mode 100644 index 0000000..b1ff2de --- /dev/null +++ b/indieAuthServer_test.go @@ -0,0 +1,144 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/PuerkitoBio/goquery" + "github.com/hacdias/indieauth" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_indieAuthServer(t *testing.T) { + defer os.RemoveAll(t.TempDir()) // I don't know why this is necessary, but it is. + + var err error + + app := &goBlog{ + httpClient: &fakeHttpClient{}, + cfg: &config{ + Db: &configDb{ + File: filepath.Join(t.TempDir(), "test.db"), + }, + Server: &configServer{ + PublicAddress: "https://example.org", + }, + DefaultBlog: "en", + Blogs: map[string]*configBlog{ + "en": { + Lang: "en", + }, + }, + User: &configUser{ + Name: "John Doe", + Nick: "jdoe", + }, + Cache: &configCache{ + Enable: false, + }, + }, + } + + app.d, err = app.buildRouter() + require.NoError(t, err) + + _ = app.initDatabase(false) + app.initComponents(false) + + app.ias.Client = &http.Client{ + Transport: &handlerRoundTripper{ + handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }), + }, + } + + iac := indieauth.NewClient( + "https://example.com/", + "https://example.com/redirect", + &http.Client{ + Transport: &handlerRoundTripper{ + handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + app.d.ServeHTTP(w, r) + }), + }, + }, + ) + require.NotNil(t, iac) + + endpoints, err := iac.DiscoverEndpoints("https://example.org/") + require.NoError(t, err) + if assert.NotNil(t, endpoints) { + assert.Equal(t, "https://example.org/indieauth", endpoints.Authorization) + assert.Equal(t, "https://example.org/indieauth/token", endpoints.Token) + } + + authinfo, redirect, err := iac.Authenticate("https://example.org/", "create") + require.NoError(t, err) + assert.NotNil(t, authinfo) + assert.NotEmpty(t, redirect) + + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, redirect, nil) + app.d.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code) + assert.Contains(t, rec.Body.String(), "https://example.com/redirect") + + parsedHtml, err := goquery.NewDocumentFromReader(strings.NewReader(rec.Body.String())) + require.NoError(t, err) + + indieauthForm := parsedHtml.Find("form[action='/indieauth/accept']") + assert.Equal(t, 1, indieauthForm.Length()) + indieAuthFormRedirectUri := indieauthForm.Find("input[name='redirect_uri']").AttrOr("value", "") + assert.Equal(t, "https://example.com/redirect", indieAuthFormRedirectUri) + indieAuthFormClientId := indieauthForm.Find("input[name='client_id']").AttrOr("value", "") + assert.Equal(t, "https://example.com/", indieAuthFormClientId) + indieAuthFormScopes := indieauthForm.Find("input[name='scopes']").AttrOr("value", "") + assert.Equal(t, "create", indieAuthFormScopes) + indieAuthFormCodeChallenge := indieauthForm.Find("input[name='code_challenge']").AttrOr("value", "") + assert.NotEmpty(t, indieAuthFormCodeChallenge) + indieAuthFormCodeChallengeMethod := indieauthForm.Find("input[name='code_challenge_method']").AttrOr("value", "") + assert.Equal(t, "S256", indieAuthFormCodeChallengeMethod) + indieAuthFormState := indieauthForm.Find("input[name='state']").AttrOr("value", "") + assert.NotEmpty(t, indieAuthFormState) + + rec = httptest.NewRecorder() + reqBody := url.Values{ + "redirect_uri": {indieAuthFormRedirectUri}, + "client_id": {indieAuthFormClientId}, + "scopes": {indieAuthFormScopes}, + "code_challenge": {indieAuthFormCodeChallenge}, + "code_challenge_method": {indieAuthFormCodeChallengeMethod}, + "state": {indieAuthFormState}, + } + req = httptest.NewRequest(http.MethodPost, "https://example.org/indieauth/accept?"+reqBody.Encode(), nil) + setLoggedIn(req, true) + app.d.ServeHTTP(rec, req) + assert.Equal(t, http.StatusFound, rec.Code) + + redirectLocation := rec.Header().Get("Location") + assert.NotEmpty(t, redirectLocation) + redirectUrl, err := url.Parse(redirectLocation) + require.NoError(t, err) + assert.NotEmpty(t, redirectUrl.Query().Get("code")) + assert.NotEmpty(t, redirectUrl.Query().Get("state")) + + validateReq := httptest.NewRequest(http.MethodGet, redirectLocation, nil) + code, err := iac.ValidateCallback(authinfo, validateReq) + require.NoError(t, err) + assert.NotEmpty(t, code) + + profile, err := iac.FetchProfile(authinfo, code) + require.NoError(t, err) + assert.NotNil(t, profile) + assert.Equal(t, "https://example.org/", profile.Me) + + // TODO: Somehow test token endpoint. I can't find a to change the round tripper of the oauth2 http.Client. + +} diff --git a/indieAuth_test.go b/indieAuth_test.go new file mode 100644 index 0000000..4ac89d9 --- /dev/null +++ b/indieAuth_test.go @@ -0,0 +1,78 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "path/filepath" + "strings" + "testing" + + "github.com/hacdias/indieauth" + "github.com/stretchr/testify/assert" +) + +func Test_checkIndieAuth(t *testing.T) { + + app := &goBlog{ + httpClient: &fakeHttpClient{}, + cfg: &config{ + Db: &configDb{ + File: filepath.Join(t.TempDir(), "test.db"), + }, + Server: &configServer{}, + DefaultBlog: "en", + Blogs: map[string]*configBlog{ + "en": { + Lang: "en", + }, + }, + }, + } + + _ = app.initDatabase(false) + app.initComponents(false) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + rec := httptest.NewRecorder() + + checked1 := false + app.checkIndieAuth(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + checked1 = true + })).ServeHTTP(rec, req) + assert.False(t, checked1) + + token, err := app.db.indieAuthSaveToken(&indieauth.AuthenticationRequest{ + ClientID: "https://example.com/", + Scopes: strings.Split("create update delete", " "), + }) + assert.NoError(t, err) + assert.NotEmpty(t, token) + + req.Header.Set("Authorization", "Bearer "+token) + + checked2 := false + app.checkIndieAuth(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + assert.Equal(t, "create update delete", r.Context().Value(indieAuthScope).(string)) + checked2 = true + })).ServeHTTP(rec, req) + assert.True(t, checked2) + +} + +func Test_addAllScopes(t *testing.T) { + + req := httptest.NewRequest(http.MethodGet, "/", nil) + rec := httptest.NewRecorder() + + checked := false + addAllScopes(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + scope := r.Context().Value(indieAuthScope).(string) + assert.Contains(t, scope, "create") + assert.Contains(t, scope, "update") + assert.Contains(t, scope, "delete") + assert.Contains(t, scope, "media") + checked = true + })).ServeHTTP(rec, req) + assert.True(t, checked) + +}