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:
Josh Hawn 2014-12-17 10:57:05 -08:00
parent 56f685c0dd
commit 88de2e11fb
5 changed files with 72 additions and 60 deletions

View file

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

View file

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

View file

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

View file

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