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: add CRUD organizations support #318

Merged
merged 4 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .docker/Dockerfile-alpine
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM alpine:3.15
FROM alpine:3.18

RUN addgroup -S ory; \
adduser -S ory -G ory -D -h /home/ory -s /bin/nologin; \
Expand Down
4 changes: 2 additions & 2 deletions .docker/Dockerfile-build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.19-alpine3.16 AS builder
FROM golang:1.19-alpine3.18 AS builder

RUN apk -U --no-cache add build-base git gcc bash

Expand All @@ -16,7 +16,7 @@ ADD . .

RUN go build -tags sqlite -o /usr/bin/ory

FROM alpine:3.15
FROM alpine:3.18

RUN addgroup -S ory; \
adduser -S ory -G ory -D -h /home/ory -s /bin/nologin; \
Expand Down
2 changes: 1 addition & 1 deletion .grype.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#only-fixed: true
ignore:
- vulnerability: CVE-2015-5237
- vulnerability: CVE-2023-42821
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export GO111MODULE := on
export PATH := .bin:${PATH}
export PWD := $(shell pwd)

GOLANGCI_LINT_VERSION = 1.48.0
GOLANGCI_LINT_VERSION = 1.54.2

GO_DEPENDENCIES = github.com/ory/go-acc \
github.com/golang/mock/mockgen \
Expand Down
17 changes: 12 additions & 5 deletions cmd/cloudx/accountexperience/accountexperience_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@ import (
"github.com/stretchr/testify/require"

"github.com/ory/cli/cmd/cloudx/testhelpers"
"github.com/ory/x/cmdx"
)

var _, _, _, _, defaultProject, defaultCmd = testhelpers.CreateDefaultAssets()
var (
defaultProject, defaultConfig, defaultEmail, defaultPassword string
defaultCmd *cmdx.CommandExecuter
)

