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" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	enforceRepoClass bool | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	var ( | ||||
| 		issuer = &TokenIssuer{} | ||||
|  | @ -44,6 +48,8 @@ func main() { | |||
| 	flag.StringVar(&cert, "tlscert", "", "Certificate file for TLS") | ||||
| 	flag.StringVar(&certKey, "tlskey", "", "Certificate key for TLS") | ||||
| 
 | ||||
| 	flag.BoolVar(&enforceRepoClass, "enforce-class", false, "Enforce policy for single repository class") | ||||
| 
 | ||||
| 	flag.Parse() | ||||
| 
 | ||||
| 	if debug { | ||||
|  | @ -157,6 +163,8 @@ type tokenResponse struct { | |||
| 	ExpiresIn    int    `json:"expires_in,omitempty"` | ||||
| } | ||||
| 
 | ||||
| var repositoryClassCache = map[string]string{} | ||||
| 
 | ||||
| func filterAccessList(ctx context.Context, scope string, requestedAccessList []auth.Access) []auth.Access { | ||||
| 	if !strings.HasSuffix(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) | ||||
| 				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" { | ||||
| 			if access.Name != "catalog" { | ||||
| 				context.GetLogger(ctx).Debugf("Unknown registry resource: %s", access.Name) | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import ( | |||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
|  | @ -32,12 +33,18 @@ func ResolveScopeSpecifiers(ctx context.Context, scopeSpecs []string) []auth.Acc | |||
| 
 | ||||
| 		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.
 | ||||
| 		for _, action := range strings.Split(actions, ",") { | ||||
| 			requestedAccess := auth.Access{ | ||||
| 				Resource: auth.Resource{ | ||||
| 					Type: resourceType, | ||||
| 					Name: resourceName, | ||||
| 					Type:  resourceType, | ||||
| 					Class: resourceClass, | ||||
| 					Name:  resourceName, | ||||
| 				}, | ||||
| 				Action: action, | ||||
| 			} | ||||
|  | @ -55,6 +62,19 @@ func ResolveScopeSpecifiers(ctx context.Context, scopeSpecs []string) []auth.Acc | |||
| 	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
 | ||||
| // `scope` parameter into a list of standard access objects.
 | ||||
| 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) | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
| // scope list string
 | ||||
| func ToScopeList(access []auth.Access) string { | ||||
| 	var s []string | ||||
| 	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, ",") | ||||
| } | ||||
|  | @ -102,6 +129,7 @@ func (issuer *TokenIssuer) CreateJWT(subject string, audience string, grantedAcc | |||
| 
 | ||||
| 		accessEntries = append(accessEntries, &token.ResourceActions{ | ||||
| 			Type:    resource.Type, | ||||
| 			Class:   resource.Class, | ||||
| 			Name:    resource.Name, | ||||
| 			Actions: actions, | ||||
| 		}) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue