mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-22 05:06:04 +03:00
One-step connection middlewares (#34)
This commit is contained in:
parent
65f0ecfa6a
commit
b0d5488f5a
@ -34,6 +34,7 @@ type InfoInterface interface {
|
||||
GetName() string
|
||||
GetCode() string
|
||||
GetLogoPath() string
|
||||
GetSecret() string
|
||||
}
|
||||
|
||||
// Config struct.
|
||||
@ -195,6 +196,11 @@ func (t Info) GetLogoPath() string {
|
||||
return t.LogoPath
|
||||
}
|
||||
|
||||
// GetSecret returns secret.
|
||||
func (t Info) GetSecret() string {
|
||||
return t.Secret
|
||||
}
|
||||
|
||||
// IsSSLVerificationEnabled returns SSL verification flag (default is true).
|
||||
func (h *HTTPClientConfig) IsSSLVerificationEnabled() bool {
|
||||
if h.SSLVerification == nil {
|
||||
|
50
core/one_step_connection.go
Normal file
50
core/one_step_connection.go
Normal file
@ -0,0 +1,50 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
retailcrm "github.com/retailcrm/api-client-go/v2"
|
||||
)
|
||||
|
||||
// ConnectionConfig returns middleware for the one-step connection configuration route.
|
||||
func ConnectionConfig(registerURL string, scopes []string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.AbortWithStatusJSON(http.StatusOK, retailcrm.ConnectionConfigResponse{
|
||||
SuccessfulResponse: retailcrm.SuccessfulResponse{Success: true},
|
||||
Scopes: scopes,
|
||||
RegisterURL: registerURL,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyConnectRequest will verify ConnectRequest and place it into the "request" context field.
|
||||
func VerifyConnectRequest(secret string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
connectReqData := c.PostForm("register")
|
||||
if connectReqData == "" {
|
||||
c.AbortWithStatusJSON(http.StatusOK, retailcrm.ErrorResponse{ErrorMessage: "No data provided"})
|
||||
return
|
||||
}
|
||||
|
||||
var r retailcrm.ConnectRequest
|
||||
err := json.Unmarshal([]byte(connectReqData), &r)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusOK, retailcrm.ErrorResponse{ErrorMessage: "Invalid JSON provided"})
|
||||
return
|
||||
}
|
||||
|
||||
if !r.Verify(secret) {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("request", r)
|
||||
}
|
||||
}
|
||||
|
||||
// MustGetConnectRequest will extract retailcrm.ConnectRequest from the request context.
|
||||
func MustGetConnectRequest(c *gin.Context) retailcrm.ConnectRequest {
|
||||
return c.MustGet("request").(retailcrm.ConnectRequest)
|
||||
}
|
166
core/one_step_connection_test.go
Normal file
166
core/one_step_connection_test.go
Normal file
@ -0,0 +1,166 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
retailcrm "github.com/retailcrm/api-client-go/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMustGetConnectRequest_Success(t *testing.T) {
|
||||
assert.Equal(t, retailcrm.ConnectRequest{}, MustGetConnectRequest(&gin.Context{
|
||||
Keys: map[string]interface{}{
|
||||
"request": retailcrm.ConnectRequest{},
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
func TestMustGetConnectRequest_Failure(t *testing.T) {
|
||||
assert.Panics(t, func() {
|
||||
MustGetConnectRequest(&gin.Context{
|
||||
Keys: map[string]interface{}{},
|
||||
})
|
||||
})
|
||||
assert.Panics(t, func() {
|
||||
MustGetConnectRequest(&gin.Context{})
|
||||
})
|
||||
assert.Panics(t, func() {
|
||||
MustGetConnectRequest(nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConnectionConfig(t *testing.T) {
|
||||
scopes := []string{
|
||||
"integration_read",
|
||||
"integration_write",
|
||||
}
|
||||
registerURL := "https://example.com"
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
g := gin.New()
|
||||
g.GET("/", ConnectionConfig(registerURL, scopes))
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "/", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
g.ServeHTTP(rr, req)
|
||||
|
||||
var cc retailcrm.ConnectionConfigResponse
|
||||
require.NoError(t, json.NewDecoder(rr.Body).Decode(&cc))
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
assert.Equal(t, retailcrm.ConnectionConfigResponse{
|
||||
SuccessfulResponse: retailcrm.SuccessfulResponse{
|
||||
Success: true,
|
||||
},
|
||||
Scopes: scopes,
|
||||
RegisterURL: registerURL,
|
||||
}, cc)
|
||||
}
|
||||
|
||||
func TestVerifyConnectRequest_NoData(t *testing.T) {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
g := gin.New()
|
||||
g.POST("/", VerifyConnectRequest("secret"))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, "/", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
g.ServeHTTP(rr, req)
|
||||
|
||||
var resp retailcrm.ErrorResponse
|
||||
require.NoError(t, json.NewDecoder(rr.Body).Decode(&resp))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
assert.Equal(t, retailcrm.ErrorResponse{ErrorMessage: "No data provided"}, resp)
|
||||
}
|
||||
|
||||
func TestVerifyConnectRequest_InvalidJSON(t *testing.T) {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
g := gin.New()
|
||||
g.POST("/", VerifyConnectRequest("secret"))
|
||||
|
||||
req, err := http.NewRequest(
|
||||
http.MethodPost, "/", strings.NewReader(url.Values{"register": {"invalid json"}}.Encode()))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
g.ServeHTTP(rr, req)
|
||||
|
||||
var resp retailcrm.ErrorResponse
|
||||
require.NoError(t, json.NewDecoder(rr.Body).Decode(&resp))
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
assert.Equal(t, retailcrm.ErrorResponse{ErrorMessage: "Invalid JSON provided"}, resp)
|
||||
}
|
||||
|
||||
func TestVerifyConnectRequest_InvalidToken(t *testing.T) {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
g := gin.New()
|
||||
g.POST("/", VerifyConnectRequest("secret"))
|
||||
|
||||
data, err := json.Marshal(retailcrm.ConnectRequest{
|
||||
Token: "token",
|
||||
APIKey: "key",
|
||||
URL: "https://example.com",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(
|
||||
http.MethodPost, "/", strings.NewReader(url.Values{"register": {string(data)}}.Encode()))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
g.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, rr.Code)
|
||||
assert.Equal(t, "{}", rr.Body.String())
|
||||
}
|
||||
|
||||
func TestVerifyConnectRequest_OK(t *testing.T) {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
g := gin.New()
|
||||
g.POST("/", VerifyConnectRequest("secret"), func(c *gin.Context) {
|
||||
_ = MustGetConnectRequest(c)
|
||||
c.AbortWithStatus(http.StatusCreated)
|
||||
})
|
||||
|
||||
data, err := json.Marshal(retailcrm.ConnectRequest{
|
||||
Token: createConnectToken("key", "secret"),
|
||||
APIKey: "key",
|
||||
URL: "https://example.com",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(
|
||||
http.MethodPost, "/", strings.NewReader(url.Values{"register": {string(data)}}.Encode()))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
g.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusCreated, rr.Code)
|
||||
}
|
||||
|
||||
func createConnectToken(apiKey, secret string) string {
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
if _, err := mac.Write([]byte(apiKey)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hex.EncodeToString(mac.Sum(nil))
|
||||
}
|
2
go.mod
2
go.mod
@ -27,7 +27,7 @@ require (
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.1
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.3
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.32
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/ugorji/go v1.2.6 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -251,6 +251,10 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.1 h1:wyM0F1VTSJPO8PVEXB0u7s6ZEs0xRCnUu7YdQ1E4UZ8=
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.1/go.mod h1:1yTZl9+gd3+/k0kAJe7sYvC+mL4fqMwIwtnSgSWZlkQ=
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.2 h1:oFQycGqwcvfgW2JrbeWmPjxH7Wmh9j762c4FRxCDGNs=
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.2/go.mod h1:1yTZl9+gd3+/k0kAJe7sYvC+mL4fqMwIwtnSgSWZlkQ=
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.3 h1:7oKwOZgRLM7eEJUvFNhzfnyIJVomy80ffOEBdYpQRIs=
|
||||
github.com/retailcrm/api-client-go/v2 v2.0.3/go.mod h1:1yTZl9+gd3+/k0kAJe7sYvC+mL4fqMwIwtnSgSWZlkQ=
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.32 h1:IBPltSoD5q2PPZJbNC/prK5F9rEVPXVx/ZzDpi7HKhs=
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.32/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
Loading…
Reference in New Issue
Block a user