Skip to content

Commit 4ff9a27

Browse files
committed
feat: implement improved resource format
1 parent 002637e commit 4ff9a27

26 files changed

+1041
-89
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
{
2+
"cookies": {
3+
"path": "/",
4+
"same_site": "Lax"
5+
},
6+
"courier": {
7+
"smtp": {
8+
},
9+
"templates": {
10+
"recovery": {
11+
"invalid": {
12+
"email": {
13+
"body": {}
14+
}
15+
},
16+
"valid": {
17+
"email": {
18+
"body": {}
19+
}
20+
}
21+
},
22+
"verification": {
23+
"invalid": {
24+
"email": {
25+
"body": {}
26+
}
27+
},
28+
"valid": {
29+
"email": {
30+
"body": {}
31+
}
32+
}
33+
}
34+
}
35+
},
36+
"identity": {
37+
"default_schema_id": "beb7e08859084ec71bc073694fff7c71eb0eb82593ca11b29068c1e02ad385c4fb8451cb3b58d8c9385ed955f31ab51b6d94e187ffea8d9ba817c12782ef5782",
38+
"schemas": [
39+
{
40+
"id": "preset://email",
41+
"url": "base64://ewogICIkaWQiOiAiaHR0cHM6Ly9zY2hlbWFzLm9yeS5zaC9wcmVzZXRzL2tyYXRvcy9pZGVudGl0eS5lbWFpbC5zY2hlbWEuanNvbiIsCiAgIiRzY2hlbWEiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwKICAidGl0bGUiOiAiUGVyc29uIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgImVtYWlsIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJmb3JtYXQiOiAiZW1haWwiLAogICAgICAgICAgInRpdGxlIjogIkUtTWFpbCIsCiAgICAgICAgICAib3J5LnNoL2tyYXRvcyI6IHsKICAgICAgICAgICAgImNyZWRlbnRpYWxzIjogewogICAgICAgICAgICAgICJwYXNzd29yZCI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgIndlYmF1dGhuIjogewogICAgICAgICAgICAgICAgImlkZW50aWZpZXIiOiB0cnVlCiAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAidG90cCI6IHsKICAgICAgICAgICAgICAgICJhY2NvdW50X25hbWUiOiB0cnVlCiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAicmVjb3ZlcnkiOiB7CiAgICAgICAgICAgICAgInZpYSI6ICJlbWFpbCIKICAgICAgICAgICAgfSwKICAgICAgICAgICAgInZlcmlmaWNhdGlvbiI6IHsKICAgICAgICAgICAgICAidmlhIjogImVtYWlsIgogICAgICAgICAgICB9CiAgICAgICAgICB9LAogICAgICAgICAgIm1heExlbmd0aCI6IDMyMAogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJlbWFpbCIKICAgICAgXSwKICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogZmFsc2UKICAgIH0KICB9Cn0K"
42+
},
43+
{
44+
"id": "beb7e08859084ec71bc073694fff7c71eb0eb82593ca11b29068c1e02ad385c4fb8451cb3b58d8c9385ed955f31ab51b6d94e187ffea8d9ba817c12782ef5782",
45+
"url": "https://storage.googleapis.com/bac-gcs-production/beb7e08859084ec71bc073694fff7c71eb0eb82593ca11b29068c1e02ad385c4fb8451cb3b58d8c9385ed955f31ab51b6d94e187ffea8d9ba817c12782ef5782.json"
46+
},
47+
{
48+
"id": "preset://username",
49+
"url": "base64://ewogICIkaWQiOiAiaHR0cHM6Ly9zY2hlbWFzLm9yeS5zaC9wcmVzZXRzL2tyYXRvcy9pZGVudGl0eS51c2VybmFtZS5zY2hlbWEuanNvbiIsCiAgIiRzY2hlbWEiOiAiaHR0cDovL2pzb24tc2NoZW1hLm9yZy9kcmFmdC0wNy9zY2hlbWEjIiwKICAidGl0bGUiOiAiUGVyc29uIiwKICAidHlwZSI6ICJvYmplY3QiLAogICJwcm9wZXJ0aWVzIjogewogICAgInRyYWl0cyI6IHsKICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgInVzZXJuYW1lIjogewogICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICJ0aXRsZSI6ICJVc2VybmFtZSIsCiAgICAgICAgICAibWF4TGVuZ3RoIjogMTAwLAogICAgICAgICAgIm9yeS5zaC9rcmF0b3MiOiB7CiAgICAgICAgICAgICJjcmVkZW50aWFscyI6IHsKICAgICAgICAgICAgICAicGFzc3dvcmQiOiB7CiAgICAgICAgICAgICAgICAiaWRlbnRpZmllciI6IHRydWUKICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICJ3ZWJhdXRobiI6IHsKICAgICAgICAgICAgICAgICJpZGVudGlmaWVyIjogdHJ1ZQogICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgInRvdHAiOiB7CiAgICAgICAgICAgICAgICAiYWNjb3VudF9uYW1lIjogdHJ1ZQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSwKICAgICAgInJlcXVpcmVkIjogWwogICAgICAgICJ1c2VybmFtZSIKICAgICAgXSwKICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogZmFsc2UKICAgIH0KICB9Cn0K"
50+
}
51+
]
52+
},
53+
"selfservice": {
54+
"allowed_return_urls": [],
55+
"default_browser_return_url": "/ui/welcome",
56+
"flows": {
57+
"error": {
58+
"ui_url": "/ui/custom-error"
59+
},
60+
"login": {
61+
"after": {
62+
"hooks": [],
63+
"oidc": {
64+
"hooks": []
65+
},
66+
"password": {
67+
"hooks": []
68+
},
69+
"webauthn": {
70+
"hooks": []
71+
}
72+
},
73+
"before": {
74+
"hooks": []
75+
},
76+
"lifespan": "30m0s",
77+
"ui_url": "/ui/custom-login"
78+
},
79+
"logout": {
80+
"after": {}
81+
},
82+
"recovery": {
83+
"after": {
84+
"hooks": []
85+
},
86+
"enabled": true,
87+
"lifespan": "30m0s",
88+
"ui_url": "/ui/recovery"
89+
},
90+
"registration": {
91+
"after": {
92+
"hooks": [],
93+
"oidc": {
94+
"hooks": [
95+
{
96+
"hook": "session"
97+
}
98+
]
99+
},
100+
"password": {
101+
"hooks": [
102+
{
103+
"hook": "session"
104+
}
105+
]
106+
},
107+
"webauthn": {
108+
"hooks": []
109+
}
110+
},
111+
"before": {
112+
"hooks": []
113+
},
114+
"enabled": true,
115+
"lifespan": "30m0s",
116+
"ui_url": "/ui/registration"
117+
},
118+
"settings": {
119+
"after": {
120+
"hooks": [],
121+
"password": {
122+
"hooks": []
123+
},
124+
"profile": {
125+
"hooks": []
126+
}
127+
},
128+
"lifespan": "30m0s",
129+
"privileged_session_max_age": "15m0s",
130+
"required_aal": "highest_available",
131+
"ui_url": "/ui/settings"
132+
},
133+
"verification": {
134+
"after": {
135+
"hooks": []
136+
},
137+
"enabled": true,
138+
"lifespan": "30m0s",
139+
"ui_url": "/ui/verification"
140+
}
141+
},
142+
"methods": {
143+
"link": {
144+
"config": {
145+
"base_url": "",
146+
"lifespan": "15m0s"
147+
},
148+
"enabled": true
149+
},
150+
"lookup_secret": {
151+
"enabled": false
152+
},
153+
"oidc": {
154+
"config": {
155+
"providers": []
156+
},
157+
"enabled": false
158+
},
159+
"password": {
160+
"config": {
161+
"haveibeenpwned_enabled": true,
162+
"identifier_similarity_check_enabled": true,
163+
"ignore_network_errors": true,
164+
"max_breaches": 1,
165+
"min_password_length": 8
166+
},
167+
"enabled": true
168+
},
169+
"profile": {
170+
"enabled": true
171+
},
172+
"totp": {
173+
"config": {
174+
"issuer": "afds"
175+
},
176+
"enabled": false
177+
},
178+
"webauthn": {
179+
"config": {
180+
"passwordless": false,
181+
"rp": {
182+
"display_name": "afds",
183+
"id": "upbeat-mclean-xjvqxk4i7e.projects.oryapis",
184+
"origin": "https://upbeat-mclean-xjvqxk4i7e.projects.oryapis"
185+
}
186+
},
187+
"enabled": false
188+
}
189+
}
190+
},
191+
"serve": {
192+
"admin": {
193+
"request_log": {
194+
"disable_for_health": true
195+
}
196+
},
197+
"public": {
198+
"cors": {
199+
"enabled": false
200+
},
201+
"request_log": {
202+
"disable_for_health": true
203+
}
204+
}
205+
},
206+
"session": {
207+
"cookie": {
208+
"path": "/",
209+
"persistent": false,
210+
"same_site": "Lax"
211+
},
212+
"lifespan": "72h0m0s",
213+
"whoami": {
214+
"required_aal": "aal1"
215+
}
216+
}
217+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
""

cloudx/.snapshots/TestUpdateProject-is_able_to_update_a_project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
},
142142
"enabled": true,
143143
"lifespan": "30m0s",
144-
"ui_url": "/ui/verification"
144+
"ui_url": "/ui/custom-verification"
145145
}
146146
},
147147
"methods": {

cloudx/cmd_get.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ func NewGetCmd() *cobra.Command {
1414
Short: fmt.Sprintf("Get a resource"),
1515
}
1616
cmd.AddCommand(NewGetProjectCmd())
17+
cmd.AddCommand(NewGetKratosConfigCmd())
1718
RegisterConfigFlag(cmd.PersistentFlags())
1819
RegisterYesFlag(cmd.PersistentFlags())
1920
cmdx.RegisterNoiseFlags(cmd.PersistentFlags())

cloudx/cmd_get_kratos_config.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package cloudx
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/ory/x/cmdx"
7+
8+
"github.com/spf13/cobra"
9+
)
10+
11+
func NewGetKratosConfigCmd() *cobra.Command {
12+
cmd := &cobra.Command{
13+
Use: "identity-config project-id",
14+
Aliases: []string{"ic", "kratos-config"},
15+
Args: cobra.ExactArgs(1),
16+
Short: fmt.Sprintf("Get an Ory Cloud project and output it as Ory Kratos configuration"),
17+
Example: `ory get kratos-config ecaaa3cb-0730-4ee8-a6df-9553cdfeef89 --format yaml > kratos-config.yaml
18+
ory get kratos-config ecaaa3cb-0730-4ee8-a6df-9553cdfeef89 --format json > kratos-config.json`,
19+
RunE: func(cmd *cobra.Command, args []string) error {
20+
h, err := NewSnakeCharmer(cmd)
21+
if err != nil {
22+
return err
23+
}
24+
25+
project, err := h.GetProject(args[0])
26+
if err != nil {
27+
return PrintOpenAPIError(cmd, err)
28+
}
29+
30+
cmdx.PrintJSONAble(cmd, outputConfig(project.Services.Identity.Config))
31+
return nil
32+
},
33+
}
34+
35+
cmdx.RegisterJSONFormatFlags(cmd.Flags())
36+
return cmd
37+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package cloudx
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/ghodss/yaml"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
"github.com/tidwall/gjson"
13+
)
14+
15+
func TestGetKratosConfig(t *testing.T) {
16+
configDir := newConfigDir(t)
17+
cmd := configAwareCmd(configDir)
18+
email, password := registerAccount(t, configDir)
19+
20+
project := createProject(t, configDir)
21+
22+
t.Run(fmt.Sprintf("is able to get project"), func(t *testing.T) {
23+
stdout, _, err := cmd.Exec(nil, "get", "kratos-config", project, "--format", "json")
24+
require.NoError(t, err)
25+
actual, err := yaml.YAMLToJSON([]byte(stdout))
26+
require.NoError(t, err)
27+
assert.Equal(t, "/ui/error", gjson.GetBytes(actual, "selfservice.flows.error.ui_url").String())
28+
})
29+
30+
t.Run("is not able to list projects if not authenticated and quiet flag", func(t *testing.T) {
31+
configDir := newConfigDir(t)
32+
cmd := configAwareCmd(configDir)
33+
_, _, err := cmd.Exec(nil, "get", "identity-config", project, "--quiet")
34+
require.ErrorIs(t, err, ErrNoConfigQuiet)
35+
})
36+
37+
t.Run("is able to get project after authenticating", func(t *testing.T) {
38+
configDir := newConfigDir(t)
39+
cmd := configPasswordAwareCmd(configDir, password)
40+
// Create the account
41+
var r bytes.Buffer
42+
r.WriteString("y\n") // Do you already have an Ory Console account you wish to use? [y/n]: y
43+
r.WriteString(email + "\n") // Email fakeEmail()
44+
stdout, _, err := cmd.Exec(&r, "get", "ic", project, "--format", "json")
45+
require.NoError(t, err)
46+
actual, err := yaml.YAMLToJSON([]byte(stdout))
47+
require.NoError(t, err)
48+
assert.Equal(t, "/ui/error", gjson.GetBytes(actual, "selfservice.flows.error.ui_url").String())
49+
})
50+
}

