Refactor auth stringSet into common.StringSet
Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)
This commit is contained in:
parent
56f685c0dd
commit
88de2e11fb
5 changed files with 72 additions and 60 deletions
|
@ -12,8 +12,10 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker-registry/auth"
|
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
|
||||||
|
"github.com/docker/docker-registry/auth"
|
||||||
|
"github.com/docker/docker-registry/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// accessSet maps a typed, named resource to
|
// accessSet maps a typed, named resource to
|
||||||
|
@ -31,13 +33,13 @@ func newAccessSet(accessItems ...auth.Access) accessSet {
|
||||||
Name: access.Name,
|
Name: access.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
set := accessSet[resource]
|
set, exists := accessSet[resource]
|
||||||
if set == nil {
|
if !exists {
|
||||||
set = make(actionSet)
|
set = newActionSet()
|
||||||
accessSet[resource] = set
|
accessSet[resource] = set
|
||||||
}
|
}
|
||||||
|
|
||||||
set[access.Action] = struct{}{}
|
set.Add(access.Action)
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessSet
|
return accessSet
|
||||||
|
@ -47,7 +49,7 @@ func newAccessSet(accessItems ...auth.Access) accessSet {
|
||||||
func (s accessSet) contains(access auth.Access) bool {
|
func (s accessSet) contains(access auth.Access) bool {
|
||||||
actionSet, ok := s[access.Resource]
|
actionSet, ok := s[access.Resource]
|
||||||
if ok {
|
if ok {
|
||||||
return actionSet.contains(access.Action)
|
return actionSet.Contains(access.Action)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -60,7 +62,7 @@ func (s accessSet) scopeParam() string {
|
||||||
scopes := make([]string, 0, len(s))
|
scopes := make([]string, 0, len(s))
|
||||||
|
|
||||||
for resource, actionSet := range s {
|
for resource, actionSet := range s {
|
||||||
actions := strings.Join(actionSet.keys(), ",")
|
actions := strings.Join(actionSet.Keys(), ",")
|
||||||
scopes = append(scopes, fmt.Sprintf("%s:%s:%s", resource.Type, resource.Name, actions))
|
scopes = append(scopes, fmt.Sprintf("%s:%s:%s", resource.Type, resource.Name, actions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,8 +242,8 @@ func (ac *accessController) Authorized(req *http.Request, accessItems ...auth.Ac
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyOpts := VerifyOptions{
|
verifyOpts := VerifyOptions{
|
||||||
TrustedIssuers: newStringSet(ac.issuer),
|
TrustedIssuers: common.NewStringSet(ac.issuer),
|
||||||
AccpetedAudiences: newStringSet(ac.service),
|
AccpetedAudiences: common.NewStringSet(ac.service),
|
||||||
Roots: ac.rootCerts,
|
Roots: ac.rootCerts,
|
||||||
TrustedKeys: ac.trustedKeys,
|
TrustedKeys: ac.trustedKeys,
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
|
||||||
"github.com/docker/docker-registry/auth"
|
"github.com/docker/docker-registry/auth"
|
||||||
|
"github.com/docker/docker-registry/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -85,8 +86,8 @@ type Token struct {
|
||||||
// VerifyOptions is used to specify
|
// VerifyOptions is used to specify
|
||||||
// options when verifying a JSON Web Token.
|
// options when verifying a JSON Web Token.
|
||||||
type VerifyOptions struct {
|
type VerifyOptions struct {
|
||||||
TrustedIssuers stringSet
|
TrustedIssuers common.StringSet
|
||||||
AccpetedAudiences stringSet
|
AccpetedAudiences common.StringSet
|
||||||
Roots *x509.CertPool
|
Roots *x509.CertPool
|
||||||
TrustedKeys map[string]libtrust.PublicKey
|
TrustedKeys map[string]libtrust.PublicKey
|
||||||
}
|
}
|
||||||
|
@ -155,13 +156,13 @@ func (t *Token) Verify(verifyOpts VerifyOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the Issuer claim is a trusted authority.
|
// Verify that the Issuer claim is a trusted authority.
|
||||||
if !verifyOpts.TrustedIssuers.contains(t.Claims.Issuer) {
|
if !verifyOpts.TrustedIssuers.Contains(t.Claims.Issuer) {
|
||||||
log.Errorf("token from untrusted issuer: %q", t.Claims.Issuer)
|
log.Errorf("token from untrusted issuer: %q", t.Claims.Issuer)
|
||||||
return ErrInvalidToken
|
return ErrInvalidToken
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the Audience claim is allowed.
|
// Verify that the Audience claim is allowed.
|
||||||
if !verifyOpts.AccpetedAudiences.contains(t.Claims.Audience) {
|
if !verifyOpts.AccpetedAudiences.Contains(t.Claims.Audience) {
|
||||||
log.Errorf("token intended for another audience: %q", t.Claims.Audience)
|
log.Errorf("token intended for another audience: %q", t.Claims.Audience)
|
||||||
return ErrInvalidToken
|
return ErrInvalidToken
|
||||||
}
|
}
|
||||||
|
@ -319,14 +320,14 @@ func (t *Token) accessSet() accessSet {
|
||||||
Name: resourceActions.Name,
|
Name: resourceActions.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
set := accessSet[resource]
|
set, exists := accessSet[resource]
|
||||||
if set == nil {
|
if !exists {
|
||||||
set = make(actionSet)
|
set = newActionSet()
|
||||||
accessSet[resource] = set
|
accessSet[resource] = set
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, action := range resourceActions.Actions {
|
for _, action := range resourceActions.Actions {
|
||||||
set[action] = struct{}{}
|
set.Add(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker-registry/auth"
|
|
||||||
"github.com/docker/libtrust"
|
"github.com/docker/libtrust"
|
||||||
|
|
||||||
|
"github.com/docker/docker-registry/auth"
|
||||||
|
"github.com/docker/docker-registry/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeRootKeys(numKeys int) ([]libtrust.PrivateKey, error) {
|
func makeRootKeys(numKeys int) ([]libtrust.PrivateKey, error) {
|
||||||
|
@ -194,8 +196,8 @@ func TestTokenVerify(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyOps := VerifyOptions{
|
verifyOps := VerifyOptions{
|
||||||
TrustedIssuers: newStringSet(issuer),
|
TrustedIssuers: common.NewStringSet(issuer),
|
||||||
AccpetedAudiences: newStringSet(audience),
|
AccpetedAudiences: common.NewStringSet(audience),
|
||||||
Roots: rootPool,
|
Roots: rootPool,
|
||||||
TrustedKeys: trustedKeys,
|
TrustedKeys: trustedKeys,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker-registry/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// joseBase64UrlEncode encodes the given data using the standard base64 url
|
// joseBase64UrlEncode encodes the given data using the standard base64 url
|
||||||
|
@ -31,47 +33,17 @@ func joseBase64UrlDecode(s string) ([]byte, error) {
|
||||||
return base64.URLEncoding.DecodeString(s)
|
return base64.URLEncoding.DecodeString(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stringSet is a useful type for looking up strings.
|
|
||||||
type stringSet map[string]struct{}
|
|
||||||
|
|
||||||
func newStringSet(strs ...string) stringSet {
|
|
||||||
set := make(stringSet, len(strs))
|
|
||||||
for _, str := range strs {
|
|
||||||
set[str] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return set
|
|
||||||
}
|
|
||||||
|
|
||||||
// contains returns whether the given key is in this StringSet.
|
|
||||||
func (ss stringSet) contains(key string) bool {
|
|
||||||
_, ok := ss[key]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// keys returns a slice of all keys in this stringSet.
|
|
||||||
func (ss stringSet) keys() []string {
|
|
||||||
keys := make([]string, 0, len(ss))
|
|
||||||
|
|
||||||
for key := range ss {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// actionSet is a special type of stringSet.
|
// actionSet is a special type of stringSet.
|
||||||
type actionSet stringSet
|
type actionSet struct {
|
||||||
|
common.StringSet
|
||||||
|
}
|
||||||
|
|
||||||
// contains calls stringSet.contains() for
|
func newActionSet(actions ...string) actionSet {
|
||||||
|
return actionSet{common.NewStringSet(actions...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains calls StringSet.Contains() for
|
||||||
// either "*" or the given action string.
|
// either "*" or the given action string.
|
||||||
func (s actionSet) contains(action string) bool {
|
func (s actionSet) Contains(action string) bool {
|
||||||
ss := stringSet(s)
|
return s.StringSet.Contains("*") || s.StringSet.Contains(action)
|
||||||
|
|
||||||
return ss.contains("*") || ss.contains(action)
|
|
||||||
}
|
|
||||||
|
|
||||||
// keys wraps stringSet.keys()
|
|
||||||
func (s actionSet) keys() []string {
|
|
||||||
return stringSet(s).keys()
|
|
||||||
}
|
}
|
||||||
|
|
35
common/stringset.go
Normal file
35
common/stringset.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
// StringSet is a useful type for looking up strings.
|
||||||
|
type StringSet map[string]struct{}
|
||||||
|
|
||||||
|
// NewStringSet creates a new StringSet with the given strings.
|
||||||
|
func NewStringSet(keys ...string) StringSet {
|
||||||
|
ss := make(StringSet, len(keys))
|
||||||
|
ss.Add(keys...)
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add inserts the given keys into this StringSet.
|
||||||
|
func (ss StringSet) Add(keys ...string) {
|
||||||
|
for _, key := range keys {
|
||||||
|
ss[key] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns whether the given key is in this StringSet.
|
||||||
|
func (ss StringSet) Contains(key string) bool {
|
||||||
|
_, ok := ss[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns a slice of all keys in this StringSet.
|
||||||
|
func (ss StringSet) Keys() []string {
|
||||||
|
keys := make([]string, 0, len(ss))
|
||||||
|
|
||||||
|
for key := range ss {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
Loading…
Reference in a new issue