mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-22 13:33:09 -06:00
Add auth fallback tests
This commit is contained in:
parent
6535be51ff
commit
f9a08db24d
|
|
@ -15,6 +15,7 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
|
|
@ -102,6 +103,22 @@ func AuthFallback(
|
|||
w http.ResponseWriter, req *http.Request, authType string,
|
||||
cfg *config.ClientAPI,
|
||||
) *util.JSONResponse {
|
||||
// We currently only support reCaptcha, so fail early if that's not requested
|
||||
if authType == authtypes.LoginTypeRecaptcha {
|
||||
if !cfg.RecaptchaEnabled {
|
||||
return writeHTTPMessage(w, req,
|
||||
"Recaptcha login is disabled on this Homeserver",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
_ = writeHTTPMessage(w, req, fmt.Sprintf("Unknown authtype %q", authType), http.StatusNotImplemented)
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("Unknown auth stage type"),
|
||||
}
|
||||
}
|
||||
|
||||
sessionID := req.URL.Query().Get("session")
|
||||
|
||||
if sessionID == "" {
|
||||
|
|
@ -130,25 +147,10 @@ func AuthFallback(
|
|||
|
||||
if req.Method == http.MethodGet {
|
||||
// Handle Recaptcha
|
||||
if authType == authtypes.LoginTypeRecaptcha {
|
||||
if err := checkRecaptchaEnabled(cfg, w, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serveRecaptcha()
|
||||
return nil
|
||||
}
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("Unknown auth stage type"),
|
||||
}
|
||||
} else if req.Method == http.MethodPost {
|
||||
// Handle Recaptcha
|
||||
if authType == authtypes.LoginTypeRecaptcha {
|
||||
if err := checkRecaptchaEnabled(cfg, w, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientIP := req.RemoteAddr
|
||||
err := req.ParseForm()
|
||||
if err != nil {
|
||||
|
|
@ -169,33 +171,13 @@ func AuthFallback(
|
|||
serveSuccess()
|
||||
return nil
|
||||
}
|
||||
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("Unknown auth stage type"),
|
||||
}
|
||||
}
|
||||
_ = writeHTTPMessage(w, req, "Bad method", http.StatusMethodNotAllowed)
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusMethodNotAllowed,
|
||||
JSON: jsonerror.NotFound("Bad method"),
|
||||
}
|
||||
}
|
||||
|
||||
// checkRecaptchaEnabled creates an error response if recaptcha is not usable on homeserver.
|
||||
func checkRecaptchaEnabled(
|
||||
cfg *config.ClientAPI,
|
||||
w http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) *util.JSONResponse {
|
||||
if !cfg.RecaptchaEnabled {
|
||||
return writeHTTPMessage(w, req,
|
||||
"Recaptcha login is disabled on this Homeserver",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeHTTPMessage writes the given header and message to the HTTP response writer.
|
||||
// Returns an error JSONResponse obtained through httputil.LogThenError if the writing failed, otherwise nil.
|
||||
func writeHTTPMessage(
|
||||
|
|
|
|||
105
clientapi/routing/auth_fallback_test.go
Normal file
105
clientapi/routing/auth_fallback_test.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/test/testrig"
|
||||
)
|
||||
|
||||
func Test_AuthFallback(t *testing.T) {
|
||||
base, _, _ := testrig.Base(nil)
|
||||
defer base.Close()
|
||||
|
||||
for _, useHCaptcha := range []bool{false, true} {
|
||||
for _, recaptchaEnabled := range []bool{false, true} {
|
||||
for _, wantErr := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("useHCaptcha(%v) - recaptchaEnabled(%v) - wantErr(%v)", useHCaptcha, recaptchaEnabled, wantErr), func(t *testing.T) {
|
||||
// Set the defaults for each test
|
||||
base.Cfg.ClientAPI.Defaults(config.DefaultOpts{Generate: true, Monolithic: true})
|
||||
base.Cfg.ClientAPI.RecaptchaEnabled = recaptchaEnabled
|
||||
base.Cfg.ClientAPI.RecaptchaPublicKey = "pub"
|
||||
base.Cfg.ClientAPI.RecaptchaPrivateKey = "priv"
|
||||
if useHCaptcha {
|
||||
base.Cfg.ClientAPI.RecaptchaSiteVerifyAPI = "https://hcaptcha.com/siteverify"
|
||||
base.Cfg.ClientAPI.RecaptchaApiJsUrl = "https://js.hcaptcha.com/1/api.js"
|
||||
base.Cfg.ClientAPI.RecaptchaFormField = "h-captcha-response"
|
||||
base.Cfg.ClientAPI.RecaptchaSitekeyClass = "h-captcha"
|
||||
}
|
||||
cfgErrs := &config.ConfigErrors{}
|
||||
base.Cfg.ClientAPI.Verify(cfgErrs, true)
|
||||
if len(*cfgErrs) > 0 {
|
||||
t.Fatalf("(hCaptcha=%v) unexpected config errors: %s", useHCaptcha, cfgErrs.Error())
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/?session=1337", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &base.Cfg.ClientAPI)
|
||||
if !recaptchaEnabled {
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("unexpected response code: %d, want %d", rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
if rec.Body.String() != "Recaptcha login is disabled on this Homeserver" {
|
||||
t.Fatalf("unexpected response body: %s", rec.Body.String())
|
||||
}
|
||||
} else {
|
||||
if !strings.Contains(rec.Body.String(), base.Cfg.ClientAPI.RecaptchaSitekeyClass) {
|
||||
t.Fatalf("body does not contain %s: %s", base.Cfg.ClientAPI.RecaptchaSitekeyClass, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if wantErr {
|
||||
_, _ = w.Write([]byte(`{"success":false}`))
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"success":true}`))
|
||||
}))
|
||||
defer srv.Close() // nolint: errcheck
|
||||
|
||||
base.Cfg.ClientAPI.RecaptchaSiteVerifyAPI = srv.URL
|
||||
|
||||
req = httptest.NewRequest(http.MethodPost, "/?session=1337", nil)
|
||||
req.Form = url.Values{}
|
||||
req.Form.Add(base.Cfg.ClientAPI.RecaptchaFormField, "someRandomValue")
|
||||
rec = httptest.NewRecorder()
|
||||
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &base.Cfg.ClientAPI)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("unknown fallbacks are handled correctly", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/?session=1337", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
AuthFallback(rec, req, "DoesNotExist", &base.Cfg.ClientAPI)
|
||||
if rec.Code != http.StatusNotImplemented {
|
||||
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusNotImplemented)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unknown methods are handled correctly", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodDelete, "/?session=1337", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &base.Cfg.ClientAPI)
|
||||
if rec.Code != http.StatusMethodNotAllowed {
|
||||
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusMethodNotAllowed)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("missing session parameter is handled correctly", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
AuthFallback(rec, req, authtypes.LoginTypeRecaptcha, &base.Cfg.ClientAPI)
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("unexpected http status: %d, want %d", rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -78,9 +78,6 @@ func (c *ClientAPI) Verify(configErrs *ConfigErrors, isMonolith bool) {
|
|||
c.TURN.Verify(configErrs)
|
||||
c.RateLimiting.Verify(configErrs)
|
||||
if c.RecaptchaEnabled {
|
||||
checkNotEmpty(configErrs, "client_api.recaptcha_public_key", c.RecaptchaPublicKey)
|
||||
checkNotEmpty(configErrs, "client_api.recaptcha_private_key", c.RecaptchaPrivateKey)
|
||||
checkNotEmpty(configErrs, "client_api.recaptcha_siteverify_api", c.RecaptchaSiteVerifyAPI)
|
||||
if c.RecaptchaSiteVerifyAPI == "" {
|
||||
c.RecaptchaSiteVerifyAPI = "https://www.google.com/recaptcha/api/siteverify"
|
||||
}
|
||||
|
|
@ -93,6 +90,10 @@ func (c *ClientAPI) Verify(configErrs *ConfigErrors, isMonolith bool) {
|
|||
if c.RecaptchaSitekeyClass == "" {
|
||||
c.RecaptchaSitekeyClass = "g-recaptcha-response"
|
||||
}
|
||||
checkNotEmpty(configErrs, "client_api.recaptcha_public_key", c.RecaptchaPublicKey)
|
||||
checkNotEmpty(configErrs, "client_api.recaptcha_private_key", c.RecaptchaPrivateKey)
|
||||
checkNotEmpty(configErrs, "client_api.recaptcha_siteverify_api", c.RecaptchaSiteVerifyAPI)
|
||||
checkNotEmpty(configErrs, "client_api.recaptcha_sitekey_class", c.RecaptchaSitekeyClass)
|
||||
}
|
||||
// Ensure there is any spam counter measure when enabling registration
|
||||
if !c.RegistrationDisabled && !c.OpenRegistrationWithoutVerificationEnabled {
|
||||
|
|
|
|||
Loading…
Reference in a new issue