Compare commits

..

2 Commits

Author SHA1 Message Date
Hidetake Iwata
fc31d467e4 Dump all claims of ID token to debug log (#68)
* Dump all claims of ID token to debug log

* Add dump when a user already has a token
2019-04-19 10:47:32 +09:00
Hidetake Iwata
6ae8e36683 Fix error of go get and remove golint (#69)
* Remove golint

* Fix error of go get github.com/int128/ghcp
2019-04-19 10:46:07 +09:00
204 changed files with 3129 additions and 15237 deletions

38
.circleci/config.yml Normal file
View File

@@ -0,0 +1,38 @@
version: 2
jobs:
build:
docker:
- image: circleci/golang:1.12.3
steps:
- run: |
mkdir -p ~/bin
echo 'export PATH="$HOME/bin:$PATH"' >> $BASH_ENV
- run: |
curl -L -o ~/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.14.0/bin/linux/amd64/kubectl
chmod +x ~/bin/kubectl
- run: |
curl -L -o ~/bin/ghcp https://github.com/int128/ghcp/releases/download/v1.3.0/ghcp_linux_amd64
chmod +x ~/bin/ghcp
- run: |
go get -v \
github.com/int128/goxzst \
github.com/tcnksm/ghr
- checkout
# workaround for https://github.com/golang/go/issues/27925
- run: sed -e '/^k8s.io\/client-go /d' -i go.sum
- run: make check
- run: make run
- run: |
if [ "$CIRCLE_TAG" ]; then
make release
fi
workflows:
version: 2
all:
jobs:
- build:
context: open-source
filters:
tags:
only: /.*/

12
.github/FUNDING.yml vendored
View File

@@ -1,12 +0,0 @@
# These are supported funding model platforms
github: [int128] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -1,20 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
## Describe the issue
A clear and concise description of what the issue is.
## To reproduce
A console log or steps to reproduce the issue.
## Your environment
- OS: e.g. macOS
- kubelogin version: e.g. v1.19
- kubectl version: e.g. v1.19
- OpenID Connect provider: e.g. Google

View File

@@ -1,15 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
## Purpose of the feature (why)
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
A clear and concise description of what you want to happen.
## Your idea (how)
A clear and concise description of any alternative solutions or features you've considered.

View File

@@ -1,20 +0,0 @@
---
name: Question
about: Feel free to ask a question
title: ''
labels: question
assignees: ''
---
## Describe the question
A clear and concise description of what the issue is.
## To reproduce
A console log or steps to reproduce the issue.
## Your environment
- OS: e.g. macOS
- kubelogin version: e.g. v1.19
- kubectl version: e.g. v1.19
- OpenID Connect provider: e.g. Google

16
.github/release.yml vendored
View File

@@ -1,16 +0,0 @@
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
changelog:
categories:
- title: Features
labels:
- '*'
exclude:
labels:
- renovate
- refactoring
- title: Refactoring
labels:
- refactoring
- title: Dependencies
labels:
- renovate

11
.github/renovate.json vendored
View File

@@ -1,11 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>int128/renovate-base",
"github>int128/go-renovate-config#v1.9.0",
"github>int128/go-renovate-config:go-version-with-dockerfile#v1.9.0",
"github>int128/go-renovate-config:kubernetes#v1.9.0",
"github>int128/go-renovate-config:github-releases#v1.9.0(**/kustomization.yaml)",
"helpers:pinGitHubActionDigests"
]
}

View File

@@ -1,34 +0,0 @@
name: acceptance-test
on:
pull_request:
branches:
- master
paths:
- .github/workflows/acceptance-test.yaml
- acceptance_test/**
push:
branches:
- master
paths:
- .github/workflows/acceptance-test.yaml
- acceptance_test/**
jobs:
test-makefile:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: make -C acceptance_test check
- run: make -C acceptance_test
env:
OIDC_ISSUER_URL: https://accounts.google.com
OIDC_CLIENT_ID: REDACTED.apps.googleusercontent.com
YOUR_EMAIL: REDACTED@gmail.com
- run: make -C acceptance_test delete-cluster
- run: make -C acceptance_test clean

View File

@@ -1,74 +0,0 @@
name: docker
on:
pull_request:
branches:
- master
paths:
- .github/workflows/docker.yaml
- pkg/**
- go.*
- Dockerfile
push:
branches:
- master
paths:
- .github/workflows/docker.yaml
- pkg/**
- go.*
- Dockerfile
tags:
- v*
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
packages: write
outputs:
image-uri: ${{ steps.build-metadata.outputs.image-uri }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5.9.0
id: metadata
with:
images: ghcr.io/${{ github.repository }}
- uses: int128/docker-build-cache-config-action@fb186e80c08f14a2e56ed9105d4594562bff013f # v1.40.0
id: cache
with:
image: ghcr.io/${{ github.repository }}/cache
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
id: build
with:
push: ${{ github.event_name == 'push' }}
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
cache-from: ${{ steps.cache.outputs.cache-from }}
cache-to: ${{ steps.cache.outputs.cache-to }}
platforms: |
linux/amd64
linux/arm64
linux/ppc64le
- uses: int128/docker-build-metadata-action@f38781ddbaa410e9f8fa55a291dce7480798f1a0 # v1.2.0
id: build-metadata
with:
metadata: ${{ steps.build.outputs.metadata }}
test:
if: needs.build.outputs.image-uri != ''
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- run: docker run --rm "$IMAGE_URI" --help
env:
IMAGE_URI: ${{ needs.build.outputs.image-uri }}

View File

@@ -1,79 +0,0 @@
name: go
on:
push:
branches:
- master
paths:
- .github/workflows/go.yaml
- pkg/**
- integration_test/**
- mocks/**
- tools/**
- '**/go.*'
tags:
- v*
pull_request:
branches:
- master
paths:
- .github/workflows/go.yaml
- pkg/**
- integration_test/**
- mocks/**
- tools/**
- '**/go.*'
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: make test
integration-test:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
runs-on: ${{ matrix.os }}
timeout-minutes: 10
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: make integration-test
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: make lint
generate:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: go mod tidy
- run: make generate
- uses: int128/update-generated-files-action@d9aac571db84cee6c16fa20190621e9deb2bc575 # v2.67.0

View File

