mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Feature: Parse user agent string in user auth token api response (#16… (#17504)
* Feature: Parse user agent string in user auth token api response (#16222) * Adding UA Parser Go modules attempt (#16222) * Bring user agent vals up per req * fix tests * doc update * update to flatten, no maps * update doc
This commit is contained in:
@@ -373,7 +373,11 @@ Content-Type: application/json
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
```http
|
||||
POST /api/admin/users/1/revoke-auth-token HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -381,7 +385,11 @@ Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Logout User
|
||||
|
||||
`POST /api/admin/users/:id/logout`
|
||||
|
||||
Logout user revokes all auth tokens (devices) for the user. User of issued auth tokens (devices) will no longer be logged in
|
||||
|
@@ -505,7 +505,11 @@ Content-Type: application/json
|
||||
"id": 361,
|
||||
"isActive": true,
|
||||
"clientIp": "127.0.0.1",
|
||||
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36",
|
||||
"browser": "Chrome",
|
||||
"browserVersion": "72.0",
|
||||
"os": "Linux",
|
||||
"osVersion": "",
|
||||
"device": "Other",
|
||||
"createdAt": "2019-03-05T21:22:54+01:00",
|
||||
"seenAt": "2019-03-06T19:41:06+01:00"
|
||||
},
|
||||
@@ -513,7 +517,11 @@ Content-Type: application/json
|
||||
"id": 364,
|
||||
"isActive": false,
|
||||
"clientIp": "127.0.0.1",
|
||||
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1",
|
||||
"browser": "Mobile Safari",
|
||||
"browserVersion": "11.0",
|
||||
"os": "iOS",
|
||||
"osVersion": "11.0",
|
||||
"device": "iPhone",
|
||||
"createdAt": "2019-03-06T19:41:19+01:00",
|
||||
"seenAt": "2019-03-06T19:41:21+01:00"
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@@ -58,6 +58,7 @@ require (
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
|
||||
github.com/ua-parser/uap-go v0.0.0-20190303233514-1004ccd816b3
|
||||
github.com/uber-go/atomic v1.3.2 // indirect
|
||||
github.com/uber/jaeger-client-go v2.16.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.0.0+incompatible // indirect
|
||||
|
2
go.sum
2
go.sum
@@ -191,6 +191,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
|
||||
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
|
||||
github.com/ua-parser/uap-go v0.0.0-20190303233514-1004ccd816b3 h1:E7xa7Zur8hLPvw+03gAeQ9esrglfV389j2PcwhiGf/I=
|
||||
github.com/ua-parser/uap-go v0.0.0-20190303233514-1004ccd816b3/go.mod h1:OBcG9bn7sHtXgarhUEb3OfCnNsgtGnkVf41ilSZ3K3E=
|
||||
github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo=
|
||||
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
|
||||
github.com/uber/jaeger-client-go v2.16.0+incompatible h1:Q2Pp6v3QYiocMxomCaJuwQGFt7E53bPYqEgug/AoBtY=
|
||||
|
@@ -3,10 +3,14 @@ package dtos
|
||||
import "time"
|
||||
|
||||
type UserToken struct {
|
||||
Id int64 `json:"id"`
|
||||
IsActive bool `json:"isActive"`
|
||||
ClientIp string `json:"clientIp"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
SeenAt time.Time `json:"seenAt"`
|
||||
Id int64 `json:"id"`
|
||||
IsActive bool `json:"isActive"`
|
||||
ClientIp string `json:"clientIp"`
|
||||
Device string `json:"device"`
|
||||
OperatingSystem string `json:"os"`
|
||||
OperatingSystemVersion string `json:"osVersion"`
|
||||
Browser string `json:"browser"`
|
||||
BrowserVersion string `json:"browserVersion"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
SeenAt time.Time `json:"seenAt"`
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/ua-parser/uap-go/uaparser"
|
||||
)
|
||||
|
||||
// GET /api/user/auth-tokens
|
||||
@@ -62,13 +63,38 @@ func (server *HTTPServer) getUserAuthTokensInternal(c *models.ReqContext, userID
|
||||
isActive = true
|
||||
}
|
||||
|
||||
parser := uaparser.NewFromSaved()
|
||||
client := parser.Parse(token.UserAgent)
|
||||
|
||||
osVersion := ""
|
||||
if client.Os.Major != "" {
|
||||
osVersion = client.Os.Major
|
||||
|
||||
if client.Os.Minor != "" {
|
||||
osVersion = osVersion + "." + client.Os.Minor
|
||||
}
|
||||
}
|
||||
|
||||
browserVersion := ""
|
||||
if client.UserAgent.Major != "" {
|
||||
browserVersion = client.UserAgent.Major
|
||||
|
||||
if client.UserAgent.Minor != "" {
|
||||
browserVersion = browserVersion + "." + client.UserAgent.Minor
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, &dtos.UserToken{
|
||||
Id: token.Id,
|
||||
IsActive: isActive,
|
||||
ClientIp: token.ClientIp,
|
||||
UserAgent: token.UserAgent,
|
||||
CreatedAt: time.Unix(token.CreatedAt, 0),
|
||||
SeenAt: time.Unix(token.SeenAt, 0),
|
||||
Id: token.Id,
|
||||
IsActive: isActive,
|
||||
ClientIp: token.ClientIp,
|
||||
Device: client.Device.ToString(),
|
||||
OperatingSystem: client.Os.Family,
|
||||
OperatingSystemVersion: osVersion,
|
||||
Browser: client.UserAgent.Family,
|
||||
BrowserVersion: browserVersion,
|
||||
CreatedAt: time.Unix(token.CreatedAt, 0),
|
||||
SeenAt: time.Unix(token.SeenAt, 0),
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -140,17 +140,27 @@ func TestUserTokenApiEndpoint(t *testing.T) {
|
||||
So(resultOne.Get("id").MustInt64(), ShouldEqual, tokens[0].Id)
|
||||
So(resultOne.Get("isActive").MustBool(), ShouldBeTrue)
|
||||
So(resultOne.Get("clientIp").MustString(), ShouldEqual, "127.0.0.1")
|
||||
So(resultOne.Get("userAgent").MustString(), ShouldEqual, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36")
|
||||
So(resultOne.Get("createdAt").MustString(), ShouldEqual, time.Unix(tokens[0].CreatedAt, 0).Format(time.RFC3339))
|
||||
So(resultOne.Get("seenAt").MustString(), ShouldEqual, time.Unix(tokens[0].SeenAt, 0).Format(time.RFC3339))
|
||||
|
||||
So(resultOne.Get("device").MustString(), ShouldEqual, "Other")
|
||||
So(resultOne.Get("browser").MustString(), ShouldEqual, "Chrome")
|
||||
So(resultOne.Get("browserVersion").MustString(), ShouldEqual, "72.0")
|
||||
So(resultOne.Get("os").MustString(), ShouldEqual, "Linux")
|
||||
So(resultOne.Get("osVersion").MustString(), ShouldEqual, "")
|
||||
|
||||
resultTwo := result.GetIndex(1)
|
||||
So(resultTwo.Get("id").MustInt64(), ShouldEqual, tokens[1].Id)
|
||||
So(resultTwo.Get("isActive").MustBool(), ShouldBeFalse)
|
||||
So(resultTwo.Get("clientIp").MustString(), ShouldEqual, "127.0.0.2")
|
||||
So(resultTwo.Get("userAgent").MustString(), ShouldEqual, "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1")
|
||||
So(resultTwo.Get("createdAt").MustString(), ShouldEqual, time.Unix(tokens[1].CreatedAt, 0).Format(time.RFC3339))
|
||||
So(resultTwo.Get("seenAt").MustString(), ShouldEqual, time.Unix(tokens[1].SeenAt, 0).Format(time.RFC3339))
|
||||
|
||||
So(resultTwo.Get("device").MustString(), ShouldEqual, "iPhone")
|
||||
So(resultTwo.Get("browser").MustString(), ShouldEqual, "Mobile Safari")
|
||||
So(resultTwo.Get("browserVersion").MustString(), ShouldEqual, "11.0")
|
||||
So(resultTwo.Get("os").MustString(), ShouldEqual, "iOS")
|
||||
So(resultTwo.Get("osVersion").MustString(), ShouldEqual, "11.0")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
16
vendor/github.com/ua-parser/uap-go/LICENSE
generated
vendored
Normal file
16
vendor/github.com/ua-parser/uap-go/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
Apache License, Version 2.0
|
||||
===========================
|
||||
|
||||
Copyright 2009 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
2
vendor/github.com/ua-parser/uap-go/uaparser/.gitignore
generated
vendored
Normal file
2
vendor/github.com/ua-parser/uap-go/uaparser/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.out
|
||||
*.test
|
8
vendor/github.com/ua-parser/uap-go/uaparser/LICENSE.md
generated
vendored
Normal file
8
vendor/github.com/ua-parser/uap-go/uaparser/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2013 Yihuan Zhou
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
30
vendor/github.com/ua-parser/uap-go/uaparser/device.go
generated
vendored
Normal file
30
vendor/github.com/ua-parser/uap-go/uaparser/device.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package uaparser
|
||||
|
||||
import "strings"
|
||||
|
||||
type Device struct {
|
||||
Family string
|
||||
Brand string
|
||||
Model string
|
||||
}
|
||||
|
||||
func (parser *deviceParser) Match(line string, dvc *Device) {
|
||||
matches := parser.Reg.FindStringSubmatchIndex(line)
|
||||
|
||||
if len(matches) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
dvc.Family = string(parser.Reg.ExpandString(nil, parser.DeviceReplacement, line, matches))
|
||||
dvc.Family = strings.TrimSpace(dvc.Family)
|
||||
|
||||
dvc.Brand = string(parser.Reg.ExpandString(nil, parser.BrandReplacement, line, matches))
|
||||
dvc.Brand = strings.TrimSpace(dvc.Brand)
|
||||
|
||||
dvc.Model = string(parser.Reg.ExpandString(nil, parser.ModelReplacement, line, matches))
|
||||
dvc.Model = strings.TrimSpace(dvc.Model)
|
||||
}
|
||||
|
||||
func (dvc *Device) ToString() string {
|
||||
return dvc.Family
|
||||
}
|
49
vendor/github.com/ua-parser/uap-go/uaparser/os.go
generated
vendored
Normal file
49
vendor/github.com/ua-parser/uap-go/uaparser/os.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package uaparser
|
||||
|
||||
type Os struct {
|
||||
Family string
|
||||
Major string
|
||||
Minor string
|
||||
Patch string
|
||||
PatchMinor string `yaml:"patch_minor"`
|
||||
}
|
||||
|
||||
func (parser *osParser) Match(line string, os *Os) {
|
||||
matches := parser.Reg.FindStringSubmatchIndex(line)
|
||||
if len(matches) > 0 {
|
||||
os.Family = string(parser.Reg.ExpandString(nil, parser.OSReplacement, line, matches))
|
||||
os.Major = string(parser.Reg.ExpandString(nil, parser.V1Replacement, line, matches))
|
||||
os.Minor = string(parser.Reg.ExpandString(nil, parser.V2Replacement, line, matches))
|
||||
os.Patch = string(parser.Reg.ExpandString(nil, parser.V3Replacement, line, matches))
|
||||
os.PatchMinor = string(parser.Reg.ExpandString(nil, parser.V4Replacement, line, matches))
|
||||
}
|
||||
}
|
||||
|
||||
func (os *Os) ToString() string {
|
||||
var str string
|
||||
if os.Family != "" {
|
||||
str += os.Family
|
||||
}
|
||||
version := os.ToVersionString()
|
||||
if version != "" {
|
||||
str += " " + version
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func (os *Os) ToVersionString() string {
|
||||
var version string
|
||||
if os.Major != "" {
|
||||
version += os.Major
|
||||
}
|
||||
if os.Minor != "" {
|
||||
version += "." + os.Minor
|
||||
}
|
||||
if os.Patch != "" {
|
||||
version += "." + os.Patch
|
||||
}
|
||||
if os.PatchMinor != "" {
|
||||
version += "." + os.PatchMinor
|
||||
}
|
||||
return version
|
||||
}
|
355
vendor/github.com/ua-parser/uap-go/uaparser/parser.go
generated
vendored
Normal file
355
vendor/github.com/ua-parser/uap-go/uaparser/parser.go
generated
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
package uaparser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type RegexesDefinitions struct {
|
||||
UA []*uaParser `yaml:"user_agent_parsers"`
|
||||
OS []*osParser `yaml:"os_parsers"`
|
||||
Device []*deviceParser `yaml:"device_parsers"`
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type UserAgentSorter []*uaParser
|
||||
func (a UserAgentSorter) Len() int { return len(a) }
|
||||
func (a UserAgentSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a UserAgentSorter) Less(i, j int) bool { return atomic.LoadUint64(&a[i].MatchesCount) > atomic.LoadUint64(&a[j].MatchesCount) }
|
||||
|
||||
type uaParser struct {
|
||||
Reg *regexp.Regexp
|
||||
Expr string `yaml:"regex"`
|
||||
Flags string `yaml:"regex_flag"`
|
||||
FamilyReplacement string `yaml:"family_replacement"`
|
||||
V1Replacement string `yaml:"v1_replacement"`
|
||||
V2Replacement string `yaml:"v2_replacement"`
|
||||
V3Replacement string `yaml:"v3_replacement"`
|
||||
MatchesCount uint64
|
||||
}
|
||||
|
||||
func (ua *uaParser) setDefaults() {
|
||||
if ua.FamilyReplacement == "" {
|
||||
ua.FamilyReplacement = "$1"
|
||||
}
|
||||
if ua.V1Replacement == "" {
|
||||
ua.V1Replacement = "$2"
|
||||
}
|
||||
if ua.V2Replacement == "" {
|
||||
ua.V2Replacement = "$3"
|
||||
}
|
||||
if ua.V3Replacement == "" {
|
||||
ua.V3Replacement = "$4"
|
||||
}
|
||||
}
|
||||
|
||||
type OsSorter []*osParser
|
||||
func (a OsSorter) Len() int { return len(a) }
|
||||
func (a OsSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a OsSorter) Less(i, j int) bool { return atomic.LoadUint64(&a[i].MatchesCount) > atomic.LoadUint64(&a[j].MatchesCount) }
|
||||
|
||||
type osParser struct {
|
||||
Reg *regexp.Regexp
|
||||
Expr string `yaml:"regex"`
|
||||
Flags string `yaml:"regex_flag"`
|
||||
OSReplacement string `yaml:"os_replacement"`
|
||||
V1Replacement string `yaml:"os_v1_replacement"`
|
||||
V2Replacement string `yaml:"os_v2_replacement"`
|
||||
V3Replacement string `yaml:"os_v3_replacement"`
|
||||
V4Replacement string `yaml:"os_v4_replacement"`
|
||||
MatchesCount uint64
|
||||
}
|
||||
|
||||
func (os *osParser) setDefaults() {
|
||||
if os.OSReplacement == "" {
|
||||
os.OSReplacement = "$1"
|
||||
}
|
||||
if os.V1Replacement == "" {
|
||||
os.V1Replacement = "$2"
|
||||
}
|
||||
if os.V2Replacement == "" {
|
||||
os.V2Replacement = "$3"
|
||||
}
|
||||
if os.V3Replacement == "" {
|
||||
os.V3Replacement = "$4"
|
||||
}
|
||||
if os.V4Replacement == "" {
|
||||
os.V4Replacement = "$5"
|
||||
}
|
||||
}
|
||||
|
||||
type DeviceSorter []*deviceParser
|
||||
func (a DeviceSorter) Len() int { return len(a) }
|
||||
func (a DeviceSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a DeviceSorter) Less(i, j int) bool { return atomic.LoadUint64(&a[i].MatchesCount) > atomic.LoadUint64(&a[j].MatchesCount) }
|
||||
|
||||
type deviceParser struct {
|
||||
Reg *regexp.Regexp
|
||||
Expr string `yaml:"regex"`
|
||||
Flags string `yaml:"regex_flag"`
|
||||
DeviceReplacement string `yaml:"device_replacement"`
|
||||
BrandReplacement string `yaml:"brand_replacement"`
|
||||
ModelReplacement string `yaml:"model_replacement"`
|
||||
MatchesCount uint64
|
||||
}
|
||||
|
||||
func (device *deviceParser) setDefaults() {
|
||||
if device.DeviceReplacement == "" {
|
||||
device.DeviceReplacement = "$1"
|
||||
}
|
||||
if device.ModelReplacement == "" {
|
||||
device.ModelReplacement = "$1"
|
||||
}
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
UserAgent *UserAgent
|
||||
Os *Os
|
||||
Device *Device
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
RegexesDefinitions
|
||||
UserAgentMisses uint64
|
||||
OsMisses uint64
|
||||
DeviceMisses uint64
|
||||
Mode int
|
||||
UseSort bool
|
||||
debugMode bool
|
||||
}
|
||||
|
||||
|
||||
const (
|
||||
EOsLookUpMode = 1 /* 00000001 */
|
||||
EUserAgentLookUpMode = 2 /* 00000010 */
|
||||
EDeviceLookUpMode = 4 /* 00000100 */
|
||||
cMinMissesTreshold = 100000
|
||||
cDefaultMissesTreshold = 500000
|
||||
cDefaultMatchIdxNotOk = 20
|
||||
cDefaultSortOption = false
|
||||
)
|
||||
|
||||
var (
|
||||
missesTreshold = uint64(500000)
|
||||
matchIdxNotOk = 20
|
||||
)
|
||||
|
||||
func (parser *Parser) mustCompile() { // until we can use yaml.UnmarshalYAML with embedded pointer struct
|
||||
for _, p := range parser.UA {
|
||||
p.Reg = compileRegex(p.Flags, p.Expr)
|
||||
p.setDefaults()
|
||||
}
|
||||
for _, p := range parser.OS {
|
||||
p.Reg = compileRegex(p.Flags, p.Expr)
|
||||
p.setDefaults()
|
||||
}
|
||||
for _, p := range parser.Device {
|
||||
p.Reg = compileRegex(p.Flags, p.Expr)
|
||||
p.setDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
func NewWithOptions(regexFile string, mode, treshold, topCnt int, useSort, debugMode bool) (*Parser, error) {
|
||||
data, err := ioutil.ReadFile(regexFile)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
if topCnt >= 0 {
|
||||
matchIdxNotOk = topCnt
|
||||
}
|
||||
if treshold > cMinMissesTreshold {
|
||||
missesTreshold = uint64(treshold)
|
||||
}
|
||||
parser, err := NewFromBytes(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parser.Mode = mode
|
||||
parser.UseSort = useSort
|
||||
parser.debugMode = debugMode
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
func New(regexFile string) (*Parser, error) {
|
||||
data, err := ioutil.ReadFile(regexFile)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
matchIdxNotOk = cDefaultMatchIdxNotOk
|
||||
missesTreshold = cDefaultMissesTreshold
|
||||
parser, err := NewFromBytes(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
func NewFromSaved() *Parser {
|
||||
parser, err := NewFromBytes(definitionYaml)
|
||||
if err != nil {
|
||||
// if the YAML is malformed, it's a programmatic error inside what
|
||||
// we've statically-compiled in our binary. Panic!
|
||||
panic(err.Error())
|
||||
}
|
||||
return parser
|
||||
}
|
||||
|
||||
func NewFromBytes(data []byte) (*Parser, error) {
|
||||
var definitions RegexesDefinitions
|
||||
if err := yaml.Unmarshal(data, &definitions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser := &Parser{definitions, 0, 0, 0, (EOsLookUpMode|EUserAgentLookUpMode|EDeviceLookUpMode), false, false}
|
||||
parser.mustCompile()
|
||||
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
func (parser *Parser) Parse(line string) *Client {
|
||||
cli := new(Client)
|
||||
var wg sync.WaitGroup
|
||||
if EUserAgentLookUpMode & parser.Mode == EUserAgentLookUpMode {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
parser.RLock()
|
||||
cli.UserAgent = parser.ParseUserAgent(line)
|
||||
parser.RUnlock()
|
||||
}()
|
||||
}
|
||||
if EOsLookUpMode & parser.Mode == EOsLookUpMode {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
parser.RLock()
|
||||
cli.Os = parser.ParseOs(line)
|
||||
parser.RUnlock()
|
||||
}()
|
||||
}
|
||||
if EDeviceLookUpMode & parser.Mode == EDeviceLookUpMode {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
parser.RLock()
|
||||
cli.Device = parser.ParseDevice(line)
|
||||
parser.RUnlock()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
if parser.UseSort == true {
|
||||
checkAndSort(parser)
|
||||
}
|
||||
return cli
|
||||
}
|
||||
|
||||
func (parser *Parser) ParseUserAgent(line string) *UserAgent {
|
||||
ua := new(UserAgent)
|
||||
foundIdx := -1
|
||||
found := false
|
||||
for i, uaPattern := range parser.UA {
|
||||
uaPattern.Match(line, ua)
|
||||
if len(ua.Family) > 0 {
|
||||
found = true
|
||||
foundIdx = i
|
||||
atomic.AddUint64(&uaPattern.MatchesCount, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
ua.Family = "Other"
|
||||
}
|
||||
if(foundIdx > matchIdxNotOk) {
|
||||
atomic.AddUint64(&parser.UserAgentMisses, 1)
|
||||
}
|
||||
return ua
|
||||
}
|
||||
|
||||
func (parser *Parser) ParseOs(line string) *Os {
|
||||
os := new(Os)
|
||||
foundIdx := -1
|
||||
found := false
|
||||
for i, osPattern := range parser.OS {
|
||||
osPattern.Match(line, os)
|
||||
if len(os.Family) > 0 {
|
||||
found = true
|
||||
foundIdx = i
|
||||
atomic.AddUint64(&osPattern.MatchesCount, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
os.Family = "Other"
|
||||
}
|
||||
if(foundIdx > matchIdxNotOk) {
|
||||
atomic.AddUint64(&parser.OsMisses, 1)
|
||||
}
|
||||
return os
|
||||
}
|
||||
|
||||
func (parser *Parser) ParseDevice(line string) *Device {
|
||||
dvc := new(Device)
|
||||
foundIdx := -1
|
||||
found := false
|
||||
for i, dvcPattern := range parser.Device {
|
||||
dvcPattern.Match(line, dvc)
|
||||
if len(dvc.Family) > 0 {
|
||||
found = true
|
||||
foundIdx = i
|
||||
atomic.AddUint64(&dvcPattern.MatchesCount, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
dvc.Family = "Other"
|
||||
}
|
||||
if(foundIdx > matchIdxNotOk) {
|
||||
atomic.AddUint64(&parser.DeviceMisses, 1)
|
||||
}
|
||||
return dvc
|
||||
}
|
||||
|
||||
func checkAndSort(parser *Parser) {
|
||||
parser.Lock()
|
||||
if(atomic.LoadUint64(&parser.UserAgentMisses) >= missesTreshold) {
|
||||
if parser.debugMode {
|
||||
fmt.Printf("%s\tSorting UserAgents slice\n", time.Now());
|
||||
}
|
||||
parser.UserAgentMisses = 0
|
||||
sort.Sort(UserAgentSorter(parser.UA));
|
||||
}
|
||||
parser.Unlock()
|
||||
parser.Lock()
|
||||
if(atomic.LoadUint64(&parser.OsMisses) >= missesTreshold) {
|
||||
if parser.debugMode {
|
||||
fmt.Printf("%s\tSorting OS slice\n", time.Now());
|
||||
}
|
||||
parser.OsMisses = 0
|
||||
sort.Sort(OsSorter(parser.OS));
|
||||
}
|
||||
parser.Unlock()
|
||||
parser.Lock()
|
||||
if(atomic.LoadUint64(&parser.DeviceMisses) >= missesTreshold) {
|
||||
if parser.debugMode {
|
||||
fmt.Printf("%s\tSorting Device slice\n", time.Now());
|
||||
}
|
||||
parser.DeviceMisses = 0
|
||||
sort.Sort(DeviceSorter(parser.Device));
|
||||
}
|
||||
parser.Unlock()
|
||||
}
|
||||
|
||||
func compileRegex(flags, expr string) *regexp.Regexp {
|
||||
if flags == "" {
|
||||
return regexp.MustCompile(expr)
|
||||
} else {
|
||||
return regexp.MustCompile(fmt.Sprintf("(?%s)%s", flags, expr))
|
||||
}
|
||||
}
|
44
vendor/github.com/ua-parser/uap-go/uaparser/user_agent.go
generated
vendored
Normal file
44
vendor/github.com/ua-parser/uap-go/uaparser/user_agent.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package uaparser
|
||||
|
||||
type UserAgent struct {
|
||||
Family string
|
||||
Major string
|
||||
Minor string
|
||||
Patch string
|
||||
}
|
||||
|
||||
func (parser *uaParser) Match(line string, ua *UserAgent) {
|
||||
matches := parser.Reg.FindStringSubmatchIndex(line)
|
||||
if len(matches) > 0 {
|
||||
ua.Family = string(parser.Reg.ExpandString(nil, parser.FamilyReplacement, line, matches))
|
||||
ua.Major = string(parser.Reg.ExpandString(nil, parser.V1Replacement, line, matches))
|
||||
ua.Minor = string(parser.Reg.ExpandString(nil, parser.V2Replacement, line, matches))
|
||||
ua.Patch = string(parser.Reg.ExpandString(nil, parser.V3Replacement, line, matches))
|
||||
}
|
||||
}
|
||||
|
||||
func (ua *UserAgent) ToString() string {
|
||||
var str string
|
||||
if ua.Family != "" {
|
||||
str += ua.Family
|
||||
}
|
||||
version := ua.ToVersionString()
|
||||
if version != "" {
|
||||
str += " " + version
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func (ua *UserAgent) ToVersionString() string {
|
||||
var version string
|
||||
if ua.Major != "" {
|
||||
version += ua.Major
|
||||
}
|
||||
if ua.Minor != "" {
|
||||
version += "." + ua.Minor
|
||||
}
|
||||
if ua.Patch != "" {
|
||||
version += "." + ua.Patch
|
||||
}
|
||||
return version
|
||||
}
|
3316
vendor/github.com/ua-parser/uap-go/uaparser/yaml.go
generated
vendored
Normal file
3316
vendor/github.com/ua-parser/uap-go/uaparser/yaml.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -192,6 +192,8 @@ github.com/smartystreets/goconvey/convey/gotest
|
||||
github.com/stretchr/testify/assert
|
||||
# github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
|
||||
github.com/teris-io/shortid
|
||||
# github.com/ua-parser/uap-go v0.0.0-20190303233514-1004ccd816b3
|
||||
github.com/ua-parser/uap-go/uaparser
|
||||
# github.com/uber/jaeger-client-go v2.16.0+incompatible
|
||||
github.com/uber/jaeger-client-go/config
|
||||
github.com/uber/jaeger-client-go/zipkin
|
||||
|
Reference in New Issue
Block a user