mirror of
https://github.com/int128/kubelogin.git
synced 2026-03-01 00:10:20 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe2fbcbc53 | ||
|
|
812a965739 | ||
|
|
6de1fca64c | ||
|
|
0eb8cdc95f | ||
|
|
995c0997d5 | ||
|
|
18b2437819 | ||
|
|
5d5a33b8ea | ||
|
|
a614943642 | ||
|
|
d223175b92 | ||
|
|
6075c9dbe7 | ||
|
|
be43c2ab82 | ||
|
|
512df0c4e4 | ||
|
|
5d5292637f | ||
|
|
76f61300d6 | ||
|
|
f7f1985a89 | ||
|
|
3d47c88a8d |
@@ -2,32 +2,19 @@ version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/golang:1.13.3
|
||||
- image: circleci/golang:1.13.4
|
||||
steps:
|
||||
- run: |
|
||||
sudo apt install -y file
|
||||
- run: |
|
||||
mkdir -p ~/bin
|
||||
echo 'export PATH="$HOME/bin:$PATH"' >> $BASH_ENV
|
||||
- run: |
|
||||
curl -L -o ~/bin/ghcp https://github.com/int128/ghcp/releases/download/v1.5.0/ghcp_linux_amd64
|
||||
chmod +x ~/bin/ghcp
|
||||
- run: |
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ~/bin v1.21.0
|
||||
- run: go get github.com/int128/goxzst
|
||||
- run: go get github.com/tcnksm/ghr
|
||||
- run: mkdir -p ~/bin
|
||||
- run: echo 'export PATH="$HOME/bin:$PATH"' >> $BASH_ENV
|
||||
- checkout
|
||||
- run: make check
|
||||
- run: bash <(curl -s https://codecov.io/bash)
|
||||
- run: make dist
|
||||
- run: |
|
||||
unzip dist/gh/kubelogin_linux_amd64.zip -d /tmp
|
||||
file /tmp/kubelogin
|
||||
/tmp/kubelogin --help
|
||||
- run: make ci-setup-linux-amd64
|
||||
- run: make VERSION=$CIRCLE_TAG ci
|
||||
- run: |
|
||||
if [ "$CIRCLE_TAG" ]; then
|
||||
make release
|
||||
make VERSION=$CIRCLE_TAG GITHUB_USERNAME=$CIRCLE_PROJECT_USERNAME GITHUB_REPONAME=$CIRCLE_PROJECT_REPONAME release
|
||||
fi
|
||||
- store_artifacts:
|
||||
path: gotest.log
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,8 +2,9 @@
|
||||
|
||||
/.kubeconfig*
|
||||
|
||||
/dist
|
||||
/dist/output
|
||||
/coverage.out
|
||||
/gotest.log
|
||||
|
||||
/kubelogin
|
||||
/kubectl-oidc_login
|
||||
|
||||
73
Makefile
73
Makefile
@@ -1,37 +1,66 @@
|
||||
# CI must provide the following variables (on tag push)
|
||||
# VERSION
|
||||
# GITHUB_USERNAME
|
||||
# GITHUB_REPONAME
|
||||
|
||||
TARGET := kubelogin
|
||||
CIRCLE_TAG ?= HEAD
|
||||
LDFLAGS := -X main.version=$(CIRCLE_TAG)
|
||||
VERSION ?= latest
|
||||
LDFLAGS := -X main.version=$(VERSION)
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(wildcard **/*.go)
|
||||
go build -o $@ -ldflags "$(LDFLAGS)"
|
||||
|
||||
.PHONY: ci
|
||||
ci:
|
||||
$(MAKE) check
|
||||
bash -c "bash <(curl -s https://codecov.io/bash)"
|
||||
$(MAKE) dist
|
||||
|
||||
.PHONY: check
|
||||
check:
|
||||
golangci-lint run
|
||||
go test -v -race -cover -coverprofile=coverage.out ./...
|
||||
go test -v -race -cover -coverprofile=coverage.out ./... > gotest.log
|
||||
|
||||
$(TARGET): $(wildcard *.go)
|
||||
go build -o $@ -ldflags "$(LDFLAGS)"
|
||||
|
||||
dist:
|
||||
# make the zip files for GitHub Releases
|
||||
VERSION=$(CIRCLE_TAG) CGO_ENABLED=0 goxzst -d dist/gh/ -i "LICENSE" -o "$(TARGET)" -t "kubelogin.rb oidc-login.yaml" -- -ldflags "$(LDFLAGS)"
|
||||
zipinfo dist/gh/kubelogin_linux_amd64.zip
|
||||
# make the Homebrew formula
|
||||
mv dist/gh/kubelogin.rb dist/
|
||||
# make the yaml for krew-index
|
||||
mkdir -p dist/plugins
|
||||
cp dist/gh/oidc-login.yaml dist/plugins/oidc-login.yaml
|
||||
.PHONY: dist
|
||||
dist: dist/output
|
||||
dist/output:
|
||||
# make the zip files for GitHub Releases
|
||||
VERSION=$(VERSION) CGO_ENABLED=0 goxzst -d dist/output -i "LICENSE" -o "$(TARGET)" -t "dist/kubelogin.rb dist/oidc-login.yaml dist/Dockerfile" -- -ldflags "$(LDFLAGS)"
|
||||
# test the zip file
|
||||
zipinfo dist/output/kubelogin_linux_amd64.zip
|
||||
# make the krew yaml structure
|
||||
mkdir -p dist/output/plugins
|
||||
mv dist/output/oidc-login.yaml dist/output/plugins/oidc-login.yaml
|
||||
|
||||
.PHONY: release
|
||||
release: dist
|
||||
# publish to the GitHub Releases
|
||||
ghr -u "$(CIRCLE_PROJECT_USERNAME)" -r "$(CIRCLE_PROJECT_REPONAME)" "$(CIRCLE_TAG)" dist/gh/
|
||||
# publish to the Homebrew tap repository
|
||||
ghcp commit -u "$(CIRCLE_PROJECT_USERNAME)" -r "homebrew-$(CIRCLE_PROJECT_REPONAME)" -m "$(CIRCLE_TAG)" -C dist/ kubelogin.rb
|
||||
# fork krew-index and create a branch
|
||||
ghcp fork-commit -u kubernetes-sigs -r krew-index -b "oidc-login-$(CIRCLE_TAG)" -m "Bump oidc-login to $(CIRCLE_TAG)" -C dist/ plugins/oidc-login.yaml
|
||||
# publish the binaries
|
||||
ghcp release -u "$(GITHUB_USERNAME)" -r "$(GITHUB_REPONAME)" -t "$(VERSION)" dist/output/
|
||||
# publish the Homebrew formula
|
||||
ghcp commit -u "$(GITHUB_USERNAME)" -r "homebrew-$(GITHUB_REPONAME)" -b "bump-$(VERSION)" -m "Bump the version to $(VERSION)" -C dist/output/ kubelogin.rb
|
||||
ghcp pull-request -u "$(GITHUB_USERNAME)" -r "homebrew-$(GITHUB_REPONAME)" -b "bump-$(VERSION)" --title "Bump the version to $(VERSION)"
|
||||
# publish the Dockerfile
|
||||
ghcp commit -u "$(GITHUB_USERNAME)" -r "$(GITHUB_REPONAME)-docker" -b "bump-$(VERSION)" -m "Bump the version to $(VERSION)" -C dist/output/ Dockerfile
|
||||
ghcp pull-request -u "$(GITHUB_USERNAME)" -r "$(GITHUB_REPONAME)-docker" -b "bump-$(VERSION)" --title "Bump the version to $(VERSION)"
|
||||
# publish the Krew manifest
|
||||
ghcp fork-commit -u kubernetes-sigs -r krew-index -b "oidc-login-$(VERSION)" -m "Bump oidc-login to $(VERSION)" -C dist/output/ plugins/oidc-login.yaml
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm $(TARGET)
|
||||
-rm -r dist/
|
||||
-rm -r dist/output/
|
||||
-rm coverage.out gotest.log
|
||||
|
||||
.PHONY: ci-setup-linux-amd64
|
||||
ci-setup-linux-amd64:
|
||||
mkdir -p ~/bin
|
||||
# https://github.com/golangci/golangci-lint
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ~/bin v1.21.0
|
||||
# https://github.com/int128/goxzst
|
||||
curl -sfL -o /tmp/goxzst.zip https://github.com/int128/goxzst/releases/download/v0.3.0/goxzst_linux_amd64.zip
|
||||
unzip /tmp/goxzst.zip -d ~/bin
|
||||
# https://github.com/int128/ghcp
|
||||
curl -sfL -o /tmp/ghcp.zip https://github.com/int128/ghcp/releases/download/v1.8.0/ghcp_linux_amd64.zip
|
||||
unzip /tmp/ghcp.zip -d ~/bin
|
||||
|
||||
@@ -125,7 +125,8 @@ Flags:
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--token-cache-dir string Path to a directory for caching tokens (default "~/.kube/cache/oidc-login")
|
||||
--grant-type string The authorization grant type to use. One of (auto|authcode|authcode-keyboard|password) (default "auto")
|
||||
--listen-port ints Port to bind to the local server. If multiple ports are given, it will try the ports in order (default [8000,18000])
|
||||
--listen-address strings Address to bind to the local server. If multiple addresses are given, it will try binding in order (default [127.0.0.1:8000,127.0.0.1:18000])
|
||||
--listen-port ints (Deprecated: use --listen-address)
|
||||
--skip-open-browser If true, it does not open the browser on authentication
|
||||
--username string If set, perform the resource owner password credentials grant
|
||||
--password string If set, use the password instead of asking it
|
||||
@@ -183,11 +184,11 @@ You need to register the following redirect URIs to the provider:
|
||||
- `http://localhost:8000`
|
||||
- `http://localhost:18000` (used if port 8000 is already in use)
|
||||
|
||||
You can change the ports by the option:
|
||||
You can change the listening address.
|
||||
|
||||
```yaml
|
||||
- --listen-port=12345
|
||||
- --listen-port=23456
|
||||
- --listen-address=127.0.0.1:12345
|
||||
- --listen-address=127.0.0.1:23456
|
||||
```
|
||||
|
||||
#### Authorization code flow with keyboard interactive
|
||||
|
||||
13
dist/Dockerfile
vendored
Normal file
13
dist/Dockerfile
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM alpine:3.11
|
||||
|
||||
ARG KUBELOGIN_VERSION="{{ env "VERSION" }}"
|
||||
ARG KUBELOGIN_SHA256="{{ sha256 .linux_amd64_archive }}"
|
||||
|
||||
# Download the release and test the checksum
|
||||
RUN wget -O /kubelogin.zip "https://github.com/int128/kubelogin/releases/download/$KUBELOGIN_VERSION/kubelogin_linux_amd64.zip" && \
|
||||
unzip /kubelogin.zip && \
|
||||
rm /kubelogin.zip && \
|
||||
echo "$KUBELOGIN_SHA256 /kubelogin" | sha256sum -c -
|
||||
|
||||
USER daemon
|
||||
ENTRYPOINT ["/kubelogin"]
|
||||
0
kubelogin.rb → dist/kubelogin.rb
vendored
0
kubelogin.rb → dist/kubelogin.rb
vendored
0
oidc-login.yaml → dist/oidc-login.yaml
vendored
0
oidc-login.yaml → dist/oidc-login.yaml
vendored
@@ -105,7 +105,8 @@ Flags:
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--grant-type string The authorization grant type to use. One of (auto|authcode|authcode-keyboard|password) (default "auto")
|
||||
--listen-port ints Port to bind to the local server. If multiple ports are given, it will try the ports in order (default [8000,18000])
|
||||
--listen-address strings Address to bind to the local server. If multiple addresses are given, it will try binding in order (default [127.0.0.1:8000,127.0.0.1:18000])
|
||||
--listen-port ints (Deprecated: use --listen-address)
|
||||
--skip-open-browser If true, it does not open the browser on authentication
|
||||
--username string If set, perform the resource owner password credentials grant
|
||||
--password string If set, use the password instead of asking it
|
||||
|
||||
@@ -111,13 +111,15 @@ func testCredentialPlugin(t *testing.T, cacheDir string, idpTLS keys.Keys, extra
|
||||
serverURL, server := localserver.Start(t, idp.NewHandler(t, service), idpTLS)
|
||||
defer server.Shutdown(t, ctx)
|
||||
idToken := newIDToken(t, serverURL, "YOUR_NONCE", tokenExpiryFuture)
|
||||
setupTokenCache(t, cacheDir, tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
}, tokencache.TokenCache{
|
||||
IDToken: idToken,
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
})
|
||||
setupTokenCache(t, cacheDir,
|
||||
tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
CACertFilename: idpTLS.CACertPath,
|
||||
}, tokencache.Value{
|
||||
IDToken: idToken,
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
})
|
||||
credentialPluginInteraction := mock_credentialplugin.NewMockInterface(ctrl)
|
||||
assertCredentialPluginOutput(t, credentialPluginInteraction, &idToken)
|
||||
|
||||
@@ -128,13 +130,15 @@ func testCredentialPlugin(t *testing.T, cacheDir string, idpTLS keys.Keys, extra
|
||||
}
|
||||
args = append(args, extraArgs...)
|
||||
runGetTokenCmd(t, ctx, openBrowserOnReadyFunc(t, ctx, idpTLS), credentialPluginInteraction, args)
|
||||
assertTokenCache(t, cacheDir, tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
}, tokencache.TokenCache{
|
||||
IDToken: idToken,
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
})
|
||||
assertTokenCache(t, cacheDir,
|
||||
tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
CACertFilename: idpTLS.CACertPath,
|
||||
}, tokencache.Value{
|
||||
IDToken: idToken,
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("HasValidRefreshToken", func(t *testing.T) {
|
||||
@@ -154,13 +158,15 @@ func testCredentialPlugin(t *testing.T, cacheDir string, idpTLS keys.Keys, extra
|
||||
service.EXPECT().Refresh("VALID_REFRESH_TOKEN").
|
||||
Return(idp.NewTokenResponse(validIDToken, "NEW_REFRESH_TOKEN"), nil)
|
||||
|
||||
setupTokenCache(t, cacheDir, tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
}, tokencache.TokenCache{
|
||||
IDToken: expiredIDToken,
|
||||
RefreshToken: "VALID_REFRESH_TOKEN",
|
||||
})
|
||||
setupTokenCache(t, cacheDir,
|
||||
tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
CACertFilename: idpTLS.CACertPath,
|
||||
}, tokencache.Value{
|
||||
IDToken: expiredIDToken,
|
||||
RefreshToken: "VALID_REFRESH_TOKEN",
|
||||
})
|
||||
credentialPluginInteraction := mock_credentialplugin.NewMockInterface(ctrl)
|
||||
assertCredentialPluginOutput(t, credentialPluginInteraction, &validIDToken)
|
||||
|
||||
@@ -171,13 +177,15 @@ func testCredentialPlugin(t *testing.T, cacheDir string, idpTLS keys.Keys, extra
|
||||
}
|
||||
args = append(args, extraArgs...)
|
||||
runGetTokenCmd(t, ctx, openBrowserOnReadyFunc(t, ctx, idpTLS), credentialPluginInteraction, args)
|
||||
assertTokenCache(t, cacheDir, tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
}, tokencache.TokenCache{
|
||||
IDToken: validIDToken,
|
||||
RefreshToken: "NEW_REFRESH_TOKEN",
|
||||
})
|
||||
assertTokenCache(t, cacheDir,
|
||||
tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
CACertFilename: idpTLS.CACertPath,
|
||||
}, tokencache.Value{
|
||||
IDToken: validIDToken,
|
||||
RefreshToken: "NEW_REFRESH_TOKEN",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("HasExpiredRefreshToken", func(t *testing.T) {
|
||||
@@ -198,13 +206,15 @@ func testCredentialPlugin(t *testing.T, cacheDir string, idpTLS keys.Keys, extra
|
||||
Return(nil, &idp.ErrorResponse{Code: "invalid_request", Description: "token has expired"}).
|
||||
MaxTimes(2) // package oauth2 will retry refreshing the token
|
||||
|
||||
setupTokenCache(t, cacheDir, tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
}, tokencache.TokenCache{
|
||||
IDToken: expiredIDToken,
|
||||
RefreshToken: "EXPIRED_REFRESH_TOKEN",
|
||||
})
|
||||
setupTokenCache(t, cacheDir,
|
||||
tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
CACertFilename: idpTLS.CACertPath,
|
||||
}, tokencache.Value{
|
||||
IDToken: expiredIDToken,
|
||||
RefreshToken: "EXPIRED_REFRESH_TOKEN",
|
||||
})
|
||||
credentialPluginInteraction := mock_credentialplugin.NewMockInterface(ctrl)
|
||||
assertCredentialPluginOutput(t, credentialPluginInteraction, &validIDToken)
|
||||
|
||||
@@ -215,13 +225,15 @@ func testCredentialPlugin(t *testing.T, cacheDir string, idpTLS keys.Keys, extra
|
||||
}
|
||||
args = append(args, extraArgs...)
|
||||
runGetTokenCmd(t, ctx, openBrowserOnReadyFunc(t, ctx, idpTLS), credentialPluginInteraction, args)
|
||||
assertTokenCache(t, cacheDir, tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
}, tokencache.TokenCache{
|
||||
IDToken: validIDToken,
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
})
|
||||
assertTokenCache(t, cacheDir,
|
||||
tokencache.Key{
|
||||
IssuerURL: serverURL,
|
||||
ClientID: "kubernetes",
|
||||
CACertFilename: idpTLS.CACertPath,
|
||||
}, tokencache.Value{
|
||||
IDToken: validIDToken,
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("ExtraScopes", func(t *testing.T) {
|
||||
@@ -272,14 +284,14 @@ func runGetTokenCmd(t *testing.T, ctx context.Context, localServerReadyFunc auth
|
||||
"kubelogin", "get-token",
|
||||
"--v=1",
|
||||
"--skip-open-browser",
|
||||
"--listen-port", "0",
|
||||
"--listen-address", "127.0.0.1:0",
|
||||
}, args...), "HEAD")
|
||||
if exitCode != 0 {
|
||||
t.Errorf("exit status wants 0 but %d", exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
func setupTokenCache(t *testing.T, cacheDir string, k tokencache.Key, v tokencache.TokenCache) {
|
||||
func setupTokenCache(t *testing.T, cacheDir string, k tokencache.Key, v tokencache.Value) {
|
||||
var r tokencache.Repository
|
||||
err := r.Save(cacheDir, k, v)
|
||||
if err != nil {
|
||||
@@ -287,13 +299,13 @@ func setupTokenCache(t *testing.T, cacheDir string, k tokencache.Key, v tokencac
|
||||
}
|
||||
}
|
||||
|
||||
func assertTokenCache(t *testing.T, cacheDir string, k tokencache.Key, want tokencache.TokenCache) {
|
||||
func assertTokenCache(t *testing.T, cacheDir string, k tokencache.Key, want tokencache.Value) {
|
||||
var r tokencache.Repository
|
||||
v, err := r.FindByKey(cacheDir, k)
|
||||
got, err := r.FindByKey(cacheDir, k)
|
||||
if err != nil {
|
||||
t.Errorf("could not set up the token cache: %s", err)
|
||||
}
|
||||
if diff := cmp.Diff(&want, v); diff != "" {
|
||||
t.Errorf("token cache mismatch: %s", diff)
|
||||
if diff := cmp.Diff(&want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ func runRootCmd(t *testing.T, ctx context.Context, localServerReadyFunc authenti
|
||||
exitCode := cmd.Run(ctx, append([]string{
|
||||
"kubelogin",
|
||||
"--v=1",
|
||||
"--listen-port", "0",
|
||||
"--listen-address", "127.0.0.1:0",
|
||||
"--skip-open-browser",
|
||||
}, args...), "HEAD")
|
||||
if exitCode != 0 {
|
||||
|
||||
19
go.mod
19
go.mod
@@ -4,23 +4,22 @@ go 1.12
|
||||
|
||||
require (
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda
|
||||
github.com/go-test/deep v1.0.4
|
||||
github.com/golang/mock v1.3.1
|
||||
github.com/google/go-cmp v0.3.1
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/golang/mock v1.4.0
|
||||
github.com/google/go-cmp v0.4.0
|
||||
github.com/google/wire v0.4.0
|
||||
github.com/int128/oauth2cli v1.8.1
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.7
|
||||
k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719
|
||||
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab
|
||||
k8s.io/klog v0.4.0
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
k8s.io/apimachinery v0.17.2
|
||||
k8s.io/client-go v0.17.2
|
||||
k8s.io/klog v1.0.0
|
||||
)
|
||||
|
||||
211
go.sum
211
go.sum
@@ -1,50 +1,79 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda h1:NyywMz59neOoVRFDz+ccfKWxn784fiHMDnZSy6T+JXY=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 h1:WSBJMqJbLxsn+bTCPyPYZfqHdJmc8MK4wrBjMft6BAM=
|
||||
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.3.0 h1:imGQZGEVEHpje5056+K+cgdO72p0LQv2xIIFXNGUf60=
|
||||
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE=
|
||||
github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
|
||||
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||
@@ -53,96 +82,149 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/int128/listener v1.0.0 h1:a9H3m4jbXgXpxJUK3fxWrh37Iic/UU/kYOGE0WtjbbI=
|
||||
github.com/int128/listener v1.0.0/go.mod h1:sho0rrH7mNRRZH4hYOYx+xwRDGmtRndaUiu2z9iumes=
|
||||
github.com/int128/oauth2cli v1.7.0 h1:lguQEIJ4IcSFRTqQ6y7avnfvPqVe0U6dlkW8mC1Epts=
|
||||
github.com/int128/oauth2cli v1.7.0/go.mod h1:bucNn0/es9IhOf0a2MWPvJ5xO5f6JYrCfitQTyjI5lA=
|
||||
github.com/int128/oauth2cli v1.8.1 h1:Vkmfx0w225l4qUpJ1ZWGw1elw7hnXAybSiYoYyh1iBw=
|
||||
github.com/int128/oauth2cli v1.8.1/go.mod h1:MkxKWhHUaPOaq/92Z5ifdCWySAKJKo04hUXaKA7OgDE=
|
||||
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
|
||||
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 h1:a4tQYYYuK9QdeO/+kEvNYyuR21S+7ve5EANok6hABhI=
|
||||
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 h1:bfLnR+k0tq5Lqt6dflRLcZiz6UaXCMt3vhYJ1l4FQ80=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g=
|
||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
|
||||
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@@ -150,26 +232,29 @@ gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.6 h1:97YCGUei5WVbkKfogoJQsLwUJ17cWvpLrgNvlcbxikE=
|
||||
gopkg.in/yaml.v2 v2.2.6/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50=
|
||||
k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A=
|
||||
k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719 h1:uV4S5IB5g4Nvi+TBVNf3e9L4wrirlwYJ6w88jUQxTUw=
|
||||
k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA=
|
||||
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g=
|
||||
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k=
|
||||
k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68=
|
||||
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ=
|
||||
k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4=
|
||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc=
|
||||
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
|
||||
k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4=
|
||||
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/client-go v0.17.2 h1:ndIfkfXEGrNhLIgkr0+qhRguSD3u6DCmonepn1O6NYc=
|
||||
k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -11,24 +11,19 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination mock_certpool/mock_certpool.go github.com/int128/kubelogin/pkg/adaptors/certpool FactoryInterface,Interface
|
||||
//go:generate mockgen -destination mock_certpool/mock_certpool.go github.com/int128/kubelogin/pkg/adaptors/certpool Interface
|
||||
|
||||
// Set provides an implementation and interface.
|
||||
var Set = wire.NewSet(
|
||||
wire.Struct(new(Factory), "*"),
|
||||
wire.Bind(new(FactoryInterface), new(*Factory)),
|
||||
wire.Value(NewFunc(New)),
|
||||
wire.Struct(new(CertPool), "*"),
|
||||
wire.Bind(new(Interface), new(*CertPool)),
|
||||
)
|
||||
|
||||
type FactoryInterface interface {
|
||||
New() Interface
|
||||
}
|
||||
|
||||
type Factory struct{}
|
||||
type NewFunc func() Interface
|
||||
|
||||
// New returns an instance which implements the Interface.
|
||||
func (f *Factory) New() Interface {
|
||||
func New() Interface {
|
||||
return &CertPool{pool: x509.NewCertPool()}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ import (
|
||||
|
||||
func TestCertPool_AddFile(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
var f Factory
|
||||
p := f.New()
|
||||
p := New()
|
||||
if err := p.AddFile("testdata/ca1.crt"); err != nil {
|
||||
t.Errorf("AddFile error: %s", err)
|
||||
}
|
||||
@@ -20,8 +19,7 @@ func TestCertPool_AddFile(t *testing.T) {
|
||||
}
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
var f Factory
|
||||
p := f.New()
|
||||
p := New()
|
||||
err := p.AddFile("testdata/Makefile")
|
||||
if err == nil {
|
||||
t.Errorf("AddFile wants an error but was nil")
|
||||
@@ -30,8 +28,7 @@ func TestCertPool_AddFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCertPool_AddBase64Encoded(t *testing.T) {
|
||||
var f Factory
|
||||
p := f.New()
|
||||
p := New()
|
||||
if err := p.AddBase64Encoded(readFile(t, "testdata/ca2.crt.base64")); err != nil {
|
||||
t.Errorf("AddBase64Encoded error: %s", err)
|
||||
}
|
||||
@@ -43,8 +40,7 @@ func TestCertPool_AddBase64Encoded(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCertPool_SetRootCAs(t *testing.T) {
|
||||
var f Factory
|
||||
p := f.New()
|
||||
p := New()
|
||||
var cfg tls.Config
|
||||
p.SetRootCAs(&cfg)
|
||||
if cfg.RootCAs != nil {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/int128/kubelogin/pkg/adaptors/certpool (interfaces: FactoryInterface,Interface)
|
||||
// Source: github.com/int128/kubelogin/pkg/adaptors/certpool (interfaces: Interface)
|
||||
|
||||
// Package mock_certpool is a generated GoMock package.
|
||||
package mock_certpool
|
||||
@@ -7,47 +7,9 @@ package mock_certpool
|
||||
import (
|
||||
tls "crypto/tls"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
certpool "github.com/int128/kubelogin/pkg/adaptors/certpool"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockFactoryInterface is a mock of FactoryInterface interface
|
||||
type MockFactoryInterface struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockFactoryInterfaceMockRecorder
|
||||
}
|
||||
|
||||
// MockFactoryInterfaceMockRecorder is the mock recorder for MockFactoryInterface
|
||||
type MockFactoryInterfaceMockRecorder struct {
|
||||
mock *MockFactoryInterface
|
||||
}
|
||||
|
||||
// NewMockFactoryInterface creates a new mock instance
|
||||
func NewMockFactoryInterface(ctrl *gomock.Controller) *MockFactoryInterface {
|
||||
mock := &MockFactoryInterface{ctrl: ctrl}
|
||||
mock.recorder = &MockFactoryInterfaceMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockFactoryInterface) EXPECT() *MockFactoryInterfaceMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// New mocks base method
|
||||
func (m *MockFactoryInterface) New() certpool.Interface {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "New")
|
||||
ret0, _ := ret[0].(certpool.Interface)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// New indicates an expected call of New
|
||||
func (mr *MockFactoryInterfaceMockRecorder) New() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockFactoryInterface)(nil).New))
|
||||
}
|
||||
|
||||
// MockInterface is a mock of Interface interface
|
||||
type MockInterface struct {
|
||||
ctrl *gomock.Controller
|
||||
|
||||
@@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/wire"
|
||||
@@ -24,16 +23,9 @@ type Interface interface {
|
||||
Run(ctx context.Context, args []string, version string) int
|
||||
}
|
||||
|
||||
var defaultListenPort = []int{8000, 18000}
|
||||
var defaultListenAddress = []string{"127.0.0.1:8000", "127.0.0.1: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
|
||||
|
||||
@@ -27,7 +27,37 @@ func TestCmd_Run(t *testing.T) {
|
||||
in: standalone.Input{
|
||||
GrantOptionSet: authentication.GrantOptionSet{
|
||||
AuthCodeOption: &authentication.AuthCodeOption{
|
||||
BindAddress: []string{"127.0.0.1:8000", "127.0.0.1:18000"},
|
||||
BindAddress: defaultListenAddress,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"when --listen-port is set, it should convert the port to address": {
|
||||
args: []string{
|
||||
executable,
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
},
|
||||
in: standalone.Input{
|
||||
GrantOptionSet: authentication.GrantOptionSet{
|
||||
AuthCodeOption: &authentication.AuthCodeOption{
|
||||
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"when --listen-port is set, it should ignore --listen-address flags": {
|
||||
args: []string{
|
||||
executable,
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
"--listen-address", "127.0.0.1:30080",
|
||||
"--listen-address", "127.0.0.1:40080",
|
||||
},
|
||||
in: standalone.Input{
|
||||
GrantOptionSet: authentication.GrantOptionSet{
|
||||
AuthCodeOption: &authentication.AuthCodeOption{
|
||||
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -41,8 +71,8 @@ func TestCmd_Run(t *testing.T) {
|
||||
"--insecure-skip-tls-verify",
|
||||
"-v1",
|
||||
"--grant-type", "authcode",
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
"--listen-address", "127.0.0.1:10080",
|
||||
"--listen-address", "127.0.0.1:20080",
|
||||
"--skip-open-browser",
|
||||
"--username", "USER",
|
||||
"--password", "PASS",
|
||||
@@ -74,8 +104,8 @@ func TestCmd_Run(t *testing.T) {
|
||||
"GrantType=password": {
|
||||
args: []string{executable,
|
||||
"--grant-type", "password",
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
"--listen-address", "127.0.0.1:10080",
|
||||
"--listen-address", "127.0.0.1:20080",
|
||||
"--username", "USER",
|
||||
"--password", "PASS",
|
||||
},
|
||||
@@ -90,8 +120,8 @@ func TestCmd_Run(t *testing.T) {
|
||||
},
|
||||
"GrantType=auto": {
|
||||
args: []string{executable,
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
"--listen-address", "127.0.0.1:10080",
|
||||
"--listen-address", "127.0.0.1:20080",
|
||||
"--username", "USER",
|
||||
"--password", "PASS",
|
||||
},
|
||||
@@ -178,8 +208,8 @@ func TestCmd_Run(t *testing.T) {
|
||||
"--insecure-skip-tls-verify",
|
||||
"-v1",
|
||||
"--grant-type", "authcode",
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
"--listen-address", "127.0.0.1:10080",
|
||||
"--listen-address", "127.0.0.1:20080",
|
||||
"--skip-open-browser",
|
||||
"--username", "USER",
|
||||
"--password", "PASS",
|
||||
@@ -222,8 +252,8 @@ func TestCmd_Run(t *testing.T) {
|
||||
"--oidc-issuer-url", "https://issuer.example.com",
|
||||
"--oidc-client-id", "YOUR_CLIENT_ID",
|
||||
"--grant-type", "password",
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
"--listen-address", "127.0.0.1:10080",
|
||||
"--listen-address", "127.0.0.1:20080",
|
||||
"--username", "USER",
|
||||
"--password", "PASS",
|
||||
},
|
||||
@@ -244,8 +274,8 @@ func TestCmd_Run(t *testing.T) {
|
||||
"get-token",
|
||||
"--oidc-issuer-url", "https://issuer.example.com",
|
||||
"--oidc-client-id", "YOUR_CLIENT_ID",
|
||||
"--listen-port", "10080",
|
||||
"--listen-port", "20080",
|
||||
"--listen-address", "127.0.0.1:10080",
|
||||
"--listen-address", "127.0.0.1:20080",
|
||||
"--username", "USER",
|
||||
"--password", "PASS",
|
||||
},
|
||||
|
||||
@@ -46,12 +46,28 @@ func (o *rootOptions) register(f *pflag.FlagSet) {
|
||||
|
||||
type authenticationOptions struct {
|
||||
GrantType string
|
||||
ListenPort []int
|
||||
ListenAddress []string
|
||||
ListenPort []int // deprecated
|
||||
SkipOpenBrowser bool
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// determineListenAddress returns the addresses from the flags.
|
||||
// Note that --listen-address is always given due to the default value.
|
||||
// If --listen-port is not set, it returns --listen-address.
|
||||
// If --listen-port is set, it returns the strings of --listen-port.
|
||||
func (o *authenticationOptions) determineListenAddress() []string {
|
||||
if len(o.ListenPort) == 0 {
|
||||
return o.ListenAddress
|
||||
}
|
||||
var a []string
|
||||
for _, p := range o.ListenPort {
|
||||
a = append(a, fmt.Sprintf("127.0.0.1:%d", p))
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
var allGrantType = strings.Join([]string{
|
||||
"auto",
|
||||
"authcode",
|
||||
@@ -61,7 +77,9 @@ var allGrantType = strings.Join([]string{
|
||||
|
||||
func (o *authenticationOptions) register(f *pflag.FlagSet) {
|
||||
f.StringVar(&o.GrantType, "grant-type", "auto", fmt.Sprintf("The authorization grant type to use. One of (%s)", allGrantType))
|
||||
f.IntSliceVar(&o.ListenPort, "listen-port", defaultListenPort, "Port to bind to the local server. If multiple ports are given, it will try the ports in order")
|
||||
f.StringSliceVar(&o.ListenAddress, "listen-address", defaultListenAddress, "Address to bind to the local server. If multiple addresses are given, it will try binding in order")
|
||||
//TODO: remove the deprecated flag
|
||||
f.IntSliceVar(&o.ListenPort, "listen-port", nil, "(Deprecated: use --listen-address)")
|
||||
f.BoolVar(&o.SkipOpenBrowser, "skip-open-browser", false, "If true, it does not open the browser on authentication")
|
||||
f.StringVar(&o.Username, "username", "", "If set, perform the resource owner password credentials grant")
|
||||
f.StringVar(&o.Password, "password", "", "If set, use the password instead of asking it")
|
||||
@@ -71,7 +89,7 @@ func (o *authenticationOptions) grantOptionSet() (s authentication.GrantOptionSe
|
||||
switch {
|
||||
case o.GrantType == "authcode" || (o.GrantType == "auto" && o.Username == ""):
|
||||
s.AuthCodeOption = &authentication.AuthCodeOption{
|
||||
BindAddress: translateListenPortToBindAddress(o.ListenPort),
|
||||
BindAddress: o.determineListenAddress(),
|
||||
SkipOpenBrowser: o.SkipOpenBrowser,
|
||||
}
|
||||
case o.GrantType == "authcode-keyboard":
|
||||
|
||||
@@ -55,8 +55,8 @@ func (cmd *Setup) New(ctx context.Context) *cobra.Command {
|
||||
SkipTLSVerify: o.SkipTLSVerify,
|
||||
GrantOptionSet: grantOptionSet,
|
||||
}
|
||||
if c.Flags().Lookup("listen-port").Changed {
|
||||
in.ListenPortArgs = o.authenticationOptions.ListenPort
|
||||
if c.Flags().Lookup("listen-address").Changed {
|
||||
in.ListenAddressArgs = o.authenticationOptions.ListenAddress
|
||||
}
|
||||
if in.IssuerURL == "" || in.ClientID == "" {
|
||||
cmd.Setup.DoStage1()
|
||||
|
||||
7
pkg/adaptors/env/env.go
vendored
7
pkg/adaptors/env/env.go
vendored
@@ -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
pkg/adaptors/env/mock_env/mock_env.go
vendored
15
pkg/adaptors/env/mock_env/mock_env.go
vendored
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
@@ -76,7 +76,7 @@ func unsetenv(t *testing.T, key string) {
|
||||
|
||||
func Test_findCurrentAuthProvider(t *testing.T) {
|
||||
t.Run("CurrentContext", func(t *testing.T) {
|
||||
auth, err := findCurrentAuthProvider(&api.Config{
|
||||
got, err := findCurrentAuthProvider(&api.Config{
|
||||
CurrentContext: "theContext",
|
||||
Contexts: map[string]*api.Context{
|
||||
"theContext": {
|
||||
@@ -118,13 +118,13 @@ func Test_findCurrentAuthProvider(t *testing.T) {
|
||||
IDToken: "YOUR_ID_TOKEN",
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
}
|
||||
if diff := deep.Equal(want, auth); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ByContextName", func(t *testing.T) {
|
||||
auth, err := findCurrentAuthProvider(&api.Config{
|
||||
got, err := findCurrentAuthProvider(&api.Config{
|
||||
Contexts: map[string]*api.Context{
|
||||
"theContext": {
|
||||
AuthInfo: "theUser",
|
||||
@@ -151,13 +151,13 @@ func Test_findCurrentAuthProvider(t *testing.T) {
|
||||
ContextName: "theContext",
|
||||
IDPIssuerURL: "https://accounts.google.com",
|
||||
}
|
||||
if diff := deep.Equal(want, auth); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ByUserName", func(t *testing.T) {
|
||||
auth, err := findCurrentAuthProvider(&api.Config{
|
||||
got, err := findCurrentAuthProvider(&api.Config{
|
||||
AuthInfos: map[string]*api.AuthInfo{
|
||||
"theUser": {
|
||||
LocationOfOrigin: "/path/to/kubeconfig",
|
||||
@@ -178,8 +178,8 @@ func Test_findCurrentAuthProvider(t *testing.T) {
|
||||
UserName: "theUser",
|
||||
IDPIssuerURL: "https://accounts.google.com",
|
||||
}
|
||||
if diff := deep.Equal(want, auth); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestKubeconfig_UpdateAuth(t *testing.T) {
|
||||
@@ -32,9 +34,10 @@ func TestKubeconfig_UpdateAuth(t *testing.T) {
|
||||
t.Fatalf("Could not read kubeconfig: %s", err)
|
||||
}
|
||||
|
||||
got := string(b)
|
||||
want := `apiVersion: v1
|
||||
clusters: []
|
||||
contexts: []
|
||||
clusters: null
|
||||
contexts: null
|
||||
current-context: ""
|
||||
kind: Config
|
||||
preferences: {}
|
||||
@@ -50,8 +53,8 @@ users:
|
||||
refresh-token: YOUR_REFRESH_TOKEN
|
||||
name: oidc
|
||||
`
|
||||
if want != string(b) {
|
||||
t.Errorf("---- kubeconfig wants ----\n%s\n---- but ----\n%s", want, string(b))
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("kubeconfig mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -81,9 +84,10 @@ users:
|
||||
t.Fatalf("Could not read kubeconfig: %s", err)
|
||||
}
|
||||
|
||||
got := string(b)
|
||||
want := `apiVersion: v1
|
||||
clusters: []
|
||||
contexts: []
|
||||
clusters: null
|
||||
contexts: null
|
||||
current-context: ""
|
||||
kind: Config
|
||||
preferences: {}
|
||||
@@ -102,8 +106,8 @@ users:
|
||||
refresh-token: YOUR_REFRESH_TOKEN
|
||||
name: oidc
|
||||
`
|
||||
if want != string(b) {
|
||||
t.Errorf("---- kubeconfig wants ----\n%s\n---- but ----\n%s", want, string(b))
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("kubeconfig mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,9 +14,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type FactoryInterface interface {
|
||||
New(ctx context.Context, config Config) (Interface, error)
|
||||
}
|
||||
type NewFunc func(ctx context.Context, config Config) (Interface, error)
|
||||
|
||||
// Config represents a configuration of OpenID Connect client.
|
||||
type Config struct {
|
||||
@@ -26,14 +24,11 @@ type Config struct {
|
||||
ExtraScopes []string // optional
|
||||
CertPool certpool.Interface
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
type Factory struct {
|
||||
Logger logger.Interface
|
||||
Logger logger.Interface
|
||||
}
|
||||
|
||||
// New returns an instance of adaptors.Interface with the given configuration.
|
||||
func (f *Factory) New(ctx context.Context, config Config) (Interface, error) {
|
||||
func New(ctx context.Context, config Config) (Interface, error) {
|
||||
var tlsConfig tls.Config
|
||||
tlsConfig.InsecureSkipVerify = config.SkipTLSVerify
|
||||
config.CertPool.SetRootCAs(&tlsConfig)
|
||||
@@ -43,7 +38,7 @@ func (f *Factory) New(ctx context.Context, config Config) (Interface, error) {
|
||||
}
|
||||
loggingTransport := &logging.Transport{
|
||||
Base: baseTransport,
|
||||
Logger: f.Logger,
|
||||
Logger: config.Logger,
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
Transport: loggingTransport,
|
||||
@@ -52,7 +47,7 @@ func (f *Factory) New(ctx context.Context, config Config) (Interface, error) {
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
|
||||
provider, err := oidc.NewProvider(ctx, config.IssuerURL)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("could not discovery the OIDCClientFactory issuer: %w", err)
|
||||
return nil, xerrors.Errorf("could not discovery the issuer: %w", err)
|
||||
}
|
||||
return &client{
|
||||
httpClient: httpClient,
|
||||
@@ -63,6 +58,6 @@ func (f *Factory) New(ctx context.Context, config Config) (Interface, error) {
|
||||
ClientSecret: config.ClientSecret,
|
||||
Scopes: append(config.ExtraScopes, oidc.ScopeOpenID),
|
||||
},
|
||||
logger: f.Logger,
|
||||
logger: config.Logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/int128/kubelogin/pkg/adaptors/oidcclient (interfaces: FactoryInterface,Interface)
|
||||
// Source: github.com/int128/kubelogin/pkg/adaptors/oidcclient (interfaces: Interface)
|
||||
|
||||
// Package mock_oidcclient is a generated GoMock package.
|
||||
package mock_oidcclient
|
||||
@@ -11,44 +11,6 @@ import (
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockFactoryInterface is a mock of FactoryInterface interface
|
||||
type MockFactoryInterface struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockFactoryInterfaceMockRecorder
|
||||
}
|
||||
|
||||
// MockFactoryInterfaceMockRecorder is the mock recorder for MockFactoryInterface
|
||||
type MockFactoryInterfaceMockRecorder struct {
|
||||
mock *MockFactoryInterface
|
||||
}
|
||||
|
||||
// NewMockFactoryInterface creates a new mock instance
|
||||
func NewMockFactoryInterface(ctrl *gomock.Controller) *MockFactoryInterface {
|
||||
mock := &MockFactoryInterface{ctrl: ctrl}
|
||||
mock.recorder = &MockFactoryInterfaceMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockFactoryInterface) EXPECT() *MockFactoryInterfaceMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// New mocks base method
|
||||
func (m *MockFactoryInterface) New(arg0 context.Context, arg1 oidcclient.Config) (oidcclient.Interface, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "New", arg0, arg1)
|
||||
ret0, _ := ret[0].(oidcclient.Interface)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// MockInterface is a mock of Interface interface
|
||||
type MockInterface struct {
|
||||
ctrl *gomock.Controller
|
||||
|
||||
@@ -9,17 +9,16 @@ 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"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination mock_oidcclient/mock_oidcclient.go github.com/int128/kubelogin/pkg/adaptors/oidcclient FactoryInterface,Interface
|
||||
//go:generate mockgen -destination mock_oidcclient/mock_oidcclient.go github.com/int128/kubelogin/pkg/adaptors/oidcclient Interface
|
||||
|
||||
// Set provides an implementation and interface for OIDC.
|
||||
var Set = wire.NewSet(
|
||||
wire.Struct(new(Factory), "*"),
|
||||
wire.Bind(new(FactoryInterface), new(*Factory)),
|
||||
wire.Value(NewFunc(New)),
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
@@ -56,11 +55,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 +168,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 {
|
||||
|
||||
@@ -34,10 +34,10 @@ func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
|
||||
}
|
||||
|
||||
// FindByKey mocks base method
|
||||
func (m *MockInterface) FindByKey(arg0 string, arg1 tokencache.Key) (*tokencache.TokenCache, error) {
|
||||
func (m *MockInterface) FindByKey(arg0 string, arg1 tokencache.Key) (*tokencache.Value, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindByKey", arg0, arg1)
|
||||
ret0, _ := ret[0].(*tokencache.TokenCache)
|
||||
ret0, _ := ret[0].(*tokencache.Value)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func (mr *MockInterfaceMockRecorder) FindByKey(arg0, arg1 interface{}) *gomock.C
|
||||
}
|
||||
|
||||
// Save mocks base method
|
||||
func (m *MockInterface) Save(arg0 string, arg1 tokencache.Key, arg2 tokencache.TokenCache) error {
|
||||
func (m *MockInterface) Save(arg0 string, arg1 tokencache.Key, arg2 tokencache.Value) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Save", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
|
||||
@@ -2,6 +2,7 @@ package tokencache
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"os"
|
||||
@@ -20,18 +21,22 @@ var Set = wire.NewSet(
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
FindByKey(dir string, key Key) (*TokenCache, error)
|
||||
Save(dir string, key Key, cache TokenCache) error
|
||||
FindByKey(dir string, key Key) (*Value, error)
|
||||
Save(dir string, key Key, value Value) error
|
||||
}
|
||||
|
||||
// Key represents a key of a token cache.
|
||||
type Key struct {
|
||||
IssuerURL string
|
||||
ClientID string
|
||||
IssuerURL string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
ExtraScopes []string
|
||||
CACertFilename string
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
// TokenCache represents a token cache.
|
||||
type TokenCache struct {
|
||||
// Value represents a value of a token cache.
|
||||
type Value struct {
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
}
|
||||
@@ -40,42 +45,52 @@ type TokenCache struct {
|
||||
// Filename of a token cache is sha256 digest of the issuer, zero-character and client ID.
|
||||
type Repository struct{}
|
||||
|
||||
func (r *Repository) FindByKey(dir string, key Key) (*TokenCache, error) {
|
||||
filename := filepath.Join(dir, computeFilename(key))
|
||||
f, err := os.Open(filename)
|
||||
func (r *Repository) FindByKey(dir string, key Key) (*Value, error) {
|
||||
filename, err := computeFilename(key)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("could not open file %s: %w", filename, err)
|
||||
return nil, xerrors.Errorf("could not compute the key: %w", err)
|
||||
}
|
||||
p := filepath.Join(dir, filename)
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("could not open file %s: %w", p, err)
|
||||
}
|
||||
defer f.Close()
|
||||
d := json.NewDecoder(f)
|
||||
var c TokenCache
|
||||
var c Value
|
||||
if err := d.Decode(&c); err != nil {
|
||||
return nil, xerrors.Errorf("could not decode json file %s: %w", filename, err)
|
||||
return nil, xerrors.Errorf("could not decode json file %s: %w", p, err)
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (r *Repository) Save(dir string, key Key, cache TokenCache) error {
|
||||
func (r *Repository) Save(dir string, key Key, value Value) error {
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return xerrors.Errorf("could not create directory %s: %w", dir, err)
|
||||
}
|
||||
filename := filepath.Join(dir, computeFilename(key))
|
||||
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
filename, err := computeFilename(key)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("could not create file %s: %w", filename, err)
|
||||
return xerrors.Errorf("could not compute the key: %w", err)
|
||||
}
|
||||
p := filepath.Join(dir, filename)
|
||||
f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("could not create file %s: %w", p, err)
|
||||
}
|
||||
defer f.Close()
|
||||
e := json.NewEncoder(f)
|
||||
if err := e.Encode(&cache); err != nil {
|
||||
return xerrors.Errorf("could not encode json to file %s: %w", filename, err)
|
||||
if err := e.Encode(&value); err != nil {
|
||||
return xerrors.Errorf("could not encode json to file %s: %w", p, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeFilename(key Key) string {
|
||||
func computeFilename(key Key) (string, error) {
|
||||
s := sha256.New()
|
||||
_, _ = s.Write([]byte(key.IssuerURL))
|
||||
_, _ = s.Write([]byte{0x00})
|
||||
_, _ = s.Write([]byte(key.ClientID))
|
||||
return hex.EncodeToString(s.Sum(nil))
|
||||
e := gob.NewEncoder(s)
|
||||
if err := e.Encode(&key); err != nil {
|
||||
return "", xerrors.Errorf("could not encode the key: %w", err)
|
||||
}
|
||||
h := hex.EncodeToString(s.Sum(nil))
|
||||
return h, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestRepository_FindByKey(t *testing.T) {
|
||||
@@ -23,22 +23,30 @@ func TestRepository_FindByKey(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
key := Key{
|
||||
IssuerURL: "YOUR_ISSUER",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
IssuerURL: "YOUR_ISSUER",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
ExtraScopes: []string{"openid", "email"},
|
||||
CACertFilename: "/path/to/cert",
|
||||
SkipTLSVerify: false,
|
||||
}
|
||||
json := `{"id_token":"YOUR_ID_TOKEN","refresh_token":"YOUR_REFRESH_TOKEN"}`
|
||||
filename := filepath.Join(dir, computeFilename(key))
|
||||
if err := ioutil.WriteFile(filename, []byte(json), 0600); err != nil {
|
||||
filename, err := computeFilename(key)
|
||||
if err != nil {
|
||||
t.Errorf("could not compute the key: %s", err)
|
||||
}
|
||||
p := filepath.Join(dir, filename)
|
||||
if err := ioutil.WriteFile(p, []byte(json), 0600); err != nil {
|
||||
t.Fatalf("could not write to the temp file: %s", err)
|
||||
}
|
||||
|
||||
tokenCache, err := r.FindByKey(dir, key)
|
||||
value, err := r.FindByKey(dir, key)
|
||||
if err != nil {
|
||||
t.Errorf("err wants nil but %+v", err)
|
||||
}
|
||||
want := &TokenCache{IDToken: "YOUR_ID_TOKEN", RefreshToken: "YOUR_REFRESH_TOKEN"}
|
||||
if diff := deep.Equal(tokenCache, want); diff != nil {
|
||||
t.Error(diff)
|
||||
want := &Value{IDToken: "YOUR_ID_TOKEN", RefreshToken: "YOUR_REFRESH_TOKEN"}
|
||||
if diff := cmp.Diff(want, value); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -58,23 +66,32 @@ func TestRepository_Save(t *testing.T) {
|
||||
}()
|
||||
|
||||
key := Key{
|
||||
IssuerURL: "YOUR_ISSUER",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
IssuerURL: "YOUR_ISSUER",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
ExtraScopes: []string{"openid", "email"},
|
||||
CACertFilename: "/path/to/cert",
|
||||
SkipTLSVerify: false,
|
||||
}
|
||||
tokenCache := TokenCache{IDToken: "YOUR_ID_TOKEN", RefreshToken: "YOUR_REFRESH_TOKEN"}
|
||||
if err := r.Save(dir, key, tokenCache); err != nil {
|
||||
value := Value{IDToken: "YOUR_ID_TOKEN", RefreshToken: "YOUR_REFRESH_TOKEN"}
|
||||
if err := r.Save(dir, key, value); err != nil {
|
||||
t.Errorf("err wants nil but %+v", err)
|
||||
}
|
||||
|
||||
filename := filepath.Join(dir, computeFilename(key))
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
filename, err := computeFilename(key)
|
||||
if err != nil {
|
||||
t.Errorf("could not compute the key: %s", err)
|
||||
}
|
||||
p := filepath.Join(dir, filename)
|
||||
b, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read the token cache file: %s", err)
|
||||
}
|
||||
want := `{"id_token":"YOUR_ID_TOKEN","refresh_token":"YOUR_REFRESH_TOKEN"}
|
||||
`
|
||||
if diff := deep.Equal(string(b), want); diff != nil {
|
||||
t.Error(diff)
|
||||
got := string(b)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
35
pkg/di/di.go
35
pkg/di/di.go
@@ -22,36 +22,27 @@ import (
|
||||
|
||||
// NewCmd returns an instance of adaptors.Cmd.
|
||||
func NewCmd() cmd.Interface {
|
||||
wire.Build(
|
||||
NewCmdForHeadless,
|
||||
|
||||
// dependencies for production
|
||||
logger.Set,
|
||||
wire.Value(authentication.DefaultLocalServerReadyFunc),
|
||||
credentialPluginAdaptor.Set,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdForHeadless returns an instance of adaptors.Cmd for headless testing.
|
||||
func NewCmdForHeadless(logger.Interface, authentication.LocalServerReadyFunc, credentialPluginAdaptor.Interface) cmd.Interface {
|
||||
wire.Build(
|
||||
// use-cases
|
||||
authentication.Set,
|
||||
wire.Value(authentication.DefaultLocalServerReadyFunc),
|
||||
standalone.Set,
|
||||
credentialPluginUseCase.Set,
|
||||
setup.Set,
|
||||
|
||||
// adaptors
|
||||
cmd.Set,
|
||||
env.Set,
|
||||
kubeconfig.Set,
|
||||
tokencache.Set,
|
||||
credentialPluginAdaptor.Set,
|
||||
oidcclient.Set,
|
||||
jwtdecoder.Set,
|
||||
certpool.Set,
|
||||
logger.Set,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdForHeadless returns an instance of adaptors.Cmd for headless testing.
|
||||
func NewCmdForHeadless(logger.Interface, authentication.LocalServerReadyFunc, credentialPluginAdaptor.Interface) cmd.Interface {
|
||||
wire.Build(
|
||||
authentication.Set,
|
||||
standalone.Set,
|
||||
credentialPluginUseCase.Set,
|
||||
setup.Set,
|
||||
|
||||
cmd.Set,
|
||||
env.Set,
|
||||
kubeconfig.Set,
|
||||
|
||||
@@ -25,12 +25,20 @@ import (
|
||||
|
||||
func NewCmd() cmd.Interface {
|
||||
loggerInterface := logger.New()
|
||||
factory := &oidcclient.Factory{
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
localServerReadyFunc := _wireLocalServerReadyFuncValue
|
||||
interaction := &credentialplugin.Interaction{}
|
||||
cmdInterface := NewCmdForHeadless(loggerInterface, localServerReadyFunc, interaction)
|
||||
return cmdInterface
|
||||
}
|
||||
|
||||
var (
|
||||
_wireLocalServerReadyFuncValue = authentication.DefaultLocalServerReadyFunc
|
||||
)
|
||||
|
||||
func NewCmdForHeadless(loggerInterface logger.Interface, localServerReadyFunc authentication.LocalServerReadyFunc, credentialpluginInterface credentialplugin.Interface) cmd.Interface {
|
||||
newFunc := _wireNewFuncValue
|
||||
decoder := &jwtdecoder.Decoder{}
|
||||
envEnv := &env.Env{}
|
||||
localServerReadyFunc := _wireLocalServerReadyFuncValue
|
||||
authCode := &authentication.AuthCode{
|
||||
Env: envEnv,
|
||||
Logger: loggerInterface,
|
||||
@@ -45,34 +53,34 @@ func NewCmd() cmd.Interface {
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
authenticationAuthentication := &authentication.Authentication{
|
||||
OIDCClientFactory: factory,
|
||||
JWTDecoder: decoder,
|
||||
Logger: loggerInterface,
|
||||
AuthCode: authCode,
|
||||
AuthCodeKeyboard: authCodeKeyboard,
|
||||
ROPC: ropc,
|
||||
NewOIDCClient: newFunc,
|
||||
JWTDecoder: decoder,
|
||||
Logger: loggerInterface,
|
||||
Env: envEnv,
|
||||
AuthCode: authCode,
|
||||
AuthCodeKeyboard: authCodeKeyboard,
|
||||
ROPC: ropc,
|
||||
}
|
||||
kubeconfigKubeconfig := &kubeconfig.Kubeconfig{
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
certpoolFactory := &certpool.Factory{}
|
||||
certpoolNewFunc := _wireCertpoolNewFuncValue
|
||||
standaloneStandalone := &standalone.Standalone{
|
||||
Authentication: authenticationAuthentication,
|
||||
Kubeconfig: kubeconfigKubeconfig,
|
||||
CertPoolFactory: certpoolFactory,
|
||||
Logger: loggerInterface,
|
||||
Authentication: authenticationAuthentication,
|
||||
Kubeconfig: kubeconfigKubeconfig,
|
||||
NewCertPool: certpoolNewFunc,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
root := &cmd.Root{
|
||||
Standalone: standaloneStandalone,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
repository := &tokencache.Repository{}
|
||||
interaction := &credentialplugin.Interaction{}
|
||||
getToken := &credentialplugin2.GetToken{
|
||||
Authentication: authenticationAuthentication,
|
||||
TokenCacheRepository: repository,
|
||||
CertPoolFactory: certpoolFactory,
|
||||
Interaction: interaction,
|
||||
NewCertPool: certpoolNewFunc,
|
||||
Interaction: credentialpluginInterface,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
cmdGetToken := &cmd.GetToken{
|
||||
@@ -80,9 +88,9 @@ func NewCmd() cmd.Interface {
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
setupSetup := &setup.Setup{
|
||||
Authentication: authenticationAuthentication,
|
||||
CertPoolFactory: certpoolFactory,
|
||||
Logger: loggerInterface,
|
||||
Authentication: authenticationAuthentication,
|
||||
NewCertPool: certpoolNewFunc,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
cmdSetup := &cmd.Setup{
|
||||
Setup: setupSetup,
|
||||
@@ -97,75 +105,6 @@ func NewCmd() cmd.Interface {
|
||||
}
|
||||
|
||||
var (
|
||||
_wireLocalServerReadyFuncValue = authentication.DefaultLocalServerReadyFunc
|
||||
_wireNewFuncValue = oidcclient.NewFunc(oidcclient.New)
|
||||
_wireCertpoolNewFuncValue = certpool.NewFunc(certpool.New)
|
||||
)
|
||||
|
||||
func NewCmdForHeadless(loggerInterface logger.Interface, localServerReadyFunc authentication.LocalServerReadyFunc, credentialpluginInterface credentialplugin.Interface) cmd.Interface {
|
||||
factory := &oidcclient.Factory{
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
decoder := &jwtdecoder.Decoder{}
|
||||
envEnv := &env.Env{}
|
||||
authCode := &authentication.AuthCode{
|
||||
Env: envEnv,
|
||||
Logger: loggerInterface,
|
||||
LocalServerReadyFunc: localServerReadyFunc,
|
||||
}
|
||||
authCodeKeyboard := &authentication.AuthCodeKeyboard{
|
||||
Env: envEnv,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
ropc := &authentication.ROPC{
|
||||
Env: envEnv,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
authenticationAuthentication := &authentication.Authentication{
|
||||
OIDCClientFactory: factory,
|
||||
JWTDecoder: decoder,
|
||||
Logger: loggerInterface,
|
||||
AuthCode: authCode,
|
||||
AuthCodeKeyboard: authCodeKeyboard,
|
||||
ROPC: ropc,
|
||||
}
|
||||
kubeconfigKubeconfig := &kubeconfig.Kubeconfig{
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
certpoolFactory := &certpool.Factory{}
|
||||
standaloneStandalone := &standalone.Standalone{
|
||||
Authentication: authenticationAuthentication,
|
||||
Kubeconfig: kubeconfigKubeconfig,
|
||||
CertPoolFactory: certpoolFactory,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
root := &cmd.Root{
|
||||
Standalone: standaloneStandalone,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
repository := &tokencache.Repository{}
|
||||
getToken := &credentialplugin2.GetToken{
|
||||
Authentication: authenticationAuthentication,
|
||||
TokenCacheRepository: repository,
|
||||
CertPoolFactory: certpoolFactory,
|
||||
Interaction: credentialpluginInterface,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
cmdGetToken := &cmd.GetToken{
|
||||
GetToken: getToken,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
setupSetup := &setup.Setup{
|
||||
Authentication: authenticationAuthentication,
|
||||
CertPoolFactory: certpoolFactory,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
cmdSetup := &cmd.Setup{
|
||||
Setup: setupSetup,
|
||||
}
|
||||
cmdCmd := &cmd.Cmd{
|
||||
Root: root,
|
||||
GetToken: cmdGetToken,
|
||||
Setup: cmdSetup,
|
||||
Logger: loggerInterface,
|
||||
}
|
||||
return cmdCmd
|
||||
}
|
||||
|
||||
20
pkg/domain/oidc/token.go
Normal file
20
pkg/domain/oidc/token.go
Normal 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
pkg/domain/oidc/token_test.go
Normal file
36
pkg/domain/oidc/token_test.go
Normal 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")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -5,19 +5,23 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/env/mock_env"
|
||||
"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().
|
||||
@@ -51,19 +53,17 @@ func TestAuthCodeKeyboard_Do(t *testing.T) {
|
||||
Env: mockEnv,
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
out, err := u.Do(ctx, nil, mockOIDCClient)
|
||||
got, err := u.Do(ctx, nil, mockOIDCClient)
|
||||
if err != nil {
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,17 +5,21 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/env/mock_env"
|
||||
"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,28 +38,24 @@ 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),
|
||||
}
|
||||
out, err := u.Do(ctx, o, mockOIDCClient)
|
||||
got, err := u.Do(ctx, o, mockOIDCClient)
|
||||
if err != nil {
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
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().
|
||||
@@ -87,19 +85,17 @@ func TestAuthCode_Do(t *testing.T) {
|
||||
Logger: mock_logger.New(t),
|
||||
Env: mockEnv,
|
||||
}
|
||||
out, err := u.Do(ctx, o, mockOIDCClient)
|
||||
got, err := u.Do(ctx, o, mockOIDCClient)
|
||||
if err != nil {
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -91,12 +90,13 @@ const passwordPrompt = "Password: "
|
||||
// If the Password is not set, it asks a password by the prompt.
|
||||
//
|
||||
type Authentication struct {
|
||||
OIDCClientFactory oidcclient.FactoryInterface
|
||||
JWTDecoder jwtdecoder.Interface
|
||||
Logger logger.Interface
|
||||
AuthCode *AuthCode
|
||||
AuthCodeKeyboard *AuthCodeKeyboard
|
||||
ROPC *ROPC
|
||||
NewOIDCClient oidcclient.NewFunc
|
||||
JWTDecoder jwtdecoder.Interface
|
||||
Logger logger.Interface
|
||||
Env env.Interface
|
||||
AuthCode *AuthCode
|
||||
AuthCodeKeyboard *AuthCodeKeyboard
|
||||
ROPC *ROPC
|
||||
}
|
||||
|
||||
func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
|
||||
@@ -109,31 +109,30 @@ 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)
|
||||
}
|
||||
|
||||
u.Logger.V(1).Infof("initializing an OpenID Connect client")
|
||||
client, err := u.OIDCClientFactory.New(ctx, oidcclient.Config{
|
||||
client, err := u.NewOIDCClient(ctx, oidcclient.Config{
|
||||
IssuerURL: in.IssuerURL,
|
||||
ClientID: in.ClientID,
|
||||
ClientSecret: in.ClientSecret,
|
||||
ExtraScopes: in.ExtraScopes,
|
||||
CertPool: in.CertPool,
|
||||
SkipTLSVerify: in.SkipTLSVerify,
|
||||
Logger: u.Logger,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("could not create an OpenID Connect client: %w", err)
|
||||
return nil, xerrors.Errorf("could not initialize the OpenID Connect client: %w", err)
|
||||
}
|
||||
|
||||
if in.RefreshToken != "" {
|
||||
@@ -141,11 +140,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)
|
||||
|
||||
@@ -5,21 +5,31 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/jwtdecoder"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"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"
|
||||
"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"
|
||||
)
|
||||
|
||||
var cmpIgnoreLogger = cmpopts.IgnoreInterfaces(struct{ logger.Interface }{})
|
||||
|
||||
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
|
||||
testingLogger := mock_logger.New(t)
|
||||
|
||||
t.Run("HasValidIDToken", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
@@ -32,32 +42,30 @@ 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),
|
||||
JWTDecoder: mockDecoder,
|
||||
Logger: testingLogger,
|
||||
Env: mockEnv,
|
||||
}
|
||||
out, err := u.Do(ctx, in)
|
||||
got, err := u.Do(ctx, in)
|
||||
if err != nil {
|
||||
t.Errorf("Do returned error: %+v", err)
|
||||
}
|
||||
want := &Output{
|
||||
AlreadyHasValidIDToken: true,
|
||||
IDToken: "VALID_ID_TOKEN",
|
||||
IDTokenSubject: "YOUR_SUBJECT",
|
||||
IDTokenExpiry: futureTime,
|
||||
IDTokenClaims: dummyTokenClaims,
|
||||
}
|
||||
if diff := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -73,50 +81,49 @@ 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().
|
||||
New(ctx, oidcclient.Config{
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}).
|
||||
Return(mockOIDCClient, nil)
|
||||
u := Authentication{
|
||||
OIDCClientFactory: mockOIDCClientFactory,
|
||||
JWTDecoder: mockDecoder,
|
||||
Logger: mock_logger.New(t),
|
||||
NewOIDCClient: func(_ context.Context, got oidcclient.Config) (oidcclient.Interface, error) {
|
||||
want := oidcclient.Config{
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}
|
||||
if diff := cmp.Diff(want, got, cmpIgnoreLogger); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
return mockOIDCClient, nil
|
||||
},
|
||||
JWTDecoder: mockDecoder,
|
||||
Logger: testingLogger,
|
||||
Env: mockEnv,
|
||||
}
|
||||
out, err := u.Do(ctx, in)
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -138,14 +145,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,41 +163,40 @@ 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().
|
||||
New(ctx, oidcclient.Config{
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}).
|
||||
Return(mockOIDCClient, nil)
|
||||
u := Authentication{
|
||||
OIDCClientFactory: mockOIDCClientFactory,
|
||||
JWTDecoder: mockDecoder,
|
||||
Logger: mock_logger.New(t),
|
||||
NewOIDCClient: func(_ context.Context, got oidcclient.Config) (oidcclient.Interface, error) {
|
||||
want := oidcclient.Config{
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}
|
||||
if diff := cmp.Diff(want, got, cmpIgnoreLogger); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
return mockOIDCClient, nil
|
||||
},
|
||||
JWTDecoder: mockDecoder,
|
||||
Logger: testingLogger,
|
||||
Env: mockEnv,
|
||||
AuthCode: &AuthCode{
|
||||
Logger: mock_logger.New(t),
|
||||
Logger: testingLogger,
|
||||
},
|
||||
}
|
||||
out, err := u.Do(ctx, in)
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -214,40 +220,38 @@ 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().
|
||||
New(ctx, oidcclient.Config{
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}).
|
||||
Return(mockOIDCClient, nil)
|
||||
u := Authentication{
|
||||
OIDCClientFactory: mockOIDCClientFactory,
|
||||
Logger: mock_logger.New(t),
|
||||
NewOIDCClient: func(_ context.Context, got oidcclient.Config) (oidcclient.Interface, error) {
|
||||
want := oidcclient.Config{
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}
|
||||
if diff := cmp.Diff(want, got, cmpIgnoreLogger); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
return mockOIDCClient, nil
|
||||
},
|
||||
Logger: testingLogger,
|
||||
ROPC: &ROPC{
|
||||
Logger: mock_logger.New(t),
|
||||
Logger: testingLogger,
|
||||
},
|
||||
}
|
||||
out, err := u.Do(ctx, in)
|
||||
got, err := u.Do(ctx, in)
|
||||
if err != nil {
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -5,18 +5,22 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-test/deep"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/env/mock_env"
|
||||
"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)
|
||||
@@ -42,19 +44,17 @@ func TestROPC_Do(t *testing.T) {
|
||||
Env: mockEnv,
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
out, err := u.Do(ctx, o, mockOIDCClient)
|
||||
got, err := u.Do(ctx, o, mockOIDCClient)
|
||||
if err != nil {
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -71,28 +71,24 @@ 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),
|
||||
}
|
||||
out, err := u.Do(ctx, o, mockOIDCClient)
|
||||
got, err := u.Do(ctx, o, mockOIDCClient)
|
||||
if err != nil {
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
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)
|
||||
@@ -120,19 +114,17 @@ func TestROPC_Do(t *testing.T) {
|
||||
Env: mockEnv,
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
out, err := u.Do(ctx, o, mockOIDCClient)
|
||||
got, err := u.Do(ctx, o, mockOIDCClient)
|
||||
if err != nil {
|
||||
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 := deep.Equal(want, out); diff != nil {
|
||||
t.Error(diff)
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Errorf("mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ type Input struct {
|
||||
type GetToken struct {
|
||||
Authentication authentication.Interface
|
||||
TokenCacheRepository tokencache.Interface
|
||||
CertPoolFactory certpool.FactoryInterface
|
||||
NewCertPool certpool.NewFunc
|
||||
Interaction credentialplugin.Interface
|
||||
Logger logger.Interface
|
||||
}
|
||||
@@ -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
|
||||
@@ -61,13 +61,20 @@ func (u *GetToken) Do(ctx context.Context, in Input) error {
|
||||
|
||||
func (u *GetToken) getTokenFromCacheOrProvider(ctx context.Context, in Input) (*authentication.Output, error) {
|
||||
u.Logger.V(1).Infof("finding a token from cache directory %s", in.TokenCacheDir)
|
||||
cacheKey := tokencache.Key{IssuerURL: in.IssuerURL, ClientID: in.ClientID}
|
||||
cache, err := u.TokenCacheRepository.FindByKey(in.TokenCacheDir, cacheKey)
|
||||
tokenCacheKey := tokencache.Key{
|
||||
IssuerURL: in.IssuerURL,
|
||||
ClientID: in.ClientID,
|
||||
ClientSecret: in.ClientSecret,
|
||||
ExtraScopes: in.ExtraScopes,
|
||||
CACertFilename: in.CACertFilename,
|
||||
SkipTLSVerify: in.SkipTLSVerify,
|
||||
}
|
||||
tokenCacheValue, err := u.TokenCacheRepository.FindByKey(in.TokenCacheDir, tokenCacheKey)
|
||||
if err != nil {
|
||||
u.Logger.V(1).Infof("could not find a token cache: %s", err)
|
||||
cache = &tokencache.TokenCache{}
|
||||
tokenCacheValue = &tokencache.Value{}
|
||||
}
|
||||
certPool := u.CertPoolFactory.New()
|
||||
certPool := u.NewCertPool()
|
||||
if in.CACertFilename != "" {
|
||||
if err := certPool.AddFile(in.CACertFilename); err != nil {
|
||||
return nil, xerrors.Errorf("could not load the certificate: %w", err)
|
||||
@@ -80,27 +87,27 @@ func (u *GetToken) getTokenFromCacheOrProvider(ctx context.Context, in Input) (*
|
||||
ExtraScopes: in.ExtraScopes,
|
||||
CertPool: certPool,
|
||||
SkipTLSVerify: in.SkipTLSVerify,
|
||||
IDToken: cache.IDToken,
|
||||
RefreshToken: cache.RefreshToken,
|
||||
IDToken: tokenCacheValue.IDToken,
|
||||
RefreshToken: tokenCacheValue.RefreshToken,
|
||||
GrantOptionSet: in.GrantOptionSet,
|
||||
})
|
||||
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)
|
||||
newCache := tokencache.TokenCache{
|
||||
u.Logger.V(1).Infof("you got a valid token until %s", out.IDTokenClaims.Expiry)
|
||||
newTokenCacheValue := tokencache.Value{
|
||||
IDToken: out.IDToken,
|
||||
RefreshToken: out.RefreshToken,
|
||||
}
|
||||
if err := u.TokenCacheRepository.Save(in.TokenCacheDir, cacheKey, newCache); err != nil {
|
||||
if err := u.TokenCacheRepository.Save(in.TokenCacheDir, tokenCacheKey, newTokenCacheValue); err != nil {
|
||||
return nil, xerrors.Errorf("could not write the token cache: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
|
||||
@@ -6,20 +6,25 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/certpool"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/certpool/mock_certpool"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/credentialplugin"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/credentialplugin/mock_credentialplugin"
|
||||
"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
|
||||
@@ -38,10 +43,6 @@ func TestGetToken_Do(t *testing.T) {
|
||||
mockCertPool := mock_certpool.NewMockInterface(ctrl)
|
||||
mockCertPool.EXPECT().
|
||||
AddFile("/path/to/cert")
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
|
||||
mockAuthentication.EXPECT().
|
||||
Do(ctx, authentication.Input{
|
||||
@@ -55,23 +56,29 @@ 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)
|
||||
tokenCacheRepository.EXPECT().
|
||||
FindByKey("/path/to/token-cache", tokencache.Key{
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
}).
|
||||
FindByKey("/path/to/token-cache",
|
||||
tokencache.Key{
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
CACertFilename: "/path/to/cert",
|
||||
SkipTLSVerify: true,
|
||||
}).
|
||||
Return(nil, xerrors.New("file not found"))
|
||||
tokenCacheRepository.EXPECT().
|
||||
Save("/path/to/token-cache",
|
||||
tokencache.Key{
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
CACertFilename: "/path/to/cert",
|
||||
SkipTLSVerify: true,
|
||||
},
|
||||
tokencache.TokenCache{
|
||||
tokencache.Value{
|
||||
IDToken: "YOUR_ID_TOKEN",
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
})
|
||||
@@ -79,12 +86,12 @@ func TestGetToken_Do(t *testing.T) {
|
||||
credentialPluginInteraction.EXPECT().
|
||||
Write(credentialplugin.Output{
|
||||
Token: "YOUR_ID_TOKEN",
|
||||
Expiry: futureTime,
|
||||
Expiry: dummyTokenClaims.Expiry,
|
||||
})
|
||||
u := GetToken{
|
||||
Authentication: mockAuthentication,
|
||||
TokenCacheRepository: tokenCacheRepository,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Interaction: credentialPluginInteraction,
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
@@ -104,10 +111,6 @@ func TestGetToken_Do(t *testing.T) {
|
||||
TokenCacheDir: "/path/to/token-cache",
|
||||
}
|
||||
mockCertPool := mock_certpool.NewMockInterface(ctrl)
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
|
||||
mockAuthentication.EXPECT().
|
||||
Do(ctx, authentication.Input{
|
||||
@@ -120,28 +123,28 @@ 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)
|
||||
tokenCacheRepository.EXPECT().
|
||||
FindByKey("/path/to/token-cache", tokencache.Key{
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}).
|
||||
Return(&tokencache.TokenCache{
|
||||
Return(&tokencache.Value{
|
||||
IDToken: "VALID_ID_TOKEN",
|
||||
}, nil)
|
||||
credentialPluginInteraction := mock_credentialplugin.NewMockInterface(ctrl)
|
||||
credentialPluginInteraction.EXPECT().
|
||||
Write(credentialplugin.Output{
|
||||
Token: "VALID_ID_TOKEN",
|
||||
Expiry: futureTime,
|
||||
Expiry: dummyTokenClaims.Expiry,
|
||||
})
|
||||
u := GetToken{
|
||||
Authentication: mockAuthentication,
|
||||
TokenCacheRepository: tokenCacheRepository,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Interaction: credentialPluginInteraction,
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
@@ -161,10 +164,6 @@ func TestGetToken_Do(t *testing.T) {
|
||||
TokenCacheDir: "/path/to/token-cache",
|
||||
}
|
||||
mockCertPool := mock_certpool.NewMockInterface(ctrl)
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
|
||||
mockAuthentication.EXPECT().
|
||||
Do(ctx, authentication.Input{
|
||||
@@ -177,14 +176,15 @@ func TestGetToken_Do(t *testing.T) {
|
||||
tokenCacheRepository := mock_tokencache.NewMockInterface(ctrl)
|
||||
tokenCacheRepository.EXPECT().
|
||||
FindByKey("/path/to/token-cache", tokencache.Key{
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
IssuerURL: "https://accounts.google.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}).
|
||||
Return(nil, xerrors.New("file not found"))
|
||||
u := GetToken{
|
||||
Authentication: mockAuthentication,
|
||||
TokenCacheRepository: tokenCacheRepository,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Interaction: mock_credentialplugin.NewMockInterface(ctrl),
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type Interface interface {
|
||||
}
|
||||
|
||||
type Setup struct {
|
||||
Authentication authentication.Interface
|
||||
CertPoolFactory certpool.FactoryInterface
|
||||
Logger logger.Interface
|
||||
Authentication authentication.Interface
|
||||
NewCertPool certpool.NewFunc
|
||||
Logger logger.Interface
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
@@ -65,19 +64,19 @@ type stage2Vars struct {
|
||||
|
||||
// Stage2Input represents an input DTO of the stage2.
|
||||
type Stage2Input struct {
|
||||
IssuerURL string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
ExtraScopes []string // optional
|
||||
CACertFilename string // If set, use the CA cert
|
||||
SkipTLSVerify bool
|
||||
ListenPortArgs []int // non-nil if set by the command arg
|
||||
GrantOptionSet authentication.GrantOptionSet
|
||||
IssuerURL string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
ExtraScopes []string // optional
|
||||
CACertFilename string // If set, use the CA cert
|
||||
SkipTLSVerify bool
|
||||
ListenAddressArgs []string // non-nil if set by the command arg
|
||||
GrantOptionSet authentication.GrantOptionSet
|
||||
}
|
||||
|
||||
func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error {
|
||||
u.Logger.Printf(`## 2. Verify authentication`)
|
||||
certPool := u.CertPoolFactory.New()
|
||||
certPool := u.NewCertPool()
|
||||
if in.CACertFilename != "" {
|
||||
if err := certPool.AddFile(in.CACertFilename); err != nil {
|
||||
return xerrors.Errorf("could not load the certificate: %w", err)
|
||||
@@ -96,7 +95,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 +103,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 {
|
||||
@@ -136,9 +135,7 @@ func makeCredentialPluginArgs(in Stage2Input) []string {
|
||||
args = append(args, "--skip-open-browser")
|
||||
}
|
||||
}
|
||||
for _, port := range in.ListenPortArgs {
|
||||
args = append(args, fmt.Sprintf("--listen-port=%d", port))
|
||||
}
|
||||
args = append(args, in.ListenAddressArgs...)
|
||||
if in.GrantOptionSet.ROPCOption != nil {
|
||||
if in.GrantOptionSet.ROPCOption.Username != "" {
|
||||
args = append(args, "--username="+in.GrantOptionSet.ROPCOption.Username)
|
||||
|
||||
@@ -6,13 +6,20 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/certpool"
|
||||
"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()
|
||||
@@ -31,10 +38,6 @@ func TestSetup_DoStage2(t *testing.T) {
|
||||
mockCertPool := mock_certpool.NewMockInterface(ctrl)
|
||||
mockCertPool.EXPECT().
|
||||
AddFile("/path/to/cert")
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
|
||||
mockAuthentication.EXPECT().
|
||||
Do(ctx, authentication.Input{
|
||||
@@ -47,16 +50,14 @@ 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,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
Logger: mock_logger.New(t),
|
||||
Authentication: mockAuthentication,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
if err := u.DoStage2(ctx, in); err != nil {
|
||||
t.Errorf("DoStage2 returned error: %+v", err)
|
||||
|
||||
@@ -46,10 +46,10 @@ See https://github.com/int128/kubelogin for more.
|
||||
// Otherwise, update the kubeconfig.
|
||||
//
|
||||
type Standalone struct {
|
||||
Authentication authentication.Interface
|
||||
Kubeconfig kubeconfig.Interface
|
||||
CertPoolFactory certpool.FactoryInterface
|
||||
Logger logger.Interface
|
||||
Authentication authentication.Interface
|
||||
Kubeconfig kubeconfig.Interface
|
||||
NewCertPool certpool.NewFunc
|
||||
Logger logger.Interface
|
||||
}
|
||||
|
||||
func (u *Standalone) Do(ctx context.Context, in Input) error {
|
||||
@@ -65,7 +65,7 @@ func (u *Standalone) Do(ctx context.Context, in Input) error {
|
||||
}
|
||||
u.Logger.V(1).Infof("using the authentication provider of the user %s", authProvider.UserName)
|
||||
u.Logger.V(1).Infof("a token will be written to %s", authProvider.LocationOfOrigin)
|
||||
certPool := u.CertPoolFactory.New()
|
||||
certPool := u.NewCertPool()
|
||||
if authProvider.IDPCertificateAuthority != "" {
|
||||
if err := certPool.AddFile(authProvider.IDPCertificateAuthority); err != nil {
|
||||
return xerrors.Errorf("could not load the certificate of idp-certificate-authority: %w", err)
|
||||
@@ -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,18 +6,23 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/certpool"
|
||||
"github.com/int128/kubelogin/pkg/adaptors/certpool/mock_certpool"
|
||||
"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
|
||||
@@ -48,10 +53,6 @@ func TestStandalone_Do(t *testing.T) {
|
||||
AddFile("/path/to/cert2")
|
||||
mockCertPool.EXPECT().
|
||||
AddBase64Encoded("BASE64ENCODED")
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
|
||||
mockKubeconfig.EXPECT().
|
||||
GetCurrentAuthProvider("/path/to/kubeconfig", kubeconfig.ContextName("theContext"), kubeconfig.UserName("theUser")).
|
||||
@@ -81,14 +82,13 @@ func TestStandalone_Do(t *testing.T) {
|
||||
Return(&authentication.Output{
|
||||
IDToken: "YOUR_ID_TOKEN",
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
IDTokenExpiry: futureTime,
|
||||
IDTokenClaims: dummyTokenClaims,
|
||||
}, nil)
|
||||
u := Standalone{
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
Logger: mock_logger.New(t),
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
if err := u.Do(ctx, in); err != nil {
|
||||
t.Errorf("Do returned error: %+v", err)
|
||||
@@ -109,10 +109,6 @@ func TestStandalone_Do(t *testing.T) {
|
||||
IDToken: "VALID_ID_TOKEN",
|
||||
}
|
||||
mockCertPool := mock_certpool.NewMockInterface(ctrl)
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
|
||||
mockKubeconfig.EXPECT().
|
||||
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
|
||||
@@ -129,14 +125,13 @@ func TestStandalone_Do(t *testing.T) {
|
||||
Return(&authentication.Output{
|
||||
AlreadyHasValidIDToken: true,
|
||||
IDToken: "VALID_ID_TOKEN",
|
||||
IDTokenExpiry: futureTime,
|
||||
IDTokenClaims: dummyTokenClaims,
|
||||
}, nil)
|
||||
u := Standalone{
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
Logger: mock_logger.New(t),
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
if err := u.Do(ctx, in); err != nil {
|
||||
t.Errorf("Do returned error: %+v", err)
|
||||
@@ -148,17 +143,15 @@ func TestStandalone_Do(t *testing.T) {
|
||||
defer ctrl.Finish()
|
||||
ctx := context.TODO()
|
||||
in := Input{}
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
|
||||
mockKubeconfig.EXPECT().
|
||||
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
|
||||
Return(nil, xerrors.New("no oidc config"))
|
||||
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
|
||||
u := Standalone{
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
Logger: mock_logger.New(t),
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
if err := u.Do(ctx, in); err == nil {
|
||||
t.Errorf("err wants non-nil but nil")
|
||||
@@ -178,10 +171,6 @@ func TestStandalone_Do(t *testing.T) {
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}
|
||||
mockCertPool := mock_certpool.NewMockInterface(ctrl)
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
|
||||
mockKubeconfig.EXPECT().
|
||||
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
|
||||
@@ -196,10 +185,10 @@ func TestStandalone_Do(t *testing.T) {
|
||||
}).
|
||||
Return(nil, xerrors.New("authentication error"))
|
||||
u := Standalone{
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
Logger: mock_logger.New(t),
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
if err := u.Do(ctx, in); err == nil {
|
||||
t.Errorf("err wants non-nil but nil")
|
||||
@@ -219,10 +208,6 @@ func TestStandalone_Do(t *testing.T) {
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
}
|
||||
mockCertPool := mock_certpool.NewMockInterface(ctrl)
|
||||
mockCertPoolFactory := mock_certpool.NewMockFactoryInterface(ctrl)
|
||||
mockCertPoolFactory.EXPECT().
|
||||
New().
|
||||
Return(mockCertPool)
|
||||
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
|
||||
mockKubeconfig.EXPECT().
|
||||
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
|
||||
@@ -249,14 +234,13 @@ func TestStandalone_Do(t *testing.T) {
|
||||
Return(&authentication.Output{
|
||||
IDToken: "YOUR_ID_TOKEN",
|
||||
RefreshToken: "YOUR_REFRESH_TOKEN",
|
||||
IDTokenExpiry: futureTime,
|
||||
IDTokenClaims: dummyTokenClaims,
|
||||
}, nil)
|
||||
u := Standalone{
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
CertPoolFactory: mockCertPoolFactory,
|
||||
Logger: mock_logger.New(t),
|
||||
Authentication: mockAuthentication,
|
||||
Kubeconfig: mockKubeconfig,
|
||||
NewCertPool: func() certpool.Interface { return mockCertPool },
|
||||
Logger: mock_logger.New(t),
|
||||
}
|
||||
if err := u.Do(ctx, in); err == nil {
|
||||
t.Errorf("err wants non-nil but nil")
|
||||
|
||||
Reference in New Issue
Block a user