buildx/util/imagetools/auth.go

104 lines
2.2 KiB
Go

package imagetools
import (
"context"
"encoding/base64"
"encoding/json"
"net/http"
"sync"
"time"
"github.com/containerd/containerd/v2/core/remotes/docker"
"github.com/distribution/reference"
"github.com/docker/cli/cli/config/types"
)
type authConfig struct {
mu sync.Mutex
authConfigCache map[string]authConfigCacheEntry
cfg Auth
}
type authConfigCacheEntry struct {
Created time.Time
Auth types.AuthConfig
}
func newAuthConfig(a Auth) *authConfig {
return &authConfig{
authConfigCache: map[string]authConfigCacheEntry{},
cfg: a,
}
}
func (a *authConfig) credentials(host string) (string, string, error) {
ac, err := a.authConfig(host)
if err != nil {
return "", "", err
}
if ac.IdentityToken != "" {
return "", ac.IdentityToken, nil
}
return ac.Username, ac.Password, nil
}
func (a *authConfig) authConfig(host string) (types.AuthConfig, error) {
const defaultExpiration = 2 * time.Minute
if host == "registry-1.docker.io" {
host = "https://index.docker.io/v1/"
}
a.mu.Lock()
defer a.mu.Unlock()
if c, ok := a.authConfigCache[host]; ok && time.Since(c.Created) <= defaultExpiration {
return c.Auth, nil
}
ac, err := a.cfg.GetAuthConfig(host)
if err != nil {
return types.AuthConfig{}, err
}
a.authConfigCache[host] = authConfigCacheEntry{
Created: time.Now(),
Auth: ac,
}
return ac, nil
}
func RegistryAuthForRef(ref string, a Auth) (string, error) {
if a == nil {
return "", nil
}
r, err := parseRef(ref)
if err != nil {
return "", err
}
host := reference.Domain(r)
if host == "docker.io" {
host = "https://index.docker.io/v1/"
}
ac, err := a.GetAuthConfig(host)
if err != nil {
return "", err
}
buf, err := json.Marshal(ac)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(buf), nil
}
type withBearerAuthorizer struct {
docker.Authorizer
AuthConfig *authConfig
}
func (a *withBearerAuthorizer) Authorize(ctx context.Context, req *http.Request) error {
ac, err := a.AuthConfig.authConfig(req.Host)
if err == nil && ac.RegistryToken != "" {
req.Header.Set("Authorization", "Bearer "+ac.RegistryToken)
return nil
}
return a.Authorizer.Authorize(ctx, req)
}