From faacae7fe623ef303e1811932362fa72cd4d1353 Mon Sep 17 00:00:00 2001 From: Scarlett Perry Date: Mon, 17 Jul 2023 13:33:38 -0400 Subject: [PATCH 1/6] support path params --- api/proxy/v1/proxy.proto | 8 +- backend/api/proxy/v1/proxy.pb.go | 165 ++++++++++++---------- backend/api/proxy/v1/proxy.pb.validate.go | 4 + backend/module/proxy/proxy.go | 32 ++++- backend/module/proxy/proxy_test.go | 39 +++++ frontend/api/src/index.d.ts | 12 ++ frontend/api/src/index.js | 34 +++++ 7 files changed, 214 insertions(+), 80 deletions(-) diff --git a/api/proxy/v1/proxy.proto b/api/proxy/v1/proxy.proto index adceb5d83a..10b713c9e2 100644 --- a/api/proxy/v1/proxy.proto +++ b/api/proxy/v1/proxy.proto @@ -36,10 +36,12 @@ message RequestProxyRequest { string service = 1 [ (validate.rules).string.min_len = 1 ]; // The HTTP method that will be used for the request string http_method = 2 [ (validate.rules).string.min_len = 1 ]; - // The URI path to call + // The URI path to call. To specify a parameter use the pattern /path/{param} string path = 3 [ (validate.rules).string.min_len = 1 ]; // The request body google.protobuf.Value request = 4 [ (clutch.api.v1.log) = false ]; + // the paramater for the dynamic path + string path_parameter = 5; } message RequestProxyResponse { @@ -58,10 +60,12 @@ message RequestProxyGetRequest { string service = 1 [ (validate.rules).string.min_len = 1 ]; // The HTTP method that will be used for the request string http_method = 2 [ (validate.rules).string.min_len = 1 ]; - // The URI path to call + // The URI path to call. To specify a parameter use the pattern /path/{param} string path = 3 [ (validate.rules).string.min_len = 1 ]; // The request body google.protobuf.Value request = 4 [ (clutch.api.v1.log) = false ]; + // the paramater for the dynamic path + string path_parameter = 5; } message RequestProxyGetResponse { diff --git a/backend/api/proxy/v1/proxy.pb.go b/backend/api/proxy/v1/proxy.pb.go index 7e95277171..53ba1451f8 100644 --- a/backend/api/proxy/v1/proxy.pb.go +++ b/backend/api/proxy/v1/proxy.pb.go @@ -33,10 +33,12 @@ type RequestProxyRequest struct { Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` // The HTTP method that will be used for the request HttpMethod string `protobuf:"bytes,2,opt,name=http_method,json=httpMethod,proto3" json:"http_method,omitempty"` - // The URI path to call + // The URI path to call. To specify a parameter use the pattern /path/{param} Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` // The request body Request *structpb.Value `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` + // the paramater for the dynamic path + PathParameter string `protobuf:"bytes,5,opt,name=path_parameter,json=pathParameter,proto3" json:"path_parameter,omitempty"` } func (x *RequestProxyRequest) Reset() { @@ -99,6 +101,13 @@ func (x *RequestProxyRequest) GetRequest() *structpb.Value { return nil } +func (x *RequestProxyRequest) GetPathParameter() string { + if x != nil { + return x.PathParameter + } + return "" +} + type RequestProxyResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -176,10 +185,12 @@ type RequestProxyGetRequest struct { Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` // The HTTP method that will be used for the request HttpMethod string `protobuf:"bytes,2,opt,name=http_method,json=httpMethod,proto3" json:"http_method,omitempty"` - // The URI path to call + // The URI path to call. To specify a parameter use the pattern /path/{param} Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` // The request body Request *structpb.Value `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` + // the paramater for the dynamic path + PathParameter string `protobuf:"bytes,5,opt,name=path_parameter,json=pathParameter,proto3" json:"path_parameter,omitempty"` } func (x *RequestProxyGetRequest) Reset() { @@ -242,6 +253,13 @@ func (x *RequestProxyGetRequest) GetRequest() *structpb.Value { return nil } +func (x *RequestProxyGetRequest) GetPathParameter() string { + if x != nil { + return x.PathParameter + } + return "" +} + type RequestProxyGetResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -322,7 +340,7 @@ var file_proxy_v1_proxy_proto_rawDesc = []byte{ 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb7, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xde, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, @@ -334,76 +352,81 @@ var file_proxy_v1_proxy_proto_rawDesc = []byte{ 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x97, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, - 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, - 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x6c, - 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x1a, 0x56, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xba, 0x01, 0x0a, 0x16, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, - 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, - 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, - 0x36, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x07, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9d, 0x02, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x4f, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, - 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x22, 0x97, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, + 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x56, 0x0a, 0x0c, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xe1, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x07, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x28, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x68, + 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, - 0x56, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x9a, 0x02, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x41, 0x50, 0x49, 0x12, 0x7f, 0x0a, 0x0c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x12, 0x24, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x63, 0x6c, 0x75, - 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x22, 0xaa, 0xe1, 0x1c, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, - 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x8c, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x12, 0x27, 0x2e, 0x63, 0x6c, 0x75, 0x74, - 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, + 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, + 0x0a, 0x0e, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x22, 0x9d, 0x02, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x4f, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, + 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, + 0xe1, 0x1c, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x56, 0x0a, + 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x9a, 0x02, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x41, + 0x50, 0x49, 0x12, 0x7f, 0x0a, 0x0c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x12, 0x24, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0xaa, 0xe1, - 0x1c, 0x02, 0x08, 0x02, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, - 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2f, 0x67, 0x65, 0x74, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x6c, 0x79, 0x66, 0x74, 0x2f, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2f, 0x62, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, + 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x22, 0xaa, 0xe1, 0x1c, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, + 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x8c, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x12, 0x27, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, + 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x28, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0xaa, 0xe1, 0x1c, 0x02, + 0x08, 0x02, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2f, 0x67, + 0x65, 0x74, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x6c, 0x79, 0x66, 0x74, 0x2f, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2f, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, + 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/backend/api/proxy/v1/proxy.pb.validate.go b/backend/api/proxy/v1/proxy.pb.validate.go index 92a450ec1b..2189f72ce4 100644 --- a/backend/api/proxy/v1/proxy.pb.validate.go +++ b/backend/api/proxy/v1/proxy.pb.validate.go @@ -119,6 +119,8 @@ func (m *RequestProxyRequest) validate(all bool) error { } } + // no validation rules for PathParameter + if len(errors) > 0 { return RequestProxyRequestMultiError(errors) } @@ -462,6 +464,8 @@ func (m *RequestProxyGetRequest) validate(all bool) error { } } + // no validation rules for PathParameter + if len(errors) > 0 { return RequestProxyGetRequestMultiError(errors) } diff --git a/backend/module/proxy/proxy.go b/backend/module/proxy/proxy.go index cf5efb19df..70fb53b9e2 100644 --- a/backend/module/proxy/proxy.go +++ b/backend/module/proxy/proxy.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "net/url" + "regexp" "strings" "github.com/golang/protobuf/ptypes/any" @@ -28,6 +29,10 @@ const ( HostHeaderKey = "Host" ) +var ( + PathParameterRegexp = regexp.MustCompile(`{(\w+)}`) +) + func New(cfg *any.Any, log *zap.Logger, scope tally.Scope) (module.Module, error) { config := &proxyv1cfg.Config{} err := cfg.UnmarshalTo(config) @@ -92,11 +97,10 @@ func (m *mod) RequestProxy(ctx context.Context, req *proxyv1.RequestProxyRequest headers.Add(k, v) } - // Parse the URL by joining both the HOST and PATH specifed by the config - parsedUrl, err := url.Parse(fmt.Sprintf("%s%s", service.Host, req.Path)) + parsedUrl, err := getParsedUrl(req, service) if err != nil { m.logger.Error("Unable to parse the configured URL", zap.Error(err)) - return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) + return nil, err } // Constructing the request object @@ -194,10 +198,11 @@ func responseToGetResponse(resp *proxyv1.RequestProxyResponse) *proxyv1.RequestP func getRequestToRequest(req *proxyv1.RequestProxyGetRequest) *proxyv1.RequestProxyRequest { return &proxyv1.RequestProxyRequest{ - Service: req.Service, - HttpMethod: req.HttpMethod, - Path: req.Path, - Request: req.Request, + Service: req.Service, + HttpMethod: req.HttpMethod, + Path: req.Path, + PathParameter: req.PathParameter, + Request: req.Request, } } @@ -235,3 +240,16 @@ func addExcludedHeaders(request *http.Request) { request.Host = hostHeader } } + +func getParsedUrl(req *proxyv1.RequestProxyRequest, service *proxyv1cfg.Service) (*url.URL, error) { + path := req.Path + if req.PathParameter != "" { + path = PathParameterRegexp.ReplaceAllString(req.Path, req.PathParameter) + } + // Parse the URL by joining both the HOST and PATH specifed by the config + parsedUrl, err := url.Parse(fmt.Sprintf("%s%s", service.Host, path)) + if err != nil { + return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) + } + return parsedUrl, nil +} diff --git a/backend/module/proxy/proxy_test.go b/backend/module/proxy/proxy_test.go index 7356a8e61d..1a3e71b7f8 100644 --- a/backend/module/proxy/proxy_test.go +++ b/backend/module/proxy/proxy_test.go @@ -306,3 +306,42 @@ func TestAddExcludedHeaders(t *testing.T) { assert.Equal(t, test.expected, req.Host) } } + +func TestGetParsedUrl(t *testing.T) { + tests := []struct { + request *proxyv1.RequestProxyRequest + expectedPath string + expectedQuery string + }{ + { + request: &proxyv1.RequestProxyRequest{ + Path: "/cars/{vehicle}/count", + PathParameter: "foo", + }, + expectedPath: "/cars/foo/count", + }, + { + request: &proxyv1.RequestProxyRequest{ + Path: "/cars/{vehicle}?color=blue", + PathParameter: "foo", + }, + expectedPath: "/cars/foo", + expectedQuery: "color=blue", + }, + { + request: &proxyv1.RequestProxyRequest{ + Path: "/cars?color=blue", + }, + expectedPath: "/cars", + expectedQuery: "color=blue", + }, + } + + for _, test := range tests { + res, err := getParsedUrl(test.request, &proxyv1cfg.Service{Host: "http://test.test"}) + assert.NoError(t, err) + assert.Equal(t, "test.test", res.Host) + assert.Equal(t, test.expectedPath, res.Path) + assert.Equal(t, test.expectedQuery, res.RawQuery) + } +} diff --git a/frontend/api/src/index.d.ts b/frontend/api/src/index.d.ts index 4950409714..eed91dd9ab 100644 --- a/frontend/api/src/index.d.ts +++ b/frontend/api/src/index.d.ts @@ -22640,6 +22640,9 @@ export namespace clutch { /** RequestProxyRequest request */ request?: (google.protobuf.IValue|null); + + /** RequestProxyRequest pathParameter */ + pathParameter?: (string|null); } /** Represents a RequestProxyRequest. */ @@ -22663,6 +22666,9 @@ export namespace clutch { /** RequestProxyRequest request. */ public request?: (google.protobuf.IValue|null); + /** RequestProxyRequest pathParameter. */ + public pathParameter: string; + /** * Verifies a RequestProxyRequest message. * @param message Plain object to verify @@ -22766,6 +22772,9 @@ export namespace clutch { /** RequestProxyGetRequest request */ request?: (google.protobuf.IValue|null); + + /** RequestProxyGetRequest pathParameter */ + pathParameter?: (string|null); } /** Represents a RequestProxyGetRequest. */ @@ -22789,6 +22798,9 @@ export namespace clutch { /** RequestProxyGetRequest request. */ public request?: (google.protobuf.IValue|null); + /** RequestProxyGetRequest pathParameter. */ + public pathParameter: string; + /** * Verifies a RequestProxyGetRequest message. * @param message Plain object to verify diff --git a/frontend/api/src/index.js b/frontend/api/src/index.js index 0c3a496c8d..623aa4e525 100644 --- a/frontend/api/src/index.js +++ b/frontend/api/src/index.js @@ -54529,6 +54529,7 @@ export const clutch = $root.clutch = (() => { * @property {string|null} [httpMethod] RequestProxyRequest httpMethod * @property {string|null} [path] RequestProxyRequest path * @property {google.protobuf.IValue|null} [request] RequestProxyRequest request + * @property {string|null} [pathParameter] RequestProxyRequest pathParameter */ /** @@ -54578,6 +54579,14 @@ export const clutch = $root.clutch = (() => { */ RequestProxyRequest.prototype.request = null; + /** + * RequestProxyRequest pathParameter. + * @member {string} pathParameter + * @memberof clutch.proxy.v1.RequestProxyRequest + * @instance + */ + RequestProxyRequest.prototype.pathParameter = ""; + /** * Verifies a RequestProxyRequest message. * @function verify @@ -54603,6 +54612,9 @@ export const clutch = $root.clutch = (() => { if (error) return "request." + error; } + if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) + if (!$util.isString(message.pathParameter)) + return "pathParameter: string expected"; return null; }; @@ -54629,6 +54641,8 @@ export const clutch = $root.clutch = (() => { throw TypeError(".clutch.proxy.v1.RequestProxyRequest.request: object expected"); message.request = $root.google.protobuf.Value.fromObject(object.request); } + if (object.pathParameter != null) + message.pathParameter = String(object.pathParameter); return message; }; @@ -54650,6 +54664,7 @@ export const clutch = $root.clutch = (() => { object.httpMethod = ""; object.path = ""; object.request = null; + object.pathParameter = ""; } if (message.service != null && message.hasOwnProperty("service")) object.service = message.service; @@ -54659,6 +54674,8 @@ export const clutch = $root.clutch = (() => { object.path = message.path; if (message.request != null && message.hasOwnProperty("request")) object.request = $root.google.protobuf.Value.toObject(message.request, options); + if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) + object.pathParameter = message.pathParameter; return object; }; @@ -54847,6 +54864,7 @@ export const clutch = $root.clutch = (() => { * @property {string|null} [httpMethod] RequestProxyGetRequest httpMethod * @property {string|null} [path] RequestProxyGetRequest path * @property {google.protobuf.IValue|null} [request] RequestProxyGetRequest request + * @property {string|null} [pathParameter] RequestProxyGetRequest pathParameter */ /** @@ -54896,6 +54914,14 @@ export const clutch = $root.clutch = (() => { */ RequestProxyGetRequest.prototype.request = null; + /** + * RequestProxyGetRequest pathParameter. + * @member {string} pathParameter + * @memberof clutch.proxy.v1.RequestProxyGetRequest + * @instance + */ + RequestProxyGetRequest.prototype.pathParameter = ""; + /** * Verifies a RequestProxyGetRequest message. * @function verify @@ -54921,6 +54947,9 @@ export const clutch = $root.clutch = (() => { if (error) return "request." + error; } + if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) + if (!$util.isString(message.pathParameter)) + return "pathParameter: string expected"; return null; }; @@ -54947,6 +54976,8 @@ export const clutch = $root.clutch = (() => { throw TypeError(".clutch.proxy.v1.RequestProxyGetRequest.request: object expected"); message.request = $root.google.protobuf.Value.fromObject(object.request); } + if (object.pathParameter != null) + message.pathParameter = String(object.pathParameter); return message; }; @@ -54968,6 +54999,7 @@ export const clutch = $root.clutch = (() => { object.httpMethod = ""; object.path = ""; object.request = null; + object.pathParameter = ""; } if (message.service != null && message.hasOwnProperty("service")) object.service = message.service; @@ -54977,6 +55009,8 @@ export const clutch = $root.clutch = (() => { object.path = message.path; if (message.request != null && message.hasOwnProperty("request")) object.request = $root.google.protobuf.Value.toObject(message.request, options); + if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) + object.pathParameter = message.pathParameter; return object; }; From 85f875486a7133b739d54d970b6c63d8c101b6af Mon Sep 17 00:00:00 2001 From: Scarlett Perry Date: Mon, 17 Jul 2023 16:13:52 -0400 Subject: [PATCH 2/6] support path regexes --- api/config/module/proxy/v1/proxy.proto | 8 +- api/proxy/v1/proxy.proto | 8 +- .../api/config/module/proxy/v1/proxy.pb.go | 72 ++++++-- .../module/proxy/v1/proxy.pb.validate.go | 63 ++++++- backend/api/proxy/v1/proxy.pb.go | 165 ++++++++---------- backend/api/proxy/v1/proxy.pb.validate.go | 4 - backend/module/proxy/proxy.go | 63 ++++--- backend/module/proxy/proxy_test.go | 39 ----- frontend/api/src/index.d.ts | 23 ++- frontend/api/src/index.js | 89 +++++----- 10 files changed, 285 insertions(+), 249 deletions(-) diff --git a/api/config/module/proxy/v1/proxy.proto b/api/config/module/proxy/v1/proxy.proto index 42004f0385..6693872925 100644 --- a/api/config/module/proxy/v1/proxy.proto +++ b/api/config/module/proxy/v1/proxy.proto @@ -22,6 +22,12 @@ message Service { } message AllowRequest { - string path = 1 [ (validate.rules).string.min_len = 1 ]; + oneof path_type { + option (validate.required) = true; + // Must match the request path exactly + string path = 1 [ (validate.rules).string.min_len = 1 ]; + // Request path must match the regex pattern + string path_regex = 3 [ (validate.rules).string.min_len = 1 ]; + } string method = 2 [ (validate.rules).string.min_len = 1 ]; } diff --git a/api/proxy/v1/proxy.proto b/api/proxy/v1/proxy.proto index 10b713c9e2..adceb5d83a 100644 --- a/api/proxy/v1/proxy.proto +++ b/api/proxy/v1/proxy.proto @@ -36,12 +36,10 @@ message RequestProxyRequest { string service = 1 [ (validate.rules).string.min_len = 1 ]; // The HTTP method that will be used for the request string http_method = 2 [ (validate.rules).string.min_len = 1 ]; - // The URI path to call. To specify a parameter use the pattern /path/{param} + // The URI path to call string path = 3 [ (validate.rules).string.min_len = 1 ]; // The request body google.protobuf.Value request = 4 [ (clutch.api.v1.log) = false ]; - // the paramater for the dynamic path - string path_parameter = 5; } message RequestProxyResponse { @@ -60,12 +58,10 @@ message RequestProxyGetRequest { string service = 1 [ (validate.rules).string.min_len = 1 ]; // The HTTP method that will be used for the request string http_method = 2 [ (validate.rules).string.min_len = 1 ]; - // The URI path to call. To specify a parameter use the pattern /path/{param} + // The URI path to call string path = 3 [ (validate.rules).string.min_len = 1 ]; // The request body google.protobuf.Value request = 4 [ (clutch.api.v1.log) = false ]; - // the paramater for the dynamic path - string path_parameter = 5; } message RequestProxyGetResponse { diff --git a/backend/api/config/module/proxy/v1/proxy.pb.go b/backend/api/config/module/proxy/v1/proxy.pb.go index b7c1d443b6..eebf0ed9b1 100644 --- a/backend/api/config/module/proxy/v1/proxy.pb.go +++ b/backend/api/config/module/proxy/v1/proxy.pb.go @@ -148,8 +148,12 @@ type AllowRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` + // Types that are assignable to PathType: + // + // *AllowRequest_Path + // *AllowRequest_PathRegex + PathType isAllowRequest_PathType `protobuf_oneof:"path_type"` + Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"` } func (x *AllowRequest) Reset() { @@ -184,13 +188,27 @@ func (*AllowRequest) Descriptor() ([]byte, []int) { return file_config_module_proxy_v1_proxy_proto_rawDescGZIP(), []int{2} } +func (m *AllowRequest) GetPathType() isAllowRequest_PathType { + if m != nil { + return m.PathType + } + return nil +} + func (x *AllowRequest) GetPath() string { - if x != nil { + if x, ok := x.GetPathType().(*AllowRequest_Path); ok { return x.Path } return "" } +func (x *AllowRequest) GetPathRegex() string { + if x, ok := x.GetPathType().(*AllowRequest_PathRegex); ok { + return x.PathRegex + } + return "" +} + func (x *AllowRequest) GetMethod() string { if x != nil { return x.Method @@ -198,6 +216,24 @@ func (x *AllowRequest) GetMethod() string { return "" } +type isAllowRequest_PathType interface { + isAllowRequest_PathType() +} + +type AllowRequest_Path struct { + // Must match the request path exactly + Path string `protobuf:"bytes,1,opt,name=path,proto3,oneof"` +} + +type AllowRequest_PathRegex struct { + // Request path must match the regex pattern + PathRegex string `protobuf:"bytes,3,opt,name=path_regex,json=pathRegex,proto3,oneof"` +} + +func (*AllowRequest_Path) isAllowRequest_PathType() {} + +func (*AllowRequest_PathRegex) isAllowRequest_PathType() {} + var File_config_module_proxy_v1_proxy_proto protoreflect.FileDescriptor var file_config_module_proxy_v1_proxy_proto_rawDesc = []byte{ @@ -230,17 +266,21 @@ var file_config_module_proxy_v1_proxy_proto_rawDesc = []byte{ 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4c, 0x0a, - 0x0c, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x06, 0x6d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x43, 0x5a, 0x41, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x79, 0x66, 0x74, 0x2f, 0x63, - 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2f, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8a, 0x01, + 0x0a, 0x0c, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, + 0x0a, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x09, 0x70, 0x61, + 0x74, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1f, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x10, 0x0a, 0x09, 0x70, 0x61, 0x74, 0x68, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x79, 0x66, 0x74, 0x2f, 0x63, 0x6c, + 0x75, 0x74, 0x63, 0x68, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x76, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -316,6 +356,10 @@ func file_config_module_proxy_v1_proxy_proto_init() { } } } + file_config_module_proxy_v1_proxy_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*AllowRequest_Path)(nil), + (*AllowRequest_PathRegex)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/backend/api/config/module/proxy/v1/proxy.pb.validate.go b/backend/api/config/module/proxy/v1/proxy.pb.validate.go index 2a1d6af1d7..39b51b6459 100644 --- a/backend/api/config/module/proxy/v1/proxy.pb.validate.go +++ b/backend/api/config/module/proxy/v1/proxy.pb.validate.go @@ -356,9 +356,9 @@ func (m *AllowRequest) validate(all bool) error { var errors []error - if utf8.RuneCountInString(m.GetPath()) < 1 { + if utf8.RuneCountInString(m.GetMethod()) < 1 { err := AllowRequestValidationError{ - field: "Path", + field: "Method", reason: "value length must be at least 1 runes", } if !all { @@ -367,10 +367,63 @@ func (m *AllowRequest) validate(all bool) error { errors = append(errors, err) } - if utf8.RuneCountInString(m.GetMethod()) < 1 { + oneofPathTypePresent := false + switch v := m.PathType.(type) { + case *AllowRequest_Path: + if v == nil { + err := AllowRequestValidationError{ + field: "PathType", + reason: "oneof value cannot be a typed-nil", + } + if !all { + return err + } + errors = append(errors, err) + } + oneofPathTypePresent = true + + if utf8.RuneCountInString(m.GetPath()) < 1 { + err := AllowRequestValidationError{ + field: "Path", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + case *AllowRequest_PathRegex: + if v == nil { + err := AllowRequestValidationError{ + field: "PathType", + reason: "oneof value cannot be a typed-nil", + } + if !all { + return err + } + errors = append(errors, err) + } + oneofPathTypePresent = true + + if utf8.RuneCountInString(m.GetPathRegex()) < 1 { + err := AllowRequestValidationError{ + field: "PathRegex", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + default: + _ = v // ensures v is used + } + if !oneofPathTypePresent { err := AllowRequestValidationError{ - field: "Method", - reason: "value length must be at least 1 runes", + field: "PathType", + reason: "value is required", } if !all { return err diff --git a/backend/api/proxy/v1/proxy.pb.go b/backend/api/proxy/v1/proxy.pb.go index 53ba1451f8..7e95277171 100644 --- a/backend/api/proxy/v1/proxy.pb.go +++ b/backend/api/proxy/v1/proxy.pb.go @@ -33,12 +33,10 @@ type RequestProxyRequest struct { Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` // The HTTP method that will be used for the request HttpMethod string `protobuf:"bytes,2,opt,name=http_method,json=httpMethod,proto3" json:"http_method,omitempty"` - // The URI path to call. To specify a parameter use the pattern /path/{param} + // The URI path to call Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` // The request body Request *structpb.Value `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` - // the paramater for the dynamic path - PathParameter string `protobuf:"bytes,5,opt,name=path_parameter,json=pathParameter,proto3" json:"path_parameter,omitempty"` } func (x *RequestProxyRequest) Reset() { @@ -101,13 +99,6 @@ func (x *RequestProxyRequest) GetRequest() *structpb.Value { return nil } -func (x *RequestProxyRequest) GetPathParameter() string { - if x != nil { - return x.PathParameter - } - return "" -} - type RequestProxyResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -185,12 +176,10 @@ type RequestProxyGetRequest struct { Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` // The HTTP method that will be used for the request HttpMethod string `protobuf:"bytes,2,opt,name=http_method,json=httpMethod,proto3" json:"http_method,omitempty"` - // The URI path to call. To specify a parameter use the pattern /path/{param} + // The URI path to call Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` // The request body Request *structpb.Value `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` - // the paramater for the dynamic path - PathParameter string `protobuf:"bytes,5,opt,name=path_parameter,json=pathParameter,proto3" json:"path_parameter,omitempty"` } func (x *RequestProxyGetRequest) Reset() { @@ -253,13 +242,6 @@ func (x *RequestProxyGetRequest) GetRequest() *structpb.Value { return nil } -func (x *RequestProxyGetRequest) GetPathParameter() string { - if x != nil { - return x.PathParameter - } - return "" -} - type RequestProxyGetResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -340,7 +322,7 @@ var file_proxy_v1_proxy_proto_rawDesc = []byte{ 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xde, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb7, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, @@ -352,81 +334,76 @@ var file_proxy_v1_proxy_proto_rawDesc = []byte{ 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x22, 0x97, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x56, 0x0a, 0x0c, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xe1, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x07, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, - 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x28, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x68, - 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x04, 0x70, 0x61, 0x74, - 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x22, 0x97, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, + 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, + 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x63, 0x6c, + 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x1a, 0x56, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xba, 0x01, 0x0a, 0x16, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x28, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, + 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x12, 0x1b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, + 0x36, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x07, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9d, 0x02, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x4f, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, - 0x0a, 0x0e, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x22, 0x9d, 0x02, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x4f, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x04, 0xa8, - 0xe1, 0x1c, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x56, 0x0a, - 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x9a, 0x02, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x41, - 0x50, 0x49, 0x12, 0x7f, 0x0a, 0x0c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x12, 0x24, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, + 0x04, 0xa8, 0xe1, 0x1c, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, + 0x56, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0x9a, 0x02, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x41, 0x50, 0x49, 0x12, 0x7f, 0x0a, 0x0c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x12, 0x24, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x63, 0x6c, 0x75, + 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x22, 0xaa, 0xe1, 0x1c, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, + 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x8c, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x12, 0x27, 0x2e, 0x63, 0x6c, 0x75, 0x74, + 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, - 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x22, 0xaa, 0xe1, 0x1c, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, - 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x8c, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x12, 0x27, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, - 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x28, 0x2e, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0xaa, 0xe1, 0x1c, 0x02, - 0x08, 0x02, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, - 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2f, 0x67, - 0x65, 0x74, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6c, 0x79, 0x66, 0x74, 0x2f, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2f, 0x62, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, - 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x79, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0xaa, 0xe1, + 0x1c, 0x02, 0x08, 0x02, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, + 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2f, 0x67, 0x65, 0x74, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x6c, 0x79, 0x66, 0x74, 0x2f, 0x63, 0x6c, 0x75, 0x74, 0x63, 0x68, 0x2f, 0x62, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/backend/api/proxy/v1/proxy.pb.validate.go b/backend/api/proxy/v1/proxy.pb.validate.go index 2189f72ce4..92a450ec1b 100644 --- a/backend/api/proxy/v1/proxy.pb.validate.go +++ b/backend/api/proxy/v1/proxy.pb.validate.go @@ -119,8 +119,6 @@ func (m *RequestProxyRequest) validate(all bool) error { } } - // no validation rules for PathParameter - if len(errors) > 0 { return RequestProxyRequestMultiError(errors) } @@ -464,8 +462,6 @@ func (m *RequestProxyGetRequest) validate(all bool) error { } } - // no validation rules for PathParameter - if len(errors) > 0 { return RequestProxyGetRequestMultiError(errors) } diff --git a/backend/module/proxy/proxy.go b/backend/module/proxy/proxy.go index 70fb53b9e2..553d5c9294 100644 --- a/backend/module/proxy/proxy.go +++ b/backend/module/proxy/proxy.go @@ -29,10 +29,6 @@ const ( HostHeaderKey = "Host" ) -var ( - PathParameterRegexp = regexp.MustCompile(`{(\w+)}`) -) - func New(cfg *any.Any, log *zap.Logger, scope tally.Scope) (module.Module, error) { config := &proxyv1cfg.Config{} err := cfg.UnmarshalTo(config) @@ -43,7 +39,16 @@ func New(cfg *any.Any, log *zap.Logger, scope tally.Scope) (module.Module, error // Validate that each services constructs a parsable URL for _, service := range config.Services { for _, ar := range service.AllowedRequests { - _, err := url.Parse(fmt.Sprintf("%s%s", service.Host, ar.Path)) + var path string + switch t := ar.PathType.(type) { + case *proxyv1cfg.AllowRequest_Path: + path = t.Path + case *proxyv1cfg.AllowRequest_PathRegex: + path = t.PathRegex + default: + return nil, fmt.Errorf("path type not supported: %s", t) + } + _, err := url.Parse(fmt.Sprintf("%s%s", service.Host, path)) if err != nil { return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) } @@ -97,10 +102,11 @@ func (m *mod) RequestProxy(ctx context.Context, req *proxyv1.RequestProxyRequest headers.Add(k, v) } - parsedUrl, err := getParsedUrl(req, service) + // Parse the URL by joining both the HOST and PATH specifed by the config + parsedUrl, err := url.Parse(fmt.Sprintf("%s%s", service.Host, req.Path)) if err != nil { m.logger.Error("Unable to parse the configured URL", zap.Error(err)) - return nil, err + return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) } // Constructing the request object @@ -198,11 +204,10 @@ func responseToGetResponse(resp *proxyv1.RequestProxyResponse) *proxyv1.RequestP func getRequestToRequest(req *proxyv1.RequestProxyGetRequest) *proxyv1.RequestProxyRequest { return &proxyv1.RequestProxyRequest{ - Service: req.Service, - HttpMethod: req.HttpMethod, - Path: req.Path, - PathParameter: req.PathParameter, - Request: req.Request, + Service: req.Service, + HttpMethod: req.HttpMethod, + Path: req.Path, + Request: req.Request, } } @@ -210,13 +215,20 @@ func isAllowedRequest(services []*proxyv1cfg.Service, service, path, method stri for _, s := range services { if s.Name == service { for _, ar := range s.AllowedRequests { - parsedUrl, err := url.Parse(fmt.Sprintf("%s%s", s.Host, path)) - if err != nil { - return false, err - } - - if parsedUrl.Path == ar.Path && strings.EqualFold(method, ar.Method) { - return true, nil + switch t := ar.PathType.(type) { + case *proxyv1cfg.AllowRequest_Path: + parsedUrl, err := url.Parse(fmt.Sprintf("%s%s", s.Host, path)) + if err != nil { + return false, err + } + return parsedUrl.Path == t.Path && strings.EqualFold(method, ar.Method), nil + case *proxyv1cfg.AllowRequest_PathRegex: + // MatchString reports whether the string contains any match of the regular expression so we add the ‘^’ and ‘$’ to ensure the regex matches the full string + r := regexp.MustCompile(fmt.Sprintf("^%s$", t.PathRegex)) + matched := r.MatchString(path) + return matched, nil + default: + return false, fmt.Errorf("path type not supported: %s", t) } } // return early here as were done checking allowed request for this service @@ -240,16 +252,3 @@ func addExcludedHeaders(request *http.Request) { request.Host = hostHeader } } - -func getParsedUrl(req *proxyv1.RequestProxyRequest, service *proxyv1cfg.Service) (*url.URL, error) { - path := req.Path - if req.PathParameter != "" { - path = PathParameterRegexp.ReplaceAllString(req.Path, req.PathParameter) - } - // Parse the URL by joining both the HOST and PATH specifed by the config - parsedUrl, err := url.Parse(fmt.Sprintf("%s%s", service.Host, path)) - if err != nil { - return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) - } - return parsedUrl, nil -} diff --git a/backend/module/proxy/proxy_test.go b/backend/module/proxy/proxy_test.go index 1a3e71b7f8..7356a8e61d 100644 --- a/backend/module/proxy/proxy_test.go +++ b/backend/module/proxy/proxy_test.go @@ -306,42 +306,3 @@ func TestAddExcludedHeaders(t *testing.T) { assert.Equal(t, test.expected, req.Host) } } - -func TestGetParsedUrl(t *testing.T) { - tests := []struct { - request *proxyv1.RequestProxyRequest - expectedPath string - expectedQuery string - }{ - { - request: &proxyv1.RequestProxyRequest{ - Path: "/cars/{vehicle}/count", - PathParameter: "foo", - }, - expectedPath: "/cars/foo/count", - }, - { - request: &proxyv1.RequestProxyRequest{ - Path: "/cars/{vehicle}?color=blue", - PathParameter: "foo", - }, - expectedPath: "/cars/foo", - expectedQuery: "color=blue", - }, - { - request: &proxyv1.RequestProxyRequest{ - Path: "/cars?color=blue", - }, - expectedPath: "/cars", - expectedQuery: "color=blue", - }, - } - - for _, test := range tests { - res, err := getParsedUrl(test.request, &proxyv1cfg.Service{Host: "http://test.test"}) - assert.NoError(t, err) - assert.Equal(t, "test.test", res.Host) - assert.Equal(t, test.expectedPath, res.Path) - assert.Equal(t, test.expectedQuery, res.RawQuery) - } -} diff --git a/frontend/api/src/index.d.ts b/frontend/api/src/index.d.ts index eed91dd9ab..9c62bb5fbf 100644 --- a/frontend/api/src/index.d.ts +++ b/frontend/api/src/index.d.ts @@ -8562,6 +8562,9 @@ export namespace clutch { /** AllowRequest path */ path?: (string|null); + /** AllowRequest pathRegex */ + pathRegex?: (string|null); + /** AllowRequest method */ method?: (string|null); } @@ -8576,11 +8579,17 @@ export namespace clutch { constructor(properties?: clutch.config.module.proxy.v1.IAllowRequest); /** AllowRequest path. */ - public path: string; + public path?: (string|null); + + /** AllowRequest pathRegex. */ + public pathRegex?: (string|null); /** AllowRequest method. */ public method: string; + /** AllowRequest pathType. */ + public pathType?: ("path"|"pathRegex"); + /** * Verifies an AllowRequest message. * @param message Plain object to verify @@ -22640,9 +22649,6 @@ export namespace clutch { /** RequestProxyRequest request */ request?: (google.protobuf.IValue|null); - - /** RequestProxyRequest pathParameter */ - pathParameter?: (string|null); } /** Represents a RequestProxyRequest. */ @@ -22666,9 +22672,6 @@ export namespace clutch { /** RequestProxyRequest request. */ public request?: (google.protobuf.IValue|null); - /** RequestProxyRequest pathParameter. */ - public pathParameter: string; - /** * Verifies a RequestProxyRequest message. * @param message Plain object to verify @@ -22772,9 +22775,6 @@ export namespace clutch { /** RequestProxyGetRequest request */ request?: (google.protobuf.IValue|null); - - /** RequestProxyGetRequest pathParameter */ - pathParameter?: (string|null); } /** Represents a RequestProxyGetRequest. */ @@ -22798,9 +22798,6 @@ export namespace clutch { /** RequestProxyGetRequest request. */ public request?: (google.protobuf.IValue|null); - /** RequestProxyGetRequest pathParameter. */ - public pathParameter: string; - /** * Verifies a RequestProxyGetRequest message. * @param message Plain object to verify diff --git a/frontend/api/src/index.js b/frontend/api/src/index.js index 623aa4e525..54974d46c5 100644 --- a/frontend/api/src/index.js +++ b/frontend/api/src/index.js @@ -20536,6 +20536,7 @@ export const clutch = $root.clutch = (() => { * @memberof clutch.config.module.proxy.v1 * @interface IAllowRequest * @property {string|null} [path] AllowRequest path + * @property {string|null} [pathRegex] AllowRequest pathRegex * @property {string|null} [method] AllowRequest method */ @@ -20556,11 +20557,19 @@ export const clutch = $root.clutch = (() => { /** * AllowRequest path. - * @member {string} path + * @member {string|null|undefined} path * @memberof clutch.config.module.proxy.v1.AllowRequest * @instance */ - AllowRequest.prototype.path = ""; + AllowRequest.prototype.path = null; + + /** + * AllowRequest pathRegex. + * @member {string|null|undefined} pathRegex + * @memberof clutch.config.module.proxy.v1.AllowRequest + * @instance + */ + AllowRequest.prototype.pathRegex = null; /** * AllowRequest method. @@ -20570,6 +20579,20 @@ export const clutch = $root.clutch = (() => { */ AllowRequest.prototype.method = ""; + // OneOf field names bound to virtual getters and setters + let $oneOfFields; + + /** + * AllowRequest pathType. + * @member {"path"|"pathRegex"|undefined} pathType + * @memberof clutch.config.module.proxy.v1.AllowRequest + * @instance + */ + Object.defineProperty(AllowRequest.prototype, "pathType", { + get: $util.oneOfGetter($oneOfFields = ["path", "pathRegex"]), + set: $util.oneOfSetter($oneOfFields) + }); + /** * Verifies an AllowRequest message. * @function verify @@ -20581,9 +20604,19 @@ export const clutch = $root.clutch = (() => { AllowRequest.verify = function verify(message) { if (typeof message !== "object" || message === null) return "object expected"; - if (message.path != null && message.hasOwnProperty("path")) + let properties = {}; + if (message.path != null && message.hasOwnProperty("path")) { + properties.pathType = 1; if (!$util.isString(message.path)) return "path: string expected"; + } + if (message.pathRegex != null && message.hasOwnProperty("pathRegex")) { + if (properties.pathType === 1) + return "pathType: multiple values"; + properties.pathType = 1; + if (!$util.isString(message.pathRegex)) + return "pathRegex: string expected"; + } if (message.method != null && message.hasOwnProperty("method")) if (!$util.isString(message.method)) return "method: string expected"; @@ -20604,6 +20637,8 @@ export const clutch = $root.clutch = (() => { let message = new $root.clutch.config.module.proxy.v1.AllowRequest(); if (object.path != null) message.path = String(object.path); + if (object.pathRegex != null) + message.pathRegex = String(object.pathRegex); if (object.method != null) message.method = String(object.method); return message; @@ -20622,14 +20657,20 @@ export const clutch = $root.clutch = (() => { if (!options) options = {}; let object = {}; - if (options.defaults) { - object.path = ""; + if (options.defaults) object.method = ""; - } - if (message.path != null && message.hasOwnProperty("path")) + if (message.path != null && message.hasOwnProperty("path")) { object.path = message.path; + if (options.oneofs) + object.pathType = "path"; + } if (message.method != null && message.hasOwnProperty("method")) object.method = message.method; + if (message.pathRegex != null && message.hasOwnProperty("pathRegex")) { + object.pathRegex = message.pathRegex; + if (options.oneofs) + object.pathType = "pathRegex"; + } return object; }; @@ -54529,7 +54570,6 @@ export const clutch = $root.clutch = (() => { * @property {string|null} [httpMethod] RequestProxyRequest httpMethod * @property {string|null} [path] RequestProxyRequest path * @property {google.protobuf.IValue|null} [request] RequestProxyRequest request - * @property {string|null} [pathParameter] RequestProxyRequest pathParameter */ /** @@ -54579,14 +54619,6 @@ export const clutch = $root.clutch = (() => { */ RequestProxyRequest.prototype.request = null; - /** - * RequestProxyRequest pathParameter. - * @member {string} pathParameter - * @memberof clutch.proxy.v1.RequestProxyRequest - * @instance - */ - RequestProxyRequest.prototype.pathParameter = ""; - /** * Verifies a RequestProxyRequest message. * @function verify @@ -54612,9 +54644,6 @@ export const clutch = $root.clutch = (() => { if (error) return "request." + error; } - if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) - if (!$util.isString(message.pathParameter)) - return "pathParameter: string expected"; return null; }; @@ -54641,8 +54670,6 @@ export const clutch = $root.clutch = (() => { throw TypeError(".clutch.proxy.v1.RequestProxyRequest.request: object expected"); message.request = $root.google.protobuf.Value.fromObject(object.request); } - if (object.pathParameter != null) - message.pathParameter = String(object.pathParameter); return message; }; @@ -54664,7 +54691,6 @@ export const clutch = $root.clutch = (() => { object.httpMethod = ""; object.path = ""; object.request = null; - object.pathParameter = ""; } if (message.service != null && message.hasOwnProperty("service")) object.service = message.service; @@ -54674,8 +54700,6 @@ export const clutch = $root.clutch = (() => { object.path = message.path; if (message.request != null && message.hasOwnProperty("request")) object.request = $root.google.protobuf.Value.toObject(message.request, options); - if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) - object.pathParameter = message.pathParameter; return object; }; @@ -54864,7 +54888,6 @@ export const clutch = $root.clutch = (() => { * @property {string|null} [httpMethod] RequestProxyGetRequest httpMethod * @property {string|null} [path] RequestProxyGetRequest path * @property {google.protobuf.IValue|null} [request] RequestProxyGetRequest request - * @property {string|null} [pathParameter] RequestProxyGetRequest pathParameter */ /** @@ -54914,14 +54937,6 @@ export const clutch = $root.clutch = (() => { */ RequestProxyGetRequest.prototype.request = null; - /** - * RequestProxyGetRequest pathParameter. - * @member {string} pathParameter - * @memberof clutch.proxy.v1.RequestProxyGetRequest - * @instance - */ - RequestProxyGetRequest.prototype.pathParameter = ""; - /** * Verifies a RequestProxyGetRequest message. * @function verify @@ -54947,9 +54962,6 @@ export const clutch = $root.clutch = (() => { if (error) return "request." + error; } - if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) - if (!$util.isString(message.pathParameter)) - return "pathParameter: string expected"; return null; }; @@ -54976,8 +54988,6 @@ export const clutch = $root.clutch = (() => { throw TypeError(".clutch.proxy.v1.RequestProxyGetRequest.request: object expected"); message.request = $root.google.protobuf.Value.fromObject(object.request); } - if (object.pathParameter != null) - message.pathParameter = String(object.pathParameter); return message; }; @@ -54999,7 +55009,6 @@ export const clutch = $root.clutch = (() => { object.httpMethod = ""; object.path = ""; object.request = null; - object.pathParameter = ""; } if (message.service != null && message.hasOwnProperty("service")) object.service = message.service; @@ -55009,8 +55018,6 @@ export const clutch = $root.clutch = (() => { object.path = message.path; if (message.request != null && message.hasOwnProperty("request")) object.request = $root.google.protobuf.Value.toObject(message.request, options); - if (message.pathParameter != null && message.hasOwnProperty("pathParameter")) - object.pathParameter = message.pathParameter; return object; }; From 8e26f7b5e44db6d166ecd965dcbf13b6de8ba073 Mon Sep 17 00:00:00 2001 From: Scarlett Perry Date: Mon, 17 Jul 2023 17:27:24 -0400 Subject: [PATCH 3/6] add tests --- backend/module/proxy/proxy.go | 9 +++++--- backend/module/proxy/proxy_test.go | 33 ++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/backend/module/proxy/proxy.go b/backend/module/proxy/proxy.go index 553d5c9294..c186de1b4f 100644 --- a/backend/module/proxy/proxy.go +++ b/backend/module/proxy/proxy.go @@ -221,12 +221,15 @@ func isAllowedRequest(services []*proxyv1cfg.Service, service, path, method stri if err != nil { return false, err } - return parsedUrl.Path == t.Path && strings.EqualFold(method, ar.Method), nil + if parsedUrl.Path == t.Path && strings.EqualFold(method, ar.Method) { + return true, nil + } case *proxyv1cfg.AllowRequest_PathRegex: // MatchString reports whether the string contains any match of the regular expression so we add the ‘^’ and ‘$’ to ensure the regex matches the full string r := regexp.MustCompile(fmt.Sprintf("^%s$", t.PathRegex)) - matched := r.MatchString(path) - return matched, nil + if r.MatchString(path) { + return true, nil + } default: return false, fmt.Errorf("path type not supported: %s", t) } diff --git a/backend/module/proxy/proxy_test.go b/backend/module/proxy/proxy_test.go index 7356a8e61d..4d1af8e971 100644 --- a/backend/module/proxy/proxy_test.go +++ b/backend/module/proxy/proxy_test.go @@ -28,16 +28,17 @@ func generateServicesConfig(host string) []*proxyv1cfg.Service { Name: "cat", Host: host, AllowedRequests: []*proxyv1cfg.AllowRequest{ - {Path: "/meow", Method: "GET"}, - {Path: "/nom", Method: "POST"}, + {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/meow"}, Method: "GET"}, + {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/nom"}, Method: "POST"}, + {PathType: &proxyv1cfg.AllowRequest_PathRegex{PathRegex: `/cat/\w+/[0-9]`}, Method: "GET"}, }, }, { Name: "meow", Host: host, AllowedRequests: []*proxyv1cfg.AllowRequest{ - {Path: "/meow", Method: "GET"}, - {Path: "/nom", Method: "POST"}, + {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/meow"}, Method: "GET"}, + {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/nom"}, Method: "POST"}, }, }, } @@ -268,6 +269,30 @@ func TestIsAllowedRequest(t *testing.T) { expect: false, shouldError: false, }, + { + id: "Regex matches request path string", + service: "cat", + path: "/cat/bengal/1", + method: "POST", + expect: true, + shouldError: false, + }, + { + id: "Regex expects number in path string", + service: "cat", + path: "/cat/bengal/abc", + method: "POST", + expect: false, + shouldError: false, + }, + { + id: "Regex expects third parameter in string", + service: "cat", + path: "/cat/bengal", + method: "POST", + expect: false, + shouldError: false, + }, } services := generateServicesConfig("http://test.test") From 861a3d08248bfbfd8fd202a0884f99aa09f6bcf9 Mon Sep 17 00:00:00 2001 From: Scarlett Perry Date: Tue, 18 Jul 2023 11:08:21 -0400 Subject: [PATCH 4/6] dont modify regex, update tests --- backend/module/proxy/proxy.go | 19 ++++++---------- backend/module/proxy/proxy_test.go | 35 +++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/backend/module/proxy/proxy.go b/backend/module/proxy/proxy.go index c186de1b4f..a9d2398e1e 100644 --- a/backend/module/proxy/proxy.go +++ b/backend/module/proxy/proxy.go @@ -36,21 +36,17 @@ func New(cfg *any.Any, log *zap.Logger, scope tally.Scope) (module.Module, error return nil, err } - // Validate that each services constructs a parsable URL for _, service := range config.Services { for _, ar := range service.AllowedRequests { - var path string switch t := ar.PathType.(type) { case *proxyv1cfg.AllowRequest_Path: - path = t.Path - case *proxyv1cfg.AllowRequest_PathRegex: - path = t.PathRegex + // For exact path type, validate that each services constructs a parsable URL + _, err := url.Parse(fmt.Sprintf("%s%s", service.Host, t.Path)) + if err != nil { + return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) + } default: - return nil, fmt.Errorf("path type not supported: %s", t) - } - _, err := url.Parse(fmt.Sprintf("%s%s", service.Host, path)) - if err != nil { - return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) + break } } } @@ -225,8 +221,7 @@ func isAllowedRequest(services []*proxyv1cfg.Service, service, path, method stri return true, nil } case *proxyv1cfg.AllowRequest_PathRegex: - // MatchString reports whether the string contains any match of the regular expression so we add the ‘^’ and ‘$’ to ensure the regex matches the full string - r := regexp.MustCompile(fmt.Sprintf("^%s$", t.PathRegex)) + r := regexp.MustCompile(t.PathRegex) if r.MatchString(path) { return true, nil } diff --git a/backend/module/proxy/proxy_test.go b/backend/module/proxy/proxy_test.go index 4d1af8e971..212669592f 100644 --- a/backend/module/proxy/proxy_test.go +++ b/backend/module/proxy/proxy_test.go @@ -39,6 +39,7 @@ func generateServicesConfig(host string) []*proxyv1cfg.Service { AllowedRequests: []*proxyv1cfg.AllowRequest{ {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/meow"}, Method: "GET"}, {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/nom"}, Method: "POST"}, + {PathType: &proxyv1cfg.AllowRequest_PathRegex{PathRegex: `^/meow/\w+/\w+$`}, Method: "GET"}, }, }, } @@ -270,7 +271,7 @@ func TestIsAllowedRequest(t *testing.T) { shouldError: false, }, { - id: "Regex matches request path string", + id: "Path string matches regex", service: "cat", path: "/cat/bengal/1", method: "POST", @@ -293,6 +294,38 @@ func TestIsAllowedRequest(t *testing.T) { expect: false, shouldError: false, }, + { + id: "Regex does not have a rule for end of string", + service: "cat", + path: "/cat/bengal/1/comments", + method: "POST", + expect: true, + shouldError: false, + }, + { + id: "Path string matches regex", + service: "meow", + path: "/meow/foo/sound", + method: "POST", + expect: true, + shouldError: false, + }, + { + id: "Path string does not match regex last character", + service: "meow", + path: "/meow/foo/sound/play", + method: "POST", + expect: false, + shouldError: false, + }, + { + id: "Path string does not match regex first character", + service: "meow", + path: "animals/meow/foo/sound/play", + method: "POST", + expect: false, + shouldError: false, + }, } services := generateServicesConfig("http://test.test") From d2ab5ac69a7ccc76bfc86dbdacee2a9fe6cafed6 Mon Sep 17 00:00:00 2001 From: Scarlett Perry Date: Tue, 18 Jul 2023 16:50:16 -0400 Subject: [PATCH 5/6] address PR feedback --- backend/module/proxy/proxy.go | 47 ++++++++++++++------- backend/module/proxy/proxy_test.go | 66 ++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/backend/module/proxy/proxy.go b/backend/module/proxy/proxy.go index a9d2398e1e..27eec7cf88 100644 --- a/backend/module/proxy/proxy.go +++ b/backend/module/proxy/proxy.go @@ -36,19 +36,9 @@ func New(cfg *any.Any, log *zap.Logger, scope tally.Scope) (module.Module, error return nil, err } - for _, service := range config.Services { - for _, ar := range service.AllowedRequests { - switch t := ar.PathType.(type) { - case *proxyv1cfg.AllowRequest_Path: - // For exact path type, validate that each services constructs a parsable URL - _, err := url.Parse(fmt.Sprintf("%s%s", service.Host, t.Path)) - if err != nil { - return nil, fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) - } - default: - break - } - } + err = validateConfigPaths(config) + if err != nil { + return nil, err } m := &mod{ @@ -221,12 +211,15 @@ func isAllowedRequest(services []*proxyv1cfg.Service, service, path, method stri return true, nil } case *proxyv1cfg.AllowRequest_PathRegex: - r := regexp.MustCompile(t.PathRegex) + r, err := regexp.Compile(t.PathRegex) + if err != nil { + return false, err + } if r.MatchString(path) { return true, nil } default: - return false, fmt.Errorf("path type not supported: %s", t) + return false, fmt.Errorf("path type not supported: %T", t) } } // return early here as were done checking allowed request for this service @@ -250,3 +243,27 @@ func addExcludedHeaders(request *http.Request) { request.Host = hostHeader } } + +func validateConfigPaths(config *proxyv1cfg.Config) error { + for _, service := range config.Services { + for _, ar := range service.AllowedRequests { + switch t := ar.PathType.(type) { + case *proxyv1cfg.AllowRequest_Path: + // For exact path type, validate that string constructs a parsable URL + _, err := url.Parse(fmt.Sprintf("%s%s", service.Host, t.Path)) + if err != nil { + return fmt.Errorf("unable to parse the configured URL for service [%s]", service.Name) + } + case *proxyv1cfg.AllowRequest_PathRegex: + // For path regex type, validate that expression can be parsed + _, err := regexp.Compile(t.PathRegex) + if err != nil { + return err + } + default: + return fmt.Errorf("path type not supported: %T", t) + } + } + } + return nil +} diff --git a/backend/module/proxy/proxy_test.go b/backend/module/proxy/proxy_test.go index 212669592f..119ae68a45 100644 --- a/backend/module/proxy/proxy_test.go +++ b/backend/module/proxy/proxy_test.go @@ -341,6 +341,72 @@ func TestIsAllowedRequest(t *testing.T) { } } +func TestValidateConfigPaths(t *testing.T) { + tests := []struct { + id string + config *proxyv1cfg.Config + shouldError bool + }{ + { + id: "All paths parsable", + config: &proxyv1cfg.Config{ + Services: []*proxyv1cfg.Service{ + { + Name: "cat", + Host: "http://test.test", + AllowedRequests: []*proxyv1cfg.AllowRequest{ + {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/meow"}}, + {PathType: &proxyv1cfg.AllowRequest_PathRegex{PathRegex: `/cat/\w+/[0-9]`}}, + }, + }, + }, + }, + shouldError: false, + }, + { + id: "Exact path type not parsable", + config: &proxyv1cfg.Config{ + Services: []*proxyv1cfg.Service{ + { + Name: "cat", + Host: "http://test.test", + AllowedRequests: []*proxyv1cfg.AllowRequest{ + {PathType: &proxyv1cfg.AllowRequest_Path{Path: `^meow`}}, + {PathType: &proxyv1cfg.AllowRequest_PathRegex{PathRegex: `/cat/\w+/[0-9]`}}, + }, + }, + }, + }, + shouldError: true, + }, + { + id: "Regex path type not parsable", + config: &proxyv1cfg.Config{ + Services: []*proxyv1cfg.Service{ + { + Name: "cat", + Host: "http://test.test", + AllowedRequests: []*proxyv1cfg.AllowRequest{ + {PathType: &proxyv1cfg.AllowRequest_Path{Path: "/meow"}}, + {PathType: &proxyv1cfg.AllowRequest_PathRegex{PathRegex: `?:\/\/)?`}}, + }, + }, + }, + }, + shouldError: true, + }, + } + + for _, test := range tests { + err := validateConfigPaths(test.config) + if test.shouldError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + } +} + func TestAddExcludedHeaders(t *testing.T) { tests := []struct { id string From e3826ae4fe9d0a7b3b558f77d5122927c944ff6e Mon Sep 17 00:00:00 2001 From: Scarlett Perry Date: Tue, 18 Jul 2023 16:54:24 -0400 Subject: [PATCH 6/6] nit --- backend/module/proxy/proxy_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/module/proxy/proxy_test.go b/backend/module/proxy/proxy_test.go index 119ae68a45..e7f51d6670 100644 --- a/backend/module/proxy/proxy_test.go +++ b/backend/module/proxy/proxy_test.go @@ -371,7 +371,7 @@ func TestValidateConfigPaths(t *testing.T) { Name: "cat", Host: "http://test.test", AllowedRequests: []*proxyv1cfg.AllowRequest{ - {PathType: &proxyv1cfg.AllowRequest_Path{Path: `^meow`}}, + {PathType: &proxyv1cfg.AllowRequest_Path{Path: "^meow"}}, {PathType: &proxyv1cfg.AllowRequest_PathRegex{PathRegex: `/cat/\w+/[0-9]`}}, }, },