diff --git a/UPGRADE.md b/UPGRADE.md index 6433671fc85..90e58771df4 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -78,7 +78,10 @@ Additionally, flag `--dangerous-force-auto-logon` has been removed it has no eff ##### Access Control & `hydra connect` -WHAT HAPPENED TO THIS COMMAND? TBD +The command `hydra connect` has been removed as it no longer serves a purpose now that the internal access control +has been removed. Every command you call now needs the environment variable `HYDRA_URL` (previously named `CLUSTER_URL`) +which should point to ORY Hydra's URL. Removing this command has an additional benefit - privileged client IDs and secrets +will no longer be stored in a plaintext file on your system if you use this command. As access control has been removed, most commands (except `token user`, `token client`, `token revoke`, `token introspect`) work without supplying any credentials at all. The listed exceptions support setting an OAuth 2.0 Client ID and Client Secret @@ -87,6 +90,12 @@ using flags `--client-id` and `--client-secret` or environment variables `OAUTH2 All other commands, such as `hydra clients create`, still support scenarios where you would need an OAuth2 Access Token. In those cases, you can supply the access token using flag `--access-token` or environment variable `OAUTH2_ACCESS_TOKEN`. +All commands now support the `--endpoint` flag which sets the `HYDRA_URL` in case you don't want to use environment variables. + +#### `hydra token user` + +Flags `--id` and `--secret` are now called `--client-id` and `--client-secret`. + #### `hydra token validate` This command has been renamed to `hydra token introspect` to properly reflect that you are performing OAuth 2.0 diff --git a/cmd/cli/handler.go b/cmd/cli/handler.go index 0d808bfe1ec..41bc2b79bab 100644 --- a/cmd/cli/handler.go +++ b/cmd/cli/handler.go @@ -25,19 +25,19 @@ import ( ) type Handler struct { - Clients *ClientHandler - Keys *JWKHandler - Warden *IntrospectionHandler - Token *TokenHandler - Migration *MigrateHandler + Clients *ClientHandler + Keys *JWKHandler + Introspection *IntrospectionHandler + Token *TokenHandler + Migration *MigrateHandler } func NewHandler(c *config.Config) *Handler { return &Handler{ - Clients: newClientHandler(c), - Keys: newJWKHandler(c), - Warden: newIntrospectionHandler(c), - Token: newTokenHandler(c), - Migration: newMigrateHandler(c), + Clients: newClientHandler(c), + Keys: newJWKHandler(c), + Introspection: newIntrospectionHandler(c), + Token: newTokenHandler(c), + Migration: newMigrateHandler(c), } } diff --git a/cmd/cli/handler_client.go b/cmd/cli/handler_client.go index a8b207229e6..052c97861bf 100644 --- a/cmd/cli/handler_client.go +++ b/cmd/cli/handler_client.go @@ -21,13 +21,13 @@ package cli import ( + "crypto/tls" "encoding/json" "fmt" + "net/http" "os" "strings" - "net/http" - "github.com/ory/hydra/config" "github.com/ory/hydra/pkg" hydra "github.com/ory/hydra/sdk/go/hydra/swagger" @@ -45,13 +45,21 @@ func newClientHandler(c *config.Config) *ClientHandler { } func (h *ClientHandler) newClientManager(cmd *cobra.Command) *hydra.OAuth2Api { - c := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash()) - c.Configuration.Transport = h.Config.OAuth2Client(cmd).Transport + c := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash(cmd)) + + fakeTlsTermination, _ := cmd.Flags().GetBool("skip-tls-verify") + c.Configuration.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: fakeTlsTermination}, + } if term, _ := cmd.Flags().GetBool("fake-tls-termination"); term { c.Configuration.DefaultHeader["X-Forwarded-Proto"] = "https" } + if token, _ := cmd.Flags().GetString("access-token"); token != "" { + c.Configuration.DefaultHeader["Authorization"] = "Bearer " + token + } + return c } @@ -81,7 +89,7 @@ func (h *ClientHandler) CreateClient(cmd *cobra.Command, args []string) { m := h.newClientManager(cmd) responseTypes, _ := cmd.Flags().GetStringSlice("response-types") grantTypes, _ := cmd.Flags().GetStringSlice("grant-types") - allowedScopes, _ := cmd.Flags().GetStringSlice("allowed-scopes") + allowedScopes, _ := cmd.Flags().GetStringSlice("scope") callbacks, _ := cmd.Flags().GetStringSlice("callbacks") name, _ := cmd.Flags().GetString("name") secret, _ := cmd.Flags().GetString("secret") diff --git a/cmd/cli/handler_introspection.go b/cmd/cli/handler_introspection.go index ecec122cc74..b782fa955ed 100644 --- a/cmd/cli/handler_introspection.go +++ b/cmd/cli/handler_introspection.go @@ -21,6 +21,7 @@ package cli import ( + "crypto/tls" //"context" //"encoding/json" "fmt" @@ -45,14 +46,29 @@ func newIntrospectionHandler(c *config.Config) *IntrospectionHandler { } } -func (h *IntrospectionHandler) IsAuthorized(cmd *cobra.Command, args []string) { +func (h *IntrospectionHandler) Introspect(cmd *cobra.Command, args []string) { if len(args) != 1 { fmt.Print(cmd.UsageString()) return } - c := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash()) - c.Configuration.Transport = h.Config.OAuth2Client(cmd).Transport + c := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash(cmd)) + + clientID, _ := cmd.Flags().GetString("client-id") + clientSecret, _ := cmd.Flags().GetString("client-secret") + if clientID == "" || clientSecret == "" { + fmt.Print(cmd.UsageString()) + fmt.Println("Please provide a Client ID and Client Secret using flags --client-id and --client-secret, or environment variables OAUTH2_CLIENT_ID and OAUTH2_CLIENT_SECRET.") + return + } + + c.Configuration.Username = clientID + c.Configuration.Password = clientSecret + + skipTLSTermination, _ := cmd.Flags().GetBool("skip-tls-verify") + c.Configuration.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSTermination}, + } if term, _ := cmd.Flags().GetBool("fake-tls-termination"); term { c.Configuration.DefaultHeader["X-Forwarded-Proto"] = "https" diff --git a/cmd/cli/handler_jwk.go b/cmd/cli/handler_jwk.go index 0a6bd629c9b..73a1dee2e2f 100644 --- a/cmd/cli/handler_jwk.go +++ b/cmd/cli/handler_jwk.go @@ -21,6 +21,7 @@ package cli import ( + "crypto/tls" "fmt" "net/http" @@ -35,12 +36,21 @@ type JWKHandler struct { } func (h *JWKHandler) newJwkManager(cmd *cobra.Command) *hydra.JsonWebKeyApi { - c := hydra.NewJsonWebKeyApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash()) - c.Configuration.Transport = h.Config.OAuth2Client(cmd).Transport + c := hydra.NewJsonWebKeyApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash(cmd)) + + skipTLSTermination, _ := cmd.Flags().GetBool("skip-tls-verify") + c.Configuration.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSTermination}, + } + if term, _ := cmd.Flags().GetBool("fake-tls-termination"); term { c.Configuration.DefaultHeader["X-Forwarded-Proto"] = "https" } + if token, _ := cmd.Flags().GetString("access-token"); token != "" { + c.Configuration.DefaultHeader["Authorization"] = "Bearer " + token + } + return c } diff --git a/cmd/cli/handler_token.go b/cmd/cli/handler_token.go index 0bdd774ed5f..2909d2014e3 100644 --- a/cmd/cli/handler_token.go +++ b/cmd/cli/handler_token.go @@ -36,12 +36,21 @@ type TokenHandler struct { } func (h *TokenHandler) newTokenManager(cmd *cobra.Command) *hydra.OAuth2Api { - c := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash()) - c.Configuration.Transport = h.Config.OAuth2Client(cmd).Transport + c := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash(cmd)) + + skipTLSTermination, _ := cmd.Flags().GetBool("skip-tls-verify") + c.Configuration.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSTermination}, + } + if term, _ := cmd.Flags().GetBool("fake-tls-termination"); term { c.Configuration.DefaultHeader["X-Forwarded-Proto"] = "https" } + if token, _ := cmd.Flags().GetString("access-token"); token != "" { + c.Configuration.DefaultHeader["Authorization"] = "Bearer " + token + } + return c } @@ -55,9 +64,23 @@ func (h *TokenHandler) RevokeToken(cmd *cobra.Command, args []string) { return } - handler := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash()) - handler.Configuration.Username = h.Config.ClientID - handler.Configuration.Password = h.Config.ClientSecret + handler := hydra.NewOAuth2ApiWithBasePath(h.Config.GetClusterURLWithoutTailingSlash(cmd)) + + skipTLSTermination, _ := cmd.Flags().GetBool("skip-tls-verify") + handler.Configuration.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSTermination}, + } + + clientID, _ := cmd.Flags().GetString("client-id") + clientSecret, _ := cmd.Flags().GetString("client-secret") + if clientID == "" || clientSecret == "" { + fmt.Print(cmd.UsageString()) + fmt.Println("Please provide a Client ID and Client Secret using flags --client-id and --client-secret, or environment variables OAUTH2_CLIENT_ID and OAUTH2_CLIENT_SECRET.") + return + } + + handler.Configuration.Username = clientID + handler.Configuration.Password = clientSecret if skip, _ := cmd.Flags().GetBool("skip-tls-verify"); skip { handler.Configuration.Transport = &http.Transport{ diff --git a/cmd/clients.go b/cmd/clients.go index b517542b215..0c6575aeef4 100644 --- a/cmd/clients.go +++ b/cmd/clients.go @@ -21,6 +21,8 @@ package cmd import ( + "os" + "github.com/spf13/cobra" ) @@ -34,7 +36,9 @@ var clientsCmd = &cobra.Command{ func init() { RootCmd.AddCommand(clientsCmd) //clientsCmd.PersistentFlags().Bool("dry", false, "do not execute the command but show the corresponding curl command instead") - clientsCmd.PersistentFlags().Bool("fake-tls-termination", false, `fake tls termination by adding "X-Forwarded-Proto: https"" to http headers`) + clientsCmd.PersistentFlags().Bool("fake-tls-termination", false, `Fake tls termination by adding "X-Forwarded-Proto: https" to http headers`) + clientsCmd.PersistentFlags().String("access-token", os.Getenv("OAUTH2_ACCESS_TOKEN"), "Set an access token to be used in the Authorization header, defaults to environment variable ACCESS_TOKEN") + clientsCmd.PersistentFlags().String("endpoint", os.Getenv("HYDRA_URL"), "Set the URL where ORY Hydra is hosted, defaults to environment variable HYDRA_URL") // Here you will define your flags and configuration settings. diff --git a/cmd/connect.go b/cmd/connect.go deleted file mode 100644 index 79c2bfaa7ee..00000000000 --- a/cmd/connect.go +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright © 2015-2018 Aeneas Rekkas - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @author Aeneas Rekkas - * @copyright 2015-2018 Aeneas Rekkas - * @license Apache-2.0 - */ - -package cmd - -import ( - "bufio" - "fmt" - "log" - "os" - "strings" - - "net/http" - "net/url" - - "github.com/spf13/cobra" -) - -// connectCmd represents the connect command -var connectCmd = &cobra.Command{ - Use: "connect", - Short: "Connect with a cluster", - Run: func(cmd *cobra.Command, args []string) { - secret := "*********" - fmt.Println("To keep the current value, press enter.") - - if u, _ := cmd.Flags().GetString("url"); u != "" { - c.ClusterURL = u - } else if u := input("Cluster URL [" + c.ClusterURL + "]: "); u != "" { - c.ClusterURL = u - } - - if u, _ := cmd.Flags().GetString("id"); u != "" { - c.ClientID = u - } else if u := input("Client ID [" + c.ClientID + "]: "); u != "" { - c.ClientID = u - } - - if c.ClientSecret == "" { - secret = "empty" - } - - if u, _ := cmd.Flags().GetString("secret"); u != "" { - fmt.Println("You should not provide secrets using command line flags. The secret might leak to bash history and similar systems.") - c.ClientSecret = u - } else if u := input("Client Secret [" + secret + "]: "); u != "" { - c.ClientSecret = u - } - - if skipNewsletter, _ := cmd.Flags().GetBool("skip-newsletter"); !c.SignedUpForNewsletter && !skipNewsletter { - u := "https://ory.us10.list-manage.com/subscribe/post?u=ffb1a878e4ec6c0ed312a3480&id=f605a41b53" - fmt.Println("You are using the CLI for the first time. It is really important to keep your installation up to date. Because this technology is open source, we have no way of knowing who you are and how to contact you. Subscribe to our release and security announcements, and never miss important patches again:") - m := input("[Enter Email Address]:") - - v := url.Values{} - v.Add("EMAIL", m) - - req, err := http.NewRequest("POST", u, strings.NewReader(v.Encode())) - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - if m == "" { - } else { - if err != nil { - fmt.Printf("There was some error: %s\n", err.Error()) - } else { - resp, err := http.DefaultClient.Do(req) - - if err != nil { - fmt.Printf("There was some error: %s\n", err.Error()) - } else { - defer resp.Body.Close() - - fmt.Println("To complete the subscription process, please click the link in the email you just received.") - c.SignedUpForNewsletter = true - } - } - } - } - - if err := c.Persist(); err != nil { - log.Fatalf("Unable to save config file because %s.", err) - } - fmt.Println("Done.") - }, -} - -func input(message string) string { - reader := bufio.NewReader(os.Stdin) - fmt.Print(message) - s, err := reader.ReadString('\n') - if err != nil { - fatal(fmt.Sprintf("Could not read user input because %s.", err)) - } - return strings.TrimSpace(s) -} - -func init() { - RootCmd.AddCommand(connectCmd) - connectCmd.Flags().String("url", "", "The cluster URL") - connectCmd.Flags().String("id", "", "The client id") - connectCmd.Flags().String("secret", "", "The client secret") - connectCmd.Flags().Bool("skip-newsletter", false, "Skip the newsletter sign up question") -} diff --git a/cmd/keys.go b/cmd/keys.go index b35fedf0190..9611e587f13 100644 --- a/cmd/keys.go +++ b/cmd/keys.go @@ -21,6 +21,8 @@ package cmd import ( + "os" + "github.com/spf13/cobra" ) @@ -33,7 +35,9 @@ var keysCmd = &cobra.Command{ func init() { RootCmd.AddCommand(keysCmd) //keysCmd.PersistentFlags().Bool("dry", false, "do not execute the command but show the corresponding curl command instead") - keysCmd.PersistentFlags().Bool("fake-tls-termination", false, `fake tls termination by adding "X-Forwarded-Proto: https"" to http headers`) + keysCmd.PersistentFlags().Bool("fake-tls-termination", false, `fake tls termination by adding "X-Forwarded-Proto: https" to http headers`) + keysCmd.PersistentFlags().String("access-token", os.Getenv("OAUTH2_ACCESS_TOKEN"), "Set an access token to be used in the Authorization header, defaults to environment variable ACCESS_TOKEN") + keysCmd.PersistentFlags().String("endpoint", os.Getenv("HYDRA_URL"), "Set the URL where ORY Hydra is hosted, defaults to environment variable HYDRA_URL") // Here you will define your flags and configuration settings. diff --git a/cmd/root.go b/cmd/root.go index fa7d69ba71d..b4d9aaf356d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -76,8 +76,8 @@ func init() { // Cobra supports Persistent Flags, which, if defined here, // will be global for your application. - RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.hydra.yaml)") - RootCmd.PersistentFlags().Bool("skip-tls-verify", false, "foolishly accept TLS certificates signed by unkown certificate authorities") + RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Config file (default is $HOME/.hydra.yaml)") + RootCmd.PersistentFlags().Bool("skip-tls-verify", false, "Foolishly accept TLS certificates signed by unkown certificate authorities") // Cobra also supports local flags, which will only run // when this action is called directly. diff --git a/cmd/root_test.go b/cmd/root_test.go index 52ac331067f..518e97d10f7 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -21,67 +21,78 @@ package cmd import ( + "crypto/tls" "fmt" + "net/http" "os" - "path/filepath" "testing" "time" - "github.com/pborman/uuid" + "github.com/phayes/freeport" "github.com/stretchr/testify/assert" ) +var port int + func init() { - c.BindPort = 13124 + var err error + port, err = freeport.GetFreePort() + if err != nil { + panic(err.Error()) + } + os.Setenv("PORT", fmt.Sprintf("%d", port)) + os.Setenv("DATABASE_URL", "memory") + os.Setenv("HYDRA_URL", fmt.Sprintf("https://localhost:%d/", port)) + os.Setenv("OAUTH2_ISSUER_URL", fmt.Sprintf("https://localhost:%d/", port)) } func TestExecute(t *testing.T) { var osArgs = make([]string, len(os.Args)) - var path = filepath.Join(os.TempDir(), fmt.Sprintf("hydra-%s.yml", uuid.New())) - os.Setenv("DATABASE_URL", "memory") - os.Setenv("FORCE_ROOT_CLIENT_ID", "admin") - os.Setenv("FORCE_ROOT_CLIENT_SECRET", "pw") - os.Setenv("OAUTH2_ISSUER_URL", "https://localhost:4444/") copy(osArgs, os.Args) + endpoint := fmt.Sprintf("https://localhost:%d/", port) + for _, c := range []struct { args []string wait func() bool expectErr bool }{ { - args: []string{"host", "--dangerous-auto-logon", "--disable-telemetry"}, + args: []string{"serve", "--disable-telemetry"}, wait: func() bool { - _, err := os.Stat(path) + client := &http.Client{ + Transport: &transporter{ + FakeTLSTermination: true, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + }, + } + + _, err := client.Get(fmt.Sprintf("https://127.0.0.1:%d/health/status", port)) if err != nil { - t.Logf("Could not stat path %s because %s", path, err) + t.Logf("HTTP request failed: %s", err) } else { time.Sleep(time.Second * 5) } return err != nil }, }, - {args: []string{"connect", "--skip-newsletter", "--id", "admin", "--secret", "pw", "--url", "https://127.0.0.1:4444"}}, - {args: []string{"clients", "create", "--id", "foobarbaz"}}, - {args: []string{"clients", "get", "foobarbaz"}}, - {args: []string{"clients", "create", "--id", "public-foo", "--is-public"}}, - {args: []string{"clients", "delete", "foobarbaz"}}, - {args: []string{"keys", "create", "foo", "-a", "HS256"}}, - {args: []string{"keys", "create", "foo", "-a", "HS256"}}, - {args: []string{"keys", "get", "foo"}}, - {args: []string{"keys", "delete", "foo"}}, - {args: []string{"token", "revoke", "foo"}}, - {args: []string{"token", "client"}}, + {args: []string{"clients", "create", "--endpoint", endpoint, "--id", "foobarbaz", "--secret", "foobar", "-g", "client_credentials"}}, + {args: []string{"clients", "get", "--endpoint", endpoint, "foobarbaz"}}, + {args: []string{"clients", "create", "--endpoint", endpoint, "--id", "public-foo", "--is-public"}}, + {args: []string{"clients", "delete", "--endpoint", endpoint, "public-foo"}}, + {args: []string{"keys", "create", "foo", "--endpoint", endpoint, "-a", "HS256"}}, + {args: []string{"keys", "create", "foo", "--endpoint", endpoint, "-a", "HS256"}}, + {args: []string{"keys", "get", "--endpoint", endpoint, "foo"}}, + {args: []string{"keys", "delete", "--endpoint", endpoint, "foo"}}, + {args: []string{"token", "revoke", "--endpoint", endpoint, "--client-secret", "foobar", "--client-id", "foobarbaz", "foo"}}, + {args: []string{"token", "client", "--endpoint", endpoint, "--client-secret", "foobar", "--client-id", "foobarbaz"}}, {args: []string{"help", "migrate", "sql"}}, - {args: []string{"help", "migrate", "ladon", "0.6.0"}}, {args: []string{"version"}}, - {args: []string{"token", "flush"}}, - {args: []string{"token", "user", "--no-open"}, wait: func() bool { - time.Sleep(time.Millisecond * 10) - return false - }}, + {args: []string{"token", "flush", "--endpoint", endpoint}}, } { - c.args = append(c.args, []string{"--skip-tls-verify", "--config", path}...) + c.args = append(c.args, []string{"--skip-tls-verify"}...) RootCmd.SetArgs(c.args) t.Run(fmt.Sprintf("command=%v", c.args), func(t *testing.T) { diff --git a/cmd/host.go b/cmd/serve.go similarity index 90% rename from cmd/host.go rename to cmd/serve.go index f3b248cda35..66cb094cbd1 100644 --- a/cmd/host.go +++ b/cmd/serve.go @@ -25,9 +25,9 @@ import ( "github.com/spf13/cobra" ) -// hostCmd represents the host command -var hostCmd = &cobra.Command{ - Use: "host", +// serveCmd represents the host command +var serveCmd = &cobra.Command{ + Use: "serve", Short: "Start the HTTP/2 host service", Long: `Starts all HTTP/2 APIs and connects to a database backend. @@ -198,19 +198,19 @@ DEBUG CONTROLS } func init() { - RootCmd.AddCommand(hostCmd) + RootCmd.AddCommand(serveCmd) // Here you will define your flags and configuration settings. // Cobra supports Persistent Flags which will work for this command // and all subcommands, e.g.: - // hostCmd.PersistentFlags().String("foo", "", "A help for foo") + // serveCmd.PersistentFlags().String("foo", "", "A help for foo") // Cobra supports local flags which will only run when this command // is called directly, e.g.: - hostCmd.Flags().BoolVar(&c.ForceHTTP, "dangerous-force-http", false, "Disable HTTP/2 over TLS (HTTPS) and serve HTTP instead. Never use this in production.") - hostCmd.Flags().Bool("dangerous-auto-logon", false, "Stores the root credentials in ~/.hydra.yml. Do not use in production.") - hostCmd.Flags().Bool("disable-telemetry", false, "Disable telemetry collection and sharing - for more information please visit https://ory.gitbooks.io/hydra/content/telemetry.html") - hostCmd.Flags().String("https-tls-key-path", "", "Path to the key file for HTTP/2 over TLS (https). You can set HTTPS_TLS_KEY_PATH or HTTPS_TLS_KEY instead.") - hostCmd.Flags().String("https-tls-cert-path", "", "Path to the certificate file for HTTP/2 over TLS (https). You can set HTTPS_TLS_CERT_PATH or HTTPS_TLS_CERT instead.") + serveCmd.Flags().BoolVar(&c.ForceHTTP, "dangerous-force-http", false, "Disable HTTP/2 over TLS (HTTPS) and serve HTTP instead. Never use this in production.") + //serveCmd.Flags().Bool("dangerous-auto-logon", false, "Stores the root credentials in ~/.hydra.yml. Do not use in production.") + serveCmd.Flags().Bool("disable-telemetry", false, "Disable telemetry collection and sharing - for more information please visit https://ory.gitbooks.io/hydra/content/telemetry.html") + serveCmd.Flags().String("https-tls-key-path", "", "Path to the key file for HTTP/2 over TLS (https). You can set HTTPS_TLS_KEY_PATH or HTTPS_TLS_KEY instead.") + serveCmd.Flags().String("https-tls-cert-path", "", "Path to the certificate file for HTTP/2 over TLS (https). You can set HTTPS_TLS_CERT_PATH or HTTPS_TLS_CERT instead.") } diff --git a/cmd/server/handler.go b/cmd/server/handler.go index e243f04124f..d2b7f19bb2e 100644 --- a/cmd/server/handler.go +++ b/cmd/server/handler.go @@ -89,18 +89,6 @@ func RunHost(c *config.Config) func(cmd *cobra.Command, args []string) { } } - if c.ClusterURL == "" { - proto := "https" - if c.ForceHTTP { - proto = "http" - } - host := "localhost" - if c.BindHost != "" { - host = c.BindHost - } - c.ClusterURL = fmt.Sprintf("%s://%s:%d", proto, host, c.BindPort) - } - n := negroni.New() if ok, _ := cmd.Flags().GetBool("disable-telemetry"); !ok && os.Getenv("DISABLE_TELEMETRY") != "1" { diff --git a/cmd/token.go b/cmd/token.go index 5df4d154f07..7cd25ae0c26 100644 --- a/cmd/token.go +++ b/cmd/token.go @@ -33,5 +33,5 @@ var tokenCmd = &cobra.Command{ func init() { RootCmd.AddCommand(tokenCmd) //tokenCmd.PersistentFlags().Bool("dry", false, "do not execute the command but show the corresponding curl command instead") - tokenCmd.PersistentFlags().Bool("fake-tls-termination", false, `fake tls termination by adding "X-Forwarded-Proto: https"" to http headers`) + tokenCmd.PersistentFlags().Bool("fake-tls-termination", false, `fake tls termination by adding "X-Forwarded-Proto: https" to http headers`) } diff --git a/cmd/token_client.go b/cmd/token_client.go index 65e92041c33..96acc77a066 100644 --- a/cmd/token_client.go +++ b/cmd/token_client.go @@ -26,6 +26,7 @@ import ( "fmt" "net/http" "net/url" + "os" "github.com/ory/go-convenience/urlx" "github.com/ory/hydra/pkg" @@ -75,12 +76,20 @@ var tokenClientCmd = &cobra.Command{ scopes, _ := cmd.Flags().GetStringSlice("scopes") - cu, err := url.Parse(c.ClusterURL) - pkg.Must(err, `Unable to parse cluster url ("%s"): %s`, c.ClusterURL, err) + cu, err := url.Parse(c.GetClusterURLWithoutTailingSlash(cmd)) + pkg.Must(err, `Unable to parse cluster url ("%s"): %s`, c.GetClusterURLWithoutTailingSlash(cmd), err) + + clientID, _ := cmd.Flags().GetString("client-id") + clientSecret, _ := cmd.Flags().GetString("client-secret") + if clientID == "" || clientSecret == "" { + fmt.Print(cmd.UsageString()) + fmt.Println("Please provide a Client ID and Client Secret using flags --client-id and --client-secret, or environment variables OAUTH2_CLIENT_ID and OAUTH2_CLIENT_SECRET.") + return + } oauthConfig := clientcredentials.Config{ - ClientID: c.ClientID, - ClientSecret: c.ClientSecret, + ClientID: clientID, + ClientSecret: clientSecret, TokenURL: urlx.AppendPaths(cu, "/oauth2/token").String(), Scopes: scopes, } @@ -91,7 +100,7 @@ var tokenClientCmd = &cobra.Command{ if verbose, _ := cmd.Flags().GetBool("verbose"); verbose { fmt.Printf("%+v\n", t) } else { - fmt.Printf("%s\n", t) + fmt.Printf("%s\n", t.AccessToken) } }, } @@ -99,6 +108,10 @@ var tokenClientCmd = &cobra.Command{ func init() { tokenCmd.AddCommand(tokenClientCmd) - tokenClientCmd.Flags().StringSlice("scopes", []string{"hydra", "hydra.*"}, "User a specific set of scopes") + tokenClientCmd.Flags().StringSlice("scopes", []string{}, "OAuth2 scope to request") tokenClientCmd.Flags().BoolP("verbose", "v", false, "Toggle verbose output mode") + tokenClientCmd.Flags().String("client-id", os.Getenv("OAUTH2_CLIENT_ID"), "Use the provided OAuth 2.0 Client ID, defaults to environment variable OAUTH2_CLIENT_ID") + tokenClientCmd.Flags().String("client-secret", os.Getenv("OAUTH2_CLIENT_SECRET"), "Use the provided OAuth 2.0 Client Secret, defaults to environment variable OAUTH2_CLIENT_SECRET") + tokenClientCmd.PersistentFlags().String("endpoint", os.Getenv("HYDRA_URL"), "Set the URL where ORY Hydra is hosted, defaults to environment variable HYDRA_URL") + } diff --git a/cmd/token_flush.go b/cmd/token_flush.go index 69ac357c942..76f40f04a96 100644 --- a/cmd/token_flush.go +++ b/cmd/token_flush.go @@ -21,6 +21,7 @@ package cmd import ( + "os" "time" "github.com/spf13/cobra" @@ -37,4 +38,6 @@ func init() { tokenCmd.AddCommand(tokenFlushCmd) tokenFlushCmd.Flags().Duration("min-age", time.Duration(0), "Skip removing tokens which do not satisfy the minimum age (1s, 1m, 1h, 1d)") + tokenFlushCmd.Flags().String("access-token", os.Getenv("OAUTH2_ACCESS_TOKEN"), "Set an access token to be used in the Authorization header, defaults to environment variable ACCESS_TOKEN") + tokenFlushCmd.Flags().String("endpoint", os.Getenv("HYDRA_URL"), "Set the URL where ORY Hydra is hosted, defaults to environment variable HYDRA_URL") } diff --git a/cmd/token_validate.go b/cmd/token_introspect.go similarity index 56% rename from cmd/token_validate.go rename to cmd/token_introspect.go index b919f2094d0..2dc5753513b 100644 --- a/cmd/token_validate.go +++ b/cmd/token_introspect.go @@ -21,17 +21,22 @@ package cmd import ( + "os" + "github.com/spf13/cobra" ) // validateCmd represents the validate command -var tokenValidatorCmd = &cobra.Command{ - Use: "validate ", - Short: "Check if an access token is valid", - Run: cmdHandler.Warden.IsAuthorized, +var tokenIntrospectCmd = &cobra.Command{ + Use: "introspect ", + Short: "Introspect an access or refresh token", + Run: cmdHandler.Introspection.Introspect, } func init() { - tokenCmd.AddCommand(tokenValidatorCmd) - tokenValidatorCmd.Flags().StringSlice("scopes", []string{""}, "Additionally check if scope was granted") + tokenCmd.AddCommand(tokenIntrospectCmd) + tokenIntrospectCmd.Flags().StringSlice("scopes", []string{}, "Additionally check if scope was granted") + + tokenIntrospectCmd.Flags().String("client-id", os.Getenv("OAUTH2_CLIENT_ID"), "Use the provided OAuth 2.0 Client ID, defaults to environment variable OAUTH2_CLIENT_ID") + tokenIntrospectCmd.Flags().String("client-secret", os.Getenv("OAUTH2_CLIENT_SECRET"), "Use the provided OAuth 2.0 Client Secret, defaults to environment variable OAUTH2_CLIENT_SECRET") } diff --git a/cmd/token_revoke.go b/cmd/token_revoke.go index 498c08bdabc..14c09244a89 100644 --- a/cmd/token_revoke.go +++ b/cmd/token_revoke.go @@ -21,6 +21,8 @@ package cmd import ( + "os" + "github.com/spf13/cobra" ) @@ -33,4 +35,7 @@ var tokenRevokeCmd = &cobra.Command{ func init() { tokenCmd.AddCommand(tokenRevokeCmd) + tokenRevokeCmd.Flags().String("client-id", os.Getenv("OAUTH2_CLIENT_ID"), "Use the provided OAuth 2.0 Client ID, defaults to environment variable OAUTH2_CLIENT_ID") + tokenRevokeCmd.Flags().String("client-secret", os.Getenv("OAUTH2_CLIENT_SECRET"), "Use the provided OAuth 2.0 Client Secret, defaults to environment variable OAUTH2_CLIENT_SECRET") + tokenRevokeCmd.Flags().String("endpoint", os.Getenv("HYDRA_URL"), "Set the URL where ORY Hydra is hosted, defaults to environment variable HYDRA_URL") } diff --git a/cmd/token_user.go b/cmd/token_user.go index f2f78ff8240..e66fb0d50dd 100644 --- a/cmd/token_user.go +++ b/cmd/token_user.go @@ -26,6 +26,7 @@ import ( "fmt" "net/http" "net/url" + "os" "time" "github.com/julienschmidt/httprouter" @@ -51,31 +52,31 @@ var tokenUserCmd = &cobra.Command{ } scopes, _ := cmd.Flags().GetStringSlice("scope") - clientId, _ := cmd.Flags().GetString("id") - clientSecret, _ := cmd.Flags().GetString("secret") redirectUrl, _ := cmd.Flags().GetString("redirect") backend, _ := cmd.Flags().GetString("token-url") frontend, _ := cmd.Flags().GetString("auth-url") - if clientId == "" { - clientId = c.ClientID - } - if clientSecret == "" { - clientSecret = c.ClientSecret + clientID, _ := cmd.Flags().GetString("client-id") + clientSecret, _ := cmd.Flags().GetString("client-secret") + if clientID == "" || clientSecret == "" { + fmt.Print(cmd.UsageString()) + fmt.Println("Please provide a Client ID and Client Secret using flags --client-id and --client-secret, or environment variables OAUTH2_CLIENT_ID and OAUTH2_CLIENT_SECRET.") + return } + if backend == "" { - bu, err := url.Parse(c.ClusterURL) - pkg.Must(err, `Unable to parse cluster url ("%s"): %s`, c.ClusterURL, err) + bu, err := url.Parse(c.GetClusterURLWithoutTailingSlash(cmd)) + pkg.Must(err, `Unable to parse cluster url ("%s"): %s`, c.GetClusterURLWithoutTailingSlash(cmd), err) backend = urlx.AppendPaths(bu, "/oauth2/token").String() } if frontend == "" { - fu, err := url.Parse(c.ClusterURL) - pkg.Must(err, `Unable to parse cluster url ("%s"): %s`, c.ClusterURL, err) + fu, err := url.Parse(c.GetClusterURLWithoutTailingSlash(cmd)) + pkg.Must(err, `Unable to parse cluster url ("%s"): %s`, c.GetClusterURLWithoutTailingSlash(cmd), err) frontend = urlx.AppendPaths(fu, "/oauth2/auth").String() } conf := oauth2.Config{ - ClientID: clientId, + ClientID: clientID, ClientSecret: clientSecret, Endpoint: oauth2.Endpoint{ TokenURL: backend, @@ -158,10 +159,13 @@ var tokenUserCmd = &cobra.Command{ func init() { tokenCmd.AddCommand(tokenUserCmd) tokenUserCmd.Flags().Bool("no-open", false, "Do not open the browser window automatically") - tokenUserCmd.Flags().StringSlice("scope", []string{"hydra", "offline", "openid"}, "Force scopes") - tokenUserCmd.Flags().String("id", "", "Force a client id, defaults to value from config file") - tokenUserCmd.Flags().String("secret", "", "Force a client secret, defaults to value from config file") + tokenUserCmd.Flags().StringSlice("scope", []string{"offline", "openid"}, "Request OAuth2 scope") + + tokenUserCmd.Flags().String("client-id", os.Getenv("OAUTH2_CLIENT_ID"), "Use the provided OAuth 2.0 Client ID, defaults to environment variable OAUTH2_CLIENT_ID") + tokenUserCmd.Flags().String("client-secret", os.Getenv("OAUTH2_CLIENT_SECRET"), "Use the provided OAuth 2.0 Client Secret, defaults to environment variable OAUTH2_CLIENT_SECRET") + tokenUserCmd.Flags().String("redirect", "http://localhost:4445/callback", "Force a redirect url") - tokenUserCmd.Flags().String("auth-url", c.ClusterURL, "Force the authorization url. The authorization url is the URL that the user will open in the browser, defaults to the cluster url value from config file") - tokenUserCmd.Flags().String("token-url", c.ClusterURL, "Force a token url. The token url is used to exchange the auth code, defaults to the cluster url value from config file") + tokenUserCmd.Flags().String("auth-url", os.Getenv("HYDRA_URL"), "Usually it is enough to specify the `endpoint` flag, but if you want to force the authorization url, use this flag") + tokenUserCmd.Flags().String("token-url", os.Getenv("HYDRA_URL"), "Usually it is enough to specify the `endpoint` flag, but if you want to force the token url, use this flag") + tokenUserCmd.PersistentFlags().String("endpoint", os.Getenv("HYDRA_URL"), "Set the URL where ORY Hydra is hosted, defaults to environment variable HYDRA_URL") } diff --git a/config/config.go b/config/config.go index 0f2e21aff86..fae043ab5fb 100644 --- a/config/config.go +++ b/config/config.go @@ -21,15 +21,12 @@ package config import ( - "context" "crypto/sha256" - "crypto/tls" "fmt" "io/ioutil" "net" "net/http" "net/url" - "os" "strings" "time" @@ -44,17 +41,12 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" - "golang.org/x/oauth2" - "golang.org/x/oauth2/clientcredentials" "gopkg.in/yaml.v2" ) type Config struct { // These are used by client commands - ClusterURL string `mapstructure:"CLUSTER_URL" yaml:"cluster_url"` - ClientID string `mapstructure:"CLIENT_ID" yaml:"client_id,omitempty"` - ClientSecret string `mapstructure:"CLIENT_SECRET" yaml:"client_secret,omitempty"` - SignedUpForNewsletter bool `yaml:"signed_up_for_newsletter,omitempty"` + EndpointURL string `mapstructure:"HYDRA_URL" yaml:"-"` // These are used by the host command BindPort int `mapstructure:"PORT" yaml:"-"` @@ -94,8 +86,11 @@ type Config struct { systemSecret []byte `yaml:"-"` } -func (c *Config) GetClusterURLWithoutTailingSlash() string { - return strings.TrimRight(c.ClusterURL, "/") +func (c *Config) GetClusterURLWithoutTailingSlash(cmd *cobra.Command) string { + if endpoint, _ := cmd.Flags().GetString("endpoint"); endpoint != "" { + return strings.TrimRight(endpoint, "/") + } + return strings.TrimRight(c.EndpointURL, "/") } func (c *Config) GetScopeStrategy() fosite.ScopeStrategy { @@ -275,7 +270,7 @@ func (c *Config) Context() *Context { func (c *Config) Resolve(join ...string) *url.URL { if c.cluster == nil { - cluster, err := url.Parse(c.ClusterURL) + cluster, err := url.Parse(c.EndpointURL) c.cluster = cluster pkg.Must(err, "Could not parse cluster url: %s", err) } @@ -287,65 +282,6 @@ func (c *Config) Resolve(join ...string) *url.URL { return urlx.AppendPaths(c.cluster, join...) } -type transporter struct { - *http.Transport - FakeTLSTermination bool -} - -func (t *transporter) RoundTrip(req *http.Request) (*http.Response, error) { - if t.FakeTLSTermination { - req.Header.Set("X-Forwarded-Proto", "https") - } - - return t.Transport.RoundTrip(req) -} - -func (c *Config) OAuth2Client(cmd *cobra.Command) *http.Client { - if c.oauth2Client != nil { - return c.oauth2Client - } - - cu, err := url.Parse(c.ClusterURL) - pkg.Must(err, `Unable to parse cluster url ("%s"): %s`, c.ClusterURL, err) - - oauthConfig := clientcredentials.Config{ - ClientID: c.ClientID, - ClientSecret: c.ClientSecret, - TokenURL: urlx.AppendPaths(cu, "/oauth2/token").String(), - Scopes: []string{"hydra", "hydra.*"}, - } - - fakeTlsTermination, _ := cmd.Flags().GetBool("fake-tls-termination") - ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{ - Transport: &transporter{ - FakeTLSTermination: fakeTlsTermination, - Transport: &http.Transport{}, - }, - }) - - if ok, _ := cmd.Flags().GetBool("skip-tls-verify"); ok { - // fmt.Println("Warning: Skipping TLS Certificate Verification.") - ctx = context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{ - Transport: &transporter{ - FakeTLSTermination: fakeTlsTermination, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - }, - }) - } - - c.oauth2Client = oauthConfig.Client(ctx) - if _, err := c.oauth2Client.Get(c.ClusterURL); err != nil { - fmt.Printf("Could not authenticate, because: %s\n", err) - fmt.Println("This can have multiple reasons, like a wrong cluster or wrong credentials. To resolve this, run `hydra connect`.") - fmt.Println("You can disable TLS verification using the `--skip-tls-verify` flag.") - os.Exit(1) - } - - return c.oauth2Client -} - func (c *Config) GetCookieSecret() []byte { if c.CookieSecret != "" { return []byte(c.CookieSecret) diff --git a/config/config_test.go b/config/config_test.go index 376cc77fffe..d1e824c8e47 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -81,14 +81,14 @@ func TestSystemSecret(t *testing.T) { } func TestResolve(t *testing.T) { - c := &Config{ClusterURL: "https://localhost:1234"} + c := &Config{EndpointURL: "https://localhost:1234"} assert.Equal(t, c.Resolve("foo", "bar").String(), "https://localhost:1234/foo/bar") assert.Equal(t, c.Resolve("/foo", "/bar").String(), "https://localhost:1234/foo/bar") - c = &Config{ClusterURL: "https://localhost:1234/"} + c = &Config{EndpointURL: "https://localhost:1234/"} assert.Equal(t, c.Resolve("/foo", "/bar").String(), "https://localhost:1234/foo/bar") - c = &Config{ClusterURL: "https://localhost:1234/bar"} + c = &Config{EndpointURL: "https://localhost:1234/bar"} assert.Equal(t, c.Resolve("/foo", "/bar").String(), "https://localhost:1234/bar/foo/bar") } diff --git a/docs/api.swagger.json b/docs/api.swagger.json index 2301d279777..0c662fe490d 100644 --- a/docs/api.swagger.json +++ b/docs/api.swagger.json @@ -1246,6 +1246,22 @@ } }, "definitions": { + "AuthenticationSession": { + "type": "object", + "properties": { + "AuthenticatedAt": { + "type": "string", + "format": "date-time" + }, + "ID": { + "type": "string" + }, + "Subject": { + "type": "string" + } + }, + "x-go-package": "github.com/ory/hydra/consent" + }, "Handler": { "type": "object", "properties": { @@ -1349,22 +1365,6 @@ "x-go-name": "HandledAuthenticationRequest", "x-go-package": "github.com/ory/hydra/consent" }, - "authenticationSession": { - "type": "object", - "properties": { - "AuthenticatedAt": { - "type": "string", - "format": "date-time" - }, - "ID": { - "type": "string" - }, - "Subject": { - "type": "string" - } - }, - "x-go-package": "github.com/ory/hydra/consent" - }, "completedRequest": { "type": "object", "title": "The response payload sent when accepting or rejecting a login or consent request.", diff --git a/sdk/php/swagger/lib/Model/AuthenticationSession.php b/sdk/php/swagger/lib/Model/AuthenticationSession.php index 10366c04b16..7f11e2e9a6f 100644 --- a/sdk/php/swagger/lib/Model/AuthenticationSession.php +++ b/sdk/php/swagger/lib/Model/AuthenticationSession.php @@ -47,7 +47,7 @@ class AuthenticationSession implements ArrayAccess * The original name of the model. * @var string */ - protected static $swaggerModelName = 'authenticationSession'; + protected static $swaggerModelName = 'AuthenticationSession'; /** * Array of property to type mappings. Used for (de)serialization