diff --git a/docs/auth.go b/docs/auth.go index a8fdb675..8351cd91 100644 --- a/docs/auth.go +++ b/docs/auth.go @@ -15,11 +15,16 @@ import ( registrytypes "github.com/docker/engine-api/types/registry" ) +const ( + // AuthClientID is used the ClientID used for the token server + AuthClientID = "docker" +) + // loginV1 tries to register/login to the v1 registry server. -func loginV1(authConfig *types.AuthConfig, apiEndpoint APIEndpoint, userAgent string) (string, error) { +func loginV1(authConfig *types.AuthConfig, apiEndpoint APIEndpoint, userAgent string) (string, string, error) { registryEndpoint, err := apiEndpoint.ToV1Endpoint(userAgent, nil) if err != nil { - return "", err + return "", "", err } serverAddress := registryEndpoint.String() @@ -27,48 +32,47 @@ func loginV1(authConfig *types.AuthConfig, apiEndpoint APIEndpoint, userAgent st logrus.Debugf("attempting v1 login to registry endpoint %s", registryEndpoint) if serverAddress == "" { - return "", fmt.Errorf("Server Error: Server Address not set.") + return "", "", fmt.Errorf("Server Error: Server Address not set.") } loginAgainstOfficialIndex := serverAddress == IndexServer req, err := http.NewRequest("GET", serverAddress+"users/", nil) if err != nil { - return "", err + return "", "", err } req.SetBasicAuth(authConfig.Username, authConfig.Password) resp, err := registryEndpoint.client.Do(req) if err != nil { // fallback when request could not be completed - return "", fallbackError{ + return "", "", fallbackError{ err: err, } } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { - return "", err + return "", "", err } if resp.StatusCode == http.StatusOK { - return "Login Succeeded", nil + return "Login Succeeded", "", nil } else if resp.StatusCode == http.StatusUnauthorized { if loginAgainstOfficialIndex { - return "", fmt.Errorf("Wrong login/password, please try again. Haven't got a Docker ID? Create one at https://hub.docker.com") + return "", "", fmt.Errorf("Wrong login/password, please try again. Haven't got a Docker ID? Create one at https://hub.docker.com") } - return "", fmt.Errorf("Wrong login/password, please try again") + return "", "", fmt.Errorf("Wrong login/password, please try again") } else if resp.StatusCode == http.StatusForbidden { if loginAgainstOfficialIndex { - return "", fmt.Errorf("Login: Account is not active. Please check your e-mail for a confirmation link.") + return "", "", fmt.Errorf("Login: Account is not active. Please check your e-mail for a confirmation link.") } // *TODO: Use registry configuration to determine what this says, if anything? - return "", fmt.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress) + return "", "", fmt.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress) } else if resp.StatusCode == http.StatusInternalServerError { // Issue #14326 logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body) - return "", fmt.Errorf("Internal Server Error") - } else { - return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, - resp.StatusCode, resp.Header) + return "", "", fmt.Errorf("Internal Server Error") } + return "", "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, + resp.StatusCode, resp.Header) } type loginCredentialStore struct { @@ -79,6 +83,14 @@ func (lcs loginCredentialStore) Basic(*url.URL) (string, string) { return lcs.authConfig.Username, lcs.authConfig.Password } +func (lcs loginCredentialStore) RefreshToken(*url.URL, string) string { + return lcs.authConfig.IdentityToken +} + +func (lcs loginCredentialStore) SetRefreshToken(u *url.URL, service, token string) { + lcs.authConfig.IdentityToken = token +} + type fallbackError struct { err error } @@ -90,7 +102,7 @@ func (err fallbackError) Error() string { // loginV2 tries to login to the v2 registry server. The given registry // endpoint will be pinged to get authorization challenges. These challenges // will be used to authenticate against the registry to validate credentials. -func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent string) (string, error) { +func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent string) (string, string, error) { logrus.Debugf("attempting v2 login to registry endpoint %s", endpoint) modifiers := DockerHeaders(userAgent, nil) @@ -101,14 +113,21 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin if !foundV2 { err = fallbackError{err: err} } - return "", err + return "", "", err } + credentialAuthConfig := *authConfig creds := loginCredentialStore{ - authConfig: authConfig, + authConfig: &credentialAuthConfig, } - tokenHandler := auth.NewTokenHandler(authTransport, creds, "") + tokenHandlerOptions := auth.TokenHandlerOptions{ + Transport: authTransport, + Credentials: creds, + OfflineAccess: true, + ClientID: AuthClientID, + } + tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) basicHandler := auth.NewBasicHandler(creds) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) tr := transport.NewTransport(authTransport, modifiers...) @@ -124,7 +143,7 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin if !foundV2 { err = fallbackError{err: err} } - return "", err + return "", "", err } resp, err := loginClient.Do(req) @@ -132,7 +151,7 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin if !foundV2 { err = fallbackError{err: err} } - return "", err + return "", "", err } defer resp.Body.Close() @@ -142,10 +161,10 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin if !foundV2 { err = fallbackError{err: err} } - return "", err + return "", "", err } - return "Login Succeeded", nil + return "Login Succeeded", credentialAuthConfig.IdentityToken, nil } diff --git a/docs/service.go b/docs/service.go index 1439e9d7..acafc34b 100644 --- a/docs/service.go +++ b/docs/service.go @@ -2,6 +2,7 @@ package registry import ( "crypto/tls" + "fmt" "net/http" "net/url" "strings" @@ -34,10 +35,19 @@ func (s *Service) ServiceConfig() *registrytypes.ServiceConfig { // Auth contacts the public registry with the provided credentials, // and returns OK if authentication was successful. // It can be used to verify the validity of a client's credentials. -func (s *Service) Auth(authConfig *types.AuthConfig, userAgent string) (status string, err error) { - endpoints, err := s.LookupPushEndpoints(authConfig.ServerAddress) +func (s *Service) Auth(authConfig *types.AuthConfig, userAgent string) (status, token string, err error) { + serverAddress := authConfig.ServerAddress + if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") { + serverAddress = "https://" + serverAddress + } + u, err := url.Parse(serverAddress) if err != nil { - return "", err + return "", "", fmt.Errorf("unable to parse server address: %v", err) + } + + endpoints, err := s.LookupPushEndpoints(u.Host) + if err != nil { + return "", "", err } for _, endpoint := range endpoints { @@ -46,7 +56,7 @@ func (s *Service) Auth(authConfig *types.AuthConfig, userAgent string) (status s login = loginV1 } - status, err = login(authConfig, endpoint, userAgent) + status, token, err = login(authConfig, endpoint, userAgent) if err == nil { return } @@ -55,10 +65,10 @@ func (s *Service) Auth(authConfig *types.AuthConfig, userAgent string) (status s logrus.Infof("Error logging in to %s endpoint, trying next endpoint: %v", endpoint.Version, err) continue } - return "", err + return "", "", err } - return "", err + return "", "", err } // splitReposSearchTerm breaks a search term into an index name and remote name diff --git a/docs/service_v2.go b/docs/service_v2.go index 744be691..4113d57d 100644 --- a/docs/service_v2.go +++ b/docs/service_v2.go @@ -10,7 +10,7 @@ import ( func (s *Service) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) { var cfg = tlsconfig.ServerDefault tlsConfig := &cfg - if hostname == DefaultNamespace { + if hostname == DefaultNamespace || hostname == DefaultV1Registry.Host { // v2 mirrors for _, mirror := range s.config.Mirrors { if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") {