Thanks to visit codestin.com
Credit goes to github.com

Skip to content
26 changes: 25 additions & 1 deletion internal/config/auth_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,32 @@ func TestTokenFromKeyringForUserErrorsIfUsernameIsBlank(t *testing.T) {
require.ErrorContains(t, err, "username cannot be blank")
}

func TestHasActiveToken(t *testing.T) {
// Given the user has logged in for a host
authCfg := newTestAuthConfig(t)
_, err := authCfg.Login("github.com", "test-user", "test-token", "", false)
require.NoError(t, err)

// When we check if that host has an active token
hasActiveToken := authCfg.HasActiveToken("github.com")

// Then there is an active token
require.True(t, hasActiveToken, "expected there to be an active token")
}

func TestHasNoActiveToken(t *testing.T) {
// Given there are no users logged in for a host
authCfg := newTestAuthConfig(t)

// When we check if any host has an active token
hasActiveToken := authCfg.HasActiveToken("github.com")

// Then there is no active token
require.False(t, hasActiveToken, "expected there to be no active token")
}

func TestTokenStoredInConfig(t *testing.T) {
// When the user has logged in insecurely
// Given the user has logged in insecurely
authCfg := newTestAuthConfig(t)
_, err := authCfg.Login("github.com", "test-user", "test-token", "", false)
require.NoError(t, err)
Expand Down
6 changes: 6 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ func (c *AuthConfig) ActiveToken(hostname string) (string, string) {
return token, source
}

// HasActiveToken returns true when a token for the hostname is present.
func (c *AuthConfig) HasActiveToken(hostname string) bool {
token, _ := c.ActiveToken(hostname)
return token != ""
}

// HasEnvToken returns true when a token has been specified in an
// environment variable, else returns false.
func (c *AuthConfig) HasEnvToken() bool {
Expand Down
3 changes: 3 additions & 0 deletions internal/gh/gh.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ type Migration interface {
// with knowledge on how to access encrypted storage when neccesarry.
// Behavior is scoped to authentication specific tasks.
type AuthConfig interface {
// HasActiveToken returns true when a token for the hostname is present.
HasActiveToken(hostname string) bool

// ActiveToken will retrieve the active auth token for the given hostname, searching environment variables,
// general configuration, and finally encrypted storage.
ActiveToken(hostname string) (token string, source string)
Expand Down
10 changes: 10 additions & 0 deletions pkg/cmd/attestation/trustedroot/trustedroot.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ func NewTrustedRootCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Com
}

if ghinstance.IsTenancy(opts.Hostname) {
c, err := f.Config()
if err != nil {
return err
}

if !c.Authentication().HasActiveToken(opts.Hostname) {
return fmt.Errorf("not authenticated with %s", opts.Hostname)
}

hc, err := f.HttpClient()
if err != nil {
return err
Expand All @@ -94,6 +103,7 @@ func NewTrustedRootCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Com
},
}

cmdutil.DisableAuthCheck(&trustedRootCmd)
trustedRootCmd.Flags().StringVarP(&opts.TufUrl, "tuf-url", "", "", "URL to the TUF repository mirror")
trustedRootCmd.Flags().StringVarP(&opts.TufRootPath, "tuf-root", "", "", "Path to the TUF root.json file on disk")
trustedRootCmd.MarkFlagsRequiredTogether("tuf-url", "tuf-root")
Expand Down
97 changes: 97 additions & 0 deletions pkg/cmd/attestation/trustedroot/trustedroot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,31 @@ package trustedroot
import (
"bytes"
"fmt"
"net/http"
"strings"
"testing"

"github.com/sigstore/sigstore-go/pkg/tuf"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/gh"
ghmock "github.com/cli/cli/v2/internal/gh/mock"
"github.com/cli/cli/v2/pkg/cmd/attestation/api"
"github.com/cli/cli/v2/pkg/cmd/attestation/test"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
)

func TestNewTrustedRootCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
Config: func() (gh.Config, error) {
return &ghmock.ConfigMock{}, nil
},
}

testcases := []struct {
Expand Down Expand Up @@ -72,6 +81,83 @@ func TestNewTrustedRootCmd(t *testing.T) {
}
}

func TestNewTrustedRootWithTenancy(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))

httpClientFunc := func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
}

cli := "--hostname foo-bar.ghe.com"

t.Run("Host with NO auth configured", func(t *testing.T) {
f := &cmdutil.Factory{
IOStreams: testIO,
Config: func() (gh.Config, error) {
return &ghmock.ConfigMock{
AuthenticationFunc: func() gh.AuthConfig {
return &stubAuthConfig{hasActiveToken: false}
},
}, nil
},
}

cmd := NewTrustedRootCmd(f, func(_ *Options) error {
return nil
})

argv := strings.Split(cli, " ")
cmd.SetArgs(argv)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
cmd.SetErr(&bytes.Buffer{})
_, err := cmd.ExecuteC()

assert.Error(t, err)
assert.ErrorContains(t, err, "not authenticated")
})

t.Run("Host with auth configured", func(t *testing.T) {
f := &cmdutil.Factory{
IOStreams: testIO,
Config: func() (gh.Config, error) {
return &ghmock.ConfigMock{
AuthenticationFunc: func() gh.AuthConfig {
return &stubAuthConfig{hasActiveToken: true}
},
}, nil
},
HttpClient: httpClientFunc,
}

cmd := NewTrustedRootCmd(f, func(_ *Options) error {
return nil
})

argv := strings.Split(cli, " ")
cmd.SetArgs(argv)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
cmd.SetErr(&bytes.Buffer{})

_, err := cmd.ExecuteC()
assert.NoError(t, err)
})
}

var newTUFErrClient tufClientInstantiator = func(o *tuf.Options) (*tuf.Client, error) {
return nil, fmt.Errorf("failed to create TUF client")
}
Expand Down Expand Up @@ -99,3 +185,14 @@ func TestGetTrustedRoot(t *testing.T) {
})

}

type stubAuthConfig struct {
config.AuthConfig
hasActiveToken bool
}

var _ gh.AuthConfig = (*stubAuthConfig)(nil)

func (c *stubAuthConfig) HasActiveToken(host string) bool {
return c.hasActiveToken
}