diff --git a/app/routes.go b/app/routes.go index 1e4118f..a557214 100644 --- a/app/routes.go +++ b/app/routes.go @@ -3,18 +3,20 @@ package app import ( "github.com/wgroeneveld/go-jamming/app/index" - "github.com/wgroeneveld/go-jamming/app/webmention" "github.com/wgroeneveld/go-jamming/app/pingback" + "github.com/wgroeneveld/go-jamming/app/webmention" ) // stole ideas from https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html // not that contempt with passing conf, but can't create receivers on non-local types, and won't move specifics into package app // https://blog.questionable.services/article/http-handler-error-handling-revisited/ is the better idea, but more work func (s *server) routes() { - s.router.HandleFunc("/", index.Handle(s.conf)).Methods("GET") - s.router.HandleFunc("/pingback", pingback.HandlePost(s.conf)).Methods("POST") - s.router.HandleFunc("/webmention", webmention.HandlePost(s.conf)).Methods("POST") - s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandleGet(s.conf))).Methods("GET") - s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandlePut(s.conf))).Methods("PUT") + cnf := s.conf + + s.router.HandleFunc("/", index.Handle(cnf)).Methods("GET") + s.router.HandleFunc("/pingback", pingback.HandlePost(cnf)).Methods("POST") + s.router.HandleFunc("/webmention", webmention.HandlePost(cnf)).Methods("POST") + s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandleGet(cnf))).Methods("GET") + s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandlePut(cnf))).Methods("PUT") } diff --git a/app/server.go b/app/server.go index c152531..8a729fa 100644 --- a/app/server.go +++ b/app/server.go @@ -2,13 +2,13 @@ package app import ( - "strconv" "net/http" + "strconv" - "github.com/wgroeneveld/go-jamming/common" + "github.com/wgroeneveld/go-jamming/common" - "github.com/gorilla/mux" - "github.com/rs/zerolog/log" + "github.com/gorilla/mux" + "github.com/rs/zerolog/log" ) type server struct { @@ -22,7 +22,7 @@ func unauthorized(w http.ResponseWriter, r *http.Request) { http.Error(w, "401 u func (s *server) authorizedOnly(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - if vars["token"] != s.conf.Token { + if vars["token"] != s.conf.Token || !s.conf.IsAnAllowedDomain(vars["domain"]) { unauthorized(w, r) return } diff --git a/app/server_test.go b/app/server_test.go new file mode 100644 index 0000000..8d6e568 --- /dev/null +++ b/app/server_test.go @@ -0,0 +1,75 @@ +package app + +import ( + "github.com/gorilla/mux" + "github.com/stretchr/testify/assert" + "github.com/wgroeneveld/go-jamming/common" + "net/http" + "net/http/httptest" + "testing" +) + +var conf = &common.Config{ + Token: "boemsjakkalakka", + AllowedWebmentionSources: []string{ "http://ewelja.be" }, +} + +func TestAuthorizedOnlyUnauthorizedWithWrongToken(t *testing.T) { + srv := &server{ + conf: conf, + } + + passed := false + handler := srv.authorizedOnly(func(writer http.ResponseWriter, request *http.Request) { + passed = true + }) + r, _ := http.NewRequest("PUT", "/whatever", nil) + w := httptest.NewRecorder() + r = mux.SetURLVars(r, map[string]string{ + "token": "invalid", + "domain": conf.AllowedWebmentionSources[0], + }) + + handler(w, r) + assert.False(t, passed, "should not have called unauthorized func") +} + +func TestAuthorizedOnlyUnauthorizedWithWrongDomain(t *testing.T) { + srv := &server{ + conf: conf, + } + + passed := false + handler := srv.authorizedOnly(func(writer http.ResponseWriter, request *http.Request) { + passed = true + }) + r, _ := http.NewRequest("PUT", "/whatever", nil) + w := httptest.NewRecorder() + r = mux.SetURLVars(r, map[string]string{ + "token": conf.Token, + "domain": "https://sexymoddafokkas.be", + }) + + handler(w, r) + assert.False(t, passed, "should not have called unauthorized func") +} + +func TestAuthorizedOnlyOkIfTokenAndDomainMatch(t *testing.T) { + srv := &server{ + conf: conf, + } + + passed := false + handler := srv.authorizedOnly(func(writer http.ResponseWriter, request *http.Request) { + passed = true + }) + r, _ := http.NewRequest("PUT", "/whatever", nil) + w := httptest.NewRecorder() + r = mux.SetURLVars(r, map[string]string{ + "token": conf.Token, + "domain": conf.AllowedWebmentionSources[0], + }) + + handler(w, r) + assert.True(t, passed, "should have passed authentication!") +} diff --git a/app/webmention/handler.go b/app/webmention/handler.go index b1429b5..3114d7d 100644 --- a/app/webmention/handler.go +++ b/app/webmention/handler.go @@ -2,15 +2,19 @@ package webmention import ( + "fmt" + "github.com/gorilla/mux" "github.com/wgroeneveld/go-jamming/app/mf" "github.com/wgroeneveld/go-jamming/app/webmention/receive" + "github.com/wgroeneveld/go-jamming/app/webmention/send" "net/http" - "fmt" "github.com/wgroeneveld/go-jamming/common" - "github.com/wgroeneveld/go-jamming/rest" + "github.com/wgroeneveld/go-jamming/rest" ) +var httpClient = &rest.HttpClient{} + func HandleGet(conf *common.Config) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { fmt.Println("handling get") @@ -19,13 +23,28 @@ func HandleGet(conf *common.Config) http.HandlerFunc { func HandlePut(conf *common.Config) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - fmt.Println("handling put") - } + since := getSinceQueryParam(r) + domain := mux.Vars(r)["domain"] + + snder := send.Sender{ + RestClient: httpClient, + Conf: conf, + } + go snder.Send(domain, since) + rest.Accept(w) + } +} + +func getSinceQueryParam(r *http.Request) string { + sinceParam, _ := r.URL.Query()["since"] + since := "" + if len(sinceParam) > 0 { + since = sinceParam[0] + } + return since } func HandlePost(conf *common.Config) http.HandlerFunc { - httpClient := &rest.HttpClient{} - return func(w http.ResponseWriter, r *http.Request) { r.ParseForm() if !validate(r, r.Header, conf) { diff --git a/app/webmention/send/send.go b/app/webmention/send/send.go index 2de63dd..7aecc37 100644 --- a/app/webmention/send/send.go +++ b/app/webmention/send/send.go @@ -1,6 +1,7 @@ package send import ( + "github.com/rs/zerolog/log" "github.com/wgroeneveld/go-jamming/app/mf" "github.com/wgroeneveld/go-jamming/app/pingback/send" "github.com/wgroeneveld/go-jamming/common" @@ -12,6 +13,10 @@ type Sender struct { Conf *common.Config } +func (snder *Sender) Send(domain string, since string) { + log.Info().Str("domain", domain).Str("since", since).Msg(` OK: someone wants to send mentions`) +} + func mention() { pingbackSender := &send.Sender{ RestClient: nil, diff --git a/common/config.go b/common/config.go index a3c82d7..5aaacb5 100644 --- a/common/config.go +++ b/common/config.go @@ -2,9 +2,9 @@ package common import ( + "errors" "os" "strconv" - "errors" "strings" "github.com/rs/zerolog/log" @@ -19,6 +19,15 @@ type Config struct { DisallowedWebmentionDomains []string } +func (c *Config) IsAnAllowedDomain(url string) bool { + for _, domain := range c.AllowedWebmentionSources { + if domain == url { + return true + } + } + return false +} + func (c *Config) FetchDomain(url string) (string, error) { for _, domain := range c.AllowedWebmentionSources { if strings.Contains(url, domain) { diff --git a/common/time.go b/common/time.go index 69bfba4..7f40d01 100644 --- a/common/time.go +++ b/common/time.go @@ -2,5 +2,6 @@ package common import "time" -// I know it's public. Not sure how to handle this in tests, package-independent +// https://labs.yulrizka.com/en/stubbing-time-dot-now-in-golang/ +// None of the above are very appealing. For now, just use the lazy way. var Now = time.Now