support generate UI token for notary

This commit is contained in:
Tan Jiang 2017-03-06 10:54:49 +08:00
parent ac6c26d6db
commit 109db458c3
4 changed files with 83 additions and 37 deletions

View File

@ -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
}

View File

@ -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)
}

View File

@ -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": &registryFilter{},
}
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": &registryFilter{},
},
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)
}

View File

@ -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")
}