diff --git a/src/common/utils/registry/auth/tokenauthorizer.go b/src/common/utils/registry/auth/tokenauthorizer.go index b65474c9b..a2a3c05aa 100644 --- a/src/common/utils/registry/auth/tokenauthorizer.go +++ b/src/common/utils/registry/auth/tokenauthorizer.go @@ -271,6 +271,6 @@ func NewUsernameTokenAuthorizer(username string, scopeType, scopeName string, sc } func (u *usernameTokenAuthorizer) generateToken(realm, service string, scopes []string) (token string, expiresIn int, issuedAt *time.Time, err error) { - token, expiresIn, issuedAt, err = token_util.GenTokenForUI(u.username, service, scopes) + token, expiresIn, issuedAt, err = token_util.RegistryTokenForUI(u.username, service, scopes) return } diff --git a/src/ui/service/token/authutils.go b/src/ui/service/token/authutils.go index 0f1e8a518..af6594897 100644 --- a/src/ui/service/token/authutils.go +++ b/src/ui/service/token/authutils.go @@ -74,25 +74,49 @@ func GetResourceActions(scopes []string) []*token.ResourceActions { return res } -// GenTokenForUI is for the UI process to call, so it won't establish a https connection from UI to proxy. -func GenTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) { +//filterAccess iterate a list of resource actions and try to use the filter that matches the resource type to filter the actions. +func filterAccess(access []*token.ResourceActions, u userInfo, filters map[string]accessFilter) error { + var err error + for _, a := range access { + f, ok := filters[a.Type] + if !ok { + a.Actions = []string{} + log.Warningf("No filter found for access type: %s, skip filter, the access of resource '%s' will be set empty.", a.Type, a.Name) + continue + } + err = f.filter(u, a) + log.Debugf("user: %s, access: %v", u.name, a) + if err != nil { + return err + } + } + return nil +} + +//RegistryTokenForUI calls genTokenForUI to get raw token for registry +func RegistryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) { + return genTokenForUI(username, service, scopes, registryFilterMap) +} + +//NotaryTokenForUI calls genTokenForUI to get raw token for notary +func NotaryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) { + return genTokenForUI(username, service, scopes, notaryFilterMap) +} + +// genTokenForUI is for the UI process to call, so it won't establish a https connection from UI to proxy. +func genTokenForUI(username string, service string, scopes []string, filters map[string]accessFilter) (string, int, *time.Time, error) { isAdmin, err := dao.IsAdminRole(username) if err != nil { return "", 0, nil, err } - f := &repositoryFilter{ - parser: &basicParser{}, - } u := userInfo{ name: username, allPerm: isAdmin, } access := GetResourceActions(scopes) - for _, a := range access { - err = f.filter(u, a) - if err != nil { - return "", 0, nil, err - } + err = filterAccess(access, u, filters) + if err != nil { + return "", 0, nil, err } return MakeRawToken(username, service, access) } diff --git a/src/ui/service/token/creator.go b/src/ui/service/token/creator.go index 861851ba9..a572ab932 100644 --- a/src/ui/service/token/creator.go +++ b/src/ui/service/token/creator.go @@ -26,6 +26,8 @@ import ( ) var creatorMap map[string]Creator +var registryFilterMap map[string]accessFilter +var notaryFilterMap map[string]accessFilter const ( notary = "harbor-notary" @@ -35,22 +37,29 @@ const ( //InitCreators initialize the token creators for different services func InitCreators() { creatorMap = make(map[string]Creator) + registryFilterMap = map[string]accessFilter{ + "repository": &repositoryFilter{ + parser: &basicParser{}, + }, + "registry": ®istryFilter{}, + } ext, err := config.ExtEndpoint() if err != nil { log.Warningf("Failed to get ext enpoint, err: %v, the token service will not be functional with notary requests", err) } else { + notaryFilterMap = map[string]accessFilter{ + "repository": &repositoryFilter{ + parser: &endpointParser{ + endpoint: strings.Split(ext, "//")[1], + }, + }, + } creatorMap[notary] = &generalCreator{ validators: []ReqValidator{ &basicAuthValidator{}, }, - service: notary, - filterMap: map[string]accessFilter{ - "repository": &repositoryFilter{ - parser: &endpointParser{ - endpoint: strings.Split(ext, "//")[1], - }, - }, - }, + service: notary, + filterMap: notaryFilterMap, } } @@ -59,13 +68,8 @@ func InitCreators() { &secretValidator{config.JobserviceSecret()}, &basicAuthValidator{}, }, - service: registry, - filterMap: map[string]accessFilter{ - "repository": &repositoryFilter{ - parser: &basicParser{}, - }, - "registry": ®istryFilter{}, - }, + service: registry, + filterMap: registryFilterMap, } } @@ -145,7 +149,6 @@ type repositoryFilter struct { func (rep repositoryFilter) filter(user userInfo, a *token.ResourceActions) error { //clear action list to assign to new acess element after perm check. - a.Actions = []string{} img, err := rep.parser.parse(a.Name) if err != nil { return err @@ -216,16 +219,9 @@ func (g generalCreator) Create(r *http.Request) (*tokenJSON, error) { user = &userInfo{} } access := GetResourceActions(scopes) - for _, a := range access { - f, ok := g.filterMap[a.Type] - if !ok { - log.Warningf("No filter found for access type: %s, skip.", a.Type) - continue - } - err = f.filter(*user, a) - if err != nil { - return nil, err - } + err = filterAccess(access, *user, g.filterMap) + if err != nil { + return nil, err } return makeToken(user.name, g.service, access) } diff --git a/src/ui/service/token/token_test.go b/src/ui/service/token/token_test.go index 780cc91a7..534e7fd2b 100644 --- a/src/ui/service/token/token_test.go +++ b/src/ui/service/token/token_test.go @@ -31,6 +31,7 @@ func TestMain(m *testing.M) { if err := config.Init(); err != nil { panic(err) } + InitCreators() result := m.Run() if result != 0 { os.Exit(result) @@ -184,3 +185,28 @@ func TestEndpointParser(t *testing.T) { } } } + +func TestFilterAccess(t *testing.T) { + //TODO put initial data in DB to verify repository filter. + var err error + s := []string{"registry:catalog:*"} + a1 := GetResourceActions(s) + a2 := GetResourceActions(s) + u := userInfo{"jack", false} + ra1 := token.ResourceActions{ + Type: "registry", + Name: "catalog", + Actions: []string{"*"}, + } + ra2 := token.ResourceActions{ + Type: "registry", + Name: "catalog", + Actions: []string{}, + } + err = filterAccess(a1, u, registryFilterMap) + assert.Nil(t, err, "Unexpected error: %v", err) + assert.Equal(t, ra1, *a1[0], "Mismatch after registry filter Map") + err = filterAccess(a2, u, notaryFilterMap) + assert.Nil(t, err, "Unexpected error: %v", err) + assert.Equal(t, ra2, *a2[0], "Mismatch after notary filter Map") +}