MCS service account authentication with Mkube (#166)
`MCS` will authenticate against `Mkube`using bearer tokens via HTTP `Authorization` header. The user will provide this token once in the login form, MCS will validate it against Mkube (list tenants) and if valid will generate and return a new MCS sessions with encrypted claims (the user Service account token will be inside the JWT in the data field) Kubernetes The provided `JWT token` corresponds to the `Kubernetes service account` that `Mkube` will use to run tasks on behalf of the user, ie: list, create, edit, delete tenants, storage class, etc. Development If you are running mcs in your local environment and wish to make request to `Mkube` you can set `MCS_M3_HOSTNAME`, if the environment variable is not present by default `MCS` will use `"http://m3:8787"`, additionally you will need to set the `MCS_MKUBE_ADMIN_ONLY=on` variable to make MCS display the Mkube UI Extract the Service account token and use it with MCS For local development you can use the jwt associated to the `m3-sa` service account, you can get the token running the following command in your terminal: ``` kubectl get secret $(kubectl get serviceaccount m3-sa -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode ``` Then run the mcs server ``` MCS_M3_HOSTNAME=http://localhost:8787 MCS_MKUBE_ADMIN_ONLY=on ./mcs server ``` Self-signed certificates and Custom certificate authority for Mkube If Mkube uses TLS with a self-signed certificate, or a certificate issued by a custom certificate authority you can add those certificates usinng the `MCS_M3_SERVER_TLS_CA_CERTIFICATE` env variable ```` MCS_M3_SERVER_TLS_CA_CERTIFICATE=cert1.pem,cert2.pem,cert3.pem ./mcs server ````
This commit is contained in:
40
docs/mcs_service_account_mkube.md
Normal file
40
docs/mcs_service_account_mkube.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# MCS service account authentication with Mkube
|
||||
|
||||
`MCS` will authenticate against `Mkube`using bearer tokens via HTTP `Authorization` header. The user will provide this token once
|
||||
in the login form, MCS will validate it against Mkube (list tenants) and if valid will generate and return a new MCS sessions
|
||||
with encrypted claims (the user Service account token will be inside the JWT in the data field)
|
||||
|
||||
# Kubernetes
|
||||
|
||||
The provided `JWT token` corresponds to the `Kubernetes service account` that `Mkube` will use to run tasks on behalf of the
|
||||
user, ie: list, create, edit, delete tenants, storage class, etc.
|
||||
|
||||
# Development
|
||||
|
||||
If you are running mcs in your local environment and wish to make request to `Mkube` you can set `MCS_M3_HOSTNAME`, if
|
||||
the environment variable is not present by default `MCS` will use `"http://m3:8787"`, additionally you will need to set the
|
||||
`MCS_MKUBE_ADMIN_ONLY=on` variable to make MCS display the Mkube UI
|
||||
|
||||
## Extract the Service account token and use it with MCS
|
||||
|
||||
For local development you can use the jwt associated to the `m3-sa` service account, you can get the token running
|
||||
the following command in your terminal:
|
||||
|
||||
```
|
||||
kubectl get secret $(kubectl get serviceaccount m3-sa -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode
|
||||
```
|
||||
|
||||
Then run the mcs server
|
||||
|
||||
```
|
||||
MCS_M3_HOSTNAME=http://localhost:8787 MCS_MKUBE_ADMIN_ONLY=on ./mcs server
|
||||
```
|
||||
|
||||
# Self-signed certificates and Custom certificate authority for Mkube
|
||||
|
||||
If Mkube uses TLS with a self-signed certificate, or a certificate issued by a custom certificate authority you can add those
|
||||
certificates usinng the `MCS_M3_SERVER_TLS_CA_CERTIFICATE` env variable
|
||||
|
||||
````
|
||||
MCS_M3_SERVER_TLS_CA_CERTIFICATE=cert1.pem,cert2.pem,cert3.pem ./mcs server
|
||||
````
|
||||
4
go.mod
4
go.mod
@@ -18,8 +18,8 @@ require (
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/mc v0.0.0-20200515235434-3b479cf92ed6
|
||||
github.com/minio/minio v0.0.0-20200516011754-9cac385aecdb
|
||||
github.com/minio/minio-go/v6 v6.0.56-0.20200502013257-a81c8c13cc3f
|
||||
github.com/minio/minio v0.0.0-20200603201854-5686a7e27319
|
||||
github.com/minio/minio-go/v6 v6.0.56
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
|
||||
50
go.sum
50
go.sum
@@ -15,6 +15,7 @@ github.com/Azure/go-autorest v11.7.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -47,10 +48,14 @@ github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=
|
||||
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheggaaa/pb v1.0.28 h1:kWGpdAcSp3MxMU9CCHOwz/8V0kCHN4+9yQm2MzWuI98=
|
||||
@@ -60,6 +65,8 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
|
||||
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
|
||||
github.com/colinmarc/hdfs/v2 v2.1.1 h1:x0hw/m+o3UE20Scso/KCkvYNc9Di39TBlCfGMkJ1/a0=
|
||||
github.com/colinmarc/hdfs/v2 v2.1.1/go.mod h1:M3x+k8UKKmxtFu++uAZ0OtDU8jR3jnaZIAc6yK4Ue0c=
|
||||
github.com/coredns/coredns v1.4.0 h1:RubBkYmkByUqZWWkjRHvNLnUHgkRVqAWgSMmRFvpE1A=
|
||||
github.com/coredns/coredns v1.4.0/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
@@ -74,11 +81,13 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/djherbis/atime v1.0.0 h1:ySLvBAM0EvOGaX7TI4dAM5lWj+RdJUCKtGSEHN8SGBg=
|
||||
github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
@@ -116,6 +125,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g=
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
@@ -232,6 +242,8 @@ github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNu
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
@@ -291,6 +303,7 @@ github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+0
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
@@ -312,6 +325,7 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKe
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8=
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jcmturner/gofork v0.0.0-20180107083740-2aebee971930/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM=
|
||||
github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
@@ -359,6 +373,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kurin/blazer v0.5.4-0.20200327014341-8f90a40f8af7 h1:smZXPopqRVVywwzou4WYWvUbJvSAzIDFizfWElpmAqY=
|
||||
github.com/kurin/blazer v0.5.4-0.20200327014341-8f90a40f8af7/go.mod h1:4FCXMUWo9DllR2Do4TtBd377ezyAJ51vB5uTBjt0pGU=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
@@ -407,13 +423,14 @@ github.com/minio/lsync v1.0.1/go.mod h1:tCFzfo0dlvdGl70IT4IAK/5Wtgb0/BrTmo/jE8pA
|
||||
github.com/minio/mc v0.0.0-20200515235434-3b479cf92ed6 h1:2SrKe2vLDLwvnYkYrJelrzyGW8t/8HCbr9yDsw+8XSI=
|
||||
github.com/minio/mc v0.0.0-20200515235434-3b479cf92ed6/go.mod h1:U3Jgk0bcSjn+QPUMisrS6nxCWOoQ6rYWSvLCB30apuU=
|
||||
github.com/minio/minio v0.0.0-20200421050159-282c9f790a03/go.mod h1:zBua5AiljGs1Irdl2XEyiJjvZVCVDIG8gjozzRBcVlw=
|
||||
github.com/minio/minio v0.0.0-20200516011754-9cac385aecdb h1:CQC7D3UDnUycuxhwImcVhMSLet/RbShosAnYcvMtEB8=
|
||||
github.com/minio/minio v0.0.0-20200516011754-9cac385aecdb/go.mod h1:wymaytM/HELuwdz7BGZHmQ3XKq2SxPsLeGxyOCaCLiA=
|
||||
github.com/minio/minio v0.0.0-20200603201854-5686a7e27319 h1:Vh7ATrN/BDjUx9XBKg5fpqSn2LPV/9BKKdldu31+2HY=
|
||||
github.com/minio/minio v0.0.0-20200603201854-5686a7e27319/go.mod h1:hW3OqYRO05rdeeuHHTif+B7WYxtDB3/hZb7eKfHXy1Y=
|
||||
github.com/minio/minio-go/v6 v6.0.53 h1:8jzpwiOzZ5Iz7/goFWqNZRICbyWYShbb5rARjrnSCNI=
|
||||
github.com/minio/minio-go/v6 v6.0.53/go.mod h1:DIvC/IApeHX8q1BAMVCXSXwpmrmM+I+iBvhvztQorfI=
|
||||
github.com/minio/minio-go/v6 v6.0.55-0.20200425081427-89eebdef2af0/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI=
|
||||
github.com/minio/minio-go/v6 v6.0.56-0.20200502013257-a81c8c13cc3f h1:ifHrI8+exqLi5RztIWWKS5k+Wu+W7DJisVXwNaCH2zs=
|
||||
github.com/minio/minio-go/v6 v6.0.56-0.20200502013257-a81c8c13cc3f/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI=
|
||||
github.com/minio/minio-go/v6 v6.0.56 h1:H4+v6UFV1V7VkEf1HjL15W9OvTL1Gy8EbMmjQZHqEbg=
|
||||
github.com/minio/minio-go/v6 v6.0.56/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI=
|
||||
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61 h1:pUSI/WKPdd77gcuoJkSzhJ4wdS8OMDOsOu99MtpXEQA=
|
||||
github.com/minio/parquet-go v0.0.0-20200414234858-838cfa8aae61/go.mod h1:4trzEJ7N1nBTd5Tt7OCZT5SEin+WiAXpdJ/WgPkESA8=
|
||||
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||
@@ -470,9 +487,12 @@ github.com/nats-io/stan.go v0.4.5 h1:lPZ9y1jVGiXcTaUc1SnEIWPYfh0avuEiHBePNJYgpPk
|
||||
github.com/nats-io/stan.go v0.4.5/go.mod h1:Ji7mK6gRZJSH1nc3ZJH6vi7zn/QnZhpR9Arm4iuzsUQ=
|
||||
github.com/ncw/directio v1.0.5 h1:JSUBhdjEvVaJvOoyPAbcW0fnd0tvRXD76wEfZ1KcQz4=
|
||||
github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t6ROk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nsqio/go-nsq v1.0.7 h1:O0pIZJYTf+x7cZBA0UMY8WxFG79lYTURmWzAAh48ljY=
|
||||
github.com/nsqio/go-nsq v1.0.7/go.mod h1:XP5zaUs3pqf+Q71EqUJs3HYfBIqfK6G83WQMdNN+Ito=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@@ -503,17 +523,25 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190704165056-9c2d0518ed81 h1:zQTtDd7fQiF9e80lbl+ShnD9/5NSq5r1EhcS8955ECg=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190704165056-9c2d0518ed81/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
@@ -548,8 +576,11 @@ github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
@@ -627,6 +658,7 @@ go.uber.org/zap v1.11.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -739,8 +771,9 @@ golang.org/x/tools v0.0.0-20190914235951-31e00f45c22e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32 h1:Xvf3ZQTm5bjXPxhI7g+dwqsCqadK1rcNtwtszuatetk=
|
||||
golang.org/x/tools v0.0.0-20200428211428-0c9eba77bc32/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
|
||||
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
@@ -770,6 +803,8 @@ gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUy
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
@@ -778,6 +813,7 @@ gopkg.in/h2non/filetype.v1 v1.0.5/go.mod h1:M0yem4rwSX5lLVrkEuRRp2/NinFMD5vgJ4Dl
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.48.0 h1:URjZc+8ugRY5mL5uUeQH/a63JcHwdX9xZaWvmNWD7z8=
|
||||
gopkg.in/ini.v1 v1.48.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
|
||||
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
|
||||
@@ -788,6 +824,8 @@ gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXt
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.2.3 h1:hHMV/yKPwMnJhPuPx7pH2Uw/3Qyf+thJYlisUc44010=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.3.0 h1:0709Jtq/6QXEuWRfAm260XqlpcwL1vxtO1tUE2qK8Z4=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
|
||||
gopkg.in/ldap.v3 v3.0.3 h1:YKRHW/2sIl05JsCtx/5ZuUueFuJyoj/6+DGXe3wp6ro=
|
||||
@@ -809,4 +847,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -37,7 +37,7 @@ import (
|
||||
type LoginDetails struct {
|
||||
|
||||
// login strategy
|
||||
// Enum: [form redirect]
|
||||
// Enum: [form redirect service-account]
|
||||
LoginStrategy string `json:"loginStrategy,omitempty"`
|
||||
|
||||
// redirect
|
||||
@@ -62,7 +62,7 @@ var loginDetailsTypeLoginStrategyPropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["form","redirect"]`), &res); err != nil {
|
||||
if err := json.Unmarshal([]byte(`["form","redirect","service-account"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
@@ -77,6 +77,9 @@ const (
|
||||
|
||||
// LoginDetailsLoginStrategyRedirect captures enum value "redirect"
|
||||
LoginDetailsLoginStrategyRedirect string = "redirect"
|
||||
|
||||
// LoginDetailsLoginStrategyServiceAccount captures enum value "service-account"
|
||||
LoginDetailsLoginStrategyServiceAccount string = "service-account"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
|
||||
81
models/login_mkube_request.go
Normal file
81
models/login_mkube_request.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// LoginMkubeRequest login mkube request
|
||||
//
|
||||
// swagger:model loginMkubeRequest
|
||||
type LoginMkubeRequest struct {
|
||||
|
||||
// jwt
|
||||
// Required: true
|
||||
Jwt *string `json:"jwt"`
|
||||
}
|
||||
|
||||
// Validate validates this login mkube request
|
||||
func (m *LoginMkubeRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateJwt(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *LoginMkubeRequest) validateJwt(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("jwt", "body", m.Jwt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *LoginMkubeRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *LoginMkubeRequest) UnmarshalBinary(b []byte) error {
|
||||
var res LoginMkubeRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"github.com/minio/minio/pkg/env"
|
||||
)
|
||||
|
||||
// GetOperatorOnly gets mcs operator mode status set on env variable
|
||||
//or default one
|
||||
func GetOperatorOnly() string {
|
||||
return strings.ToLower(env.Get(McsOperatorOnly, "off"))
|
||||
// GetOperatorOnly gets MCS mkube admin mode status set on env variable
|
||||
// or default one
|
||||
func GetOperatorOnly() bool {
|
||||
return strings.ToLower(env.Get(McsmKubeAdminOnly, "off")) == "on"
|
||||
}
|
||||
|
||||
@@ -17,5 +17,5 @@
|
||||
package acl
|
||||
|
||||
const (
|
||||
McsOperatorOnly = "MCS_OPERATOR_ONLY"
|
||||
McsmKubeAdminOnly = "MCS_MKUBE_ADMIN_ONLY"
|
||||
)
|
||||
|
||||
@@ -286,7 +286,7 @@ func actionsStringToActionSet(actions []string) iampolicy.ActionSet {
|
||||
func GetAuthorizedEndpoints(actions []string) []string {
|
||||
rangeTake := endpointRules
|
||||
|
||||
if operatorOnly == "on" {
|
||||
if operatorOnly {
|
||||
rangeTake = operatorRules
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOperatorOnlyEndpoints(t *testing.T) {
|
||||
operatorOnly = "on"
|
||||
operatorOnly = true
|
||||
|
||||
tests := []endpoint{
|
||||
{
|
||||
|
||||
@@ -26,9 +26,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
"github.com/go-openapi/swag"
|
||||
xjwt "github.com/minio/mcs/pkg/auth/jwt"
|
||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||
"github.com/minio/minio/cmd"
|
||||
@@ -182,3 +184,42 @@ func decrypt(data []byte) ([]byte, error) {
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// GetTokenFromRequest returns a token from a http Request
|
||||
// either defined on a cookie `token` or on Authorization header.
|
||||
//
|
||||
// Authorization Header needs to be like "Authorization Bearer <jwt_token>"
|
||||
func GetTokenFromRequest(r *http.Request) (*string, error) {
|
||||
// Get Auth token
|
||||
var reqToken string
|
||||
|
||||
// Token might come either as a Cookie or as a Header
|
||||
// if not set in cookie, check if it is set on Header.
|
||||
tokenCookie, err := r.Cookie("token")
|
||||
if err != nil {
|
||||
headerToken := r.Header.Get("Authorization")
|
||||
// reqToken should come as "Bearer <token>"
|
||||
splitHeaderToken := strings.Split(headerToken, "Bearer")
|
||||
if len(splitHeaderToken) <= 1 {
|
||||
return nil, errNoAuthToken
|
||||
}
|
||||
reqToken = strings.TrimSpace(splitHeaderToken[1])
|
||||
} else {
|
||||
reqToken = strings.TrimSpace(tokenCookie.Value)
|
||||
}
|
||||
return swag.String(reqToken), nil
|
||||
}
|
||||
|
||||
func GetClaimsFromTokenInRequest(req *http.Request) (*DecryptedClaims, error) {
|
||||
sessionID, err := GetTokenFromRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Perform decryption of the JWT, if MCS is able to decrypt the JWT that means a valid session
|
||||
// was used in the first place to get it
|
||||
claims, err := JWTAuthenticate(*sessionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
77
pkg/auth/mkube.go
Normal file
77
pkg/auth/mkube.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/mcs/pkg/auth/mkube"
|
||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||
)
|
||||
|
||||
// mkubeCredentialsProvider is an struct to hold the JWT (service account token)
|
||||
type mkubeCredentialsProvider struct {
|
||||
serviceAccountJWT string
|
||||
}
|
||||
|
||||
// Implementing the interfaces of the minio Provider, we use this to leverage on the existing mcs Authentication flow
|
||||
func (s mkubeCredentialsProvider) Retrieve() (credentials.Value, error) {
|
||||
return credentials.Value{
|
||||
AccessKeyID: "",
|
||||
SecretAccessKey: "",
|
||||
SessionToken: s.serviceAccountJWT,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsExpired dummy function, must be implemented in order to work with the minio provider authentication
|
||||
func (s mkubeCredentialsProvider) IsExpired() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// isServiceAccountTokenValid will make an authenticated request (using bearer token) against Mkube hostname, if the
|
||||
// request success means the provided jwt its a valid service account token and the MCS user can use it for future requests
|
||||
// until it fails
|
||||
func isServiceAccountTokenValid(client *http.Client, jwt string) bool {
|
||||
url := fmt.Sprintf("%s/api/v1/tenants", mkube.GetMkubeEndpoint())
|
||||
m3Req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
token := fmt.Sprintf("Bearer %s", jwt)
|
||||
m3Req.Header.Add("Authorization", token)
|
||||
resp, err := client.Do(m3Req)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetMcsCredentialsFromMkube will validate the provided JWT (service account token) and return it in the form of credentials.Credentials
|
||||
func GetMcsCredentialsFromMkube(jwt string) (*credentials.Credentials, error) {
|
||||
if isServiceAccountTokenValid(mkube.HTTPClient, jwt) {
|
||||
return credentials.New(mkubeCredentialsProvider{serviceAccountJWT: jwt}), nil
|
||||
}
|
||||
return nil, errInvalidCredentials
|
||||
}
|
||||
117
pkg/auth/mkube/config.go
Normal file
117
pkg/auth/mkube/config.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package mkube
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio/pkg/env"
|
||||
)
|
||||
|
||||
var (
|
||||
certDontExists = "File certificate doesn't exists: %s"
|
||||
)
|
||||
|
||||
// getMkubeEndpoint returns the hostname of mkube
|
||||
func GetMkubeEndpoint() string {
|
||||
return env.Get(McsMkubeHost, "http://m3:8787")
|
||||
}
|
||||
|
||||
// getMkubeEndpointIsSecure returns true or false depending on the protocol in Mkube URL
|
||||
func getMkubeEndpointIsSecure() bool {
|
||||
server := GetMkubeEndpoint()
|
||||
if strings.Contains(server, "://") {
|
||||
parts := strings.Split(server, "://")
|
||||
if len(parts) > 1 {
|
||||
if parts[0] == "https" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// If MCS_M3_SERVER_TLS_CA_CERTIFICATE is true mcs will load a list of certificates into the
|
||||
// http.client rootCAs store, this is useful for testing or when working with self-signed certificates
|
||||
func getMkubeServerTLSRootCAs() []string {
|
||||
caCertFileNames := strings.TrimSpace(env.Get(McsMkubeTLSCACertificate, ""))
|
||||
if caCertFileNames == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(caCertFileNames, ",")
|
||||
}
|
||||
|
||||
// FileExists verifies if a file exist on the desired location and its not a folder
|
||||
func FileExists(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
// GetMkubeHTTPClient returns an http.Client with custom configurations used by MCS to talk to Mkube
|
||||
// custom configurations include the use of CA certificates
|
||||
func getMkubeHTTPClient() *http.Client {
|
||||
httpTransport := &http.Transport{}
|
||||
// If Mkube server is running with TLS enabled and it's using a self-signed certificate
|
||||
// or a certificate issued by a custom certificate authority we prepare a new custom *http.Transport
|
||||
if getMkubeEndpointIsSecure() {
|
||||
caCertFileNames := getMkubeServerTLSRootCAs()
|
||||
tlsConfig := &tls.Config{
|
||||
// Can't use SSLv3 because of POODLE and BEAST
|
||||
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
|
||||
// Can't use TLSv1.1 because of RC4 cipher usage
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
// If CAs certificates are configured we save them to the http.Client RootCAs store
|
||||
if len(caCertFileNames) > 0 {
|
||||
certs := x509.NewCertPool()
|
||||
for _, caCert := range caCertFileNames {
|
||||
// Validate certificate exists
|
||||
if FileExists(caCert) {
|
||||
pemData, err := ioutil.ReadFile(caCert)
|
||||
if err != nil {
|
||||
// if there was an error reading pem file stop mcs
|
||||
panic(err)
|
||||
}
|
||||
certs.AppendCertsFromPEM(pemData)
|
||||
} else {
|
||||
// if provided cert filename doesn't exists stop mcs
|
||||
panic(fmt.Sprintf(certDontExists, caCert))
|
||||
}
|
||||
}
|
||||
tlsConfig.RootCAs = certs
|
||||
}
|
||||
httpTransport.TLSClientConfig = tlsConfig
|
||||
}
|
||||
|
||||
// Return http client with default configuration
|
||||
return &http.Client{
|
||||
Transport: httpTransport,
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPClient it's a public variable that contains the HTTP configuration to be used by MCS to talk to Mkube
|
||||
// This function will run only once
|
||||
var HTTPClient = getMkubeHTTPClient()
|
||||
22
pkg/auth/mkube/const.go
Normal file
22
pkg/auth/mkube/const.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package mkube
|
||||
|
||||
const (
|
||||
McsMkubeHost = "MCS_M3_HOSTNAME"
|
||||
McsMkubeTLSCACertificate = "MCS_M3_SERVER_TLS_CA_CERTIFICATE"
|
||||
)
|
||||
77
pkg/auth/mkube_test.go
Normal file
77
pkg/auth/mkube_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// RoundTripFunc .
|
||||
type RoundTripFunc func(req *http.Request) (*http.Response, error)
|
||||
|
||||
// RoundTrip .
|
||||
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return f(req)
|
||||
}
|
||||
|
||||
//NewTestClient returns *http.Client with Transport replaced to avoid making real calls
|
||||
func NewTestClient(fn RoundTripFunc) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: fn,
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isServiceAccountTokenValid(t *testing.T) {
|
||||
|
||||
successResponse := NewTestClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`OK`)),
|
||||
Header: make(http.Header),
|
||||
}, nil
|
||||
})
|
||||
|
||||
failResponse := NewTestClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: 500,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(`NOTOK`)),
|
||||
Header: make(http.Header),
|
||||
}, errors.New("something wrong")
|
||||
})
|
||||
|
||||
type args struct {
|
||||
client *http.Client
|
||||
jwt string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Success authentication - correct jwt (service account token)",
|
||||
args: args{
|
||||
client: successResponse,
|
||||
jwt: "GOODTOKEN",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Fail authentication - incorrect jwt (service account token)",
|
||||
args: args{
|
||||
client: failResponse,
|
||||
jwt: "BADTOKEN",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isServiceAccountTokenValid(tt.args.client, tt.args.jwt); got != tt.want {
|
||||
t.Errorf("isServiceAccountTokenValid() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package ws contains websocket utils for mcs project
|
||||
package ws
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// GetTokenFromRequest returns a token from a http Request
|
||||
// either defined on a cookie `token` or on Authorization header.
|
||||
//
|
||||
// Authorization Header needs to be like "Authorization Bearer <jwt_token>"
|
||||
func GetTokenFromRequest(r *http.Request) (*string, error) {
|
||||
// Get Auth token
|
||||
var reqToken string
|
||||
|
||||
// Token might come either as a Cookie or as a Header
|
||||
// if not set in cookie, check if it is set on Header.
|
||||
tokenCookie, err := r.Cookie("token")
|
||||
if err != nil {
|
||||
headerToken := r.Header.Get("Authorization")
|
||||
// reqToken should come as "Bearer <token>"
|
||||
splitHeaderToken := strings.Split(headerToken, "Bearer")
|
||||
if len(splitHeaderToken) <= 1 {
|
||||
return nil, errors.New(http.StatusBadRequest, "Authentication not valid")
|
||||
}
|
||||
reqToken = strings.TrimSpace(splitHeaderToken[1])
|
||||
} else {
|
||||
reqToken = strings.TrimSpace(tokenCookie.Value)
|
||||
}
|
||||
return swag.String(reqToken), nil
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import request from "superagent";
|
||||
import storage from "local-storage-fallback";
|
||||
import { connect, ConnectedProps } from "react-redux";
|
||||
@@ -26,9 +26,8 @@ import { CircularProgress, Paper } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { SystemState } from "../../types";
|
||||
import { userLoggedIn } from "../../actions";
|
||||
import history from "../../history";
|
||||
import api from "../../common/api";
|
||||
import { ILoginDetails } from "./types";
|
||||
import { ILoginDetails, loginStrategyType } from "./types";
|
||||
import { setCookie } from "../../common/utils";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -101,57 +100,57 @@ interface ILoginProps {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
interface ILoginState {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
error: string;
|
||||
loading: boolean;
|
||||
loginStrategy: ILoginDetails;
|
||||
interface LoginStrategyRoutes {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
class Login extends React.Component<ILoginProps, ILoginState> {
|
||||
state: ILoginState = {
|
||||
accessKey: "",
|
||||
secretKey: "",
|
||||
error: "",
|
||||
loading: false,
|
||||
loginStrategy: {
|
||||
loginStrategy: "",
|
||||
redirect: "",
|
||||
},
|
||||
};
|
||||
interface LoginStrategyPayload {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
fetchConfiguration() {
|
||||
this.setState({ loading: true }, () => {
|
||||
api
|
||||
.invoke("GET", "/api/v1/login")
|
||||
.then((loginDetails: ILoginDetails) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
this.setState({
|
||||
loading: false,
|
||||
loginStrategy: loginDetails,
|
||||
error: "",
|
||||
});
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.setState({ loading: false, error: err });
|
||||
});
|
||||
});
|
||||
const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
||||
const [accessKey, setAccessKey] = useState<string>("");
|
||||
const [jwt, setJwt] = useState<string>("");
|
||||
const [secretKey, setSecretKey] = useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [loginStrategy, setLoginStrategy] = useState<ILoginDetails>({
|
||||
loginStrategy: loginStrategyType.unknown,
|
||||
redirect: "",
|
||||
});
|
||||
|
||||
const loginStrategyEndpoints: LoginStrategyRoutes = {
|
||||
"form": "/api/v1/login",
|
||||
"service-account": "/api/v1/login/mkube",
|
||||
}
|
||||
const loginStrategyPayload: LoginStrategyPayload = {
|
||||
"form": { accessKey, secretKey },
|
||||
"service-account": { jwt },
|
||||
}
|
||||
|
||||
formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const url = "/api/v1/login";
|
||||
const { accessKey, secretKey } = this.state;
|
||||
const fetchConfiguration = () => {
|
||||
setLoading(true);
|
||||
|
||||
api
|
||||
.invoke("GET", "/api/v1/login")
|
||||
.then((loginDetails: ILoginDetails) => {
|
||||
setLoading(false);
|
||||
setLoginStrategy(loginDetails);
|
||||
setError("");
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoading(false);
|
||||
setError(err);
|
||||
});
|
||||
};
|
||||
|
||||
const formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
request
|
||||
.post(url)
|
||||
.send({ accessKey, secretKey })
|
||||
.post(loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login")
|
||||
.send(loginStrategyPayload[loginStrategy.loginStrategy])
|
||||
.then((res: any) => {
|
||||
const bodyResponse = res.body;
|
||||
|
||||
if (bodyResponse.sessionId) {
|
||||
// store the jwt token
|
||||
setCookie("token", bodyResponse.sessionId);
|
||||
@@ -164,134 +163,173 @@ class Login extends React.Component<ILoginProps, ILoginState> {
|
||||
})
|
||||
.then(() => {
|
||||
// We set the state in redux
|
||||
this.props.userLoggedIn(true);
|
||||
userLoggedIn(true);
|
||||
// There is a browser cache issue if we change the policy associated to an account and then logout and history.push("/") after login
|
||||
// therefore after login we need to use window.location redirect
|
||||
window.location.href = "/";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.setState({ error: `${err}` });
|
||||
setError(err.message);
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
this.fetchConfiguration();
|
||||
}
|
||||
useEffect(() => {
|
||||
fetchConfiguration();
|
||||
}, []);
|
||||
|
||||
render() {
|
||||
const { error, accessKey, secretKey, loginStrategy } = this.state;
|
||||
const { classes } = this.props;
|
||||
let loginComponent = null;
|
||||
|
||||
let loginComponent = null;
|
||||
|
||||
switch (loginStrategy.loginStrategy) {
|
||||
case "form": {
|
||||
loginComponent = (
|
||||
<React.Fragment>
|
||||
<Typography component="h1" variant="h6">
|
||||
Login
|
||||
</Typography>
|
||||
<form
|
||||
className={classes.form}
|
||||
noValidate
|
||||
onSubmit={this.formSubmit}
|
||||
>
|
||||
<Grid container spacing={2}>
|
||||
{error !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{error}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
switch (loginStrategy.loginStrategy) {
|
||||
case loginStrategyType.form: {
|
||||
loginComponent = (
|
||||
<React.Fragment>
|
||||
<Typography component="h1" variant="h6">
|
||||
Login
|
||||
</Typography>
|
||||
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
||||
<Grid container spacing={2}>
|
||||
{error !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
id="accessKey"
|
||||
value={accessKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
this.setState({ accessKey: e.target.value })
|
||||
}
|
||||
label="Access Key"
|
||||
name="accessKey"
|
||||
autoComplete="username"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
value={secretKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
this.setState({ secretKey: e.target.value })
|
||||
}
|
||||
name="secretKey"
|
||||
label="Secret Key"
|
||||
type="password"
|
||||
id="secretKey"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{error}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
id="accessKey"
|
||||
value={accessKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setAccessKey(e.target.value)
|
||||
}
|
||||
label="Access Key"
|
||||
name="accessKey"
|
||||
autoComplete="username"
|
||||
/>
|
||||
</Grid>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.submit}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</form>
|
||||
</React.Fragment>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "redirect": {
|
||||
loginComponent = (
|
||||
<React.Fragment>
|
||||
<Typography component="h1" variant="h6">
|
||||
Login
|
||||
</Typography>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
value={secretKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setSecretKey(e.target.value)
|
||||
}
|
||||
name="secretKey"
|
||||
label="Secret Key"
|
||||
type="password"
|
||||
id="secretKey"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Button
|
||||
component={"a"}
|
||||
href={loginStrategy.redirect}
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.submit}
|
||||
>
|
||||
Welcome
|
||||
Login
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
loginComponent = (
|
||||
<CircularProgress className={classes.loadingLoginStrategy} />
|
||||
);
|
||||
</form>
|
||||
</React.Fragment>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container className={classes.mainContainer}>
|
||||
<Grid item xs={7} className={classes.theOcean}>
|
||||
<div className={classes.oceanBg} />
|
||||
</Grid>
|
||||
<Grid item xs={5} className={classes.theLogin}>
|
||||
{loginComponent}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
case loginStrategyType.redirect: {
|
||||
loginComponent = (
|
||||
<React.Fragment>
|
||||
<Typography component="h1" variant="h6">
|
||||
Login
|
||||
</Typography>
|
||||
<Button
|
||||
component={"a"}
|
||||
href={loginStrategy.redirect}
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.submit}
|
||||
>
|
||||
Welcome
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case loginStrategyType.serviceAccount: {
|
||||
loginComponent = (
|
||||
<React.Fragment>
|
||||
<Typography component="h1" variant="h6">
|
||||
Login
|
||||
</Typography>
|
||||
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
||||
<Grid container spacing={2}>
|
||||
{error !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{error}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
id="jwt"
|
||||
value={jwt}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setJwt(e.target.value)
|
||||
}
|
||||
label="JWT"
|
||||
name="jwt"
|
||||
autoComplete="Service Account JWT Token"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
variant="contained"
|
||||
color="primary"
|
||||
className={classes.submit}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</form>
|
||||
</React.Fragment>
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
loginComponent = (
|
||||
<CircularProgress className={classes.loadingLoginStrategy} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container className={classes.mainContainer}>
|
||||
<Grid item xs={7} className={classes.theOcean}>
|
||||
<div className={classes.oceanBg} />
|
||||
</Grid>
|
||||
<Grid item xs={5} className={classes.theLogin}>
|
||||
{loginComponent}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
export default connector(withStyles(styles)(Login));
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export interface ILoginDetails {
|
||||
loginStrategy: string;
|
||||
loginStrategy: loginStrategyType;
|
||||
redirect: string;
|
||||
}
|
||||
|
||||
export enum loginStrategyType {
|
||||
unknown = "unknown",
|
||||
form = "form",
|
||||
redirect = "redirect",
|
||||
serviceAccount = "service-account",
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import (
|
||||
|
||||
mc "github.com/minio/mc/cmd"
|
||||
"github.com/minio/mc/pkg/probe"
|
||||
"github.com/minio/mcs/pkg/acl"
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
xjwt "github.com/minio/mcs/pkg/auth/jwt"
|
||||
"github.com/minio/mcs/pkg/auth/ldap"
|
||||
"github.com/minio/mcs/pkg/auth/mkube"
|
||||
"github.com/minio/minio-go/v6"
|
||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||
)
|
||||
@@ -120,7 +122,7 @@ func (c mcS3Client) watch(options mc.WatchOptions) (*mc.WatchObject, *probe.Erro
|
||||
}
|
||||
|
||||
// MCSCredentials interface with all functions to be implemented
|
||||
// by mock when testing, it should include all needed minioCredentials.Credentials api calls
|
||||
// by mock when testing, it should include all needed mcsCredentials.Credentials api calls
|
||||
// that are used within this project.
|
||||
type MCSCredentials interface {
|
||||
Get() (credentials.Value, error)
|
||||
@@ -129,17 +131,17 @@ type MCSCredentials interface {
|
||||
|
||||
// Interface implementation
|
||||
type mcsCredentials struct {
|
||||
minioCredentials *credentials.Credentials
|
||||
mcsCredentials *credentials.Credentials
|
||||
}
|
||||
|
||||
// implements *Credentials.Get()
|
||||
func (c mcsCredentials) Get() (credentials.Value, error) {
|
||||
return c.minioCredentials.Get()
|
||||
return c.mcsCredentials.Get()
|
||||
}
|
||||
|
||||
// implements *Credentials.Expire()
|
||||
func (c mcsCredentials) Expire() {
|
||||
c.minioCredentials.Expire()
|
||||
c.mcsCredentials.Expire()
|
||||
}
|
||||
|
||||
// mcsSTSAssumeRole it's a STSAssumeRole wrapper, in general
|
||||
@@ -159,22 +161,31 @@ func (s mcsSTSAssumeRole) IsExpired() bool {
|
||||
|
||||
// STSClient contains http.client configuration need it by STSAssumeRole
|
||||
var STSClient = PrepareSTSClient()
|
||||
var MinioEndpoint = getMinIOServer()
|
||||
var MkubeEndpoint = mkube.GetMkubeEndpoint()
|
||||
|
||||
func newMcsCredentials(accessKey, secretKey, location string) (*credentials.Credentials, error) {
|
||||
mcsEndpoint := getMinIOServer()
|
||||
if mcsEndpoint == "" {
|
||||
return nil, errors.New("endpoint cannot be empty for AssumeRoleSTS")
|
||||
}
|
||||
if accessKey == "" || secretKey == "" {
|
||||
return nil, errors.New("creredentials access/secretkey is mandatory for AssumeRoleSTS")
|
||||
}
|
||||
|
||||
// Future authentication methods can be added under this switch statement
|
||||
switch {
|
||||
// MKUBE authentication for MCS
|
||||
case acl.GetOperatorOnly():
|
||||
{
|
||||
if MkubeEndpoint == "" {
|
||||
return nil, errors.New("endpoint cannot be empty for Mkube")
|
||||
}
|
||||
creds, err := auth.GetMcsCredentialsFromMkube(secretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
// LDAP authentication for MCS
|
||||
case ldap.GetLDAPEnabled():
|
||||
{
|
||||
creds, err := auth.GetMcsCredentialsFromLDAP(mcsEndpoint, accessKey, secretKey)
|
||||
if MinioEndpoint == "" {
|
||||
return nil, errors.New("endpoint cannot be empty for AssumeRoleSTS")
|
||||
}
|
||||
creds, err := auth.GetMcsCredentialsFromLDAP(MinioEndpoint, accessKey, secretKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -183,6 +194,9 @@ func newMcsCredentials(accessKey, secretKey, location string) (*credentials.Cred
|
||||
// default authentication for MCS is via STS (Security Token Service) against MinIO
|
||||
default:
|
||||
{
|
||||
if MinioEndpoint == "" || accessKey == "" || secretKey == "" {
|
||||
return nil, errors.New("creredentials endpont, access and secretkey are mandatory for AssumeRoleSTS")
|
||||
}
|
||||
opts := credentials.STSAssumeRoleOptions{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
@@ -191,7 +205,7 @@ func newMcsCredentials(accessKey, secretKey, location string) (*credentials.Cred
|
||||
}
|
||||
stsAssumeRole := &credentials.STSAssumeRole{
|
||||
Client: STSClient,
|
||||
STSEndpoint: mcsEndpoint,
|
||||
STSEndpoint: MinioEndpoint,
|
||||
Options: opts,
|
||||
}
|
||||
mcsSTSWrapper := mcsSTSAssumeRole{stsAssumeRole: stsAssumeRole}
|
||||
@@ -209,7 +223,7 @@ func GetClaimsFromJWT(jwt string) (*auth.DecryptedClaims, error) {
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// getMcsCredentialsFromJWT returns the *minioCredentials.Credentials associated to the
|
||||
// getMcsCredentialsFromJWT returns the *mcsCredentials.Credentials associated to the
|
||||
// provided jwt, this is useful for running the Expire() or IsExpired() operations
|
||||
func getMcsCredentialsFromJWT(jwt string) (*credentials.Credentials, error) {
|
||||
claims, err := GetClaimsFromJWT(jwt)
|
||||
@@ -220,7 +234,7 @@ func getMcsCredentialsFromJWT(jwt string) (*credentials.Credentials, error) {
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// newMinioClient creates a new MinIO client based on the minioCredentials extracted
|
||||
// newMinioClient creates a new MinIO client based on the mcsCredentials extracted
|
||||
// from the provided jwt
|
||||
func newMinioClient(jwt string) (*minio.Client, error) {
|
||||
creds, err := getMcsCredentialsFromJWT(jwt)
|
||||
@@ -239,19 +253,18 @@ func newMinioClient(jwt string) (*minio.Client, error) {
|
||||
}
|
||||
|
||||
// newS3BucketClient creates a new mc S3Client to talk to the server based on a bucket
|
||||
func newS3BucketClient(jwt string, bucketName string) (*mc.S3Client, error) {
|
||||
func newS3BucketClient(claims *auth.DecryptedClaims, bucketName string) (*mc.S3Client, error) {
|
||||
endpoint := getMinIOServer()
|
||||
useSSL := getMinIOEndpointIsSecure()
|
||||
|
||||
claims, err := auth.JWTAuthenticate(jwt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.TrimSpace(bucketName) != "" {
|
||||
endpoint += fmt.Sprintf("/%s", bucketName)
|
||||
}
|
||||
|
||||
if claims == nil {
|
||||
return nil, fmt.Errorf("the provided credentials are invalid")
|
||||
}
|
||||
|
||||
s3Config := newS3Config(endpoint, claims.AccessKeyID, claims.SecretAccessKey, claims.SessionToken, !useSSL)
|
||||
client, pErr := mc.S3New(s3Config)
|
||||
if pErr != nil {
|
||||
@@ -269,7 +282,7 @@ func newS3BucketClient(jwt string, bucketName string) (*mc.S3Client, error) {
|
||||
// parameters.
|
||||
func newS3Config(endpoint, accessKey, secretKey, sessionToken string, isSecure bool) *mc.Config {
|
||||
// We have a valid alias and hostConfig. We populate the
|
||||
// minioCredentials from the match found in the config file.
|
||||
// mcsCredentials from the match found in the config file.
|
||||
s3Config := new(mc.Config)
|
||||
|
||||
s3Config.AppName = "mcs" // TODO: make this a constant
|
||||
|
||||
@@ -228,8 +228,3 @@ func getSecureFeaturePolicy() string {
|
||||
func getSecureExpectCTHeader() string {
|
||||
return env.Get(McsSecureExpectCTHeader, "")
|
||||
}
|
||||
|
||||
// getM3Host returns the hostname of mkube
|
||||
func getM3Host() string {
|
||||
return env.Get(McsM3Host, "http://m3:8787")
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/minio/mcs/models"
|
||||
"github.com/minio/mcs/pkg"
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
"github.com/minio/mcs/pkg/auth/mkube"
|
||||
|
||||
assetFS "github.com/elazarl/go-bindata-assetfs"
|
||||
|
||||
@@ -167,8 +168,7 @@ func FileServerMiddleware(next http.Handler) http.Handler {
|
||||
case strings.HasPrefix(r.URL.Path, "/ws"):
|
||||
serveWS(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/v1/mkube"):
|
||||
client := &http.Client{}
|
||||
serverMkube(client, w, r)
|
||||
serverMkube(mkube.HTTPClient, w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api"):
|
||||
next.ServeHTTP(w, r)
|
||||
default:
|
||||
|
||||
@@ -49,5 +49,4 @@ const (
|
||||
McsSecureReferrerPolicy = "MCS_SECURE_REFERRER_POLICY"
|
||||
McsSecureFeaturePolicy = "MCS_SECURE_FEATURE_POLICY"
|
||||
McsSecureExpectCTHeader = "MCS_SECURE_EXPECT_CT_HEADER"
|
||||
McsM3Host = "MCS_M3_HOSTNAME"
|
||||
)
|
||||
|
||||
@@ -754,6 +754,40 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/mkube": {
|
||||
"post": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Login to Mkube.",
|
||||
"operationId": "LoginMkube",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/loginMkubeRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A successful login.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/loginResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/oauth2/auth": {
|
||||
"post": {
|
||||
"security": [],
|
||||
@@ -1719,7 +1753,8 @@ func init() {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"form",
|
||||
"redirect"
|
||||
"redirect",
|
||||
"service-account"
|
||||
]
|
||||
},
|
||||
"redirect": {
|
||||
@@ -1727,6 +1762,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginMkubeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"jwt"
|
||||
],
|
||||
"properties": {
|
||||
"jwt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginOauth2AuthRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2869,6 +2915,40 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/mkube": {
|
||||
"post": {
|
||||
"security": [],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Login to Mkube.",
|
||||
"operationId": "LoginMkube",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/loginMkubeRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A successful login.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/loginResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/oauth2/auth": {
|
||||
"post": {
|
||||
"security": [],
|
||||
@@ -3834,7 +3914,8 @@ func init() {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"form",
|
||||
"redirect"
|
||||
"redirect",
|
||||
"service-account"
|
||||
]
|
||||
},
|
||||
"redirect": {
|
||||
@@ -3842,6 +3923,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginMkubeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"jwt"
|
||||
],
|
||||
"properties": {
|
||||
"jwt": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"loginOauth2AuthRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -18,6 +18,7 @@ package restapi
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -25,24 +26,45 @@ import (
|
||||
"strings"
|
||||
|
||||
apiErrors "github.com/go-openapi/errors"
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
"github.com/minio/mcs/pkg/auth/mkube"
|
||||
)
|
||||
|
||||
// serverMkube handles calls for mkube
|
||||
func serverMkube(client *http.Client, w http.ResponseWriter, req *http.Request) {
|
||||
// extract the service account token inside the jwt encrypted claims
|
||||
claims, err := auth.GetClaimsFromTokenInRequest(req)
|
||||
if err != nil {
|
||||
apiErrors.ServeError(w, req, err)
|
||||
return
|
||||
}
|
||||
m3SAToken := claims.SessionToken
|
||||
if m3SAToken == "" {
|
||||
apiErrors.ServeError(w, req, errors.New("service M3 is not available"))
|
||||
return
|
||||
}
|
||||
// destination of the request, the mkube server
|
||||
req.URL.Path = strings.Replace(req.URL.Path, "/mkube", "", 1)
|
||||
targetURL := fmt.Sprintf("%s%s", getM3Host(), req.URL.String())
|
||||
|
||||
targetURL := fmt.Sprintf("%s%s", mkube.GetMkubeEndpoint(), req.URL.String())
|
||||
body := new(bytes.Buffer)
|
||||
_, err = body.ReadFrom(req.Body)
|
||||
if err != nil {
|
||||
apiErrors.ServeError(w, req, err)
|
||||
return
|
||||
}
|
||||
// set the HTTP method, url, and m3Req body
|
||||
m3Req, err := http.NewRequest(req.Method, targetURL, req.Body)
|
||||
m3Req, err := http.NewRequest(req.Method, targetURL, body)
|
||||
if err != nil {
|
||||
apiErrors.ServeError(w, req, err)
|
||||
log.Println("error creating m3 request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// set the m3Req headers
|
||||
m3Req.Header = req.Header
|
||||
// Set the m3Req authorization headers
|
||||
// Authorization Header needs to be like "Authorization Bearer <jwt_token>"
|
||||
token := fmt.Sprintf("Bearer %s", m3SAToken)
|
||||
m3Req.Header.Add("Authorization", token)
|
||||
m3Req.Header.Add("Content-type", "application/json; charset=utf-8")
|
||||
resp, err := client.Do(m3Req)
|
||||
if err != nil {
|
||||
log.Println("error on m3 request:", err)
|
||||
|
||||
@@ -19,11 +19,15 @@ package restapi
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
"github.com/minio/minio-go/v6/pkg/credentials"
|
||||
)
|
||||
|
||||
// RoundTripFunc .
|
||||
@@ -41,8 +45,17 @@ func NewTestClient(fn RoundTripFunc) *http.Client {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_serverMkube(t *testing.T) {
|
||||
var audience = ""
|
||||
var creds = &credentials.Value{
|
||||
AccessKeyID: "fakeAccessKeyID",
|
||||
SecretAccessKey: "fakeSecretAccessKey",
|
||||
SessionToken: "fakeSessionToken",
|
||||
SignerType: 0,
|
||||
}
|
||||
|
||||
func Test_serverMkube(t *testing.T) {
|
||||
jwt, _ := auth.NewJWTWithClaimsForClient(creds, []string{""}, audience)
|
||||
dummyBody := ioutil.NopCloser(bytes.NewReader([]byte("foo")))
|
||||
OKclient := NewTestClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
@@ -83,16 +96,53 @@ func Test_serverMkube(t *testing.T) {
|
||||
args: args{
|
||||
client: OKclient,
|
||||
recorder: httptest.NewRecorder(),
|
||||
req: &http.Request{URL: testURL},
|
||||
req: &http.Request{
|
||||
Body: dummyBody,
|
||||
URL: testURL,
|
||||
Header: http.Header{
|
||||
"Authorization": []string{fmt.Sprintf("Bearer %s", jwt)},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCode: 200,
|
||||
},
|
||||
{
|
||||
name: "Unsuccessful request - wrong jwt credentials",
|
||||
args: args{
|
||||
client: OKclient,
|
||||
recorder: httptest.NewRecorder(),
|
||||
req: &http.Request{
|
||||
Body: dummyBody,
|
||||
URL: testURL,
|
||||
Header: http.Header{
|
||||
"Authorization": []string{"EAEAEAEAE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCode: 500,
|
||||
},
|
||||
{
|
||||
name: "Unsuccessful request - no mkube service account token provided",
|
||||
args: args{
|
||||
client: OKclient,
|
||||
recorder: httptest.NewRecorder(),
|
||||
req: &http.Request{
|
||||
Body: dummyBody,
|
||||
URL: testURL,
|
||||
Header: http.Header{},
|
||||
},
|
||||
},
|
||||
wantCode: 500,
|
||||
},
|
||||
{
|
||||
name: "Unsuccessful request",
|
||||
args: args{
|
||||
client: badClient,
|
||||
recorder: httptest.NewRecorder(),
|
||||
req: &http.Request{URL: testURL},
|
||||
req: &http.Request{
|
||||
URL: testURL,
|
||||
Body: dummyBody,
|
||||
},
|
||||
},
|
||||
wantCode: 500,
|
||||
},
|
||||
@@ -101,7 +151,10 @@ func Test_serverMkube(t *testing.T) {
|
||||
args: args{
|
||||
client: refusedClient,
|
||||
recorder: httptest.NewRecorder(),
|
||||
req: &http.Request{URL: testURL},
|
||||
req: &http.Request{
|
||||
URL: testURL,
|
||||
Body: dummyBody,
|
||||
},
|
||||
},
|
||||
wantCode: 500,
|
||||
},
|
||||
@@ -111,7 +164,7 @@ func Test_serverMkube(t *testing.T) {
|
||||
serverMkube(tt.args.client, tt.args.recorder, tt.args.req)
|
||||
resp := tt.args.recorder.Result()
|
||||
if resp.StatusCode != tt.wantCode {
|
||||
t.Errorf("Invalid code returned")
|
||||
t.Errorf("Invalid code returned, expected: %d received: %d", tt.wantCode, resp.StatusCode)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
@@ -141,6 +141,9 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
|
||||
UserAPILoginDetailHandler: user_api.LoginDetailHandlerFunc(func(params user_api.LoginDetailParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.LoginDetail has not yet been implemented")
|
||||
}),
|
||||
UserAPILoginMkubeHandler: user_api.LoginMkubeHandlerFunc(func(params user_api.LoginMkubeParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.LoginMkube has not yet been implemented")
|
||||
}),
|
||||
UserAPILoginOauth2AuthHandler: user_api.LoginOauth2AuthHandlerFunc(func(params user_api.LoginOauth2AuthParams) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.LoginOauth2Auth has not yet been implemented")
|
||||
}),
|
||||
@@ -293,6 +296,8 @@ type McsAPI struct {
|
||||
UserAPILoginHandler user_api.LoginHandler
|
||||
// UserAPILoginDetailHandler sets the operation handler for the login detail operation
|
||||
UserAPILoginDetailHandler user_api.LoginDetailHandler
|
||||
// UserAPILoginMkubeHandler sets the operation handler for the login mkube operation
|
||||
UserAPILoginMkubeHandler user_api.LoginMkubeHandler
|
||||
// UserAPILoginOauth2AuthHandler sets the operation handler for the login oauth2 auth operation
|
||||
UserAPILoginOauth2AuthHandler user_api.LoginOauth2AuthHandler
|
||||
// UserAPILogoutHandler sets the operation handler for the logout operation
|
||||
@@ -478,6 +483,9 @@ func (o *McsAPI) Validate() error {
|
||||
if o.UserAPILoginDetailHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LoginDetailHandler")
|
||||
}
|
||||
if o.UserAPILoginMkubeHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LoginMkubeHandler")
|
||||
}
|
||||
if o.UserAPILoginOauth2AuthHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.LoginOauth2AuthHandler")
|
||||
}
|
||||
@@ -736,6 +744,10 @@ func (o *McsAPI) initHandlerCache() {
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/login/mkube"] = user_api.NewLoginMkube(o.context, o.UserAPILoginMkubeHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/login/oauth2/auth"] = user_api.NewLoginOauth2Auth(o.context, o.UserAPILoginOauth2AuthHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
|
||||
75
restapi/operations/user_api/login_mkube.go
Normal file
75
restapi/operations/user_api/login_mkube.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// LoginMkubeHandlerFunc turns a function with the right signature into a login mkube handler
|
||||
type LoginMkubeHandlerFunc func(LoginMkubeParams) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn LoginMkubeHandlerFunc) Handle(params LoginMkubeParams) middleware.Responder {
|
||||
return fn(params)
|
||||
}
|
||||
|
||||
// LoginMkubeHandler interface for that can handle valid login mkube params
|
||||
type LoginMkubeHandler interface {
|
||||
Handle(LoginMkubeParams) middleware.Responder
|
||||
}
|
||||
|
||||
// NewLoginMkube creates a new http.Handler for the login mkube operation
|
||||
func NewLoginMkube(ctx *middleware.Context, handler LoginMkubeHandler) *LoginMkube {
|
||||
return &LoginMkube{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*LoginMkube swagger:route POST /login/mkube UserAPI loginMkube
|
||||
|
||||
Login to Mkube.
|
||||
|
||||
*/
|
||||
type LoginMkube struct {
|
||||
Context *middleware.Context
|
||||
Handler LoginMkubeHandler
|
||||
}
|
||||
|
||||
func (o *LoginMkube) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewLoginMkubeParams()
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
94
restapi/operations/user_api/login_mkube_parameters.go
Normal file
94
restapi/operations/user_api/login_mkube_parameters.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/mcs/models"
|
||||
)
|
||||
|
||||
// NewLoginMkubeParams creates a new LoginMkubeParams object
|
||||
// no default values defined in spec.
|
||||
func NewLoginMkubeParams() LoginMkubeParams {
|
||||
|
||||
return LoginMkubeParams{}
|
||||
}
|
||||
|
||||
// LoginMkubeParams contains all the bound params for the login mkube operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters LoginMkube
|
||||
type LoginMkubeParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.LoginMkubeRequest
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewLoginMkubeParams() beforehand.
|
||||
func (o *LoginMkubeParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.LoginMkubeRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body"))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body"))
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
133
restapi/operations/user_api/login_mkube_responses.go
Normal file
133
restapi/operations/user_api/login_mkube_responses.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/mcs/models"
|
||||
)
|
||||
|
||||
// LoginMkubeCreatedCode is the HTTP code returned for type LoginMkubeCreated
|
||||
const LoginMkubeCreatedCode int = 201
|
||||
|
||||
/*LoginMkubeCreated A successful login.
|
||||
|
||||
swagger:response loginMkubeCreated
|
||||
*/
|
||||
type LoginMkubeCreated struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.LoginResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginMkubeCreated creates LoginMkubeCreated with default headers values
|
||||
func NewLoginMkubeCreated() *LoginMkubeCreated {
|
||||
|
||||
return &LoginMkubeCreated{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login mkube created response
|
||||
func (o *LoginMkubeCreated) WithPayload(payload *models.LoginResponse) *LoginMkubeCreated {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the login mkube created response
|
||||
func (o *LoginMkubeCreated) SetPayload(payload *models.LoginResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LoginMkubeCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(201)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*LoginMkubeDefault Generic error response.
|
||||
|
||||
swagger:response loginMkubeDefault
|
||||
*/
|
||||
type LoginMkubeDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewLoginMkubeDefault creates LoginMkubeDefault with default headers values
|
||||
func NewLoginMkubeDefault(code int) *LoginMkubeDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &LoginMkubeDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the login mkube default response
|
||||
func (o *LoginMkubeDefault) WithStatusCode(code int) *LoginMkubeDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the login mkube default response
|
||||
func (o *LoginMkubeDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the login mkube default response
|
||||
func (o *LoginMkubeDefault) WithPayload(payload *models.Error) *LoginMkubeDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the login mkube default response
|
||||
func (o *LoginMkubeDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *LoginMkubeDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
104
restapi/operations/user_api/login_mkube_urlbuilder.go
Normal file
104
restapi/operations/user_api/login_mkube_urlbuilder.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// LoginMkubeURL generates an URL for the login mkube operation
|
||||
type LoginMkubeURL struct {
|
||||
_basePath string
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LoginMkubeURL) WithBasePath(bp string) *LoginMkubeURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *LoginMkubeURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *LoginMkubeURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/login/mkube"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *LoginMkubeURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *LoginMkubeURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *LoginMkubeURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on LoginMkubeURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on LoginMkubeURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *LoginMkubeURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func prepareSTSClientTransport() *http.Transport {
|
||||
// Can't use TLSv1.1 because of RC4 cipher usage
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
// If root CAs are configured we save them to the http.Client RootCAs store
|
||||
// If CAs certificates are configured we save them to the http.Client RootCAs store
|
||||
if len(caCertFileNames) > 0 {
|
||||
certs := x509.NewCertPool()
|
||||
for _, caCert := range caCertFileNames {
|
||||
@@ -85,7 +85,7 @@ func prepareSTSClientTransport() *http.Transport {
|
||||
}
|
||||
|
||||
// PrepareSTSClient returns an http.Client with custom configurations need it by *credentials.STSAssumeRole
|
||||
// custom configurations include skipVerification flag, and root CA certificates
|
||||
// custom configurations include the use of CA certificates
|
||||
func PrepareSTSClient() *http.Client {
|
||||
transport := prepareSTSClientTransport()
|
||||
// Return http client with default configuration
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/mcs/models"
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
"github.com/minio/mcs/restapi/operations"
|
||||
"github.com/minio/mcs/restapi/operations/user_api"
|
||||
"github.com/minio/minio-go/v6"
|
||||
@@ -181,7 +182,11 @@ func createBucketEvent(client MCS3Client, arn string, notificationEvents []model
|
||||
|
||||
// getCreateBucketEventsResponse calls createBucketEvent to add a bucket event notification
|
||||
func getCreateBucketEventsResponse(sessionID, bucketName string, eventReq *models.BucketEventRequest) error {
|
||||
s3Client, err := newS3BucketClient(sessionID, bucketName)
|
||||
claims, err := auth.JWTAuthenticate(sessionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s3Client, err := newS3BucketClient(claims, bucketName)
|
||||
if err != nil {
|
||||
log.Println("error creating S3Client:", err)
|
||||
return err
|
||||
@@ -217,7 +222,11 @@ func joinNotificationEvents(events []models.NotificationEventType) string {
|
||||
|
||||
// getDeleteBucketEventsResponse calls deleteBucketEventNotification() to delete a bucket event notification
|
||||
func getDeleteBucketEventsResponse(sessionID, bucketName string, arn string, events []models.NotificationEventType, prefix, suffix *string) error {
|
||||
s3Client, err := newS3BucketClient(sessionID, bucketName)
|
||||
claims, err := auth.JWTAuthenticate(sessionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s3Client, err := newS3BucketClient(claims, bucketName)
|
||||
if err != nil {
|
||||
log.Println("error creating S3Client:", err)
|
||||
return err
|
||||
|
||||
@@ -61,18 +61,25 @@ func registerLoginHandlers(api *operations.McsAPI) {
|
||||
}
|
||||
return user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse)
|
||||
})
|
||||
api.UserAPILoginMkubeHandler = user_api.LoginMkubeHandlerFunc(func(params user_api.LoginMkubeParams) middleware.Responder {
|
||||
loginResponse, err := getLoginMkubeResponse(params.Body)
|
||||
if err != nil {
|
||||
return user_api.NewLoginMkubeDefault(401).WithPayload(&models.Error{Code: 401, Message: swag.String(err.Error())})
|
||||
}
|
||||
return user_api.NewLoginMkubeCreated().WithPayload(loginResponse)
|
||||
})
|
||||
}
|
||||
|
||||
// login performs a check of minioCredentials against MinIO, generates some claims and returns the jwt
|
||||
// login performs a check of mcsCredentials against MinIO, generates some claims and returns the jwt
|
||||
// for subsequent authentication
|
||||
func login(credentials MCSCredentials, actions []string) (*string, error) {
|
||||
// try to obtain minioCredentials,
|
||||
// try to obtain mcsCredentials,
|
||||
tokens, err := credentials.Get()
|
||||
if err != nil {
|
||||
log.Println("error authenticating user", err)
|
||||
return nil, errInvalidCredentials
|
||||
}
|
||||
// if we made it here, the minioCredentials work, generate a jwt with claims
|
||||
// if we made it here, the mcsCredentials work, generate a jwt with claims
|
||||
jwt, err := auth.NewJWTWithClaimsForClient(&tokens, actions, getMinIOServer())
|
||||
if err != nil {
|
||||
log.Println("error authenticating user", err)
|
||||
@@ -115,7 +122,7 @@ func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, error) {
|
||||
log.Println("error login:", err)
|
||||
return nil, errInvalidCredentials
|
||||
}
|
||||
credentials := mcsCredentials{minioCredentials: creds}
|
||||
credentials := mcsCredentials{mcsCredentials: creds}
|
||||
// obtain the current policy assigned to this user
|
||||
// necessary for generating the list of allowed endpoints
|
||||
userInfo, err := adminClient.getUserInfo(ctx, *lr.AccessKey)
|
||||
@@ -127,7 +134,7 @@ func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, error) {
|
||||
// by default every user starts with an empty array of available actions
|
||||
// therefore we would have access only to pages that doesn't require any privilege
|
||||
// ie: service-account page
|
||||
actions := []string{}
|
||||
var actions []string
|
||||
// if a policy is assigned to this user we parse the actions from there
|
||||
if policy != nil {
|
||||
actions = acl.GetActionsStringFromPolicy(policy)
|
||||
@@ -148,7 +155,9 @@ func getLoginDetailsResponse() (*models.LoginDetails, error) {
|
||||
ctx := context.Background()
|
||||
loginStrategy := models.LoginDetailsLoginStrategyForm
|
||||
redirectURL := ""
|
||||
if oauth2.IsIdpEnabled() {
|
||||
if acl.GetOperatorOnly() {
|
||||
loginStrategy = models.LoginDetailsLoginStrategyServiceAccount
|
||||
} else if oauth2.IsIdpEnabled() {
|
||||
loginStrategy = models.LoginDetailsLoginStrategyRedirect
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(ctx, nil)
|
||||
@@ -238,7 +247,7 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
log.Println("error login:", err)
|
||||
return nil, errorGeneric
|
||||
}
|
||||
credentials := mcsCredentials{minioCredentials: creds}
|
||||
credentials := mcsCredentials{mcsCredentials: creds}
|
||||
jwt, err := login(credentials, actions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -251,3 +260,23 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
}
|
||||
return nil, errorGeneric
|
||||
}
|
||||
|
||||
// getLoginMkubeResponse validate the provided service account token against mkube
|
||||
func getLoginMkubeResponse(lmr *models.LoginMkubeRequest) (*models.LoginResponse, error) {
|
||||
creds, err := newMcsCredentials("", *lmr.Jwt, "")
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, errInvalidCredentials
|
||||
}
|
||||
credentials := mcsCredentials{mcsCredentials: creds}
|
||||
var actions []string
|
||||
jwt, err := login(credentials, actions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
SessionID: *jwt,
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func registerLogoutHandlers(api *operations.McsAPI) {
|
||||
})
|
||||
}
|
||||
|
||||
// logout() call Expire() on the provided minioCredentials
|
||||
// logout() call Expire() on the provided mcsCredentials
|
||||
func logout(credentials MCSCredentials) {
|
||||
credentials.Expire()
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func getLogoutResponse(jwt string) error {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
credentials := mcsCredentials{minioCredentials: creds}
|
||||
credentials := mcsCredentials{mcsCredentials: creds}
|
||||
if err != nil {
|
||||
log.Println("error creating MinIO Client:", err)
|
||||
return err
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/minio/mcs/pkg/auth"
|
||||
"github.com/minio/mcs/pkg/ws"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
@@ -99,14 +98,9 @@ func (c wsConn) readMessage() (messageType int, p []byte, err error) {
|
||||
// on the path.
|
||||
// Request should come like ws://<host>:<port>/ws/<api>
|
||||
func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
sessionID, err := ws.GetTokenFromRequest(req)
|
||||
if err != nil {
|
||||
errors.ServeError(w, req, err)
|
||||
return
|
||||
}
|
||||
// Perform authentication before upgrading to a Websocket Connection
|
||||
// authenticate WS connection with MCS
|
||||
claims, err := auth.JWTAuthenticate(*sessionID)
|
||||
claims, err := auth.GetClaimsFromTokenInRequest(req)
|
||||
if err != nil {
|
||||
log.Print("error on ws authentication: ", err)
|
||||
errors.ServeError(w, req, errors.New(http.StatusUnauthorized, err.Error()))
|
||||
@@ -152,7 +146,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
go wsAdminClient.heal(hOptions)
|
||||
case strings.HasPrefix(wsPath, `/watch`):
|
||||
wOptions := getWatchOptionsFromReq(req)
|
||||
wsS3Client, err := newWebSocketS3Client(conn, *sessionID, wOptions.BucketName)
|
||||
wsS3Client, err := newWebSocketS3Client(conn, claims, wOptions.BucketName)
|
||||
if err != nil {
|
||||
closeWsConn(conn)
|
||||
return
|
||||
@@ -188,10 +182,10 @@ func newWebSocketAdminClient(conn *websocket.Conn, autClaims *auth.DecryptedClai
|
||||
}
|
||||
|
||||
// newWebSocketS3Client returns a wsAdminClient authenticated as MCS admin
|
||||
func newWebSocketS3Client(conn *websocket.Conn, jwt, bucketName string) (*wsS3Client, error) {
|
||||
func newWebSocketS3Client(conn *websocket.Conn, claims *auth.DecryptedClaims, bucketName string) (*wsS3Client, error) {
|
||||
// Only start Websocket Interaction after user has been
|
||||
// authenticated with MinIO
|
||||
s3Client, err := newS3BucketClient(jwt, bucketName)
|
||||
s3Client, err := newS3BucketClient(claims, bucketName)
|
||||
if err != nil {
|
||||
log.Println("error creating S3Client:", err)
|
||||
conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
|
||||
37
swagger.yml
37
swagger.yml
@@ -60,6 +60,28 @@ paths:
|
||||
security: []
|
||||
tags:
|
||||
- UserAPI
|
||||
/login/mkube:
|
||||
post:
|
||||
summary: Login to Mkube.
|
||||
operationId: LoginMkube
|
||||
parameters:
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/loginMkubeRequest'
|
||||
responses:
|
||||
201:
|
||||
description: A successful login.
|
||||
schema:
|
||||
$ref: '#/definitions/loginResponse'
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
security: []
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/login/oauth2/auth:
|
||||
post:
|
||||
@@ -354,7 +376,7 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
|
||||
/service-accounts/{access_key}:
|
||||
delete:
|
||||
summary: Delete Service Account
|
||||
@@ -1173,7 +1195,7 @@ definitions:
|
||||
- get
|
||||
notificationConfig:
|
||||
type: object
|
||||
required:
|
||||
required:
|
||||
- arn
|
||||
properties:
|
||||
id:
|
||||
@@ -1212,7 +1234,7 @@ definitions:
|
||||
title: "filter event associated to the specified suffix"
|
||||
bucketEventRequest:
|
||||
type: object
|
||||
required:
|
||||
required:
|
||||
- configuration
|
||||
properties:
|
||||
configuration:
|
||||
@@ -1242,7 +1264,7 @@ definitions:
|
||||
properties:
|
||||
loginStrategy:
|
||||
type: string
|
||||
enum: [form,redirect]
|
||||
enum: [form,redirect,service-account]
|
||||
redirect:
|
||||
type: string
|
||||
loginOauth2AuthRequest:
|
||||
@@ -1255,6 +1277,13 @@ definitions:
|
||||
type: string
|
||||
code:
|
||||
type: string
|
||||
loginMkubeRequest:
|
||||
type: object
|
||||
required:
|
||||
- jwt
|
||||
properties:
|
||||
jwt:
|
||||
type: string
|
||||
loginRequest:
|
||||
type: object
|
||||
required:
|
||||
|
||||
Reference in New Issue
Block a user