From d19518fca5f8ae9e4950b8ac9a21b2f8ea04b93c Mon Sep 17 00:00:00 2001 From: Till Faelligen Date: Tue, 15 Feb 2022 11:07:24 +0100 Subject: [PATCH] Add ConsentNotGiven error Verify consent on desired endpoints Store consent on POST requests --- clientapi/jsonerror/jsonerror.go | 19 +++ clientapi/routing/consent_tracking.go | 146 ++++++++++++----------- clientapi/routing/routing.go | 160 +++++++++++++------------- internal/httputil/httpapi.go | 77 ++++++++++--- setup/config/config_global.go | 17 ++- setup/flags.go | 3 +- setup/mscs/msc2836/msc2836.go | 2 +- setup/mscs/msc2946/msc2946.go | 2 +- syncapi/routing/routing.go | 10 +- 9 files changed, 255 insertions(+), 181 deletions(-) diff --git a/clientapi/jsonerror/jsonerror.go b/clientapi/jsonerror/jsonerror.go index caa216e62..89c2d59b1 100644 --- a/clientapi/jsonerror/jsonerror.go +++ b/clientapi/jsonerror/jsonerror.go @@ -29,6 +29,13 @@ type MatrixError struct { Err string `json:"error"` } +// ConsentError is an error returned to users, who didn't accept the +// TOS of this server yet. +type ConsentError struct { + MatrixError + ConsentURI string `json:"consent_uri"` +} + func (e MatrixError) Error() string { return fmt.Sprintf("%s: %s", e.ErrCode, e.Err) } @@ -193,3 +200,15 @@ func NotTrusted(serverName string) *MatrixError { Err: fmt.Sprintf("Untrusted server '%s'", serverName), } } + +// ConsentNotGiven is an error returned to users, who didn't accept the +// TOS of this server yet. +func ConsentNotGiven(consentURI string, msg string) *ConsentError { + return &ConsentError{ + MatrixError: MatrixError{ + ErrCode: "M_CONSENT_NOT_GIVEN", + Err: msg, + }, + ConsentURI: consentURI, + } +} diff --git a/clientapi/routing/consent_tracking.go b/clientapi/routing/consent_tracking.go index d3c1e3947..2ab739067 100644 --- a/clientapi/routing/consent_tracking.go +++ b/clientapi/routing/consent_tracking.go @@ -6,9 +6,11 @@ import ( "encoding/hex" "net/http" + "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/setup/config" userapi "github.com/matrix-org/dendrite/userapi/api" "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" "github.com/sirupsen/logrus" ) @@ -21,89 +23,85 @@ type constentTemplateData struct { PublicVersion bool } -func consent(userAPI userapi.UserInternalAPI, cfg *config.ClientAPI) http.HandlerFunc { +func consent(writer http.ResponseWriter, req *http.Request, userAPI userapi.UserInternalAPI, cfg *config.ClientAPI) *util.JSONResponse { consentCfg := cfg.Matrix.UserConsentOptions - return func(writer http.ResponseWriter, req *http.Request) { - if !consentCfg.Enabled() { - writer.WriteHeader(http.StatusBadRequest) - _, _ = writer.Write([]byte("consent tracking is disabled")) - return - } - // The data used to populate the /consent request - data := constentTemplateData{ - User: req.FormValue("u"), - Version: req.FormValue("v"), - UserHMAC: req.FormValue("h"), - } - switch req.Method { - case http.MethodGet: - // display the privacy policy without a form - data.PublicVersion = data.User == "" || data.UserHMAC == "" || data.Version == "" + internalError := jsonerror.InternalServerError() - // let's see if the user already consented to the current version - if !data.PublicVersion { - res := &userapi.QueryPolicyVersionResponse{} - localPart, _, err := gomatrixserverlib.SplitID('@', data.User) - if err != nil { - logrus.WithError(err).Error("unable to print consent template") - return - } - if err = userAPI.QueryPolicyVersion(req.Context(), &userapi.QueryPolicyVersionRequest{ - LocalPart: localPart, - }, res); err != nil { - logrus.WithError(err).Error("unable to print consent template") - return - } - data.HasConsented = res.PolicyVersion == consentCfg.Version - } + // The data used to populate the /consent request + data := constentTemplateData{ + User: req.FormValue("u"), + Version: req.FormValue("v"), + UserHMAC: req.FormValue("h"), + } + switch req.Method { + case http.MethodGet: + // display the privacy policy without a form + data.PublicVersion = data.User == "" || data.UserHMAC == "" || data.Version == "" - err := consentCfg.Templates.ExecuteTemplate(writer, consentCfg.Version+".gohtml", data) - if err != nil { - logrus.WithError(err).Error("unable to print consent template") - return - } - case http.MethodPost: + // let's see if the user already consented to the current version + if !data.PublicVersion { + res := &userapi.QueryPolicyVersionResponse{} localPart, _, err := gomatrixserverlib.SplitID('@', data.User) - if err != nil { - logrus.WithError(err).Error("unable to split username") - return - } - - ok, err := validHMAC(data.User, data.UserHMAC, consentCfg.FormSecret) - if err != nil || !ok { - writer.WriteHeader(http.StatusBadRequest) - _, err = writer.Write([]byte("invalid HMAC provided")) - if err != nil { - return - } - return - } - if err := userAPI.PerformUpdatePolicyVersion( - req.Context(), - &userapi.UpdatePolicyVersionRequest{ - PolicyVersion: data.Version, - LocalPart: localPart, - }, - &userapi.UpdatePolicyVersionResponse{}, - ); err != nil { - writer.WriteHeader(http.StatusInternalServerError) - _, err = writer.Write([]byte("unable to update database")) - if err != nil { - logrus.WithError(err).Error("unable to write to database") - } - return - } - // display the privacy policy without a form - data.PublicVersion = false - data.HasConsented = true - - err = consentCfg.Templates.ExecuteTemplate(writer, consentCfg.Version+".gohtml", data) if err != nil { logrus.WithError(err).Error("unable to print consent template") - return + return &internalError } + if err = userAPI.QueryPolicyVersion(req.Context(), &userapi.QueryPolicyVersionRequest{ + LocalPart: localPart, + }, res); err != nil { + logrus.WithError(err).Error("unable to print consent template") + return &internalError + } + data.HasConsented = res.PolicyVersion == consentCfg.Version } + + err := consentCfg.Templates.ExecuteTemplate(writer, consentCfg.Version+".gohtml", data) + if err != nil { + logrus.WithError(err).Error("unable to print consent template") + return nil + } + return nil + case http.MethodPost: + localPart, _, err := gomatrixserverlib.SplitID('@', data.User) + if err != nil { + logrus.WithError(err).Error("unable to split username") + return &internalError + } + + ok, err := validHMAC(data.User, data.UserHMAC, consentCfg.FormSecret) + if err != nil || !ok { + _, err = writer.Write([]byte("invalid HMAC provided")) + if err != nil { + return &internalError + } + return &internalError + } + if err := userAPI.PerformUpdatePolicyVersion( + req.Context(), + &userapi.UpdatePolicyVersionRequest{ + PolicyVersion: data.Version, + LocalPart: localPart, + }, + &userapi.UpdatePolicyVersionResponse{}, + ); err != nil { + _, err = writer.Write([]byte("unable to update database")) + if err != nil { + logrus.WithError(err).Error("unable to write to database") + } + return &internalError + } + // display the privacy policy without a form + data.PublicVersion = false + data.HasConsented = true + + err = consentCfg.Templates.ExecuteTemplate(writer, consentCfg.Version+".gohtml", data) + if err != nil { + logrus.WithError(err).Error("unable to print consent template") + return &internalError + } + return nil } + return &util.JSONResponse{Code: http.StatusOK} } func validHMAC(username, userHMAC, secret string) (bool, error) { diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 467c6e78e..ea05604a8 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -119,19 +119,23 @@ func Setup( // unspecced consent tracking if cfg.Matrix.UserConsentOptions.Enabled() { - consentAPIMux.HandleFunc("/consent", consent(userAPI, cfg)).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) + consentAPIMux.Handle("/consent", + httputil.MakeHTMLAPI("consent", func(writer http.ResponseWriter, request *http.Request) *util.JSONResponse { + return consent(writer, request, userAPI, cfg) + }), + ).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) } r0mux := publicAPIMux.PathPrefix("/r0").Subrouter() unstableMux := publicAPIMux.PathPrefix("/unstable").Subrouter() r0mux.Handle("/createRoom", - httputil.MakeAuthAPI("createRoom", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("createRoom", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return CreateRoom(req, device, cfg, accountDB, rsAPI, asAPI) }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/join/{roomIDOrAlias}", - httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -147,7 +151,7 @@ func Setup( if mscCfg.Enabled("msc2753") { r0mux.Handle("/peek/{roomIDOrAlias}", - httputil.MakeAuthAPI(gomatrixserverlib.Peek, userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI(gomatrixserverlib.Peek, userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -162,12 +166,12 @@ func Setup( ).Methods(http.MethodPost, http.MethodOptions) } r0mux.Handle("/joined_rooms", - httputil.MakeAuthAPI("joined_rooms", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("joined_rooms", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return GetJoinedRooms(req, device, rsAPI) }), ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/join", - httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -181,7 +185,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/leave", - httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -195,7 +199,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/unpeek", - httputil.MakeAuthAPI("unpeek", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("unpeek", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -206,7 +210,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/ban", - httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -215,7 +219,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/invite", - httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -227,7 +231,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/kick", - httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -236,7 +240,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/unban", - httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("membership", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -245,7 +249,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/send/{eventType}", - httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -254,7 +258,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/send/{eventType}/{txnID}", - httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -265,7 +269,7 @@ func Setup( }), ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/event/{eventID}", - httputil.MakeAuthAPI("rooms_get_event", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_get_event", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -274,7 +278,7 @@ func Setup( }), ).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/state", httputil.MakeAuthAPI("room_state", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + r0mux.Handle("/rooms/{roomID}/state", httputil.MakeAuthAPI("room_state", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -282,7 +286,7 @@ func Setup( return OnIncomingStateRequest(req.Context(), device, rsAPI, vars["roomID"]) })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/aliases", httputil.MakeAuthAPI("aliases", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + r0mux.Handle("/rooms/{roomID}/aliases", httputil.MakeAuthAPI("aliases", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -290,7 +294,7 @@ func Setup( return GetAliases(req, rsAPI, device, vars["roomID"]) })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/state/{type:[^/]+/?}", httputil.MakeAuthAPI("room_state", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + r0mux.Handle("/rooms/{roomID}/state/{type:[^/]+/?}", httputil.MakeAuthAPI("room_state", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -301,7 +305,7 @@ func Setup( return OnIncomingStateTypeRequest(req.Context(), device, rsAPI, vars["roomID"], eventType, "", eventFormat) })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", httputil.MakeAuthAPI("room_state", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + r0mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", httputil.MakeAuthAPI("room_state", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -311,7 +315,7 @@ func Setup( })).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/state/{eventType:[^/]+/?}", - httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -323,7 +327,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/state/{eventType}/{stateKey}", - httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("send_message", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -358,7 +362,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/directory/room/{roomAlias}", - httputil.MakeAuthAPI("directory_room", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("directory_room", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -368,7 +372,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/directory/room/{roomAlias}", - httputil.MakeAuthAPI("directory_room", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("directory_room", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -387,7 +391,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) // TODO: Add AS support r0mux.Handle("/directory/list/room/{roomID}", - httputil.MakeAuthAPI("directory_list", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("directory_list", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -402,19 +406,19 @@ func Setup( ).Methods(http.MethodGet, http.MethodPost, http.MethodOptions) r0mux.Handle("/logout", - httputil.MakeAuthAPI("logout", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("logout", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return Logout(req, userAPI, device) }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/logout/all", - httputil.MakeAuthAPI("logout", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("logout", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return LogoutAll(req, userAPI, device) }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/typing/{userID}", - httputil.MakeAuthAPI("rooms_typing", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_typing", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -426,7 +430,7 @@ func Setup( }), ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/redact/{eventID}", - httputil.MakeAuthAPI("rooms_redact", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_redact", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -435,7 +439,7 @@ func Setup( }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/redact/{eventID}/{txnId}", - httputil.MakeAuthAPI("rooms_redact", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_redact", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -445,7 +449,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/sendToDevice/{eventType}/{txnID}", - httputil.MakeAuthAPI("send_to_device", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("send_to_device", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -459,7 +463,7 @@ func Setup( // rather than r0. It's an exact duplicate of the above handler. // TODO: Remove this if/when sytest is fixed! unstableMux.Handle("/sendToDevice/{eventType}/{txnID}", - httputil.MakeAuthAPI("send_to_device", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("send_to_device", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -470,7 +474,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/account/whoami", - httputil.MakeAuthAPI("whoami", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("whoami", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -479,7 +483,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/account/password", - httputil.MakeAuthAPI("password", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("password", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -488,7 +492,7 @@ func Setup( ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/account/deactivate", - httputil.MakeAuthAPI("deactivate", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("deactivate", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -556,7 +560,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/profile/{userID}/avatar_url", - httputil.MakeAuthAPI("profile_avatar_url", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("profile_avatar_url", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -581,7 +585,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/profile/{userID}/displayname", - httputil.MakeAuthAPI("profile_displayname", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("profile_displayname", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -596,19 +600,19 @@ func Setup( // PUT requests, so we need to allow this method r0mux.Handle("/account/3pid", - httputil.MakeAuthAPI("account_3pid", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("account_3pid", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return GetAssociated3PIDs(req, accountDB, device) }), ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/account/3pid", - httputil.MakeAuthAPI("account_3pid", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("account_3pid", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return CheckAndSave3PIDAssociation(req, accountDB, device, cfg) }), ).Methods(http.MethodPost, http.MethodOptions) unstableMux.Handle("/account/3pid/delete", - httputil.MakeAuthAPI("account_3pid", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("account_3pid", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return Forget3PID(req, accountDB) }), ).Methods(http.MethodPost, http.MethodOptions) @@ -634,7 +638,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/voip/turnServer", - httputil.MakeAuthAPI("turn_server", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("turn_server", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -663,7 +667,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/user/{userID}/account_data/{type}", - httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -673,7 +677,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/user/{userID}/rooms/{roomID}/account_data/{type}", - httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -683,7 +687,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/user/{userID}/account_data/{type}", - httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -693,7 +697,7 @@ func Setup( ).Methods(http.MethodGet) r0mux.Handle("/user/{userID}/rooms/{roomID}/account_data/{type}", - httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("user_account_data", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -703,7 +707,7 @@ func Setup( ).Methods(http.MethodGet) r0mux.Handle("/admin/whois/{userID}", - httputil.MakeAuthAPI("admin_whois", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("admin_whois", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -713,7 +717,7 @@ func Setup( ).Methods(http.MethodGet) r0mux.Handle("/user/{userID}/openid/request_token", - httputil.MakeAuthAPI("openid_request_token", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("openid_request_token", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -726,7 +730,7 @@ func Setup( ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/user_directory/search", - httputil.MakeAuthAPI("userdirectory_search", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("userdirectory_search", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -751,7 +755,7 @@ func Setup( ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/members", - httputil.MakeAuthAPI("rooms_members", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_members", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -761,7 +765,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/joined_members", - httputil.MakeAuthAPI("rooms_members", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_members", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -771,7 +775,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/read_markers", - httputil.MakeAuthAPI("rooms_read_markers", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_read_markers", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -784,7 +788,7 @@ func Setup( ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomID}/forget", - httputil.MakeAuthAPI("rooms_forget", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("rooms_forget", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -797,13 +801,13 @@ func Setup( ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/devices", - httputil.MakeAuthAPI("get_devices", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("get_devices", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return GetDevicesByLocalpart(req, userAPI, device) }), ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/devices/{deviceID}", - httputil.MakeAuthAPI("get_device", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("get_device", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -813,7 +817,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/devices/{deviceID}", - httputil.MakeAuthAPI("device_data", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("device_data", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -823,7 +827,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/devices/{deviceID}", - httputil.MakeAuthAPI("delete_device", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("delete_device", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -833,7 +837,7 @@ func Setup( ).Methods(http.MethodDelete, http.MethodOptions) r0mux.Handle("/delete_devices", - httputil.MakeAuthAPI("delete_devices", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("delete_devices", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return DeleteDevices(req, userAPI, device) }), ).Methods(http.MethodPost, http.MethodOptions) @@ -858,7 +862,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/user/{userId}/rooms/{roomId}/tags", - httputil.MakeAuthAPI("get_tags", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("get_tags", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -868,7 +872,7 @@ func Setup( ).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/user/{userId}/rooms/{roomId}/tags/{tag}", - httputil.MakeAuthAPI("put_tag", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("put_tag", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -878,7 +882,7 @@ func Setup( ).Methods(http.MethodPut, http.MethodOptions) r0mux.Handle("/user/{userId}/rooms/{roomId}/tags/{tag}", - httputil.MakeAuthAPI("delete_tag", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("delete_tag", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -888,7 +892,7 @@ func Setup( ).Methods(http.MethodDelete, http.MethodOptions) r0mux.Handle("/capabilities", - httputil.MakeAuthAPI("capabilities", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("capabilities", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } @@ -898,7 +902,7 @@ func Setup( // Key Backup Versions (Metadata) - getBackupKeysVersion := httputil.MakeAuthAPI("get_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + getBackupKeysVersion := httputil.MakeAuthAPI("get_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -906,11 +910,11 @@ func Setup( return KeyBackupVersion(req, userAPI, device, vars["version"]) }) - getLatestBackupKeysVersion := httputil.MakeAuthAPI("get_latest_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + getLatestBackupKeysVersion := httputil.MakeAuthAPI("get_latest_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return KeyBackupVersion(req, userAPI, device, "") }) - putBackupKeysVersion := httputil.MakeAuthAPI("put_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + putBackupKeysVersion := httputil.MakeAuthAPI("put_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -918,7 +922,7 @@ func Setup( return ModifyKeyBackupVersionAuthData(req, userAPI, device, vars["version"]) }) - deleteBackupKeysVersion := httputil.MakeAuthAPI("delete_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + deleteBackupKeysVersion := httputil.MakeAuthAPI("delete_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -926,7 +930,7 @@ func Setup( return DeleteKeyBackupVersion(req, userAPI, device, vars["version"]) }) - postNewBackupKeysVersion := httputil.MakeAuthAPI("post_new_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + postNewBackupKeysVersion := httputil.MakeAuthAPI("post_new_backup_keys_version", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return CreateKeyBackupVersion(req, userAPI, device) }) @@ -945,7 +949,7 @@ func Setup( // Inserting E2E Backup Keys // Bulk room and session - putBackupKeys := httputil.MakeAuthAPI("put_backup_keys", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + putBackupKeys := httputil.MakeAuthAPI("put_backup_keys", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { version := req.URL.Query().Get("version") if version == "" { return util.JSONResponse{ @@ -962,7 +966,7 @@ func Setup( }) // Single room bulk session - putBackupKeysRoom := httputil.MakeAuthAPI("put_backup_keys_room", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + putBackupKeysRoom := httputil.MakeAuthAPI("put_backup_keys_room", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -994,7 +998,7 @@ func Setup( }) // Single room, single session - putBackupKeysRoomSession := httputil.MakeAuthAPI("put_backup_keys_room_session", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + putBackupKeysRoomSession := httputil.MakeAuthAPI("put_backup_keys_room_session", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -1036,11 +1040,11 @@ func Setup( // Querying E2E Backup Keys - getBackupKeys := httputil.MakeAuthAPI("get_backup_keys", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + getBackupKeys := httputil.MakeAuthAPI("get_backup_keys", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), "", "") }) - getBackupKeysRoom := httputil.MakeAuthAPI("get_backup_keys_room", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + getBackupKeysRoom := httputil.MakeAuthAPI("get_backup_keys_room", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -1048,7 +1052,7 @@ func Setup( return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), vars["roomID"], "") }) - getBackupKeysRoomSession := httputil.MakeAuthAPI("get_backup_keys_room_session", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + getBackupKeysRoomSession := httputil.MakeAuthAPI("get_backup_keys_room_session", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -1068,11 +1072,11 @@ func Setup( // Cross-signing device keys - postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, keyAPI, device, accountDB, cfg) }) - postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return UploadCrossSigningDeviceSignatures(req, keyAPI, device) }) @@ -1084,27 +1088,27 @@ func Setup( // Supplying a device ID is deprecated. r0mux.Handle("/keys/upload/{deviceID}", - httputil.MakeAuthAPI("keys_upload", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("keys_upload", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return UploadKeys(req, keyAPI, device) }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/keys/upload", - httputil.MakeAuthAPI("keys_upload", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("keys_upload", userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { return UploadKeys(req, keyAPI, device) }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/keys/query", - httputil.MakeAuthAPI("keys_query", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("keys_query", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return QueryKeys(req, keyAPI, device) }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/keys/claim", - httputil.MakeAuthAPI("keys_claim", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("keys_claim", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return ClaimKeys(req, keyAPI) }), ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/rooms/{roomId}/receipt/{receiptType}/{eventId}", - httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, cfg.Matrix.UserConsentOptions, true, func(req *http.Request, device *userapi.Device) util.JSONResponse { if r := rateLimits.Limit(req); r != nil { return *r } diff --git a/internal/httputil/httpapi.go b/internal/httputil/httpapi.go index 63ba9dada..89b43b3a6 100644 --- a/internal/httputil/httpapi.go +++ b/internal/httputil/httpapi.go @@ -15,7 +15,11 @@ package httputil import ( + "bytes" "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" "fmt" "io" "net/http" @@ -54,6 +58,7 @@ func MakeAuthAPI( metricsName string, userAPI userapi.UserInternalAPI, userConsentCfg config.UserConsentOptions, + requireConsent bool, f func(*http.Request, *userapi.Device) util.JSONResponse, ) http.Handler { h := func(req *http.Request) util.JSONResponse { @@ -82,26 +87,12 @@ func MakeAuthAPI( } }() - // check if the user accepted any policy - if userConsentCfg.Enabled() { - localPart, _, err := gomatrixserverlib.SplitID('@', device.UserID) - if err != nil { - return jsonerror.InternalServerError() - } - // check which version of the policy the user accepted - res := &userapi.QueryPolicyVersionResponse{} - err = userAPI.QueryPolicyVersion(req.Context(), &userapi.QueryPolicyVersionRequest{ - LocalPart: localPart, - }, res) - if err != nil { - return jsonerror.InternalServerError() - } - - // user hasn't accepted any policy, block access. - if userConsentCfg.Version != res.PolicyVersion { + if userConsentCfg.Enabled() && requireConsent { + consentError := checkConsent(req.Context(), device.UserID, userAPI, userConsentCfg) + if consentError != nil { return util.JSONResponse{ Code: http.StatusForbidden, - JSON: jsonerror.Forbidden(userConsentCfg.BlockEventsError), + JSON: consentError, } } } @@ -117,6 +108,56 @@ func MakeAuthAPI( return MakeExternalAPI(metricsName, h) } +func checkConsent(ctx context.Context, userID string, userAPI userapi.UserInternalAPI, userConsentCfg config.UserConsentOptions) error { + localPart, _, err := gomatrixserverlib.SplitID('@', userID) + if err != nil { + return nil + } + // check which version of the policy the user accepted + res := &userapi.QueryPolicyVersionResponse{} + err = userAPI.QueryPolicyVersion(ctx, &userapi.QueryPolicyVersionRequest{ + LocalPart: localPart, + }, res) + if err != nil { + return nil + } + + // user hasn't accepted any policy, block access. + if userConsentCfg.Version != res.PolicyVersion { + uri, err := getConsentURL(userID, userConsentCfg) + if err != nil { + return jsonerror.Unknown("unable to get consent URL") + } + msg := &bytes.Buffer{} + c := struct { + ConsentURL string + }{ + ConsentURL: uri, + } + if err = userConsentCfg.BlockEventsTemplate.ExecuteTemplate(msg, "blockEventsError", c); err != nil { + logrus.Infof("error consent message: %+v", err) + return jsonerror.Unknown("unable to get consent URL") + } + return jsonerror.ConsentNotGiven(uri, msg.String()) + } + return nil +} + +// getConsentURL constructs the URL shown to users to accept the TOS +func getConsentURL(username string, config config.UserConsentOptions) (string, error) { + mac := hmac.New(sha256.New, []byte(config.FormSecret)) + _, err := mac.Write([]byte(username)) + if err != nil { + return "", err + } + hmac := hex.EncodeToString(mac.Sum(nil)) + + return fmt.Sprintf( + "%s/_matrix/consent?u=%s&h=%s&v=%s", + config.BaseURL, username, hmac, config.Version, + ), nil +} + // MakeExternalAPI turns a util.JSONRequestHandler function into an http.Handler. // This is used for APIs that are called from the internet. func MakeExternalAPI(metricsName string, f func(*http.Request) util.JSONResponse) http.Handler { diff --git a/setup/config/config_global.go b/setup/config/config_global.go index 4ef843044..d5ad9333e 100644 --- a/setup/config/config_global.go +++ b/setup/config/config_global.go @@ -5,6 +5,7 @@ import ( "html/template" "math/rand" "path/filepath" + textTemplate "text/template" "time" "github.com/matrix-org/gomatrixserverlib" @@ -15,6 +16,9 @@ type Global struct { // The name of the server. This is usually the domain name, e.g 'matrix.org', 'localhost'. ServerName gomatrixserverlib.ServerName `yaml:"server_name"` + // The base URL this homeserver will server clients on, e.g. https://matrix.org + BaseURL string `yaml:"base_url"` + // Path to the private key which will be used to sign requests and events. PrivateKeyPath Path `yaml:"private_key"` @@ -68,6 +72,7 @@ type Global struct { func (c *Global) Defaults(generate bool) { if generate { c.ServerName = "localhost" + c.BaseURL = "http://localhost" c.PrivateKeyPath = "matrix_key.pem" _, c.PrivateKey, _ = ed25519.GenerateKey(rand.New(rand.NewSource(0))) c.KeyID = "ed25519:auto" @@ -78,7 +83,7 @@ func (c *Global) Defaults(generate bool) { c.Metrics.Defaults(generate) c.DNSCache.Defaults() c.Sentry.Defaults() - c.UserConsentOptions.Defaults() + c.UserConsentOptions.Defaults(c.BaseURL) } func (c *Global) Verify(configErrs *ConfigErrors, isMonolith bool) { @@ -229,15 +234,19 @@ type UserConsentOptions struct { // The error message to display if the user hasn't given their consent yet BlockEventsError string `yaml:"block_events_error"` // All loaded templates - Templates *template.Template `yaml:"-"` + Templates *template.Template `yaml:"-"` + BlockEventsTemplate *textTemplate.Template `yaml:"-"` + // + BaseURL string `yaml:"-"` } -func (c *UserConsentOptions) Defaults() { +func (c *UserConsentOptions) Defaults(baseURL string) { c.RequireAtRegistration = false c.SendServerNoticeToGuest = false c.PolicyName = "Privacy Policy" c.Version = "1.0" c.TemplateDir = "./templates/privacy" + c.BaseURL = baseURL } func (c *UserConsentOptions) Verify(configErrors *ConfigErrors, isMonolith bool) { @@ -256,6 +265,8 @@ func (c *UserConsentOptions) Verify(configErrors *ConfigErrors, isMonolith bool) return } + c.BlockEventsTemplate = textTemplate.Must(textTemplate.New("blockEventsError").Parse(c.BlockEventsError)) + // Read all defined *.gohtml templates t, err := template.ParseGlob(filepath.Join(p, "*.gohtml")) if err != nil || t == nil { diff --git a/setup/flags.go b/setup/flags.go index 281cf3392..2f0634619 100644 --- a/setup/flags.go +++ b/setup/flags.go @@ -43,7 +43,8 @@ func ParseFlags(monolith bool) *config.Dendrite { } cfg, err := config.Load(*configPath, monolith) - + // TODO: just for testing + cfg.Global.UserConsentOptions.BaseURL = cfg.Global.BaseURL if err != nil { logrus.Fatalf("Invalid config file: %s", err) } diff --git a/setup/mscs/msc2836/msc2836.go b/setup/mscs/msc2836/msc2836.go index 8679e1995..6ad79be96 100644 --- a/setup/mscs/msc2836/msc2836.go +++ b/setup/mscs/msc2836/msc2836.go @@ -126,7 +126,7 @@ func Enable( }) base.PublicClientAPIMux.Handle("/unstable/event_relationships", - httputil.MakeAuthAPI("eventRelationships", userAPI, base.Cfg.Global.UserConsentOptions, eventRelationshipHandler(db, rsAPI, fsAPI)), + httputil.MakeAuthAPI("eventRelationships", userAPI, base.Cfg.Global.UserConsentOptions, false, eventRelationshipHandler(db, rsAPI, fsAPI)), ).Methods(http.MethodPost, http.MethodOptions) base.PublicFederationAPIMux.Handle("/unstable/event_relationships", httputil.MakeExternalAPI( diff --git a/setup/mscs/msc2946/msc2946.go b/setup/mscs/msc2946/msc2946.go index 5f20042d2..7be453554 100644 --- a/setup/mscs/msc2946/msc2946.go +++ b/setup/mscs/msc2946/msc2946.go @@ -71,7 +71,7 @@ func Enable( }) base.PublicClientAPIMux.Handle("/unstable/org.matrix.msc2946/rooms/{roomID}/spaces", - httputil.MakeAuthAPI("spaces", userAPI, base.Cfg.Global.UserConsentOptions, spacesHandler(db, rsAPI, fsAPI, base.Cfg.Global.ServerName)), + httputil.MakeAuthAPI("spaces", userAPI, base.Cfg.Global.UserConsentOptions, false, spacesHandler(db, rsAPI, fsAPI, base.Cfg.Global.ServerName)), ).Methods(http.MethodPost, http.MethodOptions) base.PublicFederationAPIMux.Handle("/unstable/org.matrix.msc2946/spaces/{roomID}", httputil.MakeExternalAPI( diff --git a/syncapi/routing/routing.go b/syncapi/routing/routing.go index a1c38300c..b3c1da88b 100644 --- a/syncapi/routing/routing.go +++ b/syncapi/routing/routing.go @@ -42,11 +42,11 @@ func Setup( r0mux := csMux.PathPrefix("/r0").Subrouter() // TODO: Add AS support for all handlers below. - r0mux.Handle("/sync", httputil.MakeAuthAPI("sync", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + r0mux.Handle("/sync", httputil.MakeAuthAPI("sync", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return srp.OnIncomingSyncRequest(req, device) })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/messages", httputil.MakeAuthAPI("room_messages", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + r0mux.Handle("/rooms/{roomID}/messages", httputil.MakeAuthAPI("room_messages", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -55,7 +55,7 @@ func Setup( })).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/user/{userId}/filter", - httputil.MakeAuthAPI("put_filter", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("put_filter", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -65,7 +65,7 @@ func Setup( ).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/user/{userId}/filter/{filterId}", - httputil.MakeAuthAPI("get_filter", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + httputil.MakeAuthAPI("get_filter", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) @@ -74,7 +74,7 @@ func Setup( }), ).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/keys/changes", httputil.MakeAuthAPI("keys_changes", userAPI, cfg.Matrix.UserConsentOptions, func(req *http.Request, device *userapi.Device) util.JSONResponse { + r0mux.Handle("/keys/changes", httputil.MakeAuthAPI("keys_changes", userAPI, cfg.Matrix.UserConsentOptions, false, func(req *http.Request, device *userapi.Device) util.JSONResponse { return srp.OnIncomingKeyChangeRequest(req, device) })).Methods(http.MethodGet, http.MethodOptions) }