test: (rbac) validate index and cluster unit test

This commit is contained in:
xushuhui 2022-04-27 14:22:12 +08:00
parent 598c655a44
commit e865ee5f67
8 changed files with 173 additions and 222 deletions

View File

@ -1,8 +1,8 @@
{ {
"DELETE-/_ingest/pipeline/:id": "ingest.delete_pipeline", "DELETE-/_ingest/pipeline/:id": "ingest.delete_pipeline",
"DELETE-/_scripts/:id": "script.delete", "DELETE-/_scripts/:id": "script.delete",
"DELETE-/_search/scroll": "clear_scroll", "DELETE-/_search/scroll": "scroll.delete",
"DELETE-/_search/scroll/:scroll_id": "clear_scroll", "DELETE-/_search/scroll/:scroll_id":"scroll.delete",
"DELETE-/_snapshot/:repository": "snapshot.delete_repository", "DELETE-/_snapshot/:repository": "snapshot.delete_repository",
"DELETE-/_snapshot/:repository/:snapshot": "snapshot.delete", "DELETE-/_snapshot/:repository/:snapshot": "snapshot.delete",
"DELETE-/_template/:name": "indices.delete_template", "DELETE-/_template/:name": "indices.delete_template",
@ -102,10 +102,10 @@
"GET-/_scripts/painless/_execute": "scripts.painless_execute", "GET-/_scripts/painless/_execute": "scripts.painless_execute",
"GET-/_scripts/:id": "scripts.get", "GET-/_scripts/:id": "scripts.get",
"GET-/_search": "cluster.search", "GET-/_search": "cluster.search",
"GET-/_search/scroll": "scroll", "GET-/_search/scroll": "scroll.get",
"GET-/_search/scroll/:scroll_id": "scroll", "GET-/_search/scroll/:scroll_id": "scroll.get",
"GET-/_search/template": "search_template", "GET-/_search/template": "search_template",
"GET-/_search_shards": "search_shards", "GET-/_search_shards": "cluster.search_shards",
"GET-/_segments": "indices.segments", "GET-/_segments": "indices.segments",
"GET-/_settings": "indices.get_settings", "GET-/_settings": "indices.get_settings",
"GET-/_settings/:name": "indices.get_settings", "GET-/_settings/:name": "indices.get_settings",
@ -192,16 +192,16 @@
"POST-/_refresh": "indices.refresh", "POST-/_refresh": "indices.refresh",
"POST-/_reindex": "reindex", "POST-/_reindex": "reindex",
"POST-/_reindex/:task_id/_rethrottle": "reindex_rethrottle", "POST-/_reindex/:task_id/_rethrottle": "reindex_rethrottle",
"POST-/_render/template": "render_search_template", "POST-/_render/template": "render_search_template.create",
"POST-/_render/template/:id": "render_search_template", "POST-/_render/template/:id": "render_search_template.get",
"POST-/_scripts/painless/_execute": "scripts_painless_execute", "POST-/_scripts/painless/_execute": "scripts_painless_execute",
"POST-/_scripts/:id": "put_script", "POST-/_scripts/:id": "scripts.put",
"POST-/_scripts/:id/:context": "put_script", "POST-/_scripts/:id/:context": "scripts.put",
"POST-/_search": "search",
"POST-/_search/scroll": "scroll", "POST-/_search/scroll": "scroll.create",
"POST-/_search/scroll/:scroll_id": "scroll", "POST-/_search/scroll/:scroll_id": "scroll.create",
"POST-/_search/template": "search_template", "POST-/_search/template": "search_template",
"POST-/_search_shards": "search_shards",
"POST-/_snapshot/:repository": "snapshot.create_repository", "POST-/_snapshot/:repository": "snapshot.create_repository",
"POST-/_snapshot/:repository/_verify": "snapshot.verify_repository", "POST-/_snapshot/:repository/_verify": "snapshot.verify_repository",
"POST-/_snapshot/:repository/:snapshot": "snapshot.create", "POST-/_snapshot/:repository/:snapshot": "snapshot.create",
@ -217,53 +217,53 @@
"POST-/:index/_alias/:name": "indices.put_alias", "POST-/:index/_alias/:name": "indices.put_alias",
"POST-/:index/_aliases/:name": "indices.put_alias", "POST-/:index/_aliases/:name": "indices.put_alias",
"POST-/:index/_analyze": "indices.analyze", "POST-/:index/_analyze": "indices.analyze",
"POST-/:index/_bulk": "bulk", "POST-/:index/_bulk": "indices.bulk",
"POST-/:index/_cache/clear": "indices.clear_cache", "POST-/:index/_cache/clear": "indices.clear_cache",
"POST-/:index/_close": "indices.close", "POST-/:index/_close": "indices.close",
"POST-/:index/_count": "count", "POST-/:index/_count": "indices.count",
"POST-/:index/_delete_by_query": "indices.delete_by_query", "POST-/:index/_delete_by_query": "indices.delete_by_query",
"POST-/:index/_doc": "doc.create", "POST-/:index/_doc": "doc.create",
"POST-/:index/_doc/:id": "doc.put", "POST-/:index/_doc/:id": "doc.update",
"POST-/:index/_doc/:id/_update": "doc.update", "POST-/:index/_doc/:id/_update": "doc.update",
"POST-/:index/_field_caps": "field_caps", "POST-/:index/_field_caps": "indices.field_caps",
"POST-/:index/_flush": "indices.flush", "POST-/:index/_flush": "indices.flush",
"POST-/:index/_flush/synced": "indices.flush_synced", "POST-/:index/_flush/synced": "indices.flush_synced",
"POST-/:index/_forcemerge": "indices.forcemerge", "POST-/:index/_forcemerge": "indices.forcemerge",
"POST-/:index/_mget": "mget", "POST-/:index/_mget": "indices.mget",
"POST-/:index/_msearch": "msearch", "POST-/:index/_msearch": "indices.msearch",
"POST-/:index/_msearch/template": "msearch_template", "POST-/:index/_msearch/template": "indices.msearch_template",
"POST-/:index/_mtermvectors": "mtermvectors", "POST-/:index/_mtermvectors": "indices.mtermvectors",
"POST-/:index/_open": "indices.open", "POST-/:index/_open": "indices.open",
"POST-/:index/_rank_eval": "rank_eval", "POST-/:index/_rank_eval": "indices.rank_eval",
"POST-/:index/_refresh": "indices.refresh", "POST-/:index/_refresh": "indices.refresh",
"POST-/:index/_search": "search", "POST-/:index/_search": "indices.search",
"POST-/:index/_search/template": "search_template", "POST-/:index/_search/template": "indices.search_template",
"POST-/:index/_search_shards": "search_shards", "POST-/:index/_search_shards": "indices.search_shards",
"POST-/:index/_shrink/:target": "indices.shrink", "POST-/:index/_shrink/:target": "indices.shrink",
"POST-/:index/_split/:target": "indices.split", "POST-/:index/_split/:target": "indices.split",
"POST-/:index/_update_by_query": "update_by_query", "POST-/:index/_update_by_query": "indices.update_by_query",
"POST-/:index/_upgrade": "indices.upgrade", "POST-/:index/_upgrade": "indices.upgrade",
"POST-/:index/_validate/query": "indices.validate_query", "POST-/:index/_validate/query": "indices.validate_query",
"POST-/:index/_mapping": "indices.put_mapping", "POST-/:index/_mapping": "indices.put_mapping",
"POST-/:index/_mappings": "indices.put_mapping", "POST-/:index/_mappings": "indices.put_mapping",
"PUT-/_bulk": "bulk", "PUT-/_bulk": "cluster.bulk",
"PUT-/_cluster/settings": "cluster.put_settings", "PUT-/_cluster/settings": "cluster.put_settings",
"PUT-/_ingest/pipeline/:id": "ingest.put_pipeline", "PUT-/_ingest/pipeline/:id": "ingest.put_pipeline",
"PUT-/_scripts/:id": "put_script", "PUT-/_scripts/:id": "scripts.put",
"PUT-/_scripts/:id/:context": "put_script", "PUT-/_scripts/:id/:context": "scripts.put",
"PUT-/_settings": "indices.put_settings", "PUT-/_settings": "cluster.settings_put",
"PUT-/_snapshot/:repository": "snapshot.create_repository", "PUT-/_snapshot/:repository": "snapshot.create_repository",
"PUT-/_snapshot/:repository/:snapshot": "snapshot.create", "PUT-/_snapshot/:repository/:snapshot": "snapshot.create",
"PUT-/_template/:name": "indices.put_template", "PUT-/_template/:name": "indices.put_template",
"PUT-/:index": "indices.create", "PUT-/:index": "indices.put",
"PUT-/:index/_alias/:name": "indices.put_alias", "PUT-/:index/_alias/:name": "indices.put_alias",
"PUT-/:index/_aliases/:name": "indices.put_alias", "PUT-/:index/_aliases/:name": "indices.put_alias",
"PUT-/:index/_bulk": "bulk", "PUT-/:index/_bulk": "indices.put_bulk",
"PUT-/:index/_doc": "index",
"PUT-/:index/_doc/:id": "index", "PUT-/:index/_doc/:id": "doc.put",
"PUT-/:index/_settings": "indices.put_settings", "PUT-/:index/_settings": "indices.put_settings",
"PUT-/:index/_shrink/:target": "indices.shrink", "PUT-/:index/_shrink/:target": "indices.shrink",

View File

@ -43,13 +43,14 @@
"cluster.msearch_template", "cluster.msearch_template",
"cluster.mtermvectors", "cluster.mtermvectors",
"cluster.rank_eval", "cluster.rank_eval",
"cluster.search" "cluster.search",
"cluster.search_shards"
], ],
"doc": [ "doc": [
"doc.*", "doc.*",
"doc.update", "doc.update",
"doc.put",
"doc.create", "doc.create",
"doc.delete", "doc.delete",
"doc.get", "doc.get",
@ -71,6 +72,7 @@
"indices.get_alias", "indices.get_alias",
"indices.recovery", "indices.recovery",
"indices.delete", "indices.delete",
"indices.put",
"indices.clear_cache", "indices.clear_cache",
"indices.update_by_query", "indices.update_by_query",
"indices.shrink", "indices.shrink",
@ -143,6 +145,8 @@
], ],
"render_search_template": [ "render_search_template": [
"render_search_template.*",
"render_search_template.create",
"render_search_template.*" "render_search_template.*"
], ],
"scripts": [ "scripts": [
@ -154,7 +158,10 @@
], ],
"scroll": [ "scroll": [
"scroll.*" "scroll.*",
"scroll.delete",
"scroll.get",
"scroll.create"
], ],
"snapshot": [ "snapshot": [

View File

@ -10,13 +10,14 @@ const UserAll = "system.user:all"
const RoleRead = "system.role:read" const RoleRead = "system.role:read"
const RoleAll = "system.role:all" const RoleAll = "system.role:all"
var RuleRead = []string{"rule::read"} const RuleRead = "alerting.rule:read"
var RuleAll = []string{"rule::read", "rule::write"} const RuleAll = "alerting.rule:all"
const InstanceRead = "gateway.instance:read"
const InstanceAll = "gateway.instance:all"
var InstanceRead = []string{"instance::read"}
var InstanceAll = []string{"instance::read", "instance::write"}
var AdminPrivilege = []string{ var AdminPrivilege = []string{
"system.role:read", "system.role:all", "system.user:read", "system.user:all", UserRead, UserAll, RoleRead, RoleAll,
} }
var BuildRoles = make(map[string]map[string]interface{}, 0) var BuildRoles = make(map[string]map[string]interface{}, 0)
@ -32,16 +33,4 @@ func init() {
"created": time.Now(), "created": time.Now(),
} }
//自定义角色=》 =》permissionKey
// userrole=> [cluster::all,clust] => permissionValue [cluster::read,cluster::write]
// login=> userrole=> cluster::all =>permissionList[]
// cluster search api require = (cluster::read)
//Permission = map[string][]string{
//
// UserRead : {UserRead},
// UserAll: {UserRead, UserWrite},
//}
//zhangsan userrole [cluster::read,cluster::write]
// cluster/_search reqire(cluster::read)
} }

