Support PKCE for auth command for OpenFaaS Pro users
* Enables PKCE in the place of implicit auth flow. * Auth command requires EULA acceptance and valid OpenFaaS Pro license or trial. * Updates feature status from alpha to generally-available Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
b562392b12
commit
ad610f1071
@@ -27,7 +27,7 @@ RUN test -z "$(gofmt -l $(find . -type f -name '*.go' -not -path "./vendor/*"))"
|
|||||||
|
|
||||||
# ldflags "-s -w" strips binary
|
# ldflags "-s -w" strips binary
|
||||||
# ldflags -X injects commit version into binary
|
# ldflags -X injects commit version into binary
|
||||||
RUN /usr/bin/license-check -path ./ --verbose=false "Alex Ellis" "OpenFaaS Author(s)"
|
RUN /usr/bin/license-check -path ./ --verbose=false "Alex Ellis" "OpenFaaS Author(s)" "OpenFaaS Ltd"
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
||||||
go test $(go list ./... | grep -v /vendor/ | grep -v /template/|grep -v /build/|grep -v /sample/) -cover
|
go test $(go list ./... | grep -v /vendor/ | grep -v /template/|grep -v /build/|grep -v /sample/) -cover
|
||||||
|
|||||||
10
LICENSE
10
LICENSE
@@ -1,6 +1,14 @@
|
|||||||
|
Annotated portions of this project are licensed under the OpenFaaS Pro
|
||||||
|
commercial license, for which a license is required to use the software.
|
||||||
|
|
||||||
|
EULA: https://github.com/openfaas/faas/blob/master/pro/EULA.md
|
||||||
|
|
||||||
|
The remainder of the source code is licensed under the MIT license.
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016-2017 Alex Ellis
|
Copyright (c) 2016-2021 OpenFaaS Ltd
|
||||||
|
Copyright (c) 2017-2021 OpenFaaS Author(s)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -83,7 +83,7 @@ The main commands supported by the CLI are:
|
|||||||
|
|
||||||
* `faas-cli secret` - manage secrets for your functions
|
* `faas-cli secret` - manage secrets for your functions
|
||||||
|
|
||||||
* `faas-cli auth` - (alpha) initiates an OAuth2 authorization flow to obtain a cookie
|
* `faas-cli auth` - initiates an OAuth2 authorization flow to obtain a token
|
||||||
|
|
||||||
* `faas-cli registry-login` - generate registry auth file in correct format by providing username and password for docker/ecr/self hosted registry
|
* `faas-cli registry-login` - generate registry auth file in correct format by providing username and password for docker/ecr/self hosted registry
|
||||||
|
|
||||||
@@ -116,13 +116,15 @@ You can chose between using a [programming language template](https://github.com
|
|||||||
|
|
||||||
#### `faas-cli auth`
|
#### `faas-cli auth`
|
||||||
|
|
||||||
The `auth` command is currently available for alpha testing. Use the `auth` command to obtain a JWT to use as a Bearer token.
|
The `auth` command is only licensed for OpenFaaS Pro customers.
|
||||||
|
|
||||||
Two flow-types are supported in the CLI.
|
Use the `auth` command to obtain a JWT to use as a Bearer token.
|
||||||
|
|
||||||
##### `code` grant - default
|
##### `code` grant - default
|
||||||
|
|
||||||
Use this flow to obtain a token.
|
Use this flow to obtain a token for interactive use from your workstation.
|
||||||
|
|
||||||
|
The code grant flow uses the PKCE extension.
|
||||||
|
|
||||||
At this time the `token` cannot be saved or retained in your OpenFaaS config file. You can pass the token using a CLI flag of `--token=$TOKEN`.
|
At this time the `token` cannot be saved or retained in your OpenFaaS config file. You can pass the token using a CLI flag of `--token=$TOKEN`.
|
||||||
|
|
||||||
@@ -131,6 +133,7 @@ Example:
|
|||||||
```sh
|
```sh
|
||||||
faas-cli auth \
|
faas-cli auth \
|
||||||
--auth-url https://tenant0.eu.auth0.com/authorize \
|
--auth-url https://tenant0.eu.auth0.com/authorize \
|
||||||
|
--token-url https://tenant0.eu.auth0.com/oauth/token \
|
||||||
--audience http://gw.example.com \
|
--audience http://gw.example.com \
|
||||||
--client-id "${OAUTH_CLIENT_ID}"
|
--client-id "${OAUTH_CLIENT_ID}"
|
||||||
```
|
```
|
||||||
@@ -402,4 +405,6 @@ See [contributing guide](https://github.com/openfaas/faas-cli/blob/master/CONTRI
|
|||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
This project is part of OpenFaaS and is licensed under the MIT License.
|
Portions of this project are licensed under the OpenFaaS Pro EULA.
|
||||||
|
|
||||||
|
The remaining source unless annotated is licensed under the MIT License.
|
||||||
|
|||||||
207
commands/auth.go
207
commands/auth.go
@@ -1,11 +1,16 @@
|
|||||||
// Copyright (c) OpenFaaS Author(s) 2017. All rights reserved.
|
// Copyright (c) OpenFaaS Ltd 2021. All rights reserved.
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
//
|
||||||
|
// Licensed for use with OpenFaaS Pro only
|
||||||
|
// See EULA: https://github.com/openfaas/faas/blob/master/pro/EULA.md
|
||||||
|
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -23,12 +28,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
scope string
|
scope string
|
||||||
authURL string
|
authURL string
|
||||||
|
tokenURL string
|
||||||
|
|
||||||
clientID string
|
clientID string
|
||||||
audience string
|
audience string
|
||||||
listenPort int
|
listenPort int
|
||||||
launchBrowser bool
|
launchBrowser bool
|
||||||
|
eula bool
|
||||||
grant string
|
grant string
|
||||||
clientSecret string
|
clientSecret string
|
||||||
redirectHost string
|
redirectHost string
|
||||||
@@ -37,11 +45,14 @@ var (
|
|||||||
func init() {
|
func init() {
|
||||||
authCmd.Flags().StringVarP(&gateway, "gateway", "g", defaultGateway, "Gateway URL starting with http(s)://")
|
authCmd.Flags().StringVarP(&gateway, "gateway", "g", defaultGateway, "Gateway URL starting with http(s)://")
|
||||||
authCmd.Flags().StringVar(&authURL, "auth-url", "", "OAuth2 Authorize URL i.e. http://idp/oauth/authorize")
|
authCmd.Flags().StringVar(&authURL, "auth-url", "", "OAuth2 Authorize URL i.e. http://idp/oauth/authorize")
|
||||||
|
authCmd.Flags().StringVar(&tokenURL, "token-url", "", "OAuth2 Token URL i.e. http://idp/oauth/token")
|
||||||
|
|
||||||
authCmd.Flags().StringVar(&clientID, "client-id", "", "OAuth2 client_id")
|
authCmd.Flags().StringVar(&clientID, "client-id", "", "OAuth2 client_id")
|
||||||
authCmd.Flags().IntVar(&listenPort, "listen-port", 31111, "OAuth2 local port for receiving cookie")
|
authCmd.Flags().IntVar(&listenPort, "listen-port", 31111, "OAuth2 local port for receiving cookie")
|
||||||
authCmd.Flags().StringVar(&audience, "audience", "", "OAuth2 audience")
|
authCmd.Flags().StringVar(&audience, "audience", "", "OAuth2 audience")
|
||||||
authCmd.Flags().BoolVar(&launchBrowser, "launch-browser", true, "Launch browser for OAuth2 redirect")
|
authCmd.Flags().BoolVar(&launchBrowser, "launch-browser", true, "Launch browser for OAuth2 redirect")
|
||||||
authCmd.Flags().StringVar(&redirectHost, "redirect-host", "http://127.0.0.1", "Host for OAuth2 redirection in the implicit flow including URL scheme")
|
authCmd.Flags().StringVar(&redirectHost, "redirect-host", "http://127.0.0.1", "Host for OAuth2 redirection in the implicit flow including URL scheme")
|
||||||
|
authCmd.Flags().BoolVar(&eula, "eula", false, "Agree to the EULA, for use with OpenFaaS Pro only")
|
||||||
|
|
||||||
authCmd.Flags().StringVar(&scope, "scope", "openid profile", "scope for OAuth2 flow - i.e. \"openid profile\"")
|
authCmd.Flags().StringVar(&scope, "scope", "openid profile", "scope for OAuth2 flow - i.e. \"openid profile\"")
|
||||||
authCmd.Flags().StringVar(&grant, "grant", "implicit", "grant for OAuth2 flow - either implicit, implicit-id or client_credentials")
|
authCmd.Flags().StringVar(&grant, "grant", "implicit", "grant for OAuth2 flow - either implicit, implicit-id or client_credentials")
|
||||||
@@ -57,9 +68,20 @@ var authCmd = &cobra.Command{
|
|||||||
[--client-secret]
|
[--client-secret]
|
||||||
[--grant GRANT]`,
|
[--grant GRANT]`,
|
||||||
Short: "Obtain a token for your OpenFaaS gateway",
|
Short: "Obtain a token for your OpenFaaS gateway",
|
||||||
Long: "Authenticate to an OpenFaaS gateway using OAuth2.",
|
Long: `Authenticate to an OpenFaaS gateway using OIDC.
|
||||||
Example: ` faas-cli auth --client-id my-id --auth-url https://tenant.auth0.com/authorize --scope "oidc profile" --audience my-id
|
|
||||||
faas-cli auth --grant=client_credentials --client-id=id --client-secret=secret --auth-url=https://tenant.auth0.com/token`,
|
Only licensed for use by OpenFaaS Pro customers.`,
|
||||||
|
Example: ` faas-cli auth \
|
||||||
|
--grant code \
|
||||||
|
--client-id my-id \
|
||||||
|
--auth-url https://tenant.auth0.com/authorize \
|
||||||
|
--token-url https://tenant.auth0.com/oauth/token \
|
||||||
|
--scope "oidc profile email"
|
||||||
|
|
||||||
|
faas-cli auth --grant=client_credentials \
|
||||||
|
--client-id=id \
|
||||||
|
--client-secret=secret \
|
||||||
|
--auth-url=https://tenant.auth0.com/oauth/token`,
|
||||||
RunE: runAuth,
|
RunE: runAuth,
|
||||||
PreRunE: preRunAuth,
|
PreRunE: preRunAuth,
|
||||||
}
|
}
|
||||||
@@ -67,10 +89,11 @@ var authCmd = &cobra.Command{
|
|||||||
func preRunAuth(cmd *cobra.Command, args []string) error {
|
func preRunAuth(cmd *cobra.Command, args []string) error {
|
||||||
return checkValues(authURL,
|
return checkValues(authURL,
|
||||||
clientID,
|
clientID,
|
||||||
|
eula,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkValues(authURL, clientID string) error {
|
func checkValues(authURL, clientID string, eula bool) error {
|
||||||
|
|
||||||
if len(authURL) == 0 {
|
if len(authURL) == 0 {
|
||||||
return fmt.Errorf("--auth-url is required and must be a valid OIDC URL")
|
return fmt.Errorf("--auth-url is required and must be a valid OIDC URL")
|
||||||
@@ -88,6 +111,10 @@ func checkValues(authURL, clientID string) error {
|
|||||||
return fmt.Errorf("--client-id is required")
|
return fmt.Errorf("--client-id is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !eula {
|
||||||
|
return fmt.Errorf("the auth command is only licensed for OpenFaaS Pro customers, see: https://github.com/openfaas/faas/blob/master/pro/EULA.md")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +126,94 @@ func runAuth(cmd *cobra.Command, args []string) error {
|
|||||||
} else if grant == "client_credentials" {
|
} else if grant == "client_credentials" {
|
||||||
return authClientCredentials()
|
return authClientCredentials()
|
||||||
}
|
}
|
||||||
|
if grant == "code" {
|
||||||
|
if len(tokenURL) == 0 {
|
||||||
|
return fmt.Errorf("--token-url is required for PKCE")
|
||||||
|
}
|
||||||
|
return authPkce("id_token")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func authPkce(grant string) error {
|
||||||
|
|
||||||
|
context, cancel := context.WithCancel(context.TODO())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
verifier := make([]byte, 32)
|
||||||
|
|
||||||
|
_, err := rand.Read(verifier)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
verifierEncoded := base64.RawURLEncoding.EncodeToString(verifier[:])
|
||||||
|
|
||||||
|
challenge := sha256.Sum256([]byte(verifierEncoded))
|
||||||
|
challengeEncoded := base64.RawURLEncoding.EncodeToString(challenge[:])
|
||||||
|
|
||||||
|
q := url.Values{}
|
||||||
|
q.Add("client_id", clientID)
|
||||||
|
|
||||||
|
q.Add("state", fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||||
|
q.Add("nonce", fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||||
|
q.Add("scope", scope)
|
||||||
|
q.Add("response_type", "code")
|
||||||
|
q.Add("audience", audience)
|
||||||
|
q.Add("code_challenge", challengeEncoded)
|
||||||
|
q.Add("code_challenge_method", "S256")
|
||||||
|
|
||||||
|
uri, err := makeRedirectURI(redirectHost, listenPort)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
q.Add("redirect_uri", uri.String())
|
||||||
|
|
||||||
|
authURLVal, _ := url.Parse(authURL)
|
||||||
|
authURLVal.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
browserBase := authURLVal
|
||||||
|
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", listenPort),
|
||||||
|
ReadTimeout: 5 * time.Second,
|
||||||
|
WriteTimeout: 5 * time.Second,
|
||||||
|
MaxHeaderBytes: 1 << 20, // Max header of 1MB
|
||||||
|
Handler: http.HandlerFunc(makeCodeCallbackHandler(cancel, errCh, verifierEncoded, clientID, uri.String())),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
fmt.Printf("Starting local token server on port %d\n", listenPort)
|
||||||
|
if err := server.ListenAndServe(); err != nil {
|
||||||
|
if err != http.ErrServerClosed {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer server.Shutdown(context)
|
||||||
|
|
||||||
|
fmt.Printf("Launching browser: %s\n", browserBase)
|
||||||
|
if launchBrowser {
|
||||||
|
err := launchURL(browserBase.String())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to launch browser")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-context.Done():
|
||||||
|
server.Shutdown(context)
|
||||||
|
case serverErr := <-errCh:
|
||||||
|
if serverErr != nil {
|
||||||
|
return serverErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +323,7 @@ func authClientCredentials() error {
|
|||||||
if res.StatusCode != http.StatusOK {
|
if res.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("cannot authenticate, code: %d.\nResponse: %s", res.StatusCode, string(tokenData))
|
return fmt.Errorf("cannot authenticate, code: %d.\nResponse: %s", res.StatusCode, string(tokenData))
|
||||||
}
|
}
|
||||||
token := ClientCredentialsToken{}
|
token := AuthToken{}
|
||||||
tokenErr := json.Unmarshal(tokenData, &token)
|
tokenErr := json.Unmarshal(tokenData, &token)
|
||||||
if tokenErr != nil {
|
if tokenErr != nil {
|
||||||
return errors.Wrapf(tokenErr, "unable to unmarshal token: %s", string(tokenData))
|
return errors.Wrapf(tokenErr, "unable to unmarshal token: %s", string(tokenData))
|
||||||
@@ -254,6 +369,70 @@ func printExampleTokenUsage(gateway, token string) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeCodeCallbackHandler(cancel context.CancelFunc, errCh chan error, verifierEncoded, clientID, redirectURI string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println(r.URL)
|
||||||
|
|
||||||
|
if r.URL.Path == "/oauth/callback" {
|
||||||
|
code := r.URL.Query().Get("code")
|
||||||
|
state := r.URL.Query().Get("state")
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
|
v.Add("code", code)
|
||||||
|
v.Add("state", state)
|
||||||
|
v.Add("code_verifier", verifierEncoded)
|
||||||
|
v.Add("grant_type", "authorization_code")
|
||||||
|
v.Add("client_id", clientID)
|
||||||
|
v.Add("redirect_uri", redirectURI)
|
||||||
|
|
||||||
|
u, err := url.Parse(tokenURL)
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBufferString(v.Encode())
|
||||||
|
req, err := http.NewRequest(http.MethodPost, u.String(), buf)
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenData, _ := ioutil.ReadAll(res.Body)
|
||||||
|
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
errCh <- fmt.Errorf("cannot authenticate, code: %d.\nResponse: %s", res.StatusCode, string(tokenData))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token := AuthToken{}
|
||||||
|
tokenErr := json.Unmarshal(tokenData, &token)
|
||||||
|
if tokenErr != nil {
|
||||||
|
errCh <- errors.Wrapf(tokenErr, "unable to unmarshal token: %s", string(tokenData))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := config.UpdateAuthConfig(gateway, token.IDToken, config.Oauth2AuthType); err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("credentials saved for", gateway)
|
||||||
|
printExampleTokenUsage(gateway, token.IDToken)
|
||||||
|
|
||||||
|
errCh <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func makeCallbackHandler(cancel context.CancelFunc) func(w http.ResponseWriter, r *http.Request) {
|
func makeCallbackHandler(cancel context.CancelFunc) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
@@ -318,9 +497,11 @@ type ClientCredentialsReq struct {
|
|||||||
GrantType string `json:"grant_type"`
|
GrantType string `json:"grant_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientCredentialsToken struct {
|
type AuthToken struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
Scope string `json:"scope"`
|
IDToken string `json:"id_token"`
|
||||||
ExpiresIn int `json:"expires_in"`
|
|
||||||
TokenType string `json:"token_type"`
|
Scope string `json:"scope"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Copyright (c) OpenFaaS Author(s) 2020. All rights reserved.
|
// Copyright (c) OpenFaaS Ltd 2021. All rights reserved.
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
//
|
||||||
|
// Licensed for use with OpenFaaS Pro only
|
||||||
|
// See EULA: https://github.com/openfaas/faas/blob/master/pro/EULA.md
|
||||||
|
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
@@ -12,6 +14,7 @@ func Test_auth(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
authURL string
|
authURL string
|
||||||
|
eula bool
|
||||||
clientID string
|
clientID string
|
||||||
wantErr string
|
wantErr string
|
||||||
}{
|
}{
|
||||||
@@ -20,31 +23,42 @@ func Test_auth(t *testing.T) {
|
|||||||
authURL: "",
|
authURL: "",
|
||||||
clientID: "",
|
clientID: "",
|
||||||
wantErr: "--auth-url is required and must be a valid OIDC URL",
|
wantErr: "--auth-url is required and must be a valid OIDC URL",
|
||||||
|
eula: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Invalid auth-url",
|
name: "Invalid auth-url",
|
||||||
authURL: "xyz",
|
authURL: "xyz",
|
||||||
clientID: "",
|
clientID: "",
|
||||||
wantErr: "--auth-url is an invalid URL: xyz",
|
wantErr: "--auth-url is an invalid URL: xyz",
|
||||||
|
eula: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid eula acceptance",
|
||||||
|
authURL: "http://xyz",
|
||||||
|
clientID: "id",
|
||||||
|
wantErr: "the auth command is only licensed for OpenFaaS Pro customers, see: https://github.com/openfaas/faas/blob/master/pro/EULA.md",
|
||||||
|
eula: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Valid auth-url, invalid client-id",
|
name: "Valid auth-url, invalid client-id",
|
||||||
authURL: "http://xyz",
|
authURL: "http://xyz",
|
||||||
clientID: "",
|
clientID: "",
|
||||||
wantErr: "--client-id is required",
|
wantErr: "--client-id is required",
|
||||||
|
eula: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Valid auth-url and client-id",
|
name: "Valid auth-url and client-id",
|
||||||
authURL: "http://xyz",
|
authURL: "http://xyz",
|
||||||
clientID: "abc",
|
clientID: "abc",
|
||||||
wantErr: "",
|
wantErr: "",
|
||||||
|
eula: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
err := checkValues(testCase.authURL, testCase.clientID)
|
err := checkValues(testCase.authURL, testCase.clientID, testCase.eula)
|
||||||
gotErr := ""
|
gotErr := ""
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gotErr = err.Error()
|
gotErr = err.Error()
|
||||||
|
|||||||
Reference in New Issue
Block a user