Skip to content

Commit

Permalink
use oras-credentials-go
Browse files Browse the repository at this point in the history
Signed-off-by: Sylvia Lei <[email protected]>
  • Loading branch information
Wwwsylvia committed May 10, 2023
1 parent 5516199 commit 2850ed0
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 699 deletions.
14 changes: 14 additions & 0 deletions cmd/notation/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"

"github.com/spf13/pflag"
"oras.land/oras-go/v2/registry/remote/auth"
)

const (
Expand Down Expand Up @@ -55,3 +56,16 @@ func (opts *SecureFlagOpts) ApplyFlags(fs *pflag.FlagSet) {
opts.Username = os.Getenv(defaultUsernameEnv)
opts.Password = os.Getenv(defaultPasswordEnv)
}

// Credential returns an auth.Credential from opts.Username and opts.Password.
func (opts *SecureFlagOpts) Credential() auth.Credential {
if opts.Username == "" {
return auth.Credential{
RefreshToken: opts.Password,
}
}
return auth.Credential{
Username: opts.Username,
Password: opts.Password,
}
}
61 changes: 61 additions & 0 deletions cmd/notation/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"reflect"
"testing"

"oras.land/oras-go/v2/registry/remote/auth"
)

func TestSecureFlagOpts_Credential(t *testing.T) {
tests := []struct {
name string
opts *SecureFlagOpts
want auth.Credential
}{
{
name: "Username and password",
opts: &SecureFlagOpts{
Username: "username",
Password: "password",
},
want: auth.Credential{
Username: "username",
Password: "password",
},
},
{
name: "Username only",
opts: &SecureFlagOpts{
Username: "username",
},
want: auth.Credential{
Username: "username",
},
},
{
name: "Password only",
opts: &SecureFlagOpts{
Password: "token",
},
want: auth.Credential{
RefreshToken: "token",
},
},
{
name: "Empty username and password",
opts: &SecureFlagOpts{
Username: "",
Password: "",
},
want: auth.EmptyCredential,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.opts.Credential(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("SecureFlagOpts.Credential() = %v, want %v", got, tt.want)
}
})
}
}
59 changes: 25 additions & 34 deletions cmd/notation/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import (
"os"
"strings"

"github.com/notaryproject/notation/internal/auth"
"github.com/notaryproject/notation/internal/cmd"
"github.com/notaryproject/notation/pkg/auth"
credentials "github.com/oras-project/oras-credentials-go"
"github.com/spf13/cobra"
"golang.org/x/term"
orasauth "oras.land/oras-go/v2/registry/remote/auth"
)

const urlDocHowToAuthenticate = "https://notaryproject.dev/docs/how-to/registry-authentication/"

type loginOpts struct {
cmd.LoggingFlagOpts
SecureFlagOpts
Expand Down Expand Up @@ -76,55 +78,44 @@ func runLogin(ctx context.Context, opts *loginOpts) error {
return err
}
}

if opts.Password == "" {
opts.Password, err = readPasswordFromPrompt(reader)
if err != nil {
return err
}
}
cred := opts.Credential()

if err := validateAuthConfig(ctx, opts, serverAddress); err != nil {
return err
credsStore, err := auth.NewCredentialsStore()
if err != nil {
return fmt.Errorf("failed to get credentials store: %v", err)
}

nativeStore, err := auth.GetCredentialsStore(ctx, serverAddress)
registry, err := getRegistryClient(ctx, &opts.SecureFlagOpts, serverAddress)
if err != nil {
return fmt.Errorf("could not get the credentials store: %v", err)
return fmt.Errorf("failed to get registry client: %v", err)
}
if err := credentials.Login(ctx, credsStore, registry, cred); err != nil {
registryName := registry.Reference.Registry
if !errors.Is(err, credentials.ErrPlaintextPutDisabled) {
return fmt.Errorf("failed to log in to %s: %v", registryName, err)
}

// init creds
creds := newCredentialFromInput(
opts.Username,
opts.Password,
)
if err = nativeStore.Store(serverAddress, creds); err != nil {
return fmt.Errorf("failed to store credentials: %v", err)
// ErrPlaintextPutDisabled returned by Login() indicates that the credential is validated
// but is not saved because there is no native credentials store available
if savedCred, err := credsStore.Get(ctx, registryName); err == nil && savedCred == cred {
// there is an existing identical credential, ignore saving error
fmt.Fprintf(os.Stderr, "Warning: The credentials store is not set up. It is recommended to configure the credentials store to securely store your credentials. See %s.\n", urlDocHowToAuthenticate)
fmt.Printf("\nAuthenticating with existing credentials...\n")
} else {
return fmt.Errorf("failed to log in to %s: the credential could not be saved because a credentials store is required to securely store the password. See %s",
registryName, urlDocHowToAuthenticate)
}
}

fmt.Println("Login Succeeded")
return nil
}

func validateAuthConfig(ctx context.Context, opts *loginOpts, serverAddress string) error {
registry, err := getRegistryClient(ctx, &opts.SecureFlagOpts, serverAddress)
if err != nil {
return err
}
return registry.Ping(ctx)
}

func newCredentialFromInput(username, password string) orasauth.Credential {
c := orasauth.Credential{
Username: username,
Password: password,
}
if c.Username == "" {
c.RefreshToken = password
}
return c
}

func readPassword(opts *loginOpts) error {
if opts.passwordStdin {
password, err := readLine(os.Stdin)
Expand Down
15 changes: 6 additions & 9 deletions cmd/notation/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"errors"
"fmt"

"github.com/notaryproject/notation/internal/auth"
"github.com/notaryproject/notation/internal/cmd"
"github.com/notaryproject/notation/pkg/auth"
credentials "github.com/oras-project/oras-credentials-go"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -40,16 +41,12 @@ func logoutCommand(opts *logoutOpts) *cobra.Command {
func runLogout(ctx context.Context, opts *logoutOpts) error {
// set log level
ctx = opts.LoggingFlagOpts.SetLoggerLevel(ctx)

// initialize
serverAddress := opts.server
nativeStore, err := auth.GetCredentialsStore(ctx, serverAddress)
credsStore, err := auth.NewCredentialsStore()
if err != nil {
return err
return fmt.Errorf("failed to get credentials store: %v", err)
}
err = nativeStore.Erase(serverAddress)
if err != nil {
return err
if err := credentials.Logout(ctx, credsStore, opts.server); err != nil {
return fmt.Errorf("failed to logout of %s: %v", opts.server, err)
}

fmt.Println("Logout Succeeded")
Expand Down
59 changes: 18 additions & 41 deletions cmd/notation/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ package main
import (
"context"
"errors"
"fmt"
"net"
"net/http"

"github.com/notaryproject/notation-go/log"
notationregistry "github.com/notaryproject/notation-go/registry"
notationerrors "github.com/notaryproject/notation/cmd/notation/internal/errors"
notationauth "github.com/notaryproject/notation/internal/auth"
"github.com/notaryproject/notation/internal/trace"
"github.com/notaryproject/notation/internal/version"
loginauth "github.com/notaryproject/notation/pkg/auth"
"github.com/notaryproject/notation/pkg/configutil"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
credentials "github.com/oras-project/oras-credentials-go"
"github.com/sirupsen/logrus"
"oras.land/oras-go/v2/registry"
"oras.land/oras-go/v2/registry/remote"
Expand Down Expand Up @@ -159,9 +161,7 @@ func setHttpDebugLog(ctx context.Context, authClient *auth.Client) {
authClient.Client.Transport = trace.NewTransport(authClient.Client.Transport)
}

func getAuthClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference) (*auth.Client, bool, error) {
var plainHTTP bool

func getAuthClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Reference) (authClient *auth.Client, plainHTTP bool, err error) {
if opts.PlainHTTP {
plainHTTP = opts.PlainHTTP
} else {
Expand All @@ -172,51 +172,28 @@ func getAuthClient(ctx context.Context, opts *SecureFlagOpts, ref registry.Refer
}
}
}
cred := auth.Credential{
Username: opts.Username,
Password: opts.Password,
}
if cred.Username == "" {
cred = auth.Credential{
RefreshToken: cred.Password,
}
}
if cred == auth.EmptyCredential {
var err error
cred, err = getSavedCreds(ctx, ref.Registry)
// local registry may not need credentials
if err != nil && !errors.Is(err, loginauth.ErrCredentialsConfigNotSet) {
return nil, false, err
}
}

authClient := &auth.Client{
Credential: func(ctx context.Context, registry string) (auth.Credential, error) {
switch registry {
case ref.Host():
return cred, nil
default:
return auth.EmptyCredential, nil
}
},
// build authClient
authClient = &auth.Client{
Cache: auth.NewCache(),
ClientID: "notation",
}
authClient.SetUserAgent("notation/" + version.GetVersion())

// update authClient
setHttpDebugLog(ctx, authClient)

return authClient, plainHTTP, nil
}

func getSavedCreds(ctx context.Context, serverAddress string) (auth.Credential, error) {
nativeStore, err := loginauth.GetCredentialsStore(ctx, serverAddress)
if err != nil {
return auth.EmptyCredential, err
cred := opts.Credential()
if cred != auth.EmptyCredential {
// use the specified credential
authClient.Credential = auth.StaticCredential(ref.Host(), cred)
} else {
// use saved credentials
credsStore, err := notationauth.NewCredentialsStore()
if err != nil {
return nil, false, fmt.Errorf("failed to get credentials store: %w", err)
}
authClient.Credential = credentials.Credential(credsStore)
}

return nativeStore.Get(serverAddress)
return
}

func pingReferrersAPI(ctx context.Context, remoteRepo *remote.Repository) error {
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ module github.com/notaryproject/notation
go 1.20

require (
github.com/docker/docker-credential-helpers v0.7.0
github.com/notaryproject/notation-core-go v1.0.0-rc.3
github.com/notaryproject/notation-go v1.0.0-rc.4
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 @@ -17,6 +17,7 @@ require (

require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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/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 Down
29 changes: 29 additions & 0 deletions internal/auth/credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package auth

import (
"fmt"

"github.com/notaryproject/notation-go/dir"
credentials "github.com/oras-project/oras-credentials-go"
)

// NewCredentialsStore returns a new credentials store from the settings in the
// configuration file.
func NewCredentialsStore() (credentials.Store, error) {
configPath, err := dir.ConfigFS().SysPath(dir.PathConfigFile)
if err != nil {
return nil, fmt.Errorf("failed to load config file: %w", err)
}

opts := credentials.StoreOptions{AllowPlaintextPut: false}
primaryStore, err := credentials.NewStore(configPath, opts)
if err != nil {
return nil, fmt.Errorf("failed to create credential store from config file: %w", err)
}

fallbackStore, err := credentials.NewStoreFromDocker(opts)
if err != nil {
return nil, fmt.Errorf("failed to create credential store from docker config file: %w", err)
}
return credentials.NewStoreWithFallbacks(primaryStore, fallbackStore), nil
}
13 changes: 0 additions & 13 deletions pkg/auth/api.go

This file was deleted.

Loading

0 comments on commit 2850ed0

Please sign in to comment.