Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support sign and auth for request #323

Merged
merged 10 commits into from
Feb 7, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions common/constant/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,21 @@ const (
const (
TRACING_REMOTE_SPAN_CTX = "tracing.remote.span.ctx"
)

const (
CONSUMER_SIGN_FILTER = "sign"
PROVIDER_AUTH_FILTER = "auth"
SERVICE_AUTH_KEY = "auth"
AUTHENTICATOR_KEY = "authenticator"
DEFAULT_AUTHENTICATOR = "accesskeys"
DEFAULT_ACCESS_KEY_STORAGE = "urlstorage"
ACCESS_KEY_STORAGE_KEY = "accessKey.storage"
REQUEST_TIMESTAMP_KEY = "timestamp"
REQUEST_SIGNATURE_KEY = "signature"
AK_KEY = "ak"
SIGNATURE_STRING_FORMAT = "%s#%s#%s#%s"
PARAMTER_SIGNATURE_ENABLE_KEY = "param.sign"
CONSUMER = "consumer"
ACCESS_KEY_ID_KEY = "accessKeyId"
SECRET_ACCESS_KEY_KEY = "secretAccessKey"
)
CodingSinger marked this conversation as resolved.
Show resolved Hide resolved
32 changes: 32 additions & 0 deletions common/extension/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package extension

import (
"github.com/apache/dubbo-go/filter"
)

var (
authenticators = make(map[string]func() filter.Authenticator)
accesskeyStorages = make(map[string]func() filter.AccessKeyStorage)
)

func SetAuthenticator(name string, fcn func() filter.Authenticator) {
authenticators[name] = fcn
}

func GetAuthenticator(name string) filter.Authenticator {
if authenticators[name] == nil {
panic("authenticator for " + name + " is not existing, make sure you have import the package.")
}
return authenticators[name]()
}

func SetAccesskeyStorages(name string, fcn func() filter.AccessKeyStorage) {
accesskeyStorages[name] = fcn
}

func GetAccesskeyStorages(name string) filter.AccessKeyStorage {
if accesskeyStorages[name] == nil {
panic("accesskeyStorages for " + name + " is not existing, make sure you have import the package.")
}
return accesskeyStorages[name]()
}
20 changes: 20 additions & 0 deletions common/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,26 @@ func (c URL) ServiceKey() string {
return buf.String()
}

func (c *URL) ColonSeparatedKey() string {
AlexStocks marked this conversation as resolved.
Show resolved Hide resolved
CodingSinger marked this conversation as resolved.
Show resolved Hide resolved
intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
if intf == "" {
return ""
}
buf := &bytes.Buffer{}
buf.WriteString(intf)
buf.WriteString(":")
version := c.GetParam(constant.VERSION_KEY, "")
if version != "" && version != "0.0.0" {
buf.WriteString(version)
}
group := c.GetParam(constant.GROUP_KEY, "")
buf.WriteString(":")
if group != "" {
buf.WriteString(group)
}
return buf.String()
}

// EncodedServiceKey ...
func (c *URL) EncodedServiceKey() string {
CodingSinger marked this conversation as resolved.
Show resolved Hide resolved
serviceKey := c.ServiceKey()
Expand Down
14 changes: 14 additions & 0 deletions common/url_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,17 @@ func TestClone(t *testing.T) {
assert.Equal(t, u1.Protocol, "dubbo")
assert.Equal(t, u2.Protocol, "provider")
}

func TestColonSeparatedKey(t *testing.T) {
u1, _ := NewURL(context.TODO(), "dubbo://127.0.0.1:20000")
u1.AddParam(constant.INTERFACE_KEY, "com.ikurento.user.UserProvider")

assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+"::")
u1.AddParam(constant.VERSION_KEY, "version1")
assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+":version1:")
u1.AddParam(constant.GROUP_KEY, "group1")
assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+":version1:group1")
u1.SetParam(constant.VERSION_KEY, "")
assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+"::group1")

}
6 changes: 6 additions & 0 deletions config/service_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ type ServiceConfig struct {
TpsLimitRejectedHandler string `yaml:"tps.limit.rejected.handler" json:"tps.limit.rejected.handler,omitempty" property:"tps.limit.rejected.handler"`
ExecuteLimit string `yaml:"execute.limit" json:"execute.limit,omitempty" property:"execute.limit"`
ExecuteLimitRejectedHandler string `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"`
Auth string `yaml:"auth" json:"auth,omitempty" property:"auth"`
ParamSign string `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"`

unexported *atomic.Bool
exported *atomic.Bool
Expand Down Expand Up @@ -220,6 +222,10 @@ func (c *ServiceConfig) getUrlMap() url.Values {
urlMap.Set(constant.EXECUTE_LIMIT_KEY, c.ExecuteLimit)
urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, c.ExecuteLimitRejectedHandler)

// auth filter
urlMap.Set(constant.SERVICE_AUTH_KEY, c.Auth)
urlMap.Set(constant.PARAMTER_SIGNATURE_ENABLE_KEY, c.ParamSign)

for _, v := range c.Methods {
prefix := "methods." + v.Name + "."
urlMap.Set(prefix+constant.LOADBALANCE_KEY, v.Loadbalance)
Expand Down
22 changes: 22 additions & 0 deletions filter/access_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package filter

import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/protocol"
)

type AccessKeyPair struct {
AccessKey string `yaml:"accessKey" json:"accessKey,omitempty" property:"accessKey"`
SecretKey string `yaml:"secretKey" json:"secretKey,omitempty" property:"secretKey"`
ConsumerSide string `yaml:"consumerSide" json:"ConsumerSide,consumerSide" property:"consumerSide"`
ProviderSide string `yaml:"providerSide" json:"providerSide,omitempty" property:"providerSide"`
Creator string `yaml:"creator" json:"creator,omitempty" property:"creator"`
Options string `yaml:"options" json:"options,omitempty" property:"options"`
}

// AccessKeyStorage
// This SPI Extension support us to store our AccessKeyPair or load AccessKeyPair from other
// storage, such as filesystem.
type AccessKeyStorage interface {
GetAccessKeyPair(protocol.Invocation, *common.URL) *AccessKeyPair
}
16 changes: 16 additions & 0 deletions filter/authenticator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package filter

import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/protocol"
)