cloudx/cmd_get_project.go

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package cloudx
33
import (
44
"fmt"
55

6+
"github.com/ory/x/cmdx"
7+
68
"github.com/spf13/cobra"
79
)
810

@@ -11,10 +13,30 @@ func NewGetProjectCmd() *cobra.Command {
1113
Use: "project id",
1214
Args: cobra.ExactArgs(1),
1315
Short: fmt.Sprintf("Get an Ory Cloud project"),
14-
Example: `ory get project ecaaa3cb-0730-4ee8-a6df-9553cdfeef89
15-
ory get project ecaaa3cb-0730-4ee8-a6df-9553cdfeef89 --format json
16-
ory get project ecaaa3cb-0730-4ee8-a6df-9553cdfeef89 --format kratos-config > kratos-config.yml`,
17-
Long: `If you wish to generate a configuration for self-hosting Ory Kratos, use ` + "`--format kratos-config`" + `.`,
16+
Example: `$ ory get project ecaaa3cb-0730-4ee8-a6df-9553cdfeef89
17+
18+
ID ecaaa3cb-0730-4ee8-a6df-9553cdfeef89
19+
SLUG good-wright-t7kzy3vugf
20+
STATE running
21+
NAME Example Project
22+
23+
$ ory get project ecaaa3cb-0730-4ee8-a6df-9553cdfeef89 --format json
24+
25+
{
26+
"name": "Example Project",
27+
"identity": {
28+
"services": {
29+
"config": {
30+
"courier": {
31+
"smtp": {
32+
"from_name": "..."
33+
}
34+
// ...
35+
}
36+
}
37+
}
38+
}
39+
}`,
1840
RunE: func(cmd *cobra.Command, args []string) error {
1941
h, err := NewSnakeCharmer(cmd)
2042
if err != nil {
@@ -26,10 +48,11 @@ ory get project ecaaa3cb-0730-4ee8-a6df-9553cdfeef89 --format kratos-config > kr
2648
return PrintOpenAPIError(cmd, err)
2749
}
2850

29-
return PrintExtendedFormat(cmd, project)
51+
cmdx.PrintRow(cmd, (*outputProject)(project))
52+
return nil
3053
},
3154
}
3255

33-
RegisterExtendedOutput(cmd.Flags())
56+
cmdx.RegisterFormatFlags(cmd.Flags())
3457
return cmd
3558
}

0 commit comments

Comments
 (0)