diff --git a/go.mod b/go.mod index 9877a7eef..351c55cf8 100644 --- a/go.mod +++ b/go.mod @@ -73,6 +73,7 @@ require ( ) require ( + github.com/form3tech-oss/jwt-go v3.2.3+incompatible github.com/ghodss/yaml v1.0.0 github.com/go-chassis/go-chassis-extension/protocol/fiber4r v0.0.0-20220825091211-99d5e9810fd7 ) @@ -99,7 +100,6 @@ require ( github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/emicklei/go-restful v2.15.1-0.20220703112237-d9c71e118c95+incompatible // indirect - github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-chassis/go-restful-swagger20 v1.0.4-0.20220704025524-9243cbee26b7 // indirect github.com/go-chassis/sc-client v0.6.1-0.20220728072125-dacdd0c834bf // indirect diff --git a/server/plugin/auth/buildin/buildin.go b/server/plugin/auth/buildin/buildin.go index 21f2d0451..d9570c7c6 100644 --- a/server/plugin/auth/buildin/buildin.go +++ b/server/plugin/auth/buildin/buildin.go @@ -19,10 +19,17 @@ package buildin import ( "context" + "encoding/json" "errors" "fmt" "net/http" "strings" + "time" + + rbacmodel "github.com/go-chassis/cari/rbac" + "github.com/go-chassis/go-chassis/v2/security/authr" + "github.com/go-chassis/go-chassis/v2/server/restful" + "github.com/patrickmn/go-cache" "github.com/apache/servicecomb-service-center/pkg/log" "github.com/apache/servicecomb-service-center/pkg/plugin" @@ -32,12 +39,14 @@ import ( "github.com/apache/servicecomb-service-center/server/plugin/auth" rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac" "github.com/apache/servicecomb-service-center/server/service/rbac/token" - rbacmodel "github.com/go-chassis/cari/rbac" - "github.com/go-chassis/go-chassis/v2/security/authr" - "github.com/go-chassis/go-chassis/v2/server/restful" ) var ErrNoRoles = errors.New("no role found in token") +var tokenCache = cache.New(cacheDefaultExpireTime, cacheDefaultCleanUpTime) + +const cacheErrorItemExpTime = 5 * time.Minute +const cacheDefaultExpireTime = 5 * time.Minute +const cacheDefaultCleanUpTime = 10 * time.Minute func init() { plugin.RegisterPlugin(plugin.Plugin{Kind: auth.AUTH, Name: "buildin", New: New}) @@ -152,6 +161,15 @@ func (ba *TokenAuthenticator) VerifyToken(req *http.Request) (interface{}, error if v == "" { return nil, rbacmodel.NewError(rbacmodel.ErrNoAuthHeader, "") } + claims, ok := tokenCache.Get(v) + if ok { + switch claimsVal := claims.(type) { + case error: + return nil, claimsVal + default: + return claimsVal, nil + } + } s := strings.Split(v, " ") if len(s) != 2 { return nil, rbacmodel.ErrInvalidHeader @@ -160,24 +178,49 @@ func (ba *TokenAuthenticator) VerifyToken(req *http.Request) (interface{}, error claims, err := authr.Authenticate(req.Context(), to) if err != nil { + SetTokenToCache(tokenCache, v, err) return nil, err } + SetTokenToCache(tokenCache, v, claims) token.WithRequest(req, to) return claims, nil } +func SetTokenToCache(tokenCache *cache.Cache, rawToken string, claims interface{}) { + switch claimsVal := claims.(type) { + case error: + tokenCache.Set(rawToken, claimsVal, cacheErrorItemExpTime) + case map[string]interface{}: + var expr int64 + switch exp := claimsVal["exp"].(type) { + case float64: + expr = int64(exp) + case json.Number: + expr, _ = exp.Int64() + default: + expr = time.Now().Add(cacheDefaultExpireTime).Unix() + } + expDur := time.Until(time.Unix(expr, 0)) + if expDur > 0 { + tokenCache.Set(rawToken, claimsVal, expDur) + } + default: + return + } +} + // this method decouple business code and perm checks func checkPerm(roleList []string, req *http.Request) ([]map[string]string, error) { hasAdmin, normalRoles := filterRoles(roleList) if hasAdmin { return nil, nil } - //todo fast check for dev role + // todo fast check for dev role targetResource := FromRequest(req) if targetResource == nil { return nil, errors.New("no valid resouce scope") } - //TODO add project + // TODO add project project := req.URL.Query().Get(":project") return rbacsvc.Allow(req.Context(), project, normalRoles, targetResource) } diff --git a/server/plugin/auth/buildin/buildin_test.go b/server/plugin/auth/buildin/buildin_test.go index 1ae618985..fa2e146f1 100644 --- a/server/plugin/auth/buildin/buildin_test.go +++ b/server/plugin/auth/buildin/buildin_test.go @@ -20,18 +20,18 @@ package buildin_test // initialize import ( "context" + "errors" "net/http" "net/http/httptest" "os" "testing" + "time" + + "github.com/form3tech-oss/jwt-go" + "github.com/patrickmn/go-cache" _ "github.com/apache/servicecomb-service-center/test" - "github.com/apache/servicecomb-service-center/pkg/rest" - "github.com/apache/servicecomb-service-center/pkg/util" - "github.com/apache/servicecomb-service-center/server/config" - "github.com/apache/servicecomb-service-center/server/plugin/auth/buildin" - rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac" beego "github.com/beego/beego/v2/server/web" "github.com/go-chassis/cari/pkg/errsvc" carirbac "github.com/go-chassis/cari/rbac" @@ -41,6 +41,12 @@ import ( "github.com/go-chassis/go-chassis/v2/security/secret" "github.com/go-chassis/go-chassis/v2/server/restful" "github.com/stretchr/testify/assert" + + "github.com/apache/servicecomb-service-center/pkg/rest" + "github.com/apache/servicecomb-service-center/pkg/util" + "github.com/apache/servicecomb-service-center/server/config" + "github.com/apache/servicecomb-service-center/server/plugin/auth/buildin" + rbacsvc "github.com/apache/servicecomb-service-center/server/service/rbac" ) func init() { @@ -175,3 +181,19 @@ func TestTokenAuthenticator_Identify(t *testing.T) { } }) } + +func TestSetTokenToCache(t *testing.T) { + tokenCache1 := cache.New(5*time.Minute, 10*time.Minute) + rawToken1 := "**1" + rawToken2 := "**2" + deleta, _ := time.ParseDuration("10m") + claims := &jwt.MapClaims{"exp": float64(time.Now().Add(deleta).Unix())} + t.Run("test Cache", func(t *testing.T) { + buildin.SetTokenToCache(tokenCache1, rawToken1, claims) + buildin.SetTokenToCache(tokenCache1, rawToken2, errors.New("bad token")) + res1, _ := tokenCache1.Get(rawToken1) + res2, _ := tokenCache1.Get(rawToken2) + assert.NotNil(t, res1) + assert.NotNil(t, res2) + }) +}