Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow using ory tunnel/proxy with project ID as well #311

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@
"override_return_to": false
},
"selfservice": {
"allowed_return_urls": [

],
"allowed_return_urls": [],
"default_browser_return_url": "/ui/welcome",
"flows": {
"error": {
Expand Down
3 changes: 2 additions & 1 deletion cmd/cloudx/client/fixtures/update_project/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"lifespan": "30m0s",
"notify_unknown_recipients": false,
"ui_url": "/ui/recovery",
"use": "code"
"use": "code",
"notify_unknown_recipients": false
},
"registration": {
"after": {
Expand Down
16 changes: 12 additions & 4 deletions cmd/cloudx/client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@ type AuthProject struct {
Slug string `json:"slug"`
}

var ErrNoConfig = stderrs.New("no ory configuration file present")
var ErrNoConfigQuiet = stderrs.New("please run `ory auth` to initialize your configuration or remove the `--quiet` flag")
var (
ErrNoConfig = stderrs.New("no ory configuration file present")
ErrNoConfigQuiet = stderrs.New("please run `ory auth` to initialize your configuration or remove the `--quiet` flag")
)

func getConfigPath(cmd *cobra.Command) (string, error) {
path, err := os.UserHomeDir()
Expand All @@ -102,6 +104,10 @@ func getConfigPath(cmd *cobra.Command) (string, error) {
), nil
}

type Command interface {
GetProject(string) (*cloud.Project, error)
}
Benehiko marked this conversation as resolved.
Show resolved Hide resolved

type CommandHelper struct {
Ctx context.Context
VerboseWriter io.Writer
Expand All @@ -114,6 +120,8 @@ type CommandHelper struct {
PwReader passwordReader
}

var _ Command = new(CommandHelper)

type PasswordReader struct{}

// NewCommandHelper creates a new CommandHelper instance which handles cobra CLI commands.
Expand All @@ -123,12 +131,12 @@ func NewCommandHelper(cmd *cobra.Command) (*CommandHelper, error) {
return nil, err
}

var out = cmd.OutOrStdout()
out := cmd.OutOrStdout()
if flagx.MustGetBool(cmd, cmdx.FlagQuiet) {
out = io.Discard
}

var outErr = cmd.ErrOrStderr()
outErr := cmd.ErrOrStderr()
if flagx.MustGetBool(cmd, cmdx.FlagQuiet) {
outErr = io.Discard
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/cloudx/client/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func TestCommandHelper(t *testing.T) {
_, _ = r.WriteString(email + "\n") // Email: FakeEmail() [RETRY]
cmd.Stdin = bufio.NewReader(&r)

var retry = false
retry := false
cmd.PwReader = func() ([]byte, error) {
if retry {
return []byte(password), nil
Expand Down Expand Up @@ -334,7 +334,7 @@ func TestCommandHelper(t *testing.T) {
"project.services.identity.config.session.cookie.domain",
"project.services.identity.config.session.cookie.name",
"project.services.identity.config.cookies.domain",
"project.services.identity.config.selfservice.allowed_return_urls.0",
"project.services.identity.config.selfservice.allowed_return_urls",
"project.services.oauth2.config.urls.self",
"project.services.oauth2.config.serve.cookies.domain",
"project.services.oauth2.config.serve.cookies.names",
Expand Down
16 changes: 16 additions & 0 deletions cmd/cloudx/client/mock_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package client

import cloud "github.com/ory/client-go"

type MockCommandHelper struct {
Project *cloud.Project
}

var _ Command = new(MockCommandHelper)

func (n *MockCommandHelper) GetProject(projectId string) (*cloud.Project, error) {
return n.Project, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@
"override_return_to": false
},
"selfservice": {
"allowed_return_urls": [

],
"allowed_return_urls": [],
"default_browser_return_url": "/ui/welcome",
"flows": {
"error": {
Expand Down
10 changes: 5 additions & 5 deletions cmd/cloudx/project/fixtures/update-kratos/json/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
}
}
},
"oauth2_provider": {
"override_return_to": false
},
"feature_flags": {
"cacheable_sessions": false
},
Expand All @@ -79,9 +82,6 @@
}
]
},
"oauth2_provider": {
"override_return_to": false
},
"selfservice": {
"allowed_return_urls": [],
"default_browser_return_url": "/ui/welcome",
Expand Down Expand Up @@ -120,7 +120,7 @@
},
"enabled": true,
"lifespan": "30m0s",
"notify_unknown_recipients": false,
"notify_unkown_recipients": false,
"ui_url": "/ui/recovery",
"use": "code"
},
Expand Down Expand Up @@ -183,7 +183,7 @@
},
"enabled": true,
"lifespan": "30m0s",
"notify_unknown_recipients": false,
"notify_unkown_recipients": false,
"ui_url": "/ui/verification",
"use": "code"
}
Expand Down
10 changes: 10 additions & 0 deletions cmd/cloudx/project/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ func TestUpdateProject(t *testing.T) {
"services.oauth2.config.serve.cookies.domain",
"services.oauth2.config.urls.self",
"services.oauth2.config.oauth2.session",
"services.identity.config.selfservice.allowed_return_urls",

// THERE IS A LIMITATION IN ASSERTX THAT DOES NOT ALLOW A DEPTH MORE THAN 5
// THAT IS WHY WE IGNORE THIS KEY HERE.
// TODO: FIX THIS
"services.identity.config.selfservice.flows.login.after.webauthn.hooks",
"services.identity.config.selfservice.flows.registration.after.webauthn.hooks",

// for kratos cmd
"serve",
"cookies",
Expand All @@ -97,6 +105,7 @@ func TestUpdateProject(t *testing.T) {
"session.cookie",
"courier.smtp.from_name",
"selfservice.allowed_return_urls.0",

// for keto cmd
// for hydra cmd
"serve.cookies.names",
Expand All @@ -118,6 +127,7 @@ func TestUpdateProject(t *testing.T) {
"services.oauth2.config.serve.cookies.names",
"services.oauth2.config.serve.cookies.domain",
"services.oauth2.config.urls.self",
"services.identity.config.selfservice.allowed_return_urls",
// for kratos cmd
"serve.public.base_url",
"serve.admin.base_url",
Expand Down
60 changes: 36 additions & 24 deletions cmd/cloudx/proxy/cmd_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ An example payload of the JSON Web Token is:
return err
}

oryURL, err := getEndpointURL(cmd)
projectSlugId := getProjectSlugId(cmd)

oryURL, err := getEndpointURL(cmd, projectSlugId)
if err != nil {
return err
}
Expand All @@ -205,23 +207,24 @@ An example payload of the JSON Web Token is:
return err
}

conf := &config{
port: flagx.MustGetInt(cmd, PortFlag),
noJWT: flagx.MustGetBool(cmd, WithoutJWTFlag),
noOpen: !flagx.MustGetBool(cmd, OpenFlag),
upstream: args[0],
cookieDomain: flagx.MustGetString(cmd, CookieDomainFlag),
publicURL: selfURL,
oryURL: oryURL,
pathPrefix: "/.ory",
defaultRedirectTo: redirectURL,
isDev: flagx.MustGetBool(cmd, DevFlag),
isDebug: flagx.MustGetBool(cmd, DebugFlag),
rewriteHost: flagx.MustGetBool(cmd, RewriteHostFlag),
corsOrigins: origins,
conf := &ProxyConfig{
Port: flagx.MustGetInt(cmd, PortFlag),
NoJWT: flagx.MustGetBool(cmd, WithoutJWTFlag),
NoOpen: !flagx.MustGetBool(cmd, OpenFlag),
Upstream: args[0],
CookieDomain: flagx.MustGetString(cmd, CookieDomainFlag),
PublicURL: selfURL,
OryURL: oryURL,
PathPrefix: "/.ory",
DefaultRedirectTo: redirectURL,
IsDev: flagx.MustGetBool(cmd, DevFlag),
IsDebug: flagx.MustGetBool(cmd, DebugFlag),
RewriteHost: flagx.MustGetBool(cmd, RewriteHostFlag),
CorsOrigins: origins,
ProjectSlugId: projectSlugId,
}
Benehiko marked this conversation as resolved.
Show resolved Hide resolved

return run(cmd, conf, version, "cloud")
return Run(cmd, conf, version, "cloud")
},
}

Expand All @@ -243,22 +246,31 @@ An example payload of the JSON Web Token is:
return proxyCmd
}

const envVarSlug = "ORY_PROJECT_SLUG"
const envVarSDK = "ORY_SDK_URL"
const envVarKratos = "ORY_KRATOS_URL"
const (
envVarSlug = "ORY_PROJECT_SLUG"
envVarSDK = "ORY_SDK_URL"
envVarKratos = "ORY_KRATOS_URL"
)

func getEndpointURL(cmd *cobra.Command) (*url.URL, error) {
var target string
func getProjectSlugId(cmd *cobra.Command) string {
if slug := os.Getenv(envVarSlug); len(slug) > 0 {
return slug
} else if slug := flagx.MustGetString(cmd, ProjectFlag); len(slug) > 0 {
return slug
}
return ""
}

func getEndpointURL(cmd *cobra.Command, slug string) (*url.URL, error) {
var target string
if slug != "" {
target = fmt.Sprintf("https://%s.projects.oryapis.com/", slug)
} else if url := stringsx.Coalesce(os.Getenv(envVarSDK), os.Getenv(envVarKratos)); len(url) > 0 {
target = url
} else if slug := flagx.MustGetString(cmd, ProjectFlag); len(slug) > 0 {
target = fmt.Sprintf("https://%s.projects.oryapis.com/", slug)
}

if len(target) == 0 {
return nil, errors.Errorf("Please provide your project slug using the --%s flag or the %s environment variable.", ProjectFlag, envVarSlug)
return nil, errors.Errorf("Please provide your project slug or project id using the --%s flag or the %s environment variable.", ProjectFlag, envVarSlug)
}

upstream, err := url.ParseRequestURI(target)
Expand Down
9 changes: 5 additions & 4 deletions cmd/cloudx/proxy/cmd_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ func newEndpointCmd(def string) *cobra.Command {

func TestGetEndpointURL(t *testing.T) {
t.Run("should fail if no project is set", func(t *testing.T) {
_, err := getEndpointURL(newEndpointCmd(""))
cmd := newEndpointCmd("")
_, err := getEndpointURL(cmd, getProjectSlugId(cmd))
require.Error(t, err)
})

t.Run("should return the right value from the flag", func(t *testing.T) {
expected := "someslug"
cmd := newEndpointCmd(expected)
actual, err := getEndpointURL(cmd)
actual, err := getEndpointURL(cmd, getProjectSlugId(cmd))
require.NoError(t, err)
assert.Equal(t, "https://"+expected+".projects.oryapis.com/", actual.String())
})
Expand All @@ -39,7 +40,7 @@ func TestGetEndpointURL(t *testing.T) {
t.Setenv(envVarSlug, expected)
cmd := newEndpointCmd("not-someslug")
cmd.SetErr(&b)
actual, err := getEndpointURL(cmd)
actual, err := getEndpointURL(cmd, getProjectSlugId(cmd))
require.Error(t, err)
assert.Nil(t, actual)
assert.Contains(t, b.String(), "Attention! We found multiple sources for the project slug. Please clean up environment variables and flags to ensure that the correct value is being used. Found values:\n\n\t--project=not-someslug\n\tORY_PROJECT_SLUG=someslug")
Expand All @@ -51,7 +52,7 @@ func TestGetEndpointURL(t *testing.T) {
t.Setenv(envVarSDK, expected)
cmd := newEndpointCmd("not-someslug")
cmd.SetErr(&b)
_, err := getEndpointURL(cmd)
_, err := getEndpointURL(cmd, getProjectSlugId(cmd))
require.Error(t, err)
})
}
35 changes: 19 additions & 16 deletions cmd/cloudx/proxy/cmd_tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ TO use a different default redirect URL, use the `+"`"+`--default-redirect-url`+
return err
}

oryURL, err := getEndpointURL(cmd)
projectSlugId := getProjectSlugId(cmd)

oryURL, err := getEndpointURL(cmd, projectSlugId)
if err != nil {
return err
}
Expand All @@ -142,23 +144,24 @@ TO use a different default redirect URL, use the `+"`"+`--default-redirect-url`+
return err
}

conf := &config{
port: flagx.MustGetInt(cmd, PortFlag),
noJWT: true,
noOpen: true,
upstream: oryURL.String(),
cookieDomain: flagx.MustGetString(cmd, CookieDomainFlag),
publicURL: selfURL,
oryURL: oryURL,
pathPrefix: "",
isTunnel: true,
defaultRedirectTo: redirectURL,
isDev: flagx.MustGetBool(cmd, DevFlag),
isDebug: flagx.MustGetBool(cmd, DebugFlag),
corsOrigins: origins,
conf := &ProxyConfig{
Port: flagx.MustGetInt(cmd, PortFlag),
NoJWT: true,
NoOpen: true,
Upstream: oryURL.String(),
CookieDomain: flagx.MustGetString(cmd, CookieDomainFlag),
PublicURL: selfURL,
OryURL: oryURL,
PathPrefix: "",
IsTunnel: true,
DefaultRedirectTo: redirectURL,
IsDev: flagx.MustGetBool(cmd, DevFlag),
IsDebug: flagx.MustGetBool(cmd, DebugFlag),
CorsOrigins: origins,
ProjectSlugId: projectSlugId,
}

return run(cmd, conf, version, "cloud")
return Run(cmd, conf, version, "cloud")
},
}

Expand Down