mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 20:26:13 +01:00
Refine the token scope generation
This commit directly maps the actoin permission in security context to the scope generated by the token service in harbor-core. Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
parent
bd46af691c
commit
eb75123638
@ -99,12 +99,14 @@ func New(ctx context.Context, name string, access []*registry_token.ResourceActi
|
|||||||
actionMap[rbac.ActionPull] = struct{}{}
|
actionMap[rbac.ActionPull] = struct{}{}
|
||||||
case "push":
|
case "push":
|
||||||
actionMap[rbac.ActionPush] = struct{}{}
|
actionMap[rbac.ActionPush] = struct{}{}
|
||||||
|
case "delete":
|
||||||
|
actionMap[rbac.ActionDelete] = struct{}{}
|
||||||
|
case "scanner-pull":
|
||||||
|
actionMap[rbac.ActionScannerPull] = struct{}{}
|
||||||
case "*":
|
case "*":
|
||||||
actionMap[rbac.ActionPull] = struct{}{}
|
actionMap[rbac.ActionPull] = struct{}{}
|
||||||
actionMap[rbac.ActionPush] = struct{}{}
|
actionMap[rbac.ActionPush] = struct{}{}
|
||||||
actionMap[rbac.ActionDelete] = struct{}{}
|
actionMap[rbac.ActionDelete] = struct{}{}
|
||||||
case "scanner-pull":
|
|
||||||
actionMap[rbac.ActionScannerPull] = struct{}{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m[l[0]] = actionMap
|
m[l[0]] = actionMap
|
||||||
|
@ -154,20 +154,3 @@ func MakeToken(username, service string, access []*token.ResourceActions) (*mode
|
|||||||
IssuedAt: now.Format(time.RFC3339),
|
IssuedAt: now.Format(time.RFC3339),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func permToActions(p string) []string {
|
|
||||||
res := make([]string, 0)
|
|
||||||
if strings.Contains(p, "W") {
|
|
||||||
res = append(res, "push")
|
|
||||||
}
|
|
||||||
if strings.Contains(p, "M") {
|
|
||||||
res = append(res, "*")
|
|
||||||
}
|
|
||||||
if strings.Contains(p, "R") {
|
|
||||||
res = append(res, "pull")
|
|
||||||
}
|
|
||||||
if strings.Contains(p, "S") {
|
|
||||||
res = append(res, "scanner-pull")
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
@ -34,6 +34,14 @@ import (
|
|||||||
var creatorMap map[string]Creator
|
var creatorMap map[string]Creator
|
||||||
var registryFilterMap map[string]accessFilter
|
var registryFilterMap map[string]accessFilter
|
||||||
var notaryFilterMap map[string]accessFilter
|
var notaryFilterMap map[string]accessFilter
|
||||||
|
var actionScopeMap = map[rbac.Action]string{
|
||||||
|
// Scopes checked by distribution, see: https://github.com/docker/distribution/blob/master/registry/handlers/app.go
|
||||||
|
rbac.ActionPull: "pull",
|
||||||
|
rbac.ActionPush: "push",
|
||||||
|
rbac.ActionDelete: "delete",
|
||||||
|
// For skipping policy check when scanner pulls artifacts
|
||||||
|
rbac.ActionScannerPull: "scanner-pull",
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Notary service
|
// Notary service
|
||||||
@ -163,7 +171,6 @@ func (rep repositoryFilter) filter(ctx context.Context, ctl project.Controller,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
projectName := img.namespace
|
projectName := img.namespace
|
||||||
permission := ""
|
|
||||||
|
|
||||||
project, err := ctl.GetByName(ctx, projectName)
|
project, err := ctl.GetByName(ctx, projectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -175,21 +182,24 @@ func (rep repositoryFilter) filter(ctx context.Context, ctl project.Controller,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
secCtx, _ := security.FromContext(ctx)
|
|
||||||
|
|
||||||
resource := rbac.NewProjectNamespace(project.ProjectID).Resource(rbac.ResourceRepository)
|
resource := rbac.NewProjectNamespace(project.ProjectID).Resource(rbac.ResourceRepository)
|
||||||
if secCtx.Can(ctx, rbac.ActionPush, resource) && secCtx.Can(ctx, rbac.ActionPull, resource) {
|
scopeList := make([]string, 0)
|
||||||
permission = "RWM"
|
for s := range resourceScopes(ctx, resource) {
|
||||||
} else if secCtx.Can(ctx, rbac.ActionPush, resource) {
|
scopeList = append(scopeList, s)
|
||||||
permission = "RW"
|
}
|
||||||
} else if secCtx.Can(ctx, rbac.ActionScannerPull, resource) {
|
a.Actions = scopeList
|
||||||
permission = "RS"
|
return nil
|
||||||
} else if secCtx.Can(ctx, rbac.ActionPull, resource) {
|
|
||||||
permission = "R"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Actions = permToActions(permission)
|
func resourceScopes(ctx context.Context, rc rbac.Resource) map[string]struct{} {
|
||||||
return nil
|
sCtx, _ := security.FromContext(ctx)
|
||||||
|
res := map[string]struct{}{}
|
||||||
|
for a, s := range actionScopeMap {
|
||||||
|
if sCtx.Can(ctx, a, rc) {
|
||||||
|
res[s] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
type generalCreator struct {
|
type generalCreator struct {
|
||||||
|
@ -156,21 +156,6 @@ func TestMakeToken(t *testing.T) {
|
|||||||
assert.Equal(t, claims.Audience, svc, "Audience mismatch")
|
assert.Equal(t, claims.Audience, svc, "Audience mismatch")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPermToActions(t *testing.T) {
|
|
||||||
perm1 := "RWM"
|
|
||||||
perm2 := "MRR"
|
|
||||||
perm3 := ""
|
|
||||||
expect1 := []string{"push", "*", "pull"}
|
|
||||||
expect2 := []string{"*", "pull"}
|
|
||||||
expect3 := make([]string, 0)
|
|
||||||
res1 := permToActions(perm1)
|
|
||||||
res2 := permToActions(perm2)
|
|
||||||
res3 := permToActions(perm3)
|
|
||||||
assert.Equal(t, res1, expect1, fmt.Sprintf("actions mismatch for permission: %s", perm1))
|
|
||||||
assert.Equal(t, res2, expect2, fmt.Sprintf("actions mismatch for permission: %s", perm2))
|
|
||||||
assert.Equal(t, res3, expect3, fmt.Sprintf("actions mismatch for permission: %s", perm3))
|
|
||||||
}
|
|
||||||
|
|
||||||
type parserTestRec struct {
|
type parserTestRec struct {
|
||||||
input string
|
input string
|
||||||
expect image
|
expect image
|
||||||
@ -224,6 +209,7 @@ func TestEndpointParser(t *testing.T) {
|
|||||||
|
|
||||||
type fakeSecurityContext struct {
|
type fakeSecurityContext struct {
|
||||||
isAdmin bool
|
isAdmin bool
|
||||||
|
rcActions map[rbac.Resource][]rbac.Action
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeSecurityContext) Name() string {
|
func (f *fakeSecurityContext) Name() string {
|
||||||
@ -245,8 +231,16 @@ func (f *fakeSecurityContext) IsSolutionUser() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
func (f *fakeSecurityContext) Can(ctx context.Context, action rbac.Action, resource rbac.Resource) bool {
|
func (f *fakeSecurityContext) Can(ctx context.Context, action rbac.Action, resource rbac.Resource) bool {
|
||||||
|
if actions, ok := f.rcActions[resource]; ok {
|
||||||
|
for _, a := range actions {
|
||||||
|
if a == action {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeSecurityContext) GetMyProjects() ([]*models.Project, error) {
|
func (f *fakeSecurityContext) GetMyProjects() ([]*models.Project, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -303,3 +297,56 @@ func TestParseScopes(t *testing.T) {
|
|||||||
l1 := parseScopes(r1)
|
l1 := parseScopes(r1)
|
||||||
assert.Equal([]string{"repository:library/registry:push,pull", "repository:hello-world/registry:pull"}, l1)
|
assert.Equal([]string{"repository:library/registry:push,pull", "repository:hello-world/registry:pull"}, l1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceScopes(t *testing.T) {
|
||||||
|
sctx := &fakeSecurityContext{
|
||||||
|
isAdmin: false,
|
||||||
|
rcActions: map[rbac.Resource][]rbac.Action{
|
||||||
|
rbac.NewProjectNamespace(1).Resource(rbac.ResourceRepository): {rbac.ActionPull, rbac.ActionScannerPull},
|
||||||
|
rbac.NewProjectNamespace(2).Resource(rbac.ResourceRepository): {rbac.ActionPull, rbac.ActionScannerPull, rbac.ActionPush},
|
||||||
|
rbac.NewProjectNamespace(3).Resource(rbac.ResourceRepository): {rbac.ActionPull, rbac.ActionScannerPull, rbac.ActionPush, rbac.ActionDelete},
|
||||||
|
rbac.NewProjectNamespace(4).Resource(rbac.ResourceRepository): {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx := security.NewContext(context.TODO(), sctx)
|
||||||
|
cases := []struct {
|
||||||
|
rc rbac.Resource
|
||||||
|
expect map[string]struct{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
rc: rbac.NewProjectNamespace(1).Resource(rbac.ResourceRepository),
|
||||||
|
expect: map[string]struct{}{
|
||||||
|
"pull": {},
|
||||||
|
"scanner-pull": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rc: rbac.NewProjectNamespace(2).Resource(rbac.ResourceRepository),
|
||||||
|
expect: map[string]struct{}{
|
||||||
|
"pull": {},
|
||||||
|
"scanner-pull": {},
|
||||||
|
"push": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rc: rbac.NewProjectNamespace(3).Resource(rbac.ResourceRepository),
|
||||||
|
expect: map[string]struct{}{
|
||||||
|
"pull": {},
|
||||||
|
"scanner-pull": {},
|
||||||
|
"push": {},
|
||||||
|
"delete": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rc: rbac.NewProjectNamespace(4).Resource(rbac.ResourceRepository),
|
||||||
|
expect: map[string]struct{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rc: rbac.NewProjectNamespace(5).Resource(rbac.ResourceRepository),
|
||||||
|
expect: map[string]struct{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
assert.Equal(t, c.expect, resourceScopes(ctx, c.rc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user