diff --git a/incognia.go b/incognia.go index e6d558a..f5c2b7c 100644 --- a/incognia.go +++ b/incognia.go @@ -7,6 +7,8 @@ import ( "fmt" "io/ioutil" "net/http" + "runtime" + "runtime/debug" "time" ) @@ -42,6 +44,7 @@ type Client struct { tokenProvider TokenProvider netClient *http.Client endpoints *endpoints + UserAgent string } type IncogniaClientConfig struct { @@ -140,6 +143,23 @@ func New(config *IncogniaClientConfig) (*Client, error) { Timeout: tokenRouteTimeout, }) + libVersion := "unknown" + if buildInfo, ok := debug.ReadBuildInfo(); ok { + for _, dep := range buildInfo.Deps { + if dep.Path == "repo.incognia.com/go/incognia" { + libVersion = dep.Version + } + } + } + + userAgent := fmt.Sprintf( + "incognia-api-go/%s (%s %s) Go/%s", + libVersion, + runtime.GOOS, + runtime.GOARCH, + runtime.Version(), + ) + tokenProvider := config.TokenProvider if tokenProvider == nil { tokenProvider = NewAutoRefreshTokenProvider(tokenClient) @@ -147,7 +167,7 @@ func New(config *IncogniaClientConfig) (*Client, error) { endpoints := getEndpoints() - return &Client{clientID: config.ClientID, clientSecret: config.ClientSecret, tokenProvider: tokenProvider, netClient: netClient, endpoints: &endpoints}, nil + return &Client{clientID: config.ClientID, clientSecret: config.ClientSecret, tokenProvider: tokenProvider, netClient: netClient, endpoints: &endpoints, UserAgent: userAgent}, nil } func (c *Client) GetSignupAssessment(signupID string) (ret *SignupAssessment, err error) { @@ -427,6 +447,7 @@ func (c *Client) registerLogin(login *Login) (*TransactionAssessment, error) { func (c *Client) doRequest(request *http.Request, response interface{}) error { request.Header.Add("Content-Type", "application/json") + request.Header.Add("User-Agent", c.UserAgent) err := c.authorizeRequest(request) if err != nil { diff --git a/incognia_test.go b/incognia_test.go index 86de7d6..9be954e 100644 --- a/incognia_test.go +++ b/incognia_test.go @@ -5,6 +5,7 @@ import ( "net/http" "net/http/httptest" "reflect" + "regexp" "strconv" "strings" "testing" @@ -21,6 +22,7 @@ const ( ) var ( + userAgentRegex = regexp.MustCompile(`^incognia-api-go(/(v[0-9]+\.[0-9]+\.[0-9]+|unknown))? \([a-z]+ [a-z0-9]+\) Go/go[0-9]+\.[0-9]+\.[0-9]+$`) now = time.Now() nowMinusSeconds = now.Add(-1 * time.Second) installationId = "installation-id" @@ -1022,6 +1024,9 @@ func (suite *IncogniaTestSuite) mockFeedbackEndpoint(expectedToken string, expec feedbackServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", "application/json") + userAgent := r.Header.Get("User-Agent") + suite.True(userAgentRegex.MatchString(userAgent), "User-Agent header does not match the expected format") + if !isRequestAuthorized(r, expectedToken) { w.WriteHeader(http.StatusForbidden) return @@ -1056,6 +1061,9 @@ func (suite *IncogniaTestSuite) mockTokenEndpointUnauthorized() *httptest.Server tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", "application/json") w.WriteHeader(http.StatusUnauthorized) + + userAgent := r.Header.Get("User-Agent") + suite.True(userAgentRegex.MatchString(userAgent), "User-Agent header does not match the expected format") })) return tokenServer @@ -1065,6 +1073,9 @@ func (suite *IncogniaTestSuite) mockPostTransactionsEndpoint(expectedToken strin transactionsServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", "application/json") + userAgent := r.Header.Get("User-Agent") + suite.True(userAgentRegex.MatchString(userAgent), "User-Agent header does not match the expected format") + if !isRequestAuthorized(r, expectedToken) { w.WriteHeader(http.StatusForbidden) return @@ -1103,6 +1114,9 @@ func (suite *IncogniaTestSuite) mockPostSignupsEndpoint(expectedToken string, ex signupsServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", "application/json") + userAgent := r.Header.Get("User-Agent") + suite.True(userAgentRegex.MatchString(userAgent), "User-Agent header does not match the expected format") + if !isRequestAuthorized(r, expectedToken) { w.WriteHeader(http.StatusForbidden) return diff --git a/token_client.go b/token_client.go index 2482453..ad17386 100644 --- a/token_client.go +++ b/token_client.go @@ -3,7 +3,10 @@ package incognia import ( "encoding/json" "errors" + "fmt" "net/http" + "runtime" + "runtime/debug" "strconv" "time" ) @@ -21,6 +24,7 @@ type TokenClient struct { ClientSecret string netClient *http.Client tokenEndpoint string + UserAgent string } type TokenClientConfig struct { @@ -37,11 +41,29 @@ func NewTokenClient(config *TokenClientConfig) *TokenClient { timeout = tokenNetClientTimeout } + libVersion := "unknown" + if buildInfo, ok := debug.ReadBuildInfo(); ok { + for _, dep := range buildInfo.Deps { + if dep.Path == "repo.incognia.com/go/incognia" { + libVersion = dep.Version + } + } + } + + userAgent := fmt.Sprintf( + "incognia-api-go/%s (%s %s) Go/%s", + libVersion, + runtime.GOOS, + runtime.GOARCH, + runtime.Version(), + ) + return &TokenClient{ ClientID: config.ClientID, ClientSecret: config.ClientSecret, netClient: &http.Client{Timeout: timeout}, tokenEndpoint: incogniaEndpoints.Token, + UserAgent: userAgent, } } @@ -53,6 +75,7 @@ func (tm TokenClient) requestToken() (Token, error) { req.SetBasicAuth(tm.ClientID, tm.ClientSecret) req.Header.Add("content-type", "application/x-www-form-urlencoded") + req.Header.Add("User-Agent", tm.UserAgent) res, err := tm.netClient.Do(req) if err != nil {