func TestOpenAXPages(t *testing.T) {
func TestMain(m *testing.M) {
defaultConfig, defaultEmail, defaultPassword, _, defaultProject, defaultCmd = testhelpers.CreateDefaultAssets()
testhelpers.RunAgainstStaging(m)
}

func TestOpenAXPages(t *testing.T) {
t.Run("is able to open login page", func(t *testing.T) {
var pages = [5]string{"login", "registration", "recovery", "verification", "settings"}
for _, p := range pages {
_, _, err := defaultCmd.Exec(nil, "open", "account-experience", p, "--project", defaultProject)
require.NoError(t, err)
_, stderr, err := defaultCmd.Exec(nil, "open", "account-experience", p, "--project", defaultProject)
require.NoError(t, err, stderr)
}
})

}
39 changes: 17 additions & 22 deletions cmd/cloudx/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import (
"time"

"github.com/ory/cli/cmd"
oldCloud "github.com/ory/client-go/114"

"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ory/cli/cmd/cloudx/client"
"github.com/ory/cli/cmd/cloudx/testhelpers"
cloud "github.com/ory/client-go"
"github.com/ory/x/pointerx"
)

Expand Down Expand Up @@ -49,14 +49,7 @@ func TestAuthenticator(t *testing.T) {
name := testhelpers.FakeName()

// Create the account
var r bytes.Buffer
_, _ = r.WriteString("n\n") // Do you want to sign in to an existing Ory Network account? [y/n]: n
_, _ = r.WriteString(email + "\n") // Email: FakeEmail()
_, _ = r.WriteString(name + "\n") // Name: FakeName()
_, _ = r.WriteString("n\n") // Subscribe to the Ory Security Newsletter to get platform and security updates? [y/n]: n
_, _ = r.WriteString("n\n") // I accept the Terms of Service [y/n]: n
_, _ = r.WriteString("y\n") // I accept the Terms of Service [y/n]: y

r := testhelpers.RegistrationBuffer(name, email, password)
stdout, stderr, err := cmd.Exec(&r, "auth")
require.NoError(t, err)

Expand Down Expand Up @@ -136,8 +129,8 @@ func TestAuthenticator(t *testing.T) {
code, err := totp.GenerateCode(secret, time.Now())
require.NoError(t, err)

_, _, err = c.FrontendApi.UpdateSettingsFlow(context.Background()).XSessionToken(ac.SessionToken).Flow(flow.Id).UpdateSettingsFlowBody(cloud.UpdateSettingsFlowBody{
UpdateSettingsFlowWithTotpMethod: &cloud.UpdateSettingsFlowWithTotpMethod{
_, _, err = c.FrontendApi.UpdateSettingsFlow(context.Background()).XSessionToken(ac.SessionToken).Flow(flow.Id).UpdateSettingsFlowBody(oldCloud.UpdateSettingsFlowBody{
UpdateSettingsFlowWithTotpMethod: &oldCloud.UpdateSettingsFlowWithTotpMethod{
TotpCode: pointerx.String(code),
Method: "totp",
},
Expand All @@ -146,6 +139,8 @@ func TestAuthenticator(t *testing.T) {
testhelpers.ClearConfig(t, configDir)

t.Run("sign in fails because second factor is missing", func(t *testing.T) {
t.Skip("TODO")

testhelpers.ClearConfig(t, configDir)

var r bytes.Buffer
Expand All @@ -162,6 +157,8 @@ func TestAuthenticator(t *testing.T) {
})

t.Run("sign in succeeds with second factor", func(t *testing.T) {
t.Skip("TODO")

testhelpers.ClearConfig(t, configDir)

var r bytes.Buffer
Expand All @@ -185,21 +182,19 @@ func TestAuthenticator(t *testing.T) {
t.Run("retry sign up on invalid data", func(t *testing.T) {
testhelpers.ClearConfig(t, configDir)

var r bytes.Buffer

_, _ = r.WriteString("n\n") // Do you want to sign in to an existing Ory Network account? [y/n]: n
_, _ = r.WriteString("not-an-email" + "\n") // Email: FakeEmail()
_, _ = r.WriteString(testhelpers.FakeName() + "\n") // Name: FakeName()
_, _ = r.WriteString("n\n") // Subscribe to the Ory Security Newsletter to get platform and security updates? [y/n]: n
_, _ = r.WriteString("y\n") // I accept the Terms of Service [y/n]: y
r := testhelpers.RegistrationBuffer(testhelpers.FakeName(), "not-an-email", password)

// Redo the flow
email := testhelpers.FakeEmail()
name := testhelpers.FakeName()
_, _ = r.WriteString(email + "\n") // Email: FakeEmail()
_, _ = r.WriteString(name + "\n") // Name: FakeName()
_, _ = r.WriteString("y\n") // Subscribe to the Ory Security Newsletter to get platform and security updates? [y/n]: n
_, _ = r.WriteString("y\n") // I accept the Terms of Service [y/n]: y
_, _ = r.WriteString(email + "\n") // Work email: FakeEmail()
_, _ = r.WriteString(password + "\n") // Password: FakePassword()
_, _ = r.WriteString(name + "\n") // Name: FakeName()
_, _ = r.WriteString("n\n") // Please inform me about platform and security updates: [y/n]: n
_, _ = r.WriteString("y\n") // I accept the Terms of Service https://www.ory.sh/ptos: [y/n]: y
_, _ = r.WriteString("Ory\n") // Company: Ory
_, _ = r.WriteString("12345\n") // Phone: 12345
_, _ = r.WriteString("Dev\n") // Job title/role: Dev

stdout, stderr, err := cmd.Exec(&r, "auth", "--"+client.ConfigFlag, configDir)
require.NoError(t, err)
Expand Down
6 changes: 3 additions & 3 deletions cmd/cloudx/client/form.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import (
"strings"
"time"

cloud "github.com/ory/client-go"
oldCloud "github.com/ory/client-go/114"

"github.com/pkg/errors"
"github.com/tidwall/sjson"

"github.com/ory/x/cmdx"
)

func getLabel(attrs *cloud.UiNodeInputAttributes, node *cloud.UiNode) string {
func getLabel(attrs *oldCloud.UiNodeInputAttributes, node *oldCloud.UiNode) string {
if attrs.Name == "identifier" {
return fmt.Sprintf("%s: ", "Email")
} else if node.Meta.Label != nil {
Expand All @@ -33,7 +33,7 @@ func getLabel(attrs *cloud.UiNodeInputAttributes, node *cloud.UiNode) string {

type passwordReader = func() ([]byte, error)

func renderForm(stdin *bufio.Reader, pwReader passwordReader, stderr io.Writer, ui cloud.UiContainer, method string, out interface{}) (err error) {
func renderForm(stdin *bufio.Reader, pwReader passwordReader, stderr io.Writer, ui oldCloud.UiContainer, method string, out interface{}) (err error) {
for _, message := range ui.Messages {
_, _ = fmt.Fprintf(stderr, "%s\n", message.Text)
}
Expand Down
124 changes: 105 additions & 19 deletions cmd/cloudx/client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"golang.org/x/term"

cloud "github.com/ory/client-go"
oldCloud "github.com/ory/client-go/114"
"github.com/ory/x/cmdx"
"github.com/ory/x/flagx"
"github.com/ory/x/jsonx"
Expand Down Expand Up @@ -281,7 +282,7 @@ func (h *CommandHelper) getField(i interface{}, path string) (*gjson.Result, err
return &result, nil
}

func (h *CommandHelper) signup(c *cloud.APIClient) (*AuthContext, error) {
func (h *CommandHelper) signup(c *oldCloud.APIClient) (*AuthContext, error) {
flow, _, err := c.FrontendApi.CreateNativeRegistrationFlow(h.Ctx).Execute()
if err != nil {
return nil, err
Expand All @@ -294,22 +295,23 @@ retryRegistration:
}
isRetry = true

var form cloud.UpdateRegistrationFlowWithPasswordMethod
var form oldCloud.UpdateRegistrationFlowWithPasswordMethod
if err := renderForm(h.Stdin, h.PwReader, h.VerboseErrWriter, flow.Ui, "password", &form); err != nil {
return nil, err
}

signup, _, err := c.FrontendApi.UpdateRegistrationFlow(h.Ctx).
Flow(flow.Id).UpdateRegistrationFlowBody(cloud.UpdateRegistrationFlowBody{
UpdateRegistrationFlowWithPasswordMethod: &form,
}).Execute()
signup, _, err := c.FrontendApi.
UpdateRegistrationFlow(h.Ctx).
Flow(flow.Id).
UpdateRegistrationFlowBody(oldCloud.UpdateRegistrationFlowBody{UpdateRegistrationFlowWithPasswordMethod: &form}).
Execute()
if err != nil {
if e, ok := err.(*cloud.GenericOpenAPIError); ok {
if e, ok := err.(*oldCloud.GenericOpenAPIError); ok {
switch m := e.Model().(type) {
case *cloud.RegistrationFlow:
case *oldCloud.RegistrationFlow:
flow = m
goto retryRegistration
case cloud.RegistrationFlow:
case oldCloud.RegistrationFlow:
flow = &m
goto retryRegistration
}
Expand All @@ -327,7 +329,7 @@ retryRegistration:
return h.sessionToContext(sess, sessionToken)
}

func (h *CommandHelper) signin(c *cloud.APIClient, sessionToken string) (*AuthContext, error) {
func (h *CommandHelper) signin(c *oldCloud.APIClient, sessionToken string) (*AuthContext, error) {
req := c.FrontendApi.CreateNativeLoginFlow(h.Ctx)
if len(sessionToken) > 0 {
req = req.XSessionToken(sessionToken).Aal("aal2")
Expand All @@ -345,7 +347,7 @@ retryLogin:
}
isRetry = true

var form interface{} = &cloud.UpdateLoginFlowWithPasswordMethod{}
var form interface{} = &oldCloud.UpdateLoginFlowWithPasswordMethod{}
method := "password"
if len(sessionToken) > 0 {
var foundTOTP bool
Expand All @@ -363,7 +365,7 @@ retryLogin:

method = "lookup_secret"
if foundTOTP {
form = &cloud.UpdateLoginFlowWithTotpMethod{}
form = &oldCloud.UpdateLoginFlowWithTotpMethod{}
method = "totp"
}
}
Expand All @@ -372,11 +374,11 @@ retryLogin:
return nil, err
}

var body cloud.UpdateLoginFlowBody
var body oldCloud.UpdateLoginFlowBody
switch e := form.(type) {
case *cloud.UpdateLoginFlowWithTotpMethod:
case *oldCloud.UpdateLoginFlowWithTotpMethod:
body.UpdateLoginFlowWithTotpMethod = e
case *cloud.UpdateLoginFlowWithPasswordMethod:
case *oldCloud.UpdateLoginFlowWithPasswordMethod:
body.UpdateLoginFlowWithPasswordMethod = e
default:
panic("unexpected type")
Expand All @@ -385,12 +387,12 @@ retryLogin:
login, _, err := c.FrontendApi.UpdateLoginFlow(h.Ctx).XSessionToken(sessionToken).
Flow(flow.Id).UpdateLoginFlowBody(body).Execute()
if err != nil {
if e, ok := err.(*cloud.GenericOpenAPIError); ok {
if e, ok := err.(*oldCloud.GenericOpenAPIError); ok {
switch m := e.Model().(type) {
case *cloud.LoginFlow:
case *oldCloud.LoginFlow:
flow = m
goto retryLogin
case cloud.LoginFlow:
case oldCloud.LoginFlow:
flow = &m
goto retryLogin
}
Expand All @@ -414,7 +416,7 @@ retryLogin:
return nil, err
}

func (h *CommandHelper) sessionToContext(session *cloud.Session, token string) (*AuthContext, error) {
func (h *CommandHelper) sessionToContext(session *oldCloud.Session, token string) (*AuthContext, error) {
email, err := h.getField(session.Identity.Traits, "email")
if err != nil {
return nil, err
Expand Down Expand Up @@ -518,6 +520,90 @@ func (h *CommandHelper) ListProjects() ([]cloud.ProjectMetadata, error) {
return projects, nil
}

func (h *CommandHelper) ListOrganizations(projectID string) (*cloud.ListOrganizationsResponse, error) {
ac, err := h.EnsureContext()
if err != nil {
return nil, err
}

c, err := newCloudClient(ac.SessionToken)
if err != nil {
return nil, err
}

organizations, res, err := c.ProjectApi.ListOrganizations(h.Ctx, projectID).Execute()
if err != nil {
return nil, handleError("unable to list organizations", res, err)
}

return organizations, nil
}

func (h *CommandHelper) CreateOrganization(projectID string, body cloud.OrganizationBody) (*cloud.Organization, error) {
ac, err := h.EnsureContext()
if err != nil {
return nil, err
}

c, err := newCloudClient(ac.SessionToken)
if err != nil {
return nil, err
}

organization, res, err := c.ProjectApi.
CreateOrganization(h.Ctx, projectID).
OrganizationBody(body).
Execute()
if err != nil {
return nil, handleError("unable to create organization", res, err)
}

return organization, nil
}

func (h *CommandHelper) UpdateOrganization(projectID, orgID string, body cloud.OrganizationBody) (*cloud.Organization, error) {
ac, err := h.EnsureContext()
if err != nil {
return nil, err
}

c, err := newCloudClient(ac.SessionToken)
if err != nil {
return nil, err
}

organization, res, err := c.ProjectApi.
UpdateOrganization(h.Ctx, projectID, orgID).
OrganizationBody(body).
Execute()
if err != nil {
return nil, handleError("unable to update organization", res, err)
}

return organization, nil
}

func (h *CommandHelper) DeleteOrganization(projectID, orgID string) error {
ac, err := h.EnsureContext()
if err != nil {
return err
}

c, err := newCloudClient(ac.SessionToken)
if err != nil {
return err
}

res, err := c.ProjectApi.
DeleteOrganization(h.Ctx, projectID, orgID).
Execute()
if err != nil {
return handleError("unable to create organization", res, err)
}

return nil
}

func (h *CommandHelper) GetProject(projectOrSlug string) (*cloud.Project, error) {
if projectOrSlug == "" {
return nil, errors.Errorf("No project selected! Please see the help message on how to set one.")
Expand Down
Loading
Loading