Update contrib token server to support repository class
Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)master
							parent
							
								
									01509db714
								
							
						
					
					
						commit
						61e65ecd9d
					
				|  | @ -18,6 +18,10 @@ import ( | ||||||
| 	"github.com/gorilla/mux" | 	"github.com/gorilla/mux" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | var ( | ||||||
|  | 	enforceRepoClass bool | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| func main() { | func main() { | ||||||
| 	var ( | 	var ( | ||||||
| 		issuer = &TokenIssuer{} | 		issuer = &TokenIssuer{} | ||||||
|  | @ -44,6 +48,8 @@ func main() { | ||||||
| 	flag.StringVar(&cert, "tlscert", "", "Certificate file for TLS") | 	flag.StringVar(&cert, "tlscert", "", "Certificate file for TLS") | ||||||
| 	flag.StringVar(&certKey, "tlskey", "", "Certificate key for TLS") | 	flag.StringVar(&certKey, "tlskey", "", "Certificate key for TLS") | ||||||
| 
 | 
 | ||||||
|  | 	flag.BoolVar(&enforceRepoClass, "enforce-class", false, "Enforce policy for single repository class") | ||||||
|  | 
 | ||||||
| 	flag.Parse() | 	flag.Parse() | ||||||
| 
 | 
 | ||||||
| 	if debug { | 	if debug { | ||||||
|  | @ -157,6 +163,8 @@ type tokenResponse struct { | ||||||
| 	ExpiresIn    int    `json:"expires_in,omitempty"` | 	ExpiresIn    int    `json:"expires_in,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | var repositoryClassCache = map[string]string{} | ||||||
|  | 
 | ||||||
| func filterAccessList(ctx context.Context, scope string, requestedAccessList []auth.Access) []auth.Access { | func filterAccessList(ctx context.Context, scope string, requestedAccessList []auth.Access) []auth.Access { | ||||||
| 	if !strings.HasSuffix(scope, "/") { | 	if !strings.HasSuffix(scope, "/") { | ||||||
| 		scope = scope + "/" | 		scope = scope + "/" | ||||||
|  | @ -168,6 +176,16 @@ func filterAccessList(ctx context.Context, scope string, requestedAccessList []a | ||||||
| 				context.GetLogger(ctx).Debugf("Resource scope not allowed: %s", access.Name) | 				context.GetLogger(ctx).Debugf("Resource scope not allowed: %s", access.Name) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  | 			if enforceRepoClass { | ||||||
|  | 				if class, ok := repositoryClassCache[access.Name]; ok { | ||||||
|  | 					if class != access.Class { | ||||||
|  | 						context.GetLogger(ctx).Debugf("Different repository class: %q, previously %q", access.Class, class) | ||||||
|  | 						continue | ||||||
|  | 					} | ||||||
|  | 				} else if strings.EqualFold(access.Action, "push") { | ||||||
|  | 					repositoryClassCache[access.Name] = access.Class | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 		} else if access.Type == "registry" { | 		} else if access.Type == "registry" { | ||||||
| 			if access.Name != "catalog" { | 			if access.Name != "catalog" { | ||||||
| 				context.GetLogger(ctx).Debugf("Unknown registry resource: %s", access.Name) | 				context.GetLogger(ctx).Debugf("Unknown registry resource: %s", access.Name) | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | @ -32,12 +33,18 @@ func ResolveScopeSpecifiers(ctx context.Context, scopeSpecs []string) []auth.Acc | ||||||
| 
 | 
 | ||||||
| 		resourceType, resourceName, actions := parts[0], parts[1], parts[2] | 		resourceType, resourceName, actions := parts[0], parts[1], parts[2] | ||||||
| 
 | 
 | ||||||
|  | 		resourceType, resourceClass := splitResourceClass(resourceType) | ||||||
|  | 		if resourceType == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		// Actions should be a comma-separated list of actions.
 | 		// Actions should be a comma-separated list of actions.
 | ||||||
| 		for _, action := range strings.Split(actions, ",") { | 		for _, action := range strings.Split(actions, ",") { | ||||||
| 			requestedAccess := auth.Access{ | 			requestedAccess := auth.Access{ | ||||||
| 				Resource: auth.Resource{ | 				Resource: auth.Resource{ | ||||||
| 					Type: resourceType, | 					Type:  resourceType, | ||||||
| 					Name: resourceName, | 					Class: resourceClass, | ||||||
|  | 					Name:  resourceName, | ||||||
| 				}, | 				}, | ||||||
| 				Action: action, | 				Action: action, | ||||||
| 			} | 			} | ||||||
|  | @ -55,6 +62,19 @@ func ResolveScopeSpecifiers(ctx context.Context, scopeSpecs []string) []auth.Acc | ||||||
| 	return requestedAccessList | 	return requestedAccessList | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | var typeRegexp = regexp.MustCompile(`^([a-z0-9]+)(\([a-z0-9]+\))?$`) | ||||||
|  | 
 | ||||||
|  | func splitResourceClass(t string) (string, string) { | ||||||
|  | 	matches := typeRegexp.FindStringSubmatch(t) | ||||||
|  | 	if len(matches) < 2 { | ||||||
|  | 		return "", "" | ||||||
|  | 	} | ||||||
|  | 	if len(matches) == 2 || len(matches[2]) < 2 { | ||||||
|  | 		return matches[1], "" | ||||||
|  | 	} | ||||||
|  | 	return matches[1], matches[2][1 : len(matches[2])-1] | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ResolveScopeList converts a scope list from a token request's
 | // ResolveScopeList converts a scope list from a token request's
 | ||||||
| // `scope` parameter into a list of standard access objects.
 | // `scope` parameter into a list of standard access objects.
 | ||||||
| func ResolveScopeList(ctx context.Context, scopeList string) []auth.Access { | func ResolveScopeList(ctx context.Context, scopeList string) []auth.Access { | ||||||
|  | @ -62,12 +82,19 @@ func ResolveScopeList(ctx context.Context, scopeList string) []auth.Access { | ||||||
| 	return ResolveScopeSpecifiers(ctx, scopes) | 	return ResolveScopeSpecifiers(ctx, scopes) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func scopeString(a auth.Access) string { | ||||||
|  | 	if a.Class != "" { | ||||||
|  | 		return fmt.Sprintf("%s(%s):%s:%s", a.Type, a.Class, a.Name, a.Action) | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("%s:%s:%s", a.Type, a.Name, a.Action) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ToScopeList converts a list of access to a
 | // ToScopeList converts a list of access to a
 | ||||||
| // scope list string
 | // scope list string
 | ||||||
| func ToScopeList(access []auth.Access) string { | func ToScopeList(access []auth.Access) string { | ||||||
| 	var s []string | 	var s []string | ||||||
| 	for _, a := range access { | 	for _, a := range access { | ||||||
| 		s = append(s, fmt.Sprintf("%s:%s:%s", a.Type, a.Name, a.Action)) | 		s = append(s, scopeString(a)) | ||||||
| 	} | 	} | ||||||
| 	return strings.Join(s, ",") | 	return strings.Join(s, ",") | ||||||
| } | } | ||||||
|  | @ -102,6 +129,7 @@ func (issuer *TokenIssuer) CreateJWT(subject string, audience string, grantedAcc | ||||||
| 
 | 
 | ||||||
| 		accessEntries = append(accessEntries, &token.ResourceActions{ | 		accessEntries = append(accessEntries, &token.ResourceActions{ | ||||||
| 			Type:    resource.Type, | 			Type:    resource.Type, | ||||||
|  | 			Class:   resource.Class, | ||||||
| 			Name:    resource.Name, | 			Name:    resource.Name, | ||||||
| 			Actions: actions, | 			Actions: actions, | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue