diff --git a/lib/auth/auth.go b/lib/auth/auth.go index f6497fd1e2c8..d07263676e9f 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -193,6 +193,12 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { } cfg.ClusterConfiguration = clusterConfig } + if cfg.AutoUpdateService == nil { + cfg.AutoUpdateService, err = local.NewAutoUpdateService(cfg.Backend) + if err != nil { + return nil, trace.Wrap(err) + } + } if cfg.Restrictions == nil { cfg.Restrictions = local.NewRestrictionsService(cfg.Backend) } @@ -398,6 +404,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { Access: cfg.Access, DynamicAccessExt: cfg.DynamicAccessExt, ClusterConfiguration: cfg.ClusterConfiguration, + AutoUpdateService: cfg.AutoUpdateService, Restrictions: cfg.Restrictions, Apps: cfg.Apps, Kubernetes: cfg.Kubernetes, @@ -629,6 +636,7 @@ type Services struct { services.SPIFFEFederations services.AccessGraphSecretsGetter services.DevicesGetter + services.AutoUpdateService } // GetWebSession returns existing web session described by req. diff --git a/lib/auth/autoupdate/autoupdatev1/service.go b/lib/auth/autoupdate/autoupdatev1/service.go new file mode 100644 index 000000000000..555a03506d55 --- /dev/null +++ b/lib/auth/autoupdate/autoupdatev1/service.go @@ -0,0 +1,270 @@ +/* + * 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 autoupdatev1 + +import ( + "context" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/services" +) + +// Cache defines only read-only service methods. +type Cache interface { + // GetAutoUpdateConfig gets the autoupdate configuration from the backend. + GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error) + + // GetAutoUpdateVersion gets the autoupdate version from the backend. + GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error) +} + +// ServiceConfig holds configuration options for the autoupdate gRPC service. +type ServiceConfig struct { + // Authorizer is the authorizer used to check access to resources. + Authorizer authz.Authorizer + // Backend is the backend used to store autoupdate resources. + Backend services.AutoUpdateService + // Cache is the cache used to store autoupdate resources. + Cache Cache +} + +// Service implements the gRPC API layer for the Autoupdate. +type Service struct { + autoupdate.UnimplementedAutoUpdateServiceServer + + authorizer authz.Authorizer + backend services.AutoUpdateService + cache Cache +} + +// NewService returns a new Autoupdate API service using the given storage layer and authorizer. +func NewService(cfg ServiceConfig) (*Service, error) { + switch { + case cfg.Backend == nil: + return nil, trace.BadParameter("backend is required") + case cfg.Authorizer == nil: + return nil, trace.BadParameter("authorizer is required") + case cfg.Cache == nil: + return nil, trace.BadParameter("cache is required") + } + return &Service{ + authorizer: cfg.Authorizer, + backend: cfg.Backend, + cache: cfg.Cache, + }, nil +} + +// GetAutoUpdateConfig gets the current autoupdate config singleton. +func (s *Service) GetAutoUpdateConfig(ctx context.Context, req *autoupdate.GetAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbRead); err != nil { + return nil, trace.Wrap(err) + } + + config, err := s.cache.GetAutoUpdateConfig(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + return config, nil +} + +// CreateAutoUpdateConfig creates autoupdate config singleton. +func (s *Service) CreateAutoUpdateConfig(ctx context.Context, req *autoupdate.CreateAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbCreate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + config, err := s.backend.CreateAutoUpdateConfig(ctx, req.Config) + return config, trace.Wrap(err) +} + +// UpdateAutoUpdateConfig updates autoupdate config singleton. +func (s *Service) UpdateAutoUpdateConfig(ctx context.Context, req *autoupdate.UpdateAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + config, err := s.backend.UpdateAutoUpdateConfig(ctx, req.Config) + return config, trace.Wrap(err) +} + +// UpsertAutoUpdateConfig updates or creates autoupdate config singleton. +func (s *Service) UpsertAutoUpdateConfig(ctx context.Context, req *autoupdate.UpsertAutoUpdateConfigRequest) (*autoupdate.AutoUpdateConfig, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbCreate, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + config, err := s.backend.UpsertAutoUpdateConfig(ctx, req.Config) + return config, trace.Wrap(err) +} + +// DeleteAutoUpdateConfig deletes autoupdate config singleton. +func (s *Service) DeleteAutoUpdateConfig(ctx context.Context, req *autoupdate.DeleteAutoUpdateConfigRequest) (*emptypb.Empty, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateConfig, types.VerbDelete); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminAction(); err != nil { + return nil, trace.Wrap(err) + } + + if err := s.backend.DeleteAutoUpdateConfig(ctx); err != nil { + return nil, trace.Wrap(err) + } + return &emptypb.Empty{}, nil +} + +// GetAutoUpdateVersion gets the current autoupdate version singleton. +func (s *Service) GetAutoUpdateVersion(ctx context.Context, req *autoupdate.GetAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbRead); err != nil { + return nil, trace.Wrap(err) + } + + version, err := s.cache.GetAutoUpdateVersion(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + return version, nil +} + +// CreateAutoUpdateVersion creates autoupdate version singleton. +func (s *Service) CreateAutoUpdateVersion(ctx context.Context, req *autoupdate.CreateAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbCreate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + autoupdateVersion, err := s.backend.CreateAutoUpdateVersion(ctx, req.Version) + return autoupdateVersion, trace.Wrap(err) +} + +// UpdateAutoUpdateVersion updates autoupdate version singleton. +func (s *Service) UpdateAutoUpdateVersion(ctx context.Context, req *autoupdate.UpdateAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + autoupdateVersion, err := s.backend.UpdateAutoUpdateVersion(ctx, req.Version) + return autoupdateVersion, trace.Wrap(err) +} + +// UpsertAutoUpdateVersion updates or creates autoupdate version singleton. +func (s *Service) UpsertAutoUpdateVersion(ctx context.Context, req *autoupdate.UpsertAutoUpdateVersionRequest) (*autoupdate.AutoUpdateVersion, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbCreate, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminActionAllowReusedMFA(); err != nil { + return nil, trace.Wrap(err) + } + + autoupdateVersion, err := s.backend.UpsertAutoUpdateVersion(ctx, req.Version) + return autoupdateVersion, trace.Wrap(err) +} + +// DeleteAutoUpdateVersion deletes autoupdate version singleton. +func (s *Service) DeleteAutoUpdateVersion(ctx context.Context, req *autoupdate.DeleteAutoUpdateVersionRequest) (*emptypb.Empty, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbDelete); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminAction(); err != nil { + return nil, trace.Wrap(err) + } + + if err := s.backend.DeleteAutoUpdateVersion(ctx); err != nil { + return nil, trace.Wrap(err) + } + return &emptypb.Empty{}, nil +} diff --git a/lib/auth/autoupdate/autoupdatev1/service_test.go b/lib/auth/autoupdate/autoupdatev1/service_test.go new file mode 100644 index 000000000000..840fd9bbf94c --- /dev/null +++ b/lib/auth/autoupdate/autoupdatev1/service_test.go @@ -0,0 +1,278 @@ +// 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 autoupdatev1 + +import ( + "context" + "fmt" + "slices" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/backend/memory" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/services/local" + "github.com/gravitational/teleport/lib/utils" +) + +var allAdminStates = map[authz.AdminActionAuthState]string{ + authz.AdminActionAuthUnauthorized: "Unauthorized", + authz.AdminActionAuthNotRequired: "NotRequired", + authz.AdminActionAuthMFAVerified: "MFAVerified", + authz.AdminActionAuthMFAVerifiedWithReuse: "MFAVerifiedWithReuse", +} + +func stateToString(state authz.AdminActionAuthState) string { + str, ok := allAdminStates[state] + if !ok { + return fmt.Sprintf("unknown(%v)", state) + } + return str +} + +// otherAdminStates returns all admin states except for those passed in +func otherAdminStates(states []authz.AdminActionAuthState) []authz.AdminActionAuthState { + var out []authz.AdminActionAuthState + for state := range allAdminStates { + found := slices.Index(states, state) != -1 + if !found { + out = append(out, state) + } + } + return out +} + +// callMethod calls a method with given name in the DatabaseObjectService service +func callMethod(t *testing.T, service *Service, method string) error { + for _, desc := range autoupdate.AutoUpdateService_ServiceDesc.Methods { + if desc.MethodName == method { + _, err := desc.Handler(service, context.Background(), func(_ any) error { return nil }, nil) + return err + } + } + require.FailNow(t, "method %v not found", method) + panic("this line should never be reached: FailNow() should interrupt the test") +} + +func TestServiceAccess(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + allowedVerbs []string + allowedStates []authz.AdminActionAuthState + disallowedStates []authz.AdminActionAuthState + }{ + { + name: "CreateAutoUpdateConfig", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbCreate}, + }, + { + name: "UpdateAutoUpdateConfig", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbUpdate}, + }, + { + name: "UpsertAutoUpdateConfig", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbUpdate, types.VerbCreate}, + }, + { + name: "GetAutoUpdateConfig", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthUnauthorized, + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbRead}, + }, + { + name: "DeleteAutoUpdateConfig", + allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, + allowedVerbs: []string{types.VerbDelete}, + }, + // AutoUpdate version check. + { + name: "CreateAutoUpdateVersion", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbCreate}, + }, + { + name: "UpdateAutoUpdateVersion", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbUpdate}, + }, + { + name: "UpsertAutoUpdateVersion", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbUpdate, types.VerbCreate}, + }, + { + name: "GetAutoUpdateVersion", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthUnauthorized, + authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, + authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbRead}, + }, + { + name: "DeleteAutoUpdateVersion", + allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, + allowedVerbs: []string{types.VerbDelete}, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + // test the method with allowed admin states, each one separately. + t.Run("allowed admin states", func(t *testing.T) { + for _, state := range tt.allowedStates { + t.Run(stateToString(state), func(t *testing.T) { + for _, verbs := range utils.Combinations(tt.allowedVerbs) { + t.Run(fmt.Sprintf("verbs=%v", verbs), func(t *testing.T) { + service := newService(t, state, fakeChecker{allowedVerbs: verbs}) + err := callMethod(t, service, tt.name) + // expect access denied except with full set of verbs. + if len(verbs) == len(tt.allowedVerbs) { + require.False(t, trace.IsAccessDenied(err)) + } else { + require.True(t, trace.IsAccessDenied(err), "expected access denied for verbs %v, got err=%v", verbs, err) + } + }) + } + }) + } + }) + + // test the method with disallowed admin states; expect failures. + t.Run("disallowed admin states", func(t *testing.T) { + disallowedStates := otherAdminStates(tt.allowedStates) + if tt.disallowedStates != nil { + disallowedStates = tt.disallowedStates + } + for _, state := range disallowedStates { + t.Run(stateToString(state), func(t *testing.T) { + // it is enough to test against tt.allowedVerbs, + // this is the only different data point compared to the test cases above. + service := newService(t, state, fakeChecker{allowedVerbs: tt.allowedVerbs}) + err := callMethod(t, service, tt.name) + require.True(t, trace.IsAccessDenied(err)) + }) + } + }) + }) + } + + // verify that all declared methods have matching test cases + t.Run("verify coverage", func(t *testing.T) { + for _, method := range autoupdate.AutoUpdateService_ServiceDesc.Methods { + t.Run(method.MethodName, func(t *testing.T) { + match := false + for _, testCase := range testCases { + match = match || testCase.name == method.MethodName + } + require.True(t, match, "method %v without coverage, no matching tests", method.MethodName) + }) + } + }) +} + +type fakeChecker struct { + allowedVerbs []string + services.AccessChecker +} + +func (f fakeChecker) CheckAccessToRule(_ services.RuleContext, _ string, resource string, verb string) error { + if resource == types.KindAutoUpdateConfig || resource == types.KindAutoUpdateVersion { + for _, allowedVerb := range f.allowedVerbs { + if allowedVerb == verb { + return nil + } + } + } + + return trace.AccessDenied("access denied to rule=%v/verb=%v", resource, verb) +} + +func newService(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker) *Service { + t.Helper() + + bk, err := memory.New(memory.Config{}) + require.NoError(t, err) + + storage, err := local.NewAutoUpdateService(bk) + require.NoError(t, err) + + return newServiceWithStorage(t, authState, checker, storage) +} + +func newServiceWithStorage(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker, storage services.AutoUpdateService) *Service { + t.Helper() + + authorizer := authz.AuthorizerFunc(func(ctx context.Context) (*authz.Context, error) { + user, err := types.NewUser("alice") + if err != nil { + return nil, err + } + return &authz.Context{ + User: user, + Checker: checker, + AdminActionAuthState: authState, + }, nil + }) + + service, err := NewService(ServiceConfig{ + Authorizer: authorizer, + Backend: storage, + Cache: storage, + }) + require.NoError(t, err) + return service +} diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 6e63918f9985..3b5676369300 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -52,6 +52,7 @@ import ( assistv1pb "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" accessmonitoringrulesv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1" auditlogv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/auditlog/v1" + autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" clusterconfigv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1" crownjewelv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" dbobjectv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/dbobject/v1" @@ -77,6 +78,7 @@ import ( "github.com/gravitational/teleport/lib/auth/accessmonitoringrules/accessmonitoringrulesv1" "github.com/gravitational/teleport/lib/auth/assist/assistv1" "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/lib/auth/autoupdate/autoupdatev1" "github.com/gravitational/teleport/lib/auth/clusterconfig/clusterconfigv1" "github.com/gravitational/teleport/lib/auth/crownjewel/crownjewelv1" "github.com/gravitational/teleport/lib/auth/dbobject/dbobjectv1" @@ -6022,6 +6024,16 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { } accessmonitoringrulesv1pb.RegisterAccessMonitoringRulesServiceServer(server, accessMonitoringRuleServer) + autoUpdateServiceServer, err := autoupdatev1.NewService(autoupdatev1.ServiceConfig{ + Authorizer: cfg.Authorizer, + Backend: cfg.AuthServer.Services, + Cache: cfg.AuthServer.Services, + }) + if err != nil { + return nil, trace.Wrap(err) + } + autoupdatev1pb.RegisterAutoUpdateServiceServer(server, autoUpdateServiceServer) + // Only register the service if this is an open source build. Enterprise builds // register the actual service via an auth plugin, if we register here then all // Enterprise builds would fail with a duplicate service registered error. diff --git a/lib/auth/init.go b/lib/auth/init.go index 1f146d7f2b3c..72ac84cd200b 100644 --- a/lib/auth/init.go +++ b/lib/auth/init.go @@ -157,6 +157,9 @@ type InitConfig struct { // ClusterConfiguration is a services that holds cluster wide configuration. ClusterConfiguration services.ClusterConfiguration + // AutoUpdateService is a service of autoupdate configuration and version. + AutoUpdateService services.AutoUpdateService + // Restrictions is a service to access network restrictions, etc Restrictions services.Restrictions diff --git a/lib/service/service.go b/lib/service/service.go index e2777bf79df0..51157db4fd55 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -1999,6 +1999,7 @@ func (process *TeleportProcess) initAuthService() error { VersionStorage: process.storage, Authority: cfg.Keygen, ClusterConfiguration: cfg.ClusterConfiguration, + AutoUpdateService: cfg.AutoUpdateService, ClusterAuditConfig: cfg.Auth.AuditConfig, ClusterNetworkingConfig: cfg.Auth.NetworkingConfig, SessionRecordingConfig: cfg.Auth.SessionRecordingConfig, diff --git a/lib/service/servicecfg/config.go b/lib/service/servicecfg/config.go index c15f61c9d272..566990eb70be 100644 --- a/lib/service/servicecfg/config.go +++ b/lib/service/servicecfg/config.go @@ -170,6 +170,9 @@ type Config struct { // ClusterConfiguration is a service that provides cluster configuration ClusterConfiguration services.ClusterConfiguration + // AutoUpdateService is a service that provides auto update configuration and version. + AutoUpdateService services.AutoUpdateService + // CipherSuites is a list of TLS ciphersuites that Teleport supports. If // omitted, a Teleport selected list of defaults will be used. CipherSuites []uint16 diff --git a/lib/services/local/autoupdate.go b/lib/services/local/autoupdate.go index 21b4172767cc..a1a04a38ea65 100644 --- a/lib/services/local/autoupdate.go +++ b/lib/services/local/autoupdate.go @@ -36,14 +36,14 @@ const ( autoUpdateVersionPrefix = "auto_update_version" ) -// AutoupdateService is responsible for managing auto update configuration and version. -type AutoupdateService struct { +// AutoUpdateService is responsible for managing auto update configuration and version. +type AutoUpdateService struct { config *generic.ServiceWrapper[*autoupdate.AutoUpdateConfig] version *generic.ServiceWrapper[*autoupdate.AutoUpdateVersion] } -// NewAutoupdateService returns a new AutoupdateService. -func NewAutoupdateService(backend backend.Backend) (*AutoupdateService, error) { +// NewAutoUpdateService returns a new AutoUpdateService. +func NewAutoUpdateService(backend backend.Backend) (*AutoUpdateService, error) { config, err := generic.NewServiceWrapper( generic.ServiceWrapperConfig[*autoupdate.AutoUpdateConfig]{ Backend: backend, @@ -75,14 +75,14 @@ func NewAutoupdateService(backend backend.Backend) (*AutoupdateService, error) { return nil, trace.Wrap(err) } - return &AutoupdateService{ + return &AutoUpdateService{ config: config, version: version, }, nil } // CreateAutoUpdateConfig creates an auto update configuration singleton. -func (s *AutoupdateService) CreateAutoUpdateConfig( +func (s *AutoUpdateService) CreateAutoUpdateConfig( ctx context.Context, c *autoupdate.AutoUpdateConfig, ) (*autoupdate.AutoUpdateConfig, error) { @@ -91,7 +91,7 @@ func (s *AutoupdateService) CreateAutoUpdateConfig( } // UpdateAutoUpdateConfig updates an auto update configuration singleton. -func (s *AutoupdateService) UpdateAutoUpdateConfig( +func (s *AutoUpdateService) UpdateAutoUpdateConfig( ctx context.Context, c *autoupdate.AutoUpdateConfig, ) (*autoupdate.AutoUpdateConfig, error) { @@ -100,7 +100,7 @@ func (s *AutoupdateService) UpdateAutoUpdateConfig( } // UpsertAutoUpdateConfig sets an auto update configuration. -func (s *AutoupdateService) UpsertAutoUpdateConfig( +func (s *AutoUpdateService) UpsertAutoUpdateConfig( ctx context.Context, c *autoupdate.AutoUpdateConfig, ) (*autoupdate.AutoUpdateConfig, error) { @@ -109,18 +109,18 @@ func (s *AutoupdateService) UpsertAutoUpdateConfig( } // GetAutoUpdateConfig gets the auto update configuration from the backend. -func (s *AutoupdateService) GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error) { +func (s *AutoUpdateService) GetAutoUpdateConfig(ctx context.Context) (*autoupdate.AutoUpdateConfig, error) { config, err := s.config.GetResource(ctx, types.MetaNameAutoUpdateConfig) return config, trace.Wrap(err) } // DeleteAutoUpdateConfig deletes the auto update configuration from the backend. -func (s *AutoupdateService) DeleteAutoUpdateConfig(ctx context.Context) error { +func (s *AutoUpdateService) DeleteAutoUpdateConfig(ctx context.Context) error { return trace.Wrap(s.config.DeleteResource(ctx, types.MetaNameAutoUpdateConfig)) } // CreateAutoUpdateVersion creates an autoupdate version resource. -func (s *AutoupdateService) CreateAutoUpdateVersion( +func (s *AutoUpdateService) CreateAutoUpdateVersion( ctx context.Context, v *autoupdate.AutoUpdateVersion, ) (*autoupdate.AutoUpdateVersion, error) { @@ -129,7 +129,7 @@ func (s *AutoupdateService) CreateAutoUpdateVersion( } // UpdateAutoUpdateVersion updates an autoupdate version resource. -func (s *AutoupdateService) UpdateAutoUpdateVersion( +func (s *AutoUpdateService) UpdateAutoUpdateVersion( ctx context.Context, v *autoupdate.AutoUpdateVersion, ) (*autoupdate.AutoUpdateVersion, error) { @@ -138,7 +138,7 @@ func (s *AutoupdateService) UpdateAutoUpdateVersion( } // UpsertAutoUpdateVersion sets autoupdate version resource. -func (s *AutoupdateService) UpsertAutoUpdateVersion( +func (s *AutoUpdateService) UpsertAutoUpdateVersion( ctx context.Context, v *autoupdate.AutoUpdateVersion, ) (*autoupdate.AutoUpdateVersion, error) { @@ -147,12 +147,12 @@ func (s *AutoupdateService) UpsertAutoUpdateVersion( } // GetAutoUpdateVersion gets the auto update version from the backend. -func (s *AutoupdateService) GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error) { +func (s *AutoUpdateService) GetAutoUpdateVersion(ctx context.Context) (*autoupdate.AutoUpdateVersion, error) { version, err := s.version.GetResource(ctx, types.MetaNameAutoUpdateVersion) return version, trace.Wrap(err) } // DeleteAutoUpdateVersion deletes the auto update version from the backend. -func (s *AutoupdateService) DeleteAutoUpdateVersion(ctx context.Context) error { +func (s *AutoUpdateService) DeleteAutoUpdateVersion(ctx context.Context) error { return trace.Wrap(s.version.DeleteResource(ctx, types.MetaNameAutoUpdateVersion)) } diff --git a/lib/services/local/autoupdate_test.go b/lib/services/local/autoupdate_test.go index a27af676589d..0a6f4e5bd2b8 100644 --- a/lib/services/local/autoupdate_test.go +++ b/lib/services/local/autoupdate_test.go @@ -43,7 +43,7 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) { bk, err := memory.New(memory.Config{}) require.NoError(t, err) - service, err := NewAutoupdateService(bk) + service, err := NewAutoUpdateService(bk) require.NoError(t, err) ctx := context.Background() @@ -99,7 +99,7 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) { bk, err := memory.New(memory.Config{}) require.NoError(t, err) - service, err := NewAutoupdateService(bk) + service, err := NewAutoUpdateService(bk) require.NoError(t, err) ctx := context.Background() @@ -155,7 +155,7 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) { bk, err := memory.New(memory.Config{}) require.NoError(t, err) - service, err := NewAutoupdateService(bk) + service, err := NewAutoUpdateService(bk) require.NoError(t, err) ctx := context.Background() @@ -190,7 +190,7 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) { bk, err := memory.New(memory.Config{}) require.NoError(t, err) - service, err := NewAutoupdateService(bk) + service, err := NewAutoUpdateService(bk) require.NoError(t, err) ctx := context.Background()