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(),
+ }
+}