diff --git a/ziti/cmd/ziti/cmd/common/options.go b/ziti/cmd/ziti/cmd/common/options.go index b29d2a02f..9801c74c6 100644 --- a/ziti/cmd/ziti/cmd/common/options.go +++ b/ziti/cmd/ziti/cmd/common/options.go @@ -17,9 +17,11 @@ package common import ( + "context" "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" "github.com/spf13/cobra" "io" + "time" ) // CommonOptions contains common options and helper methods @@ -35,3 +37,9 @@ type CommonOptions struct { ConfigIdentity string Timeout int } + +func (options *CommonOptions) TimeoutContext() (context.Context, context.CancelFunc) { + timeout := time.Duration(options.Timeout) * time.Second + + return context.WithTimeout(context.Background(), timeout) +} diff --git a/ziti/cmd/ziti/cmd/edge_controller/create_authenticator_updb.go b/ziti/cmd/ziti/cmd/edge_controller/create_authenticator_updb.go index 64a3a9f74..0c17e8eb4 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/create_authenticator_updb.go +++ b/ziti/cmd/ziti/cmd/edge_controller/create_authenticator_updb.go @@ -87,7 +87,7 @@ func runCreateIdentityPassword(idType string, options *createAuthenticatorUpdb) return err } - client, err := util.DefaultEdgeManagementClient() + client, err := util.NewEdgeManagementClient(options) if err != nil { return err diff --git a/ziti/cmd/ziti/cmd/edge_controller/create_posture_check.go b/ziti/cmd/ziti/cmd/edge_controller/create_posture_check.go index 0bf5e6f8b..919817bad 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/create_posture_check.go +++ b/ziti/cmd/ziti/cmd/edge_controller/create_posture_check.go @@ -476,7 +476,7 @@ func runCreatePostureCheckProcess(o *createPostureCheckProcessOptions) error { } func runCreatePostureCheckProcessMulti(options *createPostureCheckProcessMultiOptions) error { - managementClient, err := util.DefaultEdgeManagementClient() + managementClient, err := util.NewEdgeManagementClient(options) if err != nil { return err diff --git a/ziti/cmd/ziti/cmd/edge_controller/delete_authenticator_updb.go b/ziti/cmd/ziti/cmd/edge_controller/delete_authenticator_updb.go index 7b669874b..c51b77b30 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/delete_authenticator_updb.go +++ b/ziti/cmd/ziti/cmd/edge_controller/delete_authenticator_updb.go @@ -61,7 +61,7 @@ func runDeleteUpdb(idOrName string, options *deleteUpdbOptions) error { return err } - client, err := util.DefaultEdgeManagementClient() + client, err := util.NewEdgeManagementClient(options) if err != nil { return err diff --git a/ziti/cmd/ziti/cmd/edge_controller/list.go b/ziti/cmd/ziti/cmd/edge_controller/list.go index 269dcb8a9..07b2f161e 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/list.go +++ b/ziti/cmd/ziti/cmd/edge_controller/list.go @@ -19,20 +19,20 @@ package edge_controller import ( "encoding/json" "fmt" + "github.com/Jeffail/gabs" + "github.com/openziti/edge/rest_management_api_client/certificate_authority" "github.com/openziti/edge/rest_model" "github.com/openziti/foundation/util/errorz" - "github.com/pkg/errors" - "io" - "net/url" - "reflect" - "strings" - - "github.com/Jeffail/gabs" "github.com/openziti/ziti/ziti/cmd/ziti/cmd/common" cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/openziti/ziti/ziti/cmd/ziti/util" + "github.com/pkg/errors" "github.com/spf13/cobra" + "io" + "net/url" + "reflect" + "strings" ) // newListCmd creates a command object for the "controller list" command @@ -134,6 +134,20 @@ type paging struct { errorz.ErrorHolderImpl } +func newPagingInfo(meta *rest_model.Meta) paging { + if meta != nil && meta.Pagination != nil { + pagingInfo := paging{ + limit: *meta.Pagination.Limit, + offset: *meta.Pagination.Offset, + count: *meta.Pagination.TotalCount, + } + + return pagingInfo + } + + return paging{} +} + func (p *paging) output(o *edgeOptions) { if p.HasError() { _, _ = fmt.Fprintf(o.Out, "unable to retrieve paging information: %v\n", p.Err) @@ -804,7 +818,26 @@ func outputPostureChecks(o *edgeOptions, children []*gabs.Container, pagingInfo } func runListCAs(o *edgeOptions) error { - children, pagingInfo, err := listEntitiesWithOptions("cas", o) + client, err := util.NewEdgeManagementClient(o) + + if err != nil { + return err + } + + var filter *string = nil + + if len(o.Args) > 0 { + filter = &o.Args[0] + } + + context, cancelContext := o.TimeoutContext() + defer cancelContext() + + result, err := client.CertificateAuthority.ListCas(&certificate_authority.ListCasParams{ + Filter: filter, + Context: context, + }, nil) + if err != nil { return err } @@ -813,15 +846,42 @@ func runListCAs(o *edgeOptions) error { return nil } - for _, entity := range children { - id, _ := entity.Path("id").Data().(string) - name, _ := entity.Path("name").Data().(string) - cluster, _ := entity.Path("cluster.id").Data().(string) - if _, err := fmt.Fprintf(o.Out, "id: %v name: %v cluster-id: %v\n", id, name, cluster); err != nil { + payload := result.GetPayload() + + if payload == nil { + return errors.New("unexpected empty response payload") + } + + for _, entity := range result.GetPayload().Data { + id := *entity.ID + name := *entity.Name + identityRoles := make([]string, 0) + + for _, role := range entity.IdentityRoles { + identityRoles = append(identityRoles, role) + } + + isVerified := *entity.IsVerified + fingerprint := "" + token := "" + if isVerified { + fingerprint = *entity.Fingerprint + } else { + token = entity.VerificationToken.String() + } + + identityNameFormat := *entity.IdentityNameFormat + + if _, err := fmt.Fprintf(o.Out, "id: %v name: %v isVerified: %v token: %v identityRoles: %v identityNameFormat: %v fingerprint: %v\n", + id, name, isVerified, token, identityRoles, identityNameFormat, fingerprint, + ); err != nil { return err } } + + pagingInfo := newPagingInfo(payload.Meta) pagingInfo.output(o) + return nil } diff --git a/ziti/cmd/ziti/cmd/edge_controller/root.go b/ziti/cmd/ziti/cmd/edge_controller/root.go index d936404ae..cedc446f8 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/root.go +++ b/ziti/cmd/ziti/cmd/edge_controller/root.go @@ -42,6 +42,21 @@ type edgeOptions struct { OutputJSONResponse bool } +func (options *edgeOptions) OutputRequestJson() bool { + return options.OutputJSONRequest +} + +func (options *edgeOptions) OutputResponseJson() bool { + return options.OutputJSONResponse +} + +func (options *edgeOptions) OutputWriter() io.Writer { + return options.CommonOptions.Out +} + +func (options *edgeOptions) ErrOutputWriter() io.Writer { + return options.CommonOptions.Err +} func (options *edgeOptions) AddCommonFlags(cmd *cobra.Command) { cmd.Flags().BoolVarP(&options.OutputJSONResponse, "output-json", "j", false, "Output the full JSON response from the Ziti Edge Controller") cmd.Flags().BoolVar(&options.OutputJSONRequest, "output-request-json", false, "Output the full JSON request to the Ziti Edge Controller") diff --git a/ziti/cmd/ziti/cmd/edge_controller/update_authenticator_updb.go b/ziti/cmd/ziti/cmd/edge_controller/update_authenticator_updb.go index 940ee1489..159b4c727 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/update_authenticator_updb.go +++ b/ziti/cmd/ziti/cmd/edge_controller/update_authenticator_updb.go @@ -136,7 +136,7 @@ func setIdentityPassword(identity, password string, options edgeOptions) error { return err } - client, err := util.DefaultEdgeManagementClient() + client, err := util.NewEdgeManagementClient(&options) if err != nil { return err diff --git a/ziti/cmd/ziti/util/rest.go b/ziti/cmd/ziti/util/rest.go index bbdd09dd4..a6483088c 100644 --- a/ziti/cmd/ziti/util/rest.go +++ b/ziti/cmd/ziti/util/rest.go @@ -694,20 +694,97 @@ func EdgeControllerList(path string, params url.Values, logJSON bool, out io.Wri return jsonParsed, nil } -func NewEdgeManagementClient(session *Session) (*rest_management_api_client.ZitiEdgeManagement, error) { - httpClientTransport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 10 * time.Second, - KeepAlive: 10 * time.Second, +var _ http.RoundTripper = &edgeTransport{} - }).DialContext, - ForceAttemptHTTP2: true, - MaxIdleConns: 10, - IdleConnTimeout: 10 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, +type edgeTransport struct { + *http.Transport + RequestFunc func(*http.Request) + ResponseFunc func(*http.Response, error) +} +func (edgeTransport *edgeTransport) RoundTrip(r *http.Request) (*http.Response, error) { + if edgeTransport.RequestFunc != nil { + edgeTransport.RequestFunc(r) + } + + resp, err := edgeTransport.Transport.RoundTrip(r) + + if edgeTransport.ResponseFunc != nil { + edgeTransport.ResponseFunc(resp, err) + } + + return resp, err +} + +type EdgeManagementClientOpts interface { + OutputRequestJson() bool + OutputResponseJson() bool + OutputWriter() io.Writer + ErrOutputWriter() io.Writer +} + +func NewEdgeManagementClient(clientOpts EdgeManagementClientOpts) (*rest_management_api_client.ZitiEdgeManagement, error) { + session := &Session{} + + if err := session.Load(); err != nil { + return nil, err + } + + respFunc := func(resp *http.Response, err error) { + if clientOpts.OutputResponseJson() { + if resp == nil || resp.Body == nil { + _, _ = fmt.Fprint(clientOpts.OutputWriter(), "\n") + return + } + + resp.Body = ioutil.NopCloser(resp.Body) + bodyContent, err := ioutil.ReadAll(resp.Body) + if err != nil { + _, _ = fmt.Fprintf(clientOpts.ErrOutputWriter(), "could not read response body: %v", err) + return + } + bodyStr := string(bodyContent) + _, _ = fmt.Fprint(clientOpts.OutputWriter(), bodyStr, "\n") + } + } + + reqFunc := func(request *http.Request) { + if clientOpts.OutputRequestJson() { + if request == nil || request.Body == nil { + _, _ = fmt.Fprint(clientOpts.OutputWriter(), "\n") + return + } + + body, err := request.GetBody() + if err == nil { + _, _ = fmt.Fprintf(clientOpts.ErrOutputWriter(), "could not copy request body: %v", err) + return + } + bodyContent, err := ioutil.ReadAll(body) + if err != nil { + bodyStr := string(bodyContent) + _, _ = fmt.Fprint(clientOpts.OutputWriter(), bodyStr, "\n") + return + } + } + } + + httpClientTransport := &edgeTransport{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 10 * time.Second, + KeepAlive: 10 * time.Second, + }).DialContext, + + ForceAttemptHTTP2: true, + MaxIdleConns: 10, + IdleConnTimeout: 10 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, + ResponseFunc: respFunc, + RequestFunc: reqFunc, } rootCaPool := x509.NewCertPool() @@ -732,31 +809,13 @@ func NewEdgeManagementClient(session *Session) (*rest_management_api_client.Ziti return nil, err } - transport := httptransport.NewWithClient(parsedHost.Host, rest_management_api_client.DefaultBasePath, rest_management_api_client.DefaultSchemes, httpClient) + clientRuntime := httptransport.NewWithClient(parsedHost.Host, rest_management_api_client.DefaultBasePath, rest_management_api_client.DefaultSchemes, httpClient) - transport.DefaultAuthentication = &EdgeManagementAuth{ + clientRuntime.DefaultAuthentication = &EdgeManagementAuth{ Token: session.Token, } - return rest_management_api_client.New(transport, nil), nil -} -var defaultEdgeManagementClient *rest_management_api_client.ZitiEdgeManagement - -func DefaultEdgeManagementClient() (*rest_management_api_client.ZitiEdgeManagement, error) { - if defaultEdgeManagementClient == nil { - session := &Session{} - if err := session.Load(); err != nil { - return nil, err - } - - if client, err := NewEdgeManagementClient(session); err != nil { - return nil, err - } else { - defaultEdgeManagementClient = client - } - } - - return defaultEdgeManagementClient, nil + return rest_management_api_client.New(clientRuntime, nil), nil } type EdgeManagementAuth struct {