build in role permission check (#661)
diff --git a/docs/user-guides/rbac.md b/docs/user-guides/rbac.md
index bce3491..bdc782d 100644
--- a/docs/user-guides/rbac.md
+++ b/docs/user-guides/rbac.md
@@ -61,14 +61,28 @@
```shell script
curl -X PUT \
http://127.0.0.1:30100/v4/account-password \
- -H 'Authorization: Bear eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50Ijoicm9vdCIsImV4cCI6MTU5MzMyOTE3OSwicm9sZSI6IiJ9.OR_uruuLds1wz10_J4gDEA-L9Ma_1RrHiKEA6CS-Nilv6hHB5KyhZ9_4qqf_c0iia4uKryAGHKsXUvrjOE51tz4QCXlgCmddrkYuLQsnDezXhV3TIqzdl4R_cy8h2cZo8O_b_q7eU2Iemd6x7BJE49SLgNiP5LTXCVct5Qm_GiXYTaM4dbHIJ01V-EPmNQuBr1vKdfNa8cqWtASSp9IEkFx1YpzhFacQgmfoiSGHvxQYZldQXuAh60ZXLBDexGu6jGnG39MqVNRysvHTpZRqxZWBhmEn5DeXpgKu-zlakJMjeEma4zcN-H0MumE-nMlBT5kjKWVr1DOdtOyJI6i786ZpS0wWHV4VOxpSursoKsW_XuTZCMM8LTBgdy5icCuHUXvvWXYJxPks9Pq3DcFjPlY3IuXyfokEWxGvrAF6jzglgSrNTiRkoNBKVktEapDyrpyWfktp22mhvWF6GuNoUzztxFPJblH-TXdudzWeqx-gV1lsRPSMsW8-oq6pxJfeb-b0PNM8vAIbwvv8an4T5iNMBZMz7J9NbpVCaj5eLcgfUXktyb8eWSfANhYMxY9kQN9dHZlkASAW-sjehi-rBXYJ8aCL4EbLzrYlmFWoN0z25dxvAxmWaPRQED3METYyZHvV_G4DSQf0cB2Oer_YdoRa6HWmxnTlz0HwPEq55PM' \
+ -H 'Authorization: Bear {your_token}' \
-d '{
"currentPassword":"rootpwd",
"password":"123"
}'
```
-### Roles TODO
-currently, you can not custom and manage any role and role policy. there is only 1 build in roles
+### create a new account by account which has admin role
+```shell script
+curl -X POST \
+ http://127.0.0.1:30100/v4/account \
+ -H 'Accept: */*' \
+ -H 'Authorization: Bear {your_token}' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "name":"peter",
+ "password":"{strong_password}",
+ "role":"developer"
+
+}'
+```
+### Roles
+currently, you can not custom and manage any role and role policy. there is only 2 build in roles. rbac feature is in early development stage.
- admin: able to do anything, including manage account, even change other account password
- service: able to call most of API except account management.
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 517f1a9..5e7d911 100644
--- a/go.mod
+++ b/go.mod
@@ -23,7 +23,7 @@
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-chassis/foundation v0.1.1-0.20191113114104-2b05871e9ec4
github.com/go-chassis/go-archaius v1.3.2
- github.com/go-chassis/go-chassis v0.0.0-20200624080301-8af281f0f75b
+ github.com/go-chassis/go-chassis v0.0.0-20200709095636-48e710908928
github.com/go-chassis/paas-lager v1.1.1
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
diff --git a/pkg/model/account.go b/pkg/errors/text.go
similarity index 69%
copy from pkg/model/account.go
copy to pkg/errors/text.go
index b1da8dd..a9c3e7b 100644
--- a/pkg/model/account.go
+++ b/pkg/errors/text.go
@@ -15,19 +15,12 @@
* limitations under the License.
*/
-package model
+package errors
const (
- RoleAdmin = "admin"
- RoleAuditor = "auditor"
-)
+ ErrMsgJSON = "json is invalid"
-type Account struct {
- Name string `json:"name,omitempty"`
- Password string `json:"password,omitempty"`
- Role string `json:"role,omitempty"`
- CurrentPassword string `json:"currentPassword,omitempty"`
-}
-type Token struct {
- TokenStr string `json:"token,omitempty"`
-}
+ ErrMsgCreateAccount = "create account failed"
+ ErrMsgRolePerm = "check role permissions failed"
+ ErrMsgNoPerm = "no permission to operate"
+)
diff --git a/pkg/model/account.go b/pkg/rbacframe/account.go
similarity index 80%
rename from pkg/model/account.go
rename to pkg/rbacframe/account.go
index b1da8dd..4b32c4f 100644
--- a/pkg/model/account.go
+++ b/pkg/rbacframe/account.go
@@ -15,19 +15,29 @@
* limitations under the License.
*/
-package model
+package rbacframe
const (
- RoleAdmin = "admin"
- RoleAuditor = "auditor"
+ RoleAdmin = "admin"
)
type Account struct {
Name string `json:"name,omitempty"`
Password string `json:"password,omitempty"`
Role string `json:"role,omitempty"`
+ TokenExpiryTime string `json:"tokenExpiryTime,omitempty"`
CurrentPassword string `json:"currentPassword,omitempty"`
}
+
type Token struct {
TokenStr string `json:"token,omitempty"`
}
+
+type Role struct {
+ Project []string
+ Permissions map[string]*Permission
+}
+type Permission struct {
+ IDs []string // TODO make IDs checked by rbac
+ Verbs []string
+}
diff --git a/pkg/rbacframe/api.go b/pkg/rbacframe/api.go
new file mode 100644
index 0000000..5f7c462
--- /dev/null
+++ b/pkg/rbacframe/api.go
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Package rbacframe help other component which want to use servicecomb rbac system
+package rbacframe
+
+import (
+ "context"
+ "crypto/rsa"
+ "github.com/go-chassis/go-chassis/security/token"
+)
+
+const (
+ ClaimsUser = "account"
+ ClaimsRole = "role"
+)
+
+func AccountFromContext(ctx context.Context) (*Account, error) {
+ claims := FromContext(ctx)
+ m, ok := claims.(map[string]interface{})
+ if !ok {
+ return nil, ErrInvalidCtx
+ }
+ accountNameI := m[ClaimsUser]
+ a, ok := accountNameI.(string)
+ if !ok {
+ return nil, ErrConvertErr
+ }
+ roleI := m[ClaimsRole]
+ role, ok := roleI.(string)
+ if !ok {
+ return nil, ErrConvertErr
+ }
+ account := &Account{Name: a, Role: role}
+ return account, nil
+}
+
+//RoleFromContext only return role name
+func RoleFromContext(ctx context.Context) (string, error) {
+ claims := FromContext(ctx)
+ m, ok := claims.(map[string]interface{})
+ if !ok {
+ return "", ErrInvalidCtx
+ }
+ roleI := m[ClaimsRole]
+ role, ok := roleI.(string)
+ if !ok {
+ return "", ErrConvertErr
+ }
+ return role, nil
+}
+
+//Authenticate parse a token to claims
+func Authenticate(tokenStr string, pub *rsa.PublicKey) (interface{}, error) {
+ claims, err := token.Verify(tokenStr, func(claims interface{}, method token.SigningMethod) (interface{}, error) {
+ return pub, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return claims, nil
+}
diff --git a/pkg/rbacframe/api_test.go b/pkg/rbacframe/api_test.go
new file mode 100644
index 0000000..02b9bf2
--- /dev/null
+++ b/pkg/rbacframe/api_test.go
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package rbacframe_test
+
+import (
+ "context"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
+ "github.com/go-chassis/go-chassis/security/secret"
+ "github.com/go-chassis/go-chassis/security/token"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestFromContext(t *testing.T) {
+ ctx := rbacframe.NewContext(context.TODO(), map[string]interface{}{
+ rbacframe.ClaimsUser: "root",
+ rbacframe.ClaimsRole: "admin",
+ })
+
+ c := rbacframe.FromContext(ctx)
+ claims := c.(map[string]interface{})
+ u := claims[rbacframe.ClaimsUser]
+ r := claims[rbacframe.ClaimsRole]
+ assert.Equal(t, "root", u)
+ assert.Equal(t, "admin", r)
+
+ a, err := rbacframe.AccountFromContext(ctx)
+ assert.NoError(t, err)
+ assert.Equal(t, "root", a.Name)
+ assert.Equal(t, "admin", a.Role)
+}
+
+func TestMustAuth(t *testing.T) {
+ rbacframe.Add2WhiteAPIList("/test")
+ assert.False(t, rbacframe.MustAuth("/test"))
+ assert.True(t, rbacframe.MustAuth("/auth"))
+}
+
+func TestAuthenticate(t *testing.T) {
+ pri, pub, err := secret.GenRSAKeyPair(4096)
+ assert.NoError(t, err)
+
+ to, err := token.Sign(map[string]interface{}{
+ rbacframe.ClaimsUser: "root",
+ rbacframe.ClaimsRole: "admin",
+ }, pri, token.WithSigningMethod(token.RS512))
+ assert.NoError(t, err)
+
+ _, err = rbacframe.Authenticate(to, pub)
+ assert.NoError(t, err)
+
+ _, err = rbacframe.Authenticate("token", nil)
+ assert.Error(t, err)
+}
diff --git a/server/service/rbac/context.go b/pkg/rbacframe/context.go
similarity index 85%
rename from server/service/rbac/context.go
rename to pkg/rbacframe/context.go
index 86d70c0..22b98ef 100644
--- a/server/service/rbac/context.go
+++ b/pkg/rbacframe/context.go
@@ -15,9 +15,12 @@
* limitations under the License.
*/
-package rbac
+package rbacframe
-import "context"
+import (
+ "context"
+ "k8s.io/apimachinery/pkg/util/sets"
+)
// key is an unexported type for keys defined in this package.
// This prevents collisions with keys defined in other packages.
@@ -38,3 +41,12 @@
a := ctx.Value(accountKey)
return a
}
+
+var whiteAPIList = sets.NewString()
+
+func Add2WhiteAPIList(path ...string) {
+ whiteAPIList.Insert(path...)
+}
+func MustAuth(url string) bool {
+ return !whiteAPIList.Has(url)
+}
diff --git a/pkg/model/account.go b/pkg/rbacframe/error.go
similarity index 68%
copy from pkg/model/account.go
copy to pkg/rbacframe/error.go
index b1da8dd..383206e 100644
--- a/pkg/model/account.go
+++ b/pkg/rbacframe/error.go
@@ -15,19 +15,17 @@
* limitations under the License.
*/
-package model
+package rbacframe
-const (
- RoleAdmin = "admin"
- RoleAuditor = "auditor"
+import (
+ "errors"
)
-type Account struct {
- Name string `json:"name,omitempty"`
- Password string `json:"password,omitempty"`
- Role string `json:"role,omitempty"`
- CurrentPassword string `json:"currentPassword,omitempty"`
-}
-type Token struct {
- TokenStr string `json:"token,omitempty"`
-}
+var (
+ ErrInvalidHeader = errors.New("invalid auth header")
+ ErrNoHeader = errors.New("should provide Authorization header")
+ ErrInvalidCtx = errors.New("invalid context")
+
+ ErrConvertErr = errors.New(MsgConvertErr)
+ MsgConvertErr = "type convert error"
+)
diff --git a/pkg/util/common.go b/pkg/util/common.go
index 2c92e57..a876bc9 100644
--- a/pkg/util/common.go
+++ b/pkg/util/common.go
@@ -25,7 +25,3 @@
CtxRequestRevision = "requestRev"
CtxResponseRevision = "responseRev"
)
-
-const (
- ErrMsgConvert = "type convert error"
-)
diff --git a/pkg/validate/matcher.go b/pkg/validate/matcher.go
new file mode 100644
index 0000000..333fd8c
--- /dev/null
+++ b/pkg/validate/matcher.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package validate
+
+import "unicode"
+
+type PasswordChecker struct {
+}
+
+func (p *PasswordChecker) MatchString(s string) bool {
+ var (
+ hasMinLen = false
+ hasUpper = false
+ hasLower = false
+ hasNumber = false
+ hasSpecial = false
+ )
+ if len(s) >= 8 {
+ hasMinLen = true
+ }
+ for _, char := range s {
+ switch {
+ case unicode.IsUpper(char):
+ hasUpper = true
+ case unicode.IsLower(char):
+ hasLower = true
+ case unicode.IsNumber(char):
+ hasNumber = true
+ case unicode.IsPunct(char) || unicode.IsSymbol(char):
+ hasSpecial = true
+ }
+ }
+ return hasMinLen && hasUpper && hasLower && hasNumber && hasSpecial
+}
+func (p *PasswordChecker) String() string {
+ return "password"
+}
diff --git a/pkg/validate/validator.go b/pkg/validate/validator.go
index 1720a95..6eb1650 100644
--- a/pkg/validate/validator.go
+++ b/pkg/validate/validator.go
@@ -26,8 +26,8 @@
)
type Validator struct {
- rules map[string](*Rule)
- subs map[string](*Validator)
+ rules map[string]*Rule
+ subs map[string]*Validator
once sync.Once
}
diff --git a/server/plugin/auth/auth.go b/server/plugin/auth/auth.go
index f55a806..19a33e2 100644
--- a/server/plugin/auth/auth.go
+++ b/server/plugin/auth/auth.go
@@ -18,12 +18,9 @@
package auth
import (
- "errors"
"net/http"
)
-var ErrNoHeader = errors.New("should provide Authorization header")
-
type Auth interface {
Identify(r *http.Request) error
}
diff --git a/server/plugin/auth/buildin/buidlin_test.go b/server/plugin/auth/buildin/buidlin_test.go
index e86761e..d1b5a99 100644
--- a/server/plugin/auth/buildin/buidlin_test.go
+++ b/server/plugin/auth/buildin/buidlin_test.go
@@ -18,17 +18,21 @@
package buildin_test
import (
+ "context"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
mgr "github.com/apache/servicecomb-service-center/server/plugin"
- "github.com/apache/servicecomb-service-center/server/plugin/auth"
"github.com/apache/servicecomb-service-center/server/plugin/auth/buildin"
"github.com/apache/servicecomb-service-center/server/plugin/discovery/etcd"
etcd2 "github.com/apache/servicecomb-service-center/server/plugin/registry/etcd"
plain "github.com/apache/servicecomb-service-center/server/plugin/security/buildin"
"github.com/apache/servicecomb-service-center/server/plugin/tracing/pzipkin"
"github.com/apache/servicecomb-service-center/server/service/rbac"
+ "github.com/apache/servicecomb-service-center/server/service/rbac/dao"
"github.com/astaxie/beego"
"github.com/go-chassis/go-archaius"
+ "github.com/go-chassis/go-chassis/security/authr"
"github.com/go-chassis/go-chassis/security/secret"
+ "github.com/go-chassis/go-chassis/server/restful"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
@@ -49,6 +53,8 @@
}
func TestTokenAuthenticator_Identify(t *testing.T) {
+ dao.DeleteAccount(context.TODO(), "root")
+ dao.DeleteAccount(context.TODO(), "non-admin")
t.Run("init rbac", func(t *testing.T) {
err := archaius.Init(archaius.WithMemorySource(), archaius.WithENVSource())
assert.NoError(t, err)
@@ -69,8 +75,46 @@
})
a := buildin.New()
ta := a.(*buildin.TokenAuthenticator)
- r := httptest.NewRequest(http.MethodGet, "/any", nil)
- err := ta.Identify(r)
- t.Log(err)
- assert.Equal(t, auth.ErrNoHeader, err)
+
+ t.Run("without auth header should failed", func(t *testing.T) {
+ r := httptest.NewRequest(http.MethodGet, "/any", nil)
+ err := ta.Identify(r)
+ t.Log(err)
+ assert.Equal(t, rbacframe.ErrNoHeader, err)
+ })
+
+ t.Run("with wrong auth header should failed", func(t *testing.T) {
+ r := httptest.NewRequest(http.MethodGet, "/any", nil)
+ r.Header.Set(restful.HeaderAuth, "Bear")
+ err := ta.Identify(r)
+ t.Log(err)
+ assert.Equal(t, rbacframe.ErrInvalidHeader, err)
+ })
+
+ t.Run("with valid header and invalid token, should failed", func(t *testing.T) {
+ r := httptest.NewRequest(http.MethodGet, "/any", nil)
+ r.Header.Set(restful.HeaderAuth, "Bear fake_token")
+ err := ta.Identify(r)
+ t.Log(err)
+ assert.Error(t, err)
+ })
+ t.Run("valid admin token, should be able to operate account", func(t *testing.T) {
+ r := httptest.NewRequest(http.MethodGet, "/v4/account", nil)
+ to, err := authr.Login(context.TODO(), "root", "root")
+ assert.NoError(t, err)
+ r.Header.Set(restful.HeaderAuth, "Bear "+to)
+ err = ta.Identify(r)
+ assert.NoError(t, err)
+ })
+ t.Run("valid normal token, should no be able to operate account", func(t *testing.T) {
+ err := dao.CreateAccount(context.TODO(), &rbacframe.Account{Name: "non-admin", Password: "123", Role: "developer"})
+ assert.NoError(t, err)
+ r := httptest.NewRequest(http.MethodGet, "/v4/account", nil)
+ to, err := authr.Login(context.TODO(), "non-admin", "123")
+ assert.NoError(t, err)
+ r.Header.Set(restful.HeaderAuth, "Bear "+to)
+ err = ta.Identify(r)
+ t.Log(err)
+ assert.Error(t, err)
+ })
}
diff --git a/server/plugin/auth/buildin/buildin.go b/server/plugin/auth/buildin/buildin.go
index 3f597f7..d3cf120 100644
--- a/server/plugin/auth/buildin/buildin.go
+++ b/server/plugin/auth/buildin/buildin.go
@@ -18,11 +18,14 @@
package buildin
import (
+ "context"
"errors"
+ errorsEx "github.com/apache/servicecomb-service-center/pkg/errors"
"github.com/apache/servicecomb-service-center/pkg/log"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
mgr "github.com/apache/servicecomb-service-center/server/plugin"
- "github.com/apache/servicecomb-service-center/server/plugin/auth"
"github.com/apache/servicecomb-service-center/server/service/rbac"
+ "github.com/apache/servicecomb-service-center/server/service/rbac/dao"
"github.com/go-chassis/go-chassis/security/authr"
"github.com/go-chassis/go-chassis/server/restful"
"net/http"
@@ -44,34 +47,48 @@
if !rbac.Enabled() {
return nil
}
- if !mustAuth(req) {
+ if !rbacframe.MustAuth(req.URL.Path) {
return nil
}
v := req.Header.Get(restful.HeaderAuth)
if v == "" {
- return auth.ErrNoHeader
+ return rbacframe.ErrNoHeader
}
s := strings.Split(v, " ")
if len(s) != 2 {
- return errors.New("invalid auth header")
+ return rbacframe.ErrInvalidHeader
}
to := s[1]
- //TODO rbac
+
claims, err := authr.Authenticate(req.Context(), to)
if err != nil {
log.Errorf(err, "authenticate request failed, %s %s", req.Method, req.RequestURI)
return err
}
- req2 := req.WithContext(rbac.NewContext(req.Context(), claims))
+ //TODO rbac
+ m, ok := claims.(map[string]interface{})
+ if !ok {
+ log.Error("claims convert failed", rbacframe.ErrConvertErr)
+ return rbacframe.ErrConvertErr
+ }
+ role := m[rbacframe.ClaimsRole]
+ roleName, ok := role.(string)
+ if !ok {
+ log.Error("role convert failed", rbacframe.ErrConvertErr)
+ return rbacframe.ErrConvertErr
+ }
+ r := dao.GetResource(context.TODO(), req.URL.Path)
+ //TODO add verbs
+ allow, err := rbac.Allow(context.TODO(), roleName, req.URL.Query().Get(":project"), r, "")
+ if err != nil {
+ log.Error("", err)
+ return errors.New(errorsEx.ErrMsgRolePerm)
+ }
+ if !allow {
+ return errors.New(errorsEx.ErrMsgNoPerm)
+ }
+ req2 := req.WithContext(rbacframe.NewContext(req.Context(), claims))
*req = *req2
return nil
}
-func mustAuth(req *http.Request) bool {
- for v := range rbac.WhiteAPIList() {
- if strings.Contains(req.URL.Path, v) {
- return false
- }
- }
- return true
-}
diff --git a/server/rest/controller/v4/auth_resource.go b/server/rest/controller/v4/auth_resource.go
index 0159693..8e0e1e6 100644
--- a/server/rest/controller/v4/auth_resource.go
+++ b/server/rest/controller/v4/auth_resource.go
@@ -20,14 +20,15 @@
import (
"context"
"encoding/json"
- "errors"
+ errorsEx "github.com/apache/servicecomb-service-center/pkg/errors"
"github.com/apache/servicecomb-service-center/pkg/log"
- "github.com/apache/servicecomb-service-center/pkg/model"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
"github.com/apache/servicecomb-service-center/pkg/rest"
- util2 "github.com/apache/servicecomb-service-center/pkg/util"
"github.com/apache/servicecomb-service-center/server/rest/controller"
"github.com/apache/servicecomb-service-center/server/scerror"
+ "github.com/apache/servicecomb-service-center/server/service"
"github.com/apache/servicecomb-service-center/server/service/rbac"
+ "github.com/apache/servicecomb-service-center/server/service/rbac/dao"
"github.com/go-chassis/go-chassis/security/authr"
"io/ioutil"
"net/http"
@@ -40,9 +41,35 @@
func (r *AuthResource) URLPatterns() []rest.Route {
return []rest.Route{
{Method: http.MethodPost, Path: "/v4/token", Func: r.Login},
+ {Method: http.MethodPost, Path: "/v4/account", Func: r.CreateAccount},
{Method: http.MethodPut, Path: "/v4/account-password", Func: r.ChangePassword},
}
}
+func (r *AuthResource) CreateAccount(w http.ResponseWriter, req *http.Request) {
+ body, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ log.Error("read body err", err)
+ controller.WriteError(w, scerror.ErrInternal, err.Error())
+ return
+ }
+ a := &rbacframe.Account{}
+ if err = json.Unmarshal(body, a); err != nil {
+ log.Error("json err", err)
+ controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.ErrMsgJSON)
+ return
+ }
+ err = service.ValidateCreateAccount(a)
+ if err != nil {
+ controller.WriteError(w, scerror.ErrInvalidParams, err.Error())
+ return
+ }
+ err = dao.CreateAccount(context.TODO(), a)
+ if err != nil {
+ log.Error(errorsEx.ErrMsgCreateAccount, err)
+ controller.WriteError(w, scerror.ErrInternal, errorsEx.ErrMsgCreateAccount)
+ return
+ }
+}
func (r *AuthResource) ChangePassword(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
@@ -50,48 +77,30 @@
controller.WriteError(w, scerror.ErrInternal, err.Error())
return
}
- a := &model.Account{}
+ a := &rbacframe.Account{}
if err = json.Unmarshal(body, a); err != nil {
log.Error("json err", err)
+ controller.WriteError(w, scerror.ErrInvalidParams, errorsEx.ErrMsgJSON)
+ return
+ }
+ err = service.ValidateChangePWD(a)
+ if err != nil {
controller.WriteError(w, scerror.ErrInvalidParams, err.Error())
return
}
- if a.Password == "" {
- controller.WriteError(w, scerror.ErrInvalidParams, "new password is empty")
+ changer, err := rbacframe.AccountFromContext(req.Context())
+ if err != nil {
+ controller.WriteError(w, scerror.ErrInternal, "can not parse account info")
return
}
- claims := rbac.FromContext(req.Context())
- m, ok := claims.(map[string]interface{})
- if !ok {
- log.Error("claims convert failed", errors.New(util2.ErrMsgConvert))
- controller.WriteError(w, scerror.ErrInvalidParams, util2.ErrMsgConvert)
- return
- }
- accountNameI := m[rbac.ClaimsUser]
- changer, ok := accountNameI.(string)
- if !ok {
- log.Error("claims convert failed", errors.New(util2.ErrMsgConvert))
- controller.WriteError(w, scerror.ErrInternal, util2.ErrMsgConvert)
- return
- }
- roleI := m[rbac.ClaimsRole]
- role, ok := roleI.(string)
- if !ok {
- log.Error("claims convert failed", errors.New(util2.ErrMsgConvert))
- controller.WriteError(w, scerror.ErrInternal, util2.ErrMsgConvert)
- return
- }
- if role == "" {
- controller.WriteError(w, scerror.ErrInvalidParams, "role is empty")
- return
- }
- err = rbac.ChangePassword(context.TODO(), role, changer, a)
+ err = rbac.ChangePassword(context.TODO(), changer.Role, changer.Name, a)
if err != nil {
log.Error("change password failed", err)
controller.WriteError(w, scerror.ErrInternal, err.Error())
return
}
}
+
func (r *AuthResource) Login(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
@@ -99,7 +108,7 @@
controller.WriteError(w, scerror.ErrInternal, err.Error())
return
}
- a := &model.Account{}
+ a := &rbacframe.Account{}
if err = json.Unmarshal(body, a); err != nil {
log.Error("json err", err)
controller.WriteError(w, scerror.ErrInvalidParams, err.Error())
@@ -116,7 +125,7 @@
controller.WriteError(w, scerror.ErrInternal, err.Error())
return
}
- to := &model.Token{TokenStr: t}
+ to := &rbacframe.Token{TokenStr: t}
b, err := json.Marshal(to)
if err != nil {
log.Error("json err", err)
diff --git a/server/service/rbac/authr_plugin.go b/server/service/rbac/authr_plugin.go
index 50518ba..65ed1ce 100644
--- a/server/service/rbac/authr_plugin.go
+++ b/server/service/rbac/authr_plugin.go
@@ -21,6 +21,7 @@
"context"
"errors"
"github.com/apache/servicecomb-service-center/pkg/log"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
"github.com/apache/servicecomb-service-center/server/service/cipher"
"github.com/apache/servicecomb-service-center/server/service/rbac/dao"
"github.com/dgrijalva/jwt-go"
@@ -30,11 +31,6 @@
var ErrUnauthorized = errors.New("wrong user name or password")
-const (
- ClaimsUser = "account"
- ClaimsRole = "role"
-)
-
//EmbeddedAuthenticator is sc default auth plugin, RBAC data is persisted in etcd
type EmbeddedAuthenticator struct {
}
@@ -62,8 +58,8 @@
return "", err
}
tokenStr, err := token.Sign(map[string]interface{}{
- ClaimsUser: user,
- ClaimsRole: account.Role, //TODO more claims for RBAC, for example rule config
+ rbacframe.ClaimsUser: user,
+ rbacframe.ClaimsRole: account.Role, //TODO more claims for RBAC, for example rule config
},
secret,
token.WithExpTime("30m"),
@@ -77,20 +73,14 @@
return "", ErrUnauthorized
}
func (a *EmbeddedAuthenticator) Authenticate(ctx context.Context, tokenStr string) (interface{}, error) {
- claims, err := token.Verify(tokenStr, func(claims interface{}, method token.SigningMethod) (interface{}, error) {
- p, err := jwt.ParseRSAPublicKeyFromPEM([]byte(PublicKey()))
- if err != nil {
- log.Error("can not parse public key", err)
- return nil, err
- }
- return p, nil
- })
+ p, err := jwt.ParseRSAPublicKeyFromPEM([]byte(PublicKey()))
if err != nil {
- log.Error("verify token failed", err)
+ log.Error("can not parse public key", err)
return nil, err
}
- return claims, nil
+ return rbacframe.Authenticate(tokenStr, p)
}
+
func init() {
authr.Install("default", newEmbeddedAuthenticator)
}
diff --git a/server/service/rbac/dao/account_dao.go b/server/service/rbac/dao/account_dao.go
index 71dca8f..4287949 100644
--- a/server/service/rbac/dao/account_dao.go
+++ b/server/service/rbac/dao/account_dao.go
@@ -25,7 +25,7 @@
"fmt"
"github.com/apache/servicecomb-service-center/pkg/etcdsync"
"github.com/apache/servicecomb-service-center/pkg/log"
- "github.com/apache/servicecomb-service-center/pkg/model"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
"github.com/apache/servicecomb-service-center/server/core"
"github.com/apache/servicecomb-service-center/server/service/kv"
)
@@ -35,7 +35,7 @@
//CreateAccount save 2 kv
//1. account info
-func CreateAccount(ctx context.Context, a *model.Account) error {
+func CreateAccount(ctx context.Context, a *rbacframe.Account) error {
lock, err := etcdsync.Lock("/account-creating/"+a.Name, -1, false)
if err != nil {
return fmt.Errorf("account %s is creating", a.Name)
@@ -70,14 +70,14 @@
return nil
}
-func GetAccount(ctx context.Context, name string) (*model.Account, error) {
+func GetAccount(ctx context.Context, name string) (*rbacframe.Account, error) {
key := core.GenerateAccountKey(name)
r, err := kv.Get(ctx, key)
if err != nil {
log.Errorf(err, "can not get account info")
return nil, err
}
- a := &model.Account{}
+ a := &rbacframe.Account{}
err = json.Unmarshal(r.Value, a)
if err != nil {
log.Errorf(err, "account info is invalid")
@@ -104,7 +104,7 @@
//CreateAccount save 2 kv
//1. account info
-func EditAccount(ctx context.Context, a *model.Account) error {
+func EditAccount(ctx context.Context, a *rbacframe.Account) error {
key := core.GenerateAccountKey(a.Name)
exist, err := kv.Exist(ctx, key)
if err != nil {
diff --git a/server/service/rbac/dao/account_dao_test.go b/server/service/rbac/dao/account_dao_test.go
index 7d6cd22..31b3e74 100644
--- a/server/service/rbac/dao/account_dao_test.go
+++ b/server/service/rbac/dao/account_dao_test.go
@@ -19,7 +19,7 @@
import (
"context"
- "github.com/apache/servicecomb-service-center/pkg/model"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
mgr "github.com/apache/servicecomb-service-center/server/plugin"
"github.com/apache/servicecomb-service-center/server/plugin/discovery/etcd"
etcd2 "github.com/apache/servicecomb-service-center/server/plugin/registry/etcd"
@@ -39,7 +39,7 @@
}
func TestAccountDao_CreateAccount(t *testing.T) {
- _ = dao.CreateAccount(context.Background(), &model.Account{Name: "admin", Password: "pwd"})
+ _ = dao.CreateAccount(context.Background(), &rbacframe.Account{Name: "admin", Password: "pwd"})
t.Run("get account", func(t *testing.T) {
r, err := dao.GetAccount(context.Background(), "admin")
assert.NoError(t, err)
diff --git a/pkg/model/account.go b/server/service/rbac/dao/resource_dao.go
similarity index 63%
copy from pkg/model/account.go
copy to server/service/rbac/dao/resource_dao.go
index b1da8dd..f0b01da 100644
--- a/pkg/model/account.go
+++ b/server/service/rbac/dao/resource_dao.go
@@ -15,19 +15,22 @@
* limitations under the License.
*/
-package model
+package dao
-const (
- RoleAdmin = "admin"
- RoleAuditor = "auditor"
-)
+import "context"
-type Account struct {
- Name string `json:"name,omitempty"`
- Password string `json:"password,omitempty"`
- Role string `json:"role,omitempty"`
- CurrentPassword string `json:"currentPassword,omitempty"`
+//TODO save to etcd
+//TODO now simply write dead code "*" to map all other API except account and role to service, should define resource for every API in future
+var resourceMap = map[string]string{
+ "/v4/account": "account",
+ "/v4/role": "account",
+ "*": "service",
}
-type Token struct {
- TokenStr string `json:"token,omitempty"`
+
+func GetResource(ctx context.Context, API string) string {
+ r, ok := resourceMap[API]
+ if !ok {
+ return resourceMap["*"]
+ }
+ return r
}
diff --git a/pkg/model/account.go b/server/service/rbac/dao/role_dao.go
similarity index 61%
copy from pkg/model/account.go
copy to server/service/rbac/dao/role_dao.go
index b1da8dd..a31c486 100644
--- a/pkg/model/account.go
+++ b/server/service/rbac/dao/role_dao.go
@@ -15,19 +15,24 @@
* limitations under the License.
*/
-package model
+package dao
-const (
- RoleAdmin = "admin"
- RoleAuditor = "auditor"
+import (
+ "context"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
)
-type Account struct {
- Name string `json:"name,omitempty"`
- Password string `json:"password,omitempty"`
- Role string `json:"role,omitempty"`
- CurrentPassword string `json:"currentPassword,omitempty"`
+//TODO delete dead code and use etcd
+var roleMap = map[string]*rbacframe.Role{
+ "admin": {Permissions: map[string]*rbacframe.Permission{
+ "account": {Verbs: []string{"*"}},
+ "service": {Verbs: []string{"*"}},
+ }},
+ "developer": {Permissions: map[string]*rbacframe.Permission{
+ "service": {Verbs: []string{"*"}},
+ }},
}
-type Token struct {
- TokenStr string `json:"token,omitempty"`
+
+func GetRole(ctx context.Context, role string) *rbacframe.Role {
+ return roleMap[role]
}
diff --git a/server/service/rbac/decision.go b/server/service/rbac/decision.go
new file mode 100644
index 0000000..6df1206
--- /dev/null
+++ b/server/service/rbac/decision.go
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package rbac
+
+import (
+ "context"
+ "github.com/apache/servicecomb-service-center/pkg/log"
+ "github.com/apache/servicecomb-service-center/server/service/rbac/dao"
+)
+
+func Allow(ctx context.Context, role, project, resource, verbs string) (bool, error) {
+ r := dao.GetRole(ctx, role)
+ if r == nil {
+ log.Warn("empty role info")
+ return false, nil
+ }
+ ps := r.Permissions
+ if len(ps) == 0 {
+ log.Warn("role has no any permissions")
+ return false, nil
+ }
+ p, ok := ps[resource]
+ if !ok || p == nil {
+ log.Warn("role is not allowed to access resource")
+ return false, nil
+ }
+ //TODO check verbs and project
+ return true, nil
+}
diff --git a/server/service/rbac/decision_test.go b/server/service/rbac/decision_test.go
new file mode 100644
index 0000000..5dc1bce
--- /dev/null
+++ b/server/service/rbac/decision_test.go
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package rbac_test
+
+import (
+ "context"
+ "github.com/apache/servicecomb-service-center/server/service/rbac"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestAllow(t *testing.T) {
+ t.Run("admin can operate any resource", func(t *testing.T) {
+ ok, _ := rbac.Allow(context.TODO(), "admin", "default", "account", "create")
+ assert.True(t, ok)
+ ok, _ = rbac.Allow(context.TODO(), "admin", "default", "service", "create")
+ assert.True(t, ok)
+ })
+ t.Run("developer can not operate account", func(t *testing.T) {
+ ok, _ := rbac.Allow(context.TODO(), "developer", "default", "account", "create")
+ assert.False(t, ok)
+ })
+ t.Run("developer can not operate service", func(t *testing.T) {
+ ok, _ := rbac.Allow(context.TODO(), "developer", "default", "service", "create")
+ assert.True(t, ok)
+ })
+}
diff --git a/server/service/rbac/password.go b/server/service/rbac/password.go
index 604bc79..6e10702 100644
--- a/server/service/rbac/password.go
+++ b/server/service/rbac/password.go
@@ -20,15 +20,15 @@
import (
"context"
"errors"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
"github.com/apache/servicecomb-service-center/pkg/log"
- "github.com/apache/servicecomb-service-center/pkg/model"
"github.com/apache/servicecomb-service-center/server/service/rbac/dao"
)
-func ChangePassword(ctx context.Context, changerRole, changerName string, a *model.Account) error {
+func ChangePassword(ctx context.Context, changerRole, changerName string, a *rbacframe.Account) error {
if a.Name != "" {
- if changerRole != model.RoleAdmin { //need to check password mismatch. but admin role can change any user password without supply current password
+ if changerRole != rbacframe.RoleAdmin { //need to check password mismatch. but admin role can change any user password without supply current password
log.Error("can not change other account pwd", nil)
return ErrInputChangeAccount
}
@@ -65,6 +65,9 @@
log.Error("current pwd is wrong", nil)
return errors.New("can not change pwd")
}
+ if currentPassword == pwd {
+ return errors.New("the password can not be same as old one")
+ }
old.Password = pwd
err = dao.EditAccount(ctx, old)
if err != nil {
diff --git a/server/service/rbac/rbac.go b/server/service/rbac/rbac.go
index 54cf122..95f34f1 100644
--- a/server/service/rbac/rbac.go
+++ b/server/service/rbac/rbac.go
@@ -22,7 +22,7 @@
"crypto/rsa"
"errors"
"github.com/apache/servicecomb-service-center/pkg/log"
- "github.com/apache/servicecomb-service-center/pkg/model"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
"github.com/apache/servicecomb-service-center/server/service/cipher"
"github.com/apache/servicecomb-service-center/server/service/rbac/dao"
"github.com/astaxie/beego"
@@ -30,7 +30,6 @@
"github.com/go-chassis/go-chassis/security/authr"
"github.com/go-chassis/go-chassis/security/secret"
"io/ioutil"
- "k8s.io/apimachinery/pkg/util/sets"
)
const (
@@ -64,6 +63,7 @@
}
readPrivateKey()
readPublicKey()
+ rbacframe.Add2WhiteAPIList("/health", "/version", "/v4/token")
log.Info("rbac is enabled")
}
@@ -104,10 +104,10 @@
if pwd == "" {
log.Fatal("can not enable rbac, password is empty", nil)
}
- if err := dao.CreateAccount(context.Background(), &model.Account{
+ if err := dao.CreateAccount(context.Background(), &rbacframe.Account{
Name: admin,
Password: pwd,
- Role: model.RoleAdmin,
+ Role: rbacframe.RoleAdmin,
}); err != nil {
if err == dao.ErrDuplicated {
log.Info("rbac is enabled")
@@ -150,12 +150,3 @@
}
return p, nil
}
-
-var whiteAPIList = sets.NewString("/health", "/version", "/v4/token")
-
-func Add2WhiteAPIList(path string) {
- whiteAPIList.Insert(path)
-}
-func WhiteAPIList() sets.String {
- return whiteAPIList
-}
diff --git a/server/service/rbac/rbca_test.go b/server/service/rbac/rbca_test.go
index 805580c..6481535 100644
--- a/server/service/rbac/rbca_test.go
+++ b/server/service/rbac/rbca_test.go
@@ -20,7 +20,7 @@
import (
"context"
"fmt"
- "github.com/apache/servicecomb-service-center/pkg/model"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
mgr "github.com/apache/servicecomb-service-center/server/plugin"
"github.com/apache/servicecomb-service-center/server/plugin/discovery/etcd"
etcd2 "github.com/apache/servicecomb-service-center/server/plugin/registry/etcd"
@@ -80,7 +80,7 @@
fmt.Println("token:", token)
claims, err := authr.Authenticate(context.Background(), token)
assert.NoError(t, err)
- assert.Equal(t, "root", claims.(map[string]interface{})[rbac.ClaimsUser])
+ assert.Equal(t, "root", claims.(map[string]interface{})[rbacframe.ClaimsUser])
})
t.Run("second time init", func(t *testing.T) {
@@ -88,16 +88,16 @@
})
t.Run("change pwd,admin can change any one password", func(t *testing.T) {
- dao.CreateAccount(context.Background(), &model.Account{Name: "a", Password: "123"})
- err := rbac.ChangePassword(context.Background(), model.RoleAdmin, "admin", &model.Account{Name: "a", Password: "1234"})
+ dao.CreateAccount(context.Background(), &rbacframe.Account{Name: "a", Password: "123"})
+ err := rbac.ChangePassword(context.Background(), rbacframe.RoleAdmin, "admin", &rbacframe.Account{Name: "a", Password: "1234"})
assert.NoError(t, err)
a, err := dao.GetAccount(context.Background(), "a")
assert.NoError(t, err)
assert.Equal(t, "1234", a.Password)
})
t.Run("change self password", func(t *testing.T) {
- dao.CreateAccount(context.Background(), &model.Account{Name: "b", Password: "123"})
- err := rbac.ChangePassword(context.Background(), "", "b", &model.Account{CurrentPassword: "123", Password: "1234"})
+ dao.CreateAccount(context.Background(), &rbacframe.Account{Name: "b", Password: "123"})
+ err := rbac.ChangePassword(context.Background(), "", "b", &rbacframe.Account{CurrentPassword: "123", Password: "1234"})
assert.NoError(t, err)
a, err := dao.GetAccount(context.Background(), "b")
assert.NoError(t, err)
diff --git a/server/service/validate.go b/server/service/validate.go
index 61d5d59..1f83ec2 100644
--- a/server/service/validate.go
+++ b/server/service/validate.go
@@ -20,17 +20,30 @@
import (
"errors"
"github.com/apache/servicecomb-service-center/pkg/log"
+ "github.com/apache/servicecomb-service-center/pkg/rbacframe"
+ "github.com/apache/servicecomb-service-center/pkg/validate"
pb "github.com/apache/servicecomb-service-center/server/core/proto"
"reflect"
+ "regexp"
)
+var createAccountValidator = &validate.Validator{}
+var changePWDValidator = &validate.Validator{}
+
+func init() {
+ var roleRegex, _ = regexp.Compile(`^$|^(admin|developer)$`)
+ var accountRegex, _ = regexp.Compile(`^[a-zA-Z]\w{5,15}$`)
+ createAccountValidator.AddRule("Name", &validate.Rule{Regexp: accountRegex})
+ createAccountValidator.AddRule("Role", &validate.Rule{Regexp: roleRegex})
+ createAccountValidator.AddRule("Password", &validate.Rule{Regexp: &validate.PasswordChecker{}})
+
+ changePWDValidator.AddRule("CurrentPassword", &validate.Rule{Min: 8})
+ changePWDValidator.AddRule("Password", &validate.Rule{Regexp: &validate.PasswordChecker{}})
+}
func Validate(v interface{}) error {
- if v == nil {
- return errors.New("data is nil")
- }
- sv := reflect.ValueOf(v)
- if sv.Kind() == reflect.Ptr && sv.IsNil() {
- return errors.New("pointer is nil")
+ err := baseCheck(v)
+ if err != nil {
+ return err
}
switch t := v.(type) {
case *pb.CreateServiceRequest:
@@ -41,12 +54,10 @@
return GetServiceReqValidator().Validate(v)
case *pb.UpdateServicePropsRequest:
return UpdateServicePropsReqValidator().Validate(v)
-
case *pb.CreateDependenciesRequest:
return CreateDependenciesReqValidator().Validate(v)
case *pb.AddDependenciesRequest:
return AddDependenciesReqValidator().Validate(v)
-
case *pb.GetServiceTagsRequest:
return GetTagsReqValidator().Validate(v)
case *pb.AddServiceTagsRequest:
@@ -55,7 +66,6 @@
return UpdateTagReqValidator().Validate(v)
case *pb.DeleteServiceTagsRequest:
return DeleteTagReqValidator().Validate(v)
-
case *pb.GetAllSchemaRequest:
return GetSchemaReqValidator().Validate(v)
case *pb.GetSchemaRequest,
@@ -65,7 +75,6 @@
return ModifySchemaReqValidator().Validate(v)
case *pb.ModifySchemasRequest:
return ModifySchemasReqValidator().Validate(v)
-
case *pb.GetOneInstanceRequest,
*pb.GetInstancesRequest:
return GetInstanceReqValidator().Validate(v)
@@ -81,7 +90,6 @@
return HeartbeatReqValidator().Validate(v)
case *pb.UpdateInstancePropsRequest:
return UpdateInstancePropsReqValidator().Validate(v)
-
case *pb.GetServiceRulesRequest:
return GetRulesReqValidator().Validate(v)
case *pb.AddServiceRulesRequest:
@@ -90,7 +98,6 @@
return UpdateRuleReqValidator().Validate(v)
case *pb.DeleteServiceRulesRequest:
return DeleteRulesReqValidator().Validate(v)
-
case *pb.GetAppsRequest:
return MicroServiceKeyValidator().Validate(v)
default:
@@ -98,3 +105,28 @@
return nil
}
}
+
+func baseCheck(v interface{}) error {
+ if v == nil {
+ return errors.New("data is nil")
+ }
+ sv := reflect.ValueOf(v)
+ if sv.Kind() == reflect.Ptr && sv.IsNil() {
+ return errors.New("pointer is nil")
+ }
+ return nil
+}
+func ValidateCreateAccount(a *rbacframe.Account) error {
+ err := baseCheck(a)
+ if err != nil {
+ return err
+ }
+ return createAccountValidator.Validate(a)
+}
+func ValidateChangePWD(a *rbacframe.Account) error {
+ err := baseCheck(a)
+ if err != nil {
+ return err
+ }
+ return changePWDValidator.Validate(a)
+}