Refactor: replace ListenPort with BindAddress option (#171)

This commit is contained in:
Hidetake Iwata
2019-10-28 19:59:45 +09:00
committed by GitHub
parent 5dc06ae574
commit 93e893bc36
15 changed files with 62 additions and 45 deletions
+8
View File
@@ -2,6 +2,7 @@ package cmd
import (
"context"
"fmt"
"path/filepath"
"github.com/google/wire"
@@ -26,6 +27,13 @@ type Interface interface {
var defaultListenPort = []int{8000, 18000}
var defaultTokenCacheDir = homedir.HomeDir() + "/.kube/cache/oidc-login"
func translateListenPortToBindAddress(ports []int) (address []string) {
for _, p := range ports {
address = append(address, fmt.Sprintf("127.0.0.1:%d", p))
}
return
}
// Cmd provides interaction with command line interface (CLI).
type Cmd struct {
Root *Root
+4 -4
View File
@@ -24,7 +24,7 @@ func TestCmd_Run(t *testing.T) {
mockStandalone := mock_standalone.NewMockInterface(ctrl)
mockStandalone.EXPECT().
Do(ctx, standalone.Input{
ListenPort: defaultListenPort,
BindAddress: []string{"127.0.0.1:8000", "127.0.0.1:18000"},
})
cmd := Cmd{
@@ -53,7 +53,7 @@ func TestCmd_Run(t *testing.T) {
KubeconfigUser: "google",
CACertFilename: "/path/to/cacert",
SkipTLSVerify: true,
ListenPort: []int{10080, 20080},
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
SkipOpenBrowser: true,
Username: "USER",
Password: "PASS",
@@ -108,7 +108,7 @@ func TestCmd_Run(t *testing.T) {
getToken := mock_credentialplugin.NewMockInterface(ctrl)
getToken.EXPECT().
Do(ctx, credentialplugin.Input{
ListenPort: defaultListenPort,
BindAddress: []string{"127.0.0.1:8000", "127.0.0.1:18000"},
TokenCacheDir: defaultTokenCacheDir,
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
@@ -149,7 +149,7 @@ func TestCmd_Run(t *testing.T) {
ExtraScopes: []string{"email", "profile"},
CACertFilename: "/path/to/cacert",
SkipTLSVerify: true,
ListenPort: []int{10080, 20080},
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
SkipOpenBrowser: true,
Username: "USER",
Password: "PASS",
+1 -1
View File
@@ -70,7 +70,7 @@ func (cmd *GetToken) New(ctx context.Context) *cobra.Command {
ExtraScopes: o.ExtraScopes,
CACertFilename: o.CertificateAuthority,
SkipTLSVerify: o.SkipTLSVerify,
ListenPort: o.ListenPort,
BindAddress: translateListenPortToBindAddress(o.ListenPort),
SkipOpenBrowser: o.SkipOpenBrowser,
Username: o.Username,
Password: o.Password,
+1 -1
View File
@@ -66,7 +66,7 @@ func (cmd *Root) New(ctx context.Context, executable string) *cobra.Command {
KubeconfigUser: kubeconfig.UserName(o.User),
CACertFilename: o.CertificateAuthority,
SkipTLSVerify: o.SkipTLSVerify,
ListenPort: o.ListenPort,
BindAddress: translateListenPortToBindAddress(o.ListenPort),
SkipOpenBrowser: o.SkipOpenBrowser,
Username: o.Username,
Password: o.Password,
+5 -4
View File
@@ -2,7 +2,6 @@ package cmd
import (
"context"
"reflect"
"github.com/int128/kubelogin/pkg/usecases/setup"
"github.com/spf13/cobra"
@@ -44,18 +43,20 @@ func (cmd *Setup) New(ctx context.Context) *cobra.Command {
Use: "setup",
Short: "Show the setup instruction",
Args: cobra.NoArgs,
RunE: func(*cobra.Command, []string) error {
RunE: func(c *cobra.Command, _ []string) error {
in := setup.Stage2Input{
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
ExtraScopes: o.ExtraScopes,
SkipOpenBrowser: o.SkipOpenBrowser,
ListenPort: o.ListenPort,
ListenPortIsSet: !reflect.DeepEqual(o.ListenPort, defaultListenPort),
BindAddress: translateListenPortToBindAddress(o.ListenPort),
CACertFilename: o.CertificateAuthority,
SkipTLSVerify: o.SkipTLSVerify,
}
if c.Flags().Lookup("listen-port").Changed {
in.ListenPortArgs = o.ListenPort
}
if in.IssuerURL == "" || in.ClientID == "" {
cmd.Setup.DoStage1()
return nil
+6 -6
View File
@@ -16,7 +16,7 @@ import (
)
type Interface interface {
AuthenticateByCode(ctx context.Context, localServerPort []int, localServerReadyChan chan<- string) (*TokenSet, error)
AuthenticateByCode(ctx context.Context, bindAddress []string, localServerReadyChan chan<- string) (*TokenSet, error)
AuthenticateByPassword(ctx context.Context, username, password string) (*TokenSet, error)
Refresh(ctx context.Context, refreshToken string) (*TokenSet, error)
}
@@ -46,17 +46,17 @@ func (c *client) wrapContext(ctx context.Context) context.Context {
}
// AuthenticateByCode performs the authorization code flow.
func (c *client) AuthenticateByCode(ctx context.Context, localServerPort []int, localServerReadyChan chan<- string) (*TokenSet, error) {
func (c *client) AuthenticateByCode(ctx context.Context, bindAddress []string, localServerReadyChan chan<- string) (*TokenSet, error) {
ctx = c.wrapContext(ctx)
nonce, err := newNonce()
if err != nil {
return nil, xerrors.Errorf("could not generate a nonce parameter")
}
config := oauth2cli.Config{
OAuth2Config: c.oauth2Config,
LocalServerPort: localServerPort,
AuthCodeOptions: []oauth2.AuthCodeOption{oauth2.AccessTypeOffline, oidc.Nonce(nonce)},
LocalServerReadyChan: localServerReadyChan,
OAuth2Config: c.oauth2Config,
LocalServerBindAddress: bindAddress,
AuthCodeOptions: []oauth2.AuthCodeOption{oauth2.AccessTypeOffline, oidc.Nonce(nonce)},
LocalServerReadyChan: localServerReadyChan,
}
token, err := oauth2cli.GetToken(ctx, config)
if err != nil {
+11 -1
View File
@@ -36,6 +36,7 @@ func (m *MockFactoryInterface) EXPECT() *MockFactoryInterfaceMockRecorder {
// New mocks base method
func (m *MockFactoryInterface) New(arg0 context.Context, arg1 oidc.ClientConfig) (oidc.Interface, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "New", arg0, arg1)
ret0, _ := ret[0].(oidc.Interface)
ret1, _ := ret[1].(error)
@@ -44,6 +45,7 @@ func (m *MockFactoryInterface) New(arg0 context.Context, arg1 oidc.ClientConfig)
// New indicates an expected call of New
func (mr *MockFactoryInterfaceMockRecorder) New(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockFactoryInterface)(nil).New), arg0, arg1)
}
@@ -71,7 +73,8 @@ func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
}
// AuthenticateByCode mocks base method
func (m *MockInterface) AuthenticateByCode(arg0 context.Context, arg1 []int, arg2 chan<- string) (*oidc.TokenSet, error) {
func (m *MockInterface) AuthenticateByCode(arg0 context.Context, arg1 []string, arg2 chan<- string) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AuthenticateByCode", arg0, arg1, arg2)
ret0, _ := ret[0].(*oidc.TokenSet)
ret1, _ := ret[1].(error)
@@ -80,11 +83,13 @@ func (m *MockInterface) AuthenticateByCode(arg0 context.Context, arg1 []int, arg
// AuthenticateByCode indicates an expected call of AuthenticateByCode
func (mr *MockInterfaceMockRecorder) AuthenticateByCode(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticateByCode", reflect.TypeOf((*MockInterface)(nil).AuthenticateByCode), arg0, arg1, arg2)
}
// AuthenticateByPassword mocks base method
func (m *MockInterface) AuthenticateByPassword(arg0 context.Context, arg1, arg2 string) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AuthenticateByPassword", arg0, arg1, arg2)
ret0, _ := ret[0].(*oidc.TokenSet)
ret1, _ := ret[1].(error)
@@ -93,11 +98,13 @@ func (m *MockInterface) AuthenticateByPassword(arg0 context.Context, arg1, arg2
// AuthenticateByPassword indicates an expected call of AuthenticateByPassword
func (mr *MockInterfaceMockRecorder) AuthenticateByPassword(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticateByPassword", reflect.TypeOf((*MockInterface)(nil).AuthenticateByPassword), arg0, arg1, arg2)
}
// Refresh mocks base method
func (m *MockInterface) Refresh(arg0 context.Context, arg1 string) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Refresh", arg0, arg1)
ret0, _ := ret[0].(*oidc.TokenSet)
ret1, _ := ret[1].(error)
@@ -106,6 +113,7 @@ func (m *MockInterface) Refresh(arg0 context.Context, arg1 string) (*oidc.TokenS
// Refresh indicates an expected call of Refresh
func (mr *MockInterfaceMockRecorder) Refresh(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refresh", reflect.TypeOf((*MockInterface)(nil).Refresh), arg0, arg1)
}
@@ -134,6 +142,7 @@ func (m *MockDecoderInterface) EXPECT() *MockDecoderInterfaceMockRecorder {
// DecodeIDToken mocks base method
func (m *MockDecoderInterface) DecodeIDToken(arg0 string) (*oidc.DecodedIDToken, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DecodeIDToken", arg0)
ret0, _ := ret[0].(*oidc.DecodedIDToken)
ret1, _ := ret[1].(error)
@@ -142,5 +151,6 @@ func (m *MockDecoderInterface) DecodeIDToken(arg0 string) (*oidc.DecodedIDToken,
// DecodeIDToken indicates an expected call of DecodeIDToken
func (mr *MockDecoderInterfaceMockRecorder) DecodeIDToken(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeIDToken", reflect.TypeOf((*MockDecoderInterface)(nil).DecodeIDToken), arg0)
}
+2 -2
View File
@@ -35,7 +35,7 @@ type Interface interface {
type Input struct {
OIDCConfig kubeconfig.OIDCConfig
SkipOpenBrowser bool
ListenPort []int
BindAddress []string
Username string // If set, perform the resource owner password credentials grant
Password string // If empty, read a password using Env.ReadPassword()
CACertFilename string // If set, use the CA cert
@@ -158,7 +158,7 @@ func (u *Authentication) doAuthCodeFlow(ctx context.Context, in Input, client oi
}
})
eg.Go(func() error {
tokenSet, err := client.AuthenticateByCode(ctx, in.ListenPort, readyChan)
tokenSet, err := client.AuthenticateByCode(ctx, in.BindAddress, readyChan)
if err != nil {
return xerrors.Errorf("error while the authorization code flow: %w", err)
}
+9 -9
View File
@@ -27,7 +27,7 @@ func TestAuthentication_Do(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
in := Input{
ListenPort: []int{10000},
BindAddress: []string{"127.0.0.1:8000"},
SkipOpenBrowser: true,
CACertFilename: "/path/to/cert",
SkipTLSVerify: true,
@@ -38,8 +38,8 @@ func TestAuthentication_Do(t *testing.T) {
}
mockOIDCClient := mock_oidc.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
AuthenticateByCode(gomock.Any(), []int{10000}, gomock.Any()).
Do(func(_ context.Context, _ []int, readyChan chan<- string) {
AuthenticateByCode(gomock.Any(), []string{"127.0.0.1:8000"}, gomock.Any()).
Do(func(_ context.Context, _ []string, readyChan chan<- string) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidc.TokenSet{
@@ -83,7 +83,7 @@ func TestAuthentication_Do(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
in := Input{
ListenPort: []int{10000},
BindAddress: []string{"127.0.0.1:8000"},
OIDCConfig: kubeconfig.OIDCConfig{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
@@ -91,8 +91,8 @@ func TestAuthentication_Do(t *testing.T) {
}
mockOIDCClient := mock_oidc.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
AuthenticateByCode(gomock.Any(), []int{10000}, gomock.Any()).
Do(func(_ context.Context, _ []int, readyChan chan<- string) {
AuthenticateByCode(gomock.Any(), []string{"127.0.0.1:8000"}, gomock.Any()).
Do(func(_ context.Context, _ []string, readyChan chan<- string) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidc.TokenSet{
@@ -373,7 +373,7 @@ func TestAuthentication_Do(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
in := Input{
ListenPort: []int{10000},
BindAddress: []string{"127.0.0.1:8000"},
SkipOpenBrowser: true,
OIDCConfig: kubeconfig.OIDCConfig{
ClientID: "YOUR_CLIENT_ID",
@@ -395,8 +395,8 @@ func TestAuthentication_Do(t *testing.T) {
Refresh(ctx, "EXPIRED_REFRESH_TOKEN").
Return(nil, xerrors.New("token has expired"))
mockOIDCClient.EXPECT().
AuthenticateByCode(gomock.Any(), []int{10000}, gomock.Any()).
Do(func(_ context.Context, _ []int, readyChan chan<- string) {
AuthenticateByCode(gomock.Any(), []string{"127.0.0.1:8000"}, gomock.Any()).
Do(func(_ context.Context, _ []string, readyChan chan<- string) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidc.TokenSet{
+2 -2
View File
@@ -33,7 +33,7 @@ type Input struct {
ClientSecret string
ExtraScopes []string // optional
SkipOpenBrowser bool
ListenPort []int
BindAddress []string
Username string // If set, perform the resource owner password credentials grant
Password string // If empty, read a password using Env.ReadPassword()
CACertFilename string // If set, use the CA cert
@@ -80,7 +80,7 @@ func (u *GetToken) getTokenFromCacheOrProvider(ctx context.Context, in Input) (*
RefreshToken: cache.RefreshToken,
},
SkipOpenBrowser: in.SkipOpenBrowser,
ListenPort: in.ListenPort,
BindAddress: in.BindAddress,
Username: in.Username,
Password: in.Password,
CACertFilename: in.CACertFilename,
@@ -30,7 +30,7 @@ func TestGetToken_Do(t *testing.T) {
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
TokenCacheDir: "/path/to/token-cache",
ListenPort: []int{10000},
BindAddress: []string{"127.0.0.1:8000"},
SkipOpenBrowser: true,
Username: "USER",
Password: "PASS",
@@ -45,7 +45,7 @@ func TestGetToken_Do(t *testing.T) {
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
ListenPort: []int{10000},
BindAddress: []string{"127.0.0.1:8000"},
SkipOpenBrowser: true,
Username: "USER",
Password: "PASS",
+5 -7
View File
@@ -71,8 +71,8 @@ type Stage2Input struct {
ClientSecret string
ExtraScopes []string // optional
SkipOpenBrowser bool
ListenPort []int
ListenPortIsSet bool // true if it is set by the command arg
BindAddress []string
ListenPortArgs []int // non-nil if set by the command arg
CACertFilename string // If set, use the CA cert
SkipTLSVerify bool
}
@@ -87,7 +87,7 @@ func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error {
ExtraScopes: in.ExtraScopes,
},
SkipOpenBrowser: in.SkipOpenBrowser,
ListenPort: in.ListenPort,
BindAddress: in.BindAddress,
CACertFilename: in.CACertFilename,
SkipTLSVerify: in.SkipTLSVerify,
})
@@ -126,10 +126,8 @@ func makeCredentialPluginArgs(in Stage2Input) []string {
if in.SkipOpenBrowser {
args = append(args, "--skip-open-browser")
}
if in.ListenPortIsSet {
for _, port := range in.ListenPort {
args = append(args, fmt.Sprintf("--listen-port=%d", port))
}
for _, port := range in.ListenPortArgs {
args = append(args, fmt.Sprintf("--listen-port=%d", port))
}
if in.CACertFilename != "" {
args = append(args, "--certificate-authority="+in.CACertFilename)
+2 -2
View File
@@ -23,7 +23,7 @@ func TestSetup_DoStage2(t *testing.T) {
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"email"},
SkipOpenBrowser: true,
ListenPort: []int{8000},
BindAddress: []string{"127.0.0.1:8000"},
CACertFilename: "/path/to/cert",
SkipTLSVerify: true,
}
@@ -38,7 +38,7 @@ func TestSetup_DoStage2(t *testing.T) {
ExtraScopes: []string{"email"},
},
SkipOpenBrowser: true,
ListenPort: []int{8000},
BindAddress: []string{"127.0.0.1:8000"},
CACertFilename: "/path/to/cert",
SkipTLSVerify: true,
}).
+2 -2
View File
@@ -30,7 +30,7 @@ type Input struct {
KubeconfigContext kubeconfig.ContextName // Default to the current context but ignored if KubeconfigUser is set
KubeconfigUser kubeconfig.UserName // Default to the user of the context
SkipOpenBrowser bool
ListenPort []int
BindAddress []string
Username string // If set, perform the resource owner password credentials grant
Password string // If empty, read a password using Env.ReadPassword()
CACertFilename string // If set, use the CA cert
@@ -70,7 +70,7 @@ func (u *Standalone) Do(ctx context.Context, in Input) error {
out, err := u.Authentication.Do(ctx, auth.Input{
OIDCConfig: authProvider.OIDCConfig,
SkipOpenBrowser: in.SkipOpenBrowser,
ListenPort: in.ListenPort,
BindAddress: in.BindAddress,
Username: in.Username,
Password: in.Password,
CACertFilename: in.CACertFilename,
+2 -2
View File
@@ -26,7 +26,7 @@ func TestStandalone_Do(t *testing.T) {
KubeconfigFilename: "/path/to/kubeconfig",
KubeconfigContext: "theContext",
KubeconfigUser: "theUser",
ListenPort: []int{10000},
BindAddress: []string{"127.0.0.1:8000"},
SkipOpenBrowser: true,
Username: "USER",
Password: "PASS",
@@ -62,7 +62,7 @@ func TestStandalone_Do(t *testing.T) {
mockAuthentication.EXPECT().
Do(ctx, auth.Input{
OIDCConfig: currentAuthProvider.OIDCConfig,
ListenPort: []int{10000},
BindAddress: []string{"127.0.0.1:8000"},
SkipOpenBrowser: true,
Username: "USER",
Password: "PASS",