diff --git a/cmd/owl/web/handler.go b/cmd/owl/web/handler.go index 6ce5e38..eaed8f4 100644 --- a/cmd/owl/web/handler.go +++ b/cmd/owl/web/handler.go @@ -258,21 +258,6 @@ func userMicropubHandler(repo *owl.Repository) func(http.ResponseWriter, *http.R return } - // verify access token - token := r.Header.Get("Authorization") - if token == "" { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Unauthorized")) - return - } - token = strings.TrimPrefix(token, "Bearer ") - valid, _ := user.ValidateAccessToken(token) - if !valid { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Unauthorized")) - return - } - // parse request form err = r.ParseForm() if err != nil { @@ -280,6 +265,40 @@ func userMicropubHandler(repo *owl.Repository) func(http.ResponseWriter, *http.R w.Write([]byte("Bad request")) return } + + // verify access token + header_token := r.Header.Get("Authorization") + form_token := r.Form.Get("access_token") + if header_token != "" { + header_token = strings.TrimPrefix(header_token, "Bearer ") + } + + if header_token == "" && form_token == "" { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("Unauthorized")) + return + } + + if header_token != "" && form_token != "" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("Multiple access tokens provided")) + return + } + + var token string + if header_token != "" { + token = header_token + } else { + token = form_token + } + + valid, _ := user.ValidateAccessToken(token) + if !valid { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("Unauthorized")) + return + } + h := r.Form.Get("h") content := r.Form.Get("content") name := r.Form.Get("name") @@ -290,9 +309,9 @@ func userMicropubHandler(repo *owl.Repository) func(http.ResponseWriter, *http.R w.Write([]byte("Bad request. h must be entry")) return } - if content == "" || name == "" { + if content == "" { w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("Bad request. content and name are required")) + w.Write([]byte("Bad request. content is required")) return } @@ -313,8 +332,8 @@ func userMicropubHandler(repo *owl.Repository) func(http.ResponseWriter, *http.R return } + w.Header().Add("Location", post.FullUrl()) w.WriteHeader(http.StatusCreated) - w.Header().Set("Location", post.FullUrl()) } } diff --git a/cmd/owl/web/micropub_test.go b/cmd/owl/web/micropub_test.go index fd6e010..7947a13 100644 --- a/cmd/owl/web/micropub_test.go +++ b/cmd/owl/web/micropub_test.go @@ -45,3 +45,107 @@ func TestMicropubMinimalArticle(t *testing.T) { assertions.AssertStatus(t, rr, http.StatusCreated) } + +func TestMicropubWithoutName(t *testing.T) { + repo, user := getSingleUserTestRepo() + user.ResetPassword("testpassword") + + code, _ := user.GenerateAuthCode( + "test", "test", "test", "test", "test", + ) + token, _, _ := user.GenerateAccessToken(owl.AuthCode{ + Code: code, + ClientId: "test", + RedirectUri: "test", + CodeChallenge: "test", + CodeChallengeMethod: "test", + Scope: "test", + }) + + // Create Request and Response + form := url.Values{} + form.Add("h", "entry") + form.Add("content", "Test Content") + + req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode()))) + req.Header.Add("Authorization", "Bearer "+token) + assertions.AssertNoError(t, err, "Error creating request") + rr := httptest.NewRecorder() + router := main.SingleUserRouter(&repo) + router.ServeHTTP(rr, req) + + assertions.AssertStatus(t, rr, http.StatusCreated) + loc_header := rr.Header().Get("Location") + assertions.Assert(t, loc_header != "", "Location header should be set") +} + +func TestMicropubAccessTokenInBody(t *testing.T) { + repo, user := getSingleUserTestRepo() + user.ResetPassword("testpassword") + + code, _ := user.GenerateAuthCode( + "test", "test", "test", "test", "test", + ) + token, _, _ := user.GenerateAccessToken(owl.AuthCode{ + Code: code, + ClientId: "test", + RedirectUri: "test", + CodeChallenge: "test", + CodeChallengeMethod: "test", + Scope: "test", + }) + + // Create Request and Response + form := url.Values{} + form.Add("h", "entry") + form.Add("content", "Test Content") + form.Add("access_token", token) + + req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode()))) + assertions.AssertNoError(t, err, "Error creating request") + rr := httptest.NewRecorder() + router := main.SingleUserRouter(&repo) + router.ServeHTTP(rr, req) + + assertions.AssertStatus(t, rr, http.StatusCreated) + loc_header := rr.Header().Get("Location") + assertions.Assert(t, loc_header != "", "Location header should be set") +} + +func TestMicropubAccessTokenInBoth(t *testing.T) { + repo, user := getSingleUserTestRepo() + user.ResetPassword("testpassword") + + code, _ := user.GenerateAuthCode( + "test", "test", "test", "test", "test", + ) + token, _, _ := user.GenerateAccessToken(owl.AuthCode{ + Code: code, + ClientId: "test", + RedirectUri: "test", + CodeChallenge: "test", + CodeChallengeMethod: "test", + Scope: "test", + }) + + // Create Request and Response + form := url.Values{} + form.Add("h", "entry") + form.Add("content", "Test Content") + form.Add("access_token", token) + + req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode()))) + req.Header.Add("Authorization", "Bearer "+token) + assertions.AssertNoError(t, err, "Error creating request") + rr := httptest.NewRecorder() + router := main.SingleUserRouter(&repo) + router.ServeHTTP(rr, req) + + assertions.AssertStatus(t, rr, http.StatusBadRequest) +}