mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-25 06:36:03 +03:00
csrf middleware
This commit is contained in:
parent
06cab9921d
commit
3432853c8b
233
core/csrf.go
Normal file
233
core/csrf.go
Normal file
@ -0,0 +1,233 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
// CSRFTokenGetter func type
|
||||
type CSRFTokenGetter func(c *gin.Context) string
|
||||
|
||||
// DefaultCSRFTokenGetter default getter
|
||||
var DefaultCSRFTokenGetter = func(c *gin.Context) string {
|
||||
r := c.Request
|
||||
|
||||
if t := r.URL.Query().Get("csrf_token"); len(t) > 0 {
|
||||
return t
|
||||
} else if t := r.Header.Get("X-CSRF-Token"); len(t) > 0 {
|
||||
return t
|
||||
} else if t := r.Header.Get("X-XSRF-Token"); len(t) > 0 {
|
||||
return t
|
||||
} else if c.Request.Body != nil {
|
||||
data, _ := ioutil.ReadAll(c.Request.Body)
|
||||
c.Request.Body = ioutil.NopCloser(bytes.NewReader(data))
|
||||
t := r.FormValue("csrf_token")
|
||||
c.Request.Body = ioutil.NopCloser(bytes.NewReader(data))
|
||||
|
||||
if len(t) > 0 {
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// DefaultIgnoredMethods ignored methods for CSRF verifier middleware
|
||||
var DefaultIgnoredMethods = []string{"GET", "HEAD", "OPTIONS"}
|
||||
|
||||
type CSRF struct {
|
||||
salt string
|
||||
secret string
|
||||
sessionName string
|
||||
abortFunc gin.HandlerFunc
|
||||
csrfTokenGetter CSRFTokenGetter
|
||||
store sessions.Store
|
||||
locale *Localizer
|
||||
}
|
||||
|
||||
// NewCSRF creates CSRF struct with specified configuration and session store.
|
||||
// GenerateCSRFMiddleware and VerifyCSRFMiddleware returns CSRF middlewares.
|
||||
// Salt must be different every time (pass empty salt to use random), secret must be provided, sessionName is optional - pass empty to use default,
|
||||
// store will be used to store sessions, abortFunc will be called to return error if token is invalid, csrfTokenGetter will be used to obtain token.
|
||||
// Usage (with random salt):
|
||||
// core.NewCSRF("", "super secret", "csrf_session", store, func (c *gin.Context) {
|
||||
// c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid CSRF token"})
|
||||
// }, core.DefaultCSRFTokenGetter)
|
||||
// Note for csrfTokenGetter: if you want to read token from request body (for example, from form field) - don't forget to restore Body data!
|
||||
// Body in http.Request is io.ReadCloser instance. Reading CSRF token from form like that:
|
||||
// if t := r.FormValue("csrf_token"); len(t) > 0 {
|
||||
// return t
|
||||
// }
|
||||
// will close body - and all next middlewares won't be able to read body at all!
|
||||
// Use DefaultCSRFTokenGetter as example to implement your own token getter.
|
||||
func NewCSRF(salt, secret, sessionName string, store sessions.Store, abortFunc gin.HandlerFunc, csrfTokenGetter CSRFTokenGetter) *CSRF {
|
||||
if store == nil {
|
||||
panic("store must not be nil")
|
||||
}
|
||||
|
||||
if secret == "" {
|
||||
panic("at least secret must be provided")
|
||||
}
|
||||
|
||||
csrf := &CSRF{
|
||||
store: store,
|
||||
secret: secret,
|
||||
abortFunc: abortFunc,
|
||||
csrfTokenGetter: csrfTokenGetter,
|
||||
}
|
||||
|
||||
if salt == "" {
|
||||
salt = csrf.generateSalt()
|
||||
}
|
||||
|
||||
if sessionName == "" {
|
||||
sessionName = "csrf_token_session"
|
||||
}
|
||||
|
||||
csrf.salt = salt
|
||||
csrf.sessionName = sessionName
|
||||
|
||||
return csrf
|
||||
}
|
||||
|
||||
// strInSlice checks whether string exists in slice
|
||||
func (x *CSRF) strInSlice(slice []string, v string) bool {
|
||||
exists := false
|
||||
|
||||
for _, i := range slice {
|
||||
if i == v {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// generateCSRFToken generates new CSRF token
|
||||
func (x *CSRF) generateCSRFToken() string {
|
||||
h := sha1.New()
|
||||
io.WriteString(h, x.salt+"#"+x.secret)
|
||||
hash := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
return hash
|
||||
}
|
||||
|
||||
// generateSalt generates salt from random bytes. If it fails to generate cryptographically
|
||||
// secure salt - it will generate pseudo-random, weaker salt.
|
||||
// It will be used automatically if no salt provided.
|
||||
// Default secure salt length: 8 bytes.
|
||||
// Default pseudo-random salt length: 64 bytes.
|
||||
func (x *CSRF) generateSalt() string {
|
||||
salt := securecookie.GenerateRandomKey(8)
|
||||
|
||||
if salt == nil {
|
||||
return x.pseudoRandomString(64)
|
||||
}
|
||||
|
||||
return string(salt)
|
||||
}
|
||||
|
||||
// pseudoRandomString generates pseudo-random string with specified length
|
||||
func (x *CSRF) pseudoRandomString(length int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
data := make([]byte, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
data[i] = byte(65 + rand.Intn(90-65))
|
||||
}
|
||||
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// CSRFFromContext returns csrf token or random token. It shouldn't return empty string because it will make csrf protection useless.
|
||||
// e.g. any request without token will work fine, which is inacceptable.
|
||||
func (x *CSRF) CSRFFromContext(c *gin.Context) string {
|
||||
if i, ok := c.Get("csrf_token"); ok {
|
||||
if token, ok := i.(string); ok {
|
||||
return token
|
||||
} else {
|
||||
return x.generateCSRFToken()
|
||||
}
|
||||
} else {
|
||||
return x.generateCSRFToken()
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateCSRFMiddleware returns gin.HandlerFunc which will generate CSRF token
|
||||
// Usage:
|
||||
// engine := gin.New()
|
||||
// csrf := NewCSRF("salt", "secret", "not_found", "incorrect", localizer)
|
||||
// engine.Use(csrf.GenerateCSRFMiddleware())
|
||||
func (x *CSRF) GenerateCSRFMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
session, _ := x.store.Get(c.Request, x.sessionName)
|
||||
|
||||
if i, ok := session.Values["csrf_token"]; ok {
|
||||
if i, ok := i.(string); !ok || i == "" {
|
||||
if x.fillToken(session, c) != nil {
|
||||
x.abortFunc(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if x.fillToken(session, c) != nil {
|
||||
x.abortFunc(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fillToken stores token in session and context
|
||||
func (x *CSRF) fillToken(s *sessions.Session, c *gin.Context) error {
|
||||
s.Values["csrf_token"] = x.generateCSRFToken()
|
||||
c.Set("csrf_token", s.Values["csrf_token"])
|
||||
return s.Save(c.Request, c.Writer)
|
||||
}
|
||||
|
||||
// VerifyCSRFMiddleware verifies CSRF token
|
||||
// Usage:
|
||||
// engine := gin.New()
|
||||
// engine.Use(csrf.VerifyCSRFMiddleware())
|
||||
func (x *CSRF) VerifyCSRFMiddleware(ignoredMethods []string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if x.strInSlice(ignoredMethods, c.Request.Method) {
|
||||
return
|
||||
}
|
||||
|
||||
var token string
|
||||
session, _ := x.store.Get(c.Request, x.sessionName)
|
||||
|
||||
if i, ok := session.Values["csrf_token"]; ok {
|
||||
if i, ok := i.(string); !ok || i == "" {
|
||||
x.abortFunc(c)
|
||||
c.Abort()
|
||||
return
|
||||
} else {
|
||||
token = i
|
||||
}
|
||||
} else {
|
||||
x.abortFunc(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if x.csrfTokenGetter(c) != token {
|
||||
x.abortFunc(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
305
core/csrf_test.go
Normal file
305
core/csrf_test.go
Normal file
@ -0,0 +1,305 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type CSRFTest struct {
|
||||
suite.Suite
|
||||
csrf *CSRF
|
||||
}
|
||||
|
||||
type requestOptions struct {
|
||||
Method string
|
||||
URL string
|
||||
Headers map[string]string
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
func TestCSRF_DefaultCSRFTokenGetter_Empty(t *testing.T) {
|
||||
c := &gin.Context{Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: "",
|
||||
},
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
|
||||
}}
|
||||
|
||||
assert.Empty(t, DefaultCSRFTokenGetter(c))
|
||||
}
|
||||
|
||||
func TestCSRF_DefaultCSRFTokenGetter_URL(t *testing.T) {
|
||||
c := &gin.Context{Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: "csrf_token=token",
|
||||
},
|
||||
}}
|
||||
|
||||
assert.NotEmpty(t, DefaultCSRFTokenGetter(c))
|
||||
assert.Equal(t, "token", DefaultCSRFTokenGetter(c))
|
||||
}
|
||||
|
||||
func TestCSRF_DefaultCSRFTokenGetter_Header_CSRF(t *testing.T) {
|
||||
header := http.Header{}
|
||||
header.Add("X-CSRF-Token", "token")
|
||||
c := &gin.Context{Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: "",
|
||||
},
|
||||
Header: header,
|
||||
}}
|
||||
|
||||
assert.NotEmpty(t, DefaultCSRFTokenGetter(c))
|
||||
assert.Equal(t, "token", DefaultCSRFTokenGetter(c))
|
||||
}
|
||||
|
||||
func TestCSRF_DefaultCSRFTokenGetter_Header_XSRC(t *testing.T) {
|
||||
header := http.Header{}
|
||||
header.Add("X-XSRF-Token", "token")
|
||||
c := &gin.Context{Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: "",
|
||||
},
|
||||
Header: header,
|
||||
}}
|
||||
|
||||
assert.NotEmpty(t, DefaultCSRFTokenGetter(c))
|
||||
assert.Equal(t, "token", DefaultCSRFTokenGetter(c))
|
||||
}
|
||||
|
||||
func TestCSRF_DefaultCSRFTokenGetter_Form(t *testing.T) {
|
||||
headers := http.Header{}
|
||||
headers.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
c := &gin.Context{Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: "",
|
||||
},
|
||||
Header: headers,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte(""))),
|
||||
}}
|
||||
c.Request.PostForm = url.Values{"csrf_token": {"token"}}
|
||||
|
||||
assert.NotEmpty(t, DefaultCSRFTokenGetter(c))
|
||||
assert.Equal(t, "token", DefaultCSRFTokenGetter(c))
|
||||
|
||||
_, err := ioutil.ReadAll(c.Request.Body)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCSRF_NewCSRF_NilStore(t *testing.T) {
|
||||
defer func() {
|
||||
assert.NotNil(t, recover())
|
||||
}()
|
||||
|
||||
NewCSRF("salt", "secret", "csrf", nil, func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
}
|
||||
|
||||
func TestCSRF_NewCSRF_EmptySecret(t *testing.T) {
|
||||
defer func() {
|
||||
assert.NotNil(t, recover())
|
||||
}()
|
||||
|
||||
store := sessions.NewCookieStore([]byte("keys"))
|
||||
NewCSRF("salt", "", "csrf", store, func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
}
|
||||
|
||||
func TestCSRF_NewCSRF_SaltAndSessionNotEmpty(t *testing.T) {
|
||||
store := sessions.NewCookieStore([]byte("keys"))
|
||||
csrf := NewCSRF("salt", "secret", "", store, func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
assert.NotEmpty(t, csrf.salt)
|
||||
assert.NotEmpty(t, csrf.sessionName)
|
||||
}
|
||||
|
||||
func TestCSRF_Suite(t *testing.T) {
|
||||
suite.Run(t, new(CSRFTest))
|
||||
}
|
||||
|
||||
func (x *CSRFTest) SetupSuite() {
|
||||
store := sessions.NewCookieStore([]byte("keys"))
|
||||
x.csrf = NewCSRF("salt", "secret", "", store, func(context *gin.Context) {
|
||||
context.AbortWithStatus(900)
|
||||
}, DefaultCSRFTokenGetter)
|
||||
}
|
||||
|
||||
func (x *CSRFTest) NewServer() *gin.Engine {
|
||||
gin.SetMode(gin.TestMode)
|
||||
g := gin.New()
|
||||
g.Use(x.csrf.GenerateCSRFMiddleware(), x.csrf.VerifyCSRFMiddleware(DefaultIgnoredMethods))
|
||||
return g
|
||||
}
|
||||
|
||||
func (x *CSRFTest) request(server *gin.Engine, options requestOptions) (*httptest.ResponseRecorder, *http.Request) {
|
||||
if options.Method == "" {
|
||||
options.Method = "GET"
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(options.Method, options.URL, options.Body)
|
||||
|
||||
if options.Headers != nil {
|
||||
for key, value := range options.Headers {
|
||||
req.Header.Set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
server.ServeHTTP(w, req)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return w, req
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_strInSlice() {
|
||||
slice := []string{"alpha", "beta", "gamma"}
|
||||
|
||||
assert.False(x.T(), x.csrf.strInSlice(slice, "lambda"))
|
||||
assert.True(x.T(), x.csrf.strInSlice(slice, "alpha"))
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_generateCSRFToken() {
|
||||
assert.NotEmpty(x.T(), x.csrf.generateCSRFToken())
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_generateSalt() {
|
||||
salt := x.csrf.generateSalt()
|
||||
assert.NotEmpty(x.T(), salt)
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_pseudoRandomString() {
|
||||
assert.Len(x.T(), x.csrf.pseudoRandomString(12), 12)
|
||||
assert.Len(x.T(), x.csrf.pseudoRandomString(64), 64)
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_CSRFFromContext_NotExist() {
|
||||
c := &gin.Context{}
|
||||
token := x.csrf.CSRFFromContext(c)
|
||||
|
||||
assert.NotEmpty(x.T(), token)
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_CSRFFromContext_NotString() {
|
||||
c := &gin.Context{}
|
||||
c.Set("csrf_token", struct{}{})
|
||||
token := x.csrf.CSRFFromContext(c)
|
||||
|
||||
assert.NotEmpty(x.T(), token)
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_CSRFFromContext_Exist() {
|
||||
c := &gin.Context{}
|
||||
c.Set("csrf_token", "token")
|
||||
token := x.csrf.CSRFFromContext(c)
|
||||
|
||||
assert.NotEmpty(x.T(), token)
|
||||
assert.Equal(x.T(), "token", token)
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_GenerateCSRFMiddleware() {
|
||||
assert.NotNil(x.T(), x.csrf.GenerateCSRFMiddleware())
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_GenerateCSRFMiddleware_Middleware() {
|
||||
x.csrf.store = sessions.NewCookieStore([]byte("secret"))
|
||||
g := x.NewServer()
|
||||
g.GET("/get", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "OK")
|
||||
})
|
||||
g.POST("/post", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "OK")
|
||||
})
|
||||
|
||||
get, getReq := x.request(g, requestOptions{
|
||||
Method: "GET",
|
||||
URL: "/get",
|
||||
})
|
||||
|
||||
session, _ := x.csrf.store.Get(getReq, x.csrf.sessionName)
|
||||
post, _ := x.request(g, requestOptions{
|
||||
Method: "POST",
|
||||
URL: "/post",
|
||||
Headers: map[string]string{
|
||||
"Cookie": get.Header().Get("Set-Cookie"),
|
||||
"X-CSRF-Token": session.Values["csrf_token"].(string),
|
||||
},
|
||||
})
|
||||
|
||||
getWithToken, getReqWithToken := x.request(g, requestOptions{
|
||||
Method: "GET",
|
||||
URL: "/get",
|
||||
Headers: map[string]string{
|
||||
"Cookie": get.Header().Get("Set-Cookie"),
|
||||
"X-CSRF-Token": session.Values["csrf_token"].(string),
|
||||
},
|
||||
})
|
||||
|
||||
secondSession, _ := x.csrf.store.Get(getReqWithToken, x.csrf.sessionName)
|
||||
|
||||
assert.Equal(x.T(), session.Values["csrf_token"].(string), secondSession.Values["csrf_token"].(string))
|
||||
assert.Equal(x.T(), "OK", get.Body.String())
|
||||
assert.Equal(x.T(), http.StatusOK, get.Result().StatusCode)
|
||||
assert.Equal(x.T(), "OK", getWithToken.Body.String())
|
||||
assert.Equal(x.T(), http.StatusOK, getWithToken.Result().StatusCode)
|
||||
assert.Equal(x.T(), "OK", post.Body.String())
|
||||
assert.Equal(x.T(), http.StatusOK, post.Result().StatusCode)
|
||||
}
|
||||
|
||||
func (x *CSRFTest) Test_VerifyCSRFMiddleware_NoToken() {
|
||||
x.csrf.store = sessions.NewCookieStore([]byte("secret"))
|
||||
g := x.NewServer()
|
||||
g.GET("/get", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "OK")
|
||||
})
|
||||
g.POST("/post", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "OK")
|
||||
})
|
||||
|
||||
postWithoutSession, _ := x.request(g, requestOptions{
|
||||
Method: "POST",
|
||||
URL: "/post",
|
||||
})
|
||||
|
||||
get, getReq := x.request(g, requestOptions{
|
||||
Method: "GET",
|
||||
URL: "/get",
|
||||
})
|
||||
|
||||
session, _ := x.csrf.store.Get(getReq, x.csrf.sessionName)
|
||||
post, _ := x.request(g, requestOptions{
|
||||
Method: "POST",
|
||||
URL: "/post",
|
||||
Headers: map[string]string{
|
||||
"Cookie": get.Header().Get("Set-Cookie"),
|
||||
"X-CSRF-Token": session.Values["csrf_token"].(string),
|
||||
},
|
||||
})
|
||||
|
||||
postIncorrectToken, _ := x.request(g, requestOptions{
|
||||
Method: "POST",
|
||||
URL: "/post",
|
||||
Headers: map[string]string{
|
||||
"Cookie": get.Header().Get("Set-Cookie"),
|
||||
"X-CSRF-Token": "incorrect token",
|
||||
},
|
||||
})
|
||||
|
||||
assert.NotEqual(x.T(), "OK", postWithoutSession.Body.String())
|
||||
assert.Equal(x.T(), 900, postWithoutSession.Result().StatusCode)
|
||||
assert.NotEqual(x.T(), "OK", postIncorrectToken.Body.String())
|
||||
assert.Equal(x.T(), 900, postIncorrectToken.Result().StatusCode)
|
||||
assert.Equal(x.T(), "OK", get.Body.String())
|
||||
assert.Equal(x.T(), http.StatusOK, get.Result().StatusCode)
|
||||
assert.Equal(x.T(), "OK", post.Body.String())
|
||||
assert.Equal(x.T(), http.StatusOK, post.Result().StatusCode)
|
||||
}
|
@ -6,6 +6,8 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/op/go-logging"
|
||||
)
|
||||
|
||||
@ -18,6 +20,8 @@ type Engine struct {
|
||||
ginEngine *gin.Engine
|
||||
httpClient *http.Client
|
||||
Logger *logging.Logger
|
||||
csrf *CSRF
|
||||
Sessions sessions.Store
|
||||
Config ConfigInterface
|
||||
LogFormatter logging.Formatter
|
||||
prepared bool
|
||||
@ -157,6 +161,75 @@ func (e *Engine) HTTPClient() *http.Client {
|
||||
}
|
||||
}
|
||||
|
||||
// WithCookieSessions generates new CookieStore with optional key length.
|
||||
// Default key length is 32 bytes.
|
||||
func (e *Engine) WithCookieSessions(keyLength ...int) *Engine {
|
||||
length := 32
|
||||
|
||||
if len(keyLength) > 0 && keyLength[0] > 0 {
|
||||
length = keyLength[0]
|
||||
}
|
||||
|
||||
e.Sessions = sessions.NewCookieStore(securecookie.GenerateRandomKey(length))
|
||||
return e
|
||||
}
|
||||
|
||||
// WithCookieSessions generates new FilesystemStore with optional key length.
|
||||
// Default key length is 32 bytes.
|
||||
func (e *Engine) WithFilesystemSessions(path string, keyLength ...int) *Engine {
|
||||
length := 32
|
||||
|
||||
if len(keyLength) > 0 && keyLength[0] > 0 {
|
||||
length = keyLength[0]
|
||||
}
|
||||
|
||||
e.Sessions = sessions.NewFilesystemStore(path, securecookie.GenerateRandomKey(length))
|
||||
return e
|
||||
}
|
||||
|
||||
// InitCSRF initializes CSRF middleware. engine.Sessions must be already initialized,
|
||||
// use engine.WithCookieStore or engine.WithFilesystemStore for that.
|
||||
// Syntax is similar to core.NewCSRF, but you shouldn't pass sessionName, store and salt.
|
||||
func (e *Engine) InitCSRF(secret string, abortFunc gin.HandlerFunc, getter CSRFTokenGetter) *Engine {
|
||||
if e.Sessions == nil {
|
||||
panic("engine.Sessions must be initialized first")
|
||||
}
|
||||
|
||||
e.csrf = NewCSRF("", secret, "", e.Sessions, abortFunc, getter)
|
||||
return e
|
||||
}
|
||||
|
||||
// VerifyCSRFMiddleware returns CSRF verifier middleware
|
||||
// Usage:
|
||||
// engine.Router().Use(engine.VerifyCSRFMiddleware(core.DefaultIgnoredMethods))
|
||||
func (e *Engine) VerifyCSRFMiddleware(ignoredMethods []string) gin.HandlerFunc {
|
||||
if e.csrf == nil {
|
||||
panic("csrf is not initialized")
|
||||
}
|
||||
|
||||
return e.csrf.VerifyCSRFMiddleware(ignoredMethods)
|
||||
}
|
||||
|
||||
// GenerateCSRFMiddleware returns CSRF generator middleware
|
||||
// Usage:
|
||||
// engine.Router().Use(engine.GenerateCSRFMiddleware())
|
||||
func (e *Engine) GenerateCSRFMiddleware() gin.HandlerFunc {
|
||||
if e.csrf == nil {
|
||||
panic("csrf is not initialized")
|
||||
}
|
||||
|
||||
return e.csrf.GenerateCSRFMiddleware()
|
||||
}
|
||||
|
||||
// GetCSRFToken returns CSRF token from provided context
|
||||
func (e *Engine) GetCSRFToken(c *gin.Context) string {
|
||||
if e.csrf == nil {
|
||||
panic("csrf is not initialized")
|
||||
}
|
||||
|
||||
return e.csrf.CSRFFromContext(c)
|
||||
}
|
||||
|
||||
// ConfigureRouter will call provided callback with current gin.Engine, or panic if engine is not present
|
||||
func (e *Engine) ConfigureRouter(callback func(*gin.Engine)) *Engine {
|
||||
callback(e.Router())
|
||||
|
@ -1,9 +1,12 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -180,6 +183,112 @@ func (e *EngineTest) Test_HTTPClient() {
|
||||
assert.NotNil(e.T(), e.engine.httpClient)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_WithCookieSessions() {
|
||||
e.engine.Sessions = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
|
||||
assert.NotNil(e.T(), e.engine.Sessions)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_WithFilesystemSessions() {
|
||||
e.engine.Sessions = nil
|
||||
e.engine.WithFilesystemSessions(os.TempDir(), 4)
|
||||
|
||||
assert.NotNil(e.T(), e.engine.Sessions)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_InitCSRF_Fail() {
|
||||
defer func() {
|
||||
assert.NotNil(e.T(), recover())
|
||||
}()
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.Sessions = nil
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
assert.Nil(e.T(), e.engine.csrf)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_InitCSRF() {
|
||||
defer func() {
|
||||
assert.Nil(e.T(), recover())
|
||||
}()
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
assert.NotNil(e.T(), e.engine.csrf)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_VerifyCSRFMiddleware_Fail() {
|
||||
defer func() {
|
||||
assert.NotNil(e.T(), recover())
|
||||
}()
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.VerifyCSRFMiddleware(DefaultIgnoredMethods)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_VerifyCSRFMiddleware() {
|
||||
defer func() {
|
||||
assert.Nil(e.T(), recover())
|
||||
}()
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.VerifyCSRFMiddleware(DefaultIgnoredMethods)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_GenerateCSRFMiddleware_Fail() {
|
||||
defer func() {
|
||||
assert.NotNil(e.T(), recover())
|
||||
}()
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.GenerateCSRFMiddleware()
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_GenerateCSRFMiddleware() {
|
||||
defer func() {
|
||||
assert.Nil(e.T(), recover())
|
||||
}()
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.GenerateCSRFMiddleware()
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_GetCSRFToken_Fail() {
|
||||
defer func() {
|
||||
assert.NotNil(e.T(), recover())
|
||||
}()
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.GetCSRFToken(nil)
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_GetCSRFToken() {
|
||||
defer func() {
|
||||
assert.Nil(e.T(), recover())
|
||||
}()
|
||||
|
||||
c := &gin.Context{Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
RawQuery: "",
|
||||
},
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
|
||||
Header: http.Header{"X-CSRF-Token": []string{"token"}},
|
||||
}}
|
||||
c.Set("csrf_token", "token")
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
assert.NotEmpty(e.T(), e.engine.GetCSRFToken(c))
|
||||
assert.Equal(e.T(), "token", e.engine.GetCSRFToken(c))
|
||||
}
|
||||
|
||||
func (e *EngineTest) Test_Run_Fail() {
|
||||
defer func() {
|
||||
assert.NotNil(e.T(), recover())
|
||||
|
3
go.mod
3
go.mod
@ -15,11 +15,12 @@ require (
|
||||
github.com/gobuffalo/packr/v2 v2.7.1
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/sessions v1.2.0
|
||||
github.com/h2non/gock v1.0.10
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/jinzhu/gorm v1.9.11
|
||||
github.com/json-iterator/go v1.1.7 // indirect
|
||||
github.com/karrick/godirwalk v1.12.0 // indirect
|
||||
github.com/lib/pq v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.10 // indirect
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
|
||||
|
36
go.sum
36
go.sum
@ -15,13 +15,9 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aws/aws-sdk-go v1.23.9 h1:UYWPGrBMlrW5VCYeWMbog1T/kqZzkvvheUDQaaUAhqI=
|
||||
github.com/aws/aws-sdk-go v1.23.9/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.25.14 h1:hEsU+cukBOQe1wRRuvEgG+y6AVCyS2eyHWuTefhGxTY=
|
||||
github.com/aws/aws-sdk-go v1.25.14/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713 h1:UNOqI3EKhvbqV8f1Vm3NIwkrhq388sGCeAH2Op7w0rc=
|
||||
github.com/certifi/gocertifi v0.0.0-20190506164543-d2eda7129713/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||
github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67 h1:8k9FLYBLKT+9v2HQJ/a95ZemmTx+/ltJcAiRhVushG8=
|
||||
github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
@ -43,19 +39,13 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/getsentry/raven-go v0.0.0-20180903072508-084a9de9eb03 h1:G/9fPivTr5EiyqE9OlW65iMRUxFXMGRHgZFGo50uG8Q=
|
||||
github.com/getsentry/raven-go v0.0.0-20180903072508-084a9de9eb03/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20180827023943-5799bbbb6dce h1:KqeVCdb+M2iwyF6GzdYxTazfE1cE+133RXuGaZ5Sc1E=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20180827023943-5799bbbb6dce/go.mod h1:62qM8p4crGvNKE413gTzn4eMFin1VOJfMDWMRzHdvqM=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20190914010127-bba2ccfe37ec h1:mfeHJfPwsXp/iovjrTwxtkTMRAIXZu6Uxg6O95En1+Y=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20190914010127-bba2ccfe37ec/go.mod h1:2tmLQ8sVzr2XKwquGd7zNq3zB6fGyjJL+47JoxoF8yM=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
|
||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
@ -67,14 +57,10 @@ github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=
|
||||
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||
github.com/gobuffalo/logger v1.0.0 h1:xw9Ko9EcC5iAFprrjJ6oZco9UpzS5MQ4jAwghsLHdy4=
|
||||
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=
|
||||
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packr/v2 v2.6.0 h1:EMUzJIb5rof6r087PtGmgdzdLKpRBESJ/8jyL9MexfY=
|
||||
github.com/gobuffalo/packr/v2 v2.6.0/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
@ -99,6 +85,10 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
|
||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/h2non/gock v1.0.10 h1:EzHYzKKSLN4xk0w193uAy3tp8I3+L1jmaI2Mjg4lCgU=
|
||||
github.com/h2non/gock v1.0.10/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@ -109,8 +99,6 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw=
|
||||
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/gorm v1.9.10 h1:HvrsqdhCW78xpJF67g1hMxS6eCToo9PZH4LDB8WKPac=
|
||||
github.com/jinzhu/gorm v1.9.10/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY=
|
||||
github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE=
|
||||
github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw=
|
||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
@ -128,9 +116,6 @@ github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62F
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
|
||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/karrick/godirwalk v1.12.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
@ -147,8 +132,6 @@ github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@ -168,8 +151,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5 h1:/TjjTS4kg7vC+05gD0LE4+97f/+PRFICnK/7wJPk7kE=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.2 h1:KsHGcTByIM0mHZKQGy0nlJLOjPNjQ6MVib/3PvsBDNY=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.2/go.mod h1:JXS4+OKhbcwDoVTEj0sLFWL1vOwec2g/YBAxZ9owJqY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@ -193,12 +174,8 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/retailcrm/api-client-go v1.1.1 h1:yqsyYjBDdmDwExVlTdGucY9/IpEokXpkfTfA6z5AZ7M=
|
||||
github.com/retailcrm/api-client-go v1.1.1/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c=
|
||||
github.com/retailcrm/api-client-go v1.3.0 h1:8cEtLZ9gk+nTEVuzA/wzQhb8tRfaxfCQHLdPtO+/gek=
|
||||
github.com/retailcrm/api-client-go v1.3.0/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c=
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.31 h1:21pE1JhT49rvbMLDYJa0iiqbb/roz+eSp27fPck4uUw=
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.31/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI=
|
||||
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.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@ -268,6 +245,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -281,12 +259,9 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
@ -300,7 +275,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
|
Loading…
Reference in New Issue
Block a user