add email auth validation with middleware

This commit is contained in:
Umputun
2022-06-05 11:57:51 -05:00
parent 12e4f283fe
commit 5f8e16cbe2
2 changed files with 58 additions and 0 deletions
+30
View File
@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"
"sync"
"time"
@@ -224,6 +225,7 @@ func (s *Rest) routes() chi.Router {
router.Group(func(r chi.Router) {
r.Use(middleware.Timeout(5 * time.Second))
r.Use(logInfoWithBody, tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil)), middleware.NoCache)
r.Use(validEmaiAuth()) // reject suspicious email logins
r.Mount("/auth", authHandler)
})
@@ -650,6 +652,34 @@ func subscribersOnly(enable bool) func(http.Handler) http.Handler {
}
}
// validEmaiAuth is a middleware for auth endpoints for email method.
// it rejects login request if user or email are suspicious
func validEmaiAuth() func(http.Handler) http.Handler {
// matches ui side validation, adding min/max limitation
reUser := regexp.MustCompile(`^[\p{L}\d\s_]{4,64}$`)
return func(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.URL.Path, "/email/login") {
// not email login, skip the check
h.ServeHTTP(w, r)
return
}
if u := r.URL.Query().Get("user"); u != "" {
if !reUser.MatchString(u) {
http.Error(w, "Access denied", http.StatusForbidden)
return
}
}
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
}
func parseError(err error, defaultCode int) (code int) {
code = defaultCode
+28
View File
@@ -389,6 +389,34 @@ func TestRest_subscribersOnly(t *testing.T) {
}
}
func Test_validEmaiAuth(t *testing.T) {
tbl := []struct {
req string
status int
}{
{"/auth/email/login?site=remark42&address=umputun%example.com&user=someone", http.StatusOK},
{"/auth/email/login?site=remark42&address=umputun%example.com&user=someone+blah", http.StatusOK},
{"/auth/email/login?site=remark42&address=umputun%example.com&user=Евгений+Умпутун", http.StatusOK},
{"/auth/email/login?site=remark42&address=umputun%example.com&user=12", http.StatusForbidden},
{"/auth/email/login?site=remark42&address=umputun%example.com&user=..blah+blah", http.StatusForbidden},
{"/auth/email/login?site=remark42&address=umputun%example.com&user=someonelooong+loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong", http.StatusForbidden},
{"/auth/twitter/login?site=remark42&address=umputun%example.com&user=..blah+blah", http.StatusOK},
{"/auth/email/login?site=remark42&address=umputun%example.com", http.StatusOK},
}
for i, tt := range tbl {
t.Run(strconv.Itoa(i), func(t *testing.T) {
req := httptest.NewRequest("GET", "http://example.com"+tt.req, http.NoBody)
w := httptest.NewRecorder()
h := validEmaiAuth()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
h.ServeHTTP(w, req)
resp := w.Result()
assert.Equal(t, tt.status, resp.StatusCode)
assert.NoError(t, resp.Body.Close())
})
}
}
// randomPath pick a file or folder name which is not in use for sure
func randomPath(tempDir, basename, suffix string) (string, error) {
for i := 0; i < 10; i++ {