Refactor authorization challenges to its own package
Split challenges into its own package. Avoids possible import cycle with challenges from client. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
parent
02f4195788
commit
a1a73884f9
8 changed files with 42 additions and 37 deletions
|
@ -1,4 +1,4 @@
|
||||||
package auth
|
package challenge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
|
@ -1,4 +1,4 @@
|
||||||
package auth
|
package challenge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -18,12 +18,12 @@ type Challenge struct {
|
||||||
Parameters map[string]string
|
Parameters map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChallengeManager manages the challenges for endpoints.
|
// Manager manages the challenges for endpoints.
|
||||||
// The challenges are pulled out of HTTP responses. Only
|
// The challenges are pulled out of HTTP responses. Only
|
||||||
// responses which expect challenges should be added to
|
// responses which expect challenges should be added to
|
||||||
// the manager, since a non-unauthorized request will be
|
// the manager, since a non-unauthorized request will be
|
||||||
// viewed as not requiring challenges.
|
// viewed as not requiring challenges.
|
||||||
type ChallengeManager interface {
|
type Manager interface {
|
||||||
// GetChallenges returns the challenges for the given
|
// GetChallenges returns the challenges for the given
|
||||||
// endpoint URL.
|
// endpoint URL.
|
||||||
GetChallenges(endpoint url.URL) ([]Challenge, error)
|
GetChallenges(endpoint url.URL) ([]Challenge, error)
|
||||||
|
@ -37,19 +37,19 @@ type ChallengeManager interface {
|
||||||
AddResponse(resp *http.Response) error
|
AddResponse(resp *http.Response) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimpleChallengeManager returns an instance of
|
// NewSimpleManager returns an instance of
|
||||||
// ChallengeManger which only maps endpoints to challenges
|
// Manger which only maps endpoints to challenges
|
||||||
// based on the responses which have been added the
|
// based on the responses which have been added the
|
||||||
// manager. The simple manager will make no attempt to
|
// manager. The simple manager will make no attempt to
|
||||||
// perform requests on the endpoints or cache the responses
|
// perform requests on the endpoints or cache the responses
|
||||||
// to a backend.
|
// to a backend.
|
||||||
func NewSimpleChallengeManager() ChallengeManager {
|
func NewSimpleManager() Manager {
|
||||||
return &simpleChallengeManager{
|
return &simpleManager{
|
||||||
Challanges: make(map[string][]Challenge),
|
Challanges: make(map[string][]Challenge),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type simpleChallengeManager struct {
|
type simpleManager struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
Challanges map[string][]Challenge
|
Challanges map[string][]Challenge
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ func normalizeURL(endpoint *url.URL) {
|
||||||
endpoint.Host = canonicalAddr(endpoint)
|
endpoint.Host = canonicalAddr(endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
|
func (m *simpleManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
|
||||||
normalizeURL(&endpoint)
|
normalizeURL(&endpoint)
|
||||||
|
|
||||||
m.RLock()
|
m.RLock()
|
||||||
|
@ -68,7 +68,7 @@ func (m *simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, e
|
||||||
return challenges, nil
|
return challenges, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *simpleChallengeManager) AddResponse(resp *http.Response) error {
|
func (m *simpleManager) AddResponse(resp *http.Response) error {
|
||||||
challenges := ResponseChallenges(resp)
|
challenges := ResponseChallenges(resp)
|
||||||
if resp.Request == nil {
|
if resp.Request == nil {
|
||||||
return fmt.Errorf("missing request reference")
|
return fmt.Errorf("missing request reference")
|
|
@ -1,4 +1,4 @@
|
||||||
package auth
|
package challenge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -50,7 +50,7 @@ func TestAuthChallengeNormalization(t *testing.T) {
|
||||||
|
|
||||||
func testAuthChallengeNormalization(t *testing.T, host string) {
|
func testAuthChallengeNormalization(t *testing.T, host string) {
|
||||||
|
|
||||||
scm := NewSimpleChallengeManager()
|
scm := NewSimpleManager()
|
||||||
|
|
||||||
url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host))
|
url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -86,7 +86,7 @@ func testAuthChallengeNormalization(t *testing.T, host string) {
|
||||||
|
|
||||||
func testAuthChallengeConcurrent(t *testing.T, host string) {
|
func testAuthChallengeConcurrent(t *testing.T, host string) {
|
||||||
|
|
||||||
scm := NewSimpleChallengeManager()
|
scm := NewSimpleManager()
|
||||||
|
|
||||||
url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host))
|
url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host))
|
||||||
if err != nil {
|
if err != nil {
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/distribution/registry/client"
|
"github.com/docker/distribution/registry/client"
|
||||||
|
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||||
"github.com/docker/distribution/registry/client/transport"
|
"github.com/docker/distribution/registry/client/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ type CredentialStore interface {
|
||||||
// schemes. The handlers are tried in order, the higher priority authentication
|
// schemes. The handlers are tried in order, the higher priority authentication
|
||||||
// methods should be first. The challengeMap holds a list of challenges for
|
// methods should be first. The challengeMap holds a list of challenges for
|
||||||
// a given root API endpoint (for example "https://registry-1.docker.io/v2/").
|
// a given root API endpoint (for example "https://registry-1.docker.io/v2/").
|
||||||
func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler) transport.RequestModifier {
|
func NewAuthorizer(manager challenge.Manager, handlers ...AuthenticationHandler) transport.RequestModifier {
|
||||||
return &endpointAuthorizer{
|
return &endpointAuthorizer{
|
||||||
challenges: manager,
|
challenges: manager,
|
||||||
handlers: handlers,
|
handlers: handlers,
|
||||||
|
@ -66,7 +67,7 @@ func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
type endpointAuthorizer struct {
|
type endpointAuthorizer struct {
|
||||||
challenges ChallengeManager
|
challenges challenge.Manager
|
||||||
handlers []AuthenticationHandler
|
handlers []AuthenticationHandler
|
||||||
transport http.RoundTripper
|
transport http.RoundTripper
|
||||||
}
|
}
|
||||||
|
@ -94,11 +95,11 @@ func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error {
|
||||||
|
|
||||||
if len(challenges) > 0 {
|
if len(challenges) > 0 {
|
||||||
for _, handler := range ea.handlers {
|
for _, handler := range ea.handlers {
|
||||||
for _, challenge := range challenges {
|
for _, c := range challenges {
|
||||||
if challenge.Scheme != handler.Scheme() {
|
if c.Scheme != handler.Scheme() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil {
|
if err := handler.AuthorizeRequest(req, c.Parameters); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||||
"github.com/docker/distribution/registry/client/transport"
|
"github.com/docker/distribution/registry/client/transport"
|
||||||
"github.com/docker/distribution/testutil"
|
"github.com/docker/distribution/testutil"
|
||||||
)
|
)
|
||||||
|
@ -65,7 +66,7 @@ func testServerWithAuth(rrm testutil.RequestResponseMap, authenticate string, au
|
||||||
|
|
||||||
// ping pings the provided endpoint to determine its required authorization challenges.
|
// ping pings the provided endpoint to determine its required authorization challenges.
|
||||||
// If a version header is provided, the versions will be returned.
|
// If a version header is provided, the versions will be returned.
|
||||||
func ping(manager ChallengeManager, endpoint, versionHeader string) ([]APIVersion, error) {
|
func ping(manager challenge.Manager, endpoint, versionHeader string) ([]APIVersion, error) {
|
||||||
resp, err := http.Get(endpoint)
|
resp, err := http.Get(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -149,7 +150,7 @@ func TestEndpointAuthorizeToken(t *testing.T) {
|
||||||
e, c := testServerWithAuth(m, authenicate, validCheck)
|
e, c := testServerWithAuth(m, authenicate, validCheck)
|
||||||
defer c()
|
defer c()
|
||||||
|
|
||||||
challengeManager1 := NewSimpleChallengeManager()
|
challengeManager1 := challenge.NewSimpleManager()
|
||||||
versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
|
versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -176,7 +177,7 @@ func TestEndpointAuthorizeToken(t *testing.T) {
|
||||||
e2, c2 := testServerWithAuth(m, authenicate, validCheck)
|
e2, c2 := testServerWithAuth(m, authenicate, validCheck)
|
||||||
defer c2()
|
defer c2()
|
||||||
|
|
||||||
challengeManager2 := NewSimpleChallengeManager()
|
challengeManager2 := challenge.NewSimpleManager()
|
||||||
versions, err = ping(challengeManager2, e2+"/v2/", "x-multi-api-version")
|
versions, err = ping(challengeManager2, e2+"/v2/", "x-multi-api-version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -273,7 +274,7 @@ func TestEndpointAuthorizeRefreshToken(t *testing.T) {
|
||||||
e, c := testServerWithAuth(m, authenicate, validCheck)
|
e, c := testServerWithAuth(m, authenicate, validCheck)
|
||||||
defer c()
|
defer c()
|
||||||
|
|
||||||
challengeManager1 := NewSimpleChallengeManager()
|
challengeManager1 := challenge.NewSimpleManager()
|
||||||
versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
|
versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -306,7 +307,7 @@ func TestEndpointAuthorizeRefreshToken(t *testing.T) {
|
||||||
e2, c2 := testServerWithAuth(m, authenicate, validCheck)
|
e2, c2 := testServerWithAuth(m, authenicate, validCheck)
|
||||||
defer c2()
|
defer c2()
|
||||||
|
|
||||||
challengeManager2 := NewSimpleChallengeManager()
|
challengeManager2 := challenge.NewSimpleManager()
|
||||||
versions, err = ping(challengeManager2, e2+"/v2/", "x-api-version")
|
versions, err = ping(challengeManager2, e2+"/v2/", "x-api-version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -339,7 +340,7 @@ func TestEndpointAuthorizeRefreshToken(t *testing.T) {
|
||||||
e3, c3 := testServerWithAuth(m, authenicate, validCheck)
|
e3, c3 := testServerWithAuth(m, authenicate, validCheck)
|
||||||
defer c3()
|
defer c3()
|
||||||
|
|
||||||
challengeManager3 := NewSimpleChallengeManager()
|
challengeManager3 := challenge.NewSimpleManager()
|
||||||
versions, err = ping(challengeManager3, e3+"/v2/", "x-api-version")
|
versions, err = ping(challengeManager3, e3+"/v2/", "x-api-version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -401,7 +402,7 @@ func TestEndpointAuthorizeV2RefreshToken(t *testing.T) {
|
||||||
e, c := testServerWithAuth(m, authenicate, validCheck)
|
e, c := testServerWithAuth(m, authenicate, validCheck)
|
||||||
defer c()
|
defer c()
|
||||||
|
|
||||||
challengeManager1 := NewSimpleChallengeManager()
|
challengeManager1 := challenge.NewSimpleManager()
|
||||||
versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
|
versions, err := ping(challengeManager1, e+"/v2/", "x-api-version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -496,7 +497,7 @@ func TestEndpointAuthorizeTokenBasic(t *testing.T) {
|
||||||
password: password,
|
password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeManager := NewSimpleChallengeManager()
|
challengeManager := challenge.NewSimpleManager()
|
||||||
_, err := ping(challengeManager, e+"/v2/", "")
|
_, err := ping(challengeManager, e+"/v2/", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -614,7 +615,7 @@ func TestEndpointAuthorizeTokenBasicWithExpiresIn(t *testing.T) {
|
||||||
password: password,
|
password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeManager := NewSimpleChallengeManager()
|
challengeManager := challenge.NewSimpleManager()
|
||||||
_, err := ping(challengeManager, e+"/v2/", "")
|
_, err := ping(challengeManager, e+"/v2/", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -765,7 +766,7 @@ func TestEndpointAuthorizeTokenBasicWithExpiresInAndIssuedAt(t *testing.T) {
|
||||||
password: password,
|
password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeManager := NewSimpleChallengeManager()
|
challengeManager := challenge.NewSimpleManager()
|
||||||
_, err := ping(challengeManager, e+"/v2/", "")
|
_, err := ping(challengeManager, e+"/v2/", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -845,7 +846,7 @@ func TestEndpointAuthorizeBasic(t *testing.T) {
|
||||||
password: password,
|
password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
challengeManager := NewSimpleChallengeManager()
|
challengeManager := challenge.NewSimpleManager()
|
||||||
_, err := ping(challengeManager, e+"/v2/", "")
|
_, err := ping(challengeManager, e+"/v2/", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/distribution/context"
|
"github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/registry/client/auth"
|
"github.com/docker/distribution/registry/client/auth"
|
||||||
|
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||||
)
|
)
|
||||||
|
|
||||||
const challengeHeader = "Docker-Distribution-Api-Version"
|
const challengeHeader = "Docker-Distribution-Api-Version"
|
||||||
|
@ -62,7 +63,7 @@ func getAuthURLs(remoteURL string) ([]string, error) {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
for _, c := range auth.ResponseChallenges(resp) {
|
for _, c := range challenge.ResponseChallenges(resp) {
|
||||||
if strings.EqualFold(c.Scheme, "bearer") {
|
if strings.EqualFold(c.Scheme, "bearer") {
|
||||||
authURLs = append(authURLs, c.Parameters["realm"])
|
authURLs = append(authURLs, c.Parameters["realm"])
|
||||||
}
|
}
|
||||||
|
@ -71,7 +72,7 @@ func getAuthURLs(remoteURL string) ([]string, error) {
|
||||||
return authURLs, nil
|
return authURLs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ping(manager auth.ChallengeManager, endpoint, versionHeader string) error {
|
func ping(manager challenge.Manager, endpoint, versionHeader string) error {
|
||||||
resp, err := http.Get(endpoint)
|
resp, err := http.Get(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/docker/distribution/manifest/schema1"
|
"github.com/docker/distribution/manifest/schema1"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/distribution/registry/client/auth"
|
"github.com/docker/distribution/registry/client/auth"
|
||||||
|
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||||
"github.com/docker/distribution/registry/proxy/scheduler"
|
"github.com/docker/distribution/registry/proxy/scheduler"
|
||||||
"github.com/docker/distribution/registry/storage"
|
"github.com/docker/distribution/registry/storage"
|
||||||
"github.com/docker/distribution/registry/storage/cache/memory"
|
"github.com/docker/distribution/registry/storage/cache/memory"
|
||||||
|
@ -77,7 +78,7 @@ func (m *mockChallenger) credentialStore() auth.CredentialStore {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockChallenger) challengeManager() auth.ChallengeManager {
|
func (m *mockChallenger) challengeManager() challenge.Manager {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/distribution/registry/client"
|
"github.com/docker/distribution/registry/client"
|
||||||
"github.com/docker/distribution/registry/client/auth"
|
"github.com/docker/distribution/registry/client/auth"
|
||||||
|
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||||
"github.com/docker/distribution/registry/client/transport"
|
"github.com/docker/distribution/registry/client/transport"
|
||||||
"github.com/docker/distribution/registry/proxy/scheduler"
|
"github.com/docker/distribution/registry/proxy/scheduler"
|
||||||
"github.com/docker/distribution/registry/storage"
|
"github.com/docker/distribution/registry/storage"
|
||||||
|
@ -102,7 +103,7 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name
|
||||||
remoteURL: *remoteURL,
|
remoteURL: *remoteURL,
|
||||||
authChallenger: &remoteAuthChallenger{
|
authChallenger: &remoteAuthChallenger{
|
||||||
remoteURL: *remoteURL,
|
remoteURL: *remoteURL,
|
||||||
cm: auth.NewSimpleChallengeManager(),
|
cm: challenge.NewSimpleManager(),
|
||||||
cs: cs,
|
cs: cs,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -177,14 +178,14 @@ func (pr *proxyingRegistry) BlobStatter() distribution.BlobStatter {
|
||||||
// authChallenger encapsulates a request to the upstream to establish credential challenges
|
// authChallenger encapsulates a request to the upstream to establish credential challenges
|
||||||
type authChallenger interface {
|
type authChallenger interface {
|
||||||
tryEstablishChallenges(context.Context) error
|
tryEstablishChallenges(context.Context) error
|
||||||
challengeManager() auth.ChallengeManager
|
challengeManager() challenge.Manager
|
||||||
credentialStore() auth.CredentialStore
|
credentialStore() auth.CredentialStore
|
||||||
}
|
}
|
||||||
|
|
||||||
type remoteAuthChallenger struct {
|
type remoteAuthChallenger struct {
|
||||||
remoteURL url.URL
|
remoteURL url.URL
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
cm auth.ChallengeManager
|
cm challenge.Manager
|
||||||
cs auth.CredentialStore
|
cs auth.CredentialStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +193,7 @@ func (r *remoteAuthChallenger) credentialStore() auth.CredentialStore {
|
||||||
return r.cs
|
return r.cs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *remoteAuthChallenger) challengeManager() auth.ChallengeManager {
|
func (r *remoteAuthChallenger) challengeManager() challenge.Manager {
|
||||||
return r.cm
|
return r.cm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue