fix: URL encode resource's id before calling API endpoints

This commit is contained in:
Andi Sardina Ramos 2024-01-25 10:56:05 +02:00 committed by GitHub
parent 03d2e35488
commit 49f04f2772
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 408 additions and 29 deletions

View file

@ -76,7 +76,7 @@ func New(staticConfig static.Configuration, runtimeConfig *runtime.Configuration
// createRouter creates API routes and router.
func (h Handler) createRouter() *mux.Router {
router := mux.NewRouter()
router := mux.NewRouter().UseEncodedPath()
if h.staticConfig.API.Debug {
DebugHandler{}.Append(router)

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
@ -49,7 +50,13 @@ func (h Handler) getEntryPoints(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getEntryPoint(rw http.ResponseWriter, request *http.Request) {
entryPointID := mux.Vars(request)["entryPointID"]
scapedEntryPointID := mux.Vars(request)["entryPointID"]
entryPointID, err := url.PathUnescape(scapedEntryPointID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode entryPointID %q: %s", scapedEntryPointID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -64,7 +71,7 @@ func (h Handler) getEntryPoint(rw http.ResponseWriter, request *http.Request) {
Name: entryPointID,
}
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strconv"
"testing"
@ -169,6 +170,21 @@ func TestHandler_EntryPoints(t *testing.T) {
jsonFile: "testdata/entrypoint-bar.json",
},
},
{
desc: "one entry point by id containing slash",
path: "/api/entrypoints/" + url.PathEscape("foo / bar"),
conf: static.Configuration{
Global: &static.Global{},
API: &static.API{},
EntryPoints: map[string]*static.EntryPoint{
"foo / bar": {Address: ":81"},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/entrypoint-foo-slash-bar.json",
},
},
{
desc: "one entry point by id, that does not exist",
path: "/api/entrypoints/foo",

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
@ -99,7 +100,13 @@ func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
scapedRouterID := mux.Vars(request)["routerID"]
routerID, err := url.PathUnescape(scapedRouterID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode routerID %q: %s", scapedRouterID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -111,7 +118,7 @@ func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
result := newRouterRepresentation(routerID, router)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -151,7 +158,13 @@ func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
scapedServiceID := mux.Vars(request)["serviceID"]
serviceID, err := url.PathUnescape(scapedServiceID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode serviceID %q: %s", scapedServiceID, err), http.StatusBadRequest)
return
}
rw.Header().Add("Content-Type", "application/json")
@ -163,7 +176,7 @@ func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
result := newServiceRepresentation(serviceID, service)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -203,7 +216,13 @@ func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
middlewareID := mux.Vars(request)["middlewareID"]
scapedMiddlewareID := mux.Vars(request)["middlewareID"]
middlewareID, err := url.PathUnescape(scapedMiddlewareID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode middlewareID %q: %s", scapedMiddlewareID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -215,7 +234,7 @@ func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
result := newMiddlewareRepresentation(middlewareID, middleware)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strconv"
"testing"
@ -223,6 +224,27 @@ func TestHandler_HTTP(t *testing.T) {
jsonFile: "testdata/router-bar.json",
},
},
{
desc: "one router by id containing slash",
path: "/api/http/routers/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
Routers: map[string]*runtime.RouterInfo{
"foo / bar@myprovider": {
Router: &dynamic.Router{
EntryPoints: []string{"web"},
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
Middlewares: []string{"auth", "addPrefixTest@anotherprovider"},
},
Status: "enabled",
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/router-foo-slash-bar.json",
},
},
{
desc: "one router by id, implicitly using default TLS options",
path: "/api/http/routers/baz@myprovider",
@ -583,6 +605,35 @@ func TestHandler_HTTP(t *testing.T) {
jsonFile: "testdata/service-bar.json",
},
},
{
desc: "one service by id containing slash",
path: "/api/http/services/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
Services: map[string]*runtime.ServiceInfo{
"foo / bar@myprovider": func() *runtime.ServiceInfo {
si := &runtime.ServiceInfo{
Service: &dynamic.Service{
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1",
},
},
},
},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
}
si.UpdateServerStatus("http://127.0.0.1", "UP")
return si
}(),
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/service-foo-slash-bar.json",
},
},
{
desc: "one service by id, that does not exist",
path: "/api/http/services/nono@myprovider",
@ -819,6 +870,26 @@ func TestHandler_HTTP(t *testing.T) {
jsonFile: "testdata/middleware-auth.json",
},
},
{
desc: "one middleware by id containing slash",
path: "/api/http/middlewares/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
Middlewares: map[string]*runtime.MiddlewareInfo{
"foo / bar@myprovider": {
Middleware: &dynamic.Middleware{
AddPrefix: &dynamic.AddPrefix{
Prefix: "/titi",
},
},
UsedBy: []string{"test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/middleware-foo-slash-bar.json",
},
},
{
desc: "one middleware by id, that does not exist",
path: "/api/http/middlewares/foo@myprovider",

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
@ -92,7 +93,13 @@ func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
scapedRouterID := mux.Vars(request)["routerID"]
routerID, err := url.PathUnescape(scapedRouterID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode routerID %q: %s", scapedRouterID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -104,7 +111,7 @@ func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
result := newTCPRouterRepresentation(routerID, router)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -144,7 +151,13 @@ func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
scapedServiceID := mux.Vars(request)["serviceID"]
serviceID, err := url.PathUnescape(scapedServiceID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode serviceID %q: %s", scapedServiceID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -156,7 +169,7 @@ func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
result := newTCPServiceRepresentation(serviceID, service)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -196,7 +209,13 @@ func (h Handler) getTCPMiddlewares(rw http.ResponseWriter, request *http.Request
}
func (h Handler) getTCPMiddleware(rw http.ResponseWriter, request *http.Request) {
middlewareID := mux.Vars(request)["middlewareID"]
scapedMiddlewareID := mux.Vars(request)["middlewareID"]
middlewareID, err := url.PathUnescape(scapedMiddlewareID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode middlewareID %q: %s", scapedMiddlewareID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -208,7 +227,7 @@ func (h Handler) getTCPMiddleware(rw http.ResponseWriter, request *http.Request)
result := newTCPMiddlewareRepresentation(middlewareID, middleware)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
@ -212,6 +213,25 @@ func TestHandler_TCP(t *testing.T) {
jsonFile: "testdata/tcprouter-bar.json",
},
},
{
desc: "one TCP router by id containing slash",
path: "/api/tcp/routers/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
TCPRouters: map[string]*runtime.TCPRouterInfo{
"foo / bar@myprovider": {
TCPRouter: &dynamic.TCPRouter{
EntryPoints: []string{"web"},
Service: "foo-service@myprovider",
Rule: "Host(`foo.bar`)",
},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/tcprouter-foo-slash-bar.json",
},
},
{
desc: "one TCP router by id, that does not exist",
path: "/api/tcp/routers/foo@myprovider",
@ -476,6 +496,30 @@ func TestHandler_TCP(t *testing.T) {
jsonFile: "testdata/tcpservice-bar.json",
},
},
{
desc: "one tcp service by id containing slash",
path: "/api/tcp/services/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
TCPServices: map[string]*runtime.TCPServiceInfo{
"foo / bar@myprovider": {
TCPService: &dynamic.TCPService{
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "127.0.0.1:2345",
},
},
},
},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/tcpservice-foo-slash-bar.json",
},
},
{
desc: "one tcp service by id, that does not exist",
path: "/api/tcp/services/nono@myprovider",
@ -697,6 +741,26 @@ func TestHandler_TCP(t *testing.T) {
jsonFile: "testdata/tcpmiddleware-ipwhitelist.json",
},
},
{
desc: "one middleware by id containing slash",
path: "/api/tcp/middlewares/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
TCPMiddlewares: map[string]*runtime.TCPMiddlewareInfo{
"foo / bar@myprovider": {
TCPMiddleware: &dynamic.TCPMiddleware{
IPWhiteList: &dynamic.TCPIPWhiteList{
SourceRange: []string{"127.0.0.1/32"},
},
},
UsedBy: []string{"bar@myprovider", "test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/tcpmiddleware-foo-slash-bar.json",
},
},
{
desc: "one middleware by id, that does not exist",
path: "/api/tcp/middlewares/foo@myprovider",

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
@ -76,7 +77,13 @@ func (h Handler) getUDPRouters(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getUDPRouter(rw http.ResponseWriter, request *http.Request) {
routerID := mux.Vars(request)["routerID"]
scapedRouterID := mux.Vars(request)["routerID"]
routerID, err := url.PathUnescape(scapedRouterID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode routerID %q: %s", scapedRouterID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -88,7 +95,7 @@ func (h Handler) getUDPRouter(rw http.ResponseWriter, request *http.Request) {
result := newUDPRouterRepresentation(routerID, router)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)
@ -128,7 +135,13 @@ func (h Handler) getUDPServices(rw http.ResponseWriter, request *http.Request) {
}
func (h Handler) getUDPService(rw http.ResponseWriter, request *http.Request) {
serviceID := mux.Vars(request)["serviceID"]
scapedServiceID := mux.Vars(request)["serviceID"]
serviceID, err := url.PathUnescape(scapedServiceID)
if err != nil {
writeError(rw, fmt.Sprintf("unable to decode serviceID %q: %s", scapedServiceID, err), http.StatusBadRequest)
return
}
rw.Header().Set("Content-Type", "application/json")
@ -140,7 +153,7 @@ func (h Handler) getUDPService(rw http.ResponseWriter, request *http.Request) {
result := newUDPServiceRepresentation(serviceID, service)
err := json.NewEncoder(rw).Encode(result)
err = json.NewEncoder(rw).Encode(result)
if err != nil {
log.FromContext(request.Context()).Error(err)
writeError(rw, err.Error(), http.StatusInternalServerError)

View file

@ -6,6 +6,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
@ -190,6 +191,24 @@ func TestHandler_UDP(t *testing.T) {
jsonFile: "testdata/udprouter-bar.json",
},
},
{
desc: "one UDP router by id containing slash",
path: "/api/udp/routers/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
UDPRouters: map[string]*runtime.UDPRouterInfo{
"foo / bar@myprovider": {
UDPRouter: &dynamic.UDPRouter{
EntryPoints: []string{"web"},
Service: "foo-service@myprovider",
},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/udprouter-foo-slash-bar.json",
},
},
{
desc: "one UDP router by id, that does not exist",
path: "/api/udp/routers/foo@myprovider",
@ -453,6 +472,30 @@ func TestHandler_UDP(t *testing.T) {
jsonFile: "testdata/udpservice-bar.json",
},
},
{
desc: "one udp service by id containing slash",
path: "/api/udp/services/" + url.PathEscape("foo / bar@myprovider"),
conf: runtime.Configuration{
UDPServices: map[string]*runtime.UDPServiceInfo{
"foo / bar@myprovider": {
UDPService: &dynamic.UDPService{
LoadBalancer: &dynamic.UDPServersLoadBalancer{
Servers: []dynamic.UDPServer{
{
Address: "127.0.0.1:2345",
},
},
},
},
UsedBy: []string{"foo@myprovider", "test@myprovider"},
},
},
},
expected: expected{
statusCode: http.StatusOK,
jsonFile: "testdata/udpservice-foo-slash-bar.json",
},
},
{
desc: "one udp service by id, that does not exist",
path: "/api/udp/services/nono@myprovider",

View file

@ -0,0 +1,5 @@
{
"address": ":81",
"http": {},
"name": "foo / bar"
}

View file

@ -0,0 +1,12 @@
{
"addPrefix": {
"prefix": "/titi"
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "addprefix",
"usedBy": [
"test@myprovider"
]
}

View file

@ -0,0 +1,17 @@
{
"entryPoints": [
"web"
],
"middlewares": [
"auth",
"addPrefixTest@anotherprovider"
],
"name": "foo / bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "foo-service@myprovider",
"status": "enabled",
"using": [
"web"
]
}

View file

@ -0,0 +1,21 @@
{
"loadBalancer": {
"passHostHeader": true,
"servers": [
{
"url": "http://127.0.0.1"
}
]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"serverStatus": {
"http://127.0.0.1": "UP"
},
"status": "enabled",
"type": "loadbalancer",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}

View file

@ -0,0 +1,13 @@
{
"ipWhiteList": {
"sourceRange": ["127.0.0.1/32"]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "ipwhitelist",
"usedBy": [
"bar@myprovider",
"test@myprovider"
]
}

View file

@ -0,0 +1,13 @@
{
"entryPoints": [
"web"
],
"name": "foo / bar@myprovider",
"provider": "myprovider",
"rule": "Host(`foo.bar`)",
"service": "foo-service@myprovider",
"status": "enabled",
"using": [
"web"
]
}

View file

@ -0,0 +1,17 @@
{
"loadBalancer": {
"servers": [
{
"address": "127.0.0.1:2345"
}
]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "loadbalancer",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}

View file

@ -0,0 +1,12 @@
{
"entryPoints": [
"web"
],
"name": "foo / bar@myprovider",
"provider": "myprovider",
"service": "foo-service@myprovider",
"status": "enabled",
"using": [
"web"
]
}

View file

@ -0,0 +1,17 @@
{
"loadBalancer": {
"servers": [
{
"address": "127.0.0.1:2345"
}
]
},
"name": "foo / bar@myprovider",
"provider": "myprovider",
"status": "enabled",
"type": "loadbalancer",
"usedBy": [
"foo@myprovider",
"test@myprovider"
]
}

View file

@ -138,7 +138,7 @@ const GetTablePropsMixin = {
return {
onRowClick: row =>
this.$router.push({
path: `/${type.replace('-', '/', 'gi')}/${row.name}`
path: `/${type.replace('-', '/', 'gi')}/${encodeURIComponent(row.name)}`
}),
columns: allColumns.filter(c =>
get(propsByType, `${type}.columns`, []).includes(c.name)

View file

@ -14,7 +14,7 @@ function getAllRouters (params) {
}
function getRouterByName (name) {
return APP.api.get(`${apiBase}/routers/${name}`)
return APP.api.get(`${apiBase}/routers/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> HttpService -> getRouterByName', body.data)
return body.data
@ -32,7 +32,7 @@ function getAllServices (params) {
}
function getServiceByName (name) {
return APP.api.get(`${apiBase}/services/${name}`)
return APP.api.get(`${apiBase}/services/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> HttpService -> getServiceByName', body.data)
return body.data
@ -50,7 +50,7 @@ function getAllMiddlewares (params) {
}
function getMiddlewareByName (name) {
return APP.api.get(`${apiBase}/middlewares/${name}`)
return APP.api.get(`${apiBase}/middlewares/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> HttpService -> getMiddlewareByName', body.data)
return body.data

View file

@ -14,7 +14,7 @@ function getAllRouters (params) {
}
function getRouterByName (name) {
return APP.api.get(`${apiBase}/routers/${name}`)
return APP.api.get(`${apiBase}/routers/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> TcpService -> getRouterByName', body.data)
return body.data
@ -32,7 +32,7 @@ function getAllServices (params) {
}
function getServiceByName (name) {
return APP.api.get(`${apiBase}/services/${name}`)
return APP.api.get(`${apiBase}/services/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> TcpService -> getServiceByName', body.data)
return body.data
@ -50,7 +50,7 @@ function getAllMiddlewares (params) {
}
function getMiddlewareByName (name) {
return APP.api.get(`${apiBase}/middlewares/${name}`)
return APP.api.get(`${apiBase}/middlewares/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> TcpService -> getMiddlewareByName', body.data)
return body.data

View file

@ -14,7 +14,7 @@ function getAllRouters (params) {
}
function getRouterByName (name) {
return APP.api.get(`${apiBase}/routers/${name}`)
return APP.api.get(`${apiBase}/routers/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> UdpService -> getRouterByName', body.data)
return body.data
@ -32,7 +32,7 @@ function getAllServices (params) {
}
function getServiceByName (name) {
return APP.api.get(`${apiBase}/services/${name}`)
return APP.api.get(`${apiBase}/services/${encodeURIComponent(name)}`)
.then(body => {
console.log('Success -> UdpService -> getServiceByName', body.data)
return body.data

View file

@ -279,7 +279,7 @@ export default {
return data.service
}
return `${data.service}@${data.provider}`
return `${encodeURIComponent(data.service)}@${data.provider}`
}
},
created () {