Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use oras-credential-go to do auth #932

Merged
merged 7 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 2 additions & 13 deletions cmd/oras/internal/option/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"strconv"
"strings"

credentials "github.com/oras-project/oras-credentials-go"
"github.com/spf13/pflag"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
Expand Down Expand Up @@ -219,19 +220,7 @@ func (opts *Remote) authClient(registry string, debug bool) (client *auth.Client
if err != nil {
return nil, err
}
// For a user case with a registry from 'docker.io', the hostname is "registry-1.docker.io"
// According to the the behavior of Docker CLI,
// credential under key "https://index.docker.io/v1/" should be provided
if registry == "docker.io" {
client.Credential = func(ctx context.Context, hostname string) (auth.Credential, error) {
if hostname == "registry-1.docker.io" {
hostname = "https://index.docker.io/v1/"
}
return store.Credential(ctx, hostname)
}
} else {
client.Credential = store.Credential
}
client.Credential = credentials.Credential(store)
}
return
}
Expand Down
20 changes: 4 additions & 16 deletions cmd/oras/root/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"strings"

credentials "github.com/oras-project/oras-credentials-go"
"github.com/spf13/cobra"
"golang.org/x/term"
"oras.land/oras/cmd/oras/internal/option"
Expand Down Expand Up @@ -103,28 +104,15 @@ func runLogin(ctx context.Context, opts loginOptions) (err error) {
}
}

// Ping to ensure credential is valid
remote, err := opts.Remote.NewRegistry(opts.Hostname, opts.Common)
store, err := credential.NewStore(opts.Configs...)
if err != nil {
return err
}
if err = remote.Ping(ctx); err != nil {
return err
}

