Merge branch 'trunk' into bdehamer/att-trusted-root-tenant-aware

This commit is contained in:
Brian DeHamer 2024-09-20 09:22:40 -07:00 committed by GitHub
commit 5f60b3ca3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 157 additions and 4 deletions

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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
@ -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")

View file

@ -3,6 +3,7 @@ package trustedroot
import (
"bytes"
"fmt"
"net/http"
"strings"
"testing"
@ -10,8 +11,13 @@ import (
"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"
)
@ -19,6 +25,9 @@ func TestNewTrustedRootCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
Config: func() (gh.Config, error) {
return &ghmock.ConfigMock{}, nil
},
}
testcases := []struct {
@ -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")
}
@ -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
}

View file

@ -280,9 +280,13 @@ func (m *Manager) installBin(repo ghrepo.Interface, target string) error {
}
if asset == nil {
cs := m.io.ColorScheme()
errorMessageInRed := fmt.Sprintf(cs.Red("%[1]s unsupported for %[2]s."), repo.RepoName(), platform)
issueCreateCommand := generateMissingBinaryIssueCreateCommand(repo.RepoOwner(), repo.RepoName(), platform)
return fmt.Errorf(
"%[1]s unsupported for %[2]s. Open an issue: `gh issue create -R %[3]s/%[1]s -t'Support %[2]s'`",
repo.RepoName(), platform, repo.RepoOwner())
"%[1]s\n\nTo request support for %[2]s, open an issue on the extension's repo by running the following command:\n\n `%[3]s`",
errorMessageInRed, platform, issueCreateCommand)
}
name := repo.RepoName()
@ -334,6 +338,15 @@ func (m *Manager) installBin(repo ghrepo.Interface, target string) error {
return nil
}
func generateMissingBinaryIssueCreateCommand(repoOwner string, repoName string, currentPlatform string) string {
issueBody := generateMissingBinaryIssueBody(currentPlatform)
return fmt.Sprintf("gh issue create -R %[1]s/%[2]s --title \"Add support for the %[3]s architecture\" --body \"%[4]s\"", repoOwner, repoName, currentPlatform, issueBody)
}
func generateMissingBinaryIssueBody(currentPlatform string) string {
return fmt.Sprintf("This extension does not support the %[1]s architecture. I tried to install it on a %[1]s machine, and it failed due to the lack of an available binary. Would you be able to update the extension's build and release process to include the relevant binary? For more details, see <https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions>.", currentPlatform)
}
func writeManifest(dir, name string, data []byte) (writeErr error) {
path := filepath.Join(dir, name)
var f *os.File

View file

@ -906,7 +906,7 @@ func TestManager_Install_binary_unsupported(t *testing.T) {
m := newTestManager(tempDir, &client, nil, ios)
err := m.Install(repo, "")
assert.EqualError(t, err, "gh-bin-ext unsupported for windows-amd64. Open an issue: `gh issue create -R owner/gh-bin-ext -t'Support windows-amd64'`")
assert.EqualError(t, err, "gh-bin-ext unsupported for windows-amd64.\n\nTo request support for windows-amd64, open an issue on the extension's repo by running the following command:\n\n\t`gh issue create -R owner/gh-bin-ext --title \"Add support for the windows-amd64 architecture\" --body \"This extension does not support the windows-amd64 architecture. I tried to install it on a windows-amd64 machine, and it failed due to the lack of an available binary. Would you be able to update the extension's build and release process to include the relevant binary? For more details, see <https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions>.\"`")
assert.Equal(t, "", stdout.String())
assert.Equal(t, "", stderr.String())