Merge branch 'add-register-fallback' of github.com:Trion129/dendrite into add-register-fallback

This commit is contained in:
Andrew Morgan 2019-08-09 12:35:18 +01:00
commit f852c99eee
4 changed files with 59 additions and 53 deletions

View file

@ -1,4 +1,4 @@
// Copyright 2017 Vector Creations Ltd // Copyright 2019 Parminder Singh <parmsingh129@gmail.com>
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -25,8 +25,8 @@ import (
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
// RecaptchaTemplate is template for recaptcha auth // recaptchaTemplate is an HTML webpage template for recaptcha auth
const RecaptchaTemplate = ` const recaptchaTemplate = `
<html> <html>
<head> <head>
<title>Authentication</title> <title>Authentication</title>
@ -35,7 +35,6 @@ const RecaptchaTemplate = `
<script src="https://www.google.com/recaptcha/api.js" <script src="https://www.google.com/recaptcha/api.js"
async defer></script> async defer></script>
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script> <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<link rel="stylesheet" href="/_matrix/static/client/register/style.css">
<script> <script>
function captchaDone() { function captchaDone() {
$('#registrationForm').submit(); $('#registrationForm').submit();
@ -43,7 +42,7 @@ function captchaDone() {
</script> </script>
</head> </head>
<body> <body>
<form id="registrationForm" method="post" action="{{.MyUrl}}"> <form id="registrationForm" method="post" action="{{.myUrl}}">
<div> <div>
<p> <p>
Hello! We need to prevent computer programs and other automated Hello! We need to prevent computer programs and other automated
@ -52,9 +51,9 @@ function captchaDone() {
<p> <p>
Please verify that you're not a robot. Please verify that you're not a robot.
</p> </p>
<input type="hidden" name="session" value="{{.Session}}" /> <input type="hidden" name="session" value="{{.session}}" />
<div class="g-recaptcha" <div class="g-recaptcha"
data-sitekey="{{.SiteKey}}" data-sitekey="{{.siteKey}}"
data-callback="captchaDone"> data-callback="captchaDone">
</div> </div>
<noscript> <noscript>
@ -67,8 +66,9 @@ function captchaDone() {
</html> </html>
` `
// SuccessTemplate is template for success page after auth flow ends // successTemplate is an HTML template presented to the user after successful
const SuccessTemplate = ` // recaptcha completion
const successTemplate = `
<html> <html>
<head> <head>
<title>Success!</title> <title>Success!</title>
@ -92,8 +92,8 @@ if (window.onAuthDone) {
</html> </html>
` `
// ServeTemplate fills data in template and serves it in http.ResponseWriter // serveTemplate fills template data and serves it using http.ResponseWriter
func ServeTemplate(w http.ResponseWriter, templateHTML string, data map[string]string) { func serveTemplate(w http.ResponseWriter, templateHTML string, data map[string]string) {
t := template.Must(template.New("response").Parse(templateHTML)) t := template.Must(template.New("response").Parse(templateHTML))
if err := t.Execute(w, data); err != nil { if err := t.Execute(w, data); err != nil {
panic(err) panic(err)
@ -108,48 +108,51 @@ func AuthFallback(
sessionID := req.URL.Query().Get("session") sessionID := req.URL.Query().Get("session")
if sessionID == "" { if sessionID == "" {
_, err := w.Write([]byte("Session ID not provided")) return writeErrorMessage(w, req,
if err != nil { "Session ID not provided",
res := httputil.LogThenError(req, err) http.StatusBadRequest,
return &res )
}
return nil
} }
ServeRecaptcha := func() { serveRecaptcha := func() {
data := map[string]string{ data := map[string]string{
"MyUrl": req.URL.String(), "myUrl": req.URL.String(),
"Session": sessionID, "session": sessionID,
"SiteKey": cfg.Matrix.RecaptchaPublicKey, "siteKey": cfg.Matrix.RecaptchaPublicKey,
} }
ServeTemplate(w, RecaptchaTemplate, data) serveTemplate(w, recaptchaTemplate, data)
} }
ServeSuccess := func() { serveSuccess := func() {
data := map[string]string{} data := map[string]string{}
ServeTemplate(w, SuccessTemplate, data) serveTemplate(w, successTemplate, data)
} }
if req.Method == "GET" { if req.Method == http.MethodGet {
// Handle Recaptcha // Handle Recaptcha
if authType == authtypes.LoginTypeRecaptcha { if authType == authtypes.LoginTypeRecaptcha {
if cfg.Matrix.RecaptchaEnabled {
if cfg.Matrix.RecaptchaPublicKey == "" { if cfg.Matrix.RecaptchaPublicKey == "" {
_, err := w.Write([]byte("This Homeserver doesn't have a recaptcha public key")) return writeErrorMessage(w, req,
if err != nil { "This Homeserver doesn't have a recaptcha public key",
res := httputil.LogThenError(req, err) http.StatusInternalServerError,
return &res )
} }
return nil } else {
return writeErrorMessage(w, req,
"Recaptcha login is disabled on this Homeserver",
http.StatusBadRequest,
)
} }
ServeRecaptcha() serveRecaptcha()
return nil return nil
} }
return &util.JSONResponse{ return &util.JSONResponse{
Code: 404, Code: http.StatusNotFound,
JSON: jsonerror.NotFound("Unknown auth stage type"), JSON: jsonerror.NotFound("Unknown auth stage type"),
} }
} else if req.Method == "POST" { } else if req.Method == http.MethodPost {
clientIP := req.RemoteAddr clientIP := req.RemoteAddr
err := req.ParseForm() err := req.ParseForm()
if err != nil { if err != nil {
@ -159,18 +162,32 @@ func AuthFallback(
response := req.Form.Get("g-recaptcha-response") response := req.Form.Get("g-recaptcha-response")
if err := validateRecaptcha(&cfg, response, clientIP); err != nil { if err := validateRecaptcha(&cfg, response, clientIP); err != nil {
ServeRecaptcha() util.GetLogger(req.Context()).Error(err)
return nil return err
} }
// Success. Add recaptcha as a completed login flow // Success. Add recaptcha as a completed login flow
AddCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha) AddCompletedSessionStage(sessionID, authtypes.LoginTypeRecaptcha)
ServeSuccess() serveSuccess()
return nil return nil
} }
return &util.JSONResponse{ return &util.JSONResponse{
Code: 405, Code: http.StatusMethodNotAllowed,
JSON: jsonerror.NotFound("Bad method"), JSON: jsonerror.NotFound("Bad method"),
} }
} }
// WriteErrorMessage writes an error response with the given header and message
func writeErrorMessage(
w http.ResponseWriter, req *http.Request,
message string, header int,
) *util.JSONResponse {
w.WriteHeader(header)
_, err := w.Write([]byte(message))
if err != nil {
res := httputil.LogThenError(req, err)
return &res
}
return nil
}

View file

@ -83,17 +83,6 @@ func (d sessionsDict) GetCompletedStages(sessionID string) []authtypes.LoginType
return make([]authtypes.LoginType, 0) return make([]authtypes.LoginType, 0)
} }
// AddCompletedStage records that a session has completed an auth stage.
func (d *sessionsDict) AddCompletedStage(sessionID string, stage authtypes.LoginType) {
// Return if the stage is already present
for _, completedStage := range d.GetCompletedStages(sessionID) {
if completedStage == stage {
return
}
}
d.sessions[sessionID] = append(d.GetCompletedStages(sessionID), stage)
}
func newSessionsDict() *sessionsDict { func newSessionsDict() *sessionsDict {
return &sessionsDict{ return &sessionsDict{
sessions: make(map[string][]authtypes.LoginType), sessions: make(map[string][]authtypes.LoginType),

View file

@ -246,11 +246,11 @@ func Setup(
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) ).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
r0mux.Handle("/auth/{authType}/fallback/web", r0mux.Handle("/auth/{authType}/fallback/web",
common.MakeHTMLAPI("authfallback", func(w http.ResponseWriter, req *http.Request) *util.JSONResponse { common.MakeHTMLAPI("auth_fallback", func(w http.ResponseWriter, req *http.Request) *util.JSONResponse {
vars := mux.Vars(req) vars := mux.Vars(req)
return AuthFallback(w, req, vars["authType"], cfg) return AuthFallback(w, req, vars["authType"], cfg)
}), }),
).Methods("GET", "POST", "OPTIONS") ).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
r0mux.Handle("/pushrules/", r0mux.Handle("/pushrules/",
common.MakeExternalAPI("push_rules", func(req *http.Request) util.JSONResponse { common.MakeExternalAPI("push_rules", func(req *http.Request) util.JSONResponse {

View file

@ -45,7 +45,7 @@ func MakeExternalAPI(metricsName string, f func(*http.Request) util.JSONResponse
} }
// MakeHTMLAPI adds Span metrics to the HTML Handler function // MakeHTMLAPI adds Span metrics to the HTML Handler function
// This is used to serve HTML template // This is used to serve HTML alongside JSON error messages
func MakeHTMLAPI(metricsName string, f func(http.ResponseWriter, *http.Request) *util.JSONResponse) http.Handler { func MakeHTMLAPI(metricsName string, f func(http.ResponseWriter, *http.Request) *util.JSONResponse) http.Handler {
withSpan := func(w http.ResponseWriter, req *http.Request) { withSpan := func(w http.ResponseWriter, req *http.Request) {
span := opentracing.StartSpan(metricsName) span := opentracing.StartSpan(metricsName)