diff --git a/kit/transport/http/middleware.go b/kit/transport/http/middleware.go index 1643736a505..04372b87ee5 100644 --- a/kit/transport/http/middleware.go +++ b/kit/transport/http/middleware.go @@ -5,13 +5,12 @@ import ( "fmt" "net/http" "path" + "regexp" "strings" "time" - "github.com/influxdata/influxdb/v2/kit/platform" - "github.com/influxdata/influxdb/v2/kit/platform/errors" - "github.com/go-chi/chi" + "github.com/influxdata/influxdb/v2" "github.com/influxdata/influxdb/v2/kit/tracing" ua "github.com/mileusna/useragent" "github.com/prometheus/client_golang/prometheus" @@ -111,17 +110,32 @@ func UserAgent(r *http.Request) string { return ua.Parse(header).Name } +// Constants used for normalizing paths +const ( + fileSlug = ":file_name" + shardSlug = ":shard_id" +) + func normalizePath(p string) string { + // Normalize any paths used during backup or restore processes + p = normalizeBackupAndRestore(p) + + // Go through each part of the path and normalize IDs and UI assets var parts []string for head, tail := shiftPath(p); ; head, tail = shiftPath(tail) { piece := head - if len(piece) == platform.IDLength { - if _, err := platform.IDFromString(head); err == nil { + + // Normalize any ID's in the path as the ":id" slug + if len(piece) == influxdb.IDLength { + if _, err := influxdb.IDFromString(head); err == nil { piece = ":id" } } parts = append(parts, piece) + if tail == "/" { + // Normalize UI asset file names. The UI asset file is the last part of the path. + parts[len(parts)-1] = normalizeAssetFile(parts[len(parts)-1]) break } } @@ -137,18 +151,58 @@ func shiftPath(p string) (head, tail string) { return p[1:i], p[i:] } +// Normalize the file name for a UI asset +// For example: 838442d56d.svg will return as :file_id.svg +// Files names that do not have one of the listed extensions will be returned unchanged +func normalizeAssetFile(f string) string { + exts := []string{ + ".js", + ".svg", + ".woff2", + ".wasm", + ".map", + ".LICENSE", + } + + for _, ext := range exts { + if strings.HasSuffix(f, ext) { + return fileSlug + ext + } + } + + return f +} + +// Normalize paths used during the backup and restore process. +// Paths not matching any of the patterns will be returned unchanged. +func normalizeBackupAndRestore(pth string) string { + patterns := map[string]string{ + `restore/shards/\d+`: path.Join("restore/shards", shardSlug), + `backup/shards/\d+`: path.Join("backup/shards", shardSlug), + } + + for p, s := range patterns { + re := regexp.MustCompile(p) + if re.MatchString(pth) { + return re.ReplaceAllString(pth, s) + } + } + + return pth +} + type OrgContext string const CtxOrgKey OrgContext = "orgID" // ValidResource make sure a resource exists when a sub system needs to be mounted to an api -func ValidResource(api *API, lookupOrgByResourceID func(context.Context, platform.ID) (platform.ID, error)) Middleware { +func ValidResource(api *API, lookupOrgByResourceID func(context.Context, influxdb.ID) (influxdb.ID, error)) Middleware { return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { statusW := NewStatusResponseWriter(w) - id, err := platform.IDFromString(chi.URLParam(r, "id")) + id, err := influxdb.IDFromString(chi.URLParam(r, "id")) if err != nil { - api.Err(w, r, platform.ErrCorruptID(err)) + api.Err(w, r, influxdb.ErrCorruptID(err)) return } @@ -157,8 +211,8 @@ func ValidResource(api *API, lookupOrgByResourceID func(context.Context, platfor orgID, err := lookupOrgByResourceID(ctx, *id) if err != nil { // if this function returns an error we will squash the error message and replace it with a not found error - api.Err(w, r, &errors.Error{ - Code: errors.ENotFound, + api.Err(w, r, &influxdb.Error{ + Code: influxdb.ENotFound, Msg: "404 page not found", }) return @@ -172,11 +226,11 @@ func ValidResource(api *API, lookupOrgByResourceID func(context.Context, platfor } // OrgIDFromContext .... -func OrgIDFromContext(ctx context.Context) *platform.ID { +func OrgIDFromContext(ctx context.Context) *influxdb.ID { v := ctx.Value(CtxOrgKey) if v == nil { return nil } - id := v.(platform.ID) + id := v.(influxdb.ID) return &id } diff --git a/kit/transport/http/middleware_test.go b/kit/transport/http/middleware_test.go index 5a500498692..74e344ad948 100644 --- a/kit/transport/http/middleware_test.go +++ b/kit/transport/http/middleware_test.go @@ -5,8 +5,7 @@ import ( "path" "testing" - "github.com/influxdata/influxdb/v2/kit/platform" - + "github.com/influxdata/influxdb/v2" "github.com/influxdata/influxdb/v2/pkg/testttp" "github.com/stretchr/testify/assert" ) @@ -19,7 +18,7 @@ func Test_normalizePath(t *testing.T) { }{ { name: "1", - path: path.Join("/api/v2/organizations", platform.ID(2).String()), + path: path.Join("/api/v2/organizations", influxdb.ID(2).String()), expected: "/api/v2/organizations/:id", }, { @@ -34,9 +33,39 @@ func Test_normalizePath(t *testing.T) { }, { name: "4", - path: path.Join("/api/v2/organizations", platform.ID(2).String(), "users", platform.ID(3).String()), + path: path.Join("/api/v2/organizations", influxdb.ID(2).String(), "users", influxdb.ID(3).String()), expected: "/api/v2/organizations/:id/users/:id", }, + { + name: "5", + path: "/838442d56d.svg", + expected: "/" + fileSlug + ".svg", + }, + { + name: "6", + path: "/838442d56d.svg/extra", + expected: "/838442d56d.svg/extra", + }, + { + name: "7", + path: "/api/v2/restore/shards/1001", + expected: path.Join("/api/v2/restore/shards/", shardSlug), + }, + { + name: "8", + path: "/api/v2/restore/shards/1001/extra", + expected: path.Join("/api/v2/restore/shards/", shardSlug, "extra"), + }, + { + name: "9", + path: "/api/v2/backup/shards/1005", + expected: path.Join("/api/v2/backup/shards/", shardSlug), + }, + { + name: "10", + path: "/api/v2/backup/shards/1005/extra", + expected: path.Join("/api/v2/backup/shards/", shardSlug, "extra"), + }, } for _, tt := range tests {