diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 414b58598..1745e1cfc 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -45,6 +45,7 @@ var requiredScopes = []string{ "create:rules", "delete:rules", "read:rules", "update:rules", "read:client_keys", "read:logs", "create:roles", "delete:roles", "read:roles", "update:roles", + "create:custom_domains", "delete:custom_domains", "read:custom_domains", "update:custom_domains", } type Authenticator struct { diff --git a/internal/auth0/auth0.go b/internal/auth0/auth0.go index d21d45a57..5e1f04b7a 100644 --- a/internal/auth0/auth0.go +++ b/internal/auth0/auth0.go @@ -17,6 +17,7 @@ type API struct { Rule RuleAPI ResourceServer ResourceServerAPI Role RoleAPI + CustomDomain CustomDomainAPI } func NewAPI(m *management.Management) *API { @@ -30,6 +31,7 @@ func NewAPI(m *management.Management) *API { ResourceServer: m.ResourceServer, Rule: m.Rule, Role: m.Role, + CustomDomain: m.CustomDomain, } } diff --git a/internal/auth0/custom_domain.go b/internal/auth0/custom_domain.go new file mode 100644 index 000000000..970618476 --- /dev/null +++ b/internal/auth0/custom_domain.go @@ -0,0 +1,22 @@ +//go:generate mockgen -source=custom_domain.go -destination=custom_domain_mock.go -package=auth0 + +package auth0 + +import "gopkg.in/auth0.v5/management" + +type CustomDomainAPI interface { + // Create a new custom domain. + Create(r *management.CustomDomain, opts ...management.RequestOption) (err error) + + // Retrieve a custom domain configuration and status. + Read(id string, opts ...management.RequestOption) (c *management.CustomDomain, err error) + + // Run the verification process on a custom domain. + Verify(id string, opts ...management.RequestOption) (c *management.CustomDomain, err error) + + // Delete a custom domain and stop serving requests for it. + Delete(id string, opts ...management.RequestOption) (err error) + + // List all custom domains. + List(opts ...management.RequestOption) (c []*management.CustomDomain, err error) +} diff --git a/internal/auth0/custom_domain_mock.go b/internal/auth0/custom_domain_mock.go new file mode 100644 index 000000000..1d551aab1 --- /dev/null +++ b/internal/auth0/custom_domain_mock.go @@ -0,0 +1,131 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: custom_domain.go + +// Package auth0 is a generated GoMock package. +package auth0 + +import ( + gomock "github.com/golang/mock/gomock" + management "gopkg.in/auth0.v5/management" + reflect "reflect" +) + +// MockCustomDomainAPI is a mock of CustomDomainAPI interface +type MockCustomDomainAPI struct { + ctrl *gomock.Controller + recorder *MockCustomDomainAPIMockRecorder +} + +// MockCustomDomainAPIMockRecorder is the mock recorder for MockCustomDomainAPI +type MockCustomDomainAPIMockRecorder struct { + mock *MockCustomDomainAPI +} + +// NewMockCustomDomainAPI creates a new mock instance +func NewMockCustomDomainAPI(ctrl *gomock.Controller) *MockCustomDomainAPI { + mock := &MockCustomDomainAPI{ctrl: ctrl} + mock.recorder = &MockCustomDomainAPIMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockCustomDomainAPI) EXPECT() *MockCustomDomainAPIMockRecorder { + return m.recorder +} + +// Create mocks base method +func (m *MockCustomDomainAPI) Create(r *management.CustomDomain, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{r} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create +func (mr *MockCustomDomainAPIMockRecorder) Create(r interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{r}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockCustomDomainAPI)(nil).Create), varargs...) +} + +// Read mocks base method +func (m *MockCustomDomainAPI) Read(id string, opts ...management.RequestOption) (*management.CustomDomain, error) { + m.ctrl.T.Helper() + varargs := []interface{}{id} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Read", varargs...) + ret0, _ := ret[0].(*management.CustomDomain) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Read indicates an expected call of Read +func (mr *MockCustomDomainAPIMockRecorder) Read(id interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{id}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockCustomDomainAPI)(nil).Read), varargs...) +} + +// Verify mocks base method +func (m *MockCustomDomainAPI) Verify(id string, opts ...management.RequestOption) (*management.CustomDomain, error) { + m.ctrl.T.Helper() + varargs := []interface{}{id} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Verify", varargs...) + ret0, _ := ret[0].(*management.CustomDomain) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Verify indicates an expected call of Verify +func (mr *MockCustomDomainAPIMockRecorder) Verify(id interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{id}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockCustomDomainAPI)(nil).Verify), varargs...) +} + +// Delete mocks base method +func (m *MockCustomDomainAPI) Delete(id string, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{id} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Delete", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete +func (mr *MockCustomDomainAPIMockRecorder) Delete(id interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{id}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCustomDomainAPI)(nil).Delete), varargs...) +} + +// List mocks base method +func (m *MockCustomDomainAPI) List(opts ...management.RequestOption) ([]*management.CustomDomain, error) { + m.ctrl.T.Helper() + varargs := []interface{}{} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) + ret0, _ := ret[0].([]*management.CustomDomain) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List +func (mr *MockCustomDomainAPIMockRecorder) List(opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockCustomDomainAPI)(nil).List), opts...) +} diff --git a/internal/cli/custom_domains.go b/internal/cli/custom_domains.go new file mode 100644 index 000000000..8b93cb3b7 --- /dev/null +++ b/internal/cli/custom_domains.go @@ -0,0 +1,282 @@ +package cli + +import ( + "github.com/AlecAivazis/survey/v2" + "github.com/auth0/auth0-cli/internal/ansi" + "github.com/auth0/auth0-cli/internal/auth0" + "github.com/spf13/cobra" + "gopkg.in/auth0.v5/management" +) + +func customDomainsCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "custom-domains", + Short: "manage resources for custom-domains.", + } + + cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(customDomainsListCmd(cli)) + cmd.AddCommand(customDomainsCreateCmd(cli)) + cmd.AddCommand(customDomainsDeleteCmd(cli)) + cmd.AddCommand(customDomainsGetCmd(cli)) + cmd.AddCommand(customDomainsVerifyCmd(cli)) + + return cmd +} + +func customDomainsListCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Get custom domains configurations", + Long: ` +Retrieve details on custom domains. + + $ auth0 custom-domains list + +`, + RunE: func(cmd *cobra.Command, args []string) error { + var customDomains []*management.CustomDomain + err := ansi.Spinner("Getting custom domains", func() error { + var err error + customDomains, err = cli.api.CustomDomain.List() + return err + }) + + if err != nil { + return err + } + + cli.renderer.CustomDomainList(customDomains) + return nil + }, + } + + return cmd +} + +func customDomainsCreateCmd(cli *cli) *cobra.Command { + var flags struct { + Domain string + Type string + VerificationMethod string + } + cmd := &cobra.Command{ + Use: "create", + Short: "Configure a new custom domain", + Long: ` +Create a new custom domain. + +Note: The custom domain will need to be verified before it will accept requests. + + $ auth0 custom-domain create --domain example.org --type auth0_managed_certs --type txt + +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("domain") { + qs := []*survey.Question{ + { + Name: "Domain", + Prompt: &survey.Input{ + Message: "Domain:", + Help: "Domain name.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + if !cmd.Flags().Changed("type") { + qs := []*survey.Question{ + { + Name: "Type", + Prompt: &survey.Input{ + Message: "Type:", + Help: "Custom domain provisioning type. Must be auth0_managed_certs or self_managed_certs.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + customDomain := &management.CustomDomain{ + Domain: auth0.String(flags.Domain), + Type: auth0.String(flags.Type), + VerificationMethod: auth0.String(flags.VerificationMethod), + } + + err := ansi.Spinner("Creating custom domain", func() error { + return cli.api.CustomDomain.Create(customDomain) + }) + if err != nil { + return err + } + + cli.renderer.CustomDomainCreate(customDomain) + return nil + }, + } + + cmd.Flags().StringVarP(&flags.Domain, "domain", "d", "", "Domain name.") + cmd.Flags().StringVarP(&flags.Type, "type", "t", "", "Custom domain provisioning type. Must be auth0_managed_certs or self_managed_certs.") + cmd.Flags().StringVarP(&flags.VerificationMethod, "verification-method", "v", "txt", "Custom domain verification method. Must be txt.") + + return cmd +} + +func customDomainsDeleteCmd(cli *cli) *cobra.Command { + var flags struct { + CustomDomainID string + } + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a custom domain configuration", + Long: ` +Delete a custom domain and stop serving requests for it. + + $ auth0 custom-domains delete --custom-domain-id myCustomDomainID +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("custom-domain-id") { + qs := []*survey.Question{ + { + Name: "CustomDomainID", + Prompt: &survey.Input{ + Message: "CustomDomainID:", + Help: "ID of the custom domain to delete.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + return ansi.Spinner("Deleting custom domain", func() error { + return cli.api.CustomDomain.Delete(flags.CustomDomainID) + }) + }, + } + + cmd.Flags().StringVarP(&flags.CustomDomainID, "custom-domain-id", "i", "", "ID of the custom domain to delete.") + + return cmd +} + +func customDomainsGetCmd(cli *cli) *cobra.Command { + var flags struct { + CustomDomainID string + } + cmd := &cobra.Command{ + Use: "get", + Short: "Get custom domain configuration", + Long: ` +Retrieve a custom domain configuration and status. + + $ auth0 custom-domain get --custom-domain-id myCustomDomainID +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("custom-domain-id") { + qs := []*survey.Question{ + { + Name: "CustomDomainID", + Prompt: &survey.Input{ + Message: "CustomDomainID:", + Help: "ID of the custom domain to retrieve.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + var customDomain *management.CustomDomain + err := ansi.Spinner("Getting custom domain", func() error { + var err error + customDomain, err = cli.api.CustomDomain.Read(flags.CustomDomainID) + return err + }) + if err != nil { + return err + } + + cli.renderer.CustomDomainGet(customDomain) + return nil + }, + } + + cmd.Flags().StringVarP(&flags.CustomDomainID, "custom-domain-id", "i", "", "ID of the custom domain to retrieve.") + + return cmd +} + +func customDomainsVerifyCmd(cli *cli) *cobra.Command { + var flags struct { + CustomDomainID string + } + cmd := &cobra.Command{ + Use: "verify", + Short: "Verify a custom domain", + Long: ` +Run the verification process on a custom domain. + +Note: Check the status field to see its verification status. Once verification is complete, it may take up to 10 minutes before the custom domain can start accepting requests. + +For self_managed_certs, when the custom domain is verified for the first time, the response will also include the cname_api_key which you will need to configure your proxy. This key must be kept secret, and is used to validate the proxy requests. + +Learn more about verifying custom domains that use Auth0 Managed certificates: + - https://auth0.com/docs/custom-domains#step-2-verify-ownership + +Learn more about verifying custom domains that use Self Managed certificates: + - https://auth0.com/docs/custom-domains/self-managed-certificates#step-2-verify-ownership + + $ auth0 custom-domain verify --custom-domain-id myCustomDomainID +`, + RunE: func(cmd *cobra.Command, args []string) error { + + if !cmd.Flags().Changed("custom-domain-id") { + qs := []*survey.Question{ + { + Name: "CustomDomainID", + Prompt: &survey.Input{ + Message: "CustomDomainID:", + Help: "ID of the custom domain to verify.", + }, + }, + } + err := survey.Ask(qs, &flags) + if err != nil { + return err + } + } + + var customDomain *management.CustomDomain + err := ansi.Spinner("Verifying custom domain", func() error { + var err error + customDomain, err = cli.api.CustomDomain.Verify(flags.CustomDomainID) + return err + }) + if err != nil { + return err + } + + cli.renderer.CustomDomainVerify(customDomain) + return nil + }, + } + + cmd.Flags().StringVarP(&flags.CustomDomainID, "custom-domain-id", "i", "", "ID of the custom domain to retrieve.") + + return cmd +} diff --git a/internal/cli/custom_domains_test.go b/internal/cli/custom_domains_test.go new file mode 100644 index 000000000..b18afda5f --- /dev/null +++ b/internal/cli/custom_domains_test.go @@ -0,0 +1,130 @@ +package cli + +import ( + "bytes" + "io/ioutil" + "testing" + + "github.com/auth0/auth0-cli/internal/auth0" + "github.com/auth0/auth0-cli/internal/display" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "gopkg.in/auth0.v5/management" +) + +func TestCustomDomainsCmd(t *testing.T) { + t.Run("List", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + m := auth0.NewMockCustomDomainAPI(ctrl) + m.EXPECT().List().MaxTimes(1).Return([]*management.CustomDomain{{ID: auth0.String("testID"), Domain: auth0.String("testDomain"), Type: auth0.String("testType")}}, nil) + stdout := &bytes.Buffer{} + cli := &cli{ + renderer: &display.Renderer{ + MessageWriter: ioutil.Discard, + ResultWriter: stdout, + Format: display.OutputFormat("json"), + }, + api: &auth0.API{CustomDomain: m}, + } + + cmd := customDomainsListCmd(cli) + if err := cmd.Execute(); err != nil { + t.Fatal(err) + } + + assert.JSONEq(t, `[{"ID": "testID", "Domain": "testDomain", "Type": "testType", "Primary": false, "Status": "", "VerificationMethod": "","Verification": {"Methods": null}}]`, stdout.String()) + }) + + t.Run("Create", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + m := auth0.NewMockCustomDomainAPI(ctrl) + m.EXPECT().Create(gomock.Any()).MaxTimes(1).Return(nil) + stdout := &bytes.Buffer{} + cli := &cli{ + renderer: &display.Renderer{ + MessageWriter: ioutil.Discard, + ResultWriter: stdout, + Format: display.OutputFormat("json"), + }, + api: &auth0.API{CustomDomain: m}, + } + + cmd := customDomainsCreateCmd(cli) + cmd.SetArgs([]string{"--domain=testDomain", "--type=testType", "--verification-method=testVerificationMethod"}) + if err := cmd.Execute(); err != nil { + t.Fatal(err) + } + + assert.JSONEq(t, `[{"ID": "", "Domain": "testDomain", "Type": "testType", "Primary": false, "Status": "", "VerificationMethod": "testVerificationMethod", "Verification": {"Methods": null}}]`, stdout.String()) + }) + + t.Run("Delete", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + m := auth0.NewMockCustomDomainAPI(ctrl) + m.EXPECT().Delete(gomock.Any()).MaxTimes(1).Return(nil) + stdout := &bytes.Buffer{} + cli := &cli{ + renderer: &display.Renderer{ + MessageWriter: ioutil.Discard, + ResultWriter: stdout, + Format: display.OutputFormat("json"), + }, + api: &auth0.API{CustomDomain: m}, + } + + cmd := customDomainsDeleteCmd(cli) + cmd.SetArgs([]string{"--custom-domain-id=testCustomDomainID"}) + if err := cmd.Execute(); err != nil { + t.Fatal(err) + } + }) + + t.Run("Get", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + m := auth0.NewMockCustomDomainAPI(ctrl) + m.EXPECT().Read(gomock.Any()).MaxTimes(1).Return(&management.CustomDomain{ID: auth0.String("testID"), Domain: auth0.String("testDomain"), Type: auth0.String("testType")}, nil) + stdout := &bytes.Buffer{} + cli := &cli{ + renderer: &display.Renderer{ + MessageWriter: ioutil.Discard, + ResultWriter: stdout, + Format: display.OutputFormat("json"), + }, + api: &auth0.API{CustomDomain: m}, + } + + cmd := customDomainsGetCmd(cli) + cmd.SetArgs([]string{"--custom-domain-id=testCustomDomainID"}) + if err := cmd.Execute(); err != nil { + t.Fatal(err) + } + assert.JSONEq(t, `[{"ID": "testID", "Domain": "testDomain", "Type": "testType", "Primary": false, "Status": "", "VerificationMethod": "", "Verification": {"Methods": null}}]`, stdout.String()) + }) + + t.Run("Verify", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + m := auth0.NewMockCustomDomainAPI(ctrl) + m.EXPECT().Verify(gomock.Any()).MaxTimes(1).Return(&management.CustomDomain{ID: auth0.String("testID"), Domain: auth0.String("testDomain"), Type: auth0.String("testType")}, nil) + stdout := &bytes.Buffer{} + cli := &cli{ + renderer: &display.Renderer{ + MessageWriter: ioutil.Discard, + ResultWriter: stdout, + Format: display.OutputFormat("json"), + }, + api: &auth0.API{CustomDomain: m}, + } + + cmd := customDomainsVerifyCmd(cli) + cmd.SetArgs([]string{"--custom-domain-id=testCustomDomainID"}) + if err := cmd.Execute(); err != nil { + t.Fatal(err) + } + assert.JSONEq(t, `[{"ID": "testID", "Domain": "testDomain", "Type": "testType", "Primary": false, "Status": "", "VerificationMethod": "", "Verification": {"Methods": null}}]`, stdout.String()) + }) +} diff --git a/internal/cli/root.go b/internal/cli/root.go index 0e39af2b5..f62fd25b2 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -63,6 +63,7 @@ func Execute() { rootCmd.AddCommand(connectionsCmd(cli)) rootCmd.AddCommand(completionCmd(cli)) rootCmd.AddCommand(rolesCmd(cli)) + rootCmd.AddCommand(customDomainsCmd(cli)) // TODO(cyx): backport this later on using latest auth0/v5. // rootCmd.AddCommand(actionsCmd(cli)) diff --git a/internal/display/custom_domains.go b/internal/display/custom_domains.go new file mode 100644 index 000000000..f97534753 --- /dev/null +++ b/internal/display/custom_domains.go @@ -0,0 +1,83 @@ +package display + +import ( + "strconv" + + "github.com/auth0/auth0-cli/internal/ansi" + "github.com/auth0/auth0-cli/internal/auth0" + "gopkg.in/auth0.v5/management" +) + +type customDomainView struct { + ID string + Domain string + Type string + Primary bool + Status string + VerificationMethod string + Verification customDomainVerificationView +} + +type customDomainVerificationView struct { + Methods []map[string]interface{} +} + +func (v *customDomainView) AsTableHeader() []string { + return []string{"Domain", "Custom Domain ID", "Type", "Primary", "Status", "Verification Method"} +} + +func (v *customDomainView) AsTableRow() []string { + return []string{v.Domain, v.ID, v.Type, strconv.FormatBool(v.Primary), v.Status, v.VerificationMethod} +} + +func (r *Renderer) CustomDomainList(customDomains []*management.CustomDomain) { + r.Heading(ansi.Bold(r.Tenant), "custom-domains\n") + var res []View + for _, c := range customDomains { + res = append(res, &customDomainView{ + ID: auth0.StringValue(c.ID), + Domain: auth0.StringValue(c.Domain), + Type: auth0.StringValue(c.Type), + Primary: auth0.BoolValue(c.Primary), + Status: auth0.StringValue(c.Status), + VerificationMethod: auth0.StringValue(c.VerificationMethod), + }) + } + r.Results(res) +} + +func (r *Renderer) CustomDomainCreate(customDomain *management.CustomDomain) { + r.Heading(ansi.Bold(r.Tenant), "custom-domain created\n") + r.Results([]View{&customDomainView{ + Domain: auth0.StringValue(customDomain.Domain), + Type: auth0.StringValue(customDomain.Type), + ID: auth0.StringValue(customDomain.ID), + Primary: auth0.BoolValue(customDomain.Primary), + Status: auth0.StringValue(customDomain.Status), + VerificationMethod: auth0.StringValue(customDomain.VerificationMethod), + }}) +} + +func (r *Renderer) CustomDomainGet(customDomain *management.CustomDomain) { + r.Heading(ansi.Bold(r.Tenant), "custom-domain\n") + r.Results([]View{&customDomainView{ + Domain: auth0.StringValue(customDomain.Domain), + Type: auth0.StringValue(customDomain.Type), + ID: auth0.StringValue(customDomain.ID), + Primary: auth0.BoolValue(customDomain.Primary), + Status: auth0.StringValue(customDomain.Status), + VerificationMethod: auth0.StringValue(customDomain.VerificationMethod), + }}) +} + +func (r *Renderer) CustomDomainVerify(customDomain *management.CustomDomain) { + r.Heading(ansi.Bold(r.Tenant), "custom-domain verified\n") + r.Results([]View{&customDomainView{ + Domain: auth0.StringValue(customDomain.Domain), + Type: auth0.StringValue(customDomain.Type), + ID: auth0.StringValue(customDomain.ID), + Primary: auth0.BoolValue(customDomain.Primary), + Status: auth0.StringValue(customDomain.Status), + VerificationMethod: auth0.StringValue(customDomain.VerificationMethod), + }}) +}