Compare commits

...

240 Commits

Author SHA1 Message Date
Hidetake Iwata
775841a72b Remove deprecation of standalone mode (#680)
* Remove deprecation of standalone mode

* Update standalone-mode.md
2022-01-08 17:48:00 +09:00
renovate[bot]
564177cd18 fix(deps): update module k8s.io/klog/v2 to v2.40.1 (#679)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-19 04:11:07 +00:00
renovate[bot]
3f5514573d fix(deps): update module k8s.io/klog/v2 to v2.40.0 (#678)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-17 16:41:23 +00:00
renovate[bot]
a70c0e21b9 fix(deps): update module github.com/spf13/cobra to v1.3.0 (#677)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-14 23:53:29 +00:00
renovate[bot]
6a8dd65a1a fix(deps): update module github.com/golang-jwt/jwt/v4 to v4.2.0 (#672)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-03 20:38:18 +00:00
renovate[bot]
6b5947b174 chore(deps): update dependency golangci/golangci-lint to v1.43.0 (#669)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-28 07:10:52 +00:00
Hidetake Iwata
6ff75291d7 Use Renovate config in int128/go-actions 2021-11-28 15:44:45 +09:00
Hidetake Iwata
3cb6988b8b Refactor workflows (#668)
* Refactor workflows

* Fix runs-on

* Fix Dockerfile
2021-11-28 15:10:23 +09:00
renovate[bot]
2a14902541 fix(deps): update module github.com/chromedp/chromedp to v0.7.6 (#667)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-27 04:25:47 +00:00
renovate[bot]
78d9d1f913 fix(deps): update golang.org/x/net commit hash to d83791d (#666)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-23 23:03:26 +00:00
renovate[bot]
9f39c00cc8 fix(deps): update module k8s.io/client-go to v0.22.4 (#663)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-18 21:14:24 +00:00
renovate[bot]
6c767e8545 fix(deps): update golang.org/x/net commit hash to 6a13c67 (#664)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-18 18:27:01 +00:00
renovate[bot]
ccb00b7b58 fix(deps): update module k8s.io/apimachinery to v0.22.4 (#662)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-18 04:48:38 +00:00
renovate[bot]
cf8b89d6ae fix(deps): update golang.org/x/oauth2 commit hash to d3ed0bb (#661)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-17 03:58:13 +00:00
renovate[bot]
86a8721cac fix(deps): update golang.org/x/net commit hash to 47ca1ff (#660)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-17 01:37:59 +00:00
renovate[bot]
5f2ae5497f Merge pull request #657 from int128/renovate/k8s.io-client-go-0.x
fix(deps): update module k8s.io/client-go to v0.22.3
2021-10-28 10:29:01 +00:00
Renovate Bot
e89de7de0f fix(deps): update module k8s.io/client-go to v0.22.3 2021-10-28 10:28:26 +00:00
renovate[bot]
fd5580861f Merge pull request #656 from int128/renovate/k8s.io-apimachinery-0.x
fix(deps): update module k8s.io/apimachinery to v0.22.3
2021-10-28 10:26:21 +00:00
Renovate Bot
4382333ac9 fix(deps): update module k8s.io/apimachinery to v0.22.3 2021-10-28 10:26:17 +00:00
renovate[bot]
821265c912 fix(deps): update module k8s.io/klog/v2 to v2.30.0 (#654)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-22 15:17:05 +00:00
renovate[bot]
f5f0c7eadd fix(deps): update golang.org/x/net commit hash to d418f37 (#653)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-20 09:47:35 +00:00
Hidetake Iwata
b1af578679 Fix docker push error 2021-10-20 12:20:53 +09:00
renovate[bot]
5737f96665 fix(deps): update golang.org/x/net commit hash to c6ed85c (#652)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-20 02:08:53 +00:00
renovate[bot]
07129f8c77 fix(deps): update golang.org/x/net commit hash to 4f30a5c (#651)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-15 22:57:15 +00:00
renovate[bot]
931d4f3a89 fix(deps): update golang.org/x/net commit hash to fd004c5 (#650)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-15 01:31:08 +00:00
renovate[bot]
f76e186d72 fix(deps): update golang.org/x/net commit hash to 2b766c0 (#649)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-14 20:01:03 +00:00
renovate[bot]
3e44d7655f fix(deps): update golang.org/x/net commit hash to e13a265 (#648)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-13 20:13:11 +00:00
renovate[bot]
14f57e7232 fix(deps): update golang.org/x/net commit hash to ee2e9a0 (#647)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-13 16:48:16 +00:00
renovate[bot]
af8ae6f2f6 fix(deps): update module github.com/int128/oauth2cli to v1.14.0 (#646)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-12 02:40:53 +00:00
renovate[bot]
83a4524487 fix(deps): update golang.org/x/net commit hash to caeb26a (#645)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-11 18:40:20 +00:00
renovate[bot]
aeda430a1f fix(deps): update golang.org/x/net commit hash to 3b03d30 (#644)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-08 21:53:21 +00:00
renovate[bot]
10fdb63be3 fix(deps): update golang.org/x/net commit hash to 59d4e92 (#643)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-07 17:24:54 +00:00
renovate[bot]
fb439187d3 fix(deps): update golang.org/x/net commit hash to 62292e8 (#642)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-06 21:05:48 +00:00
renovate[bot]
73359e62db fix(deps): update golang.org/x/net commit hash to d2e5035 (#641)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-06 00:05:56 +00:00
renovate[bot]
c38b41cf85 fix(deps): update golang.org/x/oauth2 commit hash to 6b3c2da (#640)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-05 19:59:18 +00:00
renovate[bot]
e149f6443f fix(deps): update module github.com/golang-jwt/jwt/v4 to v4.1.0 (#636)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-05 13:35:11 +00:00
renovate[bot]
3c0bca7dee fix(deps): update golang.org/x/term commit hash to 03fcf44 (#637)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-05 11:33:50 +00:00
renovate[bot]
43497466f9 fix(deps): update golang.org/x/net commit hash to d4b1ae0 (#635)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-05 09:25:27 +00:00
Hidetake Iwata
0075043866 Use int128/docker-build-cache-config-action (#639) 2021-10-05 16:33:30 +09:00
renovate[bot]
b160aea35e fix(deps): update golang.org/x/net commit hash to 3c21e5b (#634)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-17 19:13:50 +00:00
renovate[bot]
52311525fe fix(deps): update module k8s.io/client-go to v0.22.2 (#632)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-17 17:31:25 +00:00
renovate[bot]
ab923d0d49 fix(deps): update module k8s.io/apimachinery to v0.22.2 (#631)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-17 16:05:50 +00:00
renovate[bot]
97908c775f fix(deps): update module github.com/coreos/go-oidc/v3 to v3.1.0 (#630)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-17 10:16:07 +00:00
renovate[bot]
c6662f32fb fix(deps): update golang.org/x/term commit hash to 140adaa (#629)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-17 00:08:20 +00:00
renovate[bot]
a07a1eb7e8 fix(deps): update golang.org/x/net commit hash to 12bc252 (#626)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-16 04:35:33 +00:00
renovate[bot]
f5f73df697 fix(deps): update github.com/pkg/browser commit hash to 681adbf (#625)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-11 10:11:49 +00:00
renovate[bot]
ae3a001dd2 fix(deps): update golang.org/x/net commit hash to a5e0955 (#624)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-08 21:19:10 +00:00
renovate[bot]
eb8f211e67 fix(deps): update golang.org/x/net commit hash to ff17edf (#623)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-08 00:40:24 +00:00
Hidetake Iwata
237e53313d Add flag to set custom browser command (#622)
* Add flag to set custom browser command

* Use --browser-command in system_test

* Add --browser-command= to setup message
2021-09-05 11:35:03 +09:00
renovate[bot]
8cce70c302 fix(deps): update github.com/pkg/browser commit hash to 6d279e1 (#621)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-04 02:44:53 +00:00
renovate[bot]
751b62b418 fix(deps): update golang.org/x/net commit hash to ad29c8a (#620)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-03 19:31:19 +00:00
renovate[bot]
1aeb6b0c0d fix(deps): update golang.org/x/net commit hash to 8d99171 (#619)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-02 19:08:13 +00:00
renovate[bot]
58d354f6bc fix(deps): update module k8s.io/klog/v2 to v2.20.0 (#618)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-02 07:32:31 +00:00
Hidetake Iwata
077d9ab90c Use docker/metadata-action@v3 (#616) 2021-08-28 22:10:41 +09:00
Joel Kaasinen
50bc986085 update README.md (#615) 2021-08-28 08:03:20 +09:00
renovate[bot]
75698aee0c fix(deps): update golang.org/x/net commit hash to e898025 (#613)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-25 19:59:14 +00:00
renovate[bot]
a17137b4e9 fix(deps): update module k8s.io/client-go to v0.22.1 (#612)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-20 00:16:31 +00:00
renovate[bot]
395a36eb53 fix(deps): update module k8s.io/apimachinery to v0.22.1 (#611)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-19 22:44:00 +00:00
renovate[bot]
b86118034f fix(deps): update golang.org/x/oauth2 commit hash to 2bc19b1 (#610)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-19 21:04:49 +00:00
renovate[bot]
a4b63da790 fix(deps): update golang.org/x/oauth2 commit hash to 7df4dd6 (#609)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-18 02:05:49 +00:00
renovate[bot]
b0d9ff907b chore(deps): update golang docker tag to v1.17 (#608)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-17 04:36:40 +00:00
renovate[bot]
c8d7fa5009 fix(deps): update golang.org/x/oauth2 commit hash to faf39c7 (#606)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-13 20:13:09 +00:00
renovate[bot]
99fc66e205 fix(deps): update golang.org/x/net commit hash to 60bc85c (#607)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-13 18:16:01 +00:00
Hidetake Iwata
33275b0a30 Export BuildKit cache to GHCR (#605) 2021-08-09 22:53:48 +09:00
Hidetake Iwata
9a850d7072 Migrate to github.com/golang-jwt/jwt/v4 (#604) 2021-08-08 11:39:45 +09:00
renovate[bot]
e5981c49c8 chore(deps): update codecov/codecov-action action to v2 (#595) 2021-08-08 10:51:03 +09:00
renovate[bot]
226683c051 fix(deps): update golang.org/x/oauth2 commit hash to 6f1e639 (#602)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-05 20:23:53 +00:00
renovate[bot]
3d6cfe5054 fix(deps): update golang.org/x/net commit hash to aaa1db6 (#603)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-05 19:35:51 +00:00
renovate[bot]
8cba4b4647 fix(deps): update module k8s.io/client-go to v0.22.0 (#601)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-04 23:32:41 +00:00
renovate[bot]
f1b17d2fc1 fix(deps): update module k8s.io/apimachinery to v0.22.0 (#600)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-04 22:57:35 +00:00
Pedro Kiefer
a0cfde7198 refactor: add --oidc-use-pkce flag to force PKCE authorization flow (#599) 2021-08-04 06:38:26 +09:00
renovate[bot]
680dfeea68 fix(deps): update golang.org/x/net commit hash to c6fcb2d (#598)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-26 23:54:50 +00:00
renovate[bot]
103451e68d fix(deps): update golang.org/x/net commit hash to 853a461 (#594)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-16 21:48:03 +00:00
renovate[bot]
480c8305b1 fix(deps): update module k8s.io/client-go to v0.21.3 (#593)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-16 09:00:36 +00:00
renovate[bot]
e472a4b261 fix(deps): update module k8s.io/apimachinery to v0.21.3 (#592)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-16 07:51:53 +00:00
renovate[bot]
849bf27c09 fix(deps): update module k8s.io/klog/v2 to v2.10.0 (#591)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-15 11:13:45 +00:00
renovate[bot]
b5462d49ad fix(deps): update module github.com/chromedp/chromedp to v0.7.4 (#589)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-13 08:52:17 +00:00
renovate[bot]
08fdfa8a61 fix(deps): update github.com/pkg/browser commit hash to 7d21f8c (#588)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-06 15:47:15 +00:00
renovate[bot]
d10adc61cf fix(deps): update module github.com/spf13/cobra to v1.2.1 (#587)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-02 16:36:21 +00:00
renovate[bot]
9ecf09d7bc fix(deps): update module github.com/spf13/cobra to v1.2.0 (#586)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-01 19:22:49 +00:00
renovate[bot]
d92802565d chore(deps): update rajatjindal/krew-release-bot action to v0.0.40 (#583)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-28 21:25:12 +00:00
renovate[bot]
14c55a1312 fix(deps): update golang.org/x/oauth2 commit hash to a41e5a7 (#585)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-28 20:09:04 +00:00
renovate[bot]
aa912cf6d0 fix(deps): update golang.org/x/oauth2 commit hash to a8dc77f (#582)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-22 22:26:23 +00:00
renovate[bot]
7e15541455 fix(deps): update golang.org/x/oauth2 commit hash to bce0382 (#581)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-22 20:21:24 +00:00
renovate[bot]
eb205ebbe8 fix(deps): update golang.org/x/oauth2 commit hash to 14747e6 (#580)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-22 18:49:24 +00:00
renovate[bot]
dc5cc7e7ad fix(deps): update github.com/pkg/browser commit hash to c198bc9 (#579)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-21 11:06:17 +00:00
renovate[bot]
06fa24bcee fix(deps): update module k8s.io/client-go to v0.21.2 (#578)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-18 02:31:09 +00:00
renovate[bot]
e5469925f5 fix(deps): update module k8s.io/apimachinery to v0.21.2 (#577)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-18 01:14:34 +00:00
renovate[bot]
6dbd197e8c fix(deps): update golang.org/x/oauth2 commit hash to d040287 (#576)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-15 20:28:06 +00:00
renovate[bot]
0e10951907 fix(deps): update golang.org/x/term commit hash to 6886f2d (#575)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-15 18:15:51 +00:00
renovate[bot]
b23f036445 fix(deps): update golang.org/x/net commit hash to 04defd4 (#573)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-14 20:00:31 +00:00
renovate[bot]
63f08f2f7a fix(deps): update module github.com/golang/mock to v1.6.0 (#572)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-11 23:01:45 +00:00
renovate[bot]
2006d13375 fix(deps): update golang.org/x/net commit hash to 84b48f8 (#571)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-10 15:28:35 +00:00
renovate[bot]
fb4d9663d5 fix(deps): update golang.org/x/net commit hash to 52da8fb (#570)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-10 13:26:15 +00:00
Hidetake Iwata
82e96fba84 Explicitly set CGO_ENABLED on build (#569) 2021-06-07 13:04:39 +09:00
renovate[bot]
8a725104e1 fix(deps): update github.com/pkg/browser commit hash to a7b7a61 (#568)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-07 01:18:44 +00:00
renovate[bot]
96d6492825 fix(deps): update module github.com/chromedp/chromedp to v0.7.3 (#566)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-27 08:08:35 +00:00
renovate[bot]
f40dc4c409 fix(deps): update module k8s.io/klog/v2 to v2.9.0 (#565)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-26 13:11:29 +00:00
renovate[bot]
7b9bb9e479 fix(deps): update golang.org/x/net commit hash to abc4532 (#564)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-25 09:34:54 +00:00
renovate[bot]
f0cb7ec1eb fix(deps): update module github.com/google/go-cmp to v0.5.6 (#563)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-25 04:58:38 +00:00
renovate[bot]
973674300e fix(deps): update golang.org/x/net commit hash to fe42d45 (#560)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-21 21:42:14 +00:00
renovate[bot]
0479bf6c68 fix(deps): update module k8s.io/client-go to v0.21.1 (#557)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-19 12:46:40 +00:00
renovate[bot]
93f2c88644 fix(deps): update module k8s.io/apimachinery to v0.21.1 (#556)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-19 11:05:03 +00:00
Suraj Banakar(बानकर) | スラジ
966e612b14 Update README.md (#558) 2021-05-19 17:50:48 +09:00
renovate[bot]
7a4099ed65 fix(deps): update golang.org/x/net commit hash to 4163338 (#554)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-14 19:23:02 +00:00
renovate[bot]
62e3d07d18 fix(deps): update golang.org/x/oauth2 commit hash to f6687ab (#555)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-14 18:13:42 +00:00
renovate[bot]
92fe0f1c3f fix(deps): update golang.org/x/net commit hash to 81045d8 (#553)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-10 11:37:56 +00:00
renovate[bot]
0903aa5636 fix(deps): update golang.org/x/net commit hash to 16afe75 (#551)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-09 01:03:58 +00:00
renovate[bot]
3338116bfb fix(deps): update module github.com/chromedp/chromedp to v0.7.2 (#552)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-08 23:22:53 +00:00
Hidetake Iwata
d0364f0942 Fix "~" is not expanded on Windows (#550)
* Run tests on macOS and Windows

* Use filepath and client-go/util/homedir package
2021-05-04 11:12:10 +09:00
renovate[bot]
ea78452b52 fix(deps): update golang.org/x/term commit hash to a79de54 (#549)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-03 10:11:02 +00:00
renovate[bot]
2d52355a37 fix(deps): update golang.org/x/net commit hash to 7fd8e65 (#548)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-03 08:49:41 +00:00
renovate[bot]
98895d12e9 fix(deps): update module github.com/chromedp/chromedp to v0.7.1 (#544)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-02 03:06:10 +00:00
renovate[bot]
bfac26d6d6 fix(deps): update golang.org/x/term commit hash to c04ba85 (#547)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-02 01:52:59 +00:00
renovate[bot]
a784b7a60b fix(deps): update golang.org/x/oauth2 commit hash to 81ed05c (#545)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-02 01:00:41 +00:00
renovate[bot]
2702f9259e fix(deps): update golang.org/x/net commit hash to f8dd838 (#546)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-02 00:05:03 +00:00
renovate[bot]
edf1bd705b fix(deps): update golang.org/x/net commit hash to 5f58ad6 (#542)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-23 22:23:22 +00:00
renovate[bot]
d8ab06b0a4 fix(deps): update golang.org/x/term commit hash to f5beecf (#541)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-22 15:16:49 +00:00
renovate[bot]
e1bb47bad2 fix(deps): update golang.org/x/net commit hash to 4e50805 (#540)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-22 02:21:55 +00:00
renovate[bot]
8dce91cc2d fix(deps): update golang.org/x/term commit hash to b80969c (#539)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-22 00:12:49 +00:00
renovate[bot]
415a52bc68 fix(deps): update golang.org/x/net commit hash to 798c215 (#538)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-20 23:01:52 +00:00
renovate[bot]
d20ceb5262 fix(deps): update golang.org/x/net commit hash to d25e304 (#537)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-20 11:31:08 +00:00
Hidetake Iwata
4ca11f13ac Add .exe extension to Windows distribution (#534) 2021-04-17 14:50:58 +09:00
Peter Holko (Ping Identity)
0b6d34e1a2 Update setup.md (#532)
Adding Ping Identity section to the setup guide
2021-04-17 14:05:28 +09:00
renovate[bot]
79882f6e3a fix(deps): update module k8s.io/client-go to v0.21.0 (#529)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-16 14:56:09 +00:00
renovate[bot]
f66835b04e fix(deps): update module k8s.io/apimachinery to v0.21.0 (#528)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-16 11:07:26 +00:00
renovate[bot]
487bbe7c9c fix(deps): update golang.org/x/term commit hash to 72f3dc4 (#527)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-16 09:01:43 +00:00
renovate[bot]
30a961dbd1 fix(deps): update golang.org/x/oauth2 commit hash to 5e61552 (#531)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-16 04:48:12 +00:00
renovate[bot]
1460c8158f fix(deps): update golang.org/x/net commit hash to e915ea6 (#530)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-16 02:45:13 +00:00
renovate[bot]
33f62ff368 fix(deps): update golang.org/x/net commit hash to a5a99cb (#525)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-05 20:18:16 +00:00
renovate[bot]
9668d0f057 fix(deps): update golang.org/x/oauth2 commit hash to 2e8d934 (#526)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-02 18:25:01 +00:00
renovate[bot]
5532f16f42 fix(deps): update golang.org/x/net commit hash to cb1fcc7 (#524)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-31 09:06:10 +00:00
renovate[bot]
add68e27e5 fix(deps): update golang.org/x/oauth2 commit hash to 22b0ada (#520)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-31 02:08:50 +00:00
renovate[bot]
bddce9d830 fix(deps): update golang.org/x/net commit hash to e572328 (#523)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-31 00:24:08 +00:00
renovate[bot]
7c2b049e5d fix(deps): update golang.org/x/net commit hash to cd0ac97 (#521)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-30 21:50:37 +00:00
Hidetake Iwata
9e354b4fe5 Build image on pull request (#522) 2021-03-28 12:52:12 +09:00
renovate[bot]
835ad7ad55 fix(deps): update module github.com/chromedp/chromedp to v0.6.10 (#518)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-24 12:26:07 +00:00
renovate[bot]
1e0b070f57 fix(deps): update golang.org/x/net commit hash to 2c4c8ec (#519)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-24 10:57:43 +00:00
Hidetake Iwata
0ccccbcb36 Do not build image on pull request (#516) 2021-03-22 14:07:08 +09:00
Hidetake Iwata
02ad24e6fe Update README.md 2021-03-22 13:49:27 +09:00
Hidetake Iwata
c011fef38c Delete .circleci directory 2021-03-22 13:48:04 +09:00
Hidetake Iwata
822ea91d21 Delete dist directory 2021-03-22 13:47:04 +09:00
Hidetake Iwata
6322b6e1fb Fix krew release (#515)
* Fix .krew.yaml

time="2021-03-22T04:06:39Z" level=fatal msg="template: .krew.yaml:28:9: executing \".krew.yaml\" at <addURIAndSha \"https://github.com/int128/kubelogin/releases/download/{{ TagName }}/kubelogin_linux_amd64.zip\" .TagName>: error calling addURIAndSha: template: url:1: function \"TagName\" not defined"

* Fix indent error
2021-03-22 13:39:51 +09:00
renovate[bot]
fa0633f8c5 fix(deps): update module k8s.io/client-go to v0.20.5 (#508)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-22 13:02:02 +09:00
Hidetake Iwata
74c9404e69 Fix multi-platform images (#514) 2021-03-22 12:10:22 +09:00
Hidetake Iwata
037d26b01f Publish multi-platform image to GHCR (#513) 2021-03-22 10:53:50 +09:00
Hidetake Iwata
256ce07e1f Migrate to krew-release-bot (#512) 2021-03-22 09:31:33 +09:00
Hidetake Iwata
23e85f8689 Migrate release to GitHub Actions (#511) 2021-03-21 22:36:25 +09:00
renovate[bot]
eb9a4121fb fix(deps): update golang.org/x/net commit hash to d523dce (#504)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-21 11:10:44 +00:00
renovate[bot]
7e24925248 fix(deps): update module k8s.io/apimachinery to v0.20.5 (#507)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-21 09:52:28 +00:00
renovate[bot]
b4f0f7feef fix(deps): update module github.com/chromedp/chromedp to v0.6.9 (#510)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-21 05:30:37 +00:00
renovate[bot]
fdff33f3df fix(deps): update golang.org/x/term commit hash to de623e6 (#506)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-17 17:40:54 +00:00
renovate[bot]
64294b9fa0 fix(deps): update golang.org/x/net commit hash to 34ac3e1 (#503)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-15 20:08:27 +00:00
Hidetake Iwata
9dcfb3a42c Migrate go test to GitHub Actions (#502) 2021-03-15 22:20:16 +09:00
Hidetake Iwata
644a7b0120 Fix trigger of system-test workflow (#501) 2021-03-15 19:28:10 +09:00
Hidetake Iwata
5f07f72889 Update get-token flags (#500) 2021-03-15 19:27:35 +09:00
Hidetake Iwata
d8af534b0b Update to Go 1.16 in system-test (#499) 2021-03-15 19:11:32 +09:00
Hidetake Iwata
eb7ce56909 Expand homedir paths in get-token options (#498)
* Expand homedir paths in get-token options

* Replace go-homedir with Go 1.16 os.UserHomeDir()
2021-03-15 19:03:11 +09:00
Mattias Appelgren
97cc85d079 repository: Expand ~ in homedir (#489)
Fixes: https://github.com/int128/kubelogin/issues/488
2021-03-14 09:07:29 +09:00
renovate[bot]
f849094c58 Update golang.org/x/oauth2 commit hash to cd4f82c (#496)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-13 20:03:37 +00:00
renovate[bot]
ebdda69c29 Update module k8s.io/klog/v2 to v2.8.0 (#495)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-13 03:56:56 +00:00
renovate[bot]
4d59503ebc Update cimg/go Docker tag to v1.16.2 (#494)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-13 01:02:56 +00:00
renovate[bot]
1a0e6ca973 Update module k8s.io/klog/v2 to v2.7.0 (#492)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-12 04:27:02 +00:00
renovate[bot]
c0d389588b Update golang.org/x/oauth2 commit hash to 5366d9d (#491)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-11 18:33:43 +00:00
renovate[bot]
ca9d3fad89 Update module github.com/chromedp/chromedp to v0.6.8 (#490)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-06 03:25:51 +00:00
renovate[bot]
6a6548c79a Update module github.com/google/go-cmp to v0.5.5 (#487)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-04 19:02:07 +00:00
renovate[bot]
bcaea01da7 Update module k8s.io/klog/v2 to v2.6.0 (#486)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-03 13:52:19 +00:00
renovate[bot]
3bf92a9ac1 Update golang.org/x/net commit hash to e18ecbb (#485)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-26 20:12:10 +00:00
renovate[bot]
78ece2f513 Update golang.org/x/net commit hash to 39120d0 (#484)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-26 13:40:01 +00:00
renovate[bot]
a9bf7a019a Update golang.org/x/net commit hash to 3d97a24 (#483)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-24 10:29:16 +00:00
renovate[bot]
03da20fe4d Update golang.org/x/net commit hash to 9060382 (#482)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-22 22:27:26 +00:00
renovate[bot]
1150aa45f8 Update module github.com/chromedp/chromedp to v0.6.6 (#481)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-22 09:53:39 +00:00
renovate[bot]
2513c3ce2c Update module github.com/golang/mock to v1.5.0 (#480)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-20 09:53:52 +00:00
renovate[bot]
f2e0a79817 Update golang.org/x/term commit hash to 6a3ed07 (#478)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-20 08:46:56 +00:00
renovate[bot]
3a59aad12a Update golang.org/x/sync commit hash to 036812b (#479)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-20 07:22:51 +00:00
renovate[bot]
21a5729719 Update golang.org/x/net commit hash to 5f55cee (#477)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-20 05:13:53 +00:00
renovate[bot]
60ae1f9d4b Update golang.org/x/oauth2 commit hash to 9bb9049 (#475)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-20 03:34:53 +00:00
renovate[bot]
b9f0f4b5b0 Update module k8s.io/client-go to v0.20.4 (#474)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-19 07:16:37 +00:00
renovate[bot]
5d0cbfeee5 Update module k8s.io/apimachinery to v0.20.4 (#473)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-19 03:51:33 +00:00
renovate[bot]
5818363cfd Update golang.org/x/oauth2 commit hash to ba52d33 (#472)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-18 23:11:11 +00:00
renovate[bot]
3cc4811a8c Update module k8s.io/client-go to v0.20.3 (#471)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-18 05:43:27 +00:00
renovate[bot]
a0c798ebfe Update module k8s.io/apimachinery to v0.20.3 (#470)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-18 03:38:31 +00:00
renovate[bot]
4e0d73e7b2 Update golang.org/x/oauth2 commit hash to 16ff188 (#469)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-16 21:42:37 +00:00
Hidetake Iwata
86681b82c5 Refactor homebrew formula (#468) 2021-02-11 20:27:52 +09:00
Hidetake Iwata
cc231f7f81 Create question.md 2021-02-11 17:55:57 +09:00
renovate[bot]
44ffd69cbf Update module coreos/go-oidc to v3 (#463)
* Update module coreos/go-oidc to v3

* Update import path to github.com/coreos/go-oidc/v3/oidc

Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Hidetake Iwata <int128@gmail.com>
2021-02-11 17:23:22 +09:00
Hidetake Iwata
c3f636300e Update to golangci-lint v1.36.0 (#460)
* Update golangci-lint.yaml

* Use golang.org/x/term

SA1019: package golang.org/x/crypto/ssh/terminal is deprecated: this package moved to golang.org/x/term.  (staticcheck)

* Add workflow name
2021-02-11 17:14:32 +09:00
renovate[bot]
f1a2539262 Update module spf13/cobra to v1.1.3 (#466)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-10 22:00:51 +00:00
renovate[bot]
5e7cb2aff1 Update golang.org/x/oauth2 commit hash to 6667018 (#465)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-10 21:23:31 +00:00
renovate[bot]
e47054ccdb Update module spf13/cobra to v1.1.2 (#464)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-10 03:26:03 +00:00
renovate[bot]
51d5af57cc Update cimg/go Docker tag to v1.15.8 (#462)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-05 00:42:53 +00:00
renovate[bot]
f29ea3a1c7 Update golang.org/x/oauth2 commit hash to 0101308 (#461)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-01 18:00:27 +00:00
renovate[bot]
1bb8fb2dc9 Update module k8s.io/klog/v2 to v2.5.0 (#459)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-27 19:33:47 +00:00
renovate[bot]
3e5c3e5918 Update golang.org/x/oauth2 commit hash to f9ce19e (#458)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-26 20:48:53 +00:00
renovate[bot]
cf85625b56 Update module google/wire to v0.5.0 (#457)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-26 03:37:13 +00:00
renovate[bot]
6987910fe6 Update golang.org/x/oauth2 commit hash to af13f52 (#456)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-25 21:04:32 +00:00
renovate[bot]
d89c2dc961 Update module coreos/go-oidc to v3 (#442)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-23 15:19:17 +09:00
renovate[bot]
4e2a99ba8e Update module chromedp/chromedp to v0.6.5 (#455)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-22 14:56:36 +00:00
renovate[bot]
3e915d0811 Update cimg/go Docker tag to v1.15.7 (#454)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-20 17:15:53 +00:00
renovate[bot]
1ca272f61a Update golang.org/x/net commit hash to 5f4716e (#453)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-19 21:31:20 +00:00
renovate[bot]
5b1cc1c994 Update module chromedp/chromedp to v0.6.4 (#452)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-17 02:49:01 +00:00
renovate[bot]
fa416f2910 Update github.com/pkg/browser commit hash to ce105d0 (#450)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-15 05:13:23 +00:00
renovate[bot]
ad65baa624 Update alpine Docker tag to v3.13 (#449)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-15 03:19:59 +00:00
renovate[bot]
25b8d29a44 Update module k8s.io/client-go to v0.20.2 (#448)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-14 03:06:18 +00:00
renovate[bot]
92b09e3e6f Update module k8s.io/apimachinery to v0.20.2 (#447)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-14 00:44:36 +00:00
renovate[bot]
10f904adbb Update golang.org/x/oauth2 commit hash to d3ed898 (#446)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-13 22:33:17 +00:00
renovate[bot]
55d2498dae Update golang.org/x/oauth2 commit hash to 8b1d76f (#445)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-13 17:31:48 +00:00
renovate[bot]
eaf5658d45 Update module chromedp/chromedp to v0.6.1 (#444)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-13 06:34:34 +00:00
renovate[bot]
5608c76764 Update golang.org/x/oauth2 commit hash to 01de73c (#443)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-12 21:18:59 +00:00
Hidetake Iwata
4915ce165f Delete CODEOWNERS 2021-01-11 11:55:00 +09:00
renovate[bot]
bd186d6cfc Update golang.org/x/sync commit hash to 09787c9 (#390)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-10 18:45:47 +00:00
renovate[bot]
411b42b6af Update golang.org/x/oauth2 commit hash to 08078c5 (#415)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-10 17:28:10 +00:00
renovate[bot]
671ee8ecf1 Update golang.org/x/net commit hash to 6772e93 (#407)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-10 16:36:05 +00:00
renovate[bot]
e5b179dfec Update golang.org/x/crypto commit hash to eec23a3 (#391)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-10 15:42:21 +00:00
renovate[bot]
435cdf4a78 Update github.com/pkg/browser commit hash to 0426ae3 (#416)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-10 14:12:42 +00:00
Hidetake Iwata
f94ff640d6 Update renovate.json 2021-01-10 22:49:33 +09:00
renovate[bot]
9be8740f45 Update cimg/go Docker tag to v1.15.6 (#434)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-10 13:16:22 +00:00
Hidetake Iwata
637d091b40 Enable renovate-approve 2021-01-10 21:36:03 +09:00
Hidetake Iwata
637fc746fd Enable automerge 2021-01-10 21:32:24 +09:00
renovate[bot]
7e063b5dda Update module yaml to v2.4.0 (#433)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-10 17:45:49 +09:00
renovate[bot]
202a8616c6 Update module chromedp/chromedp to v0.6.0 (#435)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-09 21:44:56 +09:00
renovate[bot]
21b88cf037 Update module k8s.io/klog/v2 to v2.4.0 (#441)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-01-09 20:17:26 +09:00
Yuri V
38772898fc Switch klog to v2 (#439)
Co-authored-by: Hidetake Iwata <int128@gmail.com>
2021-01-09 18:51:04 +09:00
Hidetake Iwata
58d1839f3e Create CODEOWNERS 2021-01-09 18:13:47 +09:00
renovate[bot]
c7606e8151 Update module google/go-cmp to v0.5.4 (#432)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-12-19 22:26:34 +09:00
renovate[bot]
eb0f2009e2 Update module k8s.io/client-go to v0.20.1 (#438)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-12-19 21:14:06 +09:00
Hidetake Iwata
cffb00f386 Refactor: extract tests into authentication_test.go (#431) 2020-11-23 18:20:47 +09:00
Hidetake Iwata
8e1a63b1a2 Change mutex scope to bind address port (#430) 2020-11-23 17:41:07 +09:00
Hidetake Iwata
ebf81debe1 Refactor: credentialplugin/get_token_test.go (#429)
* Refactor: extract const vars

* Refactor: extract ROPC test case
2020-11-22 20:01:45 +09:00
Hidetake Iwata
2f271b5870 Refactor: replace Input fields with oidc.Provider (#428) 2020-11-21 12:35:23 +09:00
Hidetake Iwata
b1d8e8f7e1 Refactor: rewrite with Go errors package (#427) 2020-11-21 12:10:42 +09:00
Hidetake Iwata
5a3227409c Refactor: rename to infrastructure package (#426) 2020-11-21 07:56:52 +09:00
Hidetake Iwata
13d232ec21 Refactor: move oidc/client package (#425) 2020-11-21 07:27:34 +09:00
Hidetake Iwata
9bab6b2ccd Refactor: extract tokencache and repository package (#424) 2020-11-20 07:01:16 +09:00
Hidetake Iwata
93fc548544 Refactor: extract kubeconfig package (#423)
* Refactor: extract kubeconfig package

* Refactor: use pass by value instead of reference
2020-11-18 10:36:42 +09:00
Hidetake Iwata
4773b67abd Refactor: extract credentialplugin package (#422) 2020-11-18 10:00:39 +09:00
renovate[bot]
009d03cb69 Update module google/go-cmp to v0.5.3 (#417)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-14 21:24:05 +09:00
renovate[bot]
417c556e8f Update module k8s.io/client-go to v0.19.4 (#420)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-14 20:51:43 +09:00
renovate[bot]
2542d6456c Update cimg/go Docker tag to v1.15.5 (#418)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-14 17:54:11 +09:00
renovate[bot]
e3af16ca8f Update cimg/go Docker tag to v1.15.4 (#410)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-10 21:30:26 +09:00
98 changed files with 2162 additions and 1689 deletions

View File

@@ -1,16 +0,0 @@
.PHONY: all
all:
.PHONY: install-test-deps
install-test-deps:
go get -v github.com/int128/goxzst
.PHONY: install-release-deps
install-release-deps: go
go get -v github.com/int128/goxzst github.com/int128/ghcp
go:
curl -sSfL -o go.tgz "https://golang.org/dl/go`ruby go_version_from_config.rb < config.yml`.darwin-amd64.tar.gz"
tar -xf go.tgz
rm go.tgz
./go/bin/go version

View File

@@ -1,60 +0,0 @@
version: 2.1
jobs:
test:
docker:
- image: cimg/go:1.15.3
steps:
- checkout
- restore_cache:
keys:
- go-sum-{{ checksum "go.sum" }}
- run: make -C .circleci install-test-deps
- run: mkdir -p /tmp/gotest
- run: gotestsum --junitfile /tmp/gotest/gotest.xml -- -v -race -cover -coverprofile=coverage.out ./...
- store_test_results:
path: /tmp/gotest
- save_cache:
key: go-sum-{{ checksum "go.sum" }}
paths:
- ~/go/pkg
- run: bash <(curl -s https://codecov.io/bash)
release:
macos:
# https://circleci.com/docs/2.0/testing-ios/
xcode: 11.5.0
steps:
- run: echo 'export PATH="$HOME/go/bin:$PWD/.circleci/go/bin:$PATH"' >> $BASH_ENV
- checkout
- restore_cache:
keys:
- go-macos-{{ checksum "go.sum" }}
- run: make -C .circleci install-release-deps
- run: make dist
- run: |
if [ "$CIRCLE_TAG" ]; then
make release
fi
- save_cache:
key: go-macos-{{ checksum "go.sum" }}
paths:
- ~/go/pkg
workflows:
version: 2
build:
jobs:
- test:
filters:
tags:
only: /^v.*/
- release:
context: open-source
requires:
- test
filters:
branches:
only: /^release-feature.*/
tags:
only: /^v.*/

View File

@@ -1,3 +0,0 @@
module github.com/int128/kubelogin/.circleci
go 1.13

View File

@@ -1,11 +0,0 @@
require 'yaml'
config = YAML.load(STDIN)
image = config["jobs"]["test"]["docker"][0]["image"]
if !image.start_with?("cimg/go:")
raise "unknown image #{image} in #{configPath}"
end
goVersion = image.delete_prefix("cimg/go:")
print(goVersion)

20
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,20 @@
---
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

View File

@@ -1,8 +0,0 @@
{
"extends": [
"config:base"
],
"postUpdateOptions": [
"gomodTidy"
]
}

6
.github/renovate.json5 vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": [
"config:base",
"github>int128/go-actions",
],
}

52
.github/workflows/docker.yaml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: docker
on:
pull_request:
branches:
- master
paths:
- .github/workflows/docker.yaml
- pkg/**
- go.*
- Dockerfile
- Makefile
push:
branches:
- master
paths:
- .github/workflows/docker.yaml
- pkg/**
- go.*
- Dockerfile
- Makefile
tags:
- v*
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: docker/metadata-action@v3
id: metadata
with:
images: ghcr.io/${{ github.repository }}
- uses: int128/docker-build-cache-config-action@v1
id: cache
with:
image: ghcr.io/${{ github.repository }}/cache
- uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/build-push-action@v2
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

45
.github/workflows/go.yaml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: go
on:
push:
branches:
- master
paths:
- .github/workflows/go.yaml
- pkg/**
- go.*
tags:
- v*
pull_request:
branches:
- master
paths:
- .github/workflows/go.yaml
- pkg/**
- go.*
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: int128/go-actions/setup@v1
with:
go-version: 1.16
- uses: golangci/golangci-lint-action@v2
with:
version: v1.43.0
skip-go-installation: true
skip-pkg-cache: true
skip-build-cache: true
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: int128/go-actions/setup@v1
with:
go-version: 1.16
- run: go test -v -race ./...

View File

@@ -1,14 +0,0 @@
on:
pull_request:
paths:
- .github/workflows/golangci-lint.yaml
- '**.go'
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: golangci/golangci-lint-action@v2
with:
version: v1.30

70
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,70 @@
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: macos-latest
GOOS: darwin
GOARCH: amd64
CGO_ENABLED: 1 # https://github.com/int128/kubelogin/issues/249
- runs-on: macos-latest
GOOS: darwin
GOARCH: arm64
- runs-on: windows-latest
GOOS: windows
GOARCH: amd64
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@v2
- uses: int128/go-actions/setup@v1
with:
go-version: 1.16
- run: go build -ldflags "-X main.version=${GITHUB_REF##*/}"
- uses: int128/go-actions/release@v1
with:
binary: kubelogin
publish:
if: startswith(github.ref, 'refs/tags/')
needs:
- build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- uses: rajatjindal/krew-release-bot@v0.0.40

View File

@@ -1,14 +1,23 @@
name: system-test
on:
pull_request:
paths:
branches:
- master
paths:
- .github/workflows/system-test.yaml
- system_test/**
- pkg/**
- go.*
push:
# for go mod tidy
branches: [master]
paths: [go.*]
branches:
- master
paths:
- .github/workflows/system-test.yaml
- system_test/**
- pkg/**
- go.*
jobs:
system-test:
# https://help.github.com/en/actions/automating-your-workflow-with-github-actions/software-installed-on-github-hosted-runners#ubuntu-1804-lts
@@ -16,7 +25,7 @@ jobs:
steps:
- uses: actions/setup-go@v2
with:
go-version: 1.15
go-version: 1.16
id: go
- uses: actions/checkout@v2
- uses: actions/cache@v2

5
.gitignore vendored
View File

@@ -2,10 +2,9 @@
/acceptance_test/output/
/dist/output
/coverage.out
/kubelogin
/kubectl-oidc_login
/.circleci/go/
/kubelogin_*.zip
/kubelogin_*.zip.sha256

62
.krew.yaml Normal file
View File

@@ -0,0 +1,62 @@
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

12
Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM golang:1.17 as builder
WORKDIR /builder
COPY go.* .
RUN go mod download
COPY main.go .
COPY pkg pkg
RUN go build
FROM gcr.io/distroless/base-debian10
COPY --from=builder /builder/kubelogin /
ENTRYPOINT ["/kubelogin"]

View File

@@ -1,44 +0,0 @@
# CircleCI specific variables
CIRCLE_TAG ?= latest
GITHUB_USERNAME := $(CIRCLE_PROJECT_USERNAME)
GITHUB_REPONAME := $(CIRCLE_PROJECT_REPONAME)
TARGET := kubelogin
TARGET_OSARCH := linux_amd64 darwin_amd64 windows_amd64 linux_arm linux_arm64
VERSION ?= $(CIRCLE_TAG)
LDFLAGS := -X main.version=$(VERSION)
all: $(TARGET)
$(TARGET): $(wildcard **/*.go)
go build -o $@ -ldflags "$(LDFLAGS)"
.PHONY: dist
dist: dist/output
dist/output:
# make the zip files for GitHub Releases
VERSION=$(VERSION) goxzst -d dist/output -i "LICENSE" -o "$(TARGET)" -osarch "$(TARGET_OSARCH)" -t "dist/kubelogin.rb dist/oidc-login.yaml dist/Dockerfile" -- -ldflags "$(LDFLAGS)"
# test the zip file
zipinfo dist/output/kubelogin_linux_amd64.zip
# make the krew yaml structure
mkdir -p dist/output/plugins
mv dist/output/oidc-login.yaml dist/output/plugins/oidc-login.yaml
.PHONY: release
release: dist
# publish the binaries
ghcp release -u "$(GITHUB_USERNAME)" -r "$(GITHUB_REPONAME)" -t "$(VERSION)" dist/output/
# publish the Homebrew formula
ghcp commit -u "$(GITHUB_USERNAME)" -r "homebrew-$(GITHUB_REPONAME)" -b "bump-$(VERSION)" -m "Bump the version to $(VERSION)" -C dist/output/ kubelogin.rb
ghcp pull-request -u "$(GITHUB_USERNAME)" -r "homebrew-$(GITHUB_REPONAME)" -b "bump-$(VERSION)" --title "Bump the version to $(VERSION)"
# publish the Dockerfile
ghcp commit -u "$(GITHUB_USERNAME)" -r "$(GITHUB_REPONAME)-docker" -b "bump-$(VERSION)" -m "Bump the version to $(VERSION)" -C dist/output/ Dockerfile
ghcp pull-request -u "$(GITHUB_USERNAME)" -r "$(GITHUB_REPONAME)-docker" -b "bump-$(VERSION)" --title "Bump the version to $(VERSION)"
# publish the Krew manifest
ghcp fork-commit -u kubernetes-sigs -r krew-index -b "oidc-login-$(VERSION)" -m "Bump oidc-login to $(VERSION)" -C dist/output/ plugins/oidc-login.yaml
.PHONY: clean
clean:
-rm $(TARGET)
-rm -r dist/output/
-rm coverage.out gotest.log

View File

@@ -1,4 +1,4 @@
# kubelogin [![CircleCI](https://circleci.com/gh/int128/kubelogin.svg?style=shield)](https://circleci.com/gh/int128/kubelogin) [![Go Report Card](https://goreportcard.com/badge/github.com/int128/kubelogin)](https://goreportcard.com/report/github.com/int128/kubelogin)
# 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)
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`.
@@ -31,6 +31,8 @@ kubectl krew install oidc-login
choco install kubelogin
```
If you install via GitHub releases, you need to put the `kubelogin` binary on your path under the name `kubectl-oidc_login` so that the [kubectl plugin mechanism](https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/) can find it when you invoke `kubectl oidc-login`. The other install methods do this for you.
You need to set up the OIDC provider, cluster role binding, Kubernetes API server and kubeconfig.
The kubeconfig looks like:
@@ -65,7 +67,7 @@ Kubelogin automatically opens the browser, and you can log in to the provider.
<img src="docs/keycloak-login.png" alt="keycloak-login" width="455" height="329">
After authentication, kubelogin returns the credentials to kubectl and finally kubectl calls the Kubernetes APIs with the credential.
After authentication, kubelogin returns the credentials to kubectl and kubectl then calls the Kubernetes APIs with these credentials.
```
% kubectl get pods
@@ -78,13 +80,13 @@ Kubelogin writes the ID token and refresh token to the token cache file.
If the cached ID token is valid, kubelogin just returns it.
If the cached ID token has expired, kubelogin will refresh the token using the refresh token.
If the refresh token has expired, kubelogin will perform reauthentication.
If the refresh token has expired, kubelogin will perform re-authentication (you will have to login via browser again).
### Troubleshoot
You can log out by removing the token cache directory (default `~/.kube/cache/oidc-login`).
Kubelogin will perform authentication if the token cache file does not exist.
Kubelogin will ask you to login via browser again if the token cache file does not exist i.e., it starts with a clean slate
You can dump claims of an ID token by `setup` command.
@@ -123,7 +125,7 @@ You can verify kubelogin works with your provider using [acceptance test](accept
- [Setup guide](docs/setup.md)
- [Usage and options](docs/usage.md)
- [Standalone mode](docs/standalone-mode.md) (deprecated)
- [Standalone mode](docs/standalone-mode.md)
## Related works
@@ -142,7 +144,7 @@ Your pull request will be merged into master with squash.
### Development
Go 1.15+ is required.
Go 1.16+ is required.
```sh
make

13
dist/Dockerfile vendored
View File

@@ -1,13 +0,0 @@
FROM alpine:3.12
ARG KUBELOGIN_VERSION="{{ env "VERSION" }}"
ARG KUBELOGIN_SHA256="{{ sha256 .linux_amd64_archive }}"
# Download the release and test the checksum
RUN wget -O /kubelogin.zip "https://github.com/int128/kubelogin/releases/download/$KUBELOGIN_VERSION/kubelogin_linux_amd64.zip" && \
echo "$KUBELOGIN_SHA256 /kubelogin.zip" | sha256sum -c - && \
unzip /kubelogin.zip && \
rm /kubelogin.zip
USER daemon
ENTRYPOINT ["/kubelogin"]

27
dist/kubelogin.rb vendored
View File

@@ -1,27 +0,0 @@
class Kubelogin < Formula
desc "A kubectl plugin for Kubernetes OpenID Connect authentication"
homepage "https://github.com/int128/kubelogin"
baseurl = "https://github.com/int128/kubelogin/releases/download"
version "{{ env "VERSION" }}"
if OS.mac?
kernel = "darwin"
sha256 "{{ sha256 .darwin_amd64_archive }}"
elsif OS.linux?
kernel = "linux"
sha256 "{{ sha256 .linux_amd64_archive }}"
end
url baseurl + "/#{version}/kubelogin_#{kernel}_amd64.zip"
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

86
dist/oidc-login.yaml vendored
View File

@@ -1,86 +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: {{ env "VERSION" }}
platforms:
- uri: https://github.com/int128/kubelogin/releases/download/{{ env "VERSION" }}/kubelogin_linux_amd64.zip
sha256: "{{ sha256 .linux_amd64_archive }}"
bin: kubelogin
files:
- from: kubelogin
to: .
- from: LICENSE
to: .
selector:
matchLabels:
os: linux
arch: amd64
- uri: https://github.com/int128/kubelogin/releases/download/{{ env "VERSION" }}/kubelogin_darwin_amd64.zip
sha256: "{{ sha256 .darwin_amd64_archive }}"
bin: kubelogin
files:
- from: kubelogin
to: .
- from: LICENSE
to: .
selector:
matchLabels:
os: darwin
arch: amd64
- uri: https://github.com/int128/kubelogin/releases/download/{{ env "VERSION" }}/kubelogin_windows_amd64.zip
sha256: "{{ sha256 .windows_amd64_archive }}"
bin: kubelogin.exe
files:
- from: kubelogin.exe
to: .
- from: LICENSE
to: .
selector:
matchLabels:
os: windows
arch: amd64
- uri: https://github.com/int128/kubelogin/releases/download/{{ env "VERSION" }}/kubelogin_linux_arm.zip
sha256: "{{ sha256 .linux_arm_archive }}"
bin: kubelogin
files:
- from: kubelogin
to: .
- from: LICENSE
to: .
selector:
matchLabels:
os: linux
arch: arm
- uri: https://github.com/int128/kubelogin/releases/download/{{ env "VERSION" }}/kubelogin_linux_arm64.zip
sha256: "{{ sha256 .linux_arm64_archive }}"
bin: kubelogin
files:
- from: kubelogin
to: .
- from: LICENSE
to: .
selector:
matchLabels:
os: linux
arch: arm64

View File

@@ -129,6 +129,25 @@ You do not need to set `YOUR_CLIENT_SECRET`.
If you need `groups` claim for access control,
see [jetstack/okta-kubectl-auth](https://github.com/jetstack/okta-kubectl-auth/blob/master/docs/okta-setup.md) and [#250](https://github.com/int128/kubelogin/issues/250).
### Ping Identity
Login with an account that has permissions to create applications.
Create an OIDC application with the following configuration:
- Redirect URIs:
- `http://localhost:8000`
- `http://localhost:18000` (used if the port 8000 is already in use)
- Grant type: Authorization Code
- PKCE Enforcement: Required
Leverage the following variables in the next steps.
Variable | Value
------------------------|------
`ISSUER_URL` | `https://auth.pingone.com/<PingOne Tenant Id>/as`
`YOUR_CLIENT_ID` | random string
`YOUR_CLIENT_SECRET` is not required for this configuration.
## 2. Verify authentication

View File

@@ -1,9 +1,12 @@
# Standalone mode
You can run kubelogin as a standalone command.
In this mode, you need to manually run the command before running kubectl.
Kubelogin supports the standalone mode as well.
It writes the token to the kubeconfig (typically `~/.kube/config`) after authentication.
Configure the kubeconfig like:
## Getting started
Configure your kubeconfig like:
```yaml
- name: keycloak
@@ -31,7 +34,7 @@ It automatically opens the browser and you can log in to the provider.
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
@@ -40,7 +43,7 @@ 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
@@ -64,7 +67,7 @@ users:
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
```
@@ -75,8 +78,6 @@ If the refresh token has expired, kubelogin will proceed the authentication.
## Usage
### Kubeconfig
You can set path to the kubeconfig file by the option or the environment variable just like kubectl.
It defaults to `~/.kube/config`.
@@ -104,26 +105,4 @@ Key | Direction | Value
`id-token` | Write | ID token got from the provider.
`refresh-token` | Write | Refresh token got from the provider.
### Extra scopes
You can set the 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
```
Currently kubectl does not accept multiple scopes, so you need to edit the kubeconfig as like:
```sh
kubectl config set-credentials keycloak --auth-provider-arg extra-scopes=SCOPES
sed -i '' -e s/SCOPES/email,profile/ $KUBECONFIG
```
### CA Certificates
You can use your self-signed certificates for the provider.
```sh
kubectl config set-credentials keycloak \
--auth-provider-arg idp-certificate-authority=$HOME/.kube/keycloak-ca.pem
```
See also [usage.md](usage.md).

View File

@@ -11,6 +11,7 @@ Flags:
--oidc-client-id string Client ID of the provider (mandatory)
--oidc-client-secret string Client secret of the provider
--oidc-extra-scope strings Scopes to request to the provider
--oidc-use-pkce Force PKCE usage
--token-cache-dir string Path to a directory for token cache (default "~/.kube/cache/oidc-login")
--certificate-authority stringArray Path to a cert file for the certificate authority
--certificate-authority-data stringArray Base64 encoded cert for the certificate authority
@@ -20,6 +21,7 @@ Flags:
--grant-type string Authorization grant type to use. One of (auto|authcode|authcode-keyboard|password) (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
@@ -31,13 +33,14 @@ Flags:
-h, --help help for get-token
Global Flags:
--add_dir_header If true, adds the file directory to the header
--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
--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
--log_file string If non-empty, use this log file
--log_file_max_size uint Defines the maximum size a log file can grow to. 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)
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
@@ -83,6 +86,16 @@ You can use your self-signed certificate for the provider.
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).
### 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:
@@ -207,7 +220,7 @@ Password:
## Run in Docker
You can run [the Docker image](https://quay.io/repository/int128/kubelogin) instead of the binary.
You can run [the Docker image](https://ghcr.io/int128/kubelogin) instead of the binary.
The kubeconfig looks like:
```yaml
@@ -224,7 +237,7 @@ users:
- /tmp/.token-cache:/.token-cache
- -p
- 8000:8000
- quay.io/int128/kubelogin
- ghcr.io/int128/kubelogin
- get-token
- --token-cache-dir=/.token-cache
- --listen-address=0.0.0.0:8000

39
go.mod
View File

@@ -1,28 +1,25 @@
module github.com/int128/kubelogin
go 1.12
go 1.16
require (
github.com/alexflint/go-filemutex v1.1.0
github.com/chromedp/chromedp v0.5.3
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/golang/mock v1.4.4
github.com/google/go-cmp v0.5.2
github.com/google/wire v0.4.0
github.com/int128/oauth2cli v1.13.0
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/spf13/cobra v1.1.1
github.com/chromedp/chromedp v0.7.6
github.com/coreos/go-oidc/v3 v3.1.0
github.com/golang-jwt/jwt/v4 v4.2.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.6
github.com/google/wire v0.5.0
github.com/int128/oauth2cli v1.14.0
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/spf13/cobra v1.3.0
github.com/spf13/pflag v1.0.5
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/net v0.0.0-20200822124328-c89045814202
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
gopkg.in/yaml.v2 v2.3.0
k8s.io/apimachinery v0.19.3
k8s.io/client-go v0.19.3
k8s.io/klog v1.0.0
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
gopkg.in/yaml.v2 v2.4.0
k8s.io/apimachinery v0.22.4
k8s.io/client-go v0.22.4
k8s.io/klog/v2 v2.40.1
)

634
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,8 @@ import (
"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/pkg/adaptors/browser"
"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"

View File

@@ -60,6 +60,10 @@ func (c *client) Open(url string) error {
return nil
}
func (c *client) OpenCommand(_ context.Context, url, _ string) error {
return c.Open(url)
}
type zeroClient struct {
t *testing.T
}
@@ -68,3 +72,7 @@ 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

@@ -3,11 +3,10 @@ package handler
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"testing"
"golang.org/x/xerrors"
)
func New(t *testing.T, provider Provider) *Handler {
@@ -29,7 +28,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.t.Logf("%d %s %s", wr.statusCode, r.Method, r.RequestURI)
return
}
if errResp := new(ErrorResponse); xerrors.As(err, &errResp) {
if errResp := new(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)
@@ -62,14 +61,14 @@ func (h *Handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
w.Header().Add("Content-Type", "application/json")
e := json.NewEncoder(w)
if err := e.Encode(discoveryResponse); err != nil {
return xerrors.Errorf("could not render json: %w", err)
return fmt.Errorf("could not render json: %w", err)
}
case m == "GET" && p == "/certs":
certificatesResponse := h.provider.GetCertificates()
w.Header().Add("Content-Type", "application/json")
e := json.NewEncoder(w)
if err := e.Encode(certificatesResponse); err != nil {
return xerrors.Errorf("could not render json: %w", err)
return fmt.Errorf("could not render json: %w", err)
}
case m == "GET" && p == "/auth":
q := r.URL.Query()
@@ -84,13 +83,13 @@ func (h *Handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
RawQuery: q,
})
if err != nil {
return xerrors.Errorf("authentication error: %w", err)
return fmt.Errorf("authentication error: %w", err)
}
to := fmt.Sprintf("%s?state=%s&code=%s", redirectURI, state, code)
http.Redirect(w, r, to, 302)
case m == "POST" && p == "/token":
if err := r.ParseForm(); err != nil {
return xerrors.Errorf("could not parse the form: %w", err)
return fmt.Errorf("could not parse the form: %w", err)
}
grantType := r.Form.Get("grant_type")
switch grantType {
@@ -100,12 +99,12 @@ func (h *Handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
CodeVerifier: r.Form.Get("code_verifier"),
})
if err != nil {
return xerrors.Errorf("token request error: %w", err)
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 xerrors.Errorf("could not render json: %w", err)
return fmt.Errorf("could not render json: %w", err)
}
case "password":
// 4.3. Resource Owner Password Credentials Grant
@@ -113,12 +112,12 @@ func (h *Handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
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 xerrors.Errorf("authentication error: %w", err)
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 xerrors.Errorf("could not render json: %w", err)
return fmt.Errorf("could not render json: %w", err)
}
case "refresh_token":
// 12.1. Refresh Request
@@ -126,12 +125,12 @@ func (h *Handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
refreshToken := r.Form.Get("refresh_token")
tokenResponse, err := h.provider.Refresh(refreshToken)
if err != nil {
return xerrors.Errorf("token refresh error: %w", err)
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 xerrors.Errorf("could not render json: %w", err)
return fmt.Errorf("could not render json: %w", err)
}
default:
// 5.2. Error Response

View File

@@ -4,6 +4,7 @@ package oidcserver
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"math/big"
"strings"
"testing"
@@ -13,7 +14,6 @@ import (
"github.com/int128/kubelogin/integration_test/oidcserver/handler"
"github.com/int128/kubelogin/integration_test/oidcserver/http"
"github.com/int128/kubelogin/pkg/testing/jwt"
"golang.org/x/xerrors"
)
type Server interface {
@@ -133,7 +133,7 @@ func (sv *server) AuthenticateCode(req handler.AuthenticationRequest) (code stri
func (sv *server) Exchange(req handler.TokenRequest) (*handler.TokenResponse, error) {
if req.Code != "YOUR_AUTH_CODE" {
return nil, xerrors.Errorf("code wants %s but was %s", "YOUR_AUTH_CODE", req.Code)
return nil, fmt.Errorf("code wants %s but was %s", "YOUR_AUTH_CODE", req.Code)
}
if sv.lastAuthenticationRequest.CodeChallengeMethod == "S256" {
// https://tools.ietf.org/html/rfc7636#section-4.6

View File

@@ -10,8 +10,8 @@ import (
"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/pkg/adaptors/browser"
"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"
)

View File

@@ -1,404 +0,0 @@
package cmd
import (
"context"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/pkg/testing/logger"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
"github.com/int128/kubelogin/pkg/usecases/credentialplugin"
"github.com/int128/kubelogin/pkg/usecases/credentialplugin/mock_credentialplugin"
"github.com/int128/kubelogin/pkg/usecases/standalone"
"github.com/int128/kubelogin/pkg/usecases/standalone/mock_standalone"
)
func TestCmd_Run(t *testing.T) {
const executable = "kubelogin"
const version = "HEAD"
t.Run("root", func(t *testing.T) {
tests := map[string]struct {
args []string
in standalone.Input
}{
"Defaults": {
args: []string{executable},
in: standalone.Input{
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
"when --listen-port is set, it should convert the port to address": {
args: []string{
executable,
"--listen-port", "10080",
"--listen-port", "20080",
},
in: standalone.Input{
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
"when --listen-port is set, it should ignore --listen-address flags": {
args: []string{
executable,
"--listen-port", "10080",
"--listen-port", "20080",
"--listen-address", "127.0.0.1:30080",
"--listen-address", "127.0.0.1:40080",
},
in: standalone.Input{
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
"FullOptions": {
args: []string{executable,
"--kubeconfig", "/path/to/kubeconfig",
"--context", "hello.k8s.local",
"--user", "google",
"--certificate-authority", "/path/to/cacert",
"--certificate-authority-data", "BASE64ENCODED",
"--insecure-skip-tls-verify",
"-v1",
"--grant-type", "authcode",
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--skip-open-browser",
"--authentication-timeout-sec", "10",
"--local-server-cert", "/path/to/local-server-cert",
"--local-server-key", "/path/to/local-server-key",
"--open-url-after-authentication", "https://example.com/success.html",
"--username", "USER",
"--password", "PASS",
},
in: standalone.Input{
KubeconfigFilename: "/path/to/kubeconfig",
KubeconfigContext: "hello.k8s.local",
KubeconfigUser: "google",
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
SkipOpenBrowser: true,
AuthenticationTimeout: 10 * time.Second,
LocalServerCertFile: "/path/to/local-server-cert",
LocalServerKeyFile: "/path/to/local-server-key",
OpenURLAfterAuthentication: "https://example.com/success.html",
RedirectURLHostname: "localhost",
},
},
TLSClientConfig: tlsclientconfig.Config{
CACertFilename: []string{"/path/to/cacert"},
CACertData: []string{"BASE64ENCODED"},
SkipTLSVerify: true,
},
},
},
"GrantType=authcode-keyboard": {
args: []string{executable,
"--grant-type", "authcode-keyboard",
},
in: standalone.Input{
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeKeyboardOption: &authcode.KeyboardOption{},
},
},
},
"GrantType=password": {
args: []string{executable,
"--grant-type", "password",
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--username", "USER",
"--password", "PASS",
},
in: standalone.Input{
GrantOptionSet: authentication.GrantOptionSet{
ROPCOption: &ropc.Option{
Username: "USER",
Password: "PASS",
},
},
},
},
"GrantType=auto": {
args: []string{executable,
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--username", "USER",
"--password", "PASS",
},
in: standalone.Input{
GrantOptionSet: authentication.GrantOptionSet{
ROPCOption: &ropc.Option{
Username: "USER",
Password: "PASS",
},
},
},
},
}
for name, c := range tests {
t.Run(name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
mockStandalone := mock_standalone.NewMockInterface(ctrl)
mockStandalone.EXPECT().
Do(ctx, c.in)
cmd := Cmd{
Root: &Root{
Standalone: mockStandalone,
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, c.args, 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{
Root: &Root{
Standalone: mock_standalone.NewMockInterface(ctrl),
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(context.TODO(), []string{executable, "some"}, version)
if exitCode != 1 {
t.Errorf("exitCode wants 1 but %d", exitCode)
}
})
})
t.Run("get-token", func(t *testing.T) {
tests := map[string]struct {
args []string
in credentialplugin.Input
}{
"Defaults": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
},
in: credentialplugin.Input{
TokenCacheDir: defaultTokenCacheDir,
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:8000", "127.0.0.1:18000"},
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
"FullOptions": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
"--oidc-client-secret", "YOUR_CLIENT_SECRET",
"--oidc-extra-scope", "email",
"--oidc-extra-scope", "profile",
"--certificate-authority", "/path/to/cacert",
"--certificate-authority-data", "BASE64ENCODED",
"--insecure-skip-tls-verify",
"-v1",
"--grant-type", "authcode",
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--skip-open-browser",
"--authentication-timeout-sec", "10",
"--local-server-cert", "/path/to/local-server-cert",
"--local-server-key", "/path/to/local-server-key",
"--open-url-after-authentication", "https://example.com/success.html",
"--oidc-auth-request-extra-params", "ttl=86400",
"--oidc-auth-request-extra-params", "reauth=true",
"--username", "USER",
"--password", "PASS",
},
in: credentialplugin.Input{
TokenCacheDir: defaultTokenCacheDir,
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"email", "profile"},
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
SkipOpenBrowser: true,
AuthenticationTimeout: 10 * time.Second,
LocalServerCertFile: "/path/to/local-server-cert",
LocalServerKeyFile: "/path/to/local-server-key",
OpenURLAfterAuthentication: "https://example.com/success.html",
RedirectURLHostname: "localhost",
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
},
},
TLSClientConfig: tlsclientconfig.Config{
CACertFilename: []string{"/path/to/cacert"},
CACertData: []string{"BASE64ENCODED"},
SkipTLSVerify: true,
},
},
},
"GrantType=authcode-keyboard": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
"--grant-type", "authcode-keyboard",
"--oidc-auth-request-extra-params", "ttl=86400",
},
in: credentialplugin.Input{
TokenCacheDir: defaultTokenCacheDir,
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeKeyboardOption: &authcode.KeyboardOption{
AuthRequestExtraParams: map[string]string{"ttl": "86400"},
},
},
},
},
"GrantType=password": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
"--grant-type", "password",
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--username", "USER",
"--password", "PASS",
},
in: credentialplugin.Input{
TokenCacheDir: defaultTokenCacheDir,
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
GrantOptionSet: authentication.GrantOptionSet{
ROPCOption: &ropc.Option{
Username: "USER",
Password: "PASS",
},
},
},
},
"GrantType=auto": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--username", "USER",
"--password", "PASS",
},
in: credentialplugin.Input{
TokenCacheDir: defaultTokenCacheDir,
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
GrantOptionSet: authentication.GrantOptionSet{
ROPCOption: &ropc.Option{
Username: "USER",
Password: "PASS",
},
},
},
},
}
for name, c := range tests {
t.Run(name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
getToken := mock_credentialplugin.NewMockInterface(ctrl)
getToken.EXPECT().
Do(ctx, c.in)
cmd := Cmd{
Root: &Root{
Logger: logger.New(t),
},
GetToken: &GetToken{
GetToken: getToken,
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, c.args, version)
if exitCode != 0 {
t.Errorf("exitCode wants 0 but %d", exitCode)
}
})
}
t.Run("MissingMandatoryOptions", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
cmd := Cmd{
Root: &Root{
Logger: logger.New(t),
},
GetToken: &GetToken{
GetToken: mock_credentialplugin.NewMockInterface(ctrl),
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, []string{executable, "get-token"}, version)
if exitCode != 1 {
t.Errorf("exitCode wants 1 but %d", exitCode)
}
})
t.Run("TooManyArgs", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
cmd := Cmd{
Root: &Root{
Logger: logger.New(t),
},
GetToken: &GetToken{
GetToken: mock_credentialplugin.NewMockInterface(ctrl),
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, []string{executable, "get-token", "foo"}, version)
if exitCode != 1 {
t.Errorf("exitCode wants 1 but %d", exitCode)
}
})
})
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
"github.com/spf13/pflag"
"golang.org/x/xerrors"
)
type authenticationOptions struct {
@@ -18,6 +17,7 @@ type authenticationOptions struct {
ListenPort []int // deprecated
AuthenticationTimeoutSec int
SkipOpenBrowser bool
BrowserCommand string
LocalServerCertFile string
LocalServerKeyFile string
OpenURLAfterAuthentication string
@@ -58,6 +58,7 @@ func (o *authenticationOptions) addFlags(f *pflag.FlagSet) {
panic(err)
}
f.BoolVar(&o.SkipOpenBrowser, "skip-open-browser", false, "[authcode] Do not open the browser automatically")
f.StringVar(&o.BrowserCommand, "browser-command", "", "[authcode] Command to open the browser")
f.IntVar(&o.AuthenticationTimeoutSec, "authentication-timeout-sec", defaultAuthenticationTimeoutSec, "[authcode] Timeout of authentication in seconds")
f.StringVar(&o.LocalServerCertFile, "local-server-cert", "", "[authcode] Certificate path for the local server")
f.StringVar(&o.LocalServerKeyFile, "local-server-key", "", "[authcode] Certificate key path for the local server")
@@ -68,12 +69,18 @@ func (o *authenticationOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.Password, "password", "", "[password] Password for resource owner password credentials grant")
}
func (o *authenticationOptions) expandHomedir() {
o.LocalServerCertFile = expandHomedir(o.LocalServerCertFile)
o.LocalServerKeyFile = expandHomedir(o.LocalServerKeyFile)
}
func (o *authenticationOptions) grantOptionSet() (s authentication.GrantOptionSet, err error) {
switch {
case o.GrantType == "authcode" || (o.GrantType == "auto" && o.Username == ""):
s.AuthCodeBrowserOption = &authcode.BrowserOption{
BindAddress: o.determineListenAddress(),
SkipOpenBrowser: o.SkipOpenBrowser,
BrowserCommand: o.BrowserCommand,
AuthenticationTimeout: time.Duration(o.AuthenticationTimeoutSec) * time.Second,
LocalServerCertFile: o.LocalServerCertFile,
LocalServerKeyFile: o.LocalServerKeyFile,
@@ -91,7 +98,7 @@ func (o *authenticationOptions) grantOptionSet() (s authentication.GrantOptionSe
Password: o.Password,
}
default:
err = xerrors.Errorf("grant-type must be one of (%s)", allGrantType)
err = fmt.Errorf("grant-type must be one of (%s)", allGrantType)
}
return
}

View File

@@ -0,0 +1,143 @@
package cmd
import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
"github.com/spf13/pflag"
)
func Test_authenticationOptions_grantOptionSet(t *testing.T) {
tests := map[string]struct {
args []string
want authentication.GrantOptionSet
}{
"NoFlag": {
want: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
"FullOptions": {
args: []string{
"--grant-type", "authcode",
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--skip-open-browser",
"--browser-command", "firefox",
"--authentication-timeout-sec", "10",
"--local-server-cert", "/path/to/local-server-cert",
"--local-server-key", "/path/to/local-server-key",
"--open-url-after-authentication", "https://example.com/success.html",
"--oidc-redirect-url-hostname", "example",
"--oidc-auth-request-extra-params", "ttl=86400",
"--oidc-auth-request-extra-params", "reauth=true",
"--username", "USER",
"--password", "PASS",
},
want: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
SkipOpenBrowser: true,
BrowserCommand: "firefox",
AuthenticationTimeout: 10 * time.Second,
LocalServerCertFile: "/path/to/local-server-cert",
LocalServerKeyFile: "/path/to/local-server-key",
OpenURLAfterAuthentication: "https://example.com/success.html",
RedirectURLHostname: "example",
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
},
},
},
"when --listen-port is set, it should convert the port to address": {
args: []string{
"--listen-port", "10080",
"--listen-port", "20080",
},
want: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
"when --listen-port is set, it should ignore --listen-address flags": {
args: []string{
"--listen-port", "10080",
"--listen-port", "20080",
"--listen-address", "127.0.0.1:30080",
"--listen-address", "127.0.0.1:40080",
},
want: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
"GrantType=authcode-keyboard": {
args: []string{
"--grant-type", "authcode-keyboard",
},
want: authentication.GrantOptionSet{
AuthCodeKeyboardOption: &authcode.KeyboardOption{},
},
},
"GrantType=password": {
args: []string{
"--grant-type", "password",
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--username", "USER",
"--password", "PASS",
},
want: authentication.GrantOptionSet{
ROPCOption: &ropc.Option{
Username: "USER",
Password: "PASS",
},
},
},
"GrantType=auto": {
args: []string{
"--listen-address", "127.0.0.1:10080",
"--listen-address", "127.0.0.1:20080",
"--username", "USER",
"--password", "PASS",
},
want: authentication.GrantOptionSet{
ROPCOption: &ropc.Option{
Username: "USER",
Password: "PASS",
},
},
},
}
for name, c := range tests {
t.Run(name, func(t *testing.T) {
var o authenticationOptions
f := pflag.NewFlagSet("", pflag.ContinueOnError)
o.addFlags(f)
if err := f.Parse(c.args); err != nil {
t.Fatalf("Parse error: %s", err)
}
got, err := o.grantOptionSet()
if err != nil {
t.Fatalf("grantOptionSet error: %s", err)
}
if diff := cmp.Diff(c.want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
})
}
}

View File

@@ -2,12 +2,12 @@ package cmd
import (
"context"
"path/filepath"
"runtime"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/spf13/cobra"
"k8s.io/client-go/util/homedir"
)
// Set provides an implementation and interface for Cmd.
@@ -24,7 +24,7 @@ type Interface interface {
}
var defaultListenAddress = []string{"127.0.0.1:8000", "127.0.0.1:18000"}
var defaultTokenCacheDir = homedir.HomeDir() + "/.kube/cache/oidc-login"
var defaultTokenCacheDir = filepath.Join("~", ".kube", "cache", "oidc-login")
const defaultAuthenticationTimeoutSec = 180

257
pkg/cmd/cmd_test.go Normal file
View File

@@ -0,0 +1,257 @@
package cmd
import (
"context"
"os"
"path/filepath"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/testing/logger"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/int128/kubelogin/pkg/usecases/credentialplugin"
"github.com/int128/kubelogin/pkg/usecases/credentialplugin/mock_credentialplugin"
"github.com/int128/kubelogin/pkg/usecases/standalone"
"github.com/int128/kubelogin/pkg/usecases/standalone/mock_standalone"
)
func TestCmd_Run(t *testing.T) {
const executable = "kubelogin"
const version = "HEAD"
t.Run("root", func(t *testing.T) {
tests := map[string]struct {
args []string
in standalone.Input
}{
"Defaults": {
args: []string{executable},
in: standalone.Input{
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
"FullOptions": {
args: []string{executable,
"--kubeconfig", "/path/to/kubeconfig",
"--context", "hello.k8s.local",
"--user", "google",
"-v1",
},
in: standalone.Input{
KubeconfigFilename: "/path/to/kubeconfig",
KubeconfigContext: "hello.k8s.local",
KubeconfigUser: "google",
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
}
for name, c := range tests {
t.Run(name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
mockStandalone := mock_standalone.NewMockInterface(ctrl)
mockStandalone.EXPECT().
Do(ctx, c.in)
cmd := Cmd{
Root: &Root{
Standalone: mockStandalone,
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, c.args, 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{
Root: &Root{
Standalone: mock_standalone.NewMockInterface(ctrl),
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(context.TODO(), []string{executable, "some"}, version)
if exitCode != 1 {
t.Errorf("exitCode wants 1 but %d", exitCode)
}
})
})
t.Run("get-token", func(t *testing.T) {
userHomeDir, err := os.UserHomeDir()
if err != nil {
t.Fatalf("os.UserHomeDir error: %s", err)
}
tests := map[string]struct {
args []string
in credentialplugin.Input
}{
"Defaults": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
},
in: credentialplugin.Input{
TokenCacheDir: filepath.Join(userHomeDir, ".kube/cache/oidc-login"),
Provider: oidc.Provider{
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
},
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
"FullOptions": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
"--oidc-client-secret", "YOUR_CLIENT_SECRET",
"--oidc-extra-scope", "email",
"--oidc-extra-scope", "profile",
"-v1",
},
in: credentialplugin.Input{
TokenCacheDir: filepath.Join(userHomeDir, ".kube/cache/oidc-login"),
Provider: oidc.Provider{
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"email", "profile"},
},
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
},
"HomedirExpansion": {
args: []string{executable,
"get-token",
"--oidc-issuer-url", "https://issuer.example.com",
"--oidc-client-id", "YOUR_CLIENT_ID",
"--certificate-authority", "~/.kube/ca.crt",
"--local-server-cert", "~/.kube/oidc-server.crt",
"--local-server-key", "~/.kube/oidc-server.key",
"--token-cache-dir", "~/.kube/oidc-cache",
},
in: credentialplugin.Input{
TokenCacheDir: filepath.Join(userHomeDir, ".kube/oidc-cache"),
Provider: oidc.Provider{
IssuerURL: "https://issuer.example.com",
ClientID: "YOUR_CLIENT_ID",
},
GrantOptionSet: authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
LocalServerCertFile: filepath.Join(userHomeDir, ".kube/oidc-server.crt"),
LocalServerKeyFile: filepath.Join(userHomeDir, ".kube/oidc-server.key"),
},
},
TLSClientConfig: tlsclientconfig.Config{
CACertFilename: []string{filepath.Join(userHomeDir, ".kube/ca.crt")},
},
},
},
}
for name, c := range tests {
t.Run(name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
getToken := mock_credentialplugin.NewMockInterface(ctrl)
getToken.EXPECT().
Do(ctx, c.in)
cmd := Cmd{
Root: &Root{
Logger: logger.New(t),
},
GetToken: &GetToken{
GetToken: getToken,
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, c.args, version)
if exitCode != 0 {
t.Errorf("exitCode wants 0 but %d", exitCode)
}
})
}
t.Run("MissingMandatoryOptions", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
cmd := Cmd{
Root: &Root{
Logger: logger.New(t),
},
GetToken: &GetToken{
GetToken: mock_credentialplugin.NewMockInterface(ctrl),
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, []string{executable, "get-token"}, version)
if exitCode != 1 {
t.Errorf("exitCode wants 1 but %d", exitCode)
}
})
t.Run("TooManyArgs", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
cmd := Cmd{
Root: &Root{
Logger: logger.New(t),
},
GetToken: &GetToken{
GetToken: mock_credentialplugin.NewMockInterface(ctrl),
Logger: logger.New(t),
},
Logger: logger.New(t),
}
exitCode := cmd.Run(ctx, []string{executable, "get-token", "foo"}, version)
if exitCode != 1 {
t.Errorf("exitCode wants 1 but %d", exitCode)
}
})
})
}

View File

@@ -1,11 +1,14 @@
package cmd
import (
"github.com/int128/kubelogin/pkg/adaptors/logger"
"errors"
"fmt"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/usecases/credentialplugin"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"golang.org/x/xerrors"
)
// getTokenOptions represents the options for get-token command.
@@ -14,6 +17,7 @@ type getTokenOptions struct {
ClientID string
ClientSecret string
ExtraScopes []string
UsePKCE bool
TokenCacheDir string
tlsOptions tlsOptions
authenticationOptions authenticationOptions
@@ -24,11 +28,19 @@ func (o *getTokenOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.ClientID, "oidc-client-id", "", "Client ID of the provider (mandatory)")
f.StringVar(&o.ClientSecret, "oidc-client-secret", "", "Client secret of the provider")
f.StringSliceVar(&o.ExtraScopes, "oidc-extra-scope", nil, "Scopes to request to the provider")
f.BoolVar(&o.UsePKCE, "oidc-use-pkce", false, "Force PKCE usage")
f.StringVar(&o.TokenCacheDir, "token-cache-dir", defaultTokenCacheDir, "Path to a directory for token cache")
o.tlsOptions.addFlags(f)
o.authenticationOptions.addFlags(f)
}
func (o *getTokenOptions) expandHomedir() error {
o.TokenCacheDir = expandHomedir(o.TokenCacheDir)
o.authenticationOptions.expandHomedir()
o.tlsOptions.expandHomedir()
return nil
}
type GetToken struct {
GetToken credentialplugin.Interface
Logger logger.Interface
@@ -44,29 +56,35 @@ func (cmd *GetToken) New() *cobra.Command {
return err
}
if o.IssuerURL == "" {
return xerrors.New("--oidc-issuer-url is missing")
return errors.New("--oidc-issuer-url is missing")
}
if o.ClientID == "" {
return xerrors.New("--oidc-client-id is missing")
return errors.New("--oidc-client-id is missing")
}
return nil
},
RunE: func(c *cobra.Command, _ []string) error {
if err := o.expandHomedir(); err != nil {
return err
}
grantOptionSet, err := o.authenticationOptions.grantOptionSet()
if err != nil {
return xerrors.Errorf("get-token: %w", err)
return fmt.Errorf("get-token: %w", err)
}
in := credentialplugin.Input{
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
ExtraScopes: o.ExtraScopes,
Provider: oidc.Provider{
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
UsePKCE: o.UsePKCE,
ExtraScopes: o.ExtraScopes,
},
TokenCacheDir: o.TokenCacheDir,
GrantOptionSet: grantOptionSet,
TLSClientConfig: o.tlsOptions.tlsClientConfig(),
}
if err := cmd.GetToken.Do(c.Context(), in); err != nil {
return xerrors.Errorf("get-token: %w", err)
return fmt.Errorf("get-token: %w", err)
}
return nil
},

15
pkg/cmd/homedir.go Normal file
View File

@@ -0,0 +1,15 @@
package cmd
import (
"path/filepath"
"strings"
"k8s.io/client-go/util/homedir"
)
func expandHomedir(s string) string {
if !strings.HasPrefix(s, "~") {
return s
}
return filepath.Join(homedir.HomeDir(), strings.TrimPrefix(s, "~"))
}

View File

@@ -1,12 +1,12 @@
package cmd
import (
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"fmt"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/kubeconfig"
"github.com/int128/kubelogin/pkg/usecases/standalone"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"golang.org/x/xerrors"
)
const rootDescription = `Log in to the OpenID Connect provider.
@@ -51,7 +51,7 @@ func (cmd *Root) New() *cobra.Command {
RunE: func(c *cobra.Command, _ []string) error {
grantOptionSet, err := o.authenticationOptions.grantOptionSet()
if err != nil {
return xerrors.Errorf("invalid option: %w", err)
return fmt.Errorf("invalid option: %w", err)
}
in := standalone.Input{
KubeconfigFilename: o.Kubeconfig,
@@ -61,7 +61,7 @@ func (cmd *Root) New() *cobra.Command {
TLSClientConfig: o.tlsOptions.tlsClientConfig(),
}
if err := cmd.Standalone.Do(c.Context(), in); err != nil {
return xerrors.Errorf("login: %w", err)
return fmt.Errorf("login: %w", err)
}
return nil
},

View File

@@ -1,10 +1,11 @@
package cmd
import (
"fmt"
"github.com/int128/kubelogin/pkg/usecases/setup"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"golang.org/x/xerrors"
)
// setupOptions represents the options for setup command.
@@ -13,6 +14,7 @@ type setupOptions struct {
ClientID string
ClientSecret string
ExtraScopes []string
UsePKCE bool
tlsOptions tlsOptions
authenticationOptions authenticationOptions
}
@@ -22,6 +24,7 @@ func (o *setupOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.ClientID, "oidc-client-id", "", "Client ID of the provider")
f.StringVar(&o.ClientSecret, "oidc-client-secret", "", "Client secret of the provider")
f.StringSliceVar(&o.ExtraScopes, "oidc-extra-scope", nil, "Scopes to request to the provider")
f.BoolVar(&o.UsePKCE, "oidc-use-pkce", false, "Force PKCE usage")
o.tlsOptions.addFlags(f)
o.authenticationOptions.addFlags(f)
}
@@ -39,13 +42,14 @@ func (cmd *Setup) New() *cobra.Command {
RunE: func(c *cobra.Command, _ []string) error {
grantOptionSet, err := o.authenticationOptions.grantOptionSet()
if err != nil {
return xerrors.Errorf("setup: %w", err)
return fmt.Errorf("setup: %w", err)
}
in := setup.Stage2Input{
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
ExtraScopes: o.ExtraScopes,
UsePKCE: o.UsePKCE,
GrantOptionSet: grantOptionSet,
TLSClientConfig: o.tlsOptions.tlsClientConfig(),
}
@@ -57,7 +61,7 @@ func (cmd *Setup) New() *cobra.Command {
return nil
}
if err := cmd.Setup.DoStage2(c.Context(), in); err != nil {
return xerrors.Errorf("setup: %w", err)
return fmt.Errorf("setup: %w", err)
}
return nil
},

View File

@@ -23,6 +23,15 @@ func (o *tlsOptions) addFlags(f *pflag.FlagSet) {
f.BoolVar(&o.RenegotiateFreelyAsClient, "tls-renegotiation-freely", false, "If set, allow a remote server to repeatedly request renegotiation")
}
func (o *tlsOptions) expandHomedir() {
var caCertFilenames []string
for _, caCertFilename := range o.CACertFilename {
expanded := expandHomedir(caCertFilename)
caCertFilenames = append(caCertFilenames, expanded)
}
o.CACertFilename = caCertFilenames
}
func (o tlsOptions) tlsClientConfig() tlsclientconfig.Config {
return tlsclientconfig.Config{
CACertFilename: o.CACertFilename,

View File

@@ -0,0 +1,10 @@
// Package credentialplugin provides the types for client-go credential plugins.
package credentialplugin
import "time"
// Output represents an output object of the credential plugin.
type Output struct {
Token string
Expiry time.Time
}

View File

@@ -1,18 +1,18 @@
// Package credentialpluginwriter provides a writer for a credential plugin.
package credentialpluginwriter
// Package writer provides a writer for a credential plugin.
package writer
import (
"encoding/json"
"time"
"fmt"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/stdio"
"golang.org/x/xerrors"
"github.com/int128/kubelogin/pkg/credentialplugin"
"github.com/int128/kubelogin/pkg/infrastructure/stdio"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
)
//go:generate mockgen -destination mock_credentialpluginwriter/mock_credentialpluginwriter.go github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter Interface
//go:generate mockgen -destination mock_writer/mock_writer.go github.com/int128/kubelogin/pkg/credentialplugin/writer Interface
var Set = wire.NewSet(
wire.Struct(new(Writer), "*"),
@@ -20,13 +20,7 @@ var Set = wire.NewSet(
)
type Interface interface {
Write(out Output) error
}
// Output represents an output object of the credential plugin.
type Output struct {
Token string
Expiry time.Time
Write(out credentialplugin.Output) error
}
type Writer struct {
@@ -34,7 +28,7 @@ type Writer struct {
}
// Write writes the ExecCredential to standard output for kubectl.
func (w *Writer) Write(out Output) error {
func (w *Writer) Write(out credentialplugin.Output) error {
ec := &clientauthenticationv1beta1.ExecCredential{
TypeMeta: metav1.TypeMeta{
APIVersion: "client.authentication.k8s.io/v1beta1",
@@ -47,7 +41,7 @@ func (w *Writer) Write(out Output) error {
}
e := json.NewEncoder(w.Stdout)
if err := e.Encode(ec); err != nil {
return xerrors.Errorf("could not write the ExecCredential: %w", err)
return fmt.Errorf("could not write the ExecCredential: %w", err)
}
return nil
}

View File

@@ -1,47 +1,47 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter (interfaces: Interface)
// Source: github.com/int128/kubelogin/pkg/credentialplugin/writer (interfaces: Interface)
// Package mock_credentialpluginwriter is a generated GoMock package.
package mock_credentialpluginwriter
// Package mock_writer is a generated GoMock package.
package mock_writer
import (
gomock "github.com/golang/mock/gomock"
credentialpluginwriter "github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter"
credentialplugin "github.com/int128/kubelogin/pkg/credentialplugin"
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// Write mocks base method.
func (m *MockInterface) Write(arg0 credentialpluginwriter.Output) error {
// Write mocks base method
func (m *MockInterface) Write(arg0 credentialplugin.Output) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Write", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Write indicates an expected call of Write.
// Write indicates an expected call of Write
func (mr *MockInterfaceMockRecorder) Write(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockInterface)(nil).Write), arg0)

View File

@@ -5,25 +5,26 @@ package di
import (
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/browser"
"github.com/int128/kubelogin/pkg/adaptors/clock"
"github.com/int128/kubelogin/pkg/adaptors/cmd"
"github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter"
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/mutex"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/reader"
"github.com/int128/kubelogin/pkg/adaptors/stdio"
"github.com/int128/kubelogin/pkg/adaptors/tokencache"
"github.com/int128/kubelogin/pkg/cmd"
"github.com/int128/kubelogin/pkg/credentialplugin/writer"
"github.com/int128/kubelogin/pkg/infrastructure/browser"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/infrastructure/mutex"
"github.com/int128/kubelogin/pkg/infrastructure/reader"
"github.com/int128/kubelogin/pkg/infrastructure/stdio"
kubeconfigLoader "github.com/int128/kubelogin/pkg/kubeconfig/loader"
kubeconfigWriter "github.com/int128/kubelogin/pkg/kubeconfig/writer"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/tlsclientconfig/loader"
"github.com/int128/kubelogin/pkg/tokencache/repository"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/credentialplugin"
"github.com/int128/kubelogin/pkg/usecases/setup"
"github.com/int128/kubelogin/pkg/usecases/standalone"
)
// NewCmd returns an instance of adaptors.Cmd.
// NewCmd returns an instance of infrastructure.Cmd.
func NewCmd() cmd.Interface {
wire.Build(
NewCmdForHeadless,
@@ -37,7 +38,7 @@ func NewCmd() cmd.Interface {
return nil
}
// NewCmdForHeadless returns an instance of adaptors.Cmd for headless testing.
// NewCmdForHeadless returns an instance of infrastructure.Cmd for headless testing.
func NewCmdForHeadless(clock.Interface, stdio.Stdin, stdio.Stdout, logger.Interface, browser.Interface) cmd.Interface {
wire.Build(
// use-cases
@@ -46,14 +47,15 @@ func NewCmdForHeadless(clock.Interface, stdio.Stdin, stdio.Stdout, logger.Interf
credentialplugin.Set,
setup.Set,
// adaptors
// infrastructure
cmd.Set,
reader.Set,
kubeconfig.Set,
tokencache.Set,
oidcclient.Set,
kubeconfigLoader.Set,
kubeconfigWriter.Set,
repository.Set,
client.Set,
loader.Set,
credentialpluginwriter.Set,
writer.Set,
mutex.Set,
)
return nil

View File

@@ -6,18 +6,19 @@
package di
import (
"github.com/int128/kubelogin/pkg/adaptors/browser"
"github.com/int128/kubelogin/pkg/adaptors/clock"
"github.com/int128/kubelogin/pkg/adaptors/cmd"
"github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter"
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/mutex"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/reader"
"github.com/int128/kubelogin/pkg/adaptors/stdio"
"github.com/int128/kubelogin/pkg/adaptors/tokencache"
"github.com/int128/kubelogin/pkg/cmd"
writer2 "github.com/int128/kubelogin/pkg/credentialplugin/writer"
"github.com/int128/kubelogin/pkg/infrastructure/browser"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/infrastructure/mutex"
"github.com/int128/kubelogin/pkg/infrastructure/reader"
"github.com/int128/kubelogin/pkg/infrastructure/stdio"
loader2 "github.com/int128/kubelogin/pkg/kubeconfig/loader"
"github.com/int128/kubelogin/pkg/kubeconfig/writer"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/tlsclientconfig/loader"
"github.com/int128/kubelogin/pkg/tokencache/repository"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
@@ -29,7 +30,6 @@ import (
// Injectors from di.go:
// NewCmd returns an instance of adaptors.Cmd.
func NewCmd() cmd.Interface {
clockReal := &clock.Real{}
stdin := _wireFileValue
@@ -45,10 +45,9 @@ var (
_wireOsFileValue = os.Stdout
)
// NewCmdForHeadless returns an instance of adaptors.Cmd for headless testing.
func NewCmdForHeadless(clockInterface clock.Interface, stdin stdio.Stdin, stdout stdio.Stdout, loggerInterface logger.Interface, browserInterface browser.Interface) cmd.Interface {
loaderLoader := loader.Loader{}
factory := &oidcclient.Factory{
factory := &client.Factory{
Loader: loaderLoader,
Clock: clockInterface,
Logger: loggerInterface,
@@ -69,27 +68,27 @@ func NewCmdForHeadless(clockInterface clock.Interface, stdin stdio.Stdin, stdout
Logger: loggerInterface,
}
authenticationAuthentication := &authentication.Authentication{
OIDCClient: factory,
ClientFactory: factory,
Logger: loggerInterface,
Clock: clockInterface,
AuthCodeBrowser: authcodeBrowser,
AuthCodeKeyboard: keyboard,
ROPC: ropcROPC,
}
kubeconfigKubeconfig := &kubeconfig.Kubeconfig{
Logger: loggerInterface,
}
loader3 := &loader2.Loader{}
writerWriter := &writer.Writer{}
standaloneStandalone := &standalone.Standalone{
Authentication: authenticationAuthentication,
Kubeconfig: kubeconfigKubeconfig,
Logger: loggerInterface,
Authentication: authenticationAuthentication,
KubeconfigLoader: loader3,
KubeconfigWriter: writerWriter,
Logger: loggerInterface,
}
root := &cmd.Root{
Standalone: standaloneStandalone,
Logger: loggerInterface,
}
repository := &tokencache.Repository{}
writer := &credentialpluginwriter.Writer{
repositoryRepository := &repository.Repository{}
writer3 := &writer2.Writer{
Stdout: stdout,
}
mutexMutex := &mutex.Mutex{
@@ -97,8 +96,8 @@ func NewCmdForHeadless(clockInterface clock.Interface, stdin stdio.Stdin, stdout
}
getToken := &credentialplugin.GetToken{
Authentication: authenticationAuthentication,
TokenCacheRepository: repository,
Writer: writer,
TokenCacheRepository: repositoryRepository,
Writer: writer3,
Mutex: mutexMutex,
Logger: loggerInterface,
}

View File

@@ -1,13 +1,15 @@
package browser
import (
"context"
"os"
"os/exec"
"github.com/google/wire"
"github.com/pkg/browser"
)
//go:generate mockgen -destination mock_browser/mock_browser.go github.com/int128/kubelogin/pkg/adaptors/browser Interface
//go:generate mockgen -destination mock_browser/mock_browser.go github.com/int128/kubelogin/pkg/infrastructure/browser Interface
func init() {
// In credential plugin mode, some browser launcher writes a message to stdout
@@ -23,6 +25,7 @@ var Set = wire.NewSet(
type Interface interface {
Open(url string) error
OpenCommand(ctx context.Context, url, command string) error
}
type Browser struct{}
@@ -31,3 +34,11 @@ type Browser struct{}
func (*Browser) Open(url string) error {
return browser.OpenURL(url)
}
// OpenCommand opens the browser using the command.
func (*Browser) OpenCommand(ctx context.Context, url, command string) error {
c := exec.CommandContext(ctx, command, url)
c.Stdout = os.Stderr // see above
c.Stderr = os.Stderr
return c.Run()
}

View File

@@ -1,38 +1,39 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/browser (interfaces: Interface)
// Source: github.com/int128/kubelogin/pkg/infrastructure/browser (interfaces: Interface)
// Package mock_browser is a generated GoMock package.
package mock_browser
import (
context "context"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// Open mocks base method.
// Open mocks base method
func (m *MockInterface) Open(arg0 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Open", arg0)
@@ -40,8 +41,22 @@ func (m *MockInterface) Open(arg0 string) error {
return ret0
}
// Open indicates an expected call of Open.
// Open indicates an expected call of Open
func (mr *MockInterfaceMockRecorder) Open(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockInterface)(nil).Open), arg0)
}
// OpenCommand mocks base method
func (m *MockInterface) OpenCommand(arg0 context.Context, arg1, arg2 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "OpenCommand", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// OpenCommand indicates an expected call of OpenCommand
func (mr *MockInterfaceMockRecorder) OpenCommand(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenCommand", reflect.TypeOf((*MockInterface)(nil).OpenCommand), arg0, arg1, arg2)
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/google/wire"
"github.com/spf13/pflag"
"k8s.io/klog"
"k8s.io/klog/v2"
)
// Set provides an implementation and interface for Logger.
@@ -56,5 +56,5 @@ func (*Logger) V(level int) Verbose {
// IsEnabled returns true if the level is enabled.
func (*Logger) IsEnabled(level int) bool {
return bool(klog.V(klog.Level(level)))
return klog.V(klog.Level(level)).Enabled()
}

View File

@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/mutex (interfaces: Interface)
// Source: github.com/int128/kubelogin/pkg/infrastructure/mutex (interfaces: Interface)
// Package mock_mutex is a generated GoMock package.
package mock_mutex
@@ -7,7 +7,7 @@ package mock_mutex
import (
context "context"
gomock "github.com/golang/mock/gomock"
mutex "github.com/int128/kubelogin/pkg/adaptors/mutex"
mutex "github.com/int128/kubelogin/pkg/infrastructure/mutex"
reflect "reflect"
)

View File

@@ -5,13 +5,12 @@ import (
"fmt"
"github.com/alexflint/go-filemutex"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"golang.org/x/xerrors"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"os"
"path"
)
//go:generate mockgen -destination mock_mutex/mock_mutex.go github.com/int128/kubelogin/pkg/adaptors/mutex Interface
//go:generate mockgen -destination mock_mutex/mock_mutex.go github.com/int128/kubelogin/pkg/infrastructure/mutex Interface
var Set = wire.NewSet(
wire.Struct(new(Mutex), "*"),
@@ -64,7 +63,7 @@ func (m *Mutex) Acquire(ctx context.Context, name string) (*Lock, error) {
lfn := LockFileName(name)
fm, err := filemutex.New(lfn)
if err != nil {
return nil, xerrors.Errorf("error creating mutex file %s: %w", lfn, err)
return nil, fmt.Errorf("error creating mutex file %s: %w", lfn, err)
}
lockChan := internalAcquire(fm)
@@ -75,7 +74,7 @@ func (m *Mutex) Acquire(ctx context.Context, name string) (*Lock, error) {
case err := <-lockChan:
if err != nil {
_ = internalRelease(fm, lfn, m.Logger)
return nil, xerrors.Errorf("error acquiring lock on file %s: %w", lfn, err)
return nil, fmt.Errorf("error acquiring lock on file %s: %w", lfn, err)
}
return &Lock{Data: fm, Name: name}, nil
}

View File

@@ -1,9 +1,9 @@
package mutex
import (
"github.com/int128/kubelogin/pkg/adaptors/logger"
"fmt"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"golang.org/x/net/context"
"golang.org/x/xerrors"
"math/rand"
"sync"
"testing"
@@ -33,10 +33,10 @@ func TestMutex(t *testing.T) {
time.Sleep(dur * time.Microsecond)
events <- -1
if err := m.Release(mutex); err != nil {
errors <- xerrors.Errorf("Release error: %w", err)
errors <- fmt.Errorf("Release error: %w", err)
}
} else {
errors <- xerrors.Errorf("Acquire error: %w", err)
errors <- fmt.Errorf("Acquire error: %w", err)
}
}

View File

@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/reader (interfaces: Interface)
// Source: github.com/int128/kubelogin/pkg/infrastructure/reader (interfaces: Interface)
// Package mock_reader is a generated GoMock package.
package mock_reader
@@ -9,30 +9,30 @@ import (
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// ReadPassword mocks base method.
// ReadPassword mocks base method
func (m *MockInterface) ReadPassword(arg0 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ReadPassword", arg0)
@@ -41,13 +41,13 @@ func (m *MockInterface) ReadPassword(arg0 string) (string, error) {
return ret0, ret1
}
// ReadPassword indicates an expected call of ReadPassword.
// ReadPassword indicates an expected call of ReadPassword
func (mr *MockInterfaceMockRecorder) ReadPassword(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadPassword", reflect.TypeOf((*MockInterface)(nil).ReadPassword), arg0)
}
// ReadString mocks base method.
// ReadString mocks base method
func (m *MockInterface) ReadString(arg0 string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ReadString", arg0)
@@ -56,7 +56,7 @@ func (m *MockInterface) ReadString(arg0 string) (string, error) {
return ret0, ret1
}
// ReadString indicates an expected call of ReadString.
// ReadString indicates an expected call of ReadString
func (mr *MockInterfaceMockRecorder) ReadString(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadString", reflect.TypeOf((*MockInterface)(nil).ReadString), arg0)

View File

@@ -9,12 +9,11 @@ import (
"syscall"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/stdio"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/xerrors"
"github.com/int128/kubelogin/pkg/infrastructure/stdio"
"golang.org/x/term"
)
//go:generate mockgen -destination mock_reader/mock_reader.go github.com/int128/kubelogin/pkg/adaptors/reader Interface
//go:generate mockgen -destination mock_reader/mock_reader.go github.com/int128/kubelogin/pkg/infrastructure/reader Interface
// Set provides an implementation and interface for Reader.
var Set = wire.NewSet(
@@ -34,12 +33,12 @@ type Reader struct {
// ReadString reads a string from the stdin.
func (x *Reader) ReadString(prompt string) (string, error) {
if _, err := fmt.Fprint(os.Stderr, prompt); err != nil {
return "", xerrors.Errorf("write error: %w", err)
return "", fmt.Errorf("write error: %w", err)
}
r := bufio.NewReader(x.Stdin)
s, err := r.ReadString('\n')
if err != nil {
return "", xerrors.Errorf("read error: %w", err)
return "", fmt.Errorf("read error: %w", err)
}
s = strings.TrimRight(s, "\r\n")
return s, nil
@@ -48,14 +47,14 @@ func (x *Reader) ReadString(prompt string) (string, error) {
// ReadPassword reads a password from the stdin without echo back.
func (*Reader) ReadPassword(prompt string) (string, error) {
if _, err := fmt.Fprint(os.Stderr, prompt); err != nil {
return "", xerrors.Errorf("write error: %w", err)
return "", fmt.Errorf("write error: %w", err)
}
b, err := terminal.ReadPassword(int(syscall.Stdin))
b, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", xerrors.Errorf("read error: %w", err)
return "", fmt.Errorf("read error: %w", err)
}
if _, err := fmt.Fprintln(os.Stderr); err != nil {
return "", xerrors.Errorf("write error: %w", err)
return "", fmt.Errorf("write error: %w", err)
}
return string(b), nil
}

View File

@@ -6,10 +6,9 @@ import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
"golang.org/x/xerrors"
)
// DecodeWithoutVerify decodes the JWT string and returns the claims.
@@ -17,18 +16,18 @@ import (
func DecodeWithoutVerify(s string) (*Claims, error) {
payload, err := DecodePayloadAsRawJSON(s)
if err != nil {
return nil, xerrors.Errorf("could not decode the payload: %w", err)
return nil, fmt.Errorf("could not decode the payload: %w", err)
}
var claims struct {
Subject string `json:"sub,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
}
if err := json.NewDecoder(bytes.NewReader(payload)).Decode(&claims); err != nil {
return nil, xerrors.Errorf("could not decode the json of token: %w", err)
return nil, fmt.Errorf("could not decode the json of token: %w", err)
}
var prettyJson bytes.Buffer
if err := json.Indent(&prettyJson, payload, "", " "); err != nil {
return nil, xerrors.Errorf("could not indent the json of token: %w", err)
return nil, fmt.Errorf("could not indent the json of token: %w", err)
}
return &Claims{
Subject: claims.Subject,
@@ -41,11 +40,11 @@ func DecodeWithoutVerify(s string) (*Claims, error) {
func DecodePayloadAsPrettyJSON(s string) (string, error) {
payload, err := DecodePayloadAsRawJSON(s)
if err != nil {
return "", xerrors.Errorf("could not decode the payload: %w", err)
return "", fmt.Errorf("could not decode the payload: %w", err)
}
var prettyJson bytes.Buffer
if err := json.Indent(&prettyJson, payload, "", " "); err != nil {
return "", xerrors.Errorf("could not indent the json of token: %w", err)
return "", fmt.Errorf("could not indent the json of token: %w", err)
}
return prettyJson.String(), nil
}
@@ -54,11 +53,11 @@ func DecodePayloadAsPrettyJSON(s string) (string, error) {
func DecodePayloadAsRawJSON(s string) ([]byte, error) {
parts := strings.SplitN(s, ".", 3)
if len(parts) != 3 {
return nil, xerrors.Errorf("wants %d segments but got %d segments", 3, len(parts))
return nil, fmt.Errorf("wants %d segments but got %d segments", 3, len(parts))
}
payloadJSON, err := decodePayload(parts[1])
if err != nil {
return nil, xerrors.Errorf("could not decode the payload: %w", err)
return nil, fmt.Errorf("could not decode the payload: %w", err)
}
return payloadJSON, nil
}
@@ -66,7 +65,7 @@ func DecodePayloadAsRawJSON(s string) ([]byte, error) {
func decodePayload(payload string) ([]byte, error) {
b, err := base64.URLEncoding.WithPadding(base64.NoPadding).DecodeString(payload)
if err != nil {
return nil, xerrors.Errorf("invalid base64: %w", err)
return nil, fmt.Errorf("invalid base64: %w", err)
}
return b, nil
}

View File

@@ -1,21 +1,37 @@
package kubeconfig
package loader
import (
"errors"
"fmt"
"strings"
"golang.org/x/xerrors"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/kubeconfig"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
)
func (*Kubeconfig) GetCurrentAuthProvider(explicitFilename string, contextName ContextName, userName UserName) (*AuthProvider, error) {
//go:generate mockgen -destination mock_loader/mock_loader.go github.com/int128/kubelogin/pkg/kubeconfig/loader Interface
var Set = wire.NewSet(
wire.Struct(new(Loader), "*"),
wire.Bind(new(Interface), new(*Loader)),
)
type Interface interface {
GetCurrentAuthProvider(explicitFilename string, contextName kubeconfig.ContextName, userName kubeconfig.UserName) (*kubeconfig.AuthProvider, error)
}
type Loader struct{}
func (Loader) GetCurrentAuthProvider(explicitFilename string, contextName kubeconfig.ContextName, userName kubeconfig.UserName) (*kubeconfig.AuthProvider, error) {
config, err := loadByDefaultRules(explicitFilename)
if err != nil {
return nil, xerrors.Errorf("could not load the kubeconfig: %w", err)
return nil, fmt.Errorf("could not load the kubeconfig: %w", err)
}
auth, err := findCurrentAuthProvider(config, contextName, userName)
if err != nil {
return nil, xerrors.Errorf("could not find the current auth provider: %w", err)
return nil, fmt.Errorf("could not find the current auth provider: %w", err)
}
return auth, nil
}
@@ -25,7 +41,7 @@ func loadByDefaultRules(explicitFilename string) (*api.Config, error) {
rules.ExplicitPath = explicitFilename
config, err := rules.Load()
if err != nil {
return nil, xerrors.Errorf("load error: %w", err)
return nil, fmt.Errorf("load error: %w", err)
}
return config, err
}
@@ -34,29 +50,29 @@ func loadByDefaultRules(explicitFilename string) (*api.Config, error) {
// If contextName is given, this returns the user of the context.
// If userName is given, this ignores the context and returns the user.
// If any context or user is not found, this returns an error.
func findCurrentAuthProvider(config *api.Config, contextName ContextName, userName UserName) (*AuthProvider, error) {
func findCurrentAuthProvider(config *api.Config, contextName kubeconfig.ContextName, userName kubeconfig.UserName) (*kubeconfig.AuthProvider, error) {
if userName == "" {
if contextName == "" {
contextName = ContextName(config.CurrentContext)
contextName = kubeconfig.ContextName(config.CurrentContext)
}
contextNode, ok := config.Contexts[string(contextName)]
if !ok {
return nil, xerrors.Errorf("context %s does not exist", contextName)
return nil, fmt.Errorf("context %s does not exist", contextName)
}
userName = UserName(contextNode.AuthInfo)
userName = kubeconfig.UserName(contextNode.AuthInfo)
}
userNode, ok := config.AuthInfos[string(userName)]
if !ok {
return nil, xerrors.Errorf("user %s does not exist", userName)
return nil, fmt.Errorf("user %s does not exist", userName)
}
if userNode.AuthProvider == nil {
return nil, xerrors.New("auth-provider is missing")
return nil, errors.New("auth-provider is missing")
}
if userNode.AuthProvider.Name != "oidc" {
return nil, xerrors.Errorf("auth-provider.name must be oidc but is %s", userNode.AuthProvider.Name)
return nil, fmt.Errorf("auth-provider.name must be oidc but is %s", userNode.AuthProvider.Name)
}
if userNode.AuthProvider.Config == nil {
return nil, xerrors.New("auth-provider.config is missing")
return nil, errors.New("auth-provider.config is missing")
}
m := userNode.AuthProvider.Config
@@ -64,7 +80,7 @@ func findCurrentAuthProvider(config *api.Config, contextName ContextName, userNa
if m["extra-scopes"] != "" {
extraScopes = strings.Split(m["extra-scopes"], ",")
}
return &AuthProvider{
return &kubeconfig.AuthProvider{
LocationOfOrigin: userNode.LocationOfOrigin,
UserName: userName,
ContextName: contextName,

View File

@@ -1,10 +1,11 @@
package kubeconfig
package loader
import (
"os"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/kubeconfig"
"k8s.io/client-go/tools/clientcmd/api"
)
@@ -105,7 +106,7 @@ func Test_findCurrentAuthProvider(t *testing.T) {
if err != nil {
t.Fatalf("Could not find the current auth: %s", err)
}
want := &AuthProvider{
want := &kubeconfig.AuthProvider{
LocationOfOrigin: "/path/to/kubeconfig",
UserName: "theUser",
ContextName: "theContext",
@@ -145,7 +146,7 @@ func Test_findCurrentAuthProvider(t *testing.T) {
if err != nil {
t.Fatalf("Could not find the current auth: %s", err)
}
want := &AuthProvider{
want := &kubeconfig.AuthProvider{
LocationOfOrigin: "/path/to/kubeconfig",
UserName: "theUser",
ContextName: "theContext",
@@ -173,7 +174,7 @@ func Test_findCurrentAuthProvider(t *testing.T) {
if err != nil {
t.Fatalf("Could not find the current auth: %s", err)
}
want := &AuthProvider{
want := &kubeconfig.AuthProvider{
LocationOfOrigin: "/path/to/kubeconfig",
UserName: "theUser",
IDPIssuerURL: "https://accounts.google.com",

View File

@@ -1,39 +1,39 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/kubeconfig (interfaces: Interface)
// Source: github.com/int128/kubelogin/pkg/kubeconfig/loader (interfaces: Interface)
// Package mock_kubeconfig is a generated GoMock package.
package mock_kubeconfig
// Package mock_loader is a generated GoMock package.
package mock_loader
import (
gomock "github.com/golang/mock/gomock"
kubeconfig "github.com/int128/kubelogin/pkg/adaptors/kubeconfig"
kubeconfig "github.com/int128/kubelogin/pkg/kubeconfig"
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// GetCurrentAuthProvider mocks base method.
// GetCurrentAuthProvider mocks base method
func (m *MockInterface) GetCurrentAuthProvider(arg0 string, arg1 kubeconfig.ContextName, arg2 kubeconfig.UserName) (*kubeconfig.AuthProvider, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentAuthProvider", arg0, arg1, arg2)
@@ -42,22 +42,8 @@ func (m *MockInterface) GetCurrentAuthProvider(arg0 string, arg1 kubeconfig.Cont
return ret0, ret1
}
// GetCurrentAuthProvider indicates an expected call of GetCurrentAuthProvider.
// GetCurrentAuthProvider indicates an expected call of GetCurrentAuthProvider
func (mr *MockInterfaceMockRecorder) GetCurrentAuthProvider(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentAuthProvider", reflect.TypeOf((*MockInterface)(nil).GetCurrentAuthProvider), arg0, arg1, arg2)
}
// UpdateAuthProvider mocks base method.
func (m *MockInterface) UpdateAuthProvider(arg0 *kubeconfig.AuthProvider) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateAuthProvider", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateAuthProvider indicates an expected call of UpdateAuthProvider.
func (mr *MockInterfaceMockRecorder) UpdateAuthProvider(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAuthProvider", reflect.TypeOf((*MockInterface)(nil).UpdateAuthProvider), arg0)
}

View File

@@ -1,23 +1,5 @@
package kubeconfig
import (
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/logger"
)
//go:generate mockgen -destination mock_kubeconfig/mock_kubeconfig.go github.com/int128/kubelogin/pkg/adaptors/kubeconfig Interface
// Set provides an implementation and interface for Kubeconfig.
var Set = wire.NewSet(
wire.Struct(new(Kubeconfig), "*"),
wire.Bind(new(Interface), new(*Kubeconfig)),
)
type Interface interface {
GetCurrentAuthProvider(explicitFilename string, contextName ContextName, userName UserName) (*AuthProvider, error)
UpdateAuthProvider(auth *AuthProvider) error
}
// ContextName represents name of a context.
type ContextName string
@@ -39,7 +21,3 @@ type AuthProvider struct {
IDToken string // (optional) id-token
RefreshToken string // (optional) refresh-token
}
type Kubeconfig struct {
Logger logger.Interface
}

View File

@@ -0,0 +1,48 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/kubeconfig/writer (interfaces: Interface)
// Package mock_writer is a generated GoMock package.
package mock_writer
import (
gomock "github.com/golang/mock/gomock"
kubeconfig "github.com/int128/kubelogin/pkg/kubeconfig"
reflect "reflect"
)
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// UpdateAuthProvider mocks base method
func (m *MockInterface) UpdateAuthProvider(arg0 kubeconfig.AuthProvider) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateAuthProvider", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateAuthProvider indicates an expected call of UpdateAuthProvider
func (mr *MockInterfaceMockRecorder) UpdateAuthProvider(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAuthProvider", reflect.TypeOf((*MockInterface)(nil).UpdateAuthProvider), arg0)
}

View File

@@ -1,35 +1,50 @@
package kubeconfig
package writer
import (
"fmt"
"strings"
"golang.org/x/xerrors"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/kubeconfig"
"k8s.io/client-go/tools/clientcmd"
)
func (*Kubeconfig) UpdateAuthProvider(p *AuthProvider) error {
//go:generate mockgen -destination mock_writer/mock_writer.go github.com/int128/kubelogin/pkg/kubeconfig/writer Interface
var Set = wire.NewSet(
wire.Struct(new(Writer), "*"),
wire.Bind(new(Interface), new(*Writer)),
)
type Interface interface {
UpdateAuthProvider(p kubeconfig.AuthProvider) error
}
type Writer struct{}
func (Writer) UpdateAuthProvider(p kubeconfig.AuthProvider) error {
config, err := clientcmd.LoadFromFile(p.LocationOfOrigin)
if err != nil {
return xerrors.Errorf("could not load %s: %w", p.LocationOfOrigin, err)
return fmt.Errorf("could not load %s: %w", p.LocationOfOrigin, err)
}
userNode, ok := config.AuthInfos[string(p.UserName)]
if !ok {
return xerrors.Errorf("user %s does not exist", p.UserName)
return fmt.Errorf("user %s does not exist", p.UserName)
}
if userNode.AuthProvider == nil {
return xerrors.Errorf("auth-provider is missing")
return fmt.Errorf("auth-provider is missing")
}
if userNode.AuthProvider.Name != "oidc" {
return xerrors.Errorf("auth-provider must be oidc but is %s", userNode.AuthProvider.Name)
return fmt.Errorf("auth-provider must be oidc but is %s", userNode.AuthProvider.Name)
}
copyAuthProviderConfig(p, userNode.AuthProvider.Config)
if err := clientcmd.WriteToFile(*config, p.LocationOfOrigin); err != nil {
return xerrors.Errorf("could not update %s: %w", p.LocationOfOrigin, err)
return fmt.Errorf("could not update %s: %w", p.LocationOfOrigin, err)
}
return nil
}
func copyAuthProviderConfig(p *AuthProvider, m map[string]string) {
func copyAuthProviderConfig(p kubeconfig.AuthProvider, m map[string]string) {
setOrDeleteKey(m, "idp-issuer-url", p.IDPIssuerURL)
setOrDeleteKey(m, "client-id", p.ClientID)
setOrDeleteKey(m, "client-secret", p.ClientSecret)

View File

@@ -1,4 +1,4 @@
package kubeconfig
package writer
import (
"io/ioutil"
@@ -6,10 +6,11 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/kubeconfig"
)
func TestKubeconfig_UpdateAuth(t *testing.T) {
var k Kubeconfig
var w Writer
t.Run("MinimumKeys", func(t *testing.T) {
f := newKubeconfigFile(t)
@@ -18,7 +19,7 @@ func TestKubeconfig_UpdateAuth(t *testing.T) {
t.Errorf("Could not remove the temp file: %s", err)
}
}()
if err := k.UpdateAuthProvider(&AuthProvider{
if err := w.UpdateAuthProvider(kubeconfig.AuthProvider{
LocationOfOrigin: f.Name(),
UserName: "google",
IDPIssuerURL: "https://accounts.google.com",
@@ -65,7 +66,7 @@ users:
t.Errorf("Could not remove the temp file: %s", err)
}
}()
if err := k.UpdateAuthProvider(&AuthProvider{
if err := w.UpdateAuthProvider(kubeconfig.AuthProvider{
LocationOfOrigin: f.Name(),
UserName: "google",
IDPIssuerURL: "https://accounts.google.com",

View File

@@ -1,21 +1,21 @@
package oidcclient
package client
import (
"context"
"fmt"
"net/http"
"time"
gooidc "github.com/coreos/go-oidc"
"github.com/int128/kubelogin/pkg/adaptors/clock"
"github.com/int128/kubelogin/pkg/adaptors/logger"
gooidc "github.com/coreos/go-oidc/v3/oidc"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/pkce"
"github.com/int128/oauth2cli"
"golang.org/x/oauth2"
"golang.org/x/xerrors"
)
//go:generate mockgen -destination mock_oidcclient/mock_oidcclient.go github.com/int128/kubelogin/pkg/adaptors/oidcclient Interface
//go:generate mockgen -destination mock_client/mock_client.go github.com/int128/kubelogin/pkg/oidc/client Interface
type Interface interface {
GetAuthCodeURL(in AuthCodeURLInput) string
@@ -87,7 +87,7 @@ func (c *client) GetTokenByAuthCode(ctx context.Context, in GetTokenByAuthCodeIn
}
token, err := oauth2cli.GetToken(ctx, config)
if err != nil {
return nil, xerrors.Errorf("oauth2 error: %w", err)
return nil, fmt.Errorf("oauth2 error: %w", err)
}
return c.verifyToken(ctx, token, in.Nonce)
}
@@ -108,7 +108,7 @@ func (c *client) ExchangeAuthCode(ctx context.Context, in ExchangeAuthCodeInput)
opts := tokenRequestOptions(in.PKCEParams)
token, err := cfg.Exchange(ctx, in.Code, opts...)
if err != nil {
return nil, xerrors.Errorf("exchange error: %w", err)
return nil, fmt.Errorf("exchange error: %w", err)
}
return c.verifyToken(ctx, token, in.Nonce)
}
@@ -148,7 +148,7 @@ func (c *client) GetTokenByROPC(ctx context.Context, username, password string)
ctx = c.wrapContext(ctx)
token, err := c.oauth2Config.PasswordCredentialsToken(ctx, username, password)
if err != nil {
return nil, xerrors.Errorf("resource owner password credentials flow error: %w", err)
return nil, fmt.Errorf("resource owner password credentials flow error: %w", err)
}
return c.verifyToken(ctx, token, "")
}
@@ -163,7 +163,7 @@ func (c *client) Refresh(ctx context.Context, refreshToken string) (*oidc.TokenS
source := c.oauth2Config.TokenSource(ctx, currentToken)
token, err := source.Token()
if err != nil {
return nil, xerrors.Errorf("could not refresh the token: %w", err)
return nil, fmt.Errorf("could not refresh the token: %w", err)
}
return c.verifyToken(ctx, token, "")
}
@@ -173,15 +173,15 @@ func (c *client) Refresh(ctx context.Context, refreshToken string) (*oidc.TokenS
func (c *client) verifyToken(ctx context.Context, token *oauth2.Token, nonce string) (*oidc.TokenSet, error) {
idToken, ok := token.Extra("id_token").(string)
if !ok {
return nil, xerrors.Errorf("id_token is missing in the token response: %s", token)
return nil, fmt.Errorf("id_token is missing in the token response: %s", token)
}
verifier := c.provider.Verifier(&gooidc.Config{ClientID: c.oauth2Config.ClientID, Now: c.clock.Now})
verifiedIDToken, err := verifier.Verify(ctx, idToken)
if err != nil {
return nil, xerrors.Errorf("could not verify the ID token: %w", err)
return nil, fmt.Errorf("could not verify the ID token: %w", err)
}
if nonce != "" && nonce != verifiedIDToken.Nonce {
return nil, xerrors.Errorf("nonce did not match (wants %s but got %s)", nonce, verifiedIDToken.Nonce)
return nil, fmt.Errorf("nonce did not match (wants %s but got %s)", nonce, verifiedIDToken.Nonce)
}
return &oidc.TokenSet{
IDToken: idToken,

View File

@@ -1,24 +1,24 @@
// Package oidcclient provides a client of OpenID Connect.
package oidcclient
// Package client provides a client of OpenID Connect.
package client
import (
"context"
"fmt"
"net/http"
gooidc "github.com/coreos/go-oidc"
gooidc "github.com/coreos/go-oidc/v3/oidc"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/clock"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/logging"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client/logging"
"github.com/int128/kubelogin/pkg/pkce"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/tlsclientconfig/loader"
"golang.org/x/oauth2"
"golang.org/x/xerrors"
)
//go:generate mockgen -destination mock_oidcclient/mock_factory.go github.com/int128/kubelogin/pkg/adaptors/oidcclient FactoryInterface
//go:generate mockgen -destination mock_client/mock_factory.go github.com/int128/kubelogin/pkg/oidc/client FactoryInterface
var Set = wire.NewSet(
wire.Struct(new(Factory), "*"),
@@ -35,11 +35,11 @@ type Factory struct {
Logger logger.Interface
}
// New returns an instance of adaptors.Interface with the given configuration.
// New returns an instance of infrastructure.Interface with the given configuration.
func (f *Factory) New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsclientconfig.Config) (Interface, error) {
rawTLSClientConfig, err := f.Loader.Load(tlsClientConfig)
if err != nil {
return nil, xerrors.Errorf("could not load the TLS client config: %w", err)
return nil, fmt.Errorf("could not load the TLS client config: %w", err)
}
baseTransport := &http.Transport{
TLSClientConfig: rawTLSClientConfig,
@@ -56,11 +56,14 @@ func (f *Factory) New(ctx context.Context, p oidc.Provider, tlsClientConfig tlsc
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
provider, err := gooidc.NewProvider(ctx, p.IssuerURL)
if err != nil {
return nil, xerrors.Errorf("oidc discovery error: %w", err)
return nil, fmt.Errorf("oidc discovery error: %w", err)
}
supportedPKCEMethods, err := extractSupportedPKCEMethods(provider)
if err != nil {
return nil, xerrors.Errorf("could not determine supported PKCE methods: %w", err)
return nil, fmt.Errorf("could not determine supported PKCE methods: %w", err)
}
if len(supportedPKCEMethods) == 0 && p.UsePKCE {
supportedPKCEMethods = []string{pkce.MethodS256}
}
return &client{
httpClient: httpClient,

View File

@@ -4,7 +4,7 @@ import (
"net/http"
"net/http/httputil"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
)
const (

View File

@@ -1,42 +1,42 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/oidcclient (interfaces: Interface)
// Source: github.com/int128/kubelogin/pkg/oidc/client (interfaces: Interface)
// Package mock_oidcclient is a generated GoMock package.
package mock_oidcclient
// Package mock_client is a generated GoMock package.
package mock_client
import (
context "context"
gomock "github.com/golang/mock/gomock"
oidcclient "github.com/int128/kubelogin/pkg/adaptors/oidcclient"
oidc "github.com/int128/kubelogin/pkg/oidc"
client "github.com/int128/kubelogin/pkg/oidc/client"
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// ExchangeAuthCode mocks base method.
func (m *MockInterface) ExchangeAuthCode(arg0 context.Context, arg1 oidcclient.ExchangeAuthCodeInput) (*oidc.TokenSet, error) {
// ExchangeAuthCode mocks base method
func (m *MockInterface) ExchangeAuthCode(arg0 context.Context, arg1 client.ExchangeAuthCodeInput) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ExchangeAuthCode", arg0, arg1)
ret0, _ := ret[0].(*oidc.TokenSet)
@@ -44,28 +44,28 @@ func (m *MockInterface) ExchangeAuthCode(arg0 context.Context, arg1 oidcclient.E
return ret0, ret1
}
// ExchangeAuthCode indicates an expected call of ExchangeAuthCode.
// ExchangeAuthCode indicates an expected call of ExchangeAuthCode
func (mr *MockInterfaceMockRecorder) ExchangeAuthCode(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExchangeAuthCode", reflect.TypeOf((*MockInterface)(nil).ExchangeAuthCode), arg0, arg1)
}
// GetAuthCodeURL mocks base method.
func (m *MockInterface) GetAuthCodeURL(arg0 oidcclient.AuthCodeURLInput) string {
// GetAuthCodeURL mocks base method
func (m *MockInterface) GetAuthCodeURL(arg0 client.AuthCodeURLInput) string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAuthCodeURL", arg0)
ret0, _ := ret[0].(string)
return ret0
}
// GetAuthCodeURL indicates an expected call of GetAuthCodeURL.
// GetAuthCodeURL indicates an expected call of GetAuthCodeURL
func (mr *MockInterfaceMockRecorder) GetAuthCodeURL(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthCodeURL", reflect.TypeOf((*MockInterface)(nil).GetAuthCodeURL), arg0)
}
// GetTokenByAuthCode mocks base method.
func (m *MockInterface) GetTokenByAuthCode(arg0 context.Context, arg1 oidcclient.GetTokenByAuthCodeInput, arg2 chan<- string) (*oidc.TokenSet, error) {
// GetTokenByAuthCode mocks base method
func (m *MockInterface) GetTokenByAuthCode(arg0 context.Context, arg1 client.GetTokenByAuthCodeInput, arg2 chan<- string) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetTokenByAuthCode", arg0, arg1, arg2)
ret0, _ := ret[0].(*oidc.TokenSet)
@@ -73,13 +73,13 @@ func (m *MockInterface) GetTokenByAuthCode(arg0 context.Context, arg1 oidcclient
return ret0, ret1
}
// GetTokenByAuthCode indicates an expected call of GetTokenByAuthCode.
// GetTokenByAuthCode indicates an expected call of GetTokenByAuthCode
func (mr *MockInterfaceMockRecorder) GetTokenByAuthCode(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTokenByAuthCode", reflect.TypeOf((*MockInterface)(nil).GetTokenByAuthCode), arg0, arg1, arg2)
}
// GetTokenByROPC mocks base method.
// GetTokenByROPC mocks base method
func (m *MockInterface) GetTokenByROPC(arg0 context.Context, arg1, arg2 string) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetTokenByROPC", arg0, arg1, arg2)
@@ -88,13 +88,13 @@ func (m *MockInterface) GetTokenByROPC(arg0 context.Context, arg1, arg2 string)
return ret0, ret1
}
// GetTokenByROPC indicates an expected call of GetTokenByROPC.
// GetTokenByROPC indicates an expected call of GetTokenByROPC
func (mr *MockInterfaceMockRecorder) GetTokenByROPC(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTokenByROPC", reflect.TypeOf((*MockInterface)(nil).GetTokenByROPC), arg0, arg1, arg2)
}
// Refresh mocks base method.
// Refresh mocks base method
func (m *MockInterface) Refresh(arg0 context.Context, arg1 string) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Refresh", arg0, arg1)
@@ -103,13 +103,13 @@ func (m *MockInterface) Refresh(arg0 context.Context, arg1 string) (*oidc.TokenS
return ret0, ret1
}
// Refresh indicates an expected call of Refresh.
// Refresh indicates an expected call of Refresh
func (mr *MockInterfaceMockRecorder) Refresh(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Refresh", reflect.TypeOf((*MockInterface)(nil).Refresh), arg0, arg1)
}
// SupportedPKCEMethods mocks base method.
// SupportedPKCEMethods mocks base method
func (m *MockInterface) SupportedPKCEMethods() []string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SupportedPKCEMethods")
@@ -117,7 +117,7 @@ func (m *MockInterface) SupportedPKCEMethods() []string {
return ret0
}
// SupportedPKCEMethods indicates an expected call of SupportedPKCEMethods.
// SupportedPKCEMethods indicates an expected call of SupportedPKCEMethods
func (mr *MockInterfaceMockRecorder) SupportedPKCEMethods() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SupportedPKCEMethods", reflect.TypeOf((*MockInterface)(nil).SupportedPKCEMethods))

View File

@@ -1,51 +1,51 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/oidcclient (interfaces: FactoryInterface)
// Source: github.com/int128/kubelogin/pkg/oidc/client (interfaces: FactoryInterface)
// Package mock_oidcclient is a generated GoMock package.
package mock_oidcclient
// Package mock_client is a generated GoMock package.
package mock_client
import (
context "context"
gomock "github.com/golang/mock/gomock"
oidcclient "github.com/int128/kubelogin/pkg/adaptors/oidcclient"
oidc "github.com/int128/kubelogin/pkg/oidc"
client "github.com/int128/kubelogin/pkg/oidc/client"
tlsclientconfig "github.com/int128/kubelogin/pkg/tlsclientconfig"
reflect "reflect"
)
// MockFactoryInterface is a mock of FactoryInterface interface.
// MockFactoryInterface is a mock of FactoryInterface interface
type MockFactoryInterface struct {
ctrl *gomock.Controller
recorder *MockFactoryInterfaceMockRecorder
}
// MockFactoryInterfaceMockRecorder is the mock recorder for MockFactoryInterface.
// MockFactoryInterfaceMockRecorder is the mock recorder for MockFactoryInterface
type MockFactoryInterfaceMockRecorder struct {
mock *MockFactoryInterface
}
// NewMockFactoryInterface creates a new mock instance.
// NewMockFactoryInterface creates a new mock instance
func NewMockFactoryInterface(ctrl *gomock.Controller) *MockFactoryInterface {
mock := &MockFactoryInterface{ctrl: ctrl}
mock.recorder = &MockFactoryInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockFactoryInterface) EXPECT() *MockFactoryInterfaceMockRecorder {
return m.recorder
}
// New mocks base method.
func (m *MockFactoryInterface) New(arg0 context.Context, arg1 oidc.Provider, arg2 tlsclientconfig.Config) (oidcclient.Interface, error) {
// New mocks base method
func (m *MockFactoryInterface) New(arg0 context.Context, arg1 oidc.Provider, arg2 tlsclientconfig.Config) (client.Interface, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "New", arg0, arg1, arg2)
ret0, _ := ret[0].(oidcclient.Interface)
ret0, _ := ret[0].(client.Interface)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// New indicates an expected call of New.
// New indicates an expected call of New
func (mr *MockFactoryInterfaceMockRecorder) New(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockFactoryInterface)(nil).New), arg0, arg1, arg2)

View File

@@ -4,9 +4,9 @@ import (
"crypto/rand"
"encoding/base64"
"encoding/binary"
"fmt"
"github.com/int128/kubelogin/pkg/jwt"
"golang.org/x/xerrors"
)
// Provider represents an OIDC provider.
@@ -15,6 +15,7 @@ type Provider struct {
ClientID string
ClientSecret string // optional
ExtraScopes []string // optional
UsePKCE bool // optional
}
// TokenSet represents a set of ID token and refresh token.
@@ -30,7 +31,7 @@ func (ts TokenSet) DecodeWithoutVerify() (*jwt.Claims, error) {
func NewState() (string, error) {
b, err := random32()
if err != nil {
return "", xerrors.Errorf("could not generate a random: %w", err)
return "", fmt.Errorf("could not generate a random: %w", err)
}
return base64URLEncode(b), nil
}
@@ -38,7 +39,7 @@ func NewState() (string, error) {
func NewNonce() (string, error) {
b, err := random32()
if err != nil {
return "", xerrors.Errorf("could not generate a random: %w", err)
return "", fmt.Errorf("could not generate a random: %w", err)
}
return base64URLEncode(b), nil
}
@@ -46,7 +47,7 @@ func NewNonce() (string, error) {
func random32() ([]byte, error) {
b := make([]byte, 32)
if err := binary.Read(rand.Reader, binary.LittleEndian, b); err != nil {
return nil, xerrors.Errorf("read error: %w", err)
return nil, fmt.Errorf("read error: %w", err)
}
return b, nil
}

View File

@@ -7,15 +7,14 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"golang.org/x/xerrors"
"fmt"
)
var Plain Params
const (
// code challenge methods defined as https://tools.ietf.org/html/rfc7636#section-4.3
methodS256 = "S256"
MethodS256 = "S256"
)
// Params represents a set of the PKCE parameters.
@@ -34,7 +33,7 @@ func (p Params) IsZero() bool {
// It returns Plain if no method is available.
func New(methods []string) (Params, error) {
for _, method := range methods {
if method == methodS256 {
if method == MethodS256 {
return NewS256()
}
}
@@ -45,7 +44,7 @@ func New(methods []string) (Params, error) {
func NewS256() (Params, error) {
b, err := random32()
if err != nil {
return Plain, xerrors.Errorf("could not generate a random: %w", err)
return Plain, fmt.Errorf("could not generate a random: %w", err)
}
return computeS256(b), nil
}
@@ -53,7 +52,7 @@ func NewS256() (Params, error) {
func random32() ([]byte, error) {
b := make([]byte, 32)
if err := binary.Read(rand.Reader, binary.LittleEndian, b); err != nil {
return nil, xerrors.Errorf("read error: %w", err)
return nil, fmt.Errorf("read error: %w", err)
}
return b, nil
}
@@ -64,7 +63,7 @@ func computeS256(b []byte) Params {
_, _ = s.Write([]byte(v))
return Params{
CodeChallenge: base64URLEncode(s.Sum(nil)),
CodeChallengeMethod: methodS256,
CodeChallengeMethod: MethodS256,
CodeVerifier: v,
}
}

View File

@@ -5,7 +5,7 @@ import (
"crypto/rsa"
"testing"
"github.com/dgrijalva/jwt-go"
"github.com/golang-jwt/jwt/v4"
)
var PrivateKey = generateKey(1024)

View File

@@ -3,7 +3,7 @@ package logger
import (
"fmt"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/spf13/pflag"
)

View File

@@ -5,11 +5,12 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"golang.org/x/xerrors"
)
// Set provides an implementation and interface.
@@ -29,12 +30,12 @@ func (l *Loader) Load(config tlsclientconfig.Config) (*tls.Config, error) {
rootCAs := x509.NewCertPool()
for _, f := range config.CACertFilename {
if err := addFile(rootCAs, f); err != nil {
return nil, xerrors.Errorf("could not load the certificate from %s: %w", f, err)
return nil, fmt.Errorf("could not load the certificate from %s: %w", f, err)
}
}
for _, d := range config.CACertData {
if err := addBase64Encoded(rootCAs, d); err != nil {
return nil, xerrors.Errorf("could not load the certificate: %w", err)
return nil, fmt.Errorf("could not load the certificate: %w", err)
}
}
if len(rootCAs.Subjects()) == 0 {
@@ -51,10 +52,10 @@ func (l *Loader) Load(config tlsclientconfig.Config) (*tls.Config, error) {
func addFile(p *x509.CertPool, filename string) error {
b, err := ioutil.ReadFile(filename)
if err != nil {
return xerrors.Errorf("could not read: %w", err)
return fmt.Errorf("could not read: %w", err)
}
if !p.AppendCertsFromPEM(b) {
return xerrors.New("invalid certificate")
return errors.New("invalid certificate")
}
return nil
}
@@ -62,10 +63,10 @@ func addFile(p *x509.CertPool, filename string) error {
func addBase64Encoded(p *x509.CertPool, s string) error {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return xerrors.Errorf("could not decode base64: %w", err)
return fmt.Errorf("could not decode base64: %w", err)
}
if !p.AppendCertsFromPEM(b) {
return xerrors.New("invalid certificate")
return errors.New("invalid certificate")
}
return nil
}

View File

@@ -1,40 +1,40 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/int128/kubelogin/pkg/adaptors/tokencache (interfaces: Interface)
// Source: github.com/int128/kubelogin/pkg/tokencache/repository (interfaces: Interface)
// Package mock_tokencache is a generated GoMock package.
package mock_tokencache
// Package mock_repository is a generated GoMock package.
package mock_repository
import (
gomock "github.com/golang/mock/gomock"
tokencache "github.com/int128/kubelogin/pkg/adaptors/tokencache"
oidc "github.com/int128/kubelogin/pkg/oidc"
tokencache "github.com/int128/kubelogin/pkg/tokencache"
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// FindByKey mocks base method.
// FindByKey mocks base method
func (m *MockInterface) FindByKey(arg0 string, arg1 tokencache.Key) (*oidc.TokenSet, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindByKey", arg0, arg1)
@@ -43,13 +43,13 @@ func (m *MockInterface) FindByKey(arg0 string, arg1 tokencache.Key) (*oidc.Token
return ret0, ret1
}
// FindByKey indicates an expected call of FindByKey.
// FindByKey indicates an expected call of FindByKey
func (mr *MockInterfaceMockRecorder) FindByKey(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByKey", reflect.TypeOf((*MockInterface)(nil).FindByKey), arg0, arg1)
}
// Save mocks base method.
// Save mocks base method
func (m *MockInterface) Save(arg0 string, arg1 tokencache.Key, arg2 oidc.TokenSet) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Save", arg0, arg1, arg2)
@@ -57,7 +57,7 @@ func (m *MockInterface) Save(arg0 string, arg1 tokencache.Key, arg2 oidc.TokenSe
return ret0
}
// Save indicates an expected call of Save.
// Save indicates an expected call of Save
func (mr *MockInterfaceMockRecorder) Save(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockInterface)(nil).Save), arg0, arg1, arg2)

View File

@@ -1,19 +1,20 @@
package tokencache
package repository
import (
"crypto/sha256"
"encoding/gob"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/oidc"
"golang.org/x/xerrors"
"github.com/int128/kubelogin/pkg/tokencache"
)
//go:generate mockgen -destination mock_tokencache/mock_tokencache.go github.com/int128/kubelogin/pkg/adaptors/tokencache Interface
//go:generate mockgen -destination mock_repository/mock_repository.go github.com/int128/kubelogin/pkg/tokencache/repository Interface
// Set provides an implementation and interface for Kubeconfig.
var Set = wire.NewSet(
@@ -22,20 +23,8 @@ var Set = wire.NewSet(
)
type Interface interface {
FindByKey(dir string, key Key) (*oidc.TokenSet, error)
Save(dir string, key Key, tokenSet oidc.TokenSet) error
}
// Key represents a key of a token cache.
type Key struct {
IssuerURL string
ClientID string
ClientSecret string
Username string
ExtraScopes []string
CACertFilename string
CACertData string
SkipTLSVerify bool
FindByKey(dir string, key tokencache.Key) (*oidc.TokenSet, error)
Save(dir string, key tokencache.Key, tokenSet oidc.TokenSet) error
}
type entity struct {
@@ -47,21 +36,21 @@ type entity struct {
// Filename of a token cache is sha256 digest of the issuer, zero-character and client ID.
type Repository struct{}
func (r *Repository) FindByKey(dir string, key Key) (*oidc.TokenSet, error) {
func (r *Repository) FindByKey(dir string, key tokencache.Key) (*oidc.TokenSet, error) {
filename, err := computeFilename(key)
if err != nil {
return nil, xerrors.Errorf("could not compute the key: %w", err)
return nil, fmt.Errorf("could not compute the key: %w", err)
}
p := filepath.Join(dir, filename)
f, err := os.Open(p)
if err != nil {
return nil, xerrors.Errorf("could not open file %s: %w", p, err)
return nil, fmt.Errorf("could not open file %s: %w", p, err)
}
defer f.Close()
d := json.NewDecoder(f)
var e entity
if err := d.Decode(&e); err != nil {
return nil, xerrors.Errorf("invalid json file %s: %w", p, err)
return nil, fmt.Errorf("invalid json file %s: %w", p, err)
}
return &oidc.TokenSet{
IDToken: e.IDToken,
@@ -69,18 +58,18 @@ func (r *Repository) FindByKey(dir string, key Key) (*oidc.TokenSet, error) {
}, nil
}
func (r *Repository) Save(dir string, key Key, tokenSet oidc.TokenSet) error {
func (r *Repository) Save(dir string, key tokencache.Key, tokenSet oidc.TokenSet) error {
if err := os.MkdirAll(dir, 0700); err != nil {
return xerrors.Errorf("could not create directory %s: %w", dir, err)
return fmt.Errorf("could not create directory %s: %w", dir, err)
}
filename, err := computeFilename(key)
if err != nil {
return xerrors.Errorf("could not compute the key: %w", err)
return fmt.Errorf("could not compute the key: %w", err)
}
p := filepath.Join(dir, filename)
f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return xerrors.Errorf("could not create file %s: %w", p, err)
return fmt.Errorf("could not create file %s: %w", p, err)
}
defer f.Close()
e := entity{
@@ -88,16 +77,16 @@ func (r *Repository) Save(dir string, key Key, tokenSet oidc.TokenSet) error {
RefreshToken: tokenSet.RefreshToken,
}
if err := json.NewEncoder(f).Encode(&e); err != nil {
return xerrors.Errorf("json encode error: %w", err)
return fmt.Errorf("json encode error: %w", err)
}
return nil
}
func computeFilename(key Key) (string, error) {
func computeFilename(key tokencache.Key) (string, error) {
s := sha256.New()
e := gob.NewEncoder(s)
if err := e.Encode(&key); err != nil {
return "", xerrors.Errorf("could not encode the key: %w", err)
return "", fmt.Errorf("could not encode the key: %w", err)
}
h := hex.EncodeToString(s.Sum(nil))
return h, nil

View File

@@ -1,4 +1,4 @@
package tokencache
package repository
import (
"io/ioutil"
@@ -7,6 +7,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/tokencache"
)
func TestRepository_FindByKey(t *testing.T) {
@@ -14,7 +15,7 @@ func TestRepository_FindByKey(t *testing.T) {
t.Run("Success", func(t *testing.T) {
dir := t.TempDir()
key := Key{
key := tokencache.Key{
IssuerURL: "YOUR_ISSUER",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
@@ -48,7 +49,7 @@ func TestRepository_Save(t *testing.T) {
t.Run("Success", func(t *testing.T) {
dir := t.TempDir()
key := Key{
key := tokencache.Key{
IssuerURL: "YOUR_ISSUER",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",

13
pkg/tokencache/types.go Normal file
View File

@@ -0,0 +1,13 @@
package tokencache
// Key represents a key of a token cache.
type Key struct {
IssuerURL string
ClientID string
ClientSecret string
Username string
ExtraScopes []string
CACertFilename string
CACertData string
SkipTLSVerify bool
}

View File

@@ -2,19 +2,20 @@ package authcode
import (
"context"
"fmt"
"time"
"github.com/int128/kubelogin/pkg/adaptors/browser"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/infrastructure/browser"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/pkce"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"
)
type BrowserOption struct {
SkipOpenBrowser bool
BrowserCommand string
BindAddress []string
AuthenticationTimeout time.Duration
OpenURLAfterAuthentication string
@@ -30,25 +31,25 @@ type Browser struct {
Logger logger.Interface
}
func (u *Browser) Do(ctx context.Context, o *BrowserOption, client oidcclient.Interface) (*oidc.TokenSet, error) {
func (u *Browser) Do(ctx context.Context, o *BrowserOption, oidcClient client.Interface) (*oidc.TokenSet, error) {
u.Logger.V(1).Infof("starting the authentication code flow using the browser")
state, err := oidc.NewState()
if err != nil {
return nil, xerrors.Errorf("could not generate a state: %w", err)
return nil, fmt.Errorf("could not generate a state: %w", err)
}
nonce, err := oidc.NewNonce()
if err != nil {
return nil, xerrors.Errorf("could not generate a nonce: %w", err)
return nil, fmt.Errorf("could not generate a nonce: %w", err)
}
p, err := pkce.New(client.SupportedPKCEMethods())
p, err := pkce.New(oidcClient.SupportedPKCEMethods())
if err != nil {
return nil, xerrors.Errorf("could not generate PKCE parameters: %w", err)
return nil, fmt.Errorf("could not generate PKCE parameters: %w", err)
}
successHTML := BrowserSuccessHTML
if o.OpenURLAfterAuthentication != "" {
successHTML = BrowserRedirectHTML(o.OpenURLAfterAuthentication)
}
in := oidcclient.GetTokenByAuthCodeInput{
in := client.GetTokenByAuthCodeInput{
BindAddress: o.BindAddress,
State: state,
Nonce: nonce,
@@ -71,35 +72,47 @@ func (u *Browser) Do(ctx context.Context, o *BrowserOption, client oidcclient.In
if !ok {
return nil
}
if o.SkipOpenBrowser {
u.Logger.Printf("Please visit the following URL in your browser: %s", url)
return nil
}
u.Logger.V(1).Infof("opening %s in the browser", url)
if err := u.Browser.Open(url); err != nil {
u.Logger.Printf(`error: could not open the browser: %s
Please visit the following URL in your browser manually: %s`, err, url)
return nil
}
u.openURL(ctx, o, url)
return nil
case <-ctx.Done():
return xerrors.Errorf("context cancelled while waiting for the local server: %w", ctx.Err())
return fmt.Errorf("context cancelled while waiting for the local server: %w", ctx.Err())
}
})
eg.Go(func() error {
defer close(readyChan)
tokenSet, err := client.GetTokenByAuthCode(ctx, in, readyChan)
tokenSet, err := oidcClient.GetTokenByAuthCode(ctx, in, readyChan)
if err != nil {
return xerrors.Errorf("authorization code flow error: %w", err)
return fmt.Errorf("authorization code flow error: %w", err)
}
out = tokenSet
u.Logger.V(1).Infof("got a token set by the authorization code flow")
return nil
})
if err := eg.Wait(); err != nil {
return nil, xerrors.Errorf("authentication error: %w", err)
return nil, fmt.Errorf("authentication error: %w", err)
}
u.Logger.V(1).Infof("finished the authorization code flow via the browser")
return out, nil
}
func (u *Browser) openURL(ctx context.Context, o *BrowserOption, url string) {
if o.SkipOpenBrowser {
u.Logger.Printf("Please visit the following URL in your browser: %s", url)
return
}
u.Logger.V(1).Infof("opening %s in the browser", url)
if o.BrowserCommand != "" {
if err := u.Browser.OpenCommand(ctx, url, o.BrowserCommand); err != nil {
u.Logger.Printf(`error: could not open the browser: %s
Please visit the following URL in your browser manually: %s`, err, url)
}
return
}
if err := u.Browser.Open(url); err != nil {
u.Logger.Printf(`error: could not open the browser: %s
Please visit the following URL in your browser manually: %s`, err, url)
}
}

View File

@@ -7,10 +7,10 @@ import (
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/adaptors/browser/mock_browser"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/infrastructure/browser/mock_browser"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/oidc/client/mock_client"
"github.com/int128/kubelogin/pkg/testing/logger"
)
@@ -32,11 +32,11 @@ func TestBrowser_Do(t *testing.T) {
RedirectURLHostname: "localhost",
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().SupportedPKCEMethods()
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().SupportedPKCEMethods()
mockClient.EXPECT().
GetTokenByAuthCode(gomock.Any(), gomock.Any(), gomock.Any()).
Do(func(_ context.Context, in oidcclient.GetTokenByAuthCodeInput, readyChan chan<- string) {
Do(func(_ context.Context, in client.GetTokenByAuthCodeInput, readyChan chan<- string) {
if diff := cmp.Diff(o.BindAddress, in.BindAddress); diff != "" {
t.Errorf("BindAddress mismatch (-want +got):\n%s", diff)
}
@@ -64,7 +64,7 @@ func TestBrowser_Do(t *testing.T) {
u := Browser{
Logger: logger.New(t),
}
got, err := u.Do(ctx, o, mockOIDCClient)
got, err := u.Do(ctx, o, mockClient)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}
@@ -86,11 +86,11 @@ func TestBrowser_Do(t *testing.T) {
BindAddress: []string{"127.0.0.1:8000"},
AuthenticationTimeout: 10 * time.Second,
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().SupportedPKCEMethods()
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().SupportedPKCEMethods()
mockClient.EXPECT().
GetTokenByAuthCode(gomock.Any(), gomock.Any(), gomock.Any()).
Do(func(_ context.Context, _ oidcclient.GetTokenByAuthCodeInput, readyChan chan<- string) {
Do(func(_ context.Context, _ client.GetTokenByAuthCodeInput, readyChan chan<- string) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidc.TokenSet{
@@ -104,7 +104,48 @@ func TestBrowser_Do(t *testing.T) {
Logger: logger.New(t),
Browser: mockBrowser,
}
got, err := u.Do(ctx, o, mockOIDCClient)
got, err := u.Do(ctx, o, mockClient)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}
want := &oidc.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
})
t.Run("OpenBrowserCommand", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
o := &BrowserOption{
BindAddress: []string{"127.0.0.1:8000"},
BrowserCommand: "firefox",
AuthenticationTimeout: 10 * time.Second,
}
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().SupportedPKCEMethods()
mockClient.EXPECT().
GetTokenByAuthCode(gomock.Any(), gomock.Any(), gomock.Any()).
Do(func(_ context.Context, _ client.GetTokenByAuthCodeInput, readyChan chan<- string) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidc.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
}, nil)
mockBrowser := mock_browser.NewMockInterface(ctrl)
mockBrowser.EXPECT().
OpenCommand(gomock.Any(), "LOCAL_SERVER_URL", "firefox")
u := Browser{
Logger: logger.New(t),
Browser: mockBrowser,
}
got, err := u.Do(ctx, o, mockClient)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}

View File

@@ -2,13 +2,13 @@ package authcode
import (
"context"
"fmt"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/reader"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/infrastructure/reader"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/pkce"
"golang.org/x/xerrors"
)
const keyboardPrompt = "Enter code: "
@@ -24,21 +24,21 @@ type Keyboard struct {
Logger logger.Interface
}
func (u *Keyboard) Do(ctx context.Context, o *KeyboardOption, client oidcclient.Interface) (*oidc.TokenSet, error) {
func (u *Keyboard) Do(ctx context.Context, o *KeyboardOption, oidcClient client.Interface) (*oidc.TokenSet, error) {
u.Logger.V(1).Infof("starting the authorization code flow with keyboard interactive")
state, err := oidc.NewState()
if err != nil {
return nil, xerrors.Errorf("could not generate a state: %w", err)
return nil, fmt.Errorf("could not generate a state: %w", err)
}
nonce, err := oidc.NewNonce()
if err != nil {
return nil, xerrors.Errorf("could not generate a nonce: %w", err)
return nil, fmt.Errorf("could not generate a nonce: %w", err)
}
p, err := pkce.New(client.SupportedPKCEMethods())
p, err := pkce.New(oidcClient.SupportedPKCEMethods())
if err != nil {
return nil, xerrors.Errorf("could not generate PKCE parameters: %w", err)
return nil, fmt.Errorf("could not generate PKCE parameters: %w", err)
}
authCodeURL := client.GetAuthCodeURL(oidcclient.AuthCodeURLInput{
authCodeURL := oidcClient.GetAuthCodeURL(client.AuthCodeURLInput{
State: state,
Nonce: nonce,
PKCEParams: p,
@@ -48,18 +48,18 @@ func (u *Keyboard) Do(ctx context.Context, o *KeyboardOption, client oidcclient.
u.Logger.Printf("Please visit the following URL in your browser: %s", authCodeURL)
code, err := u.Reader.ReadString(keyboardPrompt)
if err != nil {
return nil, xerrors.Errorf("could not read an authorization code: %w", err)
return nil, fmt.Errorf("could not read an authorization code: %w", err)
}
u.Logger.V(1).Infof("exchanging the code and token")
tokenSet, err := client.ExchangeAuthCode(ctx, oidcclient.ExchangeAuthCodeInput{
tokenSet, err := oidcClient.ExchangeAuthCode(ctx, client.ExchangeAuthCodeInput{
Code: code,
PKCEParams: p,
Nonce: nonce,
RedirectURI: oobRedirectURI,
})
if err != nil {
return nil, xerrors.Errorf("could not exchange the authorization code: %w", err)
return nil, fmt.Errorf("could not exchange the authorization code: %w", err)
}
u.Logger.V(1).Infof("finished the authorization code flow with keyboard interactive")
return tokenSet, nil

View File

@@ -7,10 +7,10 @@ import (
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/reader/mock_reader"
"github.com/int128/kubelogin/pkg/infrastructure/reader/mock_reader"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/oidc/client/mock_client"
"github.com/int128/kubelogin/pkg/testing/logger"
)
@@ -27,19 +27,19 @@ func TestKeyboard_Do(t *testing.T) {
o := &KeyboardOption{
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().SupportedPKCEMethods()
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().SupportedPKCEMethods()
mockClient.EXPECT().
GetAuthCodeURL(nonNil).
Do(func(in oidcclient.AuthCodeURLInput) {
Do(func(in client.AuthCodeURLInput) {
if diff := cmp.Diff(o.AuthRequestExtraParams, in.AuthRequestExtraParams); diff != "" {
t.Errorf("AuthRequestExtraParams mismatch (-want +got):\n%s", diff)
}
}).
Return("https://issuer.example.com/auth")
mockOIDCClient.EXPECT().
mockClient.EXPECT().
ExchangeAuthCode(nonNil, nonNil).
Do(func(_ context.Context, in oidcclient.ExchangeAuthCodeInput) {
Do(func(_ context.Context, in client.ExchangeAuthCodeInput) {
if in.Code != "YOUR_AUTH_CODE" {
t.Errorf("Code wants YOUR_AUTH_CODE but was %s", in.Code)
}
@@ -56,7 +56,7 @@ func TestKeyboard_Do(t *testing.T) {
Reader: mockReader,
Logger: logger.New(t),
}
got, err := u.Do(ctx, o, mockOIDCClient)
got, err := u.Do(ctx, o, mockClient)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}

View File

@@ -2,16 +2,16 @@ package authentication
import (
"context"
"fmt"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/clock"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/infrastructure/clock"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
"golang.org/x/xerrors"
)
//go:generate mockgen -destination mock_authentication/mock_authentication.go github.com/int128/kubelogin/pkg/usecases/authentication Interface
@@ -63,7 +63,7 @@ type Output struct {
// If the Password is not set, it asks a password by the prompt.
//
type Authentication struct {
OIDCClient oidcclient.FactoryInterface
ClientFactory client.FactoryInterface
Logger logger.Interface
Clock clock.Interface
AuthCodeBrowser *authcode.Browser
@@ -79,7 +79,7 @@ func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
// because the token has been verified before caching.
claims, err := in.CachedTokenSet.DecodeWithoutVerify()
if err != nil {
return nil, xerrors.Errorf("invalid token cache (you may need to remove): %w", err)
return nil, fmt.Errorf("invalid token cache (you may need to remove): %w", err)
}
if !claims.IsExpired(u.Clock) {
u.Logger.V(1).Infof("you already have a valid token until %s", claims.Expiry)
@@ -92,14 +92,14 @@ func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
}
u.Logger.V(1).Infof("initializing an OpenID Connect client")
client, err := u.OIDCClient.New(ctx, in.Provider, in.TLSClientConfig)
oidcClient, err := u.ClientFactory.New(ctx, in.Provider, in.TLSClientConfig)
if err != nil {
return nil, xerrors.Errorf("oidc error: %w", err)
return nil, fmt.Errorf("oidc error: %w", err)
}
if in.CachedTokenSet != nil && in.CachedTokenSet.RefreshToken != "" {
u.Logger.V(1).Infof("refreshing the token")
tokenSet, err := client.Refresh(ctx, in.CachedTokenSet.RefreshToken)
tokenSet, err := oidcClient.Refresh(ctx, in.CachedTokenSet.RefreshToken)
if err == nil {
return &Output{TokenSet: *tokenSet}, nil
}
@@ -107,25 +107,25 @@ func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
}
if in.GrantOptionSet.AuthCodeBrowserOption != nil {
tokenSet, err := u.AuthCodeBrowser.Do(ctx, in.GrantOptionSet.AuthCodeBrowserOption, client)
tokenSet, err := u.AuthCodeBrowser.Do(ctx, in.GrantOptionSet.AuthCodeBrowserOption, oidcClient)
if err != nil {
return nil, xerrors.Errorf("authcode-browser error: %w", err)
return nil, fmt.Errorf("authcode-browser error: %w", err)
}
return &Output{TokenSet: *tokenSet}, nil
}
if in.GrantOptionSet.AuthCodeKeyboardOption != nil {
tokenSet, err := u.AuthCodeKeyboard.Do(ctx, in.GrantOptionSet.AuthCodeKeyboardOption, client)
tokenSet, err := u.AuthCodeKeyboard.Do(ctx, in.GrantOptionSet.AuthCodeKeyboardOption, oidcClient)
if err != nil {
return nil, xerrors.Errorf("authcode-keyboard error: %w", err)
return nil, fmt.Errorf("authcode-keyboard error: %w", err)
}
return &Output{TokenSet: *tokenSet}, nil
}
if in.GrantOptionSet.ROPCOption != nil {
tokenSet, err := u.ROPC.Do(ctx, in.GrantOptionSet.ROPCOption, client)
tokenSet, err := u.ROPC.Do(ctx, in.GrantOptionSet.ROPCOption, oidcClient)
if err != nil {
return nil, xerrors.Errorf("ropc error: %w", err)
return nil, fmt.Errorf("ropc error: %w", err)
}
return &Output{TokenSet: *tokenSet}, nil
}
return nil, xerrors.Errorf("any authorization grant must be set")
return nil, fmt.Errorf("any authorization grant must be set")
}

View File

@@ -2,21 +2,21 @@ package authentication
import (
"context"
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client"
"github.com/int128/kubelogin/pkg/oidc/client/mock_client"
"github.com/int128/kubelogin/pkg/testing/clock"
testingJWT "github.com/int128/kubelogin/pkg/testing/jwt"
testingLogger "github.com/int128/kubelogin/pkg/testing/logger"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
"golang.org/x/xerrors"
)
func TestAuthentication_Do(t *testing.T) {
@@ -80,21 +80,21 @@ func TestAuthentication_Do(t *testing.T) {
RefreshToken: "VALID_REFRESH_TOKEN",
},
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().
Refresh(ctx, "VALID_REFRESH_TOKEN").
Return(&oidc.TokenSet{
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
}, nil)
mockOIDCClientFactory := mock_oidcclient.NewMockFactoryInterface(ctrl)
mockOIDCClientFactory.EXPECT().
mockClientFactory := mock_client.NewMockFactoryInterface(ctrl)
mockClientFactory.EXPECT().
New(ctx, dummyProvider, dummyTLSClientConfig).
Return(mockOIDCClient, nil)
Return(mockClient, nil)
u := Authentication{
OIDCClient: mockOIDCClientFactory,
Logger: testingLogger.New(t),
Clock: clock.Fake(expiryTime.Add(+time.Hour)),
ClientFactory: mockClientFactory,
Logger: testingLogger.New(t),
Clock: clock.Fake(expiryTime.Add(+time.Hour)),
}
got, err := u.Do(ctx, in)
if err != nil {
@@ -131,28 +131,28 @@ func TestAuthentication_Do(t *testing.T) {
RefreshToken: "EXPIRED_REFRESH_TOKEN",
},
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().SupportedPKCEMethods()
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().SupportedPKCEMethods()
mockClient.EXPECT().
Refresh(ctx, "EXPIRED_REFRESH_TOKEN").
Return(nil, xerrors.New("token has expired"))
mockOIDCClient.EXPECT().
Return(nil, errors.New("token has expired"))
mockClient.EXPECT().
GetTokenByAuthCode(gomock.Any(), gomock.Any(), gomock.Any()).
Do(func(_ context.Context, _ oidcclient.GetTokenByAuthCodeInput, readyChan chan<- string) {
Do(func(_ context.Context, _ client.GetTokenByAuthCodeInput, readyChan chan<- string) {
readyChan <- "LOCAL_SERVER_URL"
}).
Return(&oidc.TokenSet{
IDToken: "NEW_ID_TOKEN",
RefreshToken: "NEW_REFRESH_TOKEN",
}, nil)
mockOIDCClientFactory := mock_oidcclient.NewMockFactoryInterface(ctrl)
mockOIDCClientFactory.EXPECT().
mockClientFactory := mock_client.NewMockFactoryInterface(ctrl)
mockClientFactory.EXPECT().
New(ctx, dummyProvider, dummyTLSClientConfig).
Return(mockOIDCClient, nil)
Return(mockClient, nil)
u := Authentication{
OIDCClient: mockOIDCClientFactory,
Logger: testingLogger.New(t),
Clock: clock.Fake(expiryTime.Add(+time.Hour)),
ClientFactory: mockClientFactory,
Logger: testingLogger.New(t),
Clock: clock.Fake(expiryTime.Add(+time.Hour)),
AuthCodeBrowser: &authcode.Browser{
Logger: testingLogger.New(t),
},
@@ -187,20 +187,20 @@ func TestAuthentication_Do(t *testing.T) {
},
},
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidc.TokenSet{
IDToken: "YOUR_ID_TOKEN",
RefreshToken: "YOUR_REFRESH_TOKEN",
}, nil)
mockOIDCClientFactory := mock_oidcclient.NewMockFactoryInterface(ctrl)
mockOIDCClientFactory.EXPECT().
mockClientFactory := mock_client.NewMockFactoryInterface(ctrl)
mockClientFactory.EXPECT().
New(ctx, dummyProvider, dummyTLSClientConfig).
Return(mockOIDCClient, nil)
Return(mockClient, nil)
u := Authentication{
OIDCClient: mockOIDCClientFactory,
Logger: testingLogger.New(t),
ClientFactory: mockClientFactory,
Logger: testingLogger.New(t),
ROPC: &ropc.ROPC{
Logger: testingLogger.New(t),
},

View File

@@ -11,30 +11,30 @@ import (
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// Do mocks base method.
// Do mocks base method
func (m *MockInterface) Do(arg0 context.Context, arg1 authentication.Input) (*authentication.Output, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Do", arg0, arg1)
@@ -43,7 +43,7 @@ func (m *MockInterface) Do(arg0 context.Context, arg1 authentication.Input) (*au
return ret0, ret1
}
// Do indicates an expected call of Do.
// Do indicates an expected call of Do
func (mr *MockInterfaceMockRecorder) Do(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Do", reflect.TypeOf((*MockInterface)(nil).Do), arg0, arg1)

View File

@@ -2,12 +2,12 @@ package ropc
import (
"context"
"fmt"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/reader"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/infrastructure/reader"
"github.com/int128/kubelogin/pkg/oidc"
"golang.org/x/xerrors"
"github.com/int128/kubelogin/pkg/oidc/client"
)
const usernamePrompt = "Username: "
@@ -24,25 +24,25 @@ type ROPC struct {
Logger logger.Interface
}
func (u *ROPC) Do(ctx context.Context, in *Option, client oidcclient.Interface) (*oidc.TokenSet, error) {
func (u *ROPC) Do(ctx context.Context, in *Option, oidcClient client.Interface) (*oidc.TokenSet, error) {
u.Logger.V(1).Infof("starting the resource owner password credentials flow")
if in.Username == "" {
var err error
in.Username, err = u.Reader.ReadString(usernamePrompt)
if err != nil {
return nil, xerrors.Errorf("could not read a username: %w", err)
return nil, fmt.Errorf("could not read a username: %w", err)
}
}
if in.Password == "" {
var err error
in.Password, err = u.Reader.ReadPassword(passwordPrompt)
if err != nil {
return nil, xerrors.Errorf("could not read a password: %w", err)
return nil, fmt.Errorf("could not read a password: %w", err)
}
}
tokenSet, err := client.GetTokenByROPC(ctx, in.Username, in.Password)
tokenSet, err := oidcClient.GetTokenByROPC(ctx, in.Username, in.Password)
if err != nil {
return nil, xerrors.Errorf("resource owner password credentials flow error: %w", err)
return nil, fmt.Errorf("resource owner password credentials flow error: %w", err)
}
u.Logger.V(1).Infof("finished the resource owner password credentials flow")
return tokenSet, nil

View File

@@ -2,16 +2,16 @@ package ropc
import (
"context"
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/int128/kubelogin/pkg/adaptors/oidcclient/mock_oidcclient"
"github.com/int128/kubelogin/pkg/adaptors/reader/mock_reader"
"github.com/int128/kubelogin/pkg/infrastructure/reader/mock_reader"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/oidc/client/mock_client"
"github.com/int128/kubelogin/pkg/testing/logger"
"golang.org/x/xerrors"
)
func TestROPC_Do(t *testing.T) {
@@ -23,8 +23,8 @@ func TestROPC_Do(t *testing.T) {
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
defer cancel()
o := &Option{}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidc.TokenSet{
IDToken: "YOUR_ID_TOKEN",
@@ -37,7 +37,7 @@ func TestROPC_Do(t *testing.T) {
Reader: mockReader,
Logger: logger.New(t),
}
got, err := u.Do(ctx, o, mockOIDCClient)
got, err := u.Do(ctx, o, mockClient)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}
@@ -59,8 +59,8 @@ func TestROPC_Do(t *testing.T) {
Username: "USER",
Password: "PASS",
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidc.TokenSet{
IDToken: "YOUR_ID_TOKEN",
@@ -69,7 +69,7 @@ func TestROPC_Do(t *testing.T) {
u := ROPC{
Logger: logger.New(t),
}
got, err := u.Do(ctx, o, mockOIDCClient)
got, err := u.Do(ctx, o, mockClient)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}
@@ -90,8 +90,8 @@ func TestROPC_Do(t *testing.T) {
o := &Option{
Username: "USER",
}
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
mockOIDCClient.EXPECT().
mockClient := mock_client.NewMockInterface(ctrl)
mockClient.EXPECT().
GetTokenByROPC(gomock.Any(), "USER", "PASS").
Return(&oidc.TokenSet{
IDToken: "YOUR_ID_TOKEN",
@@ -103,7 +103,7 @@ func TestROPC_Do(t *testing.T) {
Reader: mockEnv,
Logger: logger.New(t),
}
got, err := u.Do(ctx, o, mockOIDCClient)
got, err := u.Do(ctx, o, mockClient)
if err != nil {
t.Errorf("Do returned error: %+v", err)
}
@@ -125,12 +125,12 @@ func TestROPC_Do(t *testing.T) {
Username: "USER",
}
mockEnv := mock_reader.NewMockInterface(ctrl)
mockEnv.EXPECT().ReadPassword(passwordPrompt).Return("", xerrors.New("error"))
mockEnv.EXPECT().ReadPassword(passwordPrompt).Return("", errors.New("error"))
u := ROPC{
Reader: mockEnv,
Logger: logger.New(t),
}
out, err := u.Do(ctx, o, mock_oidcclient.NewMockInterface(ctrl))
out, err := u.Do(ctx, o, mock_client.NewMockInterface(ctrl))
if err == nil {
t.Errorf("err wants non-nil but nil")
}

View File

@@ -5,18 +5,21 @@ package credentialplugin
import (
"context"
"fmt"
"net"
"strings"
"github.com/int128/kubelogin/pkg/adaptors/mutex"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/adaptors/tokencache"
"github.com/int128/kubelogin/pkg/credentialplugin"
"github.com/int128/kubelogin/pkg/credentialplugin/writer"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/infrastructure/mutex"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/tokencache"
"github.com/int128/kubelogin/pkg/tokencache/repository"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"golang.org/x/xerrors"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
)
//go:generate mockgen -destination mock_credentialplugin/mock_credentialplugin.go github.com/int128/kubelogin/pkg/usecases/credentialplugin Interface
@@ -32,10 +35,7 @@ type Interface interface {
// Input represents an input DTO of the GetToken use-case.
type Input struct {
IssuerURL string
ClientID string
ClientSecret string
ExtraScopes []string // optional
Provider oidc.Provider
TokenCacheDir string
GrantOptionSet authentication.GrantOptionSet
TLSClientConfig tlsclientconfig.Config
@@ -43,8 +43,8 @@ type Input struct {
type GetToken struct {
Authentication authentication.Interface
TokenCacheRepository tokencache.Interface
Writer credentialpluginwriter.Interface
TokenCacheRepository repository.Interface
Writer writer.Interface
Mutex mutex.Interface
Logger logger.Interface
}
@@ -52,21 +52,29 @@ type GetToken struct {
func (u *GetToken) Do(ctx context.Context, in Input) error {
u.Logger.V(1).Infof("WARNING: log may contain your secrets such as token or password")
// Prevent multiple concurrent token query using a file mutex. See https://github.com/int128/kubelogin/issues/389
lock, err := u.Mutex.Acquire(ctx, "get-token")
if err != nil {
return err
// Prevent multiple concurrent port binding using a file mutex.
// See https://github.com/int128/kubelogin/issues/389
bindPorts := extractBindAddressPorts(in.GrantOptionSet.AuthCodeBrowserOption)
if bindPorts != nil {
key := fmt.Sprintf("get-token-%s", strings.Join(bindPorts, "-"))
u.Logger.V(1).Infof("acquiring a lock %s", key)
lock, err := u.Mutex.Acquire(ctx, key)
if err != nil {
return fmt.Errorf("could not acquire a lock: %w", err)
}
defer func() {
if err := u.Mutex.Release(lock); err != nil {
u.Logger.V(1).Infof("could not release the lock: %s", err)
}
}()
}
defer func() {
_ = u.Mutex.Release(lock)
}()
u.Logger.V(1).Infof("finding a token from cache directory %s", in.TokenCacheDir)
tokenCacheKey := tokencache.Key{
IssuerURL: in.IssuerURL,
ClientID: in.ClientID,
ClientSecret: in.ClientSecret,
ExtraScopes: in.ExtraScopes,
IssuerURL: in.Provider.IssuerURL,
ClientID: in.Provider.ClientID,
ClientSecret: in.Provider.ClientSecret,
ExtraScopes: in.Provider.ExtraScopes,
CACertFilename: strings.Join(in.TLSClientConfig.CACertFilename, ","),
CACertData: strings.Join(in.TLSClientConfig.CACertData, ","),
SkipTLSVerify: in.TLSClientConfig.SkipTLSVerify,
@@ -80,23 +88,18 @@ func (u *GetToken) Do(ctx context.Context, in Input) error {
}
authenticationInput := authentication.Input{
Provider: oidc.Provider{
IssuerURL: in.IssuerURL,
ClientID: in.ClientID,
ClientSecret: in.ClientSecret,
ExtraScopes: in.ExtraScopes,
},
Provider: in.Provider,
GrantOptionSet: in.GrantOptionSet,
CachedTokenSet: cachedTokenSet,
TLSClientConfig: in.TLSClientConfig,
}
authenticationOutput, err := u.Authentication.Do(ctx, authenticationInput)
if err != nil {
return xerrors.Errorf("authentication error: %w", err)
return fmt.Errorf("authentication error: %w", err)
}
idTokenClaims, err := authenticationOutput.TokenSet.DecodeWithoutVerify()
if err != nil {
return xerrors.Errorf("you got an invalid token: %w", err)
return fmt.Errorf("you got an invalid token: %w", err)
}
u.Logger.V(1).Infof("you got a token: %s", idTokenClaims.Pretty)
@@ -105,16 +108,34 @@ func (u *GetToken) Do(ctx context.Context, in Input) error {
} else {
u.Logger.V(1).Infof("you got a valid token until %s", idTokenClaims.Expiry)
if err := u.TokenCacheRepository.Save(in.TokenCacheDir, tokenCacheKey, authenticationOutput.TokenSet); err != nil {
return xerrors.Errorf("could not write the token cache: %w", err)
return fmt.Errorf("could not write the token cache: %w", err)
}
}
u.Logger.V(1).Infof("writing the token to client-go")
out := credentialpluginwriter.Output{
out := credentialplugin.Output{
Token: authenticationOutput.TokenSet.IDToken,
Expiry: idTokenClaims.Expiry,
}
if err := u.Writer.Write(out); err != nil {
return xerrors.Errorf("could not write the token to client-go: %w", err)
return fmt.Errorf("could not write the token to client-go: %w", err)
}
return nil
}
func extractBindAddressPorts(o *authcode.BrowserOption) []string {
if o == nil {
return nil
}
var ports []string
for _, addr := range o.BindAddress {
_, port, err := net.SplitHostPort(addr)
if err != nil {
return nil // invalid address
}
if port == "0" {
return nil // any port
}
ports = append(ports, port)
}
return ports
}

View File

@@ -2,82 +2,87 @@ package credentialplugin
import (
"context"
"errors"
"testing"
"time"
"github.com/int128/kubelogin/pkg/adaptors/mutex"
"github.com/int128/kubelogin/pkg/adaptors/mutex/mock_mutex"
"github.com/int128/kubelogin/pkg/credentialplugin"
"github.com/int128/kubelogin/pkg/infrastructure/mutex"
"github.com/int128/kubelogin/pkg/infrastructure/mutex/mock_mutex"
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter"
"github.com/int128/kubelogin/pkg/adaptors/credentialpluginwriter/mock_credentialpluginwriter"
"github.com/int128/kubelogin/pkg/adaptors/tokencache"
"github.com/int128/kubelogin/pkg/adaptors/tokencache/mock_tokencache"
"github.com/int128/kubelogin/pkg/credentialplugin/writer/mock_writer"
"github.com/int128/kubelogin/pkg/oidc"
testingJWT "github.com/int128/kubelogin/pkg/testing/jwt"
"github.com/int128/kubelogin/pkg/testing/logger"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/tokencache"
"github.com/int128/kubelogin/pkg/tokencache/repository/mock_repository"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/mock_authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
"golang.org/x/xerrors"
)
func TestGetToken_Do(t *testing.T) {
dummyProvider := oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
}
issuedIDTokenExpiration := time.Now().Add(1 * time.Hour).Round(time.Second)
issuedIDToken := testingJWT.EncodeF(t, func(claims *testingJWT.Claims) {
claims.Issuer = "https://accounts.google.com"
claims.Subject = "YOUR_SUBJECT"
claims.ExpiresAt = issuedIDTokenExpiration.Unix()
})
issuedTokenSet := oidc.TokenSet{
IDToken: issuedIDToken,
RefreshToken: "YOUR_REFRESH_TOKEN",
}
issuedOutput := credentialplugin.Output{
Token: issuedIDToken,
Expiry: issuedIDTokenExpiration,
}
grantOptionSet := authentication.GrantOptionSet{
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:0"},
},
}
t.Run("LeastOptions", func(t *testing.T) {
var grantOptionSet authentication.GrantOptionSet
tokenSet := oidc.TokenSet{
IDToken: issuedIDToken,
RefreshToken: "YOUR_REFRESH_TOKEN",
}
t.Run("NoTokenCache", func(t *testing.T) {
tokenCacheKey := tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
in := Input{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
Provider: dummyProvider,
TokenCacheDir: "/path/to/token-cache",
GrantOptionSet: grantOptionSet,
}
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
mockAuthentication.EXPECT().
Do(ctx, authentication.Input{
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
},
Provider: dummyProvider,
GrantOptionSet: grantOptionSet,
}).
Return(&authentication.Output{TokenSet: tokenSet}, nil)
tokenCacheRepository := mock_tokencache.NewMockInterface(ctrl)
tokenCacheRepository.EXPECT().
Return(&authentication.Output{TokenSet: issuedTokenSet}, nil)
mockRepository := mock_repository.NewMockInterface(ctrl)
mockRepository.EXPECT().
FindByKey("/path/to/token-cache", tokenCacheKey).
Return(nil, xerrors.New("file not found"))
tokenCacheRepository.EXPECT().
Save("/path/to/token-cache", tokenCacheKey, tokenSet)
credentialPluginWriter := mock_credentialpluginwriter.NewMockInterface(ctrl)
credentialPluginWriter.EXPECT().
Write(credentialpluginwriter.Output{
Token: issuedIDToken,
Expiry: issuedIDTokenExpiration,
})
Return(nil, errors.New("file not found"))
mockRepository.EXPECT().
Save("/path/to/token-cache", tokenCacheKey, issuedTokenSet)
mockWriter := mock_writer.NewMockInterface(ctrl)
mockWriter.EXPECT().Write(issuedOutput)
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: tokenCacheRepository,
Writer: credentialPluginWriter,
Mutex: setupMutexMock(ctrl),
TokenCacheRepository: mockRepository,
Writer: mockWriter,
Mutex: mock_mutex.NewMockInterface(ctrl),
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err != nil {
@@ -85,69 +90,98 @@ func TestGetToken_Do(t *testing.T) {
}
})
t.Run("FullOptions", func(t *testing.T) {
t.Run("NeedBindPortMutex", func(t *testing.T) {
grantOptionSet := authentication.GrantOptionSet{
ROPCOption: &ropc.Option{Username: "YOUR_USERNAME"},
}
tokenSet := oidc.TokenSet{
IDToken: issuedIDToken,
RefreshToken: "YOUR_REFRESH_TOKEN",
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: []string{"127.0.0.1:8080"},
},
}
tokenCacheKey := tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Username: "YOUR_USERNAME",
CACertFilename: "/path/to/cert",
CACertData: "BASE64ENCODED",
SkipTLSVerify: true,
}
tlsClientConfig := tlsclientconfig.Config{
CACertFilename: []string{"/path/to/cert"},
CACertData: []string{"BASE64ENCODED"},
SkipTLSVerify: true,
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
in := Input{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
TokenCacheDir: "/path/to/token-cache",
GrantOptionSet: grantOptionSet,
TLSClientConfig: tlsClientConfig,
Provider: dummyProvider,
TokenCacheDir: "/path/to/token-cache",
GrantOptionSet: grantOptionSet,
}
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
mockAuthentication.EXPECT().
Do(ctx, authentication.Input{
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
GrantOptionSet: grantOptionSet,
TLSClientConfig: tlsClientConfig,
Provider: dummyProvider,
GrantOptionSet: grantOptionSet,
}).
Return(&authentication.Output{TokenSet: tokenSet}, nil)
tokenCacheRepository := mock_tokencache.NewMockInterface(ctrl)
tokenCacheRepository.EXPECT().
Return(&authentication.Output{TokenSet: issuedTokenSet}, nil)
mockRepository := mock_repository.NewMockInterface(ctrl)
mockRepository.EXPECT().
FindByKey("/path/to/token-cache", tokenCacheKey).
Return(nil, xerrors.New("file not found"))
tokenCacheRepository.EXPECT().
Save("/path/to/token-cache", tokenCacheKey, tokenSet)
credentialPluginWriter := mock_credentialpluginwriter.NewMockInterface(ctrl)
credentialPluginWriter.EXPECT().
Write(credentialpluginwriter.Output{
Token: issuedIDToken,
Expiry: issuedIDTokenExpiration,
})
Return(nil, errors.New("file not found"))
mockRepository.EXPECT().
Save("/path/to/token-cache", tokenCacheKey, issuedTokenSet)
mockWriter := mock_writer.NewMockInterface(ctrl)
mockWriter.EXPECT().Write(issuedOutput)
mockMutex := mock_mutex.NewMockInterface(ctrl)
mockMutex.EXPECT().
Acquire(ctx, "get-token-8080").
Return(&mutex.Lock{Data: "testData"}, nil)
mockMutex.EXPECT().
Release(&mutex.Lock{Data: "testData"})
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: tokenCacheRepository,
Writer: credentialPluginWriter,
Mutex: setupMutexMock(ctrl),
TokenCacheRepository: mockRepository,
Writer: mockWriter,
Mutex: mockMutex,
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err != nil {
t.Errorf("Do returned error: %+v", err)
}
})
t.Run("ROPC", func(t *testing.T) {
grantOptionSet := authentication.GrantOptionSet{
ROPCOption: &ropc.Option{Username: "YOUR_USERNAME"},
}
tokenCacheKey := tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Username: "YOUR_USERNAME",
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := context.TODO()
in := Input{
Provider: dummyProvider,
TokenCacheDir: "/path/to/token-cache",
GrantOptionSet: grantOptionSet,
}
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
mockAuthentication.EXPECT().
Do(ctx, authentication.Input{
Provider: dummyProvider,
GrantOptionSet: grantOptionSet,
}).
Return(&authentication.Output{TokenSet: issuedTokenSet}, nil)
mockRepository := mock_repository.NewMockInterface(ctrl)
mockRepository.EXPECT().
FindByKey("/path/to/token-cache", tokenCacheKey).
Return(nil, errors.New("file not found"))
mockRepository.EXPECT().
Save("/path/to/token-cache", tokenCacheKey, issuedTokenSet)
mockWriter := mock_writer.NewMockInterface(ctrl)
mockWriter.EXPECT().Write(issuedOutput)
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: mockRepository,
Writer: mockWriter,
Mutex: mock_mutex.NewMockInterface(ctrl),
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err != nil {
@@ -160,50 +194,36 @@ func TestGetToken_Do(t *testing.T) {
defer ctrl.Finish()
ctx := context.TODO()
in := Input{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
TokenCacheDir: "/path/to/token-cache",
Provider: dummyProvider,
TokenCacheDir: "/path/to/token-cache",
GrantOptionSet: grantOptionSet,
}
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
mockAuthentication.EXPECT().
Do(ctx, authentication.Input{
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
CachedTokenSet: &oidc.TokenSet{
IDToken: issuedIDToken,
},
Provider: dummyProvider,
CachedTokenSet: &issuedTokenSet,
GrantOptionSet: grantOptionSet,
}).
Return(&authentication.Output{
AlreadyHasValidIDToken: true,
TokenSet: oidc.TokenSet{
IDToken: issuedIDToken,
},
TokenSet: issuedTokenSet,
}, nil)
tokenCacheRepository := mock_tokencache.NewMockInterface(ctrl)
tokenCacheRepository.EXPECT().
mockRepository := mock_repository.NewMockInterface(ctrl)
mockRepository.EXPECT().
FindByKey("/path/to/token-cache", tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
}).
Return(&oidc.TokenSet{
IDToken: issuedIDToken,
}, nil)
credentialPluginWriter := mock_credentialpluginwriter.NewMockInterface(ctrl)
credentialPluginWriter.EXPECT().
Write(credentialpluginwriter.Output{
Token: issuedIDToken,
Expiry: issuedIDTokenExpiration,
})
Return(&issuedTokenSet, nil)
mockWriter := mock_writer.NewMockInterface(ctrl)
mockWriter.EXPECT().Write(issuedOutput)
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: tokenCacheRepository,
Writer: credentialPluginWriter,
Mutex: setupMutexMock(ctrl),
TokenCacheRepository: mockRepository,
Writer: mockWriter,
Mutex: mock_mutex.NewMockInterface(ctrl),
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err != nil {
@@ -216,34 +236,30 @@ func TestGetToken_Do(t *testing.T) {
defer ctrl.Finish()
ctx := context.TODO()
in := Input{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
TokenCacheDir: "/path/to/token-cache",
Provider: dummyProvider,
TokenCacheDir: "/path/to/token-cache",
GrantOptionSet: grantOptionSet,
}
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
mockAuthentication.EXPECT().
Do(ctx, authentication.Input{
Provider: oidc.Provider{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
},
Provider: dummyProvider,
GrantOptionSet: grantOptionSet,
}).
Return(nil, xerrors.New("authentication error"))
tokenCacheRepository := mock_tokencache.NewMockInterface(ctrl)
tokenCacheRepository.EXPECT().
Return(nil, errors.New("authentication error"))
mockRepository := mock_repository.NewMockInterface(ctrl)
mockRepository.EXPECT().
FindByKey("/path/to/token-cache", tokencache.Key{
IssuerURL: "https://accounts.google.com",
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
}).
Return(nil, xerrors.New("file not found"))
Return(nil, errors.New("file not found"))
u := GetToken{
Authentication: mockAuthentication,
TokenCacheRepository: tokenCacheRepository,
Writer: mock_credentialpluginwriter.NewMockInterface(ctrl),
Mutex: setupMutexMock(ctrl),
TokenCacheRepository: mockRepository,
Writer: mock_writer.NewMockInterface(ctrl),
Mutex: mock_mutex.NewMockInterface(ctrl),
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err == nil {
@@ -251,12 +267,3 @@ func TestGetToken_Do(t *testing.T) {
}
})
}
// Setup a mock that expect the mutex to be lock and unlock
func setupMutexMock(ctrl *gomock.Controller) *mock_mutex.MockInterface {
mockMutex := mock_mutex.NewMockInterface(ctrl)
lockValue := &mutex.Lock{Data: "testData"}
acquireCall := mockMutex.EXPECT().Acquire(gomock.Not(gomock.Nil()), "get-token").Return(lockValue, nil)
mockMutex.EXPECT().Release(lockValue).Return(nil).After(acquireCall)
return mockMutex
}

View File

@@ -5,7 +5,7 @@ import (
"context"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/usecases/authentication"
)

View File

@@ -2,6 +2,7 @@ package setup
import (
"context"
"fmt"
"path/filepath"
"strings"
"text/template"
@@ -9,7 +10,6 @@ import (
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"golang.org/x/xerrors"
)
var stage2Tpl = template.Must(template.New("").Parse(`
@@ -73,6 +73,7 @@ type Stage2Input struct {
ClientID string
ClientSecret string
ExtraScopes []string // optional
UsePKCE bool // optional
ListenAddressArgs []string // non-nil if set by the command arg
GrantOptionSet authentication.GrantOptionSet
TLSClientConfig tlsclientconfig.Config
@@ -86,16 +87,17 @@ func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error {
ClientID: in.ClientID,
ClientSecret: in.ClientSecret,
ExtraScopes: in.ExtraScopes,
UsePKCE: in.UsePKCE,
},
GrantOptionSet: in.GrantOptionSet,
TLSClientConfig: in.TLSClientConfig,
})
if err != nil {
return xerrors.Errorf("authentication error: %w", err)
return fmt.Errorf("authentication error: %w", err)
}
idTokenClaims, err := out.TokenSet.DecodeWithoutVerify()
if err != nil {
return xerrors.Errorf("you got an invalid token: %w", err)
return fmt.Errorf("you got an invalid token: %w", err)
}
v := stage2Vars{
@@ -107,7 +109,7 @@ func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error {
}
var b strings.Builder
if err := stage2Tpl.Execute(&b, &v); err != nil {
return xerrors.Errorf("could not render the template: %w", err)
return fmt.Errorf("could not render the template: %w", err)
}
u.Logger.Printf(b.String())
return nil
@@ -123,6 +125,9 @@ func makeCredentialPluginArgs(in Stage2Input) []string {
for _, extraScope := range in.ExtraScopes {
args = append(args, "--oidc-extra-scope="+extraScope)
}
if in.UsePKCE {
args = append(args, "--oidc-use-pkce")
}
for _, f := range in.TLSClientConfig.CACertFilename {
args = append(args, "--certificate-authority="+f)
}
@@ -137,6 +142,9 @@ func makeCredentialPluginArgs(in Stage2Input) []string {
if in.GrantOptionSet.AuthCodeBrowserOption.SkipOpenBrowser {
args = append(args, "--skip-open-browser")
}
if in.GrantOptionSet.AuthCodeBrowserOption.BrowserCommand != "" {
args = append(args, "--browser-command="+in.GrantOptionSet.AuthCodeBrowserOption.BrowserCommand)
}
if in.GrantOptionSet.AuthCodeBrowserOption.LocalServerCertFile != "" {
// Resolve the absolute path for the cert files so the user doesn't have to know
// to use one when running setup.

View File

@@ -11,30 +11,30 @@ import (
reflect "reflect"
)
// MockInterface is a mock of Interface interface.
// MockInterface is a mock of Interface interface
type MockInterface struct {
ctrl *gomock.Controller
recorder *MockInterfaceMockRecorder
}
// MockInterfaceMockRecorder is the mock recorder for MockInterface.
// MockInterfaceMockRecorder is the mock recorder for MockInterface
type MockInterfaceMockRecorder struct {
mock *MockInterface
}
// NewMockInterface creates a new mock instance.
// NewMockInterface creates a new mock instance
func NewMockInterface(ctrl *gomock.Controller) *MockInterface {
mock := &MockInterface{ctrl: ctrl}
mock.recorder = &MockInterfaceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder {
return m.recorder
}
// Do mocks base method.
// Do mocks base method
func (m *MockInterface) Do(arg0 context.Context, arg1 standalone.Input) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Do", arg0, arg1)
@@ -42,7 +42,7 @@ func (m *MockInterface) Do(arg0 context.Context, arg1 standalone.Input) error {
return ret0
}
// Do indicates an expected call of Do.
// Do indicates an expected call of Do
func (mr *MockInterfaceMockRecorder) Do(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Do", reflect.TypeOf((*MockInterface)(nil).Do), arg0, arg1)

View File

@@ -2,14 +2,16 @@ package standalone
import (
"context"
"fmt"
"github.com/google/wire"
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig"
"github.com/int128/kubelogin/pkg/adaptors/logger"
"github.com/int128/kubelogin/pkg/infrastructure/logger"
"github.com/int128/kubelogin/pkg/kubeconfig"
"github.com/int128/kubelogin/pkg/kubeconfig/loader"
"github.com/int128/kubelogin/pkg/kubeconfig/writer"
"github.com/int128/kubelogin/pkg/oidc"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"golang.org/x/xerrors"
)
//go:generate mockgen -destination mock_standalone/mock_standalone.go github.com/int128/kubelogin/pkg/usecases/standalone Interface
@@ -42,11 +44,6 @@ To show the setup instruction:
See https://github.com/int128/kubelogin for more.
`
const deprecationMessage = `NOTE: You can use the credential plugin mode for better user experience.
Kubectl automatically runs kubelogin and you do not need to run kubelogin explicitly.
See https://github.com/int128/kubelogin for more.
`
// Standalone provides the use case of explicit login.
//
// If the current auth provider is not oidc, show the error.
@@ -54,20 +51,20 @@ See https://github.com/int128/kubelogin for more.
// Otherwise, update the kubeconfig.
//
type Standalone struct {
Authentication authentication.Interface
Kubeconfig kubeconfig.Interface
Logger logger.Interface
Authentication authentication.Interface
KubeconfigLoader loader.Interface
KubeconfigWriter writer.Interface
Logger logger.Interface
}
func (u *Standalone) Do(ctx context.Context, in Input) error {
u.Logger.V(1).Infof("WARNING: log may contain your secrets such as token or password")
authProvider, err := u.Kubeconfig.GetCurrentAuthProvider(in.KubeconfigFilename, in.KubeconfigContext, in.KubeconfigUser)
authProvider, err := u.KubeconfigLoader.GetCurrentAuthProvider(in.KubeconfigFilename, in.KubeconfigContext, in.KubeconfigUser)
if err != nil {
u.Logger.Printf(oidcConfigErrorMessage)
return xerrors.Errorf("could not find the current authentication provider: %w", err)
return fmt.Errorf("could not find the current authentication provider: %w", err)
}
u.Logger.Printf(deprecationMessage)
u.Logger.V(1).Infof("using the authentication provider of the user %s", authProvider.UserName)
u.Logger.V(1).Infof("a token will be written to %s", authProvider.LocationOfOrigin)
if authProvider.IDPCertificateAuthority != "" {
@@ -99,12 +96,12 @@ func (u *Standalone) Do(ctx context.Context, in Input) error {
}
authenticationOutput, err := u.Authentication.Do(ctx, authenticationInput)
if err != nil {
return xerrors.Errorf("authentication error: %w", err)
return fmt.Errorf("authentication error: %w", err)
}
idTokenClaims, err := authenticationOutput.TokenSet.DecodeWithoutVerify()
if err != nil {
return xerrors.Errorf("you got an invalid token: %w", err)
return fmt.Errorf("you got an invalid token: %w", err)
}
u.Logger.V(1).Infof("you got a token: %s", idTokenClaims.Pretty)
if authenticationOutput.AlreadyHasValidIDToken {
@@ -116,8 +113,8 @@ func (u *Standalone) Do(ctx context.Context, in Input) error {
authProvider.IDToken = authenticationOutput.TokenSet.IDToken
authProvider.RefreshToken = authenticationOutput.TokenSet.RefreshToken
u.Logger.V(1).Infof("writing the ID token and refresh token to %s", authProvider.LocationOfOrigin)
if err := u.Kubeconfig.UpdateAuthProvider(authProvider); err != nil {
return xerrors.Errorf("could not update the kubeconfig: %w", err)
if err := u.KubeconfigWriter.UpdateAuthProvider(*authProvider); err != nil {
return fmt.Errorf("could not update the kubeconfig: %w", err)
}
return nil
}

View File

@@ -2,19 +2,20 @@ package standalone
import (
"context"
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig"
"github.com/int128/kubelogin/pkg/adaptors/kubeconfig/mock_kubeconfig"
"github.com/int128/kubelogin/pkg/kubeconfig"
"github.com/int128/kubelogin/pkg/kubeconfig/loader/mock_loader"
"github.com/int128/kubelogin/pkg/kubeconfig/writer/mock_writer"
"github.com/int128/kubelogin/pkg/oidc"
testingJWT "github.com/int128/kubelogin/pkg/testing/jwt"
"github.com/int128/kubelogin/pkg/testing/logger"
"github.com/int128/kubelogin/pkg/tlsclientconfig"
"github.com/int128/kubelogin/pkg/usecases/authentication"
"github.com/int128/kubelogin/pkg/usecases/authentication/mock_authentication"
"golang.org/x/xerrors"
)
func TestStandalone_Do(t *testing.T) {
@@ -45,12 +46,13 @@ func TestStandalone_Do(t *testing.T) {
IDPCertificateAuthority: "/path/to/cert2",
IDPCertificateAuthorityData: "BASE64ENCODED2",
}
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
mockKubeconfig.EXPECT().
mockLoader := mock_loader.NewMockInterface(ctrl)
mockLoader.EXPECT().
GetCurrentAuthProvider("/path/to/kubeconfig", kubeconfig.ContextName("theContext"), kubeconfig.UserName("theUser")).
Return(currentAuthProvider, nil)
mockKubeconfig.EXPECT().
UpdateAuthProvider(&kubeconfig.AuthProvider{
mockWriter := mock_writer.NewMockInterface(ctrl)
mockWriter.EXPECT().
UpdateAuthProvider(kubeconfig.AuthProvider{
LocationOfOrigin: "/path/to/kubeconfig",
UserName: "theUser",
IDPIssuerURL: "https://accounts.google.com",
@@ -82,9 +84,10 @@ func TestStandalone_Do(t *testing.T) {
},
}, nil)
u := Standalone{
Authentication: mockAuthentication,
Kubeconfig: mockKubeconfig,
Logger: logger.New(t),
Authentication: mockAuthentication,
KubeconfigLoader: mockLoader,
KubeconfigWriter: mockWriter,
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err != nil {
t.Errorf("Do returned error: %+v", err)
@@ -104,8 +107,8 @@ func TestStandalone_Do(t *testing.T) {
ClientSecret: "YOUR_CLIENT_SECRET",
IDToken: issuedIDToken,
}
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
mockKubeconfig.EXPECT().
mockLoader := mock_loader.NewMockInterface(ctrl)
mockLoader.EXPECT().
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
Return(currentAuthProvider, nil)
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
@@ -127,9 +130,9 @@ func TestStandalone_Do(t *testing.T) {
},
}, nil)
u := Standalone{
Authentication: mockAuthentication,
Kubeconfig: mockKubeconfig,
Logger: logger.New(t),
Authentication: mockAuthentication,
KubeconfigLoader: mockLoader,
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err != nil {
t.Errorf("Do returned error: %+v", err)
@@ -141,15 +144,15 @@ func TestStandalone_Do(t *testing.T) {
defer ctrl.Finish()
ctx := context.TODO()
in := Input{}
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
mockKubeconfig.EXPECT().
mockLoader := mock_loader.NewMockInterface(ctrl)
mockLoader.EXPECT().
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
Return(nil, xerrors.New("no oidc config"))
Return(nil, errors.New("no oidc config"))
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
u := Standalone{
Authentication: mockAuthentication,
Kubeconfig: mockKubeconfig,
Logger: logger.New(t),
Authentication: mockAuthentication,
KubeconfigLoader: mockLoader,
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err == nil {
t.Errorf("err wants non-nil but nil")
@@ -168,8 +171,8 @@ func TestStandalone_Do(t *testing.T) {
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
}
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
mockKubeconfig.EXPECT().
mockLoader := mock_loader.NewMockInterface(ctrl)
mockLoader.EXPECT().
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
Return(currentAuthProvider, nil)
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
@@ -181,11 +184,11 @@ func TestStandalone_Do(t *testing.T) {
ClientSecret: "YOUR_CLIENT_SECRET",
},
}).
Return(nil, xerrors.New("authentication error"))
Return(nil, errors.New("authentication error"))
u := Standalone{
Authentication: mockAuthentication,
Kubeconfig: mockKubeconfig,
Logger: logger.New(t),
Authentication: mockAuthentication,
KubeconfigLoader: mockLoader,
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err == nil {
t.Errorf("err wants non-nil but nil")
@@ -204,12 +207,13 @@ func TestStandalone_Do(t *testing.T) {
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
}
mockKubeconfig := mock_kubeconfig.NewMockInterface(ctrl)
mockKubeconfig.EXPECT().
mockLoader := mock_loader.NewMockInterface(ctrl)
mockLoader.EXPECT().
GetCurrentAuthProvider("", kubeconfig.ContextName(""), kubeconfig.UserName("")).
Return(currentAuthProvider, nil)
mockKubeconfig.EXPECT().
UpdateAuthProvider(&kubeconfig.AuthProvider{
mockWriter := mock_writer.NewMockInterface(ctrl)
mockWriter.EXPECT().
UpdateAuthProvider(kubeconfig.AuthProvider{
LocationOfOrigin: "/path/to/kubeconfig",
UserName: "google",
IDPIssuerURL: "https://accounts.google.com",
@@ -218,7 +222,7 @@ func TestStandalone_Do(t *testing.T) {
IDToken: issuedIDToken,
RefreshToken: "YOUR_REFRESH_TOKEN",
}).
Return(xerrors.New("I/O error"))
Return(errors.New("I/O error"))
mockAuthentication := mock_authentication.NewMockInterface(ctrl)
mockAuthentication.EXPECT().
Do(ctx, authentication.Input{
@@ -235,9 +239,10 @@ func TestStandalone_Do(t *testing.T) {
},
}, nil)
u := Standalone{
Authentication: mockAuthentication,
Kubeconfig: mockKubeconfig,
Logger: logger.New(t),
Authentication: mockAuthentication,
KubeconfigLoader: mockLoader,
KubeconfigWriter: mockWriter,
Logger: logger.New(t),
}
if err := u.Do(ctx, in); err == nil {
t.Errorf("err wants non-nil but nil")

View File

@@ -7,10 +7,6 @@ export PATH
KUBECONFIG := ../cluster/kubeconfig.yaml
export KUBECONFIG
# run the login script instead of opening chrome
BROWSER := $(BIN_DIR)/chromelogin
export BROWSER
.PHONY: test
test: build
# see the setup instruction
@@ -19,7 +15,8 @@ test: build
--oidc-client-id=YOUR_CLIENT_ID \
--oidc-client-secret=YOUR_CLIENT_SECRET \
--oidc-extra-scope=email \
--certificate-authority=$(CERT_DIR)/ca.crt
--certificate-authority=$(CERT_DIR)/ca.crt \
--browser-command=$(BIN_DIR)/chromelogin
# set up the kubeconfig
kubectl config set-credentials oidc \
--exec-api-version=client.authentication.k8s.io/v1beta1 \
@@ -30,7 +27,8 @@ test: build
--exec-arg=--oidc-client-id=YOUR_CLIENT_ID \
--exec-arg=--oidc-client-secret=YOUR_CLIENT_SECRET \
--exec-arg=--oidc-extra-scope=email \
--exec-arg=--certificate-authority=$(CERT_DIR)/ca.crt
--exec-arg=--certificate-authority=$(CERT_DIR)/ca.crt \
--exec-arg=--browser-command=$(BIN_DIR)/chromelogin
# make sure we can access the cluster
kubectl --user=oidc cluster-info
# switch the current context