View File

@ -3,7 +3,7 @@ package biz
import "infini.sh/console/internal/core" import "infini.sh/console/internal/core"
var ClusterApis = make(map[string][]string) var ClusterApis = make(map[string][]string)
var IndexApis = make([]string, 0) var IndexApis = make([]string, 50)
var RoleMap = make(map[string]Role) var RoleMap = make(map[string]Role)
var EsApiRoutes = core.NewRouter() var EsApiRoutes = core.NewRouter()
@ -25,7 +25,7 @@ type RolePermission struct {
Platform []string `json:"platform,omitempty"` Platform []string `json:"platform,omitempty"`
Cluster []string `json:"cluster"` Cluster []string `json:"cluster"`
ClusterPrivilege []string `json:"cluster_privilege"` ClusterPrivilege []string `json:"cluster_privilege"`
Index []string `json:"index"`
IndexPrivilege map[string][]string `json:"index_privilege"` IndexPrivilege map[string][]string `json:"index_privilege"`
} }
type ConsolePermisson struct { type ConsolePermisson struct {

View File

@ -3,6 +3,7 @@ package biz
import ( import (
"errors" "errors"
httprouter "infini.sh/framework/core/api/router" httprouter "infini.sh/framework/core/api/router"
"infini.sh/framework/core/util"
"strings" "strings"
) )
@ -39,24 +40,7 @@ func NewClusterRequest(ps httprouter.Params, privilege []string) ClusterRequest
} }
} }
//func ValidateEsPermission(req EsRequest, userRole RolePermission) (err error) {
//
// route, err := EsApiRoutes.Handle(req.Method, req.Path)
// if err != nil {
//
// return
// }
// if len(req.Index) > 0 {
// err = ValidateIndex(req, userRole, route)
// if err != nil {
// return err
// }
// }
// err = ValidateCluster(req, userRole, route)
// return
//}
func ValidateIndex(req IndexRequest, userRole RolePermission) (err error) { func ValidateIndex(req IndexRequest, userRole RolePermission) (err error) {
userIndexMap := make(map[string]struct{})
userClusterMap := make(map[string]struct{}) userClusterMap := make(map[string]struct{})
for _, v := range userRole.Cluster { for _, v := range userRole.Cluster {
@ -68,36 +52,31 @@ func ValidateIndex(req IndexRequest, userRole RolePermission) (err error) {
return return
} }
} }
for _, v := range userRole.Index {
userIndexMap[v] = struct{}{}
}
for _, v := range req.Index {
if _, ok := userIndexMap[v]; !ok {
err = errors.New("no index permission")
return
}
}
for _, val := range req.Privilege { for _, val := range req.Privilege {
prefix := val[:strings.Index(val, ".")] position := strings.Index(val, ".")
if position == -1 {
err = errors.New("invalid privilege parmeter")
return err
}
prefix := val[:position]
for _, v := range req.Index { for _, v := range req.Index {
privilege, ok := userRole.IndexPrivilege[v] privilege, ok := userRole.IndexPrivilege[v]
if !ok { if !ok {
err = errors.New("no index api permission in user role") err = errors.New("no index permission")
return err return err
} }
for _, p := range privilege { if util.StringInArray(privilege, prefix+".*") {
if p == prefix+".*" {
return nil return nil
} }
if p == val { if util.StringInArray(privilege, val) {
return nil return nil
} }
}
} }
} }
return errors.New("no index api permission in user role") return errors.New("no index api permission")
} }
func ValidateCluster(req ClusterRequest, userRole RolePermission) (err error) { func ValidateCluster(req ClusterRequest, userRole RolePermission) (err error) {
userClusterMap := make(map[string]struct{}) userClusterMap := make(map[string]struct{})
@ -110,19 +89,18 @@ func ValidateCluster(req ClusterRequest, userRole RolePermission) (err error) {
return return
} }
} }
// if include api.* for example: cat.* , return nil // if include api.* for example: cat.* , return nil
for _, privilege := range req.Privilege { for _, privilege := range req.Privilege {
prefix := privilege[:strings.Index(privilege, ".")] prefix := privilege[:strings.Index(privilege, ".")]
for _, v := range userRole.ClusterPrivilege {
if v == prefix+".*" {
if util.StringInArray(userRole.ClusterPrivilege, prefix+".*") {
return nil return nil
} }
if v == privilege { if util.StringInArray(userRole.ClusterPrivilege, privilege) {
return nil return nil
} }
} }
}
return errors.New("no cluster api permission") return errors.New("no cluster api permission")
} }
@ -142,7 +120,7 @@ func CombineUserRoles(roleNames []string) RolePermission {
newRole.Platform = append(newRole.Platform, v) newRole.Platform = append(newRole.Platform, v)
} }
for _, v := range role.Index { for _, v := range role.Index {
newRole.Index = append(newRole.Index, v.Name...)
for _, name := range v.Name { for _, name := range v.Name {
if _, ok := m[name]; ok { if _, ok := m[name]; ok {
m[name] = append(m[name], v.Privilege...) m[name] = append(m[name], v.Privilege...)

View File

@ -9,65 +9,57 @@ func Test_validateIndex(t *testing.T) {
type args struct { type args struct {
req IndexRequest req IndexRequest
userRole RolePermission userRole RolePermission
route string
} }
tests := []struct { tests := []struct {
name string name string
args args args args
want string want string
}{ }{
//{"no index permission", {"no index permission",
// args{ args{
// req: EsRequest{ req: IndexRequest{
// Method: "GET",
// Cluster: []string{"cluster1"}, Cluster: []string{"cluster1"},
// Index: []string{"index2"}, Index: []string{"index2"},
// Path: "/index1/_mapping", Privilege: []string{"indices.mapping"},
// }, },
// userRole: RolePermission{ userRole: RolePermission{
// Cluster: []string{ Cluster: []string{
// "cluster1", "cluster1",
// }, },
// Index: []string{
// "index1", ClusterPrivilege: []string{
// }, "cat.*",
// ClusterPrivilege: []string{ },
// "cat.*", IndexPrivilege: map[string][]string{
// }, "index1": []string{"indices.delete"},
// IndexPrivilege: []string{ },
// "indices.get_mapping", },
// }, }, "no index permission",
// }, },
// route: "indices.get_mapping", {"no index api permission",
// }, "no index permission", args{
//}, req: IndexRequest{
//{"no index api permission",
// args{ Cluster: []string{"cluster1"},
// req: EsRequest{ Index: []string{"index1"},
// Method: "GET", Privilege: []string{"indices.mapping"},
// Cluster: []string{"cluster1"}, },
// Index: []string{"index1"}, userRole: RolePermission{
// Path: "/index1/_mapping", Cluster: []string{
// }, "cluster1",
// userRole: RolePermission{ },
// Cluster: []string{
// "cluster1", ClusterPrivilege: []string{
// }, "cat.*",
// Index: []string{ },
// IndexPrivilege: map[string][]string{
// "index1", "index1": []string{"indices.delete"},
// }, },
// ClusterPrivilege: []string{ },
// "cat.*", },
// }, "no index api permission",
// IndexPrivilege: []string{ },
// "indices.delete",
// },
// },
// route: "indices.get_mapping",
// },
// "no index api permission",
//},
} }
for _, tt := range tests { for _, tt := range tests {
@ -89,7 +81,7 @@ func Test_validateCluster(t *testing.T) {
args args args args
want string want string
}{ }{
{"no cluster permission", {"no cluster",
args{ args{
req: ClusterRequest{ req: ClusterRequest{
@ -100,16 +92,14 @@ func Test_validateCluster(t *testing.T) {
Cluster: []string{ Cluster: []string{
"cluster2", "cluster2",
}, },
Index: []string{
"index1",
},
ClusterPrivilege: []string{ ClusterPrivilege: []string{
"cat.*", "cat.*",
}, },
}, },
}, "no cluster permission", }, "no cluster permission",
}, },
{"no cluster api permission", {"no cluster api",
args{ args{
req: ClusterRequest{ req: ClusterRequest{
@ -120,10 +110,7 @@ func Test_validateCluster(t *testing.T) {
Cluster: []string{ Cluster: []string{
"cluster1", "cluster1",
}, },
Index: []string{
"index1",
},
ClusterPrivilege: []string{ ClusterPrivilege: []string{
"cat.*", "cat.*",
}, },

47
internal/middleware/es.go Normal file
View File

@ -0,0 +1,47 @@
package middleware
import (
"infini.sh/console/internal/biz"
httprouter "infini.sh/framework/core/api/router"
"net/http"
)
func IndexRequired(h httprouter.Handle, route ...string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
if err != nil {
w = handleError(w, http.StatusUnauthorized, err)
return
}
newRole := biz.CombineUserRoles(claims.Roles)
indexReq := biz.NewIndexRequest(ps, route)
err = biz.ValidateIndex(indexReq, newRole)
if err != nil {
w = handleError(w, http.StatusForbidden, err)
return
}
h(w, r, ps)
}
}
func ClusterRequired(h httprouter.Handle, route ...string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
if err != nil {
w = handleError(w, http.StatusUnauthorized, err)
return
}
newRole := biz.CombineUserRoles(claims.Roles)
clusterReq := biz.NewClusterRequest(ps, route)
err = biz.ValidateCluster(clusterReq, newRole)
if err != nil {
w = handleError(w, http.StatusForbidden, err)
return
}
h(w, r, ps)
}
}

View File

@ -19,63 +19,6 @@ func LoginRequired(h httprouter.Handle) httprouter.Handle {
h(w, r, ps) h(w, r, ps)
} }
} }
func IndexRequired(h httprouter.Handle, route ...string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
if err != nil {
w = handleError(w, http.StatusUnauthorized, err)
return
}
newRole := biz.CombineUserRoles(claims.Roles)
indexReq := biz.NewIndexRequest(ps, route)
err = biz.ValidateIndex(indexReq, newRole)
if err != nil {
w = handleError(w, http.StatusForbidden, err)
return
}
h(w, r, ps)
}
}
func ClusterRequired(h httprouter.Handle, route ...string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
if err != nil {
w = handleError(w, http.StatusUnauthorized, err)
return
}
newRole := biz.CombineUserRoles(claims.Roles)
clusterReq := biz.NewClusterRequest(ps, route)
err = biz.ValidateCluster(clusterReq, newRole)
if err != nil {
w = handleError(w, http.StatusForbidden, err)
return
}
h(w, r, ps)
}
}
func EsPermissionRequired(h httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
//claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
//if err != nil {
// w = handleError(w, http.StatusUnauthorized, err)
// return
//}
//req := biz.NewEsRequest(r, ps)
//newRole := biz.CombineUserRoles(claims.Roles)
//err = biz.ValidateEsPermission(req, newRole)
//if err != nil {
// w = handleError(w, http.StatusForbidden, err)
// return
//}
h(w, r, ps)
}
}
func PermissionRequired(h httprouter.Handle, permissions ...string) httprouter.Handle { func PermissionRequired(h httprouter.Handle, permissions ...string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {