From 2b72d94f04dea88f1faecb6ba871407ba15802ab Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Sat, 20 Jan 2018 00:56:12 -0700 Subject: [PATCH] Add a circuit breaker for validating access tokens Adds #46 --- config.sample.yaml | 2 + .../matrix-media-repo/client/r0/download.go | 3 +- .../client/r0/preview_url.go | 3 +- .../matrix-media-repo/client/r0/upload.go | 3 +- .../matrix-media-repo/config/config.go | 1 + .../matrix-media-repo/matrix/matrix.go | 64 +++++++++++++++++++ .../matrix-media-repo/util/matrix.go | 28 -------- vendor/manifest | 18 ++++++ 8 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 src/github.com/turt2live/matrix-media-repo/matrix/matrix.go delete mode 100644 src/github.com/turt2live/matrix-media-repo/util/matrix.go diff --git a/config.sample.yaml b/config.sample.yaml index 86282651..19e28979 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -18,6 +18,8 @@ homeservers: - name: t2bot.io # This should match the Host header given to the media repo downloadRequiresAuth: false # Set to true to require auth on downloads and identicons csApi: "https://t2bot.io/" # The base URL to where the homeserver can actually be reached + backoffAt: 10 # The number of consecutive failures in calling this homeserver before the + # media repository will start backing off. This defaults to 10 if not given. # The file upload settings for the media repository uploads: diff --git a/src/github.com/turt2live/matrix-media-repo/client/r0/download.go b/src/github.com/turt2live/matrix-media-repo/client/r0/download.go index 1391151f..c9c3ea5a 100644 --- a/src/github.com/turt2live/matrix-media-repo/client/r0/download.go +++ b/src/github.com/turt2live/matrix-media-repo/client/r0/download.go @@ -7,6 +7,7 @@ import ( "github.com/gorilla/mux" "github.com/sirupsen/logrus" "github.com/turt2live/matrix-media-repo/client" + "github.com/turt2live/matrix-media-repo/matrix" "github.com/turt2live/matrix-media-repo/media_cache" "github.com/turt2live/matrix-media-repo/util" "github.com/turt2live/matrix-media-repo/util/errs" @@ -68,7 +69,7 @@ func ValidateUserCanDownload(r *http.Request, log *logrus.Entry) (bool) { } accessToken := util.GetAccessTokenFromRequest(r) - userId, err := util.GetUserIdFromToken(r.Context(), r.Host, accessToken) + userId, err := matrix.GetUserIdFromToken(r.Context(), r.Host, accessToken) if err != nil { log.Error("Error verifying token: " + err.Error()) } diff --git a/src/github.com/turt2live/matrix-media-repo/client/r0/preview_url.go b/src/github.com/turt2live/matrix-media-repo/client/r0/preview_url.go index b671fede..a327abd4 100644 --- a/src/github.com/turt2live/matrix-media-repo/client/r0/preview_url.go +++ b/src/github.com/turt2live/matrix-media-repo/client/r0/preview_url.go @@ -8,6 +8,7 @@ import ( "github.com/sirupsen/logrus" "github.com/turt2live/matrix-media-repo/client" "github.com/turt2live/matrix-media-repo/config" + "github.com/turt2live/matrix-media-repo/matrix" "github.com/turt2live/matrix-media-repo/services/url_service" "github.com/turt2live/matrix-media-repo/util" "github.com/turt2live/matrix-media-repo/util/errs" @@ -32,7 +33,7 @@ func PreviewUrl(w http.ResponseWriter, r *http.Request, log *logrus.Entry) inter } accessToken := util.GetAccessTokenFromRequest(r) - userId, err := util.GetUserIdFromToken(r.Context(), r.Host, accessToken) + userId, err := matrix.GetUserIdFromToken(r.Context(), r.Host, accessToken) if err != nil || userId == "" { if err != nil { log.Error("Error verifying token: " + err.Error()) diff --git a/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go b/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go index 5aff4d06..6f2fc58b 100644 --- a/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go +++ b/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" "github.com/turt2live/matrix-media-repo/client" + "github.com/turt2live/matrix-media-repo/matrix" "github.com/turt2live/matrix-media-repo/services/media_service" "github.com/turt2live/matrix-media-repo/util" ) @@ -17,7 +18,7 @@ type MediaUploadedResponse struct { func UploadMedia(w http.ResponseWriter, r *http.Request, log *logrus.Entry) interface{} { accessToken := util.GetAccessTokenFromRequest(r) - userId, err := util.GetUserIdFromToken(r.Context(), r.Host, accessToken) + userId, err := matrix.GetUserIdFromToken(r.Context(), r.Host, accessToken) if err != nil || userId == "" { if err != nil { log.Error("Error verifying token: " + err.Error()) diff --git a/src/github.com/turt2live/matrix-media-repo/config/config.go b/src/github.com/turt2live/matrix-media-repo/config/config.go index 25b92208..a64d4d28 100644 --- a/src/github.com/turt2live/matrix-media-repo/config/config.go +++ b/src/github.com/turt2live/matrix-media-repo/config/config.go @@ -18,6 +18,7 @@ type HomeserverConfig struct { Name string `yaml:"name"` DownloadRequiresAuth bool `yaml:"downloadRequiresAuth"` ClientServerApi string `yaml:"csApi"` + BackoffAt int `yaml:"backoffAt"` } type GeneralConfig struct { diff --git a/src/github.com/turt2live/matrix-media-repo/matrix/matrix.go b/src/github.com/turt2live/matrix-media-repo/matrix/matrix.go new file mode 100644 index 00000000..df6f3918 --- /dev/null +++ b/src/github.com/turt2live/matrix-media-repo/matrix/matrix.go @@ -0,0 +1,64 @@ +package matrix + +import ( + "context" + "time" + + "github.com/matrix-org/gomatrix" + "github.com/rubyist/circuitbreaker" + "github.com/turt2live/matrix-media-repo/util" +) + +type userIdResponse struct { + UserId string `json:"user_id"` +} + +var breakers = make(map[string]*circuit.Breaker) + +func GetUserIdFromToken(ctx context.Context, serverName string, accessToken string) (string, error) { + hs := util.GetHomeserverConfig(serverName) + + cb, hasCb := breakers[hs.Name] + if !hasCb { + backoffAt := int64(hs.BackoffAt) + if backoffAt <= 0 { + backoffAt = 10 // default to 10 for those who don't have this set + } + cb = circuit.NewConsecutiveBreaker(backoffAt) + breakers[hs.Name] = cb + } + + userId := "" + var replyError error + err := cb.CallContext(ctx, func() error { + mtxClient, err := gomatrix.NewClient(hs.ClientServerApi, "", accessToken) + if err != nil { + return err + } + + response := &userIdResponse{} + url := mtxClient.BuildURL("/account/whoami") + _, err = mtxClient.MakeRequest("GET", url, nil, response) + if err != nil { + // Unknown token errors should be filtered out explicitly to ensure we don't break on bad requests + if httpErr, ok := err.(gomatrix.HTTPError); ok { + if respErr, ok := httpErr.WrappedError.(gomatrix.RespError); ok { + if respErr.ErrCode == "M_UNKNOWN_TOKEN" { + replyError = err // we still want to send the error to the caller though + return nil + } + } + } + return err + } + + userId = response.UserId + return nil + }, 1*time.Minute) + + if replyError != nil { + err = replyError + } + + return userId, err +} diff --git a/src/github.com/turt2live/matrix-media-repo/util/matrix.go b/src/github.com/turt2live/matrix-media-repo/util/matrix.go deleted file mode 100644 index 3e20be37..00000000 --- a/src/github.com/turt2live/matrix-media-repo/util/matrix.go +++ /dev/null @@ -1,28 +0,0 @@ -package util - -import ( - "context" - - "github.com/matrix-org/gomatrix" -) - -type userIdResponse struct { - UserId string `json:"user_id"` -} - -func GetUserIdFromToken(ctx context.Context, serverName string, accessToken string) (string, error) { - hs := GetHomeserverConfig(serverName) - mtxClient, err := gomatrix.NewClient(hs.ClientServerApi, "", accessToken) - if err != nil { - return "", err - } - - response := &userIdResponse{} - url := mtxClient.BuildURL("/account/whoami") - _, err = mtxClient.MakeRequest("GET", url, nil, response) - if err != nil { - return "", err - } - - return response.UserId, nil -} diff --git a/vendor/manifest b/vendor/manifest index eb215136..f7ad6172 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -31,6 +31,12 @@ "revision": "349dd0209470eabd9514242c688c403c0926d266", "branch": "master" }, + { + "importpath": "github.com/cenk/backoff", + "repository": "https://github.com/cenk/backoff", + "revision": "2ea60e5f094469f9e65adb9cd103795b73ae743e", + "branch": "master" + }, { "importpath": "github.com/cupcake/sigil", "repository": "https://github.com/cupcake/sigil", @@ -56,6 +62,12 @@ "branch": "master", "path": "/opengraph" }, + { + "importpath": "github.com/facebookgo/clock", + "repository": "https://github.com/facebookgo/clock", + "revision": "600d898af40aa09a7a93ecb9265d87b0504b6f03", + "branch": "master" + }, { "importpath": "github.com/gorilla/mux", "repository": "https://github.com/gorilla/mux", @@ -134,6 +146,12 @@ "revision": "3bcf86f879c771238f8a67832a1af71308801a47", "branch": "master" }, + { + "importpath": "github.com/rubyist/circuitbreaker", + "repository": "https://github.com/rubyist/circuitbreaker", + "revision": "2074adba5ddc7d5f7559448a9c3066573521c5bf", + "branch": "master" + }, { "importpath": "github.com/rwcarlsen/goexif", "repository": "https://github.com/rwcarlsen/goexif", -- GitLab