// Authenticator
type Authenticator interface {

// give a sign to request
Sign(protocol.Invocation, *common.URL) error

// verify the signature of the request is valid or not
Authenticate(protocol.Invocation, *common.URL) error
}
30 changes: 30 additions & 0 deletions filter/filter_impl/auth/accesskey_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package auth

import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/extension"
"github.com/apache/dubbo-go/filter"
"github.com/apache/dubbo-go/protocol"
)

// DefaultAccesskeyStorage
// The default implementation of AccesskeyStorage
type DefaultAccesskeyStorage struct {
CodingSinger marked this conversation as resolved.
Show resolved Hide resolved
}

// get AccessKeyPair from url by the key "accessKeyId" and "secretAccessKey"
func (storage *DefaultAccesskeyStorage) GetAccessKeyPair(invocation protocol.Invocation, url *common.URL) *filter.AccessKeyPair {
return &filter.AccessKeyPair{
AccessKey: url.GetParam(constant.ACCESS_KEY_ID_KEY, ""),
SecretKey: url.GetParam(constant.SECRET_ACCESS_KEY_KEY, ""),
}
}

func init() {
extension.SetAccesskeyStorages(constant.DEFAULT_ACCESS_KEY_STORAGE, GetDefaultAccesskeyStorage)
}

func GetDefaultAccesskeyStorage() filter.AccessKeyStorage {
return &DefaultAccesskeyStorage{}
}
28 changes: 28 additions & 0 deletions filter/filter_impl/auth/accesskey_storage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package auth

import (
"net/url"
"testing"
)

import (
"github.com/stretchr/testify/assert"
)

import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
invocation2 "github.com/apache/dubbo-go/protocol/invocation"
)

func TestDefaultAccesskeyStorage_GetAccesskeyPair(t *testing.T) {
url := common.NewURLWithOptions(
common.WithParams(url.Values{}),
common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"),
common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey"))
invocation := &invocation2.RPCInvocation{}
storage := GetDefaultAccesskeyStorage()
accesskeyPair := storage.GetAccessKeyPair(invocation, url)
assert.Equal(t, "skey", accesskeyPair.SecretKey)
assert.Equal(t, "akey", accesskeyPair.AccessKey)
}
43 changes: 43 additions & 0 deletions filter/filter_impl/auth/consumer_sign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package auth

import (
"context"
"fmt"
)
import (
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/extension"
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/filter"
"github.com/apache/dubbo-go/protocol"
)

// ConsumerSignFilter
// This filter is working for signing the request on consumer side
type ConsumerSignFilter struct {
}

func init() {
extension.SetFilter(constant.CONSUMER_SIGN_FILTER, getConsumerSignFilter)
}

func (csf *ConsumerSignFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
logger.Infof("invoking ConsumerSign filter.")
url := invoker.GetUrl()

err := doAuthWork(&url, func(authenticator filter.Authenticator) error {
return authenticator.Sign(invocation, &url)
})
if err != nil {
panic(fmt.Sprintf("Sign for invocation %s # %s failed", url.ServiceKey(), invocation.MethodName()))

}
return invoker.Invoke(ctx, invocation)
}

func (csf *ConsumerSignFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
return result
}
func getConsumerSignFilter() filter.Filter {
return &ConsumerSignFilter{}
}
37 changes: 37 additions & 0 deletions filter/filter_impl/auth/consumer_sign_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package auth

import (
"context"
"testing"
)

import (
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/protocol"
"github.com/apache/dubbo-go/protocol/invocation"
"github.com/apache/dubbo-go/protocol/mock"
)

func TestConsumerSignFilter_Invoke(t *testing.T) {
url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
url.SetParam(constant.SECRET_ACCESS_KEY_KEY, "sk")
url.SetParam(constant.ACCESS_KEY_ID_KEY, "ak")
inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil)
filter := &ConsumerSignFilter{}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl)
result := &protocol.RPCResult{}
invoker.EXPECT().Invoke(inv).Return(result).Times(2)
invoker.EXPECT().GetUrl().Return(url).Times(2)
assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv))

url.SetParam(constant.SERVICE_AUTH_KEY, "true")
assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv))
}
Loading