Refactor: extract oidc.Claims model (#202)

* Refactor: extract oidc.Claims model

* Refactor: extract Claims.IsExpired()
This commit is contained in:
Hidetake Iwata
2019-12-26 20:17:30 +09:00
committed by GitHub
parent f7f1985a89
commit 76f61300d6
22 changed files with 248 additions and 192 deletions
+7
View File
@@ -7,6 +7,7 @@ import (
"os"
"strings"
"syscall"
"time"
"github.com/google/wire"
"github.com/pkg/browser"
@@ -33,6 +34,7 @@ type Interface interface {
ReadString(prompt string) (string, error)
ReadPassword(prompt string) (string, error)
OpenBrowser(url string) error
Now() time.Time
}
// Env provides environment specific facilities.
@@ -74,3 +76,8 @@ func (env *Env) OpenBrowser(url string) error {
}
return nil
}
// Now returns the current time.
func (*Env) Now() time.Time {
return time.Now()
}
+15
View File
@@ -7,6 +7,7 @@ package mock_env
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
time "time"
)
// MockInterface is a mock of Interface interface
@@ -32,6 +33,20 @@ func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// Now mocks base method
func (m *MockInterface) Now() time.Time {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Now")
ret0, _ := ret[0].(time.Time)
return ret0
}
// Now indicates an expected call of Now
func (mr *MockInterfaceMockRecorder) Now() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Now", reflect.TypeOf((*MockInterface)(nil).Now))
}
// OpenBrowser mocks base method
func (m *MockInterface) OpenBrowser(arg0 string) error {
m.ctrl.T.Helper()
+4 -10
View File
@@ -10,6 +10,7 @@ import (
"github.com/dgrijalva/jwt-go"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/domain/oidc"
"golang.org/x/xerrors"
)
@@ -22,21 +23,14 @@ var Set = wire.NewSet(
)
type Interface interface {
Decode(s string) (*Claims, error)
}
// Claims represents claims of a token.
type Claims struct {
Subject string
Expiry time.Time
Pretty map[string]string // string representation for debug and logging
Decode(s string) (*oidc.Claims, error)
}
type Decoder struct{}
// Decode returns the claims of the JWT.
// Note that this method does not verify the signature and always trust it.
func (d *Decoder) Decode(s string) (*Claims, error) {
func (d *Decoder) Decode(s string) (*oidc.Claims, error) {
parts := strings.Split(s, ".")
if len(parts) != 3 {
return nil, xerrors.Errorf("token contains an invalid number of segments")
@@ -53,7 +47,7 @@ func (d *Decoder) Decode(s string) (*Claims, error) {
if err := json.NewDecoder(bytes.NewBuffer(b)).Decode(&rawClaims); err != nil {
return nil, xerrors.Errorf("could not decode the json of token: %w", err)
}
return &Claims{
return &oidc.Claims{
Subject: claims.Subject,
Expiry: time.Unix(claims.ExpiresAt, 0),
Pretty: dumpRawClaims(rawClaims),
@@ -6,7 +6,7 @@ package mock_jwtdecoder
import (
gomock "github.com/golang/mock/gomock"
jwtdecoder "github.com/int128/kubelogin/pkg/adaptors/jwtdecoder"
oidc "github.com/int128/kubelogin/pkg/domain/oidc"
reflect "reflect"
)
@@ -34,10 +34,10 @@ func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
}
// Decode mocks base method
func (m *MockInterface) Decode(arg0 string) (*jwtdecoder.Claims, error) {
func (m *MockInterface) Decode(arg0 string) (*oidc.Claims, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Decode", arg0)
ret0, _ := ret[0].(*jwtdecoder.Claims)
ret0, _ := ret[0].(*oidc.Claims)
ret1, _ := ret[1].(error)
return ret0, ret1
}
+12 -9
View File
@@ -9,6 +9,7 @@ import (
"github.com/coreos/go-oidc"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/logger"
oidcModel "github.com/int128/kubelogin/pkg/domain/oidc"
"github.com/int128/oauth2cli"
"golang.org/x/oauth2"
"golang.org/x/xerrors"
@@ -56,11 +57,9 @@ type GetTokenByAuthCodeInput struct {
// TokenSet represents an output DTO of
// Interface.GetTokenByAuthCode, Interface.GetTokenByROPC and Interface.Refresh.
type TokenSet struct {
IDToken string
RefreshToken string
IDTokenSubject string
IDTokenExpiry time.Time
IDTokenClaims map[string]string // string representation of claims for logging
IDToken string
RefreshToken string
IDTokenClaims oidcModel.Claims
}
type client struct {
@@ -171,16 +170,20 @@ func (c *client) verifyToken(ctx context.Context, token *oauth2.Token, nonce str
}
return &TokenSet{
IDToken: idTokenString,
RefreshToken: token.RefreshToken,
IDTokenExpiry: idToken.Expiry,
IDTokenClaims: claims,
RefreshToken: token.RefreshToken,
}, nil
}
func dumpClaims(token *oidc.IDToken) (map[string]string, error) {
func dumpClaims(token *oidc.IDToken) (oidcModel.Claims, error) {
var rawClaims map[string]interface{}
err := token.Claims(&rawClaims)
return dumpRawClaims(rawClaims), err
pretty := dumpRawClaims(rawClaims)
return oidcModel.Claims{
Subject: token.Subject,
Expiry: token.Expiry,
Pretty: pretty,
}, err
}
func dumpRawClaims(rawClaims map[string]interface{}) map[string]string {
+2
View File
@@ -48,6 +48,7 @@ func NewCmd() cmd.Interface {
OIDCClientFactory: factory,
JWTDecoder: decoder,
Logger: loggerInterface,
Env: envEnv,
AuthCode: authCode,
AuthCodeKeyboard: authCodeKeyboard,
ROPC: ropc,
@@ -123,6 +124,7 @@ func NewCmdForHeadless(loggerInterface logger.Interface, localServerReadyFunc au
OIDCClientFactory: factory,
JWTDecoder: decoder,
Logger: loggerInterface,
Env: envEnv,
AuthCode: authCode,
AuthCodeKeyboard: authCodeKeyboard,
ROPC: ropc,
+20
View File
@@ -0,0 +1,20 @@
package oidc
import "time"
// Claims represents claims of an ID token.
type Claims struct {
Subject string
Expiry time.Time
Pretty map[string]string // string representation for debug and logging
}
// TimeProvider provides the current time.
type TimeProvider interface {
Now() time.Time
}
// IsExpired returns true if the token is expired.
func (c *Claims) IsExpired(timeProvider TimeProvider) bool {
return c.Expiry.Before(timeProvider.Now())
}
+36
View File
@@ -0,0 +1,36 @@
package oidc_test
import (
"testing"
"time"
"github.com/int128/kubelogin/pkg/domain/oidc"
)
type timeProvider time.Time
func (tp timeProvider) Now() time.Time {
return time.Time(tp)
}
func TestClaims_IsExpired(t *testing.T) {
claims := oidc.Claims{
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
}
t.Run("Expired", func(t *testing.T) {
tp := timeProvider(time.Date(2019, 1, 2, 4, 0, 0, 0, time.UTC))
got := claims.IsExpired(tp)
if got != true {
t.Errorf("IsExpired() wants true but false")
}
})
t.Run("NotExpired", func(t *testing.T) {
tp := timeProvider(time.Date(2019, 1, 2, 0, 0, 0, 0, time.UTC))
got := claims.IsExpired(tp)
if got != false {
t.Errorf("IsExpired() wants false but true")
}
})
}
+3 -5
View File
@@ -66,11 +66,9 @@ func (u *AuthCode) Do(ctx context.Context, o *AuthCodeOption, client oidcclient.
return xerrors.Errorf("error while the authorization code flow: %w", err)
}
out = Output{
IDToken: tokenSet.IDToken,
RefreshToken: tokenSet.RefreshToken,
IDTokenSubject: tokenSet.IDTokenSubject,
IDTokenExpiry: tokenSet.IDTokenExpiry,
IDTokenClaims: tokenSet.IDTokenClaims,
IDToken: tokenSet.IDToken,
IDTokenClaims: tokenSet.IDTokenClaims,
RefreshToken: tokenSet.RefreshToken,
}
return nil
})
@@ -56,10 +56,8 @@ func (u *AuthCodeKeyboard) Do(ctx context.Context, o *AuthCodeKeyboardOption, cl
return nil, xerrors.Errorf("could not get the token: %w", err)
}
return &Output{
IDToken: tokenSet.IDToken,
RefreshToken: tokenSet.RefreshToken,
IDTokenSubject: tokenSet.IDTokenSubject,
IDTokenExpiry: tokenSet.IDTokenExpiry,
IDTokenClaims: tokenSet.IDTokenClaims,
IDToken: tokenSet.IDToken,
IDTokenClaims: tokenSet.IDTokenClaims,
RefreshToken: tokenSet.RefreshToken,
}, nil
}
@@ -11,13 +11,17 @@ import (
"github.com/int128/kubelogin/pkg/adaptors/logger/mock_logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/domain/oidc"
)
var nonNil = gomock.Not(gomock.Nil())
func TestAuthCodeKeyboard_Do(t *testing.T) {
dummyTokenClaims := map[string]string{"sub": "YOUR_SUBJECT"}
futureTime := time.Now().Add(time.Hour) //TODO: inject time service
dummyTokenClaims := oidc.Claims{
Subject: "YOUR_SUBJECT",
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
Pretty: map[string]string{"sub": "YOUR_SUBJECT"},
}
timeout := 5 * time.Second
t.Run("Success", func(t *testing.T) {
@@ -37,11 +41,9 @@ func TestAuthCodeKeyboard_Do(t *testing.T) {
}
}).
Return(&oidcclient.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
IDTokenClaims: dummyTokenClaims,
RefreshToken: "YOUR_REFRESH_TOKEN",
}, nil)
mockEnv := mock_env.NewMockInterface(ctrl)
mockEnv.EXPECT().
@@ -56,11 +58,9 @@ func TestAuthCodeKeyboard_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
IDTokenClaims: dummyTokenClaims,
RefreshToken: "YOUR_REFRESH_TOKEN",
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
+18 -22
View File
@@ -11,11 +11,15 @@ import (
"github.com/int128/kubelogin/pkg/adaptors/logger/mock_logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/domain/oidc"
)
func TestAuthCode_Do(t *testing.T) {
dummyTokenClaims := map[string]string{"sub": "YOUR_SUBJECT"}
futureTime := time.Now().Add(time.Hour) //TODO: inject time service
dummyTokenClaims := oidc.Claims{
Subject: "YOUR_SUBJECT",
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
Pretty: map[string]string{"sub": "YOUR_SUBJECT"},
}
timeout := 5 * time.Second
t.Run("Success", func(t *testing.T) {
@@ -34,11 +38,9 @@ func TestAuthCode_Do(t *testing.T) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidcclient.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
u := AuthCode{
Logger: mock_logger.New(t),
@@ -48,11 +50,9 @@ func TestAuthCode_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
@@ -74,11 +74,9 @@ func TestAuthCode_Do(t *testing.T) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidcclient.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
mockEnv := mock_env.NewMockInterface(ctrl)
mockEnv.EXPECT().
@@ -92,11 +90,9 @@ func TestAuthCode_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
+9 -13
View File
@@ -2,13 +2,14 @@ package authentication
import (
"context"
"time"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/certpool"
"github.com/int128/kubelogin/pkg/adaptors/env"
"github.com/int128/kubelogin/pkg/adaptors/jwtdecoder"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/domain/oidc"
"golang.org/x/xerrors"
)
@@ -67,10 +68,8 @@ type ROPCOption struct {
// Output represents an output DTO of the Authentication use-case.
type Output struct {
AlreadyHasValidIDToken bool
IDTokenSubject string
IDTokenExpiry time.Time
IDTokenClaims map[string]string
IDToken string
IDTokenClaims oidc.Claims
RefreshToken string
}
@@ -94,6 +93,7 @@ type Authentication struct {
OIDCClientFactory oidcclient.FactoryInterface
JWTDecoder jwtdecoder.Interface
Logger logger.Interface
Env env.Interface
AuthCode *AuthCode
AuthCodeKeyboard *AuthCodeKeyboard
ROPC *ROPC
@@ -109,15 +109,13 @@ func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
if err != nil {
return nil, xerrors.Errorf("invalid token and you need to remove the cache: %w", err)
}
if claims.Expiry.After(time.Now()) { //TODO: inject time service
if !claims.IsExpired(u.Env) {
u.Logger.V(1).Infof("you already have a valid token until %s", claims.Expiry)
return &Output{
AlreadyHasValidIDToken: true,
IDToken: in.IDToken,
RefreshToken: in.RefreshToken,
IDTokenSubject: claims.Subject,
IDTokenExpiry: claims.Expiry,
IDTokenClaims: claims.Pretty,
IDTokenClaims: *claims,
}, nil
}
u.Logger.V(1).Infof("you have an expired token at %s", claims.Expiry)
@@ -141,11 +139,9 @@ func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
out, err := client.Refresh(ctx, in.RefreshToken)
if err == nil {
return &Output{
IDToken: out.IDToken,
RefreshToken: out.RefreshToken,
IDTokenSubject: out.IDTokenSubject,
IDTokenExpiry: out.IDTokenExpiry,
IDTokenClaims: out.IDTokenClaims,
IDToken: out.IDToken,
IDTokenClaims: out.IDTokenClaims,
RefreshToken: out.RefreshToken,
}, nil
}
u.Logger.V(1).Infof("could not refresh the token: %s", err)
@@ -7,18 +7,23 @@ import (
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/adaptors/jwtdecoder"
"github.com/int128/kubelogin/pkg/adaptors/env/mock_env"
"github.com/int128/kubelogin/pkg/adaptors/jwtdecoder/mock_jwtdecoder"
"github.com/int128/kubelogin/pkg/adaptors/logger/mock_logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/domain/oidc"
"golang.org/x/xerrors"
)
func TestAuthentication_Do(t *testing.T) {
dummyTokenClaims := map[string]string{"sub": "YOUR_SUBJECT"}
pastTime := time.Now().Add(-time.Hour) //TODO: inject time service
futureTime := time.Now().Add(time.Hour) //TODO: inject time service
dummyTokenClaims := oidc.Claims{
Subject: "YOUR_SUBJECT",
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
Pretty: map[string]string{"sub": "YOUR_SUBJECT"},
}
timeBeforeExpiry := time.Date(2019, 1, 2, 1, 0, 0, 0, time.UTC)
timeAfterExpiry := time.Date(2019, 1, 2, 4, 0, 0, 0, time.UTC)
timeout := 5 * time.Second
t.Run("HasValidIDToken", func(t *testing.T) {
@@ -32,18 +37,19 @@ func TestAuthentication_Do(t *testing.T) {
ClientSecret: "YOUR_CLIENT_SECRET",
IDToken: "VALID_ID_TOKEN",
}
mockEnv := mock_env.NewMockInterface(ctrl)
mockEnv.EXPECT().
Now().
Return(timeBeforeExpiry)
mockDecoder := mock_jwtdecoder.NewMockInterface(ctrl)
mockDecoder.EXPECT().
Decode("VALID_ID_TOKEN").
Return(&jwtdecoder.Claims{
Subject: "YOUR_SUBJECT",
Expiry: futureTime,
Pretty: dummyTokenClaims,
}, nil)
Return(&dummyTokenClaims, nil)
u := Authentication{
OIDCClientFactory: mock_oidcclient.NewMockFactoryInterface(ctrl),
JWTDecoder: mockDecoder,
Logger: mock_logger.New(t),
Env: mockEnv,
}
got, err := u.Do(ctx, in)
if err != nil {
@@ -52,8 +58,6 @@ func TestAuthentication_Do(t *testing.T) {
want := &Output{
AlreadyHasValidIDToken: true,
IDToken: "VALID_ID_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
@@ -73,23 +77,21 @@ func TestAuthentication_Do(t *testing.T) {
IDToken: "EXPIRED_ID_TOKEN",
RefreshToken: "VALID_REFRESH_TOKEN",
}
mockEnv := mock_env.NewMockInterface(ctrl)
mockEnv.EXPECT().
Now().
Return(timeAfterExpiry)
mockDecoder := mock_jwtdecoder.NewMockInterface(ctrl)
mockDecoder.EXPECT().
Decode("EXPIRED_ID_TOKEN").
Return(&jwtdecoder.Claims{
Subject: "YOUR_SUBJECT",
Expiry: pastTime,
Pretty: dummyTokenClaims,
}, nil)
Return(&dummyTokenClaims, nil)
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
Refresh(ctx, "VALID_REFRESH_TOKEN").
Return(&oidcclient.TokenSet{
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
mockOIDCClientFactory := mock_oidcclient.NewMockFactoryInterface(ctrl)
mockOIDCClientFactory.EXPECT().
@@ -103,17 +105,16 @@ func TestAuthentication_Do(t *testing.T) {
OIDCClientFactory: mockOIDCClientFactory,
JWTDecoder: mockDecoder,
Logger: mock_logger.New(t),
Env: mockEnv,
}
got, err := u.Do(ctx, in)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
@@ -138,14 +139,14 @@ func TestAuthentication_Do(t *testing.T) {
IDToken: "EXPIRED_ID_TOKEN",
RefreshToken: "EXPIRED_REFRESH_TOKEN",
}
mockEnv := mock_env.NewMockInterface(ctrl)
mockEnv.EXPECT().
Now().
Return(timeAfterExpiry)
mockDecoder := mock_jwtdecoder.NewMockInterface(ctrl)
mockDecoder.EXPECT().
Decode("EXPIRED_ID_TOKEN").
Return(&jwtdecoder.Claims{
Subject: "YOUR_SUBJECT",
Expiry: pastTime,
Pretty: dummyTokenClaims,
}, nil)
Return(&dummyTokenClaims, nil)
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
Refresh(ctx, "EXPIRED_REFRESH_TOKEN").
@@ -156,11 +157,9 @@ func TestAuthentication_Do(t *testing.T) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidcclient.TokenSet{
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
mockOIDCClientFactory := mock_oidcclient.NewMockFactoryInterface(ctrl)
mockOIDCClientFactory.EXPECT().
@@ -174,6 +173,7 @@ func TestAuthentication_Do(t *testing.T) {
OIDCClientFactory: mockOIDCClientFactory,
JWTDecoder: mockDecoder,
Logger: mock_logger.New(t),
Env: mockEnv,
AuthCode: &AuthCode{
Logger: mock_logger.New(t),
},
@@ -183,11 +183,9 @@ func TestAuthentication_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
@@ -214,11 +212,9 @@ func TestAuthentication_Do(t *testing.T) {
mockOIDCClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidcclient.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
mockOIDCClientFactory := mock_oidcclient.NewMockFactoryInterface(ctrl)
mockOIDCClientFactory.EXPECT().
@@ -240,11 +236,9 @@ func TestAuthentication_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
+3 -5
View File
@@ -36,10 +36,8 @@ func (u *ROPC) Do(ctx context.Context, in *ROPCOption, client oidcclient.Interfa
return nil, xerrors.Errorf("error while the resource owner password credentials flow: %w", err)
}
return &Output{
IDToken: tokenSet.IDToken,
RefreshToken: tokenSet.RefreshToken,
IDTokenSubject: tokenSet.IDTokenSubject,
IDTokenExpiry: tokenSet.IDTokenExpiry,
IDTokenClaims: tokenSet.IDTokenClaims,
IDToken: tokenSet.IDToken,
IDTokenClaims: tokenSet.IDTokenClaims,
RefreshToken: tokenSet.RefreshToken,
}, nil
}
+24 -32
View File
@@ -11,12 +11,16 @@ import (
"github.com/int128/kubelogin/pkg/adaptors/logger/mock_logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/domain/oidc"
"golang.org/x/xerrors"
)
func TestROPC_Do(t *testing.T) {
dummyTokenClaims := map[string]string{"sub": "YOUR_SUBJECT"}
futureTime := time.Now().Add(time.Hour) //TODO: inject time service
dummyTokenClaims := oidc.Claims{
Subject: "YOUR_SUBJECT",
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
Pretty: map[string]string{"sub": "YOUR_SUBJECT"},
}
timeout := 5 * time.Second
t.Run("AskUsernameAndPassword", func(t *testing.T) {
@@ -29,11 +33,9 @@ func TestROPC_Do(t *testing.T) {
mockOIDCClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidcclient.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
IDTokenClaims: dummyTokenClaims,
RefreshToken: "YOUR_REFRESH_TOKEN",
}, nil)
mockEnv := mock_env.NewMockInterface(ctrl)
mockEnv.EXPECT().ReadString(usernamePrompt).Return("USER", nil)
@@ -47,11 +49,9 @@ func TestROPC_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
@@ -71,11 +71,9 @@ func TestROPC_Do(t *testing.T) {
mockOIDCClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidcclient.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
u := ROPC{
Logger: mock_logger.New(t),
@@ -85,11 +83,9 @@ func TestROPC_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
@@ -108,11 +104,9 @@ func TestROPC_Do(t *testing.T) {
mockOIDCClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidcclient.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
mockEnv := mock_env.NewMockInterface(ctrl)
mockEnv.EXPECT().ReadPassword(passwordPrompt).Return("PASS", nil)
@@ -125,11 +119,9 @@ func TestROPC_Do(t *testing.T) {
t.Errorf("Do returned error: %+v", err)
}
want := &Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
+4 -4
View File
@@ -53,7 +53,7 @@ func (u *GetToken) Do(ctx context.Context, in Input) error {
return xerrors.Errorf("could not get a token from the cache or provider: %w", err)
}
u.Logger.V(1).Infof("writing the token to client-go")
if err := u.Interaction.Write(credentialplugin.Output{Token: out.IDToken, Expiry: out.IDTokenExpiry}); err != nil {
if err := u.Interaction.Write(credentialplugin.Output{Token: out.IDToken, Expiry: out.IDTokenClaims.Expiry}); err != nil {
return xerrors.Errorf("could not write the token to client-go: %w", err)
}
return nil
@@ -94,15 +94,15 @@ func (u *GetToken) getTokenFromCacheOrProvider(ctx context.Context, in Input) (*
if err != nil {
return nil, xerrors.Errorf("error while authentication: %w", err)
}
for k, v := range out.IDTokenClaims {
for k, v := range out.IDTokenClaims.Pretty {
u.Logger.V(1).Infof("the ID token has the claim: %s=%v", k, v)
}
if out.AlreadyHasValidIDToken {
u.Logger.V(1).Infof("you already have a valid token until %s", out.IDTokenExpiry)
u.Logger.V(1).Infof("you already have a valid token until %s", out.IDTokenClaims.Expiry)
return out, nil
}
u.Logger.V(1).Infof("you got a valid token until %s", out.IDTokenExpiry)
u.Logger.V(1).Infof("you got a valid token until %s", out.IDTokenClaims.Expiry)
newTokenCacheValue := tokencache.Value{
IDToken: out.IDToken,
RefreshToken: out.RefreshToken,
@@ -12,14 +12,18 @@ import (
"github.com/int128/kubelogin/pkg/adaptors/logger/mock_logger"
"github.com/int128/kubelogin/pkg/adaptors/tokencache"
"github.com/int128/kubelogin/pkg/adaptors/tokencache/mock_tokencache"
"github.com/int128/kubelogin/pkg/domain/oidc"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/mock_authentication"
"golang.org/x/xerrors"
)
func TestGetToken_Do(t *testing.T) {
dummyTokenClaims := map[string]string{"sub": "YOUR_SUBJECT"}
futureTime := time.Now().Add(time.Hour) //TODO: inject time service
dummyTokenClaims := oidc.Claims{
Subject: "YOUR_SUBJECT",
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
Pretty: map[string]string{"sub": "YOUR_SUBJECT"},
}
t.Run("FullOptions", func(t *testing.T) {
var grantOptionSet authentication.GrantOptionSet
@@ -55,7 +59,6 @@ func TestGetToken_Do(t *testing.T) {
Return(&authentication.Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
}, nil)
tokenCacheRepository := mock_tokencache.NewMockInterface(ctrl)
@@ -86,7 +89,7 @@ func TestGetToken_Do(t *testing.T) {
credentialPluginInteraction.EXPECT().
Write(credentialplugin.Output{
Token: "YOUR_ID_TOKEN",
Expiry: futureTime,
Expiry: dummyTokenClaims.Expiry,
})
u := GetToken{
Authentication: mockAuthentication,
@@ -127,7 +130,6 @@ func TestGetToken_Do(t *testing.T) {
Return(&authentication.Output{
AlreadyHasValidIDToken: true,
IDToken: "VALID_ID_TOKEN",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
}, nil)
tokenCacheRepository := mock_tokencache.NewMockInterface(ctrl)
@@ -144,7 +146,7 @@ func TestGetToken_Do(t *testing.T) {
credentialPluginInteraction.EXPECT().
Write(credentialplugin.Output{
Token: "VALID_ID_TOKEN",
Expiry: futureTime,
Expiry: dummyTokenClaims.Expiry,
})
u := GetToken{
Authentication: mockAuthentication,
+2 -2
View File
@@ -96,7 +96,7 @@ func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error {
return xerrors.Errorf("error while authentication: %w", err)
}
u.Logger.Printf("You got the following claims in the token:")
for k, v := range out.IDTokenClaims {
for k, v := range out.IDTokenClaims.Pretty {
u.Logger.Printf("\t%s=%s", k, v)
}
@@ -104,7 +104,7 @@ func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error {
IssuerURL: in.IssuerURL,
ClientID: in.ClientID,
Args: makeCredentialPluginArgs(in),
Subject: out.IDTokenSubject,
Subject: out.IDTokenClaims.Subject,
}
var b strings.Builder
if err := stage2Tpl.Execute(&b, &v); err != nil {
+9 -5
View File
@@ -8,11 +8,17 @@ import (
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/pkg/adaptors/certpool/mock_certpool"
"github.com/int128/kubelogin/pkg/adaptors/logger/mock_logger"
"github.com/int128/kubelogin/pkg/domain/oidc"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/mock_authentication"
)
func TestSetup_DoStage2(t *testing.T) {
dummyTokenClaims := oidc.Claims{
Subject: "YOUR_SUBJECT",
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
Pretty: map[string]string{"sub": "YOUR_SUBJECT"},
}
var grantOptionSet authentication.GrantOptionSet
ctrl := gomock.NewController(t)
defer ctrl.Finish()
@@ -47,11 +53,9 @@ func TestSetup_DoStage2(t *testing.T) {
GrantOptionSet: grantOptionSet,
}).
Return(&authentication.Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenSubject: "YOUR_SUBJECT",
IDTokenExpiry: time.Now().Add(time.Hour),
IDTokenClaims: map[string]string{"iss": "https://accounts.google.com"},
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenClaims: dummyTokenClaims,
}, nil)
u := Setup{
Authentication: mockAuthentication,
+3 -3
View File
@@ -95,15 +95,15 @@ func (u *Standalone) Do(ctx context.Context, in Input) error {
if err != nil {
return xerrors.Errorf("error while authentication: %w", err)
}
for k, v := range out.IDTokenClaims {
for k, v := range out.IDTokenClaims.Pretty {
u.Logger.V(1).Infof("the ID token has the claim: %s=%v", k, v)
}
if out.AlreadyHasValidIDToken {
u.Logger.Printf("You already have a valid token until %s", out.IDTokenExpiry)
u.Logger.Printf("You already have a valid token until %s", out.IDTokenClaims.Expiry)
return nil
}
u.Logger.Printf("You got a valid token until %s", out.IDTokenExpiry)
u.Logger.Printf("You got a valid token until %s", out.IDTokenClaims.Expiry)
authProvider.IDToken = out.IDToken
authProvider.RefreshToken = out.RefreshToken
u.Logger.V(1).Infof("writing the ID token and refresh token to %s", authProvider.LocationOfOrigin)
+6 -5
View File
@@ -10,14 +10,18 @@ import (
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig"
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig/mock_kubeconfig"
"github.com/int128/kubelogin/pkg/adaptors/logger/mock_logger"
"github.com/int128/kubelogin/pkg/domain/oidc"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/mock_authentication"
"golang.org/x/xerrors"
)
func TestStandalone_Do(t *testing.T) {
dummyTokenClaims := map[string]string{"sub": "YOUR_SUBJECT"}
futureTime := time.Now().Add(time.Hour) //TODO: inject time service
dummyTokenClaims := oidc.Claims{
Subject: "YOUR_SUBJECT",
Expiry: time.Date(2019, 1, 2, 3, 4, 5, 0, time.UTC),
Pretty: map[string]string{"sub": "YOUR_SUBJECT"},
}
t.Run("FullOptions", func(t *testing.T) {
var grantOptionSet authentication.GrantOptionSet
@@ -81,7 +85,6 @@ func TestStandalone_Do(t *testing.T) {
Return(&authentication.Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
}, nil)
u := Standalone{
@@ -129,7 +132,6 @@ func TestStandalone_Do(t *testing.T) {
Return(&authentication.Output{
AlreadyHasValidIDToken: true,
IDToken: "VALID_ID_TOKEN",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
}, nil)
u := Standalone{
@@ -249,7 +251,6 @@ func TestStandalone_Do(t *testing.T) {
Return(&authentication.Output{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
IDTokenExpiry: futureTime,
IDTokenClaims: dummyTokenClaims,
}, nil)
u := Standalone{