diff --git a/api/client/client.go b/api/client/client.go index 28871aa871957..e3e1184250572 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -74,6 +74,7 @@ import ( crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" dbobjectv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" dbobjectimportrulev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobjectimportrule/v1" + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" discoveryconfigv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1" dynamicwindowsv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/dynamicwindows/v1" @@ -5217,3 +5218,9 @@ func (c *Client) ProvisioningServiceClient() provisioningv1.ProvisioningServiceC func (c *Client) IntegrationsClient() integrationpb.IntegrationServiceClient { return c.integrationsClient() } + +// DecisionClient returns an unadorned DecisionService client using the +// underlying Auth gRPC connection. +func (c *Client) DecisionClient() decisionpb.DecisionServiceClient { + return decisionpb.NewDecisionServiceClient(c.conn) +} diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 741f4626c957c..2ba196f37fd39 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -56,6 +56,7 @@ import ( crownjewelv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" dbobjectv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" dbobjectimportrulev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobjectimportrule/v1" + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" discoveryconfigv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1" dynamicwindowsv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/dynamicwindows/v1" gitserverv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/gitserver/v1" @@ -109,6 +110,7 @@ import ( "github.com/gravitational/teleport/lib/auth/vnetconfig/vnetconfigv1" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/backend" + "github.com/gravitational/teleport/lib/decision/decisionv1" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/httplib" @@ -5466,6 +5468,14 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { loginrulev1pb.RegisterLoginRuleServiceServer(server, loginrulev1.NotImplementedService{}) } + decisionService, err := decisionv1.NewService(decisionv1.ServiceConfig{ + Authorizer: cfg.Authorizer, + }) + if err != nil { + return nil, trace.Wrap(err) + } + decisionpb.RegisterDecisionServiceServer(server, decisionService) + return authServer, nil } diff --git a/lib/decision/decisionv1/decision_service.go b/lib/decision/decisionv1/decision_service.go new file mode 100644 index 0000000000000..d45fbf3caeefb --- /dev/null +++ b/lib/decision/decisionv1/decision_service.go @@ -0,0 +1,48 @@ +// Teleport +// Copyright (C) 2024 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 decisionv1 + +import ( + "github.com/gravitational/trace" + + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/lib/authz" +) + +// ServiceConfig holds creation parameters for [Service]. +type ServiceConfig struct { + // Authorizer used by the service. + Authorizer authz.Authorizer +} + +// Service implements the teleport.decision.v1alpha1.DecisionService gRPC API. +type Service struct { + decisionpb.UnimplementedDecisionServiceServer + + authorizer authz.Authorizer +} + +// NewService creates a new [Service] instance. +func NewService(cfg ServiceConfig) (*Service, error) { + if cfg.Authorizer == nil { + return nil, trace.BadParameter("param Authorizer required") + } + + return &Service{ + authorizer: cfg.Authorizer, + }, nil +} diff --git a/lib/decision/decisionv1/decision_service_test.go b/lib/decision/decisionv1/decision_service_test.go new file mode 100644 index 0000000000000..72f92fcfbe8b7 --- /dev/null +++ b/lib/decision/decisionv1/decision_service_test.go @@ -0,0 +1,45 @@ +// Teleport +// Copyright (C) 2024 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 decisionv1_test + +import ( + "context" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/assert" + + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" +) + +func TestDecisionService_notImplemented(t *testing.T) { + t.Parallel() + + env := NewTestenv(t) + decisionClient := env.DecisionClient + ctx := context.Background() + + t.Run("EvaluateSSHAccess", func(t *testing.T) { + _, err := decisionClient.EvaluateSSHAccess(ctx, &decisionpb.EvaluateSSHAccessRequest{}) + assert.ErrorAs(t, err, new(*trace.NotImplementedError), "EvaluateSSHAccess error mismatch") + }) + + t.Run("EvaluateDatabaseAccess", func(t *testing.T) { + _, err := decisionClient.EvaluateDatabaseAccess(ctx, &decisionpb.EvaluateDatabaseAccessRequest{}) + assert.ErrorAs(t, err, new(*trace.NotImplementedError), "EvaluateDatabaseAccess error mismatch") + }) +} diff --git a/lib/decision/decisionv1/env_test.go b/lib/decision/decisionv1/env_test.go new file mode 100644 index 0000000000000..6211cdb0d94ca --- /dev/null +++ b/lib/decision/decisionv1/env_test.go @@ -0,0 +1,74 @@ +// Teleport +// Copyright (C) 2024 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 decisionv1_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/constants" + decisionpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/decision/v1alpha1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/auth/authclient" +) + +// Testenv is a test environment for decisionv1.Service. +type Testenv struct { + TestServer *auth.TestServer + + // AuthAdminClient is an admin Auth client. + AuthAdminClient *authclient.Client + + // DecisionClient is an admin decision client. + // Created from AuthAdminClient. + DecisionClient decisionpb.DecisionServiceClient +} + +func NewTestenv(t *testing.T) *Testenv { + t.Helper() + + testServer, err := auth.NewTestServer(auth.TestServerConfig{ + Auth: auth.TestAuthServerConfig{ + Dir: t.TempDir(), + AuthPreferenceSpec: &types.AuthPreferenceSpecV2{ + SecondFactor: constants.SecondFactorOTP, // Required. + }, + }, + }) + require.NoError(t, err, "NewTestServer failed") + t.Cleanup(func() { + assert.NoError(t, + testServer.Shutdown(context.Background()), + "testServer.Shutdown failed") + }) + + adminClient, err := testServer.NewClient(auth.TestAdmin()) + require.NoError(t, err, "NewClient failed") + t.Cleanup(func() { + assert.NoError(t, adminClient.Close(), "adminClient.Close() failed") + }) + + return &Testenv{ + TestServer: testServer, + AuthAdminClient: adminClient, + DecisionClient: adminClient.DecisionClient(), + } +}