// Store the validated credential
store, err := credential.NewStore(opts.Configs...)
remote, err := opts.Remote.NewRegistry(opts.Hostname, opts.Common)
if err != nil {
return err
}
// For a user case that login 'docker.io',
// According the the behavior of Docker CLI,
// credential should be added under key "https://index.docker.io/v1/"
hostname := opts.Hostname
if hostname == "docker.io" {
hostname = "https://index.docker.io/v1/"
}
if err := store.Store(hostname, opts.Credential()); err != nil {
if err = credentials.Login(ctx, store, remote, opts.Credential()); err != nil {
return err
}
fmt.Println("Login Succeeded")
Expand Down
10 changes: 2 additions & 8 deletions cmd/oras/root/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package root
import (
"context"

credentials "github.com/oras-project/oras-credentials-go"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"oras.land/oras/internal/credential"
Expand Down Expand Up @@ -61,12 +62,5 @@ func runLogout(ctx context.Context, opts logoutOptions) error {
if err != nil {
return err
}
// For a user case that logout from 'docker.io',
// According the the behavior of Docker CLI,
// credential under key "https://index.docker.io/v1/" should be removed
hostname := opts.hostname
if hostname == "docker.io" {
hostname = "https://index.docker.io/v1/"
}
return store.Erase(hostname)
return credentials.Logout(ctx, store, opts.hostname)
}
5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ module oras.land/oras
go 1.20

require (
github.com/docker/cli v23.0.4+incompatible
github.com/need-being/go-tree v0.1.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/oras-project/oras-credentials-go v0.0.0-20230424070720-ba6b33c40845
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
Expand All @@ -16,11 +16,8 @@ require (
)

require (
github.com/docker/docker v23.0.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.7.0 // indirect
gotest.tools/v3 v3.4.0 // indirect
)
35 changes: 2 additions & 33 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
qweeah marked this conversation as resolved.
Show resolved Hide resolved
github.com/docker/cli v23.0.4+incompatible h1:xClB7PsiATttDHj8ce5qvJcikiApNy7teRR1XkoBZGs=
github.com/docker/cli v23.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/need-being/go-tree v0.1.0 h1:blQrtD006cFm97UDeMUfixwPc9o06A6c+uLaUskdNNw=
Expand All @@ -18,8 +12,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/oras-project/oras-credentials-go v0.0.0-20230424070720-ba6b33c40845 h1:lE/TKx3cblnIRMazUdXgHmoM8TXs4fZqv3EXvEF3A2I=
github.com/oras-project/oras-credentials-go v0.0.0-20230424070720-ba6b33c40845/go.mod h1:yww9XqMCWjNh4Z7S5Ek7KeV9j/yhbyVxgSinGdNNwEg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
Expand All @@ -32,42 +26,17 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
oras.land/oras-go/v2 v2.0.2 h1:3aSQdJ7EUC0ft2e9PjJB9Jzastz5ojPA4LzZ3Q4YbUc=
oras.land/oras-go/v2 v2.0.2/go.mod h1:PWnWc/Kyyg7wUTUsDHshrsJkzuxXzreeMd6NrfdnFSo=
112 changes: 11 additions & 101 deletions internal/credential/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,114 +16,24 @@ limitations under the License.
package credential

import (
"context"
"fmt"
"os"

"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/credentials"
"github.com/docker/cli/cli/config/types"
"oras.land/oras-go/v2/registry/remote/auth"
credentials "github.com/oras-project/oras-credentials-go"
qweeah marked this conversation as resolved.
Show resolved Hide resolved
)

// Store provides credential CRUD operations.
type Store struct {
configs []*configfile.ConfigFile
}

// NewStore generates a store based on the passed in config file path.
func NewStore(configPaths ...string) (*Store, error) {
// NewStore generates a store based on the passed-in config file paths.
func NewStore(configPaths ...string) (credentials.Store, error) {
opts := credentials.StoreOptions{AllowPlaintextPut: true}
if len(configPaths) == 0 {
// No config path passed, load default docker config file.
cfg, err := config.Load(config.Dir())
if err != nil {
return nil, err
}
if !cfg.ContainsAuth() {
cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore)
}

return &Store{
configs: []*configfile.ConfigFile{cfg},
}, nil
// use default docker config file path
return credentials.NewStoreFromDocker(opts)
}

var configs []*configfile.ConfigFile
for _, path := range configPaths {
cfg, err := loadConfigFile(path)
if err != nil {
return nil, fmt.Errorf("%s: %w", path, err)
}
configs = append(configs, cfg)
}

return &Store{
configs: configs,
}, nil
}

// loadConfigFile reads the credential-related configurationfrom the given path.
func loadConfigFile(path string) (*configfile.ConfigFile, error) {
var cfg *configfile.ConfigFile
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
cfg = configfile.New(path)
} else {
return nil, err
}
} else {
file, err := os.Open(path)
var stores []credentials.Store
for _, config := range configPaths {
store, err := credentials.NewStore(config, opts)
if err != nil {
return nil, err
}
defer file.Close()
cfg = configfile.New(path)
if err := cfg.LoadFromReader(file); err != nil {
return nil, err
}
}

if !cfg.ContainsAuth() {
cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore)
}
return cfg, nil
}

// Store stores a credential for a given registry.
func (s *Store) Store(registry string, cred auth.Credential) error {
authConf := types.AuthConfig{
Username: cred.Username,
Password: cred.Password,
ServerAddress: registry,
IdentityToken: cred.RefreshToken,
RegistryToken: cred.AccessToken,
}
return s.configs[0].GetCredentialsStore(registry).Store(authConf)
}

// Erase erases a credential for a given registry.
func (s *Store) Erase(registry string) error {
return s.configs[0].GetCredentialsStore(registry).Erase(registry)
}

// Credential iterates all the config files, returns the first non-empty
// credential in a best-effort way.
func (s *Store) Credential(ctx context.Context, registry string) (auth.Credential, error) {
for _, c := range s.configs {
authConf, err := c.GetCredentialsStore(registry).Get(registry)
if err != nil {
return auth.EmptyCredential, err
}
cred := auth.Credential{
Username: authConf.Username,
Password: authConf.Password,
AccessToken: authConf.RegistryToken,
RefreshToken: authConf.IdentityToken,
}
if cred != auth.EmptyCredential {
return cred, nil
}
stores = append(stores, store)
}
return auth.EmptyCredential, nil
return credentials.NewStoreWithFallbacks(stores[0], stores[1:]...), nil
}
85 changes: 0 additions & 85 deletions internal/credential/store_test.go

This file was deleted.