diff --git a/utils/source.go b/utils/source.go index 5306b597..5f3ee81f 100644 --- a/utils/source.go +++ b/utils/source.go @@ -3,7 +3,11 @@ package utils import ( "fmt" "net/http" + "net/url" + "regexp" "strings" + + log "github.com/sirupsen/logrus" ) // SourceExtractor extracts the source from the request, e.g. that may be client ip, or particular header that @@ -29,6 +33,20 @@ func NewExtractor(variable string) (SourceExtractor, error) { if variable == "client.ip" { return ExtractorFunc(extractClientIP), nil } + if strings.HasPrefix(variable, "client.ip.api.") { + apiRegex := strings.TrimPrefix(variable, "client.ip.api.(") + if len(apiRegex) == 0 { + return nil, fmt.Errorf("api extraction regex not provided") + } + + reg := strings.TrimSuffix(apiRegex, ")") + lambda := makeClientIPWithPathExtractor(reg) + if lambda == nil { + return nil, fmt.Errorf("Failed to compile regex: %s", reg) + } + + return lambda, nil + } if variable == "request.host" { return ExtractorFunc(extractHost), nil } @@ -59,3 +77,35 @@ func makeHeaderExtractor(header string) SourceExtractor { return req.Header.Get(header), 1, nil }) } + +//Create regex extractor function from client ip +request path +func makeClientIPWithPathExtractor(extractRegex string) SourceExtractor { + compiled, err := regexp.Compile(extractRegex) + if err != nil { + return nil + } + + return ExtractorFunc(func(req *http.Request) (string, int64, error) { + vals := strings.SplitN(req.RemoteAddr, ":", 2) + if len(vals[0]) == 0 { + return "", 0, fmt.Errorf("failed to parse client IP: %v", req.RemoteAddr) + } + + client := vals[0] + u, err := url.Parse(req.RequestURI) + if err != nil { + return "", 0, fmt.Errorf("Failed to parse URI") + } + + api := compiled.FindString(u.Path) + ret := client + api + if log.StandardLogger().Level >= log.DebugLevel { + fields := log.Fields{} + fields["api"] = api + fields["client"] = client + logEntry := log.StandardLogger().WithFields(fields) + logEntry.Debug("vulcand/oxy/SourceExtractor: extracted API") + } + return ret, 1, nil + }) +} diff --git a/utils/source_test.go b/utils/source_test.go new file mode 100644 index 00000000..968525a7 --- /dev/null +++ b/utils/source_test.go @@ -0,0 +1,23 @@ +package utils + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestPathExtractor(t *testing.T) { + extractFunc, err := NewExtractor("client.ip.api.(^([a-zA-Z0-9/]{0,10}))") + if err != nil { + panic("Fail to init extractor") + } + + testUrl := "https://test.com/cms/apiv2/workspaces/8148f4dc-f8c1-4d03-8fd8-6b14488aa016/tables/W3CIISLog?test=1234&xxx=567" + req := httptest.NewRequest(http.MethodGet, testUrl, nil) + + key, status, err := extractFunc.Extract(req) + + t.Log(key) + t.Log(status) + +}