@@ -1,78 +0,0 @@
name: release
on:
push:
branches:
- master
paths:
- .github/workflows/release.yaml
- pkg/**
- go.*
tags:
- v*
pull_request:
branches:
- master
paths:
- .github/workflows/release.yaml
- pkg/**
- go.*
jobs:
build:
strategy:
matrix:
platform:
- runs-on: ubuntu-latest
GOOS: linux
GOARCH: amd64
CGO_ENABLED: 0 # https://github.com/int128/kubelogin/issues/567
- runs-on: ubuntu-latest
GOOS: linux
GOARCH: arm64
- runs-on: ubuntu-latest
GOOS: linux
GOARCH: arm
- runs-on: ubuntu-latest
GOOS: linux
GOARCH: ppc64le
- runs-on: macos-latest
GOOS: darwin
GOARCH: amd64
CGO_ENABLED: 1 # https://github.com/int128/kubelogin/issues/249
- runs-on: macos-latest
GOOS: darwin
GOARCH: arm64
CGO_ENABLED: 1 # https://github.com/int128/kubelogin/issues/762
- runs-on: windows-latest
GOOS: windows
GOARCH: amd64
- runs-on: windows-latest
GOOS: windows
GOARCH: arm64
runs-on: ${{ matrix.platform.runs-on }}
env:
GOOS: ${{ matrix.platform.GOOS }}
GOARCH: ${{ matrix.platform.GOARCH }}
CGO_ENABLED: ${{ matrix.platform.CGO_ENABLED }}
timeout-minutes: 10
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: go build -ldflags '-X main.version=${{ github.ref_name }}'
- uses: int128/go-release-action@2979cc5b15ceb7ae458e95b0a9467afc7ae25259 # v2.0.0
with:
binary: kubelogin
publish:
if: github.ref_type == 'tag'
needs:
- build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: rajatjindal/krew-release-bot@3d9faef30a82761d610544f62afddca00993eef9 # v0.0.47

View File

@@ -1,43 +0,0 @@
name: system-test
on:
pull_request:
branches:
- master
paths:
- .github/workflows/system-test.yaml
- system_test/**
- pkg/**
- go.*
push:
branches:
- master
paths:
- .github/workflows/system-test.yaml
- system_test/**
- pkg/**
- go.*
jobs:
system-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- run: sudo apt-get update
# Install certutil.
# https://packages.ubuntu.com/xenial/libnss3-tools
# Install keyring related packages.
# https://github.com/zalando/go-keyring/issues/45
- run: sudo apt-get install --no-install-recommends -y libnss3-tools dbus-x11 gnome-keyring
- run: echo '127.0.0.1 dex-server' | sudo tee -a /etc/hosts
- run: make -C system_test -j3
- run: make -C system_test logs
if: always()

11
.gitignore vendored
View File

@@ -1,12 +1,5 @@
/.idea
/tools/bin
/acceptance_test/output/
/coverage.out
/dist
/kubelogin
/kubectl-oidc_login
/kubelogin_*.zip
/kubelogin_*.zip.sha256
/.kubeconfig

View File

@@ -1,68 +0,0 @@
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
name: oidc-login
spec:
homepage: https://github.com/int128/kubelogin
shortDescription: Log in to the OpenID Connect provider
description: |
This is a kubectl plugin for Kubernetes OpenID Connect (OIDC) authentication.
## Credential plugin mode
kubectl executes oidc-login before calling the Kubernetes APIs.
oidc-login automatically opens the browser and you can log in to the provider.
After authentication, kubectl gets the token from oidc-login and you can access the cluster.
See https://github.com/int128/kubelogin#credential-plugin-mode for more.
## Standalone mode
Run `kubectl oidc-login`.
It automatically opens the browser and you can log in to the provider.
After authentication, it writes the token to the kubeconfig and you can access the cluster.
See https://github.com/int128/kubelogin#standalone-mode for more.
caveats: |
You need to setup the OIDC provider, Kubernetes API server, role binding and kubeconfig.
version: {{ .TagName }}
platforms:
- bin: kubelogin
{{ addURIAndSha "https://github.com/int128/kubelogin/releases/download/{{ .TagName }}/kubelogin_linux_amd64.zip" .TagName }}
selector:
matchLabels:
os: linux
arch: amd64
- bin: kubelogin
{{ addURIAndSha "https://github.com/int128/kubelogin/releases/download/{{ .TagName }}/kubelogin_linux_arm64.zip" .TagName }}
selector:
matchLabels:
os: linux
arch: arm64
- bin: kubelogin
{{ addURIAndSha "https://github.com/int128/kubelogin/releases/download/{{ .TagName }}/kubelogin_linux_arm.zip" .TagName }}
selector:
matchLabels:
os: linux
arch: arm
- bin: kubelogin
{{ addURIAndSha "https://github.com/int128/kubelogin/releases/download/{{ .TagName }}/kubelogin_darwin_amd64.zip" .TagName }}
selector:
matchLabels:
os: darwin
arch: amd64
- bin: kubelogin
{{ addURIAndSha "https://github.com/int128/kubelogin/releases/download/{{ .TagName }}/kubelogin_darwin_arm64.zip" .TagName }}
selector:
matchLabels:
os: darwin
arch: arm64
- bin: kubelogin.exe
{{ addURIAndSha "https://github.com/int128/kubelogin/releases/download/{{ .TagName }}/kubelogin_windows_amd64.zip" .TagName }}
selector:
matchLabels:
os: windows
arch: amd64
- bin: kubelogin.exe
{{ addURIAndSha "https://github.com/int128/kubelogin/releases/download/{{ .TagName }}/kubelogin_windows_arm64.zip" .TagName }}
selector:
matchLabels:
os: windows
arch: arm64

View File

@@ -1,12 +0,0 @@
dir: mocks/{{.SrcPackagePath}}_mock
pkgname: '{{.SrcPackageName}}_mock'
filename: mocks.go
template: testify
packages:
github.com/int128/kubelogin:
config:
all: true
recursive: true
io:
interfaces:
Closer: {}

View File

@@ -1,20 +0,0 @@
FROM --platform=$BUILDPLATFORM golang:1.25.3@sha256:6d4e5e74f47db00f7f24da5f53c1b4198ae46862a47395e30477365458347bf2 AS builder
WORKDIR /builder
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download
# Copy the go source
COPY main.go .
COPY pkg pkg
ARG TARGETOS
ARG TARGETARCH
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build
FROM gcr.io/distroless/base-debian12
COPY --from=builder /builder/kubelogin /
ENTRYPOINT ["/kubelogin"]

View File

@@ -1,20 +1,35 @@
.PHONY: all
all:
TARGET := kubelogin
TARGET_PLUGIN := kubectl-oidc_login
CIRCLE_TAG ?= HEAD
LDFLAGS := -X main.version=$(CIRCLE_TAG)
.PHONY: test
test:
go test -v -race ./pkg/...
.PHONY: check run release clean
.PHONY: integration-test
integration-test:
go test -v -race ./integration_test/...
all: $(TARGET)
.PHONY: generate
generate:
go tool github.com/google/wire/cmd/wire ./pkg/di
rm -fr mocks/
go tool mockery
check:
go vet
$(MAKE) -C adaptors_test/keys/testdata
go test -v -race ./...
.PHONY: lint
lint:
go tool golangci-lint run
$(TARGET): $(wildcard *.go)
go build -o $@ -ldflags "$(LDFLAGS)"
$(TARGET_PLUGIN): $(TARGET)
ln -sf $(TARGET) $@
run: $(TARGET_PLUGIN)
-PATH=.:$(PATH) kubectl oidc-login --help
dist:
VERSION=$(CIRCLE_TAG) goxzst -d dist/gh/ -o "$(TARGET)" -t "kubelogin.rb oidc-login.yaml" -- -ldflags "$(LDFLAGS)"
mv dist/gh/kubelogin.rb dist/
release: dist
ghr -u "$(CIRCLE_PROJECT_USERNAME)" -r "$(CIRCLE_PROJECT_REPONAME)" "$(CIRCLE_TAG)" dist/gh/
ghcp -u "$(CIRCLE_PROJECT_USERNAME)" -r "homebrew-$(CIRCLE_PROJECT_REPONAME)" -m "$(CIRCLE_TAG)" -C dist/ kubelogin.rb
clean:
-rm $(TARGET)
-rm $(TARGET_PLUGIN)
-rm -r dist/

198
README.md
View File

@@ -1,146 +1,140 @@
# kubelogin [![go](https://github.com/int128/kubelogin/actions/workflows/go.yaml/badge.svg)](https://github.com/int128/kubelogin/actions/workflows/go.yaml) [![Go Report Card](https://goreportcard.com/badge/github.com/int128/kubelogin)](https://goreportcard.com/report/github.com/int128/kubelogin)
# kubelogin [![CircleCI](https://circleci.com/gh/int128/kubelogin.svg?style=shield)](https://circleci.com/gh/int128/kubelogin)
This is a kubectl plugin for [Kubernetes OpenID Connect (OIDC) authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens), also known as `kubectl oidc-login`.
This is a kubectl plugin for [Kubernetes OpenID Connect (OIDC) authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
It gets a token from the OIDC provider and writes it to the kubeconfig.
Here is an example of Kubernetes authentication with the Google Identity Platform:
<img alt="screencast" src="https://user-images.githubusercontent.com/321266/85427290-86e43700-b5b6-11ea-9e97-ffefd736c9b7.gif" width="572" height="391">
Kubelogin is designed to run as a [client-go credential plugin](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins).
When you run kubectl, kubelogin opens the browser and you can log in to the provider.
Then kubelogin gets a token from the provider and kubectl access Kubernetes APIs with the token.
Take a look at the diagram:
![Diagram of the credential plugin](docs/credential-plugin-diagram.svg)
## Getting Started
### Setup
You need to setup the following components:
Install the latest release from [Homebrew](https://brew.sh/), [Krew](https://github.com/kubernetes-sigs/krew), [Chocolatey](https://chocolatey.org/packages/kubelogin) or [GitHub Releases](https://github.com/int128/kubelogin/releases).
- OIDC provider
- Kubernetes API server
- Role for your group or user
- kubectl authentication
You can install the latest release from [Homebrew](https://brew.sh/), [Krew](https://github.com/kubernetes-sigs/krew) or [GitHub Releases](https://github.com/int128/kubelogin/releases) as follows:
```sh
# Homebrew (macOS and Linux)
# Homebrew
brew tap int128/kubelogin
brew install kubelogin
# Krew (macOS, Linux, Windows and ARM)
# Krew
kubectl krew install oidc-login
# Chocolatey (Windows)
choco install kubelogin
# GitHub Releases
curl -LO https://github.com/int128/kubelogin/releases/download/v1.9.1/kubelogin_linux_amd64.zip
unzip kubelogin_linux_amd64.zip
ln -s kubelogin kubectl-oidc_login
```
If you install via GitHub releases, save the binary as the name `kubectl-oidc_login` on your path.
When you invoke `kubectl oidc-login`, kubectl finds it by the [naming convention of kubectl plugins](https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/).
The other install methods do this for you.
After initial setup or when the token has been expired, just run:
You need to set up the OIDC provider, cluster role binding, Kubernetes API server and kubeconfig.
Your kubeconfig looks like this:
```yaml
users:
- name: oidc
user:
exec:
apiVersion: client.authentication.k8s.io/v1
command: kubectl
args:
- oidc-login
- get-token
- --oidc-issuer-url=ISSUER_URL
- --oidc-client-id=YOUR_CLIENT_ID
```
% kubelogin
Using current-context: hello.k8s.local
Open http://localhost:8000 for authorization
Got a token for subject 0123456789 (valid until 2019-04-12 11:00:49 +0900 JST)
Updated ~/.kube/config
```
See the [setup guide](docs/setup.md) for more.
or run as a kubectl plugin:
### Run
```
% kubectl oidc-login
```
Run kubectl.
It opens the browser and you can log in to the provider.
After authentication, it gets an ID token and refresh token and writes them to the kubeconfig.
For more, see the following documents:
- [Getting Started with Keycloak](docs/keycloak.md)
- [Getting Started with Google Identity Platform](docs/google.md)
- [Team Operation](docs/team_ops.md)
## Configuration
This supports the following options.
```
kubelogin [OPTIONS]
Application Options:
--kubeconfig= Path to the kubeconfig file (default: ~/.kube/config) [$KUBECONFIG]
--listen-port= Port used by kubelogin to bind its webserver (default: 8000) [$KUBELOGIN_LISTEN_PORT]
--insecure-skip-tls-verify If set, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
[$KUBELOGIN_INSECURE_SKIP_TLS_VERIFY]
--skip-open-browser If set, it does not open the browser on authentication. [$KUBELOGIN_SKIP_OPEN_BROWSER]
-v, --v= If set to 1 or greater, show debug log (default: 0)
Help Options:
-h, --help Show this help message
```
This also supports the following keys of `auth-provider` in kubeconfig.
See [kubectl authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#using-kubectl).
Key | Direction | Value
----|-----------|------
`idp-issuer-url` | Read (Mandatory) | Issuer URL of the provider.
`client-id` | Read (Mandatory) | Client ID of the provider.
`client-secret` | Read (Mandatory) | Client Secret of the provider.
`idp-certificate-authority` | Read | CA certificate path of the provider.
`idp-certificate-authority-data` | Read | Base64 encoded CA certificate of the provider.
`extra-scopes` | Read | Scopes to request to the provider (comma separated).
`id-token` | Write | ID token got from the provider.
`refresh-token` | Write | Refresh token got from the provider.
### Kubeconfig path
You can set the environment variable `KUBECONFIG` to point the config file.
Default to `~/.kube/config`.
```sh
kubectl get pods
export KUBECONFIG="$PWD/.kubeconfig"
```
Kubectl executes kubelogin before calling the Kubernetes APIs.
Kubelogin automatically opens the browser, and you can log in to the provider.
After the authentication, kubelogin returns the credentials to kubectl.
Kubectl then calls the Kubernetes APIs with the credentials.
### Extra scopes
```console
% kubectl get pods
Open http://localhost:8000 for authentication
NAME READY STATUS RESTARTS AGE
echoserver-86c78fdccd-nzmd5 1/1 Running 0 26d
You can set extra scopes to request to the provider by `extra-scopes` in the kubeconfig.
```sh
kubectl config set-credentials keycloak --auth-provider-arg extra-scopes=email
```
Kubelogin stores the ID token and refresh token to the cache.
If the ID token is valid, it just returns it.
If the ID token has expired, it will refresh the token using the refresh token.
If the refresh token has expired, it will perform re-authentication.
Note that kubectl does not accept multiple scopes and you need to edit the kubeconfig as like:
## Troubleshooting
### Token cache
Kubelogin stores the token cache to the file system by default.
For enhanced security, it is recommended to store it to the keyring.
See the [token cache](docs/usage.md#token-cache) for details.
You can log out by deleting the token cache.
```console
% kubectl oidc-login clean
Deleted the token cache at /home/user/.kube/cache/oidc-login
Deleted the token cache from the keyring
```sh
kubectl config set-credentials keycloak --auth-provider-arg extra-scopes=SCOPES
sed -i '' -e s/SCOPES/email,profile/ $KUBECONFIG
```
Kubelogin will ask you to log in via the browser again.
If the browser has a cookie for the provider, you need to log out from the provider or clear the cookie.
### ID token claims
### CA Certificates
You can run `setup` command to dump the claims of an ID token from the provider.
You can set your self-signed certificates for the OIDC provider (not Kubernetes API server) by `idp-certificate-authority` and `idp-certificate-authority-data` in the kubeconfig.
```console
% kubectl oidc-login setup --oidc-issuer-url=ISSUER_URL --oidc-client-id=REDACTED
...
You got a token with the following claims:
{
"sub": "********",
"iss": "https://accounts.google.com",
"aud": "********",
...
}
```sh
kubectl config set-credentials keycloak \
--auth-provider-arg idp-certificate-authority=$HOME/.kube/keycloak-ca.pem
```
You can set `-v1` option to increase the log level.
If kubelogin could not parse the certificate, it shows a warning and skips it.
```yaml
users:
- name: oidc
user:
exec:
apiVersion: client.authentication.k8s.io/v1
command: kubectl
args:
- oidc-login
- get-token
- -v1
```
You can run the [acceptance test](acceptance_test) to verify if kubelogin works with your provider.
### HTTP Proxy
## Docs
You can set the following environment variables if you are behind a proxy: `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY`.
See also [net/http#ProxyFromEnvironment](https://golang.org/pkg/net/http/#ProxyFromEnvironment).
- [Setup guide](docs/setup.md)
- [Usage and options](docs/usage.md)
- [Standalone mode](docs/standalone-mode.md)
- [System test](system_test)
- [Acceptance_test for identity providers](acceptance_test)
## Contributions
This is an open source software licensed under Apache License 2.0.
Feel free to open issues and pull requests for improving code and documents.

View File

@@ -1,47 +0,0 @@
CLUSTER_NAME := kubelogin-acceptance-test
OUTPUT_DIR := $(CURDIR)/output
KUBECONFIG := $(OUTPUT_DIR)/kubeconfig.yaml
export KUBECONFIG
.PHONY: cluster
cluster:
# Create a cluster.
mkdir -p $(OUTPUT_DIR)
sed -e "s|OIDC_ISSUER_URL|$(OIDC_ISSUER_URL)|" -e "s|OIDC_CLIENT_ID|$(OIDC_CLIENT_ID)|" cluster.yaml > $(OUTPUT_DIR)/cluster.yaml
kind create cluster --name $(CLUSTER_NAME) --config $(OUTPUT_DIR)/cluster.yaml
# Set up the access control.
kubectl create clusterrole cluster-readonly --verb=get,watch,list --resource='*.*'
kubectl create clusterrolebinding cluster-readonly --clusterrole=cluster-readonly --user=$(YOUR_EMAIL)
# Set up kubectl.
kubectl config set-credentials oidc \
--exec-api-version=client.authentication.k8s.io/v1 \
--exec-interactive-mode=Never \
--exec-command=$(CURDIR)/../kubelogin \
--exec-arg=get-token \
--exec-arg=--token-cache-dir=$(OUTPUT_DIR)/token-cache \
--exec-arg=--oidc-issuer-url=$(OIDC_ISSUER_URL) \
--exec-arg=--oidc-client-id=$(OIDC_CLIENT_ID) \
--exec-arg=--oidc-extra-scope=email
# Switch the default user.
kubectl config set-context --current --user=oidc
# Show the kubeconfig.
kubectl config view
.PHONY: clean
clean:
-rm -r $(OUTPUT_DIR)
.PHONY: delete-cluster
delete-cluster:
kind delete cluster --name $(CLUSTER_NAME)
.PHONY: check
check:
docker version
kind version
kubectl version --client

View File

@@ -1,72 +0,0 @@
# kubelogin/acceptance_test
This is a manual test to verify if the Kubernetes OIDC authentication works with your OIDC provider.
## Purpose
This test checks the following points:
1. You can set up your OIDC provider using the [setup guide](../docs/setup.md).
1. The plugin works with your OIDC provider.
## Getting Started
### Prerequisite
You need to build the plugin into the parent directory.
```sh
make -C ..
```
You need to set up your provider.
See the [setup guide](../docs/setup.md) for more.
You need to install the following tools:
- Docker
- Kind
- kubectl
You can check if the tools are available.
```sh
make check
```
### 1. Create a cluster
Create a cluster.
For example, you can create a cluster with Google account authentication.
```sh
make OIDC_ISSUER_URL=https://accounts.google.com \
OIDC_CLIENT_ID=REDACTED.apps.googleusercontent.com \
YOUR_EMAIL=REDACTED@gmail.com
```
It will do the following steps:
1. Create a cluster.
1. Set up access control. It allows read-only access from your email address.
1. Set up kubectl to enable the plugin.
You can change kubectl configuration in generated `output/kubeconfig.yaml`.
### 2. Run kubectl
Make sure you can log in to the provider and access the cluster.
```console
% export KUBECONFIG=$PWD/output/kubeconfig.yaml
% kubectl get pods -A
```
### Clean up
To delete the cluster and generated files:
```sh
make delete-cluster
make clean
```

View File

@@ -1,13 +0,0 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
kubeadmConfigPatches:
- |
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
metadata:
name: config
apiServer:
extraArgs:
oidc-issuer-url: OIDC_ISSUER_URL
oidc-client-id: OIDC_CLIENT_ID
oidc-username-claim: email

75
adaptors/cmd.go Normal file
View File

@@ -0,0 +1,75 @@
package adaptors
import (
"context"
"fmt"
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/usecases/interfaces"
"github.com/jessevdk/go-flags"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"go.uber.org/dig"
)
func NewCmd(i Cmd) adaptors.Cmd {
return &i
}
type Cmd struct {
dig.In
Login usecases.Login
Logger adaptors.Logger
}
func (cmd *Cmd) Run(ctx context.Context, args []string, version string) int {
var o cmdOptions
parser := flags.NewParser(&o, flags.HelpFlag)
parser.LongDescription = fmt.Sprintf(`Version %s
This updates the kubeconfig for Kubernetes OpenID Connect (OIDC) authentication.`,
version)
args, err := parser.ParseArgs(args[1:])
if err != nil {
cmd.Logger.Printf("Error: %s", err)
return 1
}
if len(args) > 0 {
cmd.Logger.Printf("Error: too many arguments")
return 1
}
cmd.Logger.SetLevel(adaptors.LogLevel(o.Verbose))
kubeConfig, err := o.ExpandKubeConfig()
if err != nil {
cmd.Logger.Printf("Error: invalid option: %s", err)
return 1
}
in := usecases.LoginIn{
KubeConfig: kubeConfig,
ListenPort: o.ListenPort,
SkipTLSVerify: o.SkipTLSVerify,
SkipOpenBrowser: o.SkipOpenBrowser,
}
if err := cmd.Login.Do(ctx, in); err != nil {
cmd.Logger.Printf("Error: %s", err)
return 1
}
return 0
}
type cmdOptions struct {
KubeConfig string `long:"kubeconfig" default:"~/.kube/config" env:"KUBECONFIG" description:"Path to the kubeconfig file"`
ListenPort int `long:"listen-port" default:"8000" env:"KUBELOGIN_LISTEN_PORT" description:"Port used by kubelogin to bind its webserver"`
SkipTLSVerify bool `long:"insecure-skip-tls-verify" env:"KUBELOGIN_INSECURE_SKIP_TLS_VERIFY" description:"If set, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"`
SkipOpenBrowser bool `long:"skip-open-browser" env:"KUBELOGIN_SKIP_OPEN_BROWSER" description:"If set, it does not open the browser on authentication."`
Verbose int `long:"v" short:"v" default:"0" description:"If set to 1 or greater, show debug log"`
}
// ExpandKubeConfig returns an expanded KubeConfig path.
func (c *cmdOptions) ExpandKubeConfig() (string, error) {
d, err := homedir.Expand(c.KubeConfig)
if err != nil {
return "", errors.Wrapf(err, "could not expand %s", c.KubeConfig)
}
return d, nil
}

98
adaptors/cmd_test.go Normal file
View File

@@ -0,0 +1,98 @@
package adaptors
import (
"context"
"testing"
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/adaptors/mock_adaptors"
"github.com/int128/kubelogin/usecases/interfaces"
"github.com/int128/kubelogin/usecases/mock_usecases"
"github.com/mitchellh/go-homedir"
)
func TestCmd_Run(t *testing.T) {
const executable = "kubelogin"
const version = "HEAD"
t.Run("Defaults", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
login := mock_usecases.NewMockLogin(ctrl)
login.EXPECT().
Do(ctx, usecases.LoginIn{
KubeConfig: expand(t, "~/.kube/config"),
ListenPort: 8000,
})
logger := mock_adaptors.NewLogger(t, ctrl)
logger.EXPECT().
SetLevel(adaptors.LogLevel(0))
cmd := Cmd{
Login: login,
Logger: logger,
}
exitCode := cmd.Run(ctx, []string{executable}, version)
if exitCode != 0 {
t.Errorf("exitCode wants 0 but %d", exitCode)
}
})
t.Run("FullOptions", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
login := mock_usecases.NewMockLogin(ctrl)
login.EXPECT().
Do(ctx, usecases.LoginIn{
KubeConfig: expand(t, "~/.kube/config"),
ListenPort: 10080,
SkipTLSVerify: true,
SkipOpenBrowser: true,
})
logger := mock_adaptors.NewLogger(t, ctrl)
logger.EXPECT().
SetLevel(adaptors.LogLevel(1))
cmd := Cmd{
Login: login,
Logger: logger,
}
exitCode := cmd.Run(ctx, []string{executable,
"--listen-port", "10080",
"--insecure-skip-tls-verify",
"--skip-open-browser",
"-v1",
}, version)
if exitCode != 0 {
t.Errorf("exitCode wants 0 but %d", exitCode)
}
})
t.Run("TooManyArgs", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cmd := Cmd{
Login: mock_usecases.NewMockLogin(ctrl),
Logger: mock_adaptors.NewLogger(t, ctrl),
}
exitCode := cmd.Run(context.TODO(), []string{executable, "some"}, version)
if exitCode != 1 {
t.Errorf("exitCode wants 1 but %d", exitCode)
}
})
}
func expand(t *testing.T, path string) string {
d, err := homedir.Expand(path)
if err != nil {
t.Fatalf("could not expand: %s", err)
}
return d
}

82
adaptors/http.go Normal file
View File

@@ -0,0 +1,82 @@
package adaptors
import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"io/ioutil"
"net/http"
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/infrastructure"
"github.com/pkg/errors"
"go.uber.org/dig"
)
func NewHTTP(i HTTP) adaptors.HTTP {
return &i
}
type HTTP struct {
dig.In
Logger adaptors.Logger
}
func (*HTTP) NewClientConfig() adaptors.HTTPClientConfig {
return &httpClientConfig{
certPool: x509.NewCertPool(),
}
}
func (h *HTTP) NewClient(config adaptors.HTTPClientConfig) (*http.Client, error) {
return &http.Client{
Transport: &infrastructure.LoggingTransport{
Base: &http.Transport{
TLSClientConfig: config.TLSConfig(),
Proxy: http.ProxyFromEnvironment,
},
Logger: h.Logger,
},
}, nil
}
type httpClientConfig struct {
certPool *x509.CertPool
skipTLSVerify bool
}
func (c *httpClientConfig) AddCertificateFromFile(filename string) error {
b, err := ioutil.ReadFile(filename)
if err != nil {
return errors.Wrapf(err, "could not read %s", filename)
}
if c.certPool.AppendCertsFromPEM(b) != true {
return errors.Errorf("could not append certificate from %s", filename)
}
return nil
}
func (c *httpClientConfig) AddEncodedCertificate(base64String string) error {
b, err := base64.StdEncoding.DecodeString(base64String)
if err != nil {
return errors.Wrapf(err, "could not decode base64")
}
if c.certPool.AppendCertsFromPEM(b) != true {
return errors.Errorf("could not append certificate")
}
return nil
}
func (c *httpClientConfig) TLSConfig() *tls.Config {
tlsConfig := &tls.Config{
InsecureSkipVerify: c.skipTLSVerify,
}
if len(c.certPool.Subjects()) > 0 {
tlsConfig.RootCAs = c.certPool
}
return tlsConfig
}
func (c *httpClientConfig) SetSkipTLSVerify(b bool) {
c.skipTLSVerify = b
}

View File

@@ -0,0 +1,82 @@
package adaptors
import (
"context"
"crypto/tls"
"net/http"
"github.com/coreos/go-oidc"
"k8s.io/client-go/tools/clientcmd/api"
)
//go:generate mockgen -package mock_adaptors -destination ../mock_adaptors/mock_adaptors.go github.com/int128/kubelogin/adaptors/interfaces KubeConfig,HTTP,HTTPClientConfig,OIDC,Logger
type Cmd interface {
Run(ctx context.Context, args []string, version string) int
}
type KubeConfig interface {
LoadFromFile(filename string) (*api.Config, error)
WriteToFile(config *api.Config, filename string) error
}
type HTTP interface {
NewClientConfig() HTTPClientConfig
NewClient(config HTTPClientConfig) (*http.Client, error)
}
type HTTPClientConfig interface {
AddCertificateFromFile(filename string) error
AddEncodedCertificate(base64String string) error
SetSkipTLSVerify(b bool)
TLSConfig() *tls.Config
}
type OIDC interface {
Authenticate(ctx context.Context, in OIDCAuthenticateIn, cb OIDCAuthenticateCallback) (*OIDCAuthenticateOut, error)
VerifyIDToken(ctx context.Context, in OIDCVerifyTokenIn) (*oidc.IDToken, error)
}
type OIDCAuthenticateIn struct {
Issuer string
ClientID string
ClientSecret string
ExtraScopes []string // Additional scopes
Client *http.Client // HTTP client for oidc and oauth2
LocalServerPort int // HTTP server port
SkipOpenBrowser bool // skip opening browser if true
}
type OIDCAuthenticateCallback struct {
ShowLocalServerURL func(url string)
}
type OIDCAuthenticateOut struct {
VerifiedIDToken *oidc.IDToken
IDToken string
RefreshToken string
}
type OIDCVerifyTokenIn struct {
IDToken string
Issuer string
ClientID string
Client *http.Client
}
type Logger interface {
Printf(format string, v ...interface{})
Debugf(level LogLevel, format string, v ...interface{})
SetLevel(level LogLevel)
IsEnabled(level LogLevel) bool
}
// LogLevel represents a log level for debug.
//
// 0 = None
// 1 = Including in/out
// 2 = Including transport headers
// 3 = Including transport body
//
type LogLevel int

30
adaptors/kubeconfig.go Normal file
View File

@@ -0,0 +1,30 @@
package adaptors
import (
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/pkg/errors"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
)
func NewKubeConfig() adaptors.KubeConfig {
return &KubeConfig{}
}
type KubeConfig struct{}
func (*KubeConfig) LoadFromFile(filename string) (*api.Config, error) {
config, err := clientcmd.LoadFromFile(filename)
if err != nil {
return nil, errors.Wrapf(err, "could not read the kubeconfig from %s", filename)
}
return config, err
}
func (*KubeConfig) WriteToFile(config *api.Config, filename string) error {
err := clientcmd.WriteToFile(*config, filename)
if err != nil {
return errors.Wrapf(err, "could not write the kubeconfig to %s", filename)
}
return err
}

49
adaptors/logger.go Normal file
View File

@@ -0,0 +1,49 @@
package adaptors
import (
"log"
"os"
"github.com/int128/kubelogin/adaptors/interfaces"
)
// NewLogger returns a Logger with the standard log.Logger for messages and debug.
func NewLogger() adaptors.Logger {
return &Logger{
stdLogger: log.New(os.Stderr, "", 0),
debugLogger: log.New(os.Stderr, "", log.Ltime|log.Lmicroseconds),
}
}
// NewLoggerWith returns a Logger with the given standard log.Logger.
func NewLoggerWith(l stdLogger) *Logger {
return &Logger{
stdLogger: l,
debugLogger: l,
}
}
type stdLogger interface {
Printf(format string, v ...interface{})
}
// Logger wraps the standard log.Logger and just provides debug level.
type Logger struct {
stdLogger
debugLogger stdLogger
level adaptors.LogLevel
}
func (l *Logger) Debugf(level adaptors.LogLevel, format string, v ...interface{}) {
if l.IsEnabled(level) {
l.debugLogger.Printf(format, v...)
}
}
func (l *Logger) SetLevel(level adaptors.LogLevel) {
l.level = level
}
func (l *Logger) IsEnabled(level adaptors.LogLevel) bool {
return level <= l.level
}

53
adaptors/logger_test.go Normal file
View File

@@ -0,0 +1,53 @@
package adaptors
import (
"fmt"
"testing"
"github.com/int128/kubelogin/adaptors/interfaces"
)
type mockStdLogger struct {
count int
}
func (l *mockStdLogger) Printf(format string, v ...interface{}) {
l.count++
}
func TestLogger_Debugf(t *testing.T) {
for _, c := range []struct {
loggerLevel adaptors.LogLevel
debugfLevel adaptors.LogLevel
count int
}{
{0, 0, 1},
{0, 1, 0},
{1, 0, 1},
{1, 1, 1},
{1, 2, 0},
{2, 1, 1},
{2, 2, 1},
{2, 3, 0},
} {
t.Run(fmt.Sprintf("%+v", c), func(t *testing.T) {
m := &mockStdLogger{}
l := &Logger{debugLogger: m, level: c.loggerLevel}
l.Debugf(c.debugfLevel, "hello")
if m.count != c.count {
t.Errorf("count wants %d but %d", c.count, m.count)
}
})
}
}
func TestLogger_Printf(t *testing.T) {
m := &mockStdLogger{}
l := &Logger{stdLogger: m}
l.Printf("hello")
if m.count != 1 {
t.Errorf("count wants %d but %d", 1, m.count)
}
}

View File

@@ -0,0 +1,31 @@
package mock_adaptors
import (
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/adaptors/interfaces"
)
func NewLogger(t testingLogger, ctrl *gomock.Controller) *Logger {
return &Logger{
MockLogger: NewMockLogger(ctrl),
testingLogger: t,
}
}
type testingLogger interface {
Logf(format string, v ...interface{})
}
// Logger provides mock feature but overrides output methods with actual logging.
type Logger struct {
*MockLogger
testingLogger testingLogger
}
func (l *Logger) Printf(format string, v ...interface{}) {
l.testingLogger.Logf(format, v...)
}
func (l *Logger) Debugf(level adaptors.LogLevel, format string, v ...interface{}) {
l.testingLogger.Logf(format, v...)
}

View File

@@ -0,0 +1,305 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/adaptors/interfaces (interfaces: KubeConfig,HTTP,HTTPClientConfig,OIDC,Logger)
// Package mock_adaptors is a generated GoMock package.
package mock_adaptors
import (
context "context"
tls "crypto/tls"
go_oidc "github.com/coreos/go-oidc"
gomock "github.com/golang/mock/gomock"
interfaces "github.com/int128/kubelogin/adaptors/interfaces"
api "k8s.io/client-go/tools/clientcmd/api"
http "net/http"
reflect "reflect"
)
// MockKubeConfig is a mock of KubeConfig interface
type MockKubeConfig struct {
ctrl *gomock.Controller
recorder *MockKubeConfigMockRecorder
}
// MockKubeConfigMockRecorder is the mock recorder for MockKubeConfig
type MockKubeConfigMockRecorder struct {
mock *MockKubeConfig
}
// NewMockKubeConfig creates a new mock instance
func NewMockKubeConfig(ctrl *gomock.Controller) *MockKubeConfig {
mock := &MockKubeConfig{ctrl: ctrl}
mock.recorder = &MockKubeConfigMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockKubeConfig) EXPECT() *MockKubeConfigMockRecorder {
return m.recorder
}
// LoadFromFile mocks base method
func (m *MockKubeConfig) LoadFromFile(arg0 string) (*api.Config, error) {
ret := m.ctrl.Call(m, "LoadFromFile", arg0)
ret0, _ := ret[0].(*api.Config)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LoadFromFile indicates an expected call of LoadFromFile
func (mr *MockKubeConfigMockRecorder) LoadFromFile(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadFromFile", reflect.TypeOf((*MockKubeConfig)(nil).LoadFromFile), arg0)
}
// WriteToFile mocks base method
func (m *MockKubeConfig) WriteToFile(arg0 *api.Config, arg1 string) error {
ret := m.ctrl.Call(m, "WriteToFile", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// WriteToFile indicates an expected call of WriteToFile
func (mr *MockKubeConfigMockRecorder) WriteToFile(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteToFile", reflect.TypeOf((*MockKubeConfig)(nil).WriteToFile), arg0, arg1)
}
// MockHTTP is a mock of HTTP interface
type MockHTTP struct {
ctrl *gomock.Controller
recorder *MockHTTPMockRecorder
}
// MockHTTPMockRecorder is the mock recorder for MockHTTP
type MockHTTPMockRecorder struct {
mock *MockHTTP
}
// NewMockHTTP creates a new mock instance
func NewMockHTTP(ctrl *gomock.Controller) *MockHTTP {
mock := &MockHTTP{ctrl: ctrl}
mock.recorder = &MockHTTPMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockHTTP) EXPECT() *MockHTTPMockRecorder {
return m.recorder
}
// NewClient mocks base method
func (m *MockHTTP) NewClient(arg0 interfaces.HTTPClientConfig) (*http.Client, error) {
ret := m.ctrl.Call(m, "NewClient", arg0)
ret0, _ := ret[0].(*http.Client)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// NewClient indicates an expected call of NewClient
func (mr *MockHTTPMockRecorder) NewClient(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewClient", reflect.TypeOf((*MockHTTP)(nil).NewClient), arg0)
}
// NewClientConfig mocks base method
func (m *MockHTTP) NewClientConfig() interfaces.HTTPClientConfig {
ret := m.ctrl.Call(m, "NewClientConfig")
ret0, _ := ret[0].(interfaces.HTTPClientConfig)
return ret0
}
// NewClientConfig indicates an expected call of NewClientConfig
func (mr *MockHTTPMockRecorder) NewClientConfig() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewClientConfig", reflect.TypeOf((*MockHTTP)(nil).NewClientConfig))
}
// MockHTTPClientConfig is a mock of HTTPClientConfig interface
type MockHTTPClientConfig struct {
ctrl *gomock.Controller
recorder *MockHTTPClientConfigMockRecorder
}
// MockHTTPClientConfigMockRecorder is the mock recorder for MockHTTPClientConfig
type MockHTTPClientConfigMockRecorder struct {
mock *MockHTTPClientConfig
}
// NewMockHTTPClientConfig creates a new mock instance
func NewMockHTTPClientConfig(ctrl *gomock.Controller) *MockHTTPClientConfig {
mock := &MockHTTPClientConfig{ctrl: ctrl}
mock.recorder = &MockHTTPClientConfigMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockHTTPClientConfig) EXPECT() *MockHTTPClientConfigMockRecorder {
return m.recorder
}
// AddCertificateFromFile mocks base method
func (m *MockHTTPClientConfig) AddCertificateFromFile(arg0 string) error {
ret := m.ctrl.Call(m, "AddCertificateFromFile", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// AddCertificateFromFile indicates an expected call of AddCertificateFromFile
func (mr *MockHTTPClientConfigMockRecorder) AddCertificateFromFile(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddCertificateFromFile", reflect.TypeOf((*MockHTTPClientConfig)(nil).AddCertificateFromFile), arg0)
}
// AddEncodedCertificate mocks base method
func (m *MockHTTPClientConfig) AddEncodedCertificate(arg0 string) error {
ret := m.ctrl.Call(m, "AddEncodedCertificate", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// AddEncodedCertificate indicates an expected call of AddEncodedCertificate
func (mr *MockHTTPClientConfigMockRecorder) AddEncodedCertificate(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddEncodedCertificate", reflect.TypeOf((*MockHTTPClientConfig)(nil).AddEncodedCertificate), arg0)
}
// SetSkipTLSVerify mocks base method
func (m *MockHTTPClientConfig) SetSkipTLSVerify(arg0 bool) {
m.ctrl.Call(m, "SetSkipTLSVerify", arg0)
}
// SetSkipTLSVerify indicates an expected call of SetSkipTLSVerify
func (mr *MockHTTPClientConfigMockRecorder) SetSkipTLSVerify(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSkipTLSVerify", reflect.TypeOf((*MockHTTPClientConfig)(nil).SetSkipTLSVerify), arg0)
}
// TLSConfig mocks base method
func (m *MockHTTPClientConfig) TLSConfig() *tls.Config {
ret := m.ctrl.Call(m, "TLSConfig")
ret0, _ := ret[0].(*tls.Config)
return ret0
}
// TLSConfig indicates an expected call of TLSConfig
func (mr *MockHTTPClientConfigMockRecorder) TLSConfig() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TLSConfig", reflect.TypeOf((*MockHTTPClientConfig)(nil).TLSConfig))
}
// MockOIDC is a mock of OIDC interface
type MockOIDC struct {
ctrl *gomock.Controller
recorder *MockOIDCMockRecorder
}
// MockOIDCMockRecorder is the mock recorder for MockOIDC
type MockOIDCMockRecorder struct {
mock *MockOIDC
}
// NewMockOIDC creates a new mock instance
func NewMockOIDC(ctrl *gomock.Controller) *MockOIDC {
mock := &MockOIDC{ctrl: ctrl}
mock.recorder = &MockOIDCMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockOIDC) EXPECT() *MockOIDCMockRecorder {
return m.recorder
}
// Authenticate mocks base method
func (m *MockOIDC) Authenticate(arg0 context.Context, arg1 interfaces.OIDCAuthenticateIn, arg2 interfaces.OIDCAuthenticateCallback) (*interfaces.OIDCAuthenticateOut, error) {
ret := m.ctrl.Call(m, "Authenticate", arg0, arg1, arg2)
ret0, _ := ret[0].(*interfaces.OIDCAuthenticateOut)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Authenticate indicates an expected call of Authenticate
func (mr *MockOIDCMockRecorder) Authenticate(arg0, arg1, arg2 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authenticate", reflect.TypeOf((*MockOIDC)(nil).Authenticate), arg0, arg1, arg2)
}
// VerifyIDToken mocks base method
func (m *MockOIDC) VerifyIDToken(arg0 context.Context, arg1 interfaces.OIDCVerifyTokenIn) (*go_oidc.IDToken, error) {
ret := m.ctrl.Call(m, "VerifyIDToken", arg0, arg1)
ret0, _ := ret[0].(*go_oidc.IDToken)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// VerifyIDToken indicates an expected call of VerifyIDToken
func (mr *MockOIDCMockRecorder) VerifyIDToken(arg0, arg1 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyIDToken", reflect.TypeOf((*MockOIDC)(nil).VerifyIDToken), arg0, arg1)
}
// MockLogger is a mock of Logger interface
type MockLogger struct {
ctrl *gomock.Controller
recorder *MockLoggerMockRecorder
}
// MockLoggerMockRecorder is the mock recorder for MockLogger
type MockLoggerMockRecorder struct {
mock *MockLogger
}
// NewMockLogger creates a new mock instance
func NewMockLogger(ctrl *gomock.Controller) *MockLogger {
mock := &MockLogger{ctrl: ctrl}
mock.recorder = &MockLoggerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockLogger) EXPECT() *MockLoggerMockRecorder {
return m.recorder
}
// Debugf mocks base method
func (m *MockLogger) Debugf(arg0 interfaces.LogLevel, arg1 string, arg2 ...interface{}) {
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
m.ctrl.Call(m, "Debugf", varargs...)
}
// Debugf indicates an expected call of Debugf
func (mr *MockLoggerMockRecorder) Debugf(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...)
}
// IsEnabled mocks base method
func (m *MockLogger) IsEnabled(arg0 interfaces.LogLevel) bool {
ret := m.ctrl.Call(m, "IsEnabled", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// IsEnabled indicates an expected call of IsEnabled
func (mr *MockLoggerMockRecorder) IsEnabled(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsEnabled", reflect.TypeOf((*MockLogger)(nil).IsEnabled), arg0)
}
// Printf mocks base method
func (m *MockLogger) Printf(arg0 string, arg1 ...interface{}) {
varargs := []interface{}{arg0}
for _, a := range arg1 {
varargs = append(varargs, a)
}
m.ctrl.Call(m, "Printf", varargs...)
}
// Printf indicates an expected call of Printf
func (mr *MockLoggerMockRecorder) Printf(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0}, arg1...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Printf", reflect.TypeOf((*MockLogger)(nil).Printf), varargs...)
}
// SetLevel mocks base method
func (m *MockLogger) SetLevel(arg0 interfaces.LogLevel) {
m.ctrl.Call(m, "SetLevel", arg0)
}
// SetLevel indicates an expected call of SetLevel
func (mr *MockLoggerMockRecorder) SetLevel(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLevel", reflect.TypeOf((*MockLogger)(nil).SetLevel), arg0)
}

73
adaptors/oidc.go Normal file
View File

@@ -0,0 +1,73 @@
package adaptors
import (
"context"
"github.com/coreos/go-oidc"
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/oauth2cli"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)
func NewOIDC() adaptors.OIDC {
return &OIDC{}
}
type OIDC struct{}
func (*OIDC) Authenticate(ctx context.Context, in adaptors.OIDCAuthenticateIn, cb adaptors.OIDCAuthenticateCallback) (*adaptors.OIDCAuthenticateOut, error) {
if in.Client != nil {
ctx = context.WithValue(ctx, oauth2.HTTPClient, in.Client)
}
provider, err := oidc.NewProvider(ctx, in.Issuer)
if err != nil {
return nil, errors.Wrapf(err, "could not discovery the OIDC issuer")
}
flow := oauth2cli.AuthCodeFlow{
Config: oauth2.Config{
Endpoint: provider.Endpoint(),
ClientID: in.ClientID,
ClientSecret: in.ClientSecret,
Scopes: append(in.ExtraScopes, oidc.ScopeOpenID),
},
LocalServerPort: in.LocalServerPort,
SkipOpenBrowser: in.SkipOpenBrowser,
AuthCodeOptions: []oauth2.AuthCodeOption{oauth2.AccessTypeOffline},
ShowLocalServerURL: cb.ShowLocalServerURL,
}
token, err := flow.GetToken(ctx)
if err != nil {
return nil, errors.Wrapf(err, "could not get a token")
}
idToken, ok := token.Extra("id_token").(string)
if !ok {
return nil, errors.Errorf("id_token is missing in the token response: %s", token)
}
verifier := provider.Verifier(&oidc.Config{ClientID: in.ClientID})
verifiedIDToken, err := verifier.Verify(ctx, idToken)
if err != nil {
return nil, errors.Wrapf(err, "could not verify the id_token")
}
return &adaptors.OIDCAuthenticateOut{
VerifiedIDToken: verifiedIDToken,
IDToken: idToken,
RefreshToken: token.RefreshToken,
}, nil
}
func (*OIDC) VerifyIDToken(ctx context.Context, in adaptors.OIDCVerifyTokenIn) (*oidc.IDToken, error) {
if in.Client != nil {
ctx = context.WithValue(ctx, oauth2.HTTPClient, in.Client)
}
provider, err := oidc.NewProvider(ctx, in.Issuer)
if err != nil {
return nil, errors.Wrapf(err, "could not discovery the OIDC issuer")
}
verifier := provider.Verifier(&oidc.Config{ClientID: in.ClientID})
verifiedIDToken, err := verifier.Verify(ctx, in.IDToken)
if err != nil {
return nil, errors.Wrapf(err, "could not verify the id_token")
}
return verifiedIDToken, nil
}

View File

@@ -0,0 +1,38 @@
package authserver
import (
"crypto/rsa"
"net/http"
"testing"
)
// Config represents server configuration.
type Config struct {
Issuer string
Scope string
TLSServerCert string
TLSServerKey string
IDToken string
RefreshToken string
IDTokenKeyPair *rsa.PrivateKey
}
// Start starts a HTTP server.
func Start(t *testing.T, c Config) *http.Server {
s := &http.Server{
Addr: "localhost:9000",
Handler: newHandler(t, c),
}
go func() {
var err error
if c.TLSServerCert != "" && c.TLSServerKey != "" {
err = s.ListenAndServeTLS(c.TLSServerCert, c.TLSServerKey)
} else {
err = s.ListenAndServe()
}
if err != nil && err != http.ErrServerClosed {
t.Error(err)
}
}()
return s
}

View File

@@ -0,0 +1,108 @@
package authserver
import (
"encoding/base64"
"fmt"
"math/big"
"net/http"
"testing"
"text/template"
"github.com/pkg/errors"
)
type handler struct {
t *testing.T
discovery *template.Template
token *template.Template
jwks *template.Template
authCode string
// Template values
Issuer string
Scope string // Default to openid
IDToken string
RefreshToken string
PrivateKey struct{ N, E string }
}
func newHandler(t *testing.T, c Config) *handler {
tpl, err := template.ParseFiles(
"authserver/testdata/oidc-discovery.json",
"authserver/testdata/oidc-token.json",
"authserver/testdata/oidc-jwks.json",
)
if err != nil {
t.Fatalf("could not read the templates: %s", err)
}
h := handler{
t: t,
discovery: tpl.Lookup("oidc-discovery.json"),
token: tpl.Lookup("oidc-token.json"),
jwks: tpl.Lookup("oidc-jwks.json"),
authCode: "3d24a8bd-35e6-457d-999e-e04bb1dfcec7",
Issuer: c.Issuer,
Scope: c.Scope,
IDToken: c.IDToken,
RefreshToken: c.RefreshToken,
}
if h.Scope == "" {
h.Scope = "openid"
}
if c.IDTokenKeyPair != nil {
h.PrivateKey.E = base64.RawURLEncoding.EncodeToString(big.NewInt(int64(c.IDTokenKeyPair.E)).Bytes())
h.PrivateKey.N = base64.RawURLEncoding.EncodeToString(c.IDTokenKeyPair.N.Bytes())
}
return &h
}
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := h.serveHTTP(w, r); err != nil {
h.t.Logf("[auth-server] Error: %s", err)
w.WriteHeader(500)
}
}
func (h *handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
m := r.Method
p := r.URL.Path
h.t.Logf("[auth-server] %s %s", m, r.RequestURI)
switch {
case m == "GET" && p == "/.well-known/openid-configuration":
w.Header().Add("Content-Type", "application/json")
if err := h.discovery.Execute(w, h); err != nil {
return errors.Wrapf(err, "could not execute the template")
}
case m == "GET" && p == "/protocol/openid-connect/auth":
// Authentication Response
// http://openid.net/specs/openid-connect-core-1_0.html#AuthResponse
q := r.URL.Query()
if h.Scope != q.Get("scope") {
return errors.Errorf("scope wants %s but %s", h.Scope, q.Get("scope"))
}
to := fmt.Sprintf("%s?state=%s&code=%s", q.Get("redirect_uri"), q.Get("state"), h.authCode)
http.Redirect(w, r, to, 302)
case m == "POST" && p == "/protocol/openid-connect/token":
// Token Response
// http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse
if err := r.ParseForm(); err != nil {
return errors.Wrapf(err, "could not parse the form")
}
if h.authCode != r.Form.Get("code") {
return errors.Errorf("code wants %s but %s", h.authCode, r.Form.Get("code"))
}
w.Header().Add("Content-Type", "application/json")
if err := h.token.Execute(w, h); err != nil {
return errors.Wrapf(err, "could not execute the template")
}
case m == "GET" && p == "/protocol/openid-connect/certs":
w.Header().Add("Content-Type", "application/json")
if err := h.jwks.Execute(w, h); err != nil {
return errors.Wrapf(err, "could not execute the template")
}
default:
http.Error(w, "Not Found", 404)
}
return nil
}

View File

@@ -0,0 +1,85 @@
{
"issuer": "{{ .Issuer }}",
"authorization_endpoint": "{{ .Issuer }}/protocol/openid-connect/auth",
"token_endpoint": "{{ .Issuer }}/protocol/openid-connect/token",
"token_introspection_endpoint": "{{ .Issuer }}/protocol/openid-connect/token/introspect",
"userinfo_endpoint": "{{ .Issuer }}/protocol/openid-connect/userinfo",
"end_session_endpoint": "{{ .Issuer }}/protocol/openid-connect/logout",
"jwks_uri": "{{ .Issuer }}/protocol/openid-connect/certs",
"check_session_iframe": "{{ .Issuer }}/protocol/openid-connect/login-status-iframe.html",
"grant_types_supported": [
"authorization_code",
"implicit",
"refresh_token",
"password",
"client_credentials"
],
"response_types_supported": [
"code",
"none",
"id_token",
"token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"subject_types_supported": [
"public",
"pairwise"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"userinfo_signing_alg_values_supported": [
"RS256"
],
"request_object_signing_alg_values_supported": [
"none",
"RS256"
],
"response_modes_supported": [
"query",
"fragment",
"form_post"
],
"registration_endpoint": "{{ .Issuer }}/clients-registrations/openid-connect",
"token_endpoint_auth_methods_supported": [
"private_key_jwt",
"client_secret_basic",
"client_secret_post",
"client_secret_jwt"
],
"token_endpoint_auth_signing_alg_values_supported": [
"RS256"
],
"claims_supported": [
"sub",
"iss",
"auth_time",
"name",
"given_name",
"family_name",
"preferred_username",
"email"
],
"claim_types_supported": [
"normal"
],
"claims_parameter_supported": false,
"scopes_supported": [
"openid",
"offline_access",
"phone",
"address",
"email",
"profile"
],
"request_parameter_supported": true,
"request_uri_parameter_supported": true,
"code_challenge_methods_supported": [
"plain",
"S256"
],
"tls_client_certificate_bound_access_tokens": true
}

View File

@@ -0,0 +1,12 @@
{
"keys": [
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "xxx",
"n": "{{ .PrivateKey.N }}",
"e": "{{ .PrivateKey.E }}"
}
]
}

View File

@@ -0,0 +1,7 @@
{
"access_token": "7eaae8ab-8f69-45d9-ab7c-73560cd9444d",
"token_type": "Bearer",
"refresh_token": "{{ .RefreshToken }}",
"expires_in": 3600,
"id_token": "{{ .IDToken }}"
}

230
adaptors_test/cmd_test.go Normal file
View File

@@ -0,0 +1,230 @@
package adaptors_test
import (
"context"
"crypto/tls"
"net/http"
"os"
"testing"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/adaptors_test/authserver"
"github.com/int128/kubelogin/adaptors_test/keys"
"github.com/int128/kubelogin/adaptors_test/kubeconfig"
"github.com/int128/kubelogin/adaptors_test/logger"
"github.com/int128/kubelogin/di"
)
// Run the integration tests.
//
// 1. Start the auth server at port 9000.
// 2. Run the Cmd.
// 3. Open a request for port 8000.
// 4. Wait for the Cmd.
// 5. Shutdown the auth server.
//
func TestCmd_Run(t *testing.T) {
timeout := 1 * time.Second
t.Run("NoTLS", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
idToken := newIDToken(t, "http://localhost:9000")
serverConfig := authserver.Config{
Issuer: "http://localhost:9000",
IDToken: idToken,
IDTokenKeyPair: keys.JWSKeyPair,
RefreshToken: "REFRESH_TOKEN",
}
server := authserver.Start(t, serverConfig)
defer server.Shutdown(ctx)
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: "http://localhost:9000",
})
defer os.Remove(kubeConfigFilename)
startBrowserRequest(t, ctx, nil)
runCmd(t, ctx, "--kubeconfig", kubeConfigFilename, "--skip-open-browser")
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: idToken,
RefreshToken: "REFRESH_TOKEN",
})
})
t.Run("ExtraScopes", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
idToken := newIDToken(t, "http://localhost:9000")
serverConfig := authserver.Config{
Issuer: "http://localhost:9000",
IDToken: idToken,
IDTokenKeyPair: keys.JWSKeyPair,
RefreshToken: "REFRESH_TOKEN",
Scope: "profile groups openid",
}
server := authserver.Start(t, serverConfig)
defer server.Shutdown(ctx)
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: "http://localhost:9000",
ExtraScopes: "profile,groups",
})
defer os.Remove(kubeConfigFilename)
startBrowserRequest(t, ctx, nil)
runCmd(t, ctx, "--kubeconfig", kubeConfigFilename, "--skip-open-browser")
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: idToken,
RefreshToken: "REFRESH_TOKEN",
})
})
t.Run("CACert", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
idToken := newIDToken(t, "https://localhost:9000")
serverConfig := authserver.Config{
Issuer: "https://localhost:9000",
IDToken: idToken,
IDTokenKeyPair: keys.JWSKeyPair,
RefreshToken: "REFRESH_TOKEN",
TLSServerCert: keys.TLSServerCert,
TLSServerKey: keys.TLSServerKey,
}
server := authserver.Start(t, serverConfig)
defer server.Shutdown(ctx)
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: "https://localhost:9000",
IDPCertificateAuthority: keys.TLSCACert,
})
defer os.Remove(kubeConfigFilename)
startBrowserRequest(t, ctx, keys.TLSCACertAsConfig)
runCmd(t, ctx, "--kubeconfig", kubeConfigFilename, "--skip-open-browser")
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: idToken,
RefreshToken: "REFRESH_TOKEN",
})
})
t.Run("CACertData", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
idToken := newIDToken(t, "https://localhost:9000")
serverConfig := authserver.Config{
Issuer: "https://localhost:9000",
IDToken: idToken,
IDTokenKeyPair: keys.JWSKeyPair,
RefreshToken: "REFRESH_TOKEN",
TLSServerCert: keys.TLSServerCert,
TLSServerKey: keys.TLSServerKey,
}
server := authserver.Start(t, serverConfig)
defer server.Shutdown(ctx)
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: "https://localhost:9000",
IDPCertificateAuthorityData: keys.TLSCACertAsBase64,
})
defer os.Remove(kubeConfigFilename)
startBrowserRequest(t, ctx, keys.TLSCACertAsConfig)
runCmd(t, ctx, "--kubeconfig", kubeConfigFilename, "--skip-open-browser")
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: idToken,
RefreshToken: "REFRESH_TOKEN",
})
})
t.Run("AlreadyHaveValidToken", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
serverConfig := authserver.Config{
Issuer: "http://localhost:9000",
IDTokenKeyPair: keys.JWSKeyPair,
}
server := authserver.Start(t, serverConfig)
defer server.Shutdown(ctx)
idToken := newIDToken(t, "http://localhost:9000")
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: "http://localhost:9000",
IDToken: idToken,
})
defer os.Remove(kubeConfigFilename)
runCmd(t, ctx, "--kubeconfig", kubeConfigFilename, "--skip-open-browser")
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: idToken,
})
})
}
func newIDToken(t *testing.T, issuer string) string {
t.Helper()
var claims struct {
jwt.StandardClaims
Groups []string `json:"groups"`
}
claims.StandardClaims = jwt.StandardClaims{
Issuer: issuer,
Audience: "kubernetes",
ExpiresAt: time.Now().Add(time.Hour).Unix(),
Subject: "SUBJECT",
IssuedAt: time.Now().Unix(),
}
claims.Groups = []string{"admin", "users"}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
s, err := token.SignedString(keys.JWSKeyPair)
if err != nil {
t.Fatalf("Could not sign the claims: %s", err)
}
return s
}
func runCmd(t *testing.T, ctx context.Context, args ...string) {
t.Helper()
newLogger := func() adaptors.Logger {
return logger.New(t)
}
if err := di.InvokeWithExtra(func(cmd adaptors.Cmd) {
exitCode := cmd.Run(ctx, append([]string{"kubelogin", "--v=1"}, args...), "HEAD")
if exitCode != 0 {
t.Errorf("exit status wants 0 but %d", exitCode)
}
}, newLogger); err != nil {
t.Errorf("Invoke returned error: %+v", err)
}
}
func startBrowserRequest(t *testing.T, ctx context.Context, tlsConfig *tls.Config) {
t.Helper()
client := http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
req, err := http.NewRequest("GET", "http://localhost:8000/", nil)
if err != nil {
t.Errorf("could not create a request: %s", err)
return
}
req = req.WithContext(ctx)
go func() {
time.Sleep(50 * time.Millisecond)
resp, err := client.Do(req)
if err != nil {
t.Errorf("could not send a request: %s", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
t.Errorf("StatusCode wants 200 but %d", resp.StatusCode)
}
}()
}

View File

@@ -0,0 +1,71 @@
package keys
import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"io/ioutil"
"github.com/pkg/errors"
)
// TLSCACert is path to the CA certificate.
// This should be generated by Makefile before test.
const TLSCACert = "keys/testdata/ca.crt"
// TLSCACertAsBase64 is a base64 encoded string of TLSCACert.
var TLSCACertAsBase64 string
// TLSCACertAsConfig is a TLS config including TLSCACert.
var TLSCACertAsConfig = &tls.Config{RootCAs: x509.NewCertPool()}
// TLSServerCert is path to the server certificate.
// This should be generated by Makefile before test.
const TLSServerCert = "keys/testdata/server.crt"
// TLSServerKey is path to the server key.
// This should be generated by Makefile before test.
const TLSServerKey = "keys/testdata/server.key"
// JWSKey is path to the key for signing ID tokens.
const JWSKey = "keys/testdata/jws.key"
// JWSKeyPair is the key pair loaded from JWSKey.
var JWSKeyPair *rsa.PrivateKey
func init() {
var err error
JWSKeyPair, err = readPrivateKey(JWSKey)
if err != nil {
panic(err)
}
b, err := ioutil.ReadFile(TLSCACert)
if err != nil {
panic(err)
}
TLSCACertAsBase64 = base64.StdEncoding.EncodeToString(b)
if !TLSCACertAsConfig.RootCAs.AppendCertsFromPEM(b) {
panic("could not append the CA cert")
}
}
func readPrivateKey(name string) (*rsa.PrivateKey, error) {
b, err := ioutil.ReadFile(name)
if err != nil {
return nil, errors.Wrapf(err, "could not read JWSKey")
}
block, rest := pem.Decode(b)
if block == nil {
return nil, errors.New("could not decode PEM")
}
if len(rest) > 0 {
return nil, errors.New("PEM should contain single key but multiple keys")
}
k, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, errors.Wrapf(err, "could not parse the key")
}
return k, nil
}

View File

@@ -0,0 +1,4 @@
/CA
*.key
*.csr
*.crt

53
adaptors_test/keys/testdata/Makefile vendored Normal file
View File

@@ -0,0 +1,53 @@
.PHONY: clean
all: server.crt ca.crt jws.key
clean:
rm -v ca.* server.*
ca.key:
openssl genrsa -out $@ 1024
ca.csr: openssl.cnf ca.key
openssl req -config openssl.cnf \
-new \
-key ca.key \
-subj "/CN=Hello CA" \
-out $@
openssl req -noout -text -in $@
ca.crt: ca.csr ca.key
openssl x509 -req \
-signkey ca.key \
-in ca.csr \
-out $@
openssl x509 -text -in $@
server.key:
openssl genrsa -out $@ 1024
server.csr: openssl.cnf server.key
openssl req -config openssl.cnf \
-new \
-key server.key \
-subj "/CN=localhost" \
-out $@
openssl req -noout -text -in $@
server.crt: openssl.cnf server.csr ca.key ca.crt
rm -fr ./CA
mkdir -p ./CA
touch CA/index.txt
touch CA/index.txt.attr
echo 00 > CA/serial
openssl ca -config openssl.cnf \
-extensions v3_req \
-batch \
-cert ca.crt \
-keyfile ca.key \
-in server.csr \
-out $@
openssl x509 -text -in $@
jws.key:
openssl genrsa -out $@ 1024

37
adaptors_test/keys/testdata/openssl.cnf vendored Normal file
View File

@@ -0,0 +1,37 @@
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = ./CA
certs = $dir
crl_dir = $dir
database = $dir/index.txt
new_certs_dir = $dir
default_md = sha256
policy = policy_match
serial = $dir/serial
default_days = 365
[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
x509_extensions = v3_ca
[ req_distinguished_name ]
commonName = Common Name (e.g. server FQDN or YOUR name)
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = DNS:localhost
[ v3_ca ]
basicConstraints = CA:true

View File

@@ -2,11 +2,11 @@ package kubeconfig
import (
"html/template"
"io/ioutil"
"os"
"path/filepath"
"testing"
"gopkg.in/yaml.v3"
"gopkg.in/yaml.v2"
)
// Values represents values in .kubeconfig template.
@@ -16,22 +16,16 @@ type Values struct {
IDPCertificateAuthority string
IDPCertificateAuthorityData string
IDToken string
RefreshToken string
}
// Create creates a kubeconfig file and returns path to it.
func Create(t *testing.T, v *Values) string {
t.Helper()
name := filepath.Join(t.TempDir(), "kubeconfig")
f, err := os.Create(name)
f, err := ioutil.TempFile("", "kubeconfig")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := f.Close(); err != nil {
t.Fatal(err)
}
}()
defer f.Close()
tpl, err := template.ParseFiles("kubeconfig/testdata/kubeconfig.yaml")
if err != nil {
t.Fatal(err)
@@ -39,7 +33,7 @@ func Create(t *testing.T, v *Values) string {
if err := tpl.Execute(f, v); err != nil {
t.Fatal(err)
}
return name
return f.Name()
}
type AuthProviderConfig struct {
@@ -55,11 +49,7 @@ func Verify(t *testing.T, kubeconfig string, want AuthProviderConfig) {
t.Errorf("could not open kubeconfig: %s", err)
return
}
defer func() {
if err := f.Close(); err != nil {
t.Errorf("could not close kubeconfig: %s", err)
}
}()
defer f.Close()
var y struct {
Users []struct {

View File

@@ -30,8 +30,5 @@ users:
#{{ end }}
#{{ if .IDToken }}
id-token: {{ .IDToken }}
#{{ end }}
#{{ if .RefreshToken }}
refresh-token: {{ .RefreshToken }}
#{{ end }}
name: oidc

View File

@@ -0,0 +1,21 @@
package logger
import (
"github.com/int128/kubelogin/adaptors"
)
func New(t testingLogger) *adaptors.Logger {
return adaptors.NewLoggerWith(&bridge{t})
}
type testingLogger interface {
Logf(format string, v ...interface{})
}
type bridge struct {
t testingLogger
}
func (b *bridge) Printf(format string, v ...interface{}) {
b.t.Logf(format, v...)
}

42
di/di.go Normal file
View File

@@ -0,0 +1,42 @@
// Package di provides dependency injection.
package di
import (
"github.com/int128/kubelogin/adaptors"
adaptorsInterfaces "github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/usecases"
"github.com/pkg/errors"
"go.uber.org/dig"
)
var constructors = []interface{}{
usecases.NewLogin,
adaptors.NewCmd,
adaptors.NewKubeConfig,
adaptors.NewOIDC,
adaptors.NewHTTP,
}
var extraConstructors = []interface{}{
adaptors.NewLogger,
}
// Invoke runs the function with the default constructors.
func Invoke(f func(cmd adaptorsInterfaces.Cmd)) error {
return InvokeWithExtra(f, extraConstructors...)
}
// InvokeWithExtra runs the function with the given constructors.
func InvokeWithExtra(f func(cmd adaptorsInterfaces.Cmd), extra ...interface{}) error {
c := dig.New()
for _, constructor := range append(constructors, extra...) {
if err := c.Provide(constructor); err != nil {
return errors.Wrapf(err, "could not provide the constructor")
}
}
if err := c.Invoke(f); err != nil {
return errors.Wrapf(err, "could not invoke")
}
return nil
}

18
di/di_test.go Normal file
View File

@@ -0,0 +1,18 @@
package di_test
import (
"testing"
adaptors "github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/di"
)
func TestInvoke(t *testing.T) {
if err := di.Invoke(func(cmd adaptors.Cmd) {
if cmd == nil {
t.Errorf("cmd wants non-nil but nil")
}
}); err != nil {
t.Fatalf("Invoke returned error: %+v", err)
}
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

100
docs/google.md Normal file
View File

@@ -0,0 +1,100 @@
# Getting Started with Google Identity Platform
## Prerequisite
- You have a Google account.
- You have the Cluster Admin role of the Kubernetes cluster.
- You can configure the Kubernetes API server.
- `kubectl` and `kubelogin` are installed to your computer.
## 1. Setup Google API
Open [Google APIs Console](https://console.developers.google.com/apis/credentials) and create an OAuth client with the following setting:
- Application Type: Other
## 2. Setup Kubernetes API server
Configure your Kubernetes API Server accepts [OpenID Connect Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
### kops
If you are using [kops](https://github.com/kubernetes/kops), run `kops edit cluster` and append the following settings:
```yaml
spec:
kubeAPIServer:
oidcIssuerURL: https://accounts.google.com
oidcClientID: YOUR_CLIENT_ID.apps.googleusercontent.com
```
## 3. Setup Kubernetes cluster
Here assign the `cluster-admin` role to you.
```yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: oidc-admin-group
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: User
name: https://accounts.google.com#1234567890
```
You can create a custom role and assign it as well.
## 4. Setup kubectl
Configure `kubectl` for the OIDC authentication.
```sh
kubectl config set-credentials NAME \
--auth-provider oidc \
--auth-provider-arg idp-issuer-url=https://accounts.google.com \
--auth-provider-arg client-id=YOUR_CLIENT_ID.apps.googleusercontent.com \
--auth-provider-arg client-secret=YOUR_CLIENT_SECRET
```
## 5. Run kubelogin
Run `kubelogin`.
```
% kubelogin
2018/08/10 10:36:38 Reading .kubeconfig
2018/08/10 10:36:38 Using current context: hello.k8s.local
2018/08/10 10:36:41 Open http://localhost:8000 for authorization
2018/08/10 10:36:45 GET /
2018/08/10 10:37:07 GET /?state=...&session_state=...&code=ey...
2018/08/10 10:37:08 Updated .kubeconfig
```
Now your `~/.kube/config` should be like:
```yaml
users:
- name: hello.k8s.local
user:
auth-provider:
config:
idp-issuer-url: https://accounts.google.com
client-id: YOUR_CLIENT_ID.apps.googleusercontent.com
client-secret: YOUR_SECRET
id-token: ey... # kubelogin will update ID token here
refresh-token: ey... # kubelogin will update refresh token here
name: oidc
```
Make sure you can access to the Kubernetes cluster.
```
% kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-1-2-3-4.us-west-2.compute.internal Ready node 21d v1.9.6
ip-1-2-3-5.us-west-2.compute.internal Ready node 20d v1.9.6
```

107
docs/keycloak.md Normal file
View File

@@ -0,0 +1,107 @@
# Getting Started with Keycloak
## Prerequisite
- You have administrator access to the Keycloak.
- You have the Cluster Admin role of the Kubernetes cluster.
- You can configure the Kubernetes API server.
- `kubectl` and `kubelogin` are installed to your computer.
## 1. Setup Keycloak
Open the Keycloak and create an OIDC client as follows:
- Redirect URL: `http://localhost:8000/`
- Issuer URL: `https://keycloak.example.com/auth/realms/YOUR_REALM`
- Client ID: `kubernetes`
- Groups claim: `groups`
Create a group `kubernetes:admin` and join to it.
This is used for group based access control.
## 2. Setup Kubernetes API server
Configure your Kubernetes API server accepts [OpenID Connect Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
### kops
If you are using [kops](https://github.com/kubernetes/kops), run `kops edit cluster` and append the following settings:
```yaml
spec:
kubeAPIServer:
oidcIssuerURL: https://keycloak.example.com/auth/realms/YOUR_REALM
oidcClientID: kubernetes
oidcGroupsClaim: groups
```
## 3. Setup Kubernetes cluster
Here assign the `cluster-admin` role to the `kubernetes:admin` group.
```yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: keycloak-admin-group
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
name: /kubernetes:admin
```
You can create a custom role and assign it as well.
## 4. Setup kubectl
Configure `kubectl` for the OIDC authentication.
```sh
kubectl config set-credentials NAME \
--auth-provider oidc \
--auth-provider-arg idp-issuer-url=https://keycloak.example.com/auth/realms/YOUR_REALM \
--auth-provider-arg client-id=kubernetes \
--auth-provider-arg client-secret=YOUR_CLIENT_SECRET
```
## 5. Run kubelogin
Run `kubelogin`.
```
% kubelogin
2018/08/10 10:36:38 Reading .kubeconfig
2018/08/10 10:36:38 Using current context: hello.k8s.local
2018/08/10 10:36:41 Open http://localhost:8000 for authorization
2018/08/10 10:36:45 GET /
2018/08/10 10:37:07 GET /?state=...&session_state=...&code=ey...
2018/08/10 10:37:08 Updated .kubeconfig
```
Now your `~/.kube/config` should be like:
```yaml
users:
- name: hello.k8s.local
user:
auth-provider:
config:
idp-issuer-url: https://keycloak.example.com/auth/realms/YOUR_REALM
client-id: kubernetes
client-secret: YOUR_SECRET
id-token: ey... # kubelogin will update ID token here
refresh-token: ey... # kubelogin will update refresh token here
name: oidc
```
Make sure you can access to the Kubernetes cluster.
```
% kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-1-2-3-4.us-west-2.compute.internal Ready node 21d v1.9.6
ip-1-2-3-5.us-west-2.compute.internal Ready node 20d v1.9.6
```

View File

@@ -1,99 +0,0 @@
# Kubernetes OpenID Connection authentication
This document guides how to set up Kubernetes OpenID Connect (OIDC) authentication.
Let's see the following steps:
1. Set up the OIDC provider
1. Verify authentication
1. Bind a cluster role
1. Set up the Kubernetes API server
1. Set up the kubeconfig
1. Verify cluster access
## 1. Set up the OIDC provider
Kubelogin supports the authentication flows such as Device Authorization Grant or Authorization Code Flow.
For the details of flows supported in Kubelogin, see the [usage](usage.md).
For the details of your provider, ask the administrator of your provider.
## 2. Authenticate with the OpenID Connect Provider
Run the following command to show the instruction to set up the configuration:
```sh
kubectl oidc-login setup --oidc-issuer-url=ISSUER_URL --oidc-client-id=YOUR_CLIENT_ID
```
Set the following flags:
- Set the issuer URL of your OpenID Connect provider to `--oidc-issuer-url`.
- Set the client ID for your OpenID Connect provider to `--oidc-client-id`.
- If your provider requires a client secret, set `--oidc-client-secret`.
If your provider supports the Device Authorization Grant, set `--grant-type=device-code`.
It launches the browser and navigates to the authentication page of your provider.
If your provider supports the Authorization Code Flow, set `--grant-type=authcode`.
It starts a local server for the authentication.
It launches the browser and navigates to the authentication page of your provider.
You can see the full options:
```sh
kubectl oidc-login setup --help
```
## 3. Bind a cluster role
You can run the following command to bind `cluster-admin` role to you:
```sh
kubectl create clusterrolebinding oidc-cluster-admin --clusterrole=cluster-admin --user='ISSUER_URL#YOUR_SUBJECT'
```
## 4. Set up the Kubernetes API server
Add the following flags to kube-apiserver:
```
--oidc-issuer-url=ISSUER_URL
--oidc-client-id=YOUR_CLIENT_ID
```
See [Kubernetes Authenticating: OpenID Connect Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens) for the all flags.
## 5. Set up the kubeconfig
Add `oidc` user to the kubeconfig.
```sh
kubectl config set-credentials oidc \
--exec-interactive-mode=Never \
--exec-api-version=client.authentication.k8s.io/v1 \
--exec-command=kubectl \
--exec-arg=oidc-login \
--exec-arg=get-token \
--exec-arg=--oidc-issuer-url=ISSUER_URL \
--exec-arg=--oidc-client-id=YOUR_CLIENT_ID
```
If your provider requires a client secret, add `--oidc-client-secret=YOUR_CLIENT_SECRET`.
For security, it is recommended to add `--token-cache-storage=keyring` to store the token cache to the keyring instead of the file system.
If you encounter an error, see the [token cache](usage.md#token-cache) for details.
## 6. Verify cluster access
Make sure you can access the Kubernetes cluster.
```sh
kubectl --user=oidc cluster-info
```
You can switch the current context to oidc.
```sh
kubectl config set-context --current --user=oidc
```
You can share the kubeconfig to your team members for on-boarding.

View File

@@ -1,106 +0,0 @@
# Standalone mode
Kubelogin supports the standalone mode as well.
It writes the token to the kubeconfig (typically `~/.kube/config`) after authentication.
## Getting started
Configure your kubeconfig like:
```yaml
- name: keycloak
user:
auth-provider:
config:
client-id: YOUR_CLIENT_ID
client-secret: YOUR_CLIENT_SECRET
idp-issuer-url: https://issuer.example.com
name: oidc
```
Run kubelogin:
```sh
kubelogin
# or run as a kubectl plugin
kubectl oidc-login
```
It automatically opens the browser and you can log in to the provider.
<img src="keycloak-login.png" alt="keycloak-login" width="455" height="329">
After authentication, kubelogin writes the ID token and refresh token to the kubeconfig.
```console
% kubelogin
Open http://localhost:8000 for authentication
You got a valid token until 2019-05-18 10:28:51 +0900 JST
Updated ~/.kubeconfig
```
Now you can access the cluster.
```console
% kubectl get pods
NAME READY STATUS RESTARTS AGE
echoserver-86c78fdccd-nzmd5 1/1 Running 0 26d
```
Your kubeconfig looks like:
```yaml
users:
- name: keycloak
user:
auth-provider:
config:
client-id: YOUR_CLIENT_ID
client-secret: YOUR_CLIENT_SECRET
idp-issuer-url: https://issuer.example.com
id-token: ey... # kubelogin will add or update the ID token here
refresh-token: ey... # kubelogin will add or update the refresh token here
name: oidc
```
If the ID token is valid, kubelogin does nothing.
```console
% kubelogin
You already have a valid token until 2019-05-18 10:28:51 +0900 JST
```
If the ID token has expired, kubelogin will refresh the token using the refresh token in the kubeconfig.
If the refresh token has expired, kubelogin will proceed the authentication.
## Usage
You can set path to the kubeconfig file by the option or the environment variable just like kubectl.
It defaults to `~/.kube/config`.
```sh
# by the option
kubelogin --kubeconfig /path/to/kubeconfig
# by the environment variable
KUBECONFIG="/path/to/kubeconfig1:/path/to/kubeconfig2" kubelogin
```
If you set multiple files, kubelogin will find the file which has the current authentication (i.e. `user` and `auth-provider`) and write a token to it.
Kubelogin supports the following keys of `auth-provider` in a kubeconfig.
See [kubectl authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#using-kubectl) for more.
| Key | Direction | Value |
| -------------------------------- | ---------------- | ---------------------------------------------------- |
| `idp-issuer-url` | Read (Mandatory) | Issuer URL of the provider. |
| `client-id` | Read (Mandatory) | Client ID of the provider. |
| `client-secret` | Read (Mandatory) | Client Secret of the provider. |
| `idp-certificate-authority` | Read | CA certificate path of the provider. |
| `idp-certificate-authority-data` | Read | Base64 encoded CA certificate of the provider. |
| `extra-scopes` | Read | Scopes to request to the provider (comma separated). |
| `id-token` | Write | ID token got from the provider. |
| `refresh-token` | Write | Refresh token got from the provider. |
See also [usage.md](usage.md).

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

40
docs/team_ops.md Normal file
View File

@@ -0,0 +1,40 @@
# Team Operation
## kops
Export the kubeconfig.
```sh
KUBECONFIG=.kubeconfig kops export kubecfg hello.k8s.local
```
Remove the `admin` access from the kubeconfig.
It should look as like:
```yaml
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: LS...
server: https://api.hello.k8s.example.com
name: hello.k8s.local
contexts:
- context:
cluster: hello.k8s.local
user: hello.k8s.local
name: hello.k8s.local
current-context: hello.k8s.local
preferences: {}
users:
- name: hello.k8s.local
user:
auth-provider:
name: oidc
config:
client-id: YOUR_CLIEND_ID
client-secret: YOUR_CLIENT_SECRET
idp-issuer-url: YOUR_ISSUER
```
You can share the kubeconfig to your team members for easy onboarding.

View File

@@ -1,340 +0,0 @@
# Usage
Kubelogin supports the following options:
```
Usage:
kubelogin get-token [flags]
Flags:
--oidc-issuer-url string Issuer URL of the provider (mandatory)
--oidc-client-id string Client ID of the provider (mandatory)
--oidc-client-secret string Client secret of the provider
--oidc-redirect-url string [authcode, authcode-keyboard] Redirect URL
--oidc-extra-scope strings Scopes to request to the provider
--oidc-use-access-token Instead of using the id_token, use the access_token to authenticate to Kubernetes
--oidc-request-header stringToString HTTP headers to send with an authentication request (default [])
--force-refresh If set, refresh the ID token regardless of its expiration time
--token-cache-dir string Path to a directory of the token cache (default "~/.kube/cache/oidc-login")
--token-cache-storage string Storage for the token cache. One of (disk|keyring|none) (default "disk")
--certificate-authority stringArray Path to a cert file for the certificate authority
--certificate-authority-data stringArray Base64 encoded cert for the certificate authority
--insecure-skip-tls-verify [SECURITY RISK] If set, the server's certificate will not be checked for validity
--tls-renegotiation-once If set, allow a remote server to request renegotiation once per connection
--tls-renegotiation-freely If set, allow a remote server to repeatedly request renegotiation
--oidc-pkce-method string PKCE code challenge method. Automatically determined by default. One of (auto|no|S256) (default "auto")
--grant-type string Authorization grant type to use. One of (auto|authcode|authcode-keyboard|password|device-code|client-credentials) (default "auto")
--listen-address strings [authcode] Address to bind to the local server. If multiple addresses are set, it will try binding in order (default [127.0.0.1:8000,127.0.0.1:18000])
--skip-open-browser [authcode] Do not open the browser automatically
--browser-command string [authcode] Command to open the browser
--authentication-timeout-sec int [authcode] Timeout of authentication in seconds (default 180)
--local-server-cert string [authcode] Certificate path for the local server
--local-server-key string [authcode] Certificate key path for the local server
--open-url-after-authentication string [authcode] If set, open the URL in the browser after authentication
--oidc-auth-request-extra-params stringToString [authcode, authcode-keyboard, client-credentials] Extra query parameters to send with an authentication request (default [])
--username string [password] Username for resource owner password credentials grant
--password string [password] Password for resource owner password credentials grant
-h, --help help for get-token
Global Flags:
--add_dir_header If true, adds the file directory to the header of the log messages
--alsologtostderr log to standard error as well as files (no effect when -logtostderr=true)
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory (no effect when -logtostderr=true)
--log_file string If non-empty, use this log file (no effect when -logtostderr=true)
--log_file_max_size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files (default true)
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files (no effect when -logtostderr=true)
--stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true) (default 2)
-v, --v Level number for the log level verbosity
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```
## Options
### Authentication timeout
By default, you need to log in to your provider in the browser within 3 minutes.
This prevents a process from remaining forever.
You can change the timeout by the following flag:
```yaml
- --authentication-timeout-sec=60
```
For now this timeout works only for the authorization code flow.
### Extra scopes
You can set the extra scopes to request to the provider by `--oidc-extra-scope`.
```yaml
- --oidc-extra-scope=email
- --oidc-extra-scope=profile
```
### PKCE
Kubelogin automatically uses the PKCE if the provider supports it.
It determines the code challenge method by the `code_challenge_methods_supported` claim of the OpenID Connect Discovery document.
If your provider does not return a valid `code_challenge_methods_supported` claim,
you can enforce the code challenge method by `--oidc-pkce-method`.
```yaml
- --oidc-pkce-method=S256
```
For the most providers, you don't need to set this option explicitly.
### HTTP headers
If your provider requires extra HTTP headers, you can set them by `--oidc-request-header`.
For Azure AD Single Page Application with PKCE, you can set `Origin` header as follows:
```yaml
- --oidc-request-header=Origin=localhost
```
### CA certificate
You can use your self-signed certificate for the provider.
```yaml
- --certificate-authority=/home/user/.kube/keycloak-ca.pem
- --certificate-authority-data=LS0t...
```
### HTTP proxy
You can set the following environment variables if you are behind a proxy: `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY`.
See also [net/http#ProxyFromEnvironment](https://golang.org/pkg/net/http/#ProxyFromEnvironment).
### Token cache
Kubelogin stores the token cache to the file system by default.
You can store the token cache to the OS keyring for enhanced security.
It depends on [zalando/go-keyring](https://github.com/zalando/go-keyring).
```yaml
- --token-cache-storage=keyring
```
You can delete the token cache by the clean command.
```console
% kubectl oidc-login clean
Deleted the token cache at /home/user/.kube/cache/oidc-login
Deleted the token cache from the keyring
```
For systems with immutable storage and no keyring, a cache type of none is available.
### Home directory expansion
If a value in the following options begins with a tilde character `~`, it is expanded to the home directory.
- `--certificate-authority`
- `--local-server-cert`
- `--local-server-key`
- `--token-cache-dir`
## Authentication flows
Kubelogin support the following flows:
- [Device Authorization Grant](#device-authorization-grant)
- [Authorization Code Flow](#authorization-code-flow)
- [Authorization Code Flow with a Keyboard](#authorization-code-flow-with-a-keyboard)
- [Resource Owner Password Credentials Grant](#resource-owner-password-credentials-grant)
- [Client Credentials Flow](#client-credentials-flow)
### Device Authorization Grant
It performs the [Device Authorization Grant (RFC 8628)](https://tools.ietf.org/html/rfc8628) when `--grant-type=device-code` is set.
```yaml
- --grant-type=device-code
```
It automatically opens the browser.
If the provider returns the `verification_uri_complete` parameter, you don't need to enter the code.
Otherwise, you need to enter the code shown.
If you encounter a problem with the browser, you can change the browser command or skip opening the browser.
```yaml
# Change the browser command
- --browser-command=google-chrome
# Do not open the browser
- --skip-open-browser
```
### Authorization Code Flow
It performs the [Authorization Code Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth) when `--grant-type=authcode` is set or the flag is not given.
It starts the local server at port 8000 or 18000 by default.
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 listening address.
```yaml
- --listen-address=127.0.0.1:12345
- --listen-address=127.0.0.1:23456
```
The redirect URL defaults to `http://localhost` with the listening port.
You can override the redirect URL.
```yaml
- --oidc-redirect-url=http://127.0.0.1:8000/
- --oidc-redirect-url=http://your-local-hostname:8000/
```
You can specify a certificate for the local webserver if HTTPS is required by your identity provider.
```yaml
- --local-server-cert=localhost.crt
- --local-server-key=localhost.key
```
You can add extra parameters to the authentication request.
```yaml
- --oidc-auth-request-extra-params=ttl=86400
```
When authentication completed, kubelogin shows a message to close the browser.
You can change the URL to show after authentication.
```yaml
- --open-url-after-authentication=https://example.com/success.html
```
If you encounter a problem with the browser, you can change the browser command or skip opening the browser.
```yaml
# Change the browser command
- --browser-command=google-chrome
# Do not open the browser
- --skip-open-browser
```
### Authorization Code Flow with a keyboard
If you cannot access the browser, instead use the authorization code flow with a keyboard.
```yaml
- --grant-type=authcode-keyboard
```
You need to explicitly set the redirect URL.
```yaml
- --oidc-redirect-url=urn:ietf:wg:oauth:2.0:oob
- --oidc-redirect-url=http://localhost
```
Kubelogin will show the URL and prompt.
Open the URL in the browser and then copy the code shown.
```
% kubectl get pods
Open https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&client_id=...
Enter code: YOUR_CODE
```
You can add extra parameters to the authentication request.
```yaml
- --oidc-auth-request-extra-params=ttl=86400
```
### Resource Owner Password Credentials Grant
It performs the [Resource Owner Password Credentials Grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3)
when `--grant-type=password` or `--username` is set.
Note that most OIDC providers do not support this grant.
Keycloak supports this grant but you need to explicitly enable the "Direct Access Grants" feature in the client settings.
You can set the username and password.
```yaml
- --username=USERNAME
- --password=PASSWORD
```
If the password is not set, kubelogin will show the prompt for the password.
```yaml
- --username=USERNAME
```
```
% kubectl get pods
Password:
```
If the username is not set, kubelogin will show the prompt for the username and password.
```yaml
- --grant-type=password
```
```
% kubectl get pods
Username: foo
Password:
```
### Client Credentials Flow
It performs the [OAuth 2.0 Client Credentials Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-1.3.4) when `--grant-type=client-credentials` is set.
```yaml
- --grant-type=client-credentials
```
Per specification, this flow only returns authorization tokens.
## Run in Docker
You can run [the Docker image](https://ghcr.io/int128/kubelogin) instead of the binary.
The kubeconfig looks like:
```yaml
users:
- name: oidc
user:
exec:
apiVersion: client.authentication.k8s.io/v1
command: docker
args:
- run
- --rm
- -v
- /tmp/.token-cache:/.token-cache
- -p
- 8000:8000
- ghcr.io/int128/kubelogin
- get-token
- --token-cache-dir=/.token-cache
- --listen-address=0.0.0.0:8000
- --oidc-issuer-url=ISSUER_URL
- --oidc-client-id=YOUR_CLIENT_ID
- --oidc-client-secret=YOUR_CLIENT_SECRET
```
Known limitations:
- It cannot open the browser automatically.
- The container port and listen port must be equal for consistency of the redirect URI.

300
go.mod
View File

@@ -1,277 +1,35 @@
module github.com/int128/kubelogin
go 1.25.3
require (
github.com/chromedp/chromedp v0.14.2
github.com/coreos/go-oidc/v3 v3.17.0
github.com/gofrs/flock v0.13.0
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/go-cmp v0.7.0
github.com/google/wire v0.7.0
github.com/int128/oauth2cli v1.18.0
github.com/int128/oauth2dev v1.1.0
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/spf13/cobra v1.10.1
github.com/spf13/pflag v1.0.10
github.com/stretchr/testify v1.11.1
github.com/zalando/go-keyring v0.2.6
golang.org/x/oauth2 v0.33.0
golang.org/x/sync v0.18.0
golang.org/x/term v0.37.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.33.4
k8s.io/client-go v0.33.4
k8s.io/klog/v2 v2.130.1
)
require (
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
4d63.com/gochecknoglobals v0.2.2 // indirect
al.essio.dev/pkg/shellescape v1.5.1 // indirect
codeberg.org/chavacava/garif v0.2.0 // indirect
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect
dev.gaijin.team/go/golib v0.6.0 // indirect
github.com/4meepo/tagalign v1.4.3 // indirect
github.com/Abirdcfly/dupword v0.1.6 // indirect
github.com/AdminBenni/iota-mixing v1.0.0 // indirect
github.com/AlwxSin/noinlineerr v1.0.5 // indirect
github.com/Antonboom/errname v1.1.1 // indirect
github.com/Antonboom/nilnil v1.1.1 // indirect
github.com/Antonboom/testifylint v1.6.4 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/Djarvur/go-err113 v0.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/MirrexOne/unqueryvet v1.2.1 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
github.com/alecthomas/chroma/v2 v2.20.0 // indirect
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
github.com/alexkohler/prealloc v1.0.0 // indirect
github.com/alfatraining/structtag v1.0.0 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/alingse/nilnesserr v0.2.0 // indirect
github.com/ashanbrown/forbidigo/v2 v2.1.0 // indirect
github.com/ashanbrown/makezero/v2 v2.0.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bkielbasa/cyclop v1.2.3 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
github.com/bombsimon/wsl/v4 v4.7.0 // indirect
github.com/bombsimon/wsl/v5 v5.2.0 // indirect
github.com/breml/bidichk v0.3.3 // indirect
github.com/breml/errchkjson v0.4.1 // indirect
github.com/brunoga/deep v1.2.4 // indirect
github.com/butuzov/ireturn v0.4.0 // indirect
github.com/butuzov/mirror v1.3.0 // indirect
github.com/catenacyber/perfsprint v0.9.1 // indirect
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charithe/durationcheck v0.0.10 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 // indirect
github.com/chromedp/sysutil v1.1.0 // indirect
github.com/ckaznocha/intrange v0.3.1 // indirect
github.com/curioswitch/go-reassign v0.3.0 // indirect
github.com/daixiang0/gci v0.13.7 // indirect
github.com/danieljoos/wincred v1.2.2 // indirect
github.com/dave/dst v0.27.3 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/denis-tingaikin/go-header v0.5.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/ettle/strcase v0.2.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/firefart/nonamedreturns v1.0.6 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
github.com/ghostiam/protogetter v0.3.16 // indirect
github.com/go-critic/go-critic v0.13.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.2.0 // indirect
github.com/go-toolsmith/astfmt v1.1.0 // indirect
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/godoc-lint/godoc-lint v0.10.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golangci/asciicheck v0.5.0 // indirect
github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
github.com/golangci/go-printf-func-name v0.1.1 // indirect
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
github.com/golangci/golangci-lint/v2 v2.5.0 // indirect
github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect
github.com/golangci/misspell v0.7.0 // indirect
github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe // indirect
github.com/golangci/plugin-module-register v0.1.2 // indirect
github.com/golangci/revgrep v0.8.0 // indirect
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
github.com/google/subcommands v1.2.0 // indirect
github.com/gordonklaus/ineffassign v0.2.0 // indirect
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
github.com/gostaticanalysis/comment v1.5.0 // indirect
github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/int128/listener v1.3.0 // indirect
github.com/jedib0t/go-pretty/v6 v6.6.7 // indirect
github.com/jgautheron/goconst v1.8.2 // indirect
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
github.com/jjti/go-spancheck v0.6.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/julz/importas v0.2.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect
github.com/kisielk/errcheck v1.9.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
github.com/knadh/koanf/parsers/yaml v0.1.0 // indirect
github.com/knadh/koanf/providers/env v1.0.0 // indirect
github.com/knadh/koanf/providers/file v1.1.2 // indirect
github.com/knadh/koanf/providers/posflag v0.1.0 // indirect
github.com/knadh/koanf/providers/structs v0.1.0 // indirect
github.com/knadh/koanf/v2 v2.3.0 // indirect
github.com/kulti/thelper v0.7.1 // indirect
github.com/kunwardeep/paralleltest v1.0.14 // indirect
github.com/lasiar/canonicalheader v1.1.2 // indirect
github.com/ldez/exptostd v0.4.4 // indirect
github.com/ldez/gomoddirectives v0.7.0 // indirect
github.com/ldez/grignotin v0.10.1 // indirect
github.com/ldez/tagliatelle v0.7.2 // indirect
github.com/ldez/usetesting v0.5.0 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/macabu/inamedparam v0.2.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect
github.com/manuelarte/funcorder v0.5.0 // indirect
github.com/maratori/testableexamples v1.0.0 // indirect
github.com/maratori/testpackage v1.1.1 // indirect
github.com/matoous/godox v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mgechev/revive v1.12.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/coreos/go-oidc v2.0.0+incompatible
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/mock v1.2.0
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
github.com/imdario/mergo v0.3.7 // indirect
github.com/int128/oauth2cli v1.2.1
github.com/jessevdk/go-flags v1.4.0
github.com/json-iterator/go v1.1.6 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/moricho/tparallel v0.3.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
github.com/nunnatsa/ginkgolinter v0.21.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/polyfloyd/go-errorlint v1.8.0 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/quasilyte/go-ruleguard v0.4.4 // indirect
github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
github.com/quasilyte/gogrep v0.5.0 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
github.com/raeperd/recvcheck v0.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/ryancurrah/gomodguard v1.4.1 // indirect
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
github.com/securego/gosec/v2 v2.22.8 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
github.com/sonatard/noctx v0.4.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/viper v1.20.0 // indirect
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tetafro/godot v1.5.4 // indirect
github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
github.com/timonwong/loggercheck v0.11.0 // indirect
github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
github.com/ultraware/funlen v0.2.0 // indirect
github.com/ultraware/whitespace v0.2.0 // indirect
github.com/uudashr/gocognit v1.2.0 // indirect
github.com/uudashr/iface v1.4.1 // indirect
github.com/vektra/mockery/v3 v3.6.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xen0n/gosmopolitan v1.3.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.3.0 // indirect
github.com/ykadowak/zerologlint v0.1.5 // indirect
gitlab.com/bosi/decorder v0.4.2 // indirect
go-simpler.org/musttag v0.14.0 // indirect
go-simpler.org/sloglint v0.11.1 // indirect
go.augendre.info/arangolint v0.2.0 // indirect
go.augendre.info/fatcontext v0.8.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect
golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.37.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pkg/errors v0.8.1
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/spf13/pflag v1.0.3 // indirect
github.com/stretchr/testify v1.3.0 // indirect
go.uber.org/dig v1.7.0
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c // indirect
golang.org/x/net v0.0.0-20190328230028-74de082e2cca // indirect
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
honnef.co/go/tools v0.6.1 // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
mvdan.cc/gofumpt v0.9.1 // indirect
mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
tool (
github.com/golangci/golangci-lint/v2/cmd/golangci-lint
github.com/google/wire/cmd/wire
github.com/vektra/mockery/v3
gopkg.in/square/go-jose.v2 v2.3.0 // indirect
gopkg.in/yaml.v2 v2.2.2
k8s.io/api v0.0.0-20190222213804-5cb15d344471 // indirect
k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628 // indirect
k8s.io/client-go v10.0.0+incompatible
k8s.io/klog v0.2.0 // indirect
sigs.k8s.io/yaml v1.1.0 // indirect
)

1172
go.sum

File diff suppressed because it is too large Load Diff

50
infrastructure/http.go Normal file
View File

@@ -0,0 +1,50 @@
package infrastructure
import (
"net/http"
"net/http/httputil"
"github.com/int128/kubelogin/adaptors/interfaces"
)
const (
logLevelDumpHeaders = 2
logLevelDumpBody = 3
)
type LoggingTransport struct {
Base http.RoundTripper
Logger adaptors.Logger
}
func (t *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
if !t.IsDumpEnabled() {
return t.Base.RoundTrip(req)
}
reqDump, err := httputil.DumpRequestOut(req, t.IsDumpBodyEnabled())
if err != nil {
t.Logger.Debugf(logLevelDumpHeaders, "Error: could not dump the request: %s", err)
return t.Base.RoundTrip(req)
}
t.Logger.Debugf(logLevelDumpHeaders, "%s", string(reqDump))
resp, err := t.Base.RoundTrip(req)
if err != nil {
return resp, err
}
respDump, err := httputil.DumpResponse(resp, t.IsDumpBodyEnabled())
if err != nil {
t.Logger.Debugf(logLevelDumpHeaders, "Error: could not dump the response: %s", err)
return resp, err
}
t.Logger.Debugf(logLevelDumpHeaders, "%s", string(respDump))
return resp, err
}
func (t *LoggingTransport) IsDumpEnabled() bool {
return t.Logger.IsEnabled(logLevelDumpHeaders)
}
func (t *LoggingTransport) IsDumpBodyEnabled() bool {
return t.Logger.IsEnabled(logLevelDumpBody)
}

View File

@@ -0,0 +1,90 @@
package infrastructure
import (
"bufio"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/adaptors/mock_adaptors"
)
type mockTransport struct {
req *http.Request
resp *http.Response
}
func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
t.req = req
return t.resp, nil
}
func TestLoggingTransport_RoundTrip(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
logger := mock_adaptors.NewLogger(t, ctrl)
logger.EXPECT().
IsEnabled(gomock.Any()).
Return(true).
AnyTimes()
req := httptest.NewRequest("GET", "http://example.com/hello", nil)
resp, err := http.ReadResponse(bufio.NewReader(strings.NewReader(`HTTP/1.1 200 OK
Host: example.com
dummy`)), req)
if err != nil {
t.Errorf("could not create a response: %s", err)
}
defer resp.Body.Close()
transport := &LoggingTransport{
Base: &mockTransport{resp: resp},
Logger: logger,
}
gotResp, err := transport.RoundTrip(req)
if err != nil {
t.Errorf("RoundTrip error: %s", err)
}
if gotResp != resp {
t.Errorf("resp wants %v but %v", resp, gotResp)
}
}
func TestLoggingTransport_IsDumpEnabled(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
logger := mock_adaptors.NewLogger(t, ctrl)
logger.EXPECT().
IsEnabled(adaptors.LogLevel(logLevelDumpHeaders)).
Return(true)
transport := &LoggingTransport{
Logger: logger,
}
if transport.IsDumpEnabled() != true {
t.Errorf("IsDumpEnabled wants true")
}
}
func TestLoggingTransport_IsDumpBodyEnabled(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
logger := mock_adaptors.NewLogger(t, ctrl)
logger.EXPECT().
IsEnabled(adaptors.LogLevel(logLevelDumpBody)).
Return(true)
transport := &LoggingTransport{
Logger: logger,
}
if transport.IsDumpBodyEnabled() != true {
t.Errorf("IsDumpBodyEnabled wants true")
}
}

View File

@@ -1,27 +0,0 @@
package integration_test
import (
"context"
"os"
"testing"
"time"
"github.com/int128/kubelogin/integration_test/httpdriver"
"github.com/int128/kubelogin/pkg/di"
"github.com/int128/kubelogin/pkg/testing/clock"
"github.com/int128/kubelogin/pkg/testing/logger"
)
func TestClean(t *testing.T) {
tokenCacheDir := t.TempDir()
cmd := di.NewCmdForHeadless(clock.Fake(time.Now()), os.Stdin, os.Stdout, logger.New(t), httpdriver.Zero(t))
exitCode := cmd.Run(context.TODO(), []string{
"kubelogin",
"clean",
"--token-cache-dir", tokenCacheDir,
}, "HEAD")
if exitCode != 0 {
t.Errorf("exit status wants 0 but %d", exitCode)
}
}

View File

@@ -1,448 +0,0 @@
package integration_test
import (
"bytes"
"context"
"encoding/json"
"io"
"os"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/integration_test/httpdriver"
"github.com/int128/kubelogin/integration_test/keypair"
"github.com/int128/kubelogin/integration_test/oidcserver"
"github.com/int128/kubelogin/integration_test/oidcserver/testconfig"
"github.com/int128/kubelogin/pkg/di"
"github.com/int128/kubelogin/pkg/infrastructure/browser"
"github.com/int128/kubelogin/pkg/testing/clock"
"github.com/int128/kubelogin/pkg/testing/logger"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthenticationv1 "k8s.io/client-go/pkg/apis/clientauthentication/v1"
)
// Run the integration tests of the credential plugin use-case.
//
// 1. Start the auth server.
// 2. Run the Cmd.
// 3. Open a request for the local server.
// 4. Verify the output.
func TestCredentialPlugin(t *testing.T) {
timeout := 10 * time.Second
now := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
tokenCacheDir := t.TempDir()
for name, tc := range map[string]struct {
keyPair keypair.KeyPair
args []string
}{
"NoTLS": {},
"TLS": {
keyPair: keypair.Server,
args: []string{"--certificate-authority", keypair.Server.CACertPath},
},
} {
httpDriverConfig := httpdriver.Config{
TLSConfig: tc.keyPair.TLSConfig,
BodyContains: "Authenticated",
}
t.Run(name, func(t *testing.T) {
t.Run("AuthCode", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, tc.keyPair, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "S256",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpDriverConfig),
now: now,
stdout: &stdout,
args: tc.args,
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("ROPC", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, tc.keyPair, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
Username: "USER1",
Password: "PASS1",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.Zero(t),
now: now,
stdout: &stdout,
args: append([]string{
"--username", "USER1",
"--password", "PASS1",
}, tc.args...),
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("TokenCacheLifecycle", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, tc.keyPair, testconfig.Config{})
t.Run("NoCache", func(t *testing.T) {
svc.SetConfig(testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "S256",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
RefreshToken: "REFRESH_TOKEN_1",
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpDriverConfig),
now: now,
stdout: &stdout,
args: tc.args,
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("Valid", func(t *testing.T) {
svc.SetConfig(testconfig.Config{})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.Zero(t),
now: now,
stdout: &stdout,
args: tc.args,
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("Refresh", func(t *testing.T) {
svc.SetConfig(testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
RefreshToken: "REFRESH_TOKEN_1",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(3 * time.Hour),
RefreshToken: "REFRESH_TOKEN_2",
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpDriverConfig),
now: now.Add(2 * time.Hour),
stdout: &stdout,
args: tc.args,
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(3*time.Hour))
})
t.Run("RefreshAgain", func(t *testing.T) {
svc.SetConfig(testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
RefreshToken: "REFRESH_TOKEN_2",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(5 * time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpDriverConfig),
now: now.Add(4 * time.Hour),
stdout: &stdout,
args: tc.args,
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(5*time.Hour))
})
})
})
}
t.Run("PKCE", func(t *testing.T) {
t.Run("Not supported by provider", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: nil,
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{BodyContains: "Authenticated"}),
now: now,
stdout: &stdout,
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("Enforce", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "S256",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: nil,
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{BodyContains: "Authenticated"}),
now: now,
stdout: &stdout,
args: []string{"--oidc-use-pkce"},
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
})
t.Run("TLSData", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, keypair.Server, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "S256",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{TLSConfig: keypair.Server.TLSConfig, BodyContains: "Authenticated"}),
now: now,
stdout: &stdout,
args: []string{"--certificate-authority-data", keypair.Server.CACertBase64},
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("ExtraScopes", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "email profile openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "S256",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{BodyContains: "Authenticated"}),
now: now,
stdout: &stdout,
args: []string{
"--oidc-extra-scope", "email",
"--oidc-extra-scope", "profile",
},
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("OpenURLAfterAuthentication", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "S256",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{BodyContains: "URL=https://example.com/success"}),
now: now,
stdout: &stdout,
args: []string{"--open-url-after-authentication", "https://example.com/success"},
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("RedirectURLHTTPS", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "https://localhost:",
CodeChallengeMethod: "S256",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{
TLSConfig: keypair.Server.TLSConfig,
BodyContains: "Authenticated",
}),
now: now,
stdout: &stdout,
args: []string{
"--local-server-cert", keypair.Server.CertPath,
"--local-server-key", keypair.Server.KeyPath,
},
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
t.Run("ExtraParams", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
svc := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
CodeChallengeMethod: "S256",
ExtraParams: map[string]string{
"ttl": "86400",
"reauth": "false",
},
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
CodeChallengeMethodsSupported: []string{"plain", "S256"},
},
})
var stdout bytes.Buffer
runGetToken(t, ctx, getTokenConfig{
tokenCacheDir: tokenCacheDir,
issuerURL: svc.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{BodyContains: "Authenticated"}),
now: now,
stdout: &stdout,
args: []string{
"--oidc-auth-request-extra-params", "ttl=86400",
"--oidc-auth-request-extra-params", "reauth=false",
},
})
assertCredentialPluginStdout(t, &stdout, svc.LastTokenResponse().IDToken, now.Add(time.Hour))
})
}
type getTokenConfig struct {
tokenCacheDir string
issuerURL string
httpDriver browser.Interface
stdout io.Writer
now time.Time
args []string
}
func runGetToken(t *testing.T, ctx context.Context, cfg getTokenConfig) {
cmd := di.NewCmdForHeadless(clock.Fake(cfg.now), os.Stdin, cfg.stdout, logger.New(t), cfg.httpDriver)
t.Setenv(
"KUBERNETES_EXEC_INFO",
`{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1","spec":{"interactive":true}}`,
)
exitCode := cmd.Run(ctx, append([]string{
"kubelogin",
"get-token",
"--token-cache-dir", cfg.tokenCacheDir,
"--oidc-issuer-url", cfg.issuerURL,
"--oidc-client-id", "kubernetes",
"--listen-address", "127.0.0.1:0",
}, cfg.args...), "latest")
if exitCode != 0 {
t.Errorf("exit status wants 0 but %d", exitCode)
}
}
func assertCredentialPluginStdout(t *testing.T, stdout io.Reader, token string, expiry time.Time) {
var got clientauthenticationv1.ExecCredential
if err := json.NewDecoder(stdout).Decode(&got); err != nil {
t.Errorf("could not decode json of the credential plugin: %s", err)
return
}
want := clientauthenticationv1.ExecCredential{
TypeMeta: metav1.TypeMeta{
APIVersion: "client.authentication.k8s.io/v1",
Kind: "ExecCredential",
},
Status: &clientauthenticationv1.ExecCredentialStatus{
Token: token,
ExpirationTimestamp: &metav1.Time{Time: expiry},
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("stdout mismatch (-want +got):\n%s", diff)
}
}

View File

@@ -1,82 +0,0 @@
// Package httpdriver provides a test double of the browser.
package httpdriver
import (
"context"
"crypto/tls"
"io"
"net/http"
"strings"
"testing"
)
type Config struct {
TLSConfig *tls.Config
BodyContains string
}
// New returns a client to simulate browser access.
func New(ctx context.Context, t *testing.T, config Config) *client {
return &client{ctx, t, config}
}
// Zero returns a client which call is not expected.
func Zero(t *testing.T) *zeroClient {
return &zeroClient{t}
}
type client struct {
ctx context.Context
t *testing.T
config Config
}
func (c *client) Open(url string) error {
client := http.Client{Transport: &http.Transport{TLSClientConfig: c.config.TLSConfig}}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
c.t.Errorf("could not create a request: %s", err)
return nil
}
req = req.WithContext(c.ctx)
resp, err := client.Do(req)
if err != nil {
c.t.Errorf("could not send a request: %s", err)
return nil
}
defer func() {
if err := resp.Body.Close(); err != nil {
c.t.Errorf("could not close response body: %s", err)
}
}()
if resp.StatusCode != 200 {
c.t.Errorf("StatusCode wants 200 but %d", resp.StatusCode)
}
b, err := io.ReadAll(resp.Body)
if err != nil {
c.t.Errorf("could not read body: %s", err)
return nil
}
body := string(b)
if !strings.Contains(body, c.config.BodyContains) {
c.t.Errorf("body should contain %s but was %s", c.config.BodyContains, body)
}
return nil
}
func (c *client) OpenCommand(_ context.Context, url, _ string) error {
return c.Open(url)
}
type zeroClient struct {
t *testing.T
}
func (c *zeroClient) Open(url string) error {
c.t.Errorf("unexpected function call Open(%s)", url)
return nil
}
func (c *zeroClient) OpenCommand(_ context.Context, url, _ string) error {
return c.Open(url)
}

View File

@@ -1,65 +0,0 @@
package keypair
import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"io"
"os"
"strings"
)
// KeyPair represents a pair of certificate and key.
type KeyPair struct {
CertPath string
KeyPath string
CACertPath string
CACertBase64 string
TLSConfig *tls.Config
}
// None represents non-TLS.
var None KeyPair
// Server is a KeyPair for TLS server.
// These files should be generated by Makefile before test.
var Server = KeyPair{
CertPath: "keypair/testdata/server.crt",
KeyPath: "keypair/testdata/server.key",
CACertPath: "keypair/testdata/ca.crt",
CACertBase64: readAsBase64("keypair/testdata/ca.crt"),
TLSConfig: newTLSConfig("keypair/testdata/ca.crt"),
}
func readAsBase64(name string) string {
f, err := os.Open(name)
if err != nil {
panic(err)
}
defer func() {
if err := f.Close(); err != nil {
panic(err)
}
}()
var s strings.Builder
e := base64.NewEncoder(base64.StdEncoding, &s)
if _, err := io.Copy(e, f); err != nil {
panic(err)
}
if err := e.Close(); err != nil {
panic(err)
}
return s.String()
}
func newTLSConfig(name string) *tls.Config {
b, err := os.ReadFile(name)
if err != nil {
panic(err)
}
p := x509.NewCertPool()
if !p.AppendCertsFromPEM(b) {
panic("could not append the CA cert")
}
return &tls.Config{RootCAs: p}
}

View File

@@ -1,26 +0,0 @@
EXPIRY := 3650
OUTPUT_DIR := testdata
TARGETS := ca.key
TARGETS += ca.crt
TARGETS += server.key
TARGETS += server.crt
.PHONY: all
all: $(TARGETS)
ca.key:
openssl genrsa -out $@ 2048
ca.csr: ca.key
openssl req -new -key ca.key -out $@ -subj "/CN=hello-ca" -config openssl.cnf
ca.crt: ca.key ca.csr
openssl x509 -req -in ca.csr -signkey ca.key -out $@ -days $(EXPIRY)
server.key:
openssl genrsa -out $@ 2048
server.csr: openssl.cnf server.key
openssl req -new -key server.key -out $@ -subj "/CN=localhost" -config openssl.cnf
server.crt: openssl.cnf server.csr ca.crt ca.key
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out $@ -sha256 -days $(EXPIRY) -extensions v3_req -extfile openssl.cnf
.PHONY: clean
clean:
-rm -v $(TARGETS)

View File

@@ -1,17 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICojCCAYoCCQCNsdXicWqF2DANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDDAho
ZWxsby1jYTAeFw0yMDAyMDYwMTMzMzNaFw0zMDAyMDMwMTMzMzNaMBMxETAPBgNV
BAMMCGhlbGxvLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcPN
dS88B6ewqVn/m9yO74OLIwNrqMci8l7olP9XlcJhxUZs+3WZQpsSj5nC4yEx8uPQ
bTtBKnXXVDe+8k7OsLTruu9+isTaYk4o/TZbuw/N31ZAiT0pJw8hdypTQyMLbeDr
Vl4bbrfbYywx30DyrHxUkgzOWs459Uwc1wWu0W7M21GY4KENHFE3OAcD58FMvvrh
vgkslATwwW4M2UtXUFJ8XHh26g/J450DU2gwNxpcSdIsvFE6zSyAxU55RElph7mE
ru9cNWAYhCRZvlZQ2VlH7C6JQ3SHyA9RZmBbPpXhtl9zavFkGx2MEwDp/3FmUukR
yJnS2KnAo0QdBeS1LQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQAXGfoDwuKY4TyF
fhKg553Y5I6VDCDc97jyW9yyfc0kvPjQc4EGQV+1eQqMSeh2THpgEEKk9hH/g35a
grPRcBTsEWpbQd16yWyulQeyOtPeWZB2FvAigMaAdMmeXlTs6++gJ6PjPuACa2Jl
nJ/AjCqKFxkn0yEVkPTY0c/I9A12xhCmATqIrQiK1pPowiFxQb4M8Cm0z0AkaJZr
iW0NCOJlLzBqRpFquL4umNaIsxTmOshfM70NpQGRjKREBuK6S0qWsRR0wz4b9Rvi
62qW4zU94q2EDIoCItjHP4twGENXJDC0vLCsKfA5AvbPzszd5/4ifYe2C00Rn7/O
lIxrspMm
-----END CERTIFICATE-----

View File

@@ -1,17 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICrDCCAZQCAQAwEzERMA8GA1UEAwwIaGVsbG8tY2EwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDFw811LzwHp7CpWf+b3I7vg4sjA2uoxyLyXuiU/1eV
wmHFRmz7dZlCmxKPmcLjITHy49BtO0EqdddUN77yTs6wtOu6736KxNpiTij9Nlu7
D83fVkCJPSknDyF3KlNDIwtt4OtWXhtut9tjLDHfQPKsfFSSDM5azjn1TBzXBa7R
bszbUZjgoQ0cUTc4BwPnwUy++uG+CSyUBPDBbgzZS1dQUnxceHbqD8njnQNTaDA3
GlxJ0iy8UTrNLIDFTnlESWmHuYSu71w1YBiEJFm+VlDZWUfsLolDdIfID1FmYFs+
leG2X3Nq8WQbHYwTAOn/cWZS6RHImdLYqcCjRB0F5LUtAgMBAAGgVDBSBgkqhkiG
9w0BCQ4xRTBDMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMBQGA1UdEQQNMAuCCWxv
Y2FsaG9zdDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEA
o/Uthp3NWx0ydTn/GBZI+vA3gI7Qd9UwLvwkeinldRlPM9DOXpG6LR0C40f6u+fj
mXvUDerveYJI8rpo+Ds0UVqy63AH/zZLG7M96L5Nv2KnK40bkfVNez858Yqp1u17
/ci1ZsQIElU5v2qKozaHdQThDVtD5ZZdZoQwLvBLE/Dwpe/4VZZFh8smPMR+Mhcq
+b7gpSy1RiUffk0ZMjuF9Nc9OODdQMTCf+86i0qWXGVzkhfHKAGv+xarHEztcmxF
GUgUYW8DMvYBjEzaGRM1n0aIFkQO6y8SUXvGMIGBSMC4jfBH3ghIXg1+nD6Uah4D
16r+CLjFsDUdn/DK/fIQVw==
-----END CERTIFICATE REQUEST-----

View File

@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAxcPNdS88B6ewqVn/m9yO74OLIwNrqMci8l7olP9XlcJhxUZs
+3WZQpsSj5nC4yEx8uPQbTtBKnXXVDe+8k7OsLTruu9+isTaYk4o/TZbuw/N31ZA
iT0pJw8hdypTQyMLbeDrVl4bbrfbYywx30DyrHxUkgzOWs459Uwc1wWu0W7M21GY
4KENHFE3OAcD58FMvvrhvgkslATwwW4M2UtXUFJ8XHh26g/J450DU2gwNxpcSdIs
vFE6zSyAxU55RElph7mEru9cNWAYhCRZvlZQ2VlH7C6JQ3SHyA9RZmBbPpXhtl9z
avFkGx2MEwDp/3FmUukRyJnS2KnAo0QdBeS1LQIDAQABAoIBAHSoWtMsaMnPNlu/
thM32K0auIGP6/rkdQ3pxGLX+M9jmY7oSzNOHHj4xssklZyroS45CmLU2Ez2tG1+
cMm4iR4dqwxbaBbtpjDlEDLF1PiUiwmadHlANb1PpJsJwZHR41UOn2QUITR/ig+H
K2gZhM0QjkaU/Uj9a5zyJ/UC6iupmgCtj8ij65B49qKMODxV4gqZstRSZiJ3gb6R
TBeR3PUWQS62MZueEQz2eF0eXkXKsFWcbLfHArjfIJ533zW68A0vabgqOhCwJTks
+rkyaKUjEwJJQcgpCfUI4t9HAwICqYtw0fQdDDaMak2XTDFnGHn1/VfSi818U5Sa
jZ+/uIECgYEA4uCd5ISsLWebSv2r5f97N8+WcW0MSJ0XP5MniiMvlaNOtJmoN3H0
PlgR3uwpH9TNWHITZEEb/I93r2E3f24v2018g0uJuwiRDusxHs7yuxw/93Y5rwtG
3twL1k0GyeLCxfM2y2QQU/awXISx7nNk2f0umvTWmjrEw632oQSWCSECgYEA3yaF
zDk7k1u1GdZAh+pQAyUtjzHSvQjiE2JzLaH8BTorACrczRascX5yyMi0QfLYnoYt
UL9dCb4Z8HUclj55kBSx8anvVtf3XAT3hZ3sm8LV3JLGDeURGS8rGdV+tyFk7zw9
XgvaLj3xjB5CoJvtOhbXvqF9M9yp4TMBb5El7o0CgYBTx5Bm15thNPZCrgQxbbOJ
u42ZmyRDGEeCgYvDVhT3VBP3WxqkRt9juk/3GwxgpcuikpWYmvaDwFL5H5RH6V+g
wy9sqJNWzuYKNU2xS8iU0ezJLA5HFon4OBfi7hTIroUwZgzg9LWW2+zqbVHrdQ9T
9Eumiy1ITNVmUTJW6YOiIQKBgBE4BsEAdZFkVTAeMTKLqQrlFoPjI1DE27UFNsAB
rNG2cFT9+bW1ly7WxAKsQgSIuaBZ2CtP6Nz0l0nPr5oEThsJDcYJB9faqFKoa3Ua
/4PxX9E6Xh/6WfxogFno+HMnF4PCUTXtkjNZQkc+moOMJJ0D4DfsfB3BXDZtWiIC
wDuNAoGBAN5ZFTXwE1VpWxIWq5dV9+0aVOP/eB+h7Pt+u2xyRip31FGcpGEzGniu
VmOzWApW7NmvD0QWtstJWlCpsw+rOptAL0oVunlB70KuYuA+2Q5g/Cw3kUrvXqbe
mkCoV21eFAmpU+I6z/n7pUDXPuUsmTkzmwedv0HUuNSQJmHZOMPD
-----END RSA PRIVATE KEY-----

View File

@@ -1 +0,0 @@
9E12C7A1AF348811

View File

@@ -1,14 +0,0 @@
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ req_distinguished_name ]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
extendedKeyUsage = serverAuth
[alt_names]
DNS.1 = localhost

View File

@@ -1,18 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIC7zCCAdegAwIBAgIJAJ4Sx6GvNIgRMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV
BAMMCGhlbGxvLWNhMB4XDTIwMDIwNjAxMzMzM1oXDTMwMDIwMzAxMzMzM1owFDES
MBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA3CJvDbbiiY/o9lgzeABLCtZe56h2w9Dzejy02M6F7yVyphLtJr+/AxI8k5Er
MFRitkzgIUkLn/90CZjPI3k5OnLtUAJ4XLj4gUGjziy4TKyVaU0XK41ZSbDAchbW
349lsEgW5ZnL4qNaZeCvvbYec+RroVc6ZXCcDp4BATTkC3gSVF92+TzrrlbkZ5+W
YVeoMpAPWDPq2zwQx4RcYlpkpI/fzLezRqjRcZJx3FDgkGuwwhzXfVUpxJ/DYLXZ
yHCKyaT7e8YIs8e7TRekOwrLCfssfhJSdWopf6aYRZFV+2ovP0Nggn6XJNh/g1QK
o4wzJAf6v5WMc0jEvb7EuSG+nwIDAQABo0UwQzAJBgNVHRMEAjAAMAsGA1UdDwQE
AwIF4DAUBgNVHREEDTALgglsb2NhbGhvc3QwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
DQYJKoZIhvcNAQELBQADggEBACqlq8b7trNRtKUm1PbY7dnrAFCOnV4OT2R98s17
Q6tmCXM1DvQ101W0ih/lh6iPyU4JM2A0kvO+gizuL/Dmvb6oh+3ox0mMLposptso
gCE1K3SXlvlcLdM6hXRJ5+XwlSCHM6o2Y4yABnKjT6Zr+CMh86a5abDx33hkJ4QR
6I+/iBHVLiCVv0wUF3jD/T+HxinEQrB4cQsSgKmfPClrc8n7rkWWE+MdwEL4VZCH
ufabw178aYibVvJ8k3rushjLlftkDyCNno2rz8YWrnaxabVR0EqSPInyYQenmv2r
/CedVKpmdH2RV8ubkwqa0s6cmpRkyu6FS2g3LviJhmm0nwQ=
-----END CERTIFICATE-----

View File

@@ -1,17 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICrTCCAZUCAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA3CJvDbbiiY/o9lgzeABLCtZe56h2w9Dzejy02M6F
7yVyphLtJr+/AxI8k5ErMFRitkzgIUkLn/90CZjPI3k5OnLtUAJ4XLj4gUGjziy4
TKyVaU0XK41ZSbDAchbW349lsEgW5ZnL4qNaZeCvvbYec+RroVc6ZXCcDp4BATTk
C3gSVF92+TzrrlbkZ5+WYVeoMpAPWDPq2zwQx4RcYlpkpI/fzLezRqjRcZJx3FDg
kGuwwhzXfVUpxJ/DYLXZyHCKyaT7e8YIs8e7TRekOwrLCfssfhJSdWopf6aYRZFV
+2ovP0Nggn6XJNh/g1QKo4wzJAf6v5WMc0jEvb7EuSG+nwIDAQABoFQwUgYJKoZI
hvcNAQkOMUUwQzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAUBgNVHREEDTALggls
b2NhbGhvc3QwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEB
ACUQ0j3XuUZY3fso5tEgOGVjVsTU+C4pIsEw3e1KyIB93i56ANKaq1uBLtnlWtYi
R4RxZ3Sf08GzoEHAHpWoQ4BQ3WGDQjxSdezbudWMuNnNyvyhkh36tmmp/PLA4iZD
Q/d1odzGWg1HMqY0/Q3hfz40MQ9IEBBm+5zKw3tLsKNIKdSdlY7Ul3Z9PUsqsOVW
uF0LKsTMEh1CpbYnOBS2EQjComVM5kYfdQwDNh+BMok8rH7mHFYRmrwYUrU9njsM
eoKqhLkoSu6hw1Cgd9Yru5lC541KfxsSN4Cj6rkm+Qv/5zjvIPxYZ+akSQQR9hR3
2O9THHKqaQS0xmD+y8NjfYk=
-----END CERTIFICATE REQUEST-----

View File

@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA3CJvDbbiiY/o9lgzeABLCtZe56h2w9Dzejy02M6F7yVyphLt
Jr+/AxI8k5ErMFRitkzgIUkLn/90CZjPI3k5OnLtUAJ4XLj4gUGjziy4TKyVaU0X
K41ZSbDAchbW349lsEgW5ZnL4qNaZeCvvbYec+RroVc6ZXCcDp4BATTkC3gSVF92
+TzrrlbkZ5+WYVeoMpAPWDPq2zwQx4RcYlpkpI/fzLezRqjRcZJx3FDgkGuwwhzX
fVUpxJ/DYLXZyHCKyaT7e8YIs8e7TRekOwrLCfssfhJSdWopf6aYRZFV+2ovP0Ng
gn6XJNh/g1QKo4wzJAf6v5WMc0jEvb7EuSG+nwIDAQABAoIBAEYvWFb4C0wurOj2
ABrvhP2Ekaesh4kxMp+zgTlqxzsTJnWarS/gjKcPBm9KJon3La3P3tnd7y3pBXcV
2F0IBl4DTHRpBTUS6HBVnENc8LnJgK2dHZkOLPyYtRLrA0Et+A73PQ2hNmchC+5V
b9K9oQH0Pvim1gCHocnrSIi480hQPazO9/gnHvFtu9Tdsx9kM9jgChhh5VaR+nzM
uw6MpUSzCri3/W6K5Hz8F1lLQcZ6o3vyyFtLPjFrWT4J1UiHqAoxuCGeWTvMbGQl
9Cg0SMX4FLBpbIoMonhM3hb9Cw3FVRGU/1i4oF+gEIGlX/v9CPT5HAZmD59rawc8
11x7yTECgYEA8EYVGKauLHeI1nJdCoVrWI73W1wtwt6prJbzltpoAR4tb3Qu6gBX
JQNd+Ifhl28lK6lPnCGk/SsmfiKSp/XE4IyS1GBd2LpxWj5R50GePCg4hAzk9Xyx
M9SJAFQw73pODgu4RWFXTCOMo9crahJ6X/O+MckHqbFqqohJ8nChGDkCgYEA6oro
Ql/ymO7UdYCay7OlzKeg8ud6XgkZ+wkpSG/5TQ0QZWVN3aSOLztEdVfh3PAqWob0
KgVhLmq6CYwq+HSzU92bvFqCgYUMU8tYzeRboGLxyEdAGL+EW0j8wQyZKYR5cNz4
yM0hpM6kbJ0zZqkYVM/XfRT2RTGwTMakF/FOHZcCgYEAmf4guTriuIcoAWEstniK
Myj16ezrO1Df6Eia+B0kuUqxDhSlmL39HDDLQmU8NYU7in8qEcQSbVwBgKgB3HoM
42nVFR5qJ2RfD9qPPar1klKo3iExgRCYtcJKyBYtgt6dNi1WvcjEXX0PP1bBcWtE
WUjrphbUvXKDDabp1eNPrCkCgYBOw/F2APTeyS4Oe+8AQ8eFcDIMARLGK7ZO6Oe1
TO1jI+UCuD+rFI0vbW7zHV1brkf6+OFcj0vwo6TweeMgZ0il/IFFgvva9UyLg3nC
Q1NGDJR4Fv1+kiqn4V4IkuuI1tVVws/F16XZzA/J7g0KB/WE3fvXJMgDuskjL36C
D+aU5wKBgQCvEg+mJYny1QiR/mQuowX34xf4CkMl7Xq9YDH7W/3AlwuPrPNHaZjh
SvSCz9I8vV0E4ur6atazgCblnvA/G3d4r8YYx+e1l30WJHMgoZRxxHMH74tmUPAj
Klic16BJikSQclMeFdlJqf2UHd37eEuYnxorpep8YGP7/eN1ghHWAw==
-----END RSA PRIVATE KEY-----

View File

@@ -1,4 +0,0 @@
apiVersion: v1
current-context: dummy
kind: Config
preferences: {}

View File

@@ -1,156 +0,0 @@
// Package handler provides HTTP handlers for the OpenID Connect Provider.
package handler
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"testing"
"github.com/int128/kubelogin/integration_test/oidcserver/service"
)
func Register(t *testing.T, mux *http.ServeMux, provider service.Provider) {
h := &Handlers{t, provider}
mux.HandleFunc("GET /.well-known/openid-configuration", h.Discovery)
mux.HandleFunc("GET /certs", h.GetCertificates)
mux.HandleFunc("GET /auth", h.AuthenticateCode)
mux.HandleFunc("POST /token", h.Exchange)
}
// Handlers provides HTTP handlers for the OpenID Connect Provider.
// You need to implement the Provider interface.
// Note that this skips some security checks and is only for testing.
type Handlers struct {
t *testing.T
provider service.Provider
}
func (h *Handlers) handleError(w http.ResponseWriter, r *http.Request, f func() error) {
err := f()
if err == nil {
return
}
if errResp := new(service.ErrorResponse); errors.As(err, &errResp) {
h.t.Logf("400 %s %s: %s", r.Method, r.RequestURI, err)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(400)
e := json.NewEncoder(w)
if err := e.Encode(errResp); err != nil {
h.t.Errorf("idp/handler: could not write the response: %s", err)
}
return
}
h.t.Logf("500 %s %s: %s", r.Method, r.RequestURI, err)
http.Error(w, err.Error(), 500)
}
func (h *Handlers) Discovery(w http.ResponseWriter, r *http.Request) {
h.handleError(w, r, func() error {
discoveryResponse := h.provider.Discovery()
w.Header().Add("Content-Type", "application/json")
e := json.NewEncoder(w)
if err := e.Encode(discoveryResponse); err != nil {
return fmt.Errorf("could not render json: %w", err)
}
return nil
})
}
func (h *Handlers) GetCertificates(w http.ResponseWriter, r *http.Request) {
h.handleError(w, r, func() error {
certificatesResponse := h.provider.GetCertificates()
w.Header().Add("Content-Type", "application/json")
e := json.NewEncoder(w)
if err := e.Encode(certificatesResponse); err != nil {
return fmt.Errorf("could not render json: %w", err)
}
return nil
})
}
func (h *Handlers) AuthenticateCode(w http.ResponseWriter, r *http.Request) {
h.handleError(w, r, func() error {
q := r.URL.Query()
redirectURI, state := q.Get("redirect_uri"), q.Get("state")
code, err := h.provider.AuthenticateCode(service.AuthenticationRequest{
RedirectURI: redirectURI,
State: state,
Scope: q.Get("scope"),
Nonce: q.Get("nonce"),
CodeChallenge: q.Get("code_challenge"),
CodeChallengeMethod: q.Get("code_challenge_method"),
RawQuery: q,
})
if err != nil {
return fmt.Errorf("authentication error: %w", err)
}
redirectTo, err := url.Parse(redirectURI)
if err != nil {
return fmt.Errorf("invalid redirect_uri: %w", err)
}
redirectTo.RawQuery = url.Values{"state": {state}, "code": {code}}.Encode()
http.Redirect(w, r, redirectTo.String(), http.StatusFound)
return nil
})
}
func (h *Handlers) Exchange(w http.ResponseWriter, r *http.Request) {
h.handleError(w, r, func() error {
if err := r.ParseForm(); err != nil {
return fmt.Errorf("could not parse the form: %w", err)
}
grantType := r.Form.Get("grant_type")
switch grantType {
case "authorization_code":
tokenResponse, err := h.provider.Exchange(service.TokenRequest{
Code: r.Form.Get("code"),
CodeVerifier: r.Form.Get("code_verifier"),
})
if err != nil {
return fmt.Errorf("token request error: %w", err)
}
w.Header().Add("Content-Type", "application/json")
e := json.NewEncoder(w)
if err := e.Encode(tokenResponse); err != nil {
return fmt.Errorf("could not render json: %w", err)
}
case "password":
// 4.3. Resource Owner Password Credentials Grant
// https://tools.ietf.org/html/rfc6749#section-4.3
username, password, scope := r.Form.Get("username"), r.Form.Get("password"), r.Form.Get("scope")
tokenResponse, err := h.provider.AuthenticatePassword(username, password, scope)
if err != nil {
return fmt.Errorf("authentication error: %w", err)
}
w.Header().Add("Content-Type", "application/json")
e := json.NewEncoder(w)
if err := e.Encode(tokenResponse); err != nil {
return fmt.Errorf("could not render json: %w", err)
}
case "refresh_token":
// 12.1. Refresh Request
// https://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken
refreshToken := r.Form.Get("refresh_token")
tokenResponse, err := h.provider.Refresh(refreshToken)
if err != nil {
return fmt.Errorf("token refresh error: %w", err)
}
w.Header().Add("Content-Type", "application/json")
e := json.NewEncoder(w)
if err := e.Encode(tokenResponse); err != nil {
return fmt.Errorf("could not render json: %w", err)
}
default:
// 5.2. Error Response
// https://tools.ietf.org/html/rfc6749#section-5.2
return &service.ErrorResponse{
Code: "invalid_grant",
Description: fmt.Sprintf("unknown grant_type %s", grantType),
}
}
return nil
})
}

View File

@@ -1,54 +0,0 @@
// Package oidcserver provides a stub of OpenID Connect provider.
package oidcserver
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/http/httptest"
"testing"
"github.com/int128/kubelogin/integration_test/keypair"
"github.com/int128/kubelogin/integration_test/oidcserver/handler"
"github.com/int128/kubelogin/integration_test/oidcserver/service"
"github.com/int128/kubelogin/integration_test/oidcserver/testconfig"
)
// New starts a server for the OpenID Connect provider.
func New(t *testing.T, kp keypair.KeyPair, config testconfig.Config) service.Service {
mux := http.NewServeMux()
serverURL := startServer(t, mux, kp)
svc := service.New(t, serverURL, config)
handler.Register(t, mux, svc)
return svc
}
func startServer(t *testing.T, h http.Handler, kp keypair.KeyPair) string {
if kp == keypair.None {
srv := httptest.NewServer(h)
t.Cleanup(srv.Close)
return srv.URL
}
// Unfortunately, httptest package did not work with keypair.KeyPair.
// We use httptest package only for allocating a new port.
portAllocator := httptest.NewUnstartedServer(h)
t.Cleanup(portAllocator.Close)
serverURL := fmt.Sprintf("https://localhost:%d", portAllocator.Listener.Addr().(*net.TCPAddr).Port)
srv := &http.Server{Handler: h}
go func() {
err := srv.ServeTLS(portAllocator.Listener, kp.CertPath, kp.KeyPath)
if err != nil && !errors.Is(err, http.ErrServerClosed) {
t.Error(err)
}
}()
t.Cleanup(func() {
if err := srv.Shutdown(context.TODO()); err != nil {
t.Errorf("could not shutdown the server: %s", err)
}
})
return serverURL
}

View File

@@ -1,184 +0,0 @@
package service
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"math/big"
"strings"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/integration_test/oidcserver/testconfig"
testingJWT "github.com/int128/kubelogin/pkg/testing/jwt"
)
func New(t *testing.T, issuerURL string, config testconfig.Config) Service {
return &service{
config: config,
t: t,
issuerURL: issuerURL,
}
}
type service struct {
config testconfig.Config
t *testing.T
issuerURL string
lastAuthenticationRequest *AuthenticationRequest
lastTokenResponse *TokenResponse
}
func (svc *service) IssuerURL() string {
return svc.issuerURL
}
func (svc *service) SetConfig(cfg testconfig.Config) {
svc.config = cfg
}
func (svc *service) LastTokenResponse() *TokenResponse {
return svc.lastTokenResponse
}
func (svc *service) Discovery() *DiscoveryResponse {
// based on https://accounts.google.com/.well-known/openid-configuration
return &DiscoveryResponse{
Issuer: svc.issuerURL,
AuthorizationEndpoint: svc.issuerURL + "/auth",
TokenEndpoint: svc.issuerURL + "/token",
JwksURI: svc.issuerURL + "/certs",
UserinfoEndpoint: svc.issuerURL + "/userinfo",
RevocationEndpoint: svc.issuerURL + "/revoke",
ResponseTypesSupported: []string{"code id_token"},
SubjectTypesSupported: []string{"public"},
IDTokenSigningAlgValuesSupported: []string{"RS256"},
ScopesSupported: []string{"openid", "email", "profile"},
TokenEndpointAuthMethodsSupported: []string{"client_secret_post", "client_secret_basic"},
CodeChallengeMethodsSupported: svc.config.Response.CodeChallengeMethodsSupported,
ClaimsSupported: []string{"aud", "email", "exp", "iat", "iss", "name", "sub"},
}
}
func (svc *service) GetCertificates() *CertificatesResponse {
idTokenKeyPair := testingJWT.PrivateKey
return &CertificatesResponse{
Keys: []*CertificatesResponseKey{
{
Kty: "RSA",
Alg: "RS256",
Use: "sig",
Kid: "dummy",
E: base64.RawURLEncoding.EncodeToString(big.NewInt(int64(idTokenKeyPair.E)).Bytes()),
N: base64.RawURLEncoding.EncodeToString(idTokenKeyPair.N.Bytes()),
},
},
}
}
func (svc *service) AuthenticateCode(req AuthenticationRequest) (code string, err error) {
if req.Scope != svc.config.Want.Scope {
svc.t.Errorf("scope wants `%s` but was `%s`", svc.config.Want.Scope, req.Scope)
}
if !strings.HasPrefix(req.RedirectURI, svc.config.Want.RedirectURIPrefix) {
svc.t.Errorf("redirectURI wants prefix `%s` but was `%s`", svc.config.Want.RedirectURIPrefix, req.RedirectURI)
}
if diff := cmp.Diff(svc.config.Want.CodeChallengeMethod, req.CodeChallengeMethod); diff != "" {
svc.t.Errorf("code_challenge_method mismatch (-want +got):\n%s", diff)
}
for k, v := range svc.config.Want.ExtraParams {
got := req.RawQuery.Get(k)
if got != v {
svc.t.Errorf("parameter %s wants `%s` but was `%s`", k, v, got)
}
}
svc.lastAuthenticationRequest = &req
return "YOUR_AUTH_CODE", nil
}
func (svc *service) Exchange(req TokenRequest) (*TokenResponse, error) {
if req.Code != "YOUR_AUTH_CODE" {
return nil, fmt.Errorf("code wants %s but was %s", "YOUR_AUTH_CODE", req.Code)
}
if svc.lastAuthenticationRequest.CodeChallengeMethod == "S256" {
// https://tools.ietf.org/html/rfc7636#section-4.6
challenge := computeS256Challenge(req.CodeVerifier)
if challenge != svc.lastAuthenticationRequest.CodeChallenge {
svc.t.Errorf("pkce S256 challenge did not match (want %s but was %s)", svc.lastAuthenticationRequest.CodeChallenge, challenge)
}
}
resp := &TokenResponse{
TokenType: "Bearer",
ExpiresIn: 3600,
AccessToken: "YOUR_ACCESS_TOKEN",
RefreshToken: svc.config.Response.RefreshToken,
IDToken: testingJWT.EncodeF(svc.t, func(claims *testingJWT.Claims) {
claims.Issuer = svc.issuerURL
claims.Subject = "SUBJECT"
claims.IssuedAt = jwt.NewNumericDate(svc.config.Response.IDTokenExpiry.Add(-time.Hour))
claims.ExpiresAt = jwt.NewNumericDate(svc.config.Response.IDTokenExpiry)
claims.Audience = []string{"kubernetes"}
claims.Nonce = svc.lastAuthenticationRequest.Nonce
}),
}
svc.lastTokenResponse = resp
return resp, nil
}
func computeS256Challenge(verifier string) string {
c := sha256.Sum256([]byte(verifier))
return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(c[:])
}
func (svc *service) AuthenticatePassword(username, password, scope string) (*TokenResponse, error) {
if scope != svc.config.Want.Scope {
svc.t.Errorf("scope wants `%s` but was `%s`", svc.config.Want.Scope, scope)
}
if username != svc.config.Want.Username {
svc.t.Errorf("username wants `%s` but was `%s`", svc.config.Want.Username, username)
}
if password != svc.config.Want.Password {
svc.t.Errorf("password wants `%s` but was `%s`", svc.config.Want.Password, password)
}
resp := &TokenResponse{
TokenType: "Bearer",
ExpiresIn: 3600,
AccessToken: "YOUR_ACCESS_TOKEN",
RefreshToken: svc.config.Response.RefreshToken,
IDToken: testingJWT.EncodeF(svc.t, func(claims *testingJWT.Claims) {
claims.Issuer = svc.issuerURL
claims.Subject = "SUBJECT"
claims.IssuedAt = jwt.NewNumericDate(svc.config.Response.IDTokenExpiry.Add(-time.Hour))
claims.ExpiresAt = jwt.NewNumericDate(svc.config.Response.IDTokenExpiry)
claims.Audience = []string{"kubernetes"}
}),
}
svc.lastTokenResponse = resp
return resp, nil
}
func (svc *service) Refresh(refreshToken string) (*TokenResponse, error) {
if refreshToken != svc.config.Want.RefreshToken {
svc.t.Errorf("refreshToken wants %s but was %s", svc.config.Want.RefreshToken, refreshToken)
}
if svc.config.Response.RefreshError != "" {
return nil, &ErrorResponse{Code: "invalid_request", Description: svc.config.Response.RefreshError}
}
resp := &TokenResponse{
TokenType: "Bearer",
ExpiresIn: 3600,
AccessToken: "YOUR_ACCESS_TOKEN",
RefreshToken: svc.config.Response.RefreshToken,
IDToken: testingJWT.EncodeF(svc.t, func(claims *testingJWT.Claims) {
claims.Issuer = svc.issuerURL
claims.Subject = "SUBJECT"
claims.IssuedAt = jwt.NewNumericDate(svc.config.Response.IDTokenExpiry.Add(-time.Hour))
claims.ExpiresAt = jwt.NewNumericDate(svc.config.Response.IDTokenExpiry)
claims.Audience = []string{"kubernetes"}
}),
}
svc.lastTokenResponse = resp
return resp, nil
}

View File

@@ -1,108 +0,0 @@
package service
import (
"fmt"
"net/url"
"github.com/int128/kubelogin/integration_test/oidcserver/testconfig"
)
// Service represents the test service of OpenID Connect Provider.
// It provides the feature of Provider and additional methods for testing.
type Service interface {
Provider
IssuerURL() string
SetConfig(config testconfig.Config)
LastTokenResponse() *TokenResponse
}
// Provider represents an OpenID Connect Provider.
//
// If an implemented method returns an ErrorResponse,
// the handler will respond 400 and corresponding json of the ErrorResponse.
// Otherwise, the handler will respond 500 and fail the current test.
type Provider interface {
Discovery() *DiscoveryResponse
GetCertificates() *CertificatesResponse
AuthenticateCode(req AuthenticationRequest) (code string, err error)
Exchange(req TokenRequest) (*TokenResponse, error)
AuthenticatePassword(username, password, scope string) (*TokenResponse, error)
Refresh(refreshToken string) (*TokenResponse, error)
}
// DiscoveryResponse represents the type of:
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse
type DiscoveryResponse struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
RevocationEndpoint string `json:"revocation_endpoint"`
JwksURI string `json:"jwks_uri"`
ResponseTypesSupported []string `json:"response_types_supported"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
ScopesSupported []string `json:"scopes_supported"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
ClaimsSupported []string `json:"claims_supported"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
}
// CertificatesResponse represents the type of:
// https://datatracker.ietf.org/doc/html/rfc7517#section-5
type CertificatesResponse struct {
Keys []*CertificatesResponseKey `json:"keys"`
}
// CertificatesResponseKey represents the type of:
// https://datatracker.ietf.org/doc/html/rfc7517#section-4
type CertificatesResponseKey struct {
Kty string `json:"kty"`
Alg string `json:"alg"`
Use string `json:"use"`
Kid string `json:"kid"`
N string `json:"n"`
E string `json:"e"`
}
// AuthenticationRequest represents the type of:
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
type AuthenticationRequest struct {
RedirectURI string
State string
Scope string // space separated string
Nonce string
CodeChallenge string
CodeChallengeMethod string
RawQuery url.Values
}
// TokenRequest represents the type of:
// https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
type TokenRequest struct {
Code string
CodeVerifier string
}
// TokenResponse represents the type of:
// https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
IDToken string `json:"id_token"`
}
// ErrorResponse represents the error response described in the following section:
// 5.2 Error Response
// https://tools.ietf.org/html/rfc6749#section-5.2
type ErrorResponse struct {
Code string `json:"error"`
Description string `json:"error_description"`
}
func (err *ErrorResponse) Error() string {
return fmt.Sprintf("%s(%s)", err.Code, err.Description)
}

View File

@@ -1,28 +0,0 @@
package testconfig
import "time"
// Want represents a set of expected values.
type Want struct {
Scope string
RedirectURIPrefix string
CodeChallengeMethod string
ExtraParams map[string]string // optional
Username string // optional
Password string // optional
RefreshToken string // optional
}
// Response represents a set of response values.
type Response struct {
IDTokenExpiry time.Time
RefreshToken string
RefreshError string // if set, Refresh() will return the error
CodeChallengeMethodsSupported []string
}
// Config represents a configuration of the OpenID Connect provider.
type Config struct {
Want Want
Response Response
}

View File

@@ -1,307 +0,0 @@
package integration_test
import (
"context"
"os"
"testing"
"time"
"github.com/int128/kubelogin/integration_test/httpdriver"
"github.com/int128/kubelogin/integration_test/keypair"
"github.com/int128/kubelogin/integration_test/kubeconfig"
"github.com/int128/kubelogin/integration_test/oidcserver"
"github.com/int128/kubelogin/integration_test/oidcserver/testconfig"
"github.com/int128/kubelogin/pkg/di"
"github.com/int128/kubelogin/pkg/infrastructure/browser"
"github.com/int128/kubelogin/pkg/testing/clock"
"github.com/int128/kubelogin/pkg/testing/logger"
)
// Run the integration tests of the Login use-case.
//
// 1. Start the auth server.
// 2. Run the Cmd.
// 3. Open a request for the local server.
// 4. Verify the kubeconfig.
func TestStandalone(t *testing.T) {
timeout := 3 * time.Second
now := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
for name, tc := range map[string]struct {
keyPair keypair.KeyPair
args []string
}{
"NoTLS": {},
"TLS": {
keyPair: keypair.Server,
},
} {
httpDriverOption := httpdriver.Config{
TLSConfig: tc.keyPair.TLSConfig,
BodyContains: "Authenticated",
}
t.Run(name, func(t *testing.T) {
t.Run("AuthCode", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
sv := oidcserver.New(t, tc.keyPair, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
},
})
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: sv.IssuerURL(),
IDPCertificateAuthority: tc.keyPair.CACertPath,
})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.New(ctx, t, httpDriverOption),
now: now,
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: sv.LastTokenResponse().RefreshToken,
})
})
t.Run("ROPC", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
sv := oidcserver.New(t, tc.keyPair, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
Username: "USER1",
Password: "PASS1",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
},
})
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: sv.IssuerURL(),
IDPCertificateAuthority: tc.keyPair.CACertPath,
})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.Zero(t),
now: now,
args: []string{
"--username", "USER1",
"--password", "PASS1",
},
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: sv.LastTokenResponse().RefreshToken,
})
})
t.Run("TokenLifecycle", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
sv := oidcserver.New(t, tc.keyPair, testconfig.Config{})
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: sv.IssuerURL(),
IDPCertificateAuthority: tc.keyPair.CACertPath,
})
t.Run("NoToken", func(t *testing.T) {
sv.SetConfig(testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
RefreshToken: "REFRESH_TOKEN_1",
},
})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.New(ctx, t, httpDriverOption),
now: now,
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: "REFRESH_TOKEN_1",
})
})
t.Run("Valid", func(t *testing.T) {
sv.SetConfig(testconfig.Config{})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.Zero(t),
now: now,
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: "REFRESH_TOKEN_1",
})
})
t.Run("Refresh", func(t *testing.T) {
sv.SetConfig(testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
RefreshToken: "REFRESH_TOKEN_1",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(3 * time.Hour),
RefreshToken: "REFRESH_TOKEN_2",
},
})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.New(ctx, t, httpDriverOption),
now: now.Add(2 * time.Hour),
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: "REFRESH_TOKEN_2",
})
})
t.Run("RefreshAgain", func(t *testing.T) {
sv.SetConfig(testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
RefreshToken: "REFRESH_TOKEN_2",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(5 * time.Hour),
},
})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.New(ctx, t, httpDriverOption),
now: now.Add(4 * time.Hour),
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: "REFRESH_TOKEN_2",
})
})
})
})
}
t.Run("TLSData", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
sv := oidcserver.New(t, keypair.Server, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
},
})
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: sv.IssuerURL(),
IDPCertificateAuthorityData: keypair.Server.CACertBase64,
})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{TLSConfig: keypair.Server.TLSConfig}),
now: now,
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: sv.LastTokenResponse().RefreshToken,
})
})
t.Run("env_KUBECONFIG", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
sv := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "openid",
RedirectURIPrefix: "http://localhost:",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
},
})
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: sv.IssuerURL(),
})
t.Setenv("KUBECONFIG", kubeConfigFilename+string(os.PathListSeparator)+"kubeconfig/testdata/dummy.yaml")
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{}),
now: now,
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: sv.LastTokenResponse().RefreshToken,
})
})
t.Run("ExtraScopes", func(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
sv := oidcserver.New(t, keypair.None, testconfig.Config{
Want: testconfig.Want{
Scope: "profile groups openid",
RedirectURIPrefix: "http://localhost:",
},
Response: testconfig.Response{
IDTokenExpiry: now.Add(time.Hour),
},
})
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
Issuer: sv.IssuerURL(),
ExtraScopes: "profile,groups",
})
runStandalone(t, ctx, standaloneConfig{
issuerURL: sv.IssuerURL(),
kubeConfigFilename: kubeConfigFilename,
httpDriver: httpdriver.New(ctx, t, httpdriver.Config{}),
now: now,
})
kubeconfig.Verify(t, kubeConfigFilename, kubeconfig.AuthProviderConfig{
IDToken: sv.LastTokenResponse().IDToken,
RefreshToken: sv.LastTokenResponse().RefreshToken,
})
})
}
type standaloneConfig struct {
issuerURL string
kubeConfigFilename string
httpDriver browser.Interface
now time.Time
args []string
}
func runStandalone(t *testing.T, ctx context.Context, cfg standaloneConfig) {
cmd := di.NewCmdForHeadless(clock.Fake(cfg.now), os.Stdin, os.Stdout, logger.New(t), cfg.httpDriver)
exitCode := cmd.Run(ctx, append([]string{
"kubelogin",
"--kubeconfig", cfg.kubeConfigFilename,
"--listen-address", "127.0.0.1:0",
}, cfg.args...), "HEAD")
if exitCode != 0 {
t.Errorf("exit status wants 0 but %d", exitCode)
}
}

80
kubeconfig/auth.go Normal file
View File

@@ -0,0 +1,80 @@
package kubeconfig
import (
"strings"
"github.com/pkg/errors"
"k8s.io/client-go/tools/clientcmd/api"
)
// FindOIDCAuthProvider returns the current OIDC authProvider.
// If the context, auth-info or auth-provider does not exist, this returns an error.
// If auth-provider is not "oidc", this returns an error.
func FindOIDCAuthProvider(config *api.Config) (*OIDCAuthProvider, error) {
context := config.Contexts[config.CurrentContext]
if context == nil {
return nil, errors.Errorf("context %s does not exist", config.CurrentContext)
}
authInfo := config.AuthInfos[context.AuthInfo]
if authInfo == nil {
return nil, errors.Errorf("auth-info %s does not exist", context.AuthInfo)
}
if authInfo.AuthProvider == nil {
return nil, errors.Errorf("auth-provider is not set")
}
if authInfo.AuthProvider.Name != "oidc" {
return nil, errors.Errorf("auth-provider name is %s but must be oidc", authInfo.AuthProvider.Name)
}
return (*OIDCAuthProvider)(authInfo.AuthProvider), nil
}
// OIDCAuthProvider represents OIDC configuration in the kubeconfig.
type OIDCAuthProvider api.AuthProviderConfig
// IDPIssuerURL returns the idp-issuer-url.
func (c *OIDCAuthProvider) IDPIssuerURL() string {
return c.Config["idp-issuer-url"]
}
// ClientID returns the client-id.
func (c *OIDCAuthProvider) ClientID() string {
return c.Config["client-id"]
}
// ClientSecret returns the client-secret.
func (c *OIDCAuthProvider) ClientSecret() string {
return c.Config["client-secret"]
}
// IDPCertificateAuthority returns the idp-certificate-authority.
func (c *OIDCAuthProvider) IDPCertificateAuthority() string {
return c.Config["idp-certificate-authority"]
}
// IDPCertificateAuthorityData returns the idp-certificate-authority-data.
func (c *OIDCAuthProvider) IDPCertificateAuthorityData() string {
return c.Config["idp-certificate-authority-data"]
}
// ExtraScopes returns the extra-scopes.
func (c *OIDCAuthProvider) ExtraScopes() []string {
if c.Config["extra-scopes"] == "" {
return []string{}
}
return strings.Split(c.Config["extra-scopes"], ",")
}
// IDToken returns the id-token.
func (c *OIDCAuthProvider) IDToken() string {
return c.Config["id-token"]
}
// SetIDToken replaces the id-token.
func (c *OIDCAuthProvider) SetIDToken(idToken string) {
c.Config["id-token"] = idToken
}
// SetRefreshToken replaces the refresh-token.
func (c *OIDCAuthProvider) SetRefreshToken(refreshToken string) {
c.Config["refresh-token"] = refreshToken
}

15
kubelogin.rb Normal file
View File

@@ -0,0 +1,15 @@
class Kubelogin < Formula
desc "A kubectl plugin for Kubernetes OpenID Connect authentication"
homepage "https://github.com/int128/kubelogin"
url "https://github.com/int128/kubelogin/releases/download/{{ env "VERSION" }}/kubelogin_darwin_amd64.zip"
version "{{ env "VERSION" }}"
sha256 "{{ .darwin_amd64_zip_sha256 }}"
def install
bin.install "kubelogin" => "kubelogin"
ln_s bin/"kubelogin", bin/"kubectl-oidc_login"
end
test do
system "#{bin}/kubelogin -h"
system "#{bin}/kubectl-oidc_login -h"
end
end

15
main.go
View File

@@ -2,18 +2,19 @@ package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/int128/kubelogin/pkg/di"
"github.com/int128/kubelogin/adaptors/interfaces"
"github.com/int128/kubelogin/di"
)
var version = "HEAD"
func main() {
ctx := context.Background()
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
defer stop()
os.Exit(di.NewCmd().Run(ctx, os.Args, version))
if err := di.Invoke(func(cmd adaptors.Cmd) {
os.Exit(cmd.Run(context.Background(), os.Args, version))
}); err != nil {
log.Fatalf("Error: %s", err)
}
}

View File

@@ -1,895 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package service_mock
import (
"github.com/int128/kubelogin/integration_test/oidcserver/service"
"github.com/int128/kubelogin/integration_test/oidcserver/testconfig"
mock "github.com/stretchr/testify/mock"
)
// NewMockService creates a new instance of MockService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockService(t interface {
mock.TestingT
Cleanup(func())
}) *MockService {
mock := &MockService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockService is an autogenerated mock type for the Service type
type MockService struct {
mock.Mock
}
type MockService_Expecter struct {
mock *mock.Mock
}
func (_m *MockService) EXPECT() *MockService_Expecter {
return &MockService_Expecter{mock: &_m.Mock}
}
// AuthenticateCode provides a mock function for the type MockService
func (_mock *MockService) AuthenticateCode(req service.AuthenticationRequest) (string, error) {
ret := _mock.Called(req)
if len(ret) == 0 {
panic("no return value specified for AuthenticateCode")
}
var r0 string
var r1 error
if returnFunc, ok := ret.Get(0).(func(service.AuthenticationRequest) (string, error)); ok {
return returnFunc(req)
}
if returnFunc, ok := ret.Get(0).(func(service.AuthenticationRequest) string); ok {
r0 = returnFunc(req)
} else {
r0 = ret.Get(0).(string)
}
if returnFunc, ok := ret.Get(1).(func(service.AuthenticationRequest) error); ok {
r1 = returnFunc(req)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockService_AuthenticateCode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AuthenticateCode'
type MockService_AuthenticateCode_Call struct {
*mock.Call
}
// AuthenticateCode is a helper method to define mock.On call
// - req service.AuthenticationRequest
func (_e *MockService_Expecter) AuthenticateCode(req interface{}) *MockService_AuthenticateCode_Call {
return &MockService_AuthenticateCode_Call{Call: _e.mock.On("AuthenticateCode", req)}
}
func (_c *MockService_AuthenticateCode_Call) Run(run func(req service.AuthenticationRequest)) *MockService_AuthenticateCode_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 service.AuthenticationRequest
if args[0] != nil {
arg0 = args[0].(service.AuthenticationRequest)
}
run(
arg0,
)
})
return _c
}
func (_c *MockService_AuthenticateCode_Call) Return(code string, err error) *MockService_AuthenticateCode_Call {
_c.Call.Return(code, err)
return _c
}
func (_c *MockService_AuthenticateCode_Call) RunAndReturn(run func(req service.AuthenticationRequest) (string, error)) *MockService_AuthenticateCode_Call {
_c.Call.Return(run)
return _c
}
// AuthenticatePassword provides a mock function for the type MockService
func (_mock *MockService) AuthenticatePassword(username string, password string, scope string) (*service.TokenResponse, error) {
ret := _mock.Called(username, password, scope)
if len(ret) == 0 {
panic("no return value specified for AuthenticatePassword")
}
var r0 *service.TokenResponse
var r1 error
if returnFunc, ok := ret.Get(0).(func(string, string, string) (*service.TokenResponse, error)); ok {
return returnFunc(username, password, scope)
}
if returnFunc, ok := ret.Get(0).(func(string, string, string) *service.TokenResponse); ok {
r0 = returnFunc(username, password, scope)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.TokenResponse)
}
}
if returnFunc, ok := ret.Get(1).(func(string, string, string) error); ok {
r1 = returnFunc(username, password, scope)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockService_AuthenticatePassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AuthenticatePassword'
type MockService_AuthenticatePassword_Call struct {
*mock.Call
}
// AuthenticatePassword is a helper method to define mock.On call
// - username string
// - password string
// - scope string
func (_e *MockService_Expecter) AuthenticatePassword(username interface{}, password interface{}, scope interface{}) *MockService_AuthenticatePassword_Call {
return &MockService_AuthenticatePassword_Call{Call: _e.mock.On("AuthenticatePassword", username, password, scope)}
}
func (_c *MockService_AuthenticatePassword_Call) Run(run func(username string, password string, scope string)) *MockService_AuthenticatePassword_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
var arg2 string
if args[2] != nil {
arg2 = args[2].(string)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockService_AuthenticatePassword_Call) Return(tokenResponse *service.TokenResponse, err error) *MockService_AuthenticatePassword_Call {
_c.Call.Return(tokenResponse, err)
return _c
}
func (_c *MockService_AuthenticatePassword_Call) RunAndReturn(run func(username string, password string, scope string) (*service.TokenResponse, error)) *MockService_AuthenticatePassword_Call {
_c.Call.Return(run)
return _c
}
// Discovery provides a mock function for the type MockService
func (_mock *MockService) Discovery() *service.DiscoveryResponse {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Discovery")
}
var r0 *service.DiscoveryResponse
if returnFunc, ok := ret.Get(0).(func() *service.DiscoveryResponse); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.DiscoveryResponse)
}
}
return r0
}
// MockService_Discovery_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Discovery'
type MockService_Discovery_Call struct {
*mock.Call
}
// Discovery is a helper method to define mock.On call
func (_e *MockService_Expecter) Discovery() *MockService_Discovery_Call {
return &MockService_Discovery_Call{Call: _e.mock.On("Discovery")}
}
func (_c *MockService_Discovery_Call) Run(run func()) *MockService_Discovery_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockService_Discovery_Call) Return(discoveryResponse *service.DiscoveryResponse) *MockService_Discovery_Call {
_c.Call.Return(discoveryResponse)
return _c
}
func (_c *MockService_Discovery_Call) RunAndReturn(run func() *service.DiscoveryResponse) *MockService_Discovery_Call {
_c.Call.Return(run)
return _c
}
// Exchange provides a mock function for the type MockService
func (_mock *MockService) Exchange(req service.TokenRequest) (*service.TokenResponse, error) {
ret := _mock.Called(req)
if len(ret) == 0 {
panic("no return value specified for Exchange")
}
var r0 *service.TokenResponse
var r1 error
if returnFunc, ok := ret.Get(0).(func(service.TokenRequest) (*service.TokenResponse, error)); ok {
return returnFunc(req)
}
if returnFunc, ok := ret.Get(0).(func(service.TokenRequest) *service.TokenResponse); ok {
r0 = returnFunc(req)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.TokenResponse)
}
}
if returnFunc, ok := ret.Get(1).(func(service.TokenRequest) error); ok {
r1 = returnFunc(req)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockService_Exchange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Exchange'
type MockService_Exchange_Call struct {
*mock.Call
}
// Exchange is a helper method to define mock.On call
// - req service.TokenRequest
func (_e *MockService_Expecter) Exchange(req interface{}) *MockService_Exchange_Call {
return &MockService_Exchange_Call{Call: _e.mock.On("Exchange", req)}
}
func (_c *MockService_Exchange_Call) Run(run func(req service.TokenRequest)) *MockService_Exchange_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 service.TokenRequest
if args[0] != nil {
arg0 = args[0].(service.TokenRequest)
}
run(
arg0,
)
})
return _c
}
func (_c *MockService_Exchange_Call) Return(tokenResponse *service.TokenResponse, err error) *MockService_Exchange_Call {
_c.Call.Return(tokenResponse, err)
return _c
}
func (_c *MockService_Exchange_Call) RunAndReturn(run func(req service.TokenRequest) (*service.TokenResponse, error)) *MockService_Exchange_Call {
_c.Call.Return(run)
return _c
}
// GetCertificates provides a mock function for the type MockService
func (_mock *MockService) GetCertificates() *service.CertificatesResponse {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetCertificates")
}
var r0 *service.CertificatesResponse
if returnFunc, ok := ret.Get(0).(func() *service.CertificatesResponse); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.CertificatesResponse)
}
}
return r0
}
// MockService_GetCertificates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCertificates'
type MockService_GetCertificates_Call struct {
*mock.Call
}
// GetCertificates is a helper method to define mock.On call
func (_e *MockService_Expecter) GetCertificates() *MockService_GetCertificates_Call {
return &MockService_GetCertificates_Call{Call: _e.mock.On("GetCertificates")}
}
func (_c *MockService_GetCertificates_Call) Run(run func()) *MockService_GetCertificates_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockService_GetCertificates_Call) Return(certificatesResponse *service.CertificatesResponse) *MockService_GetCertificates_Call {
_c.Call.Return(certificatesResponse)
return _c
}
func (_c *MockService_GetCertificates_Call) RunAndReturn(run func() *service.CertificatesResponse) *MockService_GetCertificates_Call {
_c.Call.Return(run)
return _c
}
// IssuerURL provides a mock function for the type MockService
func (_mock *MockService) IssuerURL() string {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for IssuerURL")
}
var r0 string
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// MockService_IssuerURL_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IssuerURL'
type MockService_IssuerURL_Call struct {
*mock.Call
}
// IssuerURL is a helper method to define mock.On call
func (_e *MockService_Expecter) IssuerURL() *MockService_IssuerURL_Call {
return &MockService_IssuerURL_Call{Call: _e.mock.On("IssuerURL")}
}
func (_c *MockService_IssuerURL_Call) Run(run func()) *MockService_IssuerURL_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockService_IssuerURL_Call) Return(s string) *MockService_IssuerURL_Call {
_c.Call.Return(s)
return _c
}
func (_c *MockService_IssuerURL_Call) RunAndReturn(run func() string) *MockService_IssuerURL_Call {
_c.Call.Return(run)
return _c
}
// LastTokenResponse provides a mock function for the type MockService
func (_mock *MockService) LastTokenResponse() *service.TokenResponse {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for LastTokenResponse")
}
var r0 *service.TokenResponse
if returnFunc, ok := ret.Get(0).(func() *service.TokenResponse); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.TokenResponse)
}
}
return r0
}
// MockService_LastTokenResponse_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LastTokenResponse'
type MockService_LastTokenResponse_Call struct {
*mock.Call
}
// LastTokenResponse is a helper method to define mock.On call
func (_e *MockService_Expecter) LastTokenResponse() *MockService_LastTokenResponse_Call {
return &MockService_LastTokenResponse_Call{Call: _e.mock.On("LastTokenResponse")}
}
func (_c *MockService_LastTokenResponse_Call) Run(run func()) *MockService_LastTokenResponse_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockService_LastTokenResponse_Call) Return(tokenResponse *service.TokenResponse) *MockService_LastTokenResponse_Call {
_c.Call.Return(tokenResponse)
return _c
}
func (_c *MockService_LastTokenResponse_Call) RunAndReturn(run func() *service.TokenResponse) *MockService_LastTokenResponse_Call {
_c.Call.Return(run)
return _c
}
// Refresh provides a mock function for the type MockService
func (_mock *MockService) Refresh(refreshToken string) (*service.TokenResponse, error) {
ret := _mock.Called(refreshToken)
if len(ret) == 0 {
panic("no return value specified for Refresh")
}
var r0 *service.TokenResponse
var r1 error
if returnFunc, ok := ret.Get(0).(func(string) (*service.TokenResponse, error)); ok {
return returnFunc(refreshToken)
}
if returnFunc, ok := ret.Get(0).(func(string) *service.TokenResponse); ok {
r0 = returnFunc(refreshToken)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.TokenResponse)
}
}
if returnFunc, ok := ret.Get(1).(func(string) error); ok {
r1 = returnFunc(refreshToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockService_Refresh_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Refresh'
type MockService_Refresh_Call struct {
*mock.Call
}
// Refresh is a helper method to define mock.On call
// - refreshToken string
func (_e *MockService_Expecter) Refresh(refreshToken interface{}) *MockService_Refresh_Call {
return &MockService_Refresh_Call{Call: _e.mock.On("Refresh", refreshToken)}
}
func (_c *MockService_Refresh_Call) Run(run func(refreshToken string)) *MockService_Refresh_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
run(
arg0,
)
})
return _c
}
func (_c *MockService_Refresh_Call) Return(tokenResponse *service.TokenResponse, err error) *MockService_Refresh_Call {
_c.Call.Return(tokenResponse, err)
return _c
}
func (_c *MockService_Refresh_Call) RunAndReturn(run func(refreshToken string) (*service.TokenResponse, error)) *MockService_Refresh_Call {
_c.Call.Return(run)
return _c
}
// SetConfig provides a mock function for the type MockService
func (_mock *MockService) SetConfig(config testconfig.Config) {
_mock.Called(config)
return
}
// MockService_SetConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetConfig'
type MockService_SetConfig_Call struct {
*mock.Call
}
// SetConfig is a helper method to define mock.On call
// - config testconfig.Config
func (_e *MockService_Expecter) SetConfig(config interface{}) *MockService_SetConfig_Call {
return &MockService_SetConfig_Call{Call: _e.mock.On("SetConfig", config)}
}
func (_c *MockService_SetConfig_Call) Run(run func(config testconfig.Config)) *MockService_SetConfig_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 testconfig.Config
if args[0] != nil {
arg0 = args[0].(testconfig.Config)
}
run(
arg0,
)
})
return _c
}
func (_c *MockService_SetConfig_Call) Return() *MockService_SetConfig_Call {
_c.Call.Return()
return _c
}
func (_c *MockService_SetConfig_Call) RunAndReturn(run func(config testconfig.Config)) *MockService_SetConfig_Call {
_c.Run(run)
return _c
}
// NewMockProvider creates a new instance of MockProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockProvider(t interface {
mock.TestingT
Cleanup(func())
}) *MockProvider {
mock := &MockProvider{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockProvider is an autogenerated mock type for the Provider type
type MockProvider struct {
mock.Mock
}
type MockProvider_Expecter struct {
mock *mock.Mock
}
func (_m *MockProvider) EXPECT() *MockProvider_Expecter {
return &MockProvider_Expecter{mock: &_m.Mock}
}
// AuthenticateCode provides a mock function for the type MockProvider
func (_mock *MockProvider) AuthenticateCode(req service.AuthenticationRequest) (string, error) {
ret := _mock.Called(req)
if len(ret) == 0 {
panic("no return value specified for AuthenticateCode")
}
var r0 string
var r1 error
if returnFunc, ok := ret.Get(0).(func(service.AuthenticationRequest) (string, error)); ok {
return returnFunc(req)
}
if returnFunc, ok := ret.Get(0).(func(service.AuthenticationRequest) string); ok {
r0 = returnFunc(req)
} else {
r0 = ret.Get(0).(string)
}
if returnFunc, ok := ret.Get(1).(func(service.AuthenticationRequest) error); ok {
r1 = returnFunc(req)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProvider_AuthenticateCode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AuthenticateCode'
type MockProvider_AuthenticateCode_Call struct {
*mock.Call
}
// AuthenticateCode is a helper method to define mock.On call
// - req service.AuthenticationRequest
func (_e *MockProvider_Expecter) AuthenticateCode(req interface{}) *MockProvider_AuthenticateCode_Call {
return &MockProvider_AuthenticateCode_Call{Call: _e.mock.On("AuthenticateCode", req)}
}
func (_c *MockProvider_AuthenticateCode_Call) Run(run func(req service.AuthenticationRequest)) *MockProvider_AuthenticateCode_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 service.AuthenticationRequest
if args[0] != nil {
arg0 = args[0].(service.AuthenticationRequest)
}
run(
arg0,
)
})
return _c
}
func (_c *MockProvider_AuthenticateCode_Call) Return(code string, err error) *MockProvider_AuthenticateCode_Call {
_c.Call.Return(code, err)
return _c
}
func (_c *MockProvider_AuthenticateCode_Call) RunAndReturn(run func(req service.AuthenticationRequest) (string, error)) *MockProvider_AuthenticateCode_Call {
_c.Call.Return(run)
return _c
}
// AuthenticatePassword provides a mock function for the type MockProvider
func (_mock *MockProvider) AuthenticatePassword(username string, password string, scope string) (*service.TokenResponse, error) {
ret := _mock.Called(username, password, scope)
if len(ret) == 0 {
panic("no return value specified for AuthenticatePassword")
}
var r0 *service.TokenResponse
var r1 error
if returnFunc, ok := ret.Get(0).(func(string, string, string) (*service.TokenResponse, error)); ok {
return returnFunc(username, password, scope)
}
if returnFunc, ok := ret.Get(0).(func(string, string, string) *service.TokenResponse); ok {
r0 = returnFunc(username, password, scope)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.TokenResponse)
}
}
if returnFunc, ok := ret.Get(1).(func(string, string, string) error); ok {
r1 = returnFunc(username, password, scope)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProvider_AuthenticatePassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AuthenticatePassword'
type MockProvider_AuthenticatePassword_Call struct {
*mock.Call
}
// AuthenticatePassword is a helper method to define mock.On call
// - username string
// - password string
// - scope string
func (_e *MockProvider_Expecter) AuthenticatePassword(username interface{}, password interface{}, scope interface{}) *MockProvider_AuthenticatePassword_Call {
return &MockProvider_AuthenticatePassword_Call{Call: _e.mock.On("AuthenticatePassword", username, password, scope)}
}
func (_c *MockProvider_AuthenticatePassword_Call) Run(run func(username string, password string, scope string)) *MockProvider_AuthenticatePassword_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
var arg2 string
if args[2] != nil {
arg2 = args[2].(string)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockProvider_AuthenticatePassword_Call) Return(tokenResponse *service.TokenResponse, err error) *MockProvider_AuthenticatePassword_Call {
_c.Call.Return(tokenResponse, err)
return _c
}
func (_c *MockProvider_AuthenticatePassword_Call) RunAndReturn(run func(username string, password string, scope string) (*service.TokenResponse, error)) *MockProvider_AuthenticatePassword_Call {
_c.Call.Return(run)
return _c
}
// Discovery provides a mock function for the type MockProvider
func (_mock *MockProvider) Discovery() *service.DiscoveryResponse {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Discovery")
}
var r0 *service.DiscoveryResponse
if returnFunc, ok := ret.Get(0).(func() *service.DiscoveryResponse); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.DiscoveryResponse)
}
}
return r0
}
// MockProvider_Discovery_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Discovery'
type MockProvider_Discovery_Call struct {
*mock.Call
}
// Discovery is a helper method to define mock.On call
func (_e *MockProvider_Expecter) Discovery() *MockProvider_Discovery_Call {
return &MockProvider_Discovery_Call{Call: _e.mock.On("Discovery")}
}
func (_c *MockProvider_Discovery_Call) Run(run func()) *MockProvider_Discovery_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockProvider_Discovery_Call) Return(discoveryResponse *service.DiscoveryResponse) *MockProvider_Discovery_Call {
_c.Call.Return(discoveryResponse)
return _c
}
func (_c *MockProvider_Discovery_Call) RunAndReturn(run func() *service.DiscoveryResponse) *MockProvider_Discovery_Call {
_c.Call.Return(run)
return _c
}
// Exchange provides a mock function for the type MockProvider
func (_mock *MockProvider) Exchange(req service.TokenRequest) (*service.TokenResponse, error) {
ret := _mock.Called(req)
if len(ret) == 0 {
panic("no return value specified for Exchange")
}
var r0 *service.TokenResponse
var r1 error
if returnFunc, ok := ret.Get(0).(func(service.TokenRequest) (*service.TokenResponse, error)); ok {
return returnFunc(req)
}
if returnFunc, ok := ret.Get(0).(func(service.TokenRequest) *service.TokenResponse); ok {
r0 = returnFunc(req)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.TokenResponse)
}
}
if returnFunc, ok := ret.Get(1).(func(service.TokenRequest) error); ok {
r1 = returnFunc(req)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProvider_Exchange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Exchange'
type MockProvider_Exchange_Call struct {
*mock.Call
}
// Exchange is a helper method to define mock.On call
// - req service.TokenRequest
func (_e *MockProvider_Expecter) Exchange(req interface{}) *MockProvider_Exchange_Call {
return &MockProvider_Exchange_Call{Call: _e.mock.On("Exchange", req)}
}
func (_c *MockProvider_Exchange_Call) Run(run func(req service.TokenRequest)) *MockProvider_Exchange_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 service.TokenRequest
if args[0] != nil {
arg0 = args[0].(service.TokenRequest)
}
run(
arg0,
)
})
return _c
}
func (_c *MockProvider_Exchange_Call) Return(tokenResponse *service.TokenResponse, err error) *MockProvider_Exchange_Call {
_c.Call.Return(tokenResponse, err)
return _c
}
func (_c *MockProvider_Exchange_Call) RunAndReturn(run func(req service.TokenRequest) (*service.TokenResponse, error)) *MockProvider_Exchange_Call {
_c.Call.Return(run)
return _c
}
// GetCertificates provides a mock function for the type MockProvider
func (_mock *MockProvider) GetCertificates() *service.CertificatesResponse {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetCertificates")
}
var r0 *service.CertificatesResponse
if returnFunc, ok := ret.Get(0).(func() *service.CertificatesResponse); ok {
r0 = returnFunc()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.CertificatesResponse)
}
}
return r0
}
// MockProvider_GetCertificates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCertificates'
type MockProvider_GetCertificates_Call struct {
*mock.Call
}
// GetCertificates is a helper method to define mock.On call
func (_e *MockProvider_Expecter) GetCertificates() *MockProvider_GetCertificates_Call {
return &MockProvider_GetCertificates_Call{Call: _e.mock.On("GetCertificates")}
}
func (_c *MockProvider_GetCertificates_Call) Run(run func()) *MockProvider_GetCertificates_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockProvider_GetCertificates_Call) Return(certificatesResponse *service.CertificatesResponse) *MockProvider_GetCertificates_Call {
_c.Call.Return(certificatesResponse)
return _c
}
func (_c *MockProvider_GetCertificates_Call) RunAndReturn(run func() *service.CertificatesResponse) *MockProvider_GetCertificates_Call {
_c.Call.Return(run)
return _c
}
// Refresh provides a mock function for the type MockProvider
func (_mock *MockProvider) Refresh(refreshToken string) (*service.TokenResponse, error) {
ret := _mock.Called(refreshToken)
if len(ret) == 0 {
panic("no return value specified for Refresh")
}
var r0 *service.TokenResponse
var r1 error
if returnFunc, ok := ret.Get(0).(func(string) (*service.TokenResponse, error)); ok {
return returnFunc(refreshToken)
}
if returnFunc, ok := ret.Get(0).(func(string) *service.TokenResponse); ok {
r0 = returnFunc(refreshToken)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*service.TokenResponse)
}
}
if returnFunc, ok := ret.Get(1).(func(string) error); ok {
r1 = returnFunc(refreshToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProvider_Refresh_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Refresh'
type MockProvider_Refresh_Call struct {
*mock.Call
}
// Refresh is a helper method to define mock.On call
// - refreshToken string
func (_e *MockProvider_Expecter) Refresh(refreshToken interface{}) *MockProvider_Refresh_Call {
return &MockProvider_Refresh_Call{Call: _e.mock.On("Refresh", refreshToken)}
}
func (_c *MockProvider_Refresh_Call) Run(run func(refreshToken string)) *MockProvider_Refresh_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
run(
arg0,
)
})
return _c
}
func (_c *MockProvider_Refresh_Call) Return(tokenResponse *service.TokenResponse, err error) *MockProvider_Refresh_Call {
_c.Call.Return(tokenResponse, err)
return _c
}
func (_c *MockProvider_Refresh_Call) RunAndReturn(run func(refreshToken string) (*service.TokenResponse, error)) *MockProvider_Refresh_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,101 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package cmd_mock
import (
"context"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Run provides a mock function for the type MockInterface
func (_mock *MockInterface) Run(ctx context.Context, args []string, version string) int {
ret := _mock.Called(ctx, args, version)
if len(ret) == 0 {
panic("no return value specified for Run")
}
var r0 int
if returnFunc, ok := ret.Get(0).(func(context.Context, []string, string) int); ok {
r0 = returnFunc(ctx, args, version)
} else {
r0 = ret.Get(0).(int)
}
return r0
}
// MockInterface_Run_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Run'
type MockInterface_Run_Call struct {
*mock.Call
}
// Run is a helper method to define mock.On call
// - ctx context.Context
// - args []string
// - version string
func (_e *MockInterface_Expecter) Run(ctx interface{}, args interface{}, version interface{}) *MockInterface_Run_Call {
return &MockInterface_Run_Call{Call: _e.mock.On("Run", ctx, args, version)}
}
func (_c *MockInterface_Run_Call) Run(run func(ctx context.Context, args []string, version string)) *MockInterface_Run_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 []string
if args[1] != nil {
arg1 = args[1].([]string)
}
var arg2 string
if args[2] != nil {
arg2 = args[2].(string)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockInterface_Run_Call) Return(n int) *MockInterface_Run_Call {
_c.Call.Return(n)
return _c
}
func (_c *MockInterface_Run_Call) RunAndReturn(run func(ctx context.Context, args []string, version string) int) *MockInterface_Run_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,90 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package reader_mock
import (
"github.com/int128/kubelogin/pkg/credentialplugin"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Read provides a mock function for the type MockInterface
func (_mock *MockInterface) Read() (credentialplugin.Input, error) {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Read")
}
var r0 credentialplugin.Input
var r1 error
if returnFunc, ok := ret.Get(0).(func() (credentialplugin.Input, error)); ok {
return returnFunc()
}
if returnFunc, ok := ret.Get(0).(func() credentialplugin.Input); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(credentialplugin.Input)
}
if returnFunc, ok := ret.Get(1).(func() error); ok {
r1 = returnFunc()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read'
type MockInterface_Read_Call struct {
*mock.Call
}
// Read is a helper method to define mock.On call
func (_e *MockInterface_Expecter) Read() *MockInterface_Read_Call {
return &MockInterface_Read_Call{Call: _e.mock.On("Read")}
}
func (_c *MockInterface_Read_Call) Run(run func()) *MockInterface_Read_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockInterface_Read_Call) Return(input credentialplugin.Input, err error) *MockInterface_Read_Call {
_c.Call.Return(input, err)
return _c
}
func (_c *MockInterface_Read_Call) RunAndReturn(run func() (credentialplugin.Input, error)) *MockInterface_Read_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,88 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package writer_mock
import (
"github.com/int128/kubelogin/pkg/credentialplugin"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Write provides a mock function for the type MockInterface
func (_mock *MockInterface) Write(out credentialplugin.Output) error {
ret := _mock.Called(out)
if len(ret) == 0 {
panic("no return value specified for Write")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(credentialplugin.Output) error); ok {
r0 = returnFunc(out)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write'
type MockInterface_Write_Call struct {
*mock.Call
}
// Write is a helper method to define mock.On call
// - out credentialplugin.Output
func (_e *MockInterface_Expecter) Write(out interface{}) *MockInterface_Write_Call {
return &MockInterface_Write_Call{Call: _e.mock.On("Write", out)}
}
func (_c *MockInterface_Write_Call) Run(run func(out credentialplugin.Output)) *MockInterface_Write_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 credentialplugin.Output
if args[0] != nil {
arg0 = args[0].(credentialplugin.Output)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_Write_Call) Return(err error) *MockInterface_Write_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_Write_Call) RunAndReturn(run func(out credentialplugin.Output) error) *MockInterface_Write_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,152 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package browser_mock
import (
"context"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Open provides a mock function for the type MockInterface
func (_mock *MockInterface) Open(url string) error {
ret := _mock.Called(url)
if len(ret) == 0 {
panic("no return value specified for Open")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(string) error); ok {
r0 = returnFunc(url)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_Open_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Open'
type MockInterface_Open_Call struct {
*mock.Call
}
// Open is a helper method to define mock.On call
// - url string
func (_e *MockInterface_Expecter) Open(url interface{}) *MockInterface_Open_Call {
return &MockInterface_Open_Call{Call: _e.mock.On("Open", url)}
}
func (_c *MockInterface_Open_Call) Run(run func(url string)) *MockInterface_Open_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_Open_Call) Return(err error) *MockInterface_Open_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_Open_Call) RunAndReturn(run func(url string) error) *MockInterface_Open_Call {
_c.Call.Return(run)
return _c
}
// OpenCommand provides a mock function for the type MockInterface
func (_mock *MockInterface) OpenCommand(ctx context.Context, url string, command string) error {
ret := _mock.Called(ctx, url, command)
if len(ret) == 0 {
panic("no return value specified for OpenCommand")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = returnFunc(ctx, url, command)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_OpenCommand_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OpenCommand'
type MockInterface_OpenCommand_Call struct {
*mock.Call
}
// OpenCommand is a helper method to define mock.On call
// - ctx context.Context
// - url string
// - command string
func (_e *MockInterface_Expecter) OpenCommand(ctx interface{}, url interface{}, command interface{}) *MockInterface_OpenCommand_Call {
return &MockInterface_OpenCommand_Call{Call: _e.mock.On("OpenCommand", ctx, url, command)}
}
func (_c *MockInterface_OpenCommand_Call) Run(run func(ctx context.Context, url string, command string)) *MockInterface_OpenCommand_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
var arg2 string
if args[2] != nil {
arg2 = args[2].(string)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockInterface_OpenCommand_Call) Return(err error) *MockInterface_OpenCommand_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_OpenCommand_Call) RunAndReturn(run func(ctx context.Context, url string, command string) error) *MockInterface_OpenCommand_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,82 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package clock_mock
import (
"time"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Now provides a mock function for the type MockInterface
func (_mock *MockInterface) Now() time.Time {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Now")
}
var r0 time.Time
if returnFunc, ok := ret.Get(0).(func() time.Time); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(time.Time)
}
return r0
}
// MockInterface_Now_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Now'
type MockInterface_Now_Call struct {
*mock.Call
}
// Now is a helper method to define mock.On call
func (_e *MockInterface_Expecter) Now() *MockInterface_Now_Call {
return &MockInterface_Now_Call{Call: _e.mock.On("Now")}
}
func (_c *MockInterface_Now_Call) Run(run func()) *MockInterface_Now_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockInterface_Now_Call) Return(time1 time.Time) *MockInterface_Now_Call {
_c.Call.Return(time1)
return _c
}
func (_c *MockInterface_Now_Call) RunAndReturn(run func() time.Time) *MockInterface_Now_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,398 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package logger_mock
import (
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/spf13/pflag"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// AddFlags provides a mock function for the type MockInterface
func (_mock *MockInterface) AddFlags(f *pflag.FlagSet) {
_mock.Called(f)
return
}
// MockInterface_AddFlags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddFlags'
type MockInterface_AddFlags_Call struct {
*mock.Call
}
// AddFlags is a helper method to define mock.On call
// - f *pflag.FlagSet
func (_e *MockInterface_Expecter) AddFlags(f interface{}) *MockInterface_AddFlags_Call {
return &MockInterface_AddFlags_Call{Call: _e.mock.On("AddFlags", f)}
}
func (_c *MockInterface_AddFlags_Call) Run(run func(f *pflag.FlagSet)) *MockInterface_AddFlags_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 *pflag.FlagSet
if args[0] != nil {
arg0 = args[0].(*pflag.FlagSet)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_AddFlags_Call) Return() *MockInterface_AddFlags_Call {
_c.Call.Return()
return _c
}
func (_c *MockInterface_AddFlags_Call) RunAndReturn(run func(f *pflag.FlagSet)) *MockInterface_AddFlags_Call {
_c.Run(run)
return _c
}
// IsEnabled provides a mock function for the type MockInterface
func (_mock *MockInterface) IsEnabled(level int) bool {
ret := _mock.Called(level)
if len(ret) == 0 {
panic("no return value specified for IsEnabled")
}
var r0 bool
if returnFunc, ok := ret.Get(0).(func(int) bool); ok {
r0 = returnFunc(level)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// MockInterface_IsEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsEnabled'
type MockInterface_IsEnabled_Call struct {
*mock.Call
}
// IsEnabled is a helper method to define mock.On call
// - level int
func (_e *MockInterface_Expecter) IsEnabled(level interface{}) *MockInterface_IsEnabled_Call {
return &MockInterface_IsEnabled_Call{Call: _e.mock.On("IsEnabled", level)}
}
func (_c *MockInterface_IsEnabled_Call) Run(run func(level int)) *MockInterface_IsEnabled_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 int
if args[0] != nil {
arg0 = args[0].(int)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_IsEnabled_Call) Return(b bool) *MockInterface_IsEnabled_Call {
_c.Call.Return(b)
return _c
}
func (_c *MockInterface_IsEnabled_Call) RunAndReturn(run func(level int) bool) *MockInterface_IsEnabled_Call {
_c.Call.Return(run)
return _c
}
// Printf provides a mock function for the type MockInterface
func (_mock *MockInterface) Printf(format string, args ...interface{}) {
if len(args) > 0 {
_mock.Called(format, args)
} else {
_mock.Called(format)
}
return
}
// MockInterface_Printf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Printf'
type MockInterface_Printf_Call struct {
*mock.Call
}
// Printf is a helper method to define mock.On call
// - format string
// - args ...interface{}
func (_e *MockInterface_Expecter) Printf(format interface{}, args ...interface{}) *MockInterface_Printf_Call {
return &MockInterface_Printf_Call{Call: _e.mock.On("Printf",
append([]interface{}{format}, args...)...)}
}
func (_c *MockInterface_Printf_Call) Run(run func(format string, args ...interface{})) *MockInterface_Printf_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 []interface{}
var variadicArgs []interface{}
if len(args) > 1 {
variadicArgs = args[1].([]interface{})
}
arg1 = variadicArgs
run(
arg0,
arg1...,
)
})
return _c
}
func (_c *MockInterface_Printf_Call) Return() *MockInterface_Printf_Call {
_c.Call.Return()
return _c
}
func (_c *MockInterface_Printf_Call) RunAndReturn(run func(format string, args ...interface{})) *MockInterface_Printf_Call {
_c.Run(run)
return _c
}
// V provides a mock function for the type MockInterface
func (_mock *MockInterface) V(level int) logger.Verbose {
ret := _mock.Called(level)
if len(ret) == 0 {
panic("no return value specified for V")
}
var r0 logger.Verbose
if returnFunc, ok := ret.Get(0).(func(int) logger.Verbose); ok {
r0 = returnFunc(level)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(logger.Verbose)
}
}
return r0
}
// MockInterface_V_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'V'
type MockInterface_V_Call struct {
*mock.Call
}
// V is a helper method to define mock.On call
// - level int
func (_e *MockInterface_Expecter) V(level interface{}) *MockInterface_V_Call {
return &MockInterface_V_Call{Call: _e.mock.On("V", level)}
}
func (_c *MockInterface_V_Call) Run(run func(level int)) *MockInterface_V_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 int
if args[0] != nil {
arg0 = args[0].(int)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_V_Call) Return(verbose logger.Verbose) *MockInterface_V_Call {
_c.Call.Return(verbose)
return _c
}
func (_c *MockInterface_V_Call) RunAndReturn(run func(level int) logger.Verbose) *MockInterface_V_Call {
_c.Call.Return(run)
return _c
}
// NewMockVerbose creates a new instance of MockVerbose. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockVerbose(t interface {
mock.TestingT
Cleanup(func())
}) *MockVerbose {
mock := &MockVerbose{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockVerbose is an autogenerated mock type for the Verbose type
type MockVerbose struct {
mock.Mock
}
type MockVerbose_Expecter struct {
mock *mock.Mock
}
func (_m *MockVerbose) EXPECT() *MockVerbose_Expecter {
return &MockVerbose_Expecter{mock: &_m.Mock}
}
// Infof provides a mock function for the type MockVerbose
func (_mock *MockVerbose) Infof(format string, args ...interface{}) {
if len(args) > 0 {
_mock.Called(format, args)
} else {
_mock.Called(format)
}
return
}
// MockVerbose_Infof_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Infof'
type MockVerbose_Infof_Call struct {
*mock.Call
}
// Infof is a helper method to define mock.On call
// - format string
// - args ...interface{}
func (_e *MockVerbose_Expecter) Infof(format interface{}, args ...interface{}) *MockVerbose_Infof_Call {
return &MockVerbose_Infof_Call{Call: _e.mock.On("Infof",
append([]interface{}{format}, args...)...)}
}
func (_c *MockVerbose_Infof_Call) Run(run func(format string, args ...interface{})) *MockVerbose_Infof_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 []interface{}
var variadicArgs []interface{}
if len(args) > 1 {
variadicArgs = args[1].([]interface{})
}
arg1 = variadicArgs
run(
arg0,
arg1...,
)
})
return _c
}
func (_c *MockVerbose_Infof_Call) Return() *MockVerbose_Infof_Call {
_c.Call.Return()
return _c
}
func (_c *MockVerbose_Infof_Call) RunAndReturn(run func(format string, args ...interface{})) *MockVerbose_Infof_Call {
_c.Run(run)
return _c
}
// newMockgoLogger creates a new instance of mockgoLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func newMockgoLogger(t interface {
mock.TestingT
Cleanup(func())
}) *mockgoLogger {
mock := &mockgoLogger{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// mockgoLogger is an autogenerated mock type for the goLogger type
type mockgoLogger struct {
mock.Mock
}
type mockgoLogger_Expecter struct {
mock *mock.Mock
}
func (_m *mockgoLogger) EXPECT() *mockgoLogger_Expecter {
return &mockgoLogger_Expecter{mock: &_m.Mock}
}
// Printf provides a mock function for the type mockgoLogger
func (_mock *mockgoLogger) Printf(format string, v ...interface{}) {
if len(v) > 0 {
_mock.Called(format, v)
} else {
_mock.Called(format)
}
return
}
// mockgoLogger_Printf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Printf'
type mockgoLogger_Printf_Call struct {
*mock.Call
}
// Printf is a helper method to define mock.On call
// - format string
// - v ...interface{}
func (_e *mockgoLogger_Expecter) Printf(format interface{}, v ...interface{}) *mockgoLogger_Printf_Call {
return &mockgoLogger_Printf_Call{Call: _e.mock.On("Printf",
append([]interface{}{format}, v...)...)}
}
func (_c *mockgoLogger_Printf_Call) Run(run func(format string, v ...interface{})) *mockgoLogger_Printf_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 []interface{}
var variadicArgs []interface{}
if len(args) > 1 {
variadicArgs = args[1].([]interface{})
}
arg1 = variadicArgs
run(
arg0,
arg1...,
)
})
return _c
}
func (_c *mockgoLogger_Printf_Call) Return() *mockgoLogger_Printf_Call {
_c.Call.Return()
return _c
}
func (_c *mockgoLogger_Printf_Call) RunAndReturn(run func(format string, v ...interface{})) *mockgoLogger_Printf_Call {
_c.Run(run)
return _c
}

View File

@@ -1,156 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package reader_mock
import (
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// ReadPassword provides a mock function for the type MockInterface
func (_mock *MockInterface) ReadPassword(prompt string) (string, error) {
ret := _mock.Called(prompt)
if len(ret) == 0 {
panic("no return value specified for ReadPassword")
}
var r0 string
var r1 error
if returnFunc, ok := ret.Get(0).(func(string) (string, error)); ok {
return returnFunc(prompt)
}
if returnFunc, ok := ret.Get(0).(func(string) string); ok {
r0 = returnFunc(prompt)
} else {
r0 = ret.Get(0).(string)
}
if returnFunc, ok := ret.Get(1).(func(string) error); ok {
r1 = returnFunc(prompt)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_ReadPassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadPassword'
type MockInterface_ReadPassword_Call struct {
*mock.Call
}
// ReadPassword is a helper method to define mock.On call
// - prompt string
func (_e *MockInterface_Expecter) ReadPassword(prompt interface{}) *MockInterface_ReadPassword_Call {
return &MockInterface_ReadPassword_Call{Call: _e.mock.On("ReadPassword", prompt)}
}
func (_c *MockInterface_ReadPassword_Call) Run(run func(prompt string)) *MockInterface_ReadPassword_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_ReadPassword_Call) Return(s string, err error) *MockInterface_ReadPassword_Call {
_c.Call.Return(s, err)
return _c
}
func (_c *MockInterface_ReadPassword_Call) RunAndReturn(run func(prompt string) (string, error)) *MockInterface_ReadPassword_Call {
_c.Call.Return(run)
return _c
}
// ReadString provides a mock function for the type MockInterface
func (_mock *MockInterface) ReadString(prompt string) (string, error) {
ret := _mock.Called(prompt)
if len(ret) == 0 {
panic("no return value specified for ReadString")
}
var r0 string
var r1 error
if returnFunc, ok := ret.Get(0).(func(string) (string, error)); ok {
return returnFunc(prompt)
}
if returnFunc, ok := ret.Get(0).(func(string) string); ok {
r0 = returnFunc(prompt)
} else {
r0 = ret.Get(0).(string)
}
if returnFunc, ok := ret.Get(1).(func(string) error); ok {
r1 = returnFunc(prompt)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_ReadString_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReadString'
type MockInterface_ReadString_Call struct {
*mock.Call
}
// ReadString is a helper method to define mock.On call
// - prompt string
func (_e *MockInterface_Expecter) ReadString(prompt interface{}) *MockInterface_ReadString_Call {
return &MockInterface_ReadString_Call{Call: _e.mock.On("ReadString", prompt)}
}
func (_c *MockInterface_ReadString_Call) Run(run func(prompt string)) *MockInterface_ReadString_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_ReadString_Call) Return(s string, err error) *MockInterface_ReadString_Call {
_c.Call.Return(s, err)
return _c
}
func (_c *MockInterface_ReadString_Call) RunAndReturn(run func(prompt string) (string, error)) *MockInterface_ReadString_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,183 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package stdio_mock
import (
mock "github.com/stretchr/testify/mock"
)
// NewMockStdout creates a new instance of MockStdout. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockStdout(t interface {
mock.TestingT
Cleanup(func())
}) *MockStdout {
mock := &MockStdout{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockStdout is an autogenerated mock type for the Stdout type
type MockStdout struct {
mock.Mock
}
type MockStdout_Expecter struct {
mock *mock.Mock
}
func (_m *MockStdout) EXPECT() *MockStdout_Expecter {
return &MockStdout_Expecter{mock: &_m.Mock}
}
// Write provides a mock function for the type MockStdout
func (_mock *MockStdout) Write(p []byte) (int, error) {
ret := _mock.Called(p)
if len(ret) == 0 {
panic("no return value specified for Write")
}
var r0 int
var r1 error
if returnFunc, ok := ret.Get(0).(func([]byte) (int, error)); ok {
return returnFunc(p)
}
if returnFunc, ok := ret.Get(0).(func([]byte) int); ok {
r0 = returnFunc(p)
} else {
r0 = ret.Get(0).(int)
}
if returnFunc, ok := ret.Get(1).(func([]byte) error); ok {
r1 = returnFunc(p)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockStdout_Write_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Write'
type MockStdout_Write_Call struct {
*mock.Call
}
// Write is a helper method to define mock.On call
// - p []byte
func (_e *MockStdout_Expecter) Write(p interface{}) *MockStdout_Write_Call {
return &MockStdout_Write_Call{Call: _e.mock.On("Write", p)}
}
func (_c *MockStdout_Write_Call) Run(run func(p []byte)) *MockStdout_Write_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 []byte
if args[0] != nil {
arg0 = args[0].([]byte)
}
run(
arg0,
)
})
return _c
}
func (_c *MockStdout_Write_Call) Return(n int, err error) *MockStdout_Write_Call {
_c.Call.Return(n, err)
return _c
}
func (_c *MockStdout_Write_Call) RunAndReturn(run func(p []byte) (int, error)) *MockStdout_Write_Call {
_c.Call.Return(run)
return _c
}
// NewMockStdin creates a new instance of MockStdin. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockStdin(t interface {
mock.TestingT
Cleanup(func())
}) *MockStdin {
mock := &MockStdin{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockStdin is an autogenerated mock type for the Stdin type
type MockStdin struct {
mock.Mock
}
type MockStdin_Expecter struct {
mock *mock.Mock
}
func (_m *MockStdin) EXPECT() *MockStdin_Expecter {
return &MockStdin_Expecter{mock: &_m.Mock}
}
// Read provides a mock function for the type MockStdin
func (_mock *MockStdin) Read(p []byte) (int, error) {
ret := _mock.Called(p)
if len(ret) == 0 {
panic("no return value specified for Read")
}
var r0 int
var r1 error
if returnFunc, ok := ret.Get(0).(func([]byte) (int, error)); ok {
return returnFunc(p)
}
if returnFunc, ok := ret.Get(0).(func([]byte) int); ok {
r0 = returnFunc(p)
} else {
r0 = ret.Get(0).(int)
}
if returnFunc, ok := ret.Get(1).(func([]byte) error); ok {
r1 = returnFunc(p)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockStdin_Read_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Read'
type MockStdin_Read_Call struct {
*mock.Call
}
// Read is a helper method to define mock.On call
// - p []byte
func (_e *MockStdin_Expecter) Read(p interface{}) *MockStdin_Read_Call {
return &MockStdin_Read_Call{Call: _e.mock.On("Read", p)}
}
func (_c *MockStdin_Read_Call) Run(run func(p []byte)) *MockStdin_Read_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 []byte
if args[0] != nil {
arg0 = args[0].([]byte)
}
run(
arg0,
)
})
return _c
}
func (_c *MockStdin_Read_Call) Return(n int, err error) *MockStdin_Read_Call {
_c.Call.Return(n, err)
return _c
}
func (_c *MockStdin_Read_Call) RunAndReturn(run func(p []byte) (int, error)) *MockStdin_Read_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,82 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package jwt_mock
import (
"time"
mock "github.com/stretchr/testify/mock"
)
// NewMockClock creates a new instance of MockClock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockClock(t interface {
mock.TestingT
Cleanup(func())
}) *MockClock {
mock := &MockClock{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockClock is an autogenerated mock type for the Clock type
type MockClock struct {
mock.Mock
}
type MockClock_Expecter struct {
mock *mock.Mock
}
func (_m *MockClock) EXPECT() *MockClock_Expecter {
return &MockClock_Expecter{mock: &_m.Mock}
}
// Now provides a mock function for the type MockClock
func (_mock *MockClock) Now() time.Time {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Now")
}
var r0 time.Time
if returnFunc, ok := ret.Get(0).(func() time.Time); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(time.Time)
}
return r0
}
// MockClock_Now_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Now'
type MockClock_Now_Call struct {
*mock.Call
}
// Now is a helper method to define mock.On call
func (_e *MockClock_Expecter) Now() *MockClock_Now_Call {
return &MockClock_Now_Call{Call: _e.mock.On("Now")}
}
func (_c *MockClock_Now_Call) Run(run func()) *MockClock_Now_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockClock_Now_Call) Return(time1 time.Time) *MockClock_Now_Call {
_c.Call.Return(time1)
return _c
}
func (_c *MockClock_Now_Call) RunAndReturn(run func() time.Time) *MockClock_Now_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,111 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package loader_mock
import (
"github.com/int128/kubelogin/pkg/kubeconfig"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// GetCurrentAuthProvider provides a mock function for the type MockInterface
func (_mock *MockInterface) GetCurrentAuthProvider(explicitFilename string, contextName kubeconfig.ContextName, userName kubeconfig.UserName) (*kubeconfig.AuthProvider, error) {
ret := _mock.Called(explicitFilename, contextName, userName)
if len(ret) == 0 {
panic("no return value specified for GetCurrentAuthProvider")
}
var r0 *kubeconfig.AuthProvider
var r1 error
if returnFunc, ok := ret.Get(0).(func(string, kubeconfig.ContextName, kubeconfig.UserName) (*kubeconfig.AuthProvider, error)); ok {
return returnFunc(explicitFilename, contextName, userName)
}
if returnFunc, ok := ret.Get(0).(func(string, kubeconfig.ContextName, kubeconfig.UserName) *kubeconfig.AuthProvider); ok {
r0 = returnFunc(explicitFilename, contextName, userName)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*kubeconfig.AuthProvider)
}
}
if returnFunc, ok := ret.Get(1).(func(string, kubeconfig.ContextName, kubeconfig.UserName) error); ok {
r1 = returnFunc(explicitFilename, contextName, userName)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_GetCurrentAuthProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrentAuthProvider'
type MockInterface_GetCurrentAuthProvider_Call struct {
*mock.Call
}
// GetCurrentAuthProvider is a helper method to define mock.On call
// - explicitFilename string
// - contextName kubeconfig.ContextName
// - userName kubeconfig.UserName
func (_e *MockInterface_Expecter) GetCurrentAuthProvider(explicitFilename interface{}, contextName interface{}, userName interface{}) *MockInterface_GetCurrentAuthProvider_Call {
return &MockInterface_GetCurrentAuthProvider_Call{Call: _e.mock.On("GetCurrentAuthProvider", explicitFilename, contextName, userName)}
}
func (_c *MockInterface_GetCurrentAuthProvider_Call) Run(run func(explicitFilename string, contextName kubeconfig.ContextName, userName kubeconfig.UserName)) *MockInterface_GetCurrentAuthProvider_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 kubeconfig.ContextName
if args[1] != nil {
arg1 = args[1].(kubeconfig.ContextName)
}
var arg2 kubeconfig.UserName
if args[2] != nil {
arg2 = args[2].(kubeconfig.UserName)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockInterface_GetCurrentAuthProvider_Call) Return(authProvider *kubeconfig.AuthProvider, err error) *MockInterface_GetCurrentAuthProvider_Call {
_c.Call.Return(authProvider, err)
return _c
}
func (_c *MockInterface_GetCurrentAuthProvider_Call) RunAndReturn(run func(explicitFilename string, contextName kubeconfig.ContextName, userName kubeconfig.UserName) (*kubeconfig.AuthProvider, error)) *MockInterface_GetCurrentAuthProvider_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,88 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package writer_mock
import (
"github.com/int128/kubelogin/pkg/kubeconfig"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// UpdateAuthProvider provides a mock function for the type MockInterface
func (_mock *MockInterface) UpdateAuthProvider(p kubeconfig.AuthProvider) error {
ret := _mock.Called(p)
if len(ret) == 0 {
panic("no return value specified for UpdateAuthProvider")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(kubeconfig.AuthProvider) error); ok {
r0 = returnFunc(p)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_UpdateAuthProvider_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateAuthProvider'
type MockInterface_UpdateAuthProvider_Call struct {
*mock.Call
}
// UpdateAuthProvider is a helper method to define mock.On call
// - p kubeconfig.AuthProvider
func (_e *MockInterface_Expecter) UpdateAuthProvider(p interface{}) *MockInterface_UpdateAuthProvider_Call {
return &MockInterface_UpdateAuthProvider_Call{Call: _e.mock.On("UpdateAuthProvider", p)}
}
func (_c *MockInterface_UpdateAuthProvider_Call) Run(run func(p kubeconfig.AuthProvider)) *MockInterface_UpdateAuthProvider_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 kubeconfig.AuthProvider
if args[0] != nil {
arg0 = args[0].(kubeconfig.AuthProvider)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_UpdateAuthProvider_Call) Return(err error) *MockInterface_UpdateAuthProvider_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_UpdateAuthProvider_Call) RunAndReturn(run func(p kubeconfig.AuthProvider) error) *MockInterface_UpdateAuthProvider_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,721 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package client_mock
import (
"context"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/pkce"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/oauth2dev"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// ExchangeAuthCode provides a mock function for the type MockInterface
func (_mock *MockInterface) ExchangeAuthCode(ctx context.Context, in client.ExchangeAuthCodeInput) (*oidc.TokenSet, error) {
ret := _mock.Called(ctx, in)
if len(ret) == 0 {
panic("no return value specified for ExchangeAuthCode")
}
var r0 *oidc.TokenSet
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, client.ExchangeAuthCodeInput) (*oidc.TokenSet, error)); ok {
return returnFunc(ctx, in)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, client.ExchangeAuthCodeInput) *oidc.TokenSet); ok {
r0 = returnFunc(ctx, in)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oidc.TokenSet)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, client.ExchangeAuthCodeInput) error); ok {
r1 = returnFunc(ctx, in)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_ExchangeAuthCode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ExchangeAuthCode'
type MockInterface_ExchangeAuthCode_Call struct {
*mock.Call
}
// ExchangeAuthCode is a helper method to define mock.On call
// - ctx context.Context
// - in client.ExchangeAuthCodeInput
func (_e *MockInterface_Expecter) ExchangeAuthCode(ctx interface{}, in interface{}) *MockInterface_ExchangeAuthCode_Call {
return &MockInterface_ExchangeAuthCode_Call{Call: _e.mock.On("ExchangeAuthCode", ctx, in)}
}
func (_c *MockInterface_ExchangeAuthCode_Call) Run(run func(ctx context.Context, in client.ExchangeAuthCodeInput)) *MockInterface_ExchangeAuthCode_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 client.ExchangeAuthCodeInput
if args[1] != nil {
arg1 = args[1].(client.ExchangeAuthCodeInput)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_ExchangeAuthCode_Call) Return(tokenSet *oidc.TokenSet, err error) *MockInterface_ExchangeAuthCode_Call {
_c.Call.Return(tokenSet, err)
return _c
}
func (_c *MockInterface_ExchangeAuthCode_Call) RunAndReturn(run func(ctx context.Context, in client.ExchangeAuthCodeInput) (*oidc.TokenSet, error)) *MockInterface_ExchangeAuthCode_Call {
_c.Call.Return(run)
return _c
}
// ExchangeDeviceCode provides a mock function for the type MockInterface
func (_mock *MockInterface) ExchangeDeviceCode(ctx context.Context, authResponse *oauth2dev.AuthorizationResponse) (*oidc.TokenSet, error) {
ret := _mock.Called(ctx, authResponse)
if len(ret) == 0 {
panic("no return value specified for ExchangeDeviceCode")
}
var r0 *oidc.TokenSet
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, *oauth2dev.AuthorizationResponse) (*oidc.TokenSet, error)); ok {
return returnFunc(ctx, authResponse)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, *oauth2dev.AuthorizationResponse) *oidc.TokenSet); ok {
r0 = returnFunc(ctx, authResponse)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oidc.TokenSet)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, *oauth2dev.AuthorizationResponse) error); ok {
r1 = returnFunc(ctx, authResponse)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_ExchangeDeviceCode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ExchangeDeviceCode'
type MockInterface_ExchangeDeviceCode_Call struct {
*mock.Call
}
// ExchangeDeviceCode is a helper method to define mock.On call
// - ctx context.Context
// - authResponse *oauth2dev.AuthorizationResponse
func (_e *MockInterface_Expecter) ExchangeDeviceCode(ctx interface{}, authResponse interface{}) *MockInterface_ExchangeDeviceCode_Call {
return &MockInterface_ExchangeDeviceCode_Call{Call: _e.mock.On("ExchangeDeviceCode", ctx, authResponse)}
}
func (_c *MockInterface_ExchangeDeviceCode_Call) Run(run func(ctx context.Context, authResponse *oauth2dev.AuthorizationResponse)) *MockInterface_ExchangeDeviceCode_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 *oauth2dev.AuthorizationResponse
if args[1] != nil {
arg1 = args[1].(*oauth2dev.AuthorizationResponse)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_ExchangeDeviceCode_Call) Return(tokenSet *oidc.TokenSet, err error) *MockInterface_ExchangeDeviceCode_Call {
_c.Call.Return(tokenSet, err)
return _c
}
func (_c *MockInterface_ExchangeDeviceCode_Call) RunAndReturn(run func(ctx context.Context, authResponse *oauth2dev.AuthorizationResponse) (*oidc.TokenSet, error)) *MockInterface_ExchangeDeviceCode_Call {
_c.Call.Return(run)
return _c
}
// GetAuthCodeURL provides a mock function for the type MockInterface
func (_mock *MockInterface) GetAuthCodeURL(in client.AuthCodeURLInput) string {
ret := _mock.Called(in)
if len(ret) == 0 {
panic("no return value specified for GetAuthCodeURL")
}
var r0 string
if returnFunc, ok := ret.Get(0).(func(client.AuthCodeURLInput) string); ok {
r0 = returnFunc(in)
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// MockInterface_GetAuthCodeURL_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAuthCodeURL'
type MockInterface_GetAuthCodeURL_Call struct {
*mock.Call
}
// GetAuthCodeURL is a helper method to define mock.On call
// - in client.AuthCodeURLInput
func (_e *MockInterface_Expecter) GetAuthCodeURL(in interface{}) *MockInterface_GetAuthCodeURL_Call {
return &MockInterface_GetAuthCodeURL_Call{Call: _e.mock.On("GetAuthCodeURL", in)}
}
func (_c *MockInterface_GetAuthCodeURL_Call) Run(run func(in client.AuthCodeURLInput)) *MockInterface_GetAuthCodeURL_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 client.AuthCodeURLInput
if args[0] != nil {
arg0 = args[0].(client.AuthCodeURLInput)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_GetAuthCodeURL_Call) Return(s string) *MockInterface_GetAuthCodeURL_Call {
_c.Call.Return(s)
return _c
}
func (_c *MockInterface_GetAuthCodeURL_Call) RunAndReturn(run func(in client.AuthCodeURLInput) string) *MockInterface_GetAuthCodeURL_Call {
_c.Call.Return(run)
return _c
}
// GetDeviceAuthorization provides a mock function for the type MockInterface
func (_mock *MockInterface) GetDeviceAuthorization(ctx context.Context) (*oauth2dev.AuthorizationResponse, error) {
ret := _mock.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for GetDeviceAuthorization")
}
var r0 *oauth2dev.AuthorizationResponse
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context) (*oauth2dev.AuthorizationResponse, error)); ok {
return returnFunc(ctx)
}
if returnFunc, ok := ret.Get(0).(func(context.Context) *oauth2dev.AuthorizationResponse); ok {
r0 = returnFunc(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oauth2dev.AuthorizationResponse)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = returnFunc(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_GetDeviceAuthorization_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDeviceAuthorization'
type MockInterface_GetDeviceAuthorization_Call struct {
*mock.Call
}
// GetDeviceAuthorization is a helper method to define mock.On call
// - ctx context.Context
func (_e *MockInterface_Expecter) GetDeviceAuthorization(ctx interface{}) *MockInterface_GetDeviceAuthorization_Call {
return &MockInterface_GetDeviceAuthorization_Call{Call: _e.mock.On("GetDeviceAuthorization", ctx)}
}
func (_c *MockInterface_GetDeviceAuthorization_Call) Run(run func(ctx context.Context)) *MockInterface_GetDeviceAuthorization_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_GetDeviceAuthorization_Call) Return(authorizationResponse *oauth2dev.AuthorizationResponse, err error) *MockInterface_GetDeviceAuthorization_Call {
_c.Call.Return(authorizationResponse, err)
return _c
}
func (_c *MockInterface_GetDeviceAuthorization_Call) RunAndReturn(run func(ctx context.Context) (*oauth2dev.AuthorizationResponse, error)) *MockInterface_GetDeviceAuthorization_Call {
_c.Call.Return(run)
return _c
}
// GetTokenByAuthCode provides a mock function for the type MockInterface
func (_mock *MockInterface) GetTokenByAuthCode(ctx context.Context, in client.GetTokenByAuthCodeInput, localServerReadyChan chan<- string) (*oidc.TokenSet, error) {
ret := _mock.Called(ctx, in, localServerReadyChan)
if len(ret) == 0 {
panic("no return value specified for GetTokenByAuthCode")
}
var r0 *oidc.TokenSet
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, client.GetTokenByAuthCodeInput, chan<- string) (*oidc.TokenSet, error)); ok {
return returnFunc(ctx, in, localServerReadyChan)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, client.GetTokenByAuthCodeInput, chan<- string) *oidc.TokenSet); ok {
r0 = returnFunc(ctx, in, localServerReadyChan)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oidc.TokenSet)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, client.GetTokenByAuthCodeInput, chan<- string) error); ok {
r1 = returnFunc(ctx, in, localServerReadyChan)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_GetTokenByAuthCode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenByAuthCode'
type MockInterface_GetTokenByAuthCode_Call struct {
*mock.Call
}
// GetTokenByAuthCode is a helper method to define mock.On call
// - ctx context.Context
// - in client.GetTokenByAuthCodeInput
// - localServerReadyChan chan<- string
func (_e *MockInterface_Expecter) GetTokenByAuthCode(ctx interface{}, in interface{}, localServerReadyChan interface{}) *MockInterface_GetTokenByAuthCode_Call {
return &MockInterface_GetTokenByAuthCode_Call{Call: _e.mock.On("GetTokenByAuthCode", ctx, in, localServerReadyChan)}
}
func (_c *MockInterface_GetTokenByAuthCode_Call) Run(run func(ctx context.Context, in client.GetTokenByAuthCodeInput, localServerReadyChan chan<- string)) *MockInterface_GetTokenByAuthCode_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 client.GetTokenByAuthCodeInput
if args[1] != nil {
arg1 = args[1].(client.GetTokenByAuthCodeInput)
}
var arg2 chan<- string
if args[2] != nil {
arg2 = args[2].(chan<- string)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockInterface_GetTokenByAuthCode_Call) Return(tokenSet *oidc.TokenSet, err error) *MockInterface_GetTokenByAuthCode_Call {
_c.Call.Return(tokenSet, err)
return _c
}
func (_c *MockInterface_GetTokenByAuthCode_Call) RunAndReturn(run func(ctx context.Context, in client.GetTokenByAuthCodeInput, localServerReadyChan chan<- string) (*oidc.TokenSet, error)) *MockInterface_GetTokenByAuthCode_Call {
_c.Call.Return(run)
return _c
}
// GetTokenByClientCredentials provides a mock function for the type MockInterface
func (_mock *MockInterface) GetTokenByClientCredentials(ctx context.Context, in client.GetTokenByClientCredentialsInput) (*oidc.TokenSet, error) {
ret := _mock.Called(ctx, in)
if len(ret) == 0 {
panic("no return value specified for GetTokenByClientCredentials")
}
var r0 *oidc.TokenSet
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, client.GetTokenByClientCredentialsInput) (*oidc.TokenSet, error)); ok {
return returnFunc(ctx, in)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, client.GetTokenByClientCredentialsInput) *oidc.TokenSet); ok {
r0 = returnFunc(ctx, in)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oidc.TokenSet)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, client.GetTokenByClientCredentialsInput) error); ok {
r1 = returnFunc(ctx, in)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_GetTokenByClientCredentials_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenByClientCredentials'
type MockInterface_GetTokenByClientCredentials_Call struct {
*mock.Call
}
// GetTokenByClientCredentials is a helper method to define mock.On call
// - ctx context.Context
// - in client.GetTokenByClientCredentialsInput
func (_e *MockInterface_Expecter) GetTokenByClientCredentials(ctx interface{}, in interface{}) *MockInterface_GetTokenByClientCredentials_Call {
return &MockInterface_GetTokenByClientCredentials_Call{Call: _e.mock.On("GetTokenByClientCredentials", ctx, in)}
}
func (_c *MockInterface_GetTokenByClientCredentials_Call) Run(run func(ctx context.Context, in client.GetTokenByClientCredentialsInput)) *MockInterface_GetTokenByClientCredentials_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 client.GetTokenByClientCredentialsInput
if args[1] != nil {
arg1 = args[1].(client.GetTokenByClientCredentialsInput)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_GetTokenByClientCredentials_Call) Return(tokenSet *oidc.TokenSet, err error) *MockInterface_GetTokenByClientCredentials_Call {
_c.Call.Return(tokenSet, err)
return _c
}
func (_c *MockInterface_GetTokenByClientCredentials_Call) RunAndReturn(run func(ctx context.Context, in client.GetTokenByClientCredentialsInput) (*oidc.TokenSet, error)) *MockInterface_GetTokenByClientCredentials_Call {
_c.Call.Return(run)
return _c
}
// GetTokenByROPC provides a mock function for the type MockInterface
func (_mock *MockInterface) GetTokenByROPC(ctx context.Context, username string, password string) (*oidc.TokenSet, error) {
ret := _mock.Called(ctx, username, password)
if len(ret) == 0 {
panic("no return value specified for GetTokenByROPC")
}
var r0 *oidc.TokenSet
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) (*oidc.TokenSet, error)); ok {
return returnFunc(ctx, username, password)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string) *oidc.TokenSet); ok {
r0 = returnFunc(ctx, username, password)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oidc.TokenSet)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = returnFunc(ctx, username, password)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_GetTokenByROPC_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenByROPC'
type MockInterface_GetTokenByROPC_Call struct {
*mock.Call
}
// GetTokenByROPC is a helper method to define mock.On call
// - ctx context.Context
// - username string
// - password string
func (_e *MockInterface_Expecter) GetTokenByROPC(ctx interface{}, username interface{}, password interface{}) *MockInterface_GetTokenByROPC_Call {
return &MockInterface_GetTokenByROPC_Call{Call: _e.mock.On("GetTokenByROPC", ctx, username, password)}
}
func (_c *MockInterface_GetTokenByROPC_Call) Run(run func(ctx context.Context, username string, password string)) *MockInterface_GetTokenByROPC_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
var arg2 string
if args[2] != nil {
arg2 = args[2].(string)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockInterface_GetTokenByROPC_Call) Return(tokenSet *oidc.TokenSet, err error) *MockInterface_GetTokenByROPC_Call {
_c.Call.Return(tokenSet, err)
return _c
}
func (_c *MockInterface_GetTokenByROPC_Call) RunAndReturn(run func(ctx context.Context, username string, password string) (*oidc.TokenSet, error)) *MockInterface_GetTokenByROPC_Call {
_c.Call.Return(run)
return _c
}
// NegotiatedPKCEMethod provides a mock function for the type MockInterface
func (_mock *MockInterface) NegotiatedPKCEMethod() pkce.Method {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for NegotiatedPKCEMethod")
}
var r0 pkce.Method
if returnFunc, ok := ret.Get(0).(func() pkce.Method); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(pkce.Method)
}
return r0
}
// MockInterface_NegotiatedPKCEMethod_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NegotiatedPKCEMethod'
type MockInterface_NegotiatedPKCEMethod_Call struct {
*mock.Call
}
// NegotiatedPKCEMethod is a helper method to define mock.On call
func (_e *MockInterface_Expecter) NegotiatedPKCEMethod() *MockInterface_NegotiatedPKCEMethod_Call {
return &MockInterface_NegotiatedPKCEMethod_Call{Call: _e.mock.On("NegotiatedPKCEMethod")}
}
func (_c *MockInterface_NegotiatedPKCEMethod_Call) Run(run func()) *MockInterface_NegotiatedPKCEMethod_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *MockInterface_NegotiatedPKCEMethod_Call) Return(method pkce.Method) *MockInterface_NegotiatedPKCEMethod_Call {
_c.Call.Return(method)
return _c
}
func (_c *MockInterface_NegotiatedPKCEMethod_Call) RunAndReturn(run func() pkce.Method) *MockInterface_NegotiatedPKCEMethod_Call {
_c.Call.Return(run)
return _c
}
// Refresh provides a mock function for the type MockInterface
func (_mock *MockInterface) Refresh(ctx context.Context, refreshToken string) (*oidc.TokenSet, error) {
ret := _mock.Called(ctx, refreshToken)
if len(ret) == 0 {
panic("no return value specified for Refresh")
}
var r0 *oidc.TokenSet
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string) (*oidc.TokenSet, error)); ok {
return returnFunc(ctx, refreshToken)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, string) *oidc.TokenSet); ok {
r0 = returnFunc(ctx, refreshToken)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oidc.TokenSet)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = returnFunc(ctx, refreshToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_Refresh_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Refresh'
type MockInterface_Refresh_Call struct {
*mock.Call
}
// Refresh is a helper method to define mock.On call
// - ctx context.Context
// - refreshToken string
func (_e *MockInterface_Expecter) Refresh(ctx interface{}, refreshToken interface{}) *MockInterface_Refresh_Call {
return &MockInterface_Refresh_Call{Call: _e.mock.On("Refresh", ctx, refreshToken)}
}
func (_c *MockInterface_Refresh_Call) Run(run func(ctx context.Context, refreshToken string)) *MockInterface_Refresh_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_Refresh_Call) Return(tokenSet *oidc.TokenSet, err error) *MockInterface_Refresh_Call {
_c.Call.Return(tokenSet, err)
return _c
}
func (_c *MockInterface_Refresh_Call) RunAndReturn(run func(ctx context.Context, refreshToken string) (*oidc.TokenSet, error)) *MockInterface_Refresh_Call {
_c.Call.Return(run)
return _c
}
// NewMockFactoryInterface creates a new instance of MockFactoryInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockFactoryInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockFactoryInterface {
mock := &MockFactoryInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockFactoryInterface is an autogenerated mock type for the FactoryInterface type
type MockFactoryInterface struct {
mock.Mock
}
type MockFactoryInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockFactoryInterface) EXPECT() *MockFactoryInterface_Expecter {
return &MockFactoryInterface_Expecter{mock: &_m.Mock}
}
// New provides a mock function for the type MockFactoryInterface
func (_mock *MockFactoryInterface) New(ctx context.Context, prov oidc.Provider, tlsClientConfig tlsclientconfig.Config) (client.Interface, error) {
ret := _mock.Called(ctx, prov, tlsClientConfig)
if len(ret) == 0 {
panic("no return value specified for New")
}
var r0 client.Interface
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, oidc.Provider, tlsclientconfig.Config) (client.Interface, error)); ok {
return returnFunc(ctx, prov, tlsClientConfig)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, oidc.Provider, tlsclientconfig.Config) client.Interface); ok {
r0 = returnFunc(ctx, prov, tlsClientConfig)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(client.Interface)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, oidc.Provider, tlsclientconfig.Config) error); ok {
r1 = returnFunc(ctx, prov, tlsClientConfig)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockFactoryInterface_New_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'New'
type MockFactoryInterface_New_Call struct {
*mock.Call
}
// New is a helper method to define mock.On call
// - ctx context.Context
// - prov oidc.Provider
// - tlsClientConfig tlsclientconfig.Config
func (_e *MockFactoryInterface_Expecter) New(ctx interface{}, prov interface{}, tlsClientConfig interface{}) *MockFactoryInterface_New_Call {
return &MockFactoryInterface_New_Call{Call: _e.mock.On("New", ctx, prov, tlsClientConfig)}
}
func (_c *MockFactoryInterface_New_Call) Run(run func(ctx context.Context, prov oidc.Provider, tlsClientConfig tlsclientconfig.Config)) *MockFactoryInterface_New_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 oidc.Provider
if args[1] != nil {
arg1 = args[1].(oidc.Provider)
}
var arg2 tlsclientconfig.Config
if args[2] != nil {
arg2 = args[2].(tlsclientconfig.Config)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockFactoryInterface_New_Call) Return(interfaceParam client.Interface, err error) *MockFactoryInterface_New_Call {
_c.Call.Return(interfaceParam, err)
return _c
}
func (_c *MockFactoryInterface_New_Call) RunAndReturn(run func(ctx context.Context, prov oidc.Provider, tlsClientConfig tlsclientconfig.Config) (client.Interface, error)) *MockFactoryInterface_New_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,90 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package logger_mock
import (
mock "github.com/stretchr/testify/mock"
)
// newMocktestingLogger creates a new instance of mocktestingLogger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func newMocktestingLogger(t interface {
mock.TestingT
Cleanup(func())
}) *mocktestingLogger {
mock := &mocktestingLogger{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// mocktestingLogger is an autogenerated mock type for the testingLogger type
type mocktestingLogger struct {
mock.Mock
}
type mocktestingLogger_Expecter struct {
mock *mock.Mock
}
func (_m *mocktestingLogger) EXPECT() *mocktestingLogger_Expecter {
return &mocktestingLogger_Expecter{mock: &_m.Mock}
}
// Logf provides a mock function for the type mocktestingLogger
func (_mock *mocktestingLogger) Logf(format string, v ...interface{}) {
if len(v) > 0 {
_mock.Called(format, v)
} else {
_mock.Called(format)
}
return
}
// mocktestingLogger_Logf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Logf'
type mocktestingLogger_Logf_Call struct {
*mock.Call
}
// Logf is a helper method to define mock.On call
// - format string
// - v ...interface{}
func (_e *mocktestingLogger_Expecter) Logf(format interface{}, v ...interface{}) *mocktestingLogger_Logf_Call {
return &mocktestingLogger_Logf_Call{Call: _e.mock.On("Logf",
append([]interface{}{format}, v...)...)}
}
func (_c *mocktestingLogger_Logf_Call) Run(run func(format string, v ...interface{})) *mocktestingLogger_Logf_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
var arg1 []interface{}
var variadicArgs []interface{}
if len(args) > 1 {
variadicArgs = args[1].([]interface{})
}
arg1 = variadicArgs
run(
arg0,
arg1...,
)
})
return _c
}
func (_c *mocktestingLogger_Logf_Call) Return() *mocktestingLogger_Logf_Call {
_c.Call.Return()
return _c
}
func (_c *mocktestingLogger_Logf_Call) RunAndReturn(run func(format string, v ...interface{})) *mocktestingLogger_Logf_Call {
_c.Run(run)
return _c
}

View File

@@ -1,101 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package loader_mock
import (
"crypto/tls"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Load provides a mock function for the type MockInterface
func (_mock *MockInterface) Load(config tlsclientconfig.Config) (*tls.Config, error) {
ret := _mock.Called(config)
if len(ret) == 0 {
panic("no return value specified for Load")
}
var r0 *tls.Config
var r1 error
if returnFunc, ok := ret.Get(0).(func(tlsclientconfig.Config) (*tls.Config, error)); ok {
return returnFunc(config)
}
if returnFunc, ok := ret.Get(0).(func(tlsclientconfig.Config) *tls.Config); ok {
r0 = returnFunc(config)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*tls.Config)
}
}
if returnFunc, ok := ret.Get(1).(func(tlsclientconfig.Config) error); ok {
r1 = returnFunc(config)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_Load_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Load'
type MockInterface_Load_Call struct {
*mock.Call
}
// Load is a helper method to define mock.On call
// - config tlsclientconfig.Config
func (_e *MockInterface_Expecter) Load(config interface{}) *MockInterface_Load_Call {
return &MockInterface_Load_Call{Call: _e.mock.On("Load", config)}
}
func (_c *MockInterface_Load_Call) Run(run func(config tlsclientconfig.Config)) *MockInterface_Load_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 tlsclientconfig.Config
if args[0] != nil {
arg0 = args[0].(tlsclientconfig.Config)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_Load_Call) Return(config1 *tls.Config, err error) *MockInterface_Load_Call {
_c.Call.Return(config1, err)
return _c
}
func (_c *MockInterface_Load_Call) RunAndReturn(run func(config tlsclientconfig.Config) (*tls.Config, error)) *MockInterface_Load_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,290 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package repository_mock
import (
"io"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/tokencache"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// DeleteAll provides a mock function for the type MockInterface
func (_mock *MockInterface) DeleteAll(config tokencache.Config) error {
ret := _mock.Called(config)
if len(ret) == 0 {
panic("no return value specified for DeleteAll")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(tokencache.Config) error); ok {
r0 = returnFunc(config)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_DeleteAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAll'
type MockInterface_DeleteAll_Call struct {
*mock.Call
}
// DeleteAll is a helper method to define mock.On call
// - config tokencache.Config
func (_e *MockInterface_Expecter) DeleteAll(config interface{}) *MockInterface_DeleteAll_Call {
return &MockInterface_DeleteAll_Call{Call: _e.mock.On("DeleteAll", config)}
}
func (_c *MockInterface_DeleteAll_Call) Run(run func(config tokencache.Config)) *MockInterface_DeleteAll_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 tokencache.Config
if args[0] != nil {
arg0 = args[0].(tokencache.Config)
}
run(
arg0,
)
})
return _c
}
func (_c *MockInterface_DeleteAll_Call) Return(err error) *MockInterface_DeleteAll_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_DeleteAll_Call) RunAndReturn(run func(config tokencache.Config) error) *MockInterface_DeleteAll_Call {
_c.Call.Return(run)
return _c
}
// FindByKey provides a mock function for the type MockInterface
func (_mock *MockInterface) FindByKey(config tokencache.Config, key tokencache.Key) (*oidc.TokenSet, error) {
ret := _mock.Called(config, key)
if len(ret) == 0 {
panic("no return value specified for FindByKey")
}
var r0 *oidc.TokenSet
var r1 error
if returnFunc, ok := ret.Get(0).(func(tokencache.Config, tokencache.Key) (*oidc.TokenSet, error)); ok {
return returnFunc(config, key)
}
if returnFunc, ok := ret.Get(0).(func(tokencache.Config, tokencache.Key) *oidc.TokenSet); ok {
r0 = returnFunc(config, key)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oidc.TokenSet)
}
}
if returnFunc, ok := ret.Get(1).(func(tokencache.Config, tokencache.Key) error); ok {
r1 = returnFunc(config, key)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_FindByKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByKey'
type MockInterface_FindByKey_Call struct {
*mock.Call
}
// FindByKey is a helper method to define mock.On call
// - config tokencache.Config
// - key tokencache.Key
func (_e *MockInterface_Expecter) FindByKey(config interface{}, key interface{}) *MockInterface_FindByKey_Call {
return &MockInterface_FindByKey_Call{Call: _e.mock.On("FindByKey", config, key)}
}
func (_c *MockInterface_FindByKey_Call) Run(run func(config tokencache.Config, key tokencache.Key)) *MockInterface_FindByKey_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 tokencache.Config
if args[0] != nil {
arg0 = args[0].(tokencache.Config)
}
var arg1 tokencache.Key
if args[1] != nil {
arg1 = args[1].(tokencache.Key)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_FindByKey_Call) Return(tokenSet *oidc.TokenSet, err error) *MockInterface_FindByKey_Call {
_c.Call.Return(tokenSet, err)
return _c
}
func (_c *MockInterface_FindByKey_Call) RunAndReturn(run func(config tokencache.Config, key tokencache.Key) (*oidc.TokenSet, error)) *MockInterface_FindByKey_Call {
_c.Call.Return(run)
return _c
}
// Lock provides a mock function for the type MockInterface
func (_mock *MockInterface) Lock(config tokencache.Config, key tokencache.Key) (io.Closer, error) {
ret := _mock.Called(config, key)
if len(ret) == 0 {
panic("no return value specified for Lock")
}
var r0 io.Closer
var r1 error
if returnFunc, ok := ret.Get(0).(func(tokencache.Config, tokencache.Key) (io.Closer, error)); ok {
return returnFunc(config, key)
}
if returnFunc, ok := ret.Get(0).(func(tokencache.Config, tokencache.Key) io.Closer); ok {
r0 = returnFunc(config, key)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(io.Closer)
}
}
if returnFunc, ok := ret.Get(1).(func(tokencache.Config, tokencache.Key) error); ok {
r1 = returnFunc(config, key)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_Lock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Lock'
type MockInterface_Lock_Call struct {
*mock.Call
}
// Lock is a helper method to define mock.On call
// - config tokencache.Config
// - key tokencache.Key
func (_e *MockInterface_Expecter) Lock(config interface{}, key interface{}) *MockInterface_Lock_Call {
return &MockInterface_Lock_Call{Call: _e.mock.On("Lock", config, key)}
}
func (_c *MockInterface_Lock_Call) Run(run func(config tokencache.Config, key tokencache.Key)) *MockInterface_Lock_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 tokencache.Config
if args[0] != nil {
arg0 = args[0].(tokencache.Config)
}
var arg1 tokencache.Key
if args[1] != nil {
arg1 = args[1].(tokencache.Key)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_Lock_Call) Return(closer io.Closer, err error) *MockInterface_Lock_Call {
_c.Call.Return(closer, err)
return _c
}
func (_c *MockInterface_Lock_Call) RunAndReturn(run func(config tokencache.Config, key tokencache.Key) (io.Closer, error)) *MockInterface_Lock_Call {
_c.Call.Return(run)
return _c
}
// Save provides a mock function for the type MockInterface
func (_mock *MockInterface) Save(config tokencache.Config, key tokencache.Key, tokenSet oidc.TokenSet) error {
ret := _mock.Called(config, key, tokenSet)
if len(ret) == 0 {
panic("no return value specified for Save")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(tokencache.Config, tokencache.Key, oidc.TokenSet) error); ok {
r0 = returnFunc(config, key, tokenSet)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_Save_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Save'
type MockInterface_Save_Call struct {
*mock.Call
}
// Save is a helper method to define mock.On call
// - config tokencache.Config
// - key tokencache.Key
// - tokenSet oidc.TokenSet
func (_e *MockInterface_Expecter) Save(config interface{}, key interface{}, tokenSet interface{}) *MockInterface_Save_Call {
return &MockInterface_Save_Call{Call: _e.mock.On("Save", config, key, tokenSet)}
}
func (_c *MockInterface_Save_Call) Run(run func(config tokencache.Config, key tokencache.Key, tokenSet oidc.TokenSet)) *MockInterface_Save_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 tokencache.Config
if args[0] != nil {
arg0 = args[0].(tokencache.Config)
}
var arg1 tokencache.Key
if args[1] != nil {
arg1 = args[1].(tokencache.Key)
}
var arg2 oidc.TokenSet
if args[2] != nil {
arg2 = args[2].(oidc.TokenSet)
}
run(
arg0,
arg1,
arg2,
)
})
return _c
}
func (_c *MockInterface_Save_Call) Return(err error) *MockInterface_Save_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_Save_Call) RunAndReturn(run func(config tokencache.Config, key tokencache.Key, tokenSet oidc.TokenSet) error) *MockInterface_Save_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,107 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package authentication_mock
import (
"context"
"github.com/int128/kubelogin/pkg/usecases/authentication"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Do provides a mock function for the type MockInterface
func (_mock *MockInterface) Do(ctx context.Context, in authentication.Input) (*authentication.Output, error) {
ret := _mock.Called(ctx, in)
if len(ret) == 0 {
panic("no return value specified for Do")
}
var r0 *authentication.Output
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, authentication.Input) (*authentication.Output, error)); ok {
return returnFunc(ctx, in)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, authentication.Input) *authentication.Output); ok {
r0 = returnFunc(ctx, in)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*authentication.Output)
}
}
if returnFunc, ok := ret.Get(1).(func(context.Context, authentication.Input) error); ok {
r1 = returnFunc(ctx, in)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockInterface_Do_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Do'
type MockInterface_Do_Call struct {
*mock.Call
}
// Do is a helper method to define mock.On call
// - ctx context.Context
// - in authentication.Input
func (_e *MockInterface_Expecter) Do(ctx interface{}, in interface{}) *MockInterface_Do_Call {
return &MockInterface_Do_Call{Call: _e.mock.On("Do", ctx, in)}
}
func (_c *MockInterface_Do_Call) Run(run func(ctx context.Context, in authentication.Input)) *MockInterface_Do_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 authentication.Input
if args[1] != nil {
arg1 = args[1].(authentication.Input)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_Do_Call) Return(output *authentication.Output, err error) *MockInterface_Do_Call {
_c.Call.Return(output, err)
return _c
}
func (_c *MockInterface_Do_Call) RunAndReturn(run func(ctx context.Context, in authentication.Input) (*authentication.Output, error)) *MockInterface_Do_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,96 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package clean_mock
import (
"context"
"github.com/int128/kubelogin/pkg/usecases/clean"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Do provides a mock function for the type MockInterface
func (_mock *MockInterface) Do(ctx context.Context, in clean.Input) error {
ret := _mock.Called(ctx, in)
if len(ret) == 0 {
panic("no return value specified for Do")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(context.Context, clean.Input) error); ok {
r0 = returnFunc(ctx, in)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_Do_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Do'
type MockInterface_Do_Call struct {
*mock.Call
}
// Do is a helper method to define mock.On call
// - ctx context.Context
// - in clean.Input
func (_e *MockInterface_Expecter) Do(ctx interface{}, in interface{}) *MockInterface_Do_Call {
return &MockInterface_Do_Call{Call: _e.mock.On("Do", ctx, in)}
}
func (_c *MockInterface_Do_Call) Run(run func(ctx context.Context, in clean.Input)) *MockInterface_Do_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 clean.Input
if args[1] != nil {
arg1 = args[1].(clean.Input)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_Do_Call) Return(err error) *MockInterface_Do_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_Do_Call) RunAndReturn(run func(ctx context.Context, in clean.Input) error) *MockInterface_Do_Call {
_c.Call.Return(run)
return _c
}

View File

@@ -1,96 +0,0 @@
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package credentialplugin_mock
import (
"context"
"github.com/int128/kubelogin/pkg/usecases/credentialplugin"
mock "github.com/stretchr/testify/mock"
)
// NewMockInterface creates a new instance of MockInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewMockInterface(t interface {
mock.TestingT
Cleanup(func())
}) *MockInterface {
mock := &MockInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// MockInterface is an autogenerated mock type for the Interface type
type MockInterface struct {
mock.Mock
}
type MockInterface_Expecter struct {
mock *mock.Mock
}
func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
return &MockInterface_Expecter{mock: &_m.Mock}
}
// Do provides a mock function for the type MockInterface
func (_mock *MockInterface) Do(ctx context.Context, in credentialplugin.Input) error {
ret := _mock.Called(ctx, in)
if len(ret) == 0 {
panic("no return value specified for Do")
}
var r0 error
if returnFunc, ok := ret.Get(0).(func(context.Context, credentialplugin.Input) error); ok {
r0 = returnFunc(ctx, in)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockInterface_Do_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Do'
type MockInterface_Do_Call struct {
*mock.Call
}
// Do is a helper method to define mock.On call
// - ctx context.Context
// - in credentialplugin.Input
func (_e *MockInterface_Expecter) Do(ctx interface{}, in interface{}) *MockInterface_Do_Call {
return &MockInterface_Do_Call{Call: _e.mock.On("Do", ctx, in)}
}
func (_c *MockInterface_Do_Call) Run(run func(ctx context.Context, in credentialplugin.Input)) *MockInterface_Do_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 credentialplugin.Input
if args[1] != nil {
arg1 = args[1].(credentialplugin.Input)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *MockInterface_Do_Call) Return(err error) *MockInterface_Do_Call {
_c.Call.Return(err)
return _c
}
func (_c *MockInterface_Do_Call) RunAndReturn(run func(ctx context.Context, in credentialplugin.Input) error) *MockInterface_Do_Call {
_c.Call.Return(run)
return _c
}

Some files were not shown because too many files have changed in this diff Show More