mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-29 05:47:31 +02:00
support generate UI token for notary
This commit is contained in:
parent
ac6c26d6db
commit
109db458c3
@ -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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
@ -74,25 +74,49 @@ func GetResourceActions(scopes []string) []*token.ResourceActions {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenTokenForUI is for the UI process to call, so it won't establish a https connection from UI to proxy.
|
//filterAccess iterate a list of resource actions and try to use the filter that matches the resource type to filter the actions.
|
||||||
func GenTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) {
|
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)
|
isAdmin, err := dao.IsAdminRole(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, nil, err
|
return "", 0, nil, err
|
||||||
}
|
}
|
||||||
f := &repositoryFilter{
|
|
||||||
parser: &basicParser{},
|
|
||||||
}
|
|
||||||
u := userInfo{
|
u := userInfo{
|
||||||
name: username,
|
name: username,
|
||||||
allPerm: isAdmin,
|
allPerm: isAdmin,
|
||||||
}
|
}
|
||||||
access := GetResourceActions(scopes)
|
access := GetResourceActions(scopes)
|
||||||
for _, a := range access {
|
err = filterAccess(access, u, filters)
|
||||||
err = f.filter(u, a)
|
if err != nil {
|
||||||
if err != nil {
|
return "", 0, nil, err
|
||||||
return "", 0, nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return MakeRawToken(username, service, access)
|
return MakeRawToken(username, service, access)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var creatorMap map[string]Creator
|
var creatorMap map[string]Creator
|
||||||
|
var registryFilterMap map[string]accessFilter
|
||||||
|
var notaryFilterMap map[string]accessFilter
|
||||||
|
|
||||||
const (
|
const (
|
||||||
notary = "harbor-notary"
|
notary = "harbor-notary"
|
||||||
@ -35,22 +37,29 @@ const (
|
|||||||
//InitCreators initialize the token creators for different services
|
//InitCreators initialize the token creators for different services
|
||||||
func InitCreators() {
|
func InitCreators() {
|
||||||
creatorMap = make(map[string]Creator)
|
creatorMap = make(map[string]Creator)
|
||||||
|
registryFilterMap = map[string]accessFilter{
|
||||||
|
"repository": &repositoryFilter{
|
||||||
|
parser: &basicParser{},
|
||||||
|
},
|
||||||
|
"registry": ®istryFilter{},
|
||||||
|
}
|
||||||
ext, err := config.ExtEndpoint()
|
ext, err := config.ExtEndpoint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("Failed to get ext enpoint, err: %v, the token service will not be functional with notary requests", err)
|
log.Warningf("Failed to get ext enpoint, err: %v, the token service will not be functional with notary requests", err)
|
||||||
} else {
|
} else {
|
||||||
|
notaryFilterMap = map[string]accessFilter{
|
||||||
|
"repository": &repositoryFilter{
|
||||||
|
parser: &endpointParser{
|
||||||
|
endpoint: strings.Split(ext, "//")[1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
creatorMap[notary] = &generalCreator{
|
creatorMap[notary] = &generalCreator{
|
||||||
validators: []ReqValidator{
|
validators: []ReqValidator{
|
||||||
&basicAuthValidator{},
|
&basicAuthValidator{},
|
||||||
},
|
},
|
||||||
service: notary,
|
service: notary,
|
||||||
filterMap: map[string]accessFilter{
|
filterMap: notaryFilterMap,
|
||||||
"repository": &repositoryFilter{
|
|
||||||
parser: &endpointParser{
|
|
||||||
endpoint: strings.Split(ext, "//")[1],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,13 +68,8 @@ func InitCreators() {
|
|||||||
&secretValidator{config.JobserviceSecret()},
|
&secretValidator{config.JobserviceSecret()},
|
||||||
&basicAuthValidator{},
|
&basicAuthValidator{},
|
||||||
},
|
},
|
||||||
service: registry,
|
service: registry,
|
||||||
filterMap: map[string]accessFilter{
|
filterMap: registryFilterMap,
|
||||||
"repository": &repositoryFilter{
|
|
||||||
parser: &basicParser{},
|
|
||||||
},
|
|
||||||
"registry": ®istryFilter{},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +149,6 @@ type repositoryFilter struct {
|
|||||||
|
|
||||||
func (rep repositoryFilter) filter(user userInfo, a *token.ResourceActions) error {
|
func (rep repositoryFilter) filter(user userInfo, a *token.ResourceActions) error {
|
||||||
//clear action list to assign to new acess element after perm check.
|
//clear action list to assign to new acess element after perm check.
|
||||||
a.Actions = []string{}
|
|
||||||
img, err := rep.parser.parse(a.Name)
|
img, err := rep.parser.parse(a.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -216,16 +219,9 @@ func (g generalCreator) Create(r *http.Request) (*tokenJSON, error) {
|
|||||||
user = &userInfo{}
|
user = &userInfo{}
|
||||||
}
|
}
|
||||||
access := GetResourceActions(scopes)
|
access := GetResourceActions(scopes)
|
||||||
for _, a := range access {
|
err = filterAccess(access, *user, g.filterMap)
|
||||||
f, ok := g.filterMap[a.Type]
|
if err != nil {
|
||||||
if !ok {
|
return nil, err
|
||||||
log.Warningf("No filter found for access type: %s, skip.", a.Type)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = f.filter(*user, a)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return makeToken(user.name, g.service, access)
|
return makeToken(user.name, g.service, access)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ func TestMain(m *testing.M) {
|
|||||||
if err := config.Init(); err != nil {
|
if err := config.Init(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
InitCreators()
|
||||||
result := m.Run()
|
result := m.Run()
|
||||||
if result != 0 {
|
if result != 0 {
|
||||||
os.Exit(result)
|
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")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user