Skip to content

Commit

Permalink
fix(sync): fixed harbor authentication issues on _catalog endpoint
Browse files Browse the repository at this point in the history
Signed-off-by: Petu Eusebiu <[email protected]>
  • Loading branch information
eusebiu-constantin-petu-dbk committed Jan 18, 2025
1 parent d6b38c0 commit 5a22a14
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 20 deletions.
1 change: 1 addition & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,5 @@ var (
ErrInvalidSearchQuery = errors.New("invalid search query")
ErrImageNotFound = errors.New("image not found")
ErrAmbiguousInput = errors.New("input is not specific enough")
ErrUnexpectedAuthHeader = errors.New("expected bearer auth header, received basic")
)
53 changes: 33 additions & 20 deletions pkg/extensions/sync/httpclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

zerr "zotregistry.dev/zot/errors"
"zotregistry.dev/zot/pkg/api/constants"
"zotregistry.dev/zot/pkg/common"
"zotregistry.dev/zot/pkg/log"
)
Expand Down Expand Up @@ -165,7 +166,7 @@ func (httpClient *Client) Ping() bool {
return false
}

httpClient.getAuthType(resp)
httpClient.authType = getAuthType(resp)

if resp.StatusCode >= http.StatusOK && resp.StatusCode <= http.StatusForbidden {
return true
Expand Down Expand Up @@ -220,21 +221,6 @@ func (httpClient *Client) MakeGetRequest(ctx context.Context, resultPtr interfac
return body, resp.Header, resp.StatusCode, err
}

func (httpClient *Client) getAuthType(resp *http.Response) {
authHeader := resp.Header.Get("www-authenticate")

authHeaderLower := strings.ToLower(authHeader)

//nolint: gocritic
if strings.Contains(authHeaderLower, "bearer") {
httpClient.authType = tokenAuth
} else if strings.Contains(authHeaderLower, "basic") {
httpClient.authType = basicAuth
} else {
httpClient.authType = noneAuth
}
}

func (httpClient *Client) setupAuth(req *http.Request, namespace string) error {
if httpClient.authType == tokenAuth {
token, err := httpClient.getToken(req.URL.String(), namespace)
Expand Down Expand Up @@ -298,7 +284,15 @@ func (httpClient *Client) makeAndDoRequest(method, mediaType, namespace, urlStr
return nil, nil, err
}

if err := httpClient.setupAuth(req, namespace); err != nil {
err = httpClient.setupAuth(req, namespace)
if err != nil {
// harbor catalog requests return basicAuth by default, even if bearer is used on the rest of endpoints.
if errors.Is(err, zerr.ErrUnexpectedAuthHeader) &&
strings.Contains(urlStr, constants.ExtCatalogPrefix) {
// try with basic auth
return httpClient.get(context.Background(), urlStr, true)
}

return nil, nil, err
}

Expand Down Expand Up @@ -371,7 +365,7 @@ func (httpClient *Client) getToken(urlStr, namespace string) (*bearerToken, erro
return nil, err
}

challengeParams, err := parseAuthHeader(resp)
challengeParams, err := parseBearerAuthHeader(resp)
if err != nil {
return nil, err
}
Expand All @@ -384,6 +378,21 @@ func (httpClient *Client) getToken(urlStr, namespace string) (*bearerToken, erro
return httpClient.getTokenFromURL(tokenURL.String(), namespace)
}

func getAuthType(resp *http.Response) authType {
authHeader := resp.Header.Get("www-authenticate")

authHeaderLower := strings.ToLower(authHeader)

//nolint: gocritic
if strings.Contains(authHeaderLower, "bearer") {
return tokenAuth
} else if strings.Contains(authHeaderLower, "basic") {
return basicAuth
} else {
return noneAuth
}
}

func newBearerToken(blob []byte) (*bearerToken, error) {
token := new(bearerToken)
if err := json.Unmarshal(blob, &token); err != nil {
Expand Down Expand Up @@ -426,14 +435,18 @@ func getTokenURLFromChallengeParams(params challengeParams, account string) (*ur
return parsedRealm, nil
}

func parseAuthHeader(resp *http.Response) (challengeParams, error) {
func parseBearerAuthHeader(resp *http.Response) (challengeParams, error) {
authHeader := resp.Header.Get("www-authenticate")

authHeaderSlice := strings.Split(authHeader, ",")

params := challengeParams{}

for _, elem := range authHeaderSlice {
if strings.Contains(strings.ToLower(elem), "basic") {
return params, zerr.ErrUnexpectedAuthHeader
}

if strings.Contains(strings.ToLower(elem), "bearer") {
elem = strings.Split(elem, " ")[1]
}
Expand Down Expand Up @@ -469,7 +482,7 @@ func parseAuthHeader(resp *http.Response) (challengeParams, error) {
func needsRetryWithUpdatedScope(err error, resp *http.Response) (bool, challengeParams) {
params := challengeParams{}
if err == nil && resp.StatusCode == http.StatusUnauthorized {
params, err = parseAuthHeader(resp)
params, err = parseBearerAuthHeader(resp)
if err != nil {
return false, params
}
Expand Down

0 comments on commit 5a22a14

Please sign in to comment.