From a9f0f3d83d7328996b66b4efc3296a6be0e0ef70 Mon Sep 17 00:00:00 2001 From: Tim Ross Date: Wed, 15 Jan 2025 18:32:50 -0500 Subject: [PATCH 1/2] Add tctl decision command Implements the new subcommand as described in RFD024e. The only deviation is in naming, i.e. `tctl decision` instead of the proposed `decision-service`. All commands are hidden now and subject to change as development of the PDP is underway. --- tool/tctl/common/cmds.go | 2 + tool/tctl/common/decision/command.go | 78 +++++++++++++++ tool/tctl/common/decision/command_test.go | 84 +++++++++++++++++ .../common/decision/evaluate_db_command.go | 75 +++++++++++++++ .../decision/evaluate_db_command_test.go | 87 +++++++++++++++++ .../common/decision/evaluate_ssh_command.go | 80 ++++++++++++++++ .../decision/evaluate_ssh_command_test.go | 94 +++++++++++++++++++ 7 files changed, 500 insertions(+) create mode 100644 tool/tctl/common/decision/command.go create mode 100644 tool/tctl/common/decision/command_test.go create mode 100644 tool/tctl/common/decision/evaluate_db_command.go create mode 100644 tool/tctl/common/decision/evaluate_db_command_test.go create mode 100644 tool/tctl/common/decision/evaluate_ssh_command.go create mode 100644 tool/tctl/common/decision/evaluate_ssh_command_test.go diff --git a/tool/tctl/common/cmds.go b/tool/tctl/common/cmds.go index 2cd7b7a579802..bf8b292381542 100644 --- a/tool/tctl/common/cmds.go +++ b/tool/tctl/common/cmds.go @@ -20,6 +20,7 @@ package common import ( "github.com/gravitational/teleport/tool/tctl/common/accessmonitoring" + "github.com/gravitational/teleport/tool/tctl/common/decision" "github.com/gravitational/teleport/tool/tctl/common/loginrule" "github.com/gravitational/teleport/tool/tctl/common/plugin" "github.com/gravitational/teleport/tool/tctl/sso/configure" @@ -67,5 +68,6 @@ func Commands() []CLICommand { &touchIDCommand{}, &TerraformCommand{}, &AutoUpdateCommand{}, + &decision.Command{}, } } diff --git a/tool/tctl/common/decision/command.go b/tool/tctl/common/decision/command.go new file mode 100644 index 0000000000000..ab043f60cde12 --- /dev/null +++ b/tool/tctl/common/decision/command.go @@ -0,0 +1,78 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package decision + +import ( + "context" + "io" + "os" + + "github.com/alecthomas/kingpin/v2" + "github.com/gravitational/trace" + + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/lib/service/servicecfg" + commonclient "github.com/gravitational/teleport/tool/tctl/common/client" + tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config" +) + +// Command is a group of commands to interact with the Teleport Decision Service. +type Command struct { + // Output is the writer that any command output should be written to. + Output io.Writer + + evaluateSSHCommand EvaluateSSHCommand + evaluateDatabaseCommand EvaluateDatabaseCommand +} + +// Initialize sets up the "tctl decision" command. +func (c *Command) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIFlags, _ *servicecfg.Config) { + if c.Output == nil { + c.Output = os.Stdout + } + + cmd := app.Command("decision", "Interact with the Teleport Decision Service.").Hidden() + c.evaluateSSHCommand.Initialize(cmd, c.Output) + c.evaluateDatabaseCommand.Initialize(cmd, c.Output) +} + +// Client contains methods required by this command +// to interact with the control plane. +type Client interface { + DecisionClient() decisionpb.DecisionServiceClient +} + +// TryRun attempts to run subcommands. +func (c *Command) TryRun(ctx context.Context, cmd string, clientFunc commonclient.InitFunc) (bool, error) { + var run func(context.Context, Client) error + switch cmd { + case c.evaluateSSHCommand.FullCommand(): + run = c.evaluateSSHCommand.Run + case c.evaluateDatabaseCommand.FullCommand(): + run = c.evaluateDatabaseCommand.Run + default: + return false, nil + } + + client, closeFn, err := clientFunc(ctx) + if err != nil { + return true, trace.Wrap(err) + } + + defer func() { closeFn(ctx) }() + return true, trace.Wrap(run(ctx, client)) +} diff --git a/tool/tctl/common/decision/command_test.go b/tool/tctl/common/decision/command_test.go new file mode 100644 index 0000000000000..20865d0867bc3 --- /dev/null +++ b/tool/tctl/common/decision/command_test.go @@ -0,0 +1,84 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package decision_test + +import ( + "bytes" + "context" + "errors" + "testing" + + "github.com/alecthomas/kingpin/v2" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" + + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/tool/tctl/common/decision" +) + +type fakeDecisionService struct { + decision.Client + + decisionClient decisionpb.DecisionServiceClient +} + +func (f fakeDecisionService) DecisionClient() decisionpb.DecisionServiceClient { + return f.decisionClient +} + +type fakeDecisionServiceClient struct { + decisionpb.DecisionServiceClient + + sshResponse *decisionpb.EvaluateSSHAccessResponse + databaseResponse *decisionpb.EvaluateDatabaseAccessResponse +} + +// EvaluateSSHAccess evaluates an SSH access attempt. +func (f fakeDecisionServiceClient) EvaluateSSHAccess(ctx context.Context, in *decisionpb.EvaluateSSHAccessRequest, opts ...grpc.CallOption) (*decisionpb.EvaluateSSHAccessResponse, error) { + return f.sshResponse, nil +} + +// EvaluateDatabaseAccess evaluate a database access attempt. +func (f fakeDecisionServiceClient) EvaluateDatabaseAccess(ctx context.Context, in *decisionpb.EvaluateDatabaseAccessRequest, opts ...grpc.CallOption) (*decisionpb.EvaluateDatabaseAccessResponse, error) { + return f.databaseResponse, nil +} + +func TestCommands(t *testing.T) { + var output bytes.Buffer + cmd := decision.Command{Output: &output} + + cmd.Initialize(kingpin.New("tctl", "test"), nil, nil) + + match, err := cmd.TryRun(context.Background(), "decision evaluate-ssh-access", func(ctx context.Context) (client *authclient.Client, close func(context.Context), err error) { + return nil, nil, errors.New("fail") + }) + assert.True(t, match, "evaluate SSH command did not match") + assert.Error(t, err, "expected failure from init function") + + match, err = cmd.TryRun(context.Background(), "decision evaluate-db-access", func(ctx context.Context) (client *authclient.Client, close func(context.Context), err error) { + return nil, nil, errors.New("fail") + }) + assert.True(t, match, "evaluate database command did not match") + assert.Error(t, err, "expected failure from init function") + + match, err = cmd.TryRun(context.Background(), "decision evaluate-foo", func(ctx context.Context) (client *authclient.Client, close func(context.Context), err error) { + return nil, nil, errors.New("fail") + }) + assert.False(t, match, "evaluate foo command matched") + assert.NoError(t, err, "error received when no command matched") +} diff --git a/tool/tctl/common/decision/evaluate_db_command.go b/tool/tctl/common/decision/evaluate_db_command.go new file mode 100644 index 0000000000000..a1847f70ba279 --- /dev/null +++ b/tool/tctl/common/decision/evaluate_db_command.go @@ -0,0 +1,75 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +package decision + +import ( + "context" + "io" + + "github.com/alecthomas/kingpin/v2" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/utils" +) + +// EvaluateDatabaseCommand is a command to evaluate +// database access via the Teleport Decision Service. +type EvaluateDatabaseCommand struct { + output io.Writer + databaseDetails databaseDetails + command *kingpin.CmdClause +} + +type databaseDetails struct { + databaseID string +} + +// Initialize sets up the "tctl decision evaluate db" command. +func (c *EvaluateDatabaseCommand) Initialize(cmd *kingpin.CmdClause, output io.Writer) { + c.output = output + c.command = cmd.Command("evaluate-db-access", "Evaluate database access for a user.").Hidden() + c.command.Flag("database-id", "The id of the target database.").StringVar(&c.databaseDetails.databaseID) +} + +// FullCommand returns the fully qualified name of +// the subcommand, i.e. tctl decision evaluate db. +func (c *EvaluateDatabaseCommand) FullCommand() string { + return c.command.FullCommand() +} + +// Run executes the subcommand. +func (c *EvaluateDatabaseCommand) Run(ctx context.Context, clt Client) error { + resp, err := clt.DecisionClient().EvaluateDatabaseAccess(ctx, &decisionpb.EvaluateDatabaseAccessRequest{ + Metadata: &decisionpb.RequestMetadata{PepVersionHint: teleport.Version}, + TlsIdentity: &decisionpb.TLSIdentity{}, + Database: &decisionpb.Resource{ + Kind: types.KindDatabase, + Name: c.databaseDetails.databaseID, + }, + }) + if err != nil { + return trace.Wrap(err) + } + + if err := utils.WriteJSON(c.output, resp); err != nil { + return trace.Wrap(err, "failed to marshal result") + } + + return nil +} diff --git a/tool/tctl/common/decision/evaluate_db_command_test.go b/tool/tctl/common/decision/evaluate_db_command_test.go new file mode 100644 index 0000000000000..0e2e8e178f21b --- /dev/null +++ b/tool/tctl/common/decision/evaluate_db_command_test.go @@ -0,0 +1,87 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package decision_test + +import ( + "bytes" + "context" + "testing" + + "github.com/alecthomas/kingpin/v2" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport" + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/tool/tctl/common/decision" +) + +func TestEvaluateDB(t *testing.T) { + tests := []struct { + name string + response *decisionpb.EvaluateDatabaseAccessResponse + }{ + { + name: "denied", + response: &decisionpb.EvaluateDatabaseAccessResponse{ + Result: &decisionpb.EvaluateDatabaseAccessResponse_Denial{ + Denial: &decisionpb.DatabaseAccessDenial{ + Metadata: &decisionpb.DenialMetadata{ + PdpVersion: teleport.Version, + UserMessage: "denial", + }, + }, + }, + }, + }, + { + name: "permitted", + response: &decisionpb.EvaluateDatabaseAccessResponse{ + Result: &decisionpb.EvaluateDatabaseAccessResponse_Permit{ + Permit: &decisionpb.DatabaseAccessPermit{ + Metadata: &decisionpb.PermitMetadata{ + PdpVersion: teleport.Version, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cmd := decision.EvaluateDatabaseCommand{} + + var output bytes.Buffer + cmd.Initialize(kingpin.New("tctl", "test").Command("decision", ""), &output) + + svc := fakeDecisionService{ + decisionClient: fakeDecisionServiceClient{ + databaseResponse: test.response, + }, + } + + err := cmd.Run(context.Background(), svc) + require.NoError(t, err, "evaluating database access failed") + + var expected bytes.Buffer + utils.WriteJSON(&expected, test.response) + require.NoError(t, err, "marshaling expected output failed") + require.Equal(t, output.String(), expected.String(), "output did not match") + }) + } +} diff --git a/tool/tctl/common/decision/evaluate_ssh_command.go b/tool/tctl/common/decision/evaluate_ssh_command.go new file mode 100644 index 0000000000000..534cef252a90f --- /dev/null +++ b/tool/tctl/common/decision/evaluate_ssh_command.go @@ -0,0 +1,80 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +package decision + +import ( + "context" + "io" + + "github.com/alecthomas/kingpin/v2" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/utils" +) + +// EvaluateSSHCommand is a command to evaluate +// SSH access via the Teleport Decision Service. +type EvaluateSSHCommand struct { + output io.Writer + + sshDetails sshDetails + command *kingpin.CmdClause +} + +type sshDetails struct { + serverID string + username string + login string +} + +// Initialize sets up the "tctl decision evaluate ssh" command. +func (c *EvaluateSSHCommand) Initialize(cmd *kingpin.CmdClause, output io.Writer) { + c.output = output + c.command = cmd.Command("evaluate-ssh-access", "Evaluate SSH access for a user.").Hidden() + c.command.Flag("username", "The username to evaluate access for.").StringVar(&c.sshDetails.username) + c.command.Flag("login", "The os login to evaluate access for.").StringVar(&c.sshDetails.login) + c.command.Flag("server-id", "The host id of the target server.").StringVar(&c.sshDetails.serverID) +} + +// FullCommand returns the fully qualified name of +// the subcommand, i.e. tctl decision evaluate ssh. +func (c *EvaluateSSHCommand) FullCommand() string { + return c.command.FullCommand() +} + +// Run executes the subcommand. +func (c *EvaluateSSHCommand) Run(ctx context.Context, clt Client) error { + resp, err := clt.DecisionClient().EvaluateSSHAccess(ctx, &decisionpb.EvaluateSSHAccessRequest{ + Metadata: &decisionpb.RequestMetadata{PepVersionHint: teleport.Version}, + SshIdentity: &decisionpb.SSHIdentity{}, + Node: &decisionpb.Resource{ + Kind: types.KindNode, + Name: c.sshDetails.serverID, + }, + }) + if err != nil { + return trace.Wrap(err) + } + + if err := utils.WriteJSON(c.output, resp); err != nil { + return trace.Wrap(err, "failed to marshal result") + } + + return nil +} diff --git a/tool/tctl/common/decision/evaluate_ssh_command_test.go b/tool/tctl/common/decision/evaluate_ssh_command_test.go new file mode 100644 index 0000000000000..d3709e73d4dbc --- /dev/null +++ b/tool/tctl/common/decision/evaluate_ssh_command_test.go @@ -0,0 +1,94 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package decision_test + +import ( + "bytes" + "context" + "testing" + + "github.com/alecthomas/kingpin/v2" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/gravitational/teleport" + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/tool/tctl/common/decision" +) + +func TestEvaluateSSH(t *testing.T) { + tests := []struct { + name string + response *decisionpb.EvaluateSSHAccessResponse + }{ + { + name: "denied", + response: &decisionpb.EvaluateSSHAccessResponse{ + Decision: &decisionpb.EvaluateSSHAccessResponse_Denial{ + Denial: &decisionpb.SSHAccessDenial{ + Metadata: &decisionpb.DenialMetadata{ + PdpVersion: teleport.Version, + UserMessage: "denial", + }, + }, + }, + }, + }, + { + name: "permitted", + response: &decisionpb.EvaluateSSHAccessResponse{ + Decision: &decisionpb.EvaluateSSHAccessResponse_Permit{ + Permit: &decisionpb.SSHAccessPermit{ + Metadata: &decisionpb.PermitMetadata{ + PdpVersion: teleport.Version, + }, + Logins: []string{"llama", "beast"}, + ForwardAgent: true, + MaxSessionTtl: durationpb.New(10), + PortForwarding: false, + ClientIdleTimeout: 1000, + DisconnectExpiredCert: true, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cmd := decision.EvaluateSSHCommand{} + + var output bytes.Buffer + cmd.Initialize(kingpin.New("tctl", "test").Command("decision", ""), &output) + + svc := fakeDecisionService{ + decisionClient: fakeDecisionServiceClient{ + sshResponse: test.response, + }, + } + + err := cmd.Run(context.Background(), svc) + require.NoError(t, err, "evaluating SSH access failed") + + var expected bytes.Buffer + utils.WriteJSON(&expected, test.response) + require.NoError(t, err, "marshaling expected output failed") + require.Equal(t, output.String(), expected.String(), "output did not match") + }) + } +} From 14035d881e6cd23900d4d1fd9802bb2c6a81b0b0 Mon Sep 17 00:00:00 2001 From: Tim Ross Date: Thu, 23 Jan 2025 16:52:48 -0500 Subject: [PATCH 2/2] clean up commands --- tool/tctl/common/decision/command.go | 30 +++++++++++++------ tool/tctl/common/decision/command_test.go | 10 ------- .../common/decision/evaluate_db_command.go | 7 ++--- .../decision/evaluate_db_command_test.go | 11 +++---- .../common/decision/evaluate_ssh_command.go | 7 ++--- .../decision/evaluate_ssh_command_test.go | 11 +++---- 6 files changed, 35 insertions(+), 41 deletions(-) diff --git a/tool/tctl/common/decision/command.go b/tool/tctl/common/decision/command.go index ab043f60cde12..849d50aff4a7f 100644 --- a/tool/tctl/common/decision/command.go +++ b/tool/tctl/common/decision/command.go @@ -18,11 +18,14 @@ package decision import ( "context" + "fmt" "io" "os" "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" "github.com/gravitational/teleport/lib/service/servicecfg" @@ -50,15 +53,9 @@ func (c *Command) Initialize(app *kingpin.Application, _ *tctlcfg.GlobalCLIFlags c.evaluateDatabaseCommand.Initialize(cmd, c.Output) } -// Client contains methods required by this command -// to interact with the control plane. -type Client interface { - DecisionClient() decisionpb.DecisionServiceClient -} - // TryRun attempts to run subcommands. func (c *Command) TryRun(ctx context.Context, cmd string, clientFunc commonclient.InitFunc) (bool, error) { - var run func(context.Context, Client) error + var run func(context.Context, decisionpb.DecisionServiceClient) error switch cmd { case c.evaluateSSHCommand.FullCommand(): run = c.evaluateSSHCommand.Run @@ -73,6 +70,21 @@ func (c *Command) TryRun(ctx context.Context, cmd string, clientFunc commonclien return true, trace.Wrap(err) } - defer func() { closeFn(ctx) }() - return true, trace.Wrap(run(ctx, client)) + defer closeFn(ctx) + return true, trace.Wrap(run(ctx, client.DecisionClient())) +} + +// WriteProtoJSON outputs the the given [proto.Message] in +// JSON format to the given [io.Writer]. +func WriteProtoJSON(w io.Writer, v proto.Message) error { + out, err := protojson.MarshalOptions{ + UseProtoNames: true, + Indent: " ", + }.Marshal(v) + if err != nil { + return trace.Wrap(err) + } + + _, err = fmt.Fprint(w, out) + return trace.Wrap(err) } diff --git a/tool/tctl/common/decision/command_test.go b/tool/tctl/common/decision/command_test.go index 20865d0867bc3..f1f05635654a6 100644 --- a/tool/tctl/common/decision/command_test.go +++ b/tool/tctl/common/decision/command_test.go @@ -31,16 +31,6 @@ import ( "github.com/gravitational/teleport/tool/tctl/common/decision" ) -type fakeDecisionService struct { - decision.Client - - decisionClient decisionpb.DecisionServiceClient -} - -func (f fakeDecisionService) DecisionClient() decisionpb.DecisionServiceClient { - return f.decisionClient -} - type fakeDecisionServiceClient struct { decisionpb.DecisionServiceClient diff --git a/tool/tctl/common/decision/evaluate_db_command.go b/tool/tctl/common/decision/evaluate_db_command.go index a1847f70ba279..e38faa0df3061 100644 --- a/tool/tctl/common/decision/evaluate_db_command.go +++ b/tool/tctl/common/decision/evaluate_db_command.go @@ -25,7 +25,6 @@ import ( "github.com/gravitational/teleport" decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/utils" ) // EvaluateDatabaseCommand is a command to evaluate @@ -54,8 +53,8 @@ func (c *EvaluateDatabaseCommand) FullCommand() string { } // Run executes the subcommand. -func (c *EvaluateDatabaseCommand) Run(ctx context.Context, clt Client) error { - resp, err := clt.DecisionClient().EvaluateDatabaseAccess(ctx, &decisionpb.EvaluateDatabaseAccessRequest{ +func (c *EvaluateDatabaseCommand) Run(ctx context.Context, clt decisionpb.DecisionServiceClient) error { + resp, err := clt.EvaluateDatabaseAccess(ctx, &decisionpb.EvaluateDatabaseAccessRequest{ Metadata: &decisionpb.RequestMetadata{PepVersionHint: teleport.Version}, TlsIdentity: &decisionpb.TLSIdentity{}, Database: &decisionpb.Resource{ @@ -67,7 +66,7 @@ func (c *EvaluateDatabaseCommand) Run(ctx context.Context, clt Client) error { return trace.Wrap(err) } - if err := utils.WriteJSON(c.output, resp); err != nil { + if err := WriteProtoJSON(c.output, resp); err != nil { return trace.Wrap(err, "failed to marshal result") } diff --git a/tool/tctl/common/decision/evaluate_db_command_test.go b/tool/tctl/common/decision/evaluate_db_command_test.go index 0e2e8e178f21b..d0ffe27ba524a 100644 --- a/tool/tctl/common/decision/evaluate_db_command_test.go +++ b/tool/tctl/common/decision/evaluate_db_command_test.go @@ -26,7 +26,6 @@ import ( "github.com/gravitational/teleport" decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" - "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/tool/tctl/common/decision" ) @@ -69,17 +68,15 @@ func TestEvaluateDB(t *testing.T) { var output bytes.Buffer cmd.Initialize(kingpin.New("tctl", "test").Command("decision", ""), &output) - svc := fakeDecisionService{ - decisionClient: fakeDecisionServiceClient{ - databaseResponse: test.response, - }, + clt := fakeDecisionServiceClient{ + databaseResponse: test.response, } - err := cmd.Run(context.Background(), svc) + err := cmd.Run(context.Background(), clt) require.NoError(t, err, "evaluating database access failed") var expected bytes.Buffer - utils.WriteJSON(&expected, test.response) + err = decision.WriteProtoJSON(&expected, test.response) require.NoError(t, err, "marshaling expected output failed") require.Equal(t, output.String(), expected.String(), "output did not match") }) diff --git a/tool/tctl/common/decision/evaluate_ssh_command.go b/tool/tctl/common/decision/evaluate_ssh_command.go index 534cef252a90f..7f27353fe26b0 100644 --- a/tool/tctl/common/decision/evaluate_ssh_command.go +++ b/tool/tctl/common/decision/evaluate_ssh_command.go @@ -25,7 +25,6 @@ import ( "github.com/gravitational/teleport" decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/utils" ) // EvaluateSSHCommand is a command to evaluate @@ -59,8 +58,8 @@ func (c *EvaluateSSHCommand) FullCommand() string { } // Run executes the subcommand. -func (c *EvaluateSSHCommand) Run(ctx context.Context, clt Client) error { - resp, err := clt.DecisionClient().EvaluateSSHAccess(ctx, &decisionpb.EvaluateSSHAccessRequest{ +func (c *EvaluateSSHCommand) Run(ctx context.Context, clt decisionpb.DecisionServiceClient) error { + resp, err := clt.EvaluateSSHAccess(ctx, &decisionpb.EvaluateSSHAccessRequest{ Metadata: &decisionpb.RequestMetadata{PepVersionHint: teleport.Version}, SshIdentity: &decisionpb.SSHIdentity{}, Node: &decisionpb.Resource{ @@ -72,7 +71,7 @@ func (c *EvaluateSSHCommand) Run(ctx context.Context, clt Client) error { return trace.Wrap(err) } - if err := utils.WriteJSON(c.output, resp); err != nil { + if err := WriteProtoJSON(c.output, resp); err != nil { return trace.Wrap(err, "failed to marshal result") } diff --git a/tool/tctl/common/decision/evaluate_ssh_command_test.go b/tool/tctl/common/decision/evaluate_ssh_command_test.go index d3709e73d4dbc..d3bee5c53090b 100644 --- a/tool/tctl/common/decision/evaluate_ssh_command_test.go +++ b/tool/tctl/common/decision/evaluate_ssh_command_test.go @@ -27,7 +27,6 @@ import ( "github.com/gravitational/teleport" decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" - "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/tool/tctl/common/decision" ) @@ -76,17 +75,15 @@ func TestEvaluateSSH(t *testing.T) { var output bytes.Buffer cmd.Initialize(kingpin.New("tctl", "test").Command("decision", ""), &output) - svc := fakeDecisionService{ - decisionClient: fakeDecisionServiceClient{ - sshResponse: test.response, - }, + clt := fakeDecisionServiceClient{ + sshResponse: test.response, } - err := cmd.Run(context.Background(), svc) + err := cmd.Run(context.Background(), clt) require.NoError(t, err, "evaluating SSH access failed") var expected bytes.Buffer - utils.WriteJSON(&expected, test.response) + err = decision.WriteProtoJSON(&expected, test.response) require.NoError(t, err, "marshaling expected output failed") require.Equal(t, output.String(), expected.String(), "output did not match") })