From 929ae90f8bcd97302fe258a60eb4942d083b550b Mon Sep 17 00:00:00 2001 From: Jakub Nyckowski Date: Tue, 7 May 2024 11:48:24 -0400 Subject: [PATCH] Add Crown Jewel resource (#40914) * Update labels datatype in Teleport crownjewel proto This commit modifies the labels from a map to repeated TeleportLabel in CrownJewel proto, and accordingly updates the getter function in proto generated code. The newly introduced TeleportLabel type contains a Key and repeated values for improved flexibility. Clean up Update and restructure CrownJewel service proto The code modifications include renaming and restructuring several types and altering various functions in the CrownJewel service proto. Major changes involve renaming DeleteCrownJewel Rename 'kind' to 'kinds' in crownjewel API This commit changes the 'kind' field to 'kinds' in the crownjewel API to better reflect its purpose of holding multiple resource types. Changes are made across several files - 'crownjewel.go', 'crownjewel.pb.go' and 'crownjewel.proto'. After rebase fixes Remove CrownJewel type from several files This commit represents the removal of the CrownJewel type from several files. CrownJewel type references are deleted in certain Go files, and imports of CrownJewel in multiple files are being adjusted. The specifics of why this type is removed is not evident from the diffs, but the deletions will affect the related functionalities. Add CrownJewel update functionality This update enables the 'UpdateCrownJewel' functionality. If a 'CrownJewel' resource already exists, it can now be updated instead of returning an error. This enhances flexibility when dealing with resource management. It also introduces the ARN field into the 'AWSMatcher' service. Update CrownJewel service with pagination and improve matchers This update adds pagination to the CrownJewel service for more efficient data handling. It also improves the matching capabilities with the inclusion of Teleport and AWS matchers and allows for selective querying with these matchers. Additionally, it includes various changes to structure and naming for better clarity and ease of use. Refactor CrownJewel protos to use ResourceHeader This refactoring replaces the previous structure of CrownJewel proto's fields Kind, Sub_kind, Version, and Metadata with a single object called Header (ResourceHeader). It simplifies the code structure and harmonizes it with other protos. Now, Header contains the essential attributes that were previously distributed across multiple separate fields. Add GetCrownJewels method to cache and accessgraph The function GetCrownJewels has been added to the cache and the accessgraph. It retrieves all CrownJewel instances from the cache. Additionally, CrownJewel retrieval functionality has been removed from auth server and existing usages have been refactored. Implement Crown Jewel creation feature Updated the method `createCrownJewel` in the resources command to handle Crown Jewel creation. Introduced a new gRPC service `crownjewelv1` to handle client-server interactions for Crown Jewel objects. Crown Jewel expiry configuration has now also been enabled. Add missing files I have no words... Update authentication and event handling Refactored the argument from 'auth.ClientI' to '*auth.Client' in 'createCrownJewel' function in 'resource_command.go'. Removed unused import 'durationpb' in 'auth_with_roles.go'. Added a new event code 'AccessGraphAccessPathChangedCode' in 'codes.go' and 'AccessGraphAccessPathChanged' in 'api.go' for better monitoring and logging of access path changes. Add access path changed event and relevant changes. update api add grpc update api add cache support add protos * Fixes after rebase * Refactor Crown Jewel resource methods Updated API methods related to the Crown Jewel resource to improve clarity and consistency. Renamed `GetCrownJewels` to `ListCrownJewels`, added pagination parameters, and ensured uniform handling of the Crown Jewel resource across different parts of the codebase. Updated relevant comments for better code understanding. * Removed CrownJewel converter, updated CrownJewel references This commit removes the CrownJewel converter and updates references from the old CrownJewel type to the new protobuf-defined CrownJewel type, streamlining the codebase. The commit also disables CrownJewel deletion since it is not supported yet. Some minor errors were fixed regarding CrownJewel resource names. * Implement Crown Jewel deletion functionality The crown jewel deletion feature has been enabled by uncommenting and simplifying the code block. Now, the delete action is directly performed on the specific 'crown jewel' mentioned based on its name. The previously unsupported action returns an informative success message after the deletion. * Refactored function 'allCombinations' and added authorization checks Moved 'allCombinations' function into 'utils' package, renamed it to 'Combinations', and updated its references in 'dbobjectimportrulev1' and 'dbobjectv1' tests. Additionally, introduced admin authorization checks to 'CreateCrownJewels', 'UpdateCrownJewels', and 'DeleteCrownJewels'. Created corresponding tests for these changes. * Add CrownJewel service to cache tests The CrownJewel service has been added to the cache test file. The service creation is implemented and assigned to new fields in multiple methods within the cache tests. A new test function `TestCrownJewel` has also been added to validate CRUD operations for the service. * Add access path changed event and update Crown Jewel tests Added a new event code for when an access path changes in the access graph, and introduced new API calls to handle these situations. Also, updated crown jewel related tests and fixed copyright year in several files. * Update protobuf import path The protobuf import path was updated from "github.com/golang/protobuf/proto" to "google.golang.org/protobuf/proto" to ensure correct use and to prevent potential issues, while maintaining functionality in crown_jewels.go. * Refactor CrownJewel and AccessGraph services This commit optimizes the ListCrownJewelsResponse in the CrownJewel service by directly assigning the response instead of using an extra loop. Additionally, the EventsStream in the AccessGraph service and related tests have been updated to EventsStreamV2, reflecting the implementation of new versions of these methods. * Add CrownJewel resource in access controls and tests In both the cache test and access control files, CrownJewel resource has been included. This provides the system's ability to manage and test CrownJewel resources. Additionally, it allows the handling of CrownJewel resources in client events. * Simplify implementation * Update copyright dates and add license headers Updated the copyright dates in several files to reflect the new year. Additionally, license headers have been added to some files where they were previously missing, ensuring the proper copyrights and GPL notice are visible. * Refactor CrownJewel codebase for better readability Fixed various typographical errors in CrownJewel's related functions and removed a TODO comment. Improved function and function calls' names that were incorrectly referring to different modules, making the code easier to read and understand across different CrownJewel services. * Add validation for Teleport and AWS matchers in service The validation has been added for Teleport and AWS matchers in the service layer to ensure that essential details are not missed when setting matchers. This includes checks to ensure that kinds and labels for Teleport, and types and tags for AWS matchers are properly set. * gci * Add CrownJewel update functionality and refactor related services This commit adds the ability to update a CrownJewel resource within the ResourceCommand. It also includes refactoring of related services, condensed code logic, and improved utilization of existing methods for marshaling/unmarshaling CrownJewel. Specific changes in service.go and crown_jewels.go files have reduced code redundancy and enhanced the code structure. * Relocate and update crown_jewels_test file This commit moves the crown_jewels_test from the local to the services library and adds test cases for unmarshalling Crown Jewels data. Also, import statements for labelv1 and utils were introduced to support these changes. * Update crown jewels test with AWS matchers Revised the crown jewels test file by including AWS matchers section in the CrownJewel object. The change ensures to specifically match AWS infrastructure following certain parameters, thus enhancing the specificity and flexibility of our matchers. Apart from this, minor reordering of objects has also been done for better code organization. * Added upsertCrownJewel function and related proto message The commit adds the UpsertCrownJewel function in the service.go file, which enables upsert operations on crown jewel resources. Alongside, the UpsertCrownJewelRequest proto message is added to the crownjewel_service.pb.go file to handle such requests. * Refactor CrownJewelsClient to use upsert This change refactors the CrownJewelsClient to use the upsert method when the force flag is on. The code has been streamlined by eliminating the need for 'already exist' error checking and separate create and update methods. * Add GetCrownJewel functionality and refactor validation A GetCrownJewel function has been added to the Service, which retrieves a specific CrownJewel resource. Also, the CrownJewel validation process has been refactored, moving the validateCrownJewel function from service.go into the crownjewel library and renaming it to ValidateCrownJewel. These changes improve modularity and code organization. * Add VerbCreate check and test for CrownJewel validation The checkAccessToKind function now also checks for the VerbCreate permission. Additionally, a new test file, object_test.go, was added to validate the functionality of the CrownJewel object. This includes various validation checks such as "NilCrownJewel", "ValidCrownJewel", "MissingMatchers", among others. * Refactor CrownJewel resource handling and validation The codebase was updated to improve CrownJewel resources handling. This includes modifications to list, get, create, update, upsert, and delete functions in several files. Additionally, a validation function has been implemented for CrownJewel objects, ensuring required fields are present. Depreciated logger service has also been removed. * Add cache implementation to CrownJewel service The CrownJewel service has been enhanced by integrating it with cache. The main changes include adding "Cache" as part of ServiceConfig and using cache to fetch crown jewels instead of directly using the backend services. The cache interface with the methods "ListCrownJewels" and "GetCrownJewel" has been added to handle these operations. * Add Cache to service configuration in crownjewelv1 tests In this commit, we added the Cache field to the service configuration used in the tests for the crownjewelv1 service. This ensures that the service configuration used in the tests matches the expected real-world configuration. * Update CrownJewelsService to use ConditionalUpdateResource This commit changes the CrownJewelsService's `UpdateCrownJewel` method to use the `ConditionalUpdateResource` method instead of the `UpdateResource` method. This change will provide a more appropriate update strategy for the specific needs of the CrownJewelsService. * Replace 'Cache' with 'Reader' in crownjewel service & remove DeleteAllCrownJewelsRequest The 'Cache' in crownjewel service has been replaced with 'Reader' to improve code readability and to match with its functionality. Also, the DeleteAllCrownJewelsRequest functionality was removed as it posed a high risk potential for data loss and it wasn * Update tests for Crown Jewel service This commit updates the test suite of the Crown Jewel service. An object is fetched from the backend before updating its expiry time, ensuring the revision is populated. In addition, a new test is introduced to handle the scenario where the revision is missing when updating a Crown Jewel. * Refactor protobuf marshaling with utils This commit replaces the usage of the `protojson` method for marshaling and unmarshaling protobuf objects in `crown_jewel.go` with the `FastMarshal` and `FastUnmarshal` methods from `utils`. protojson is failing to unmarshal resources. * Update CrownJewel test and serialization methods Updated the test condition in crown_jewels_test.go to use Proto.Equal for object comparisons. Also, swapped out the FastMarshal and FastUnmarshal methods with protojson's Marshal and Unmarshal in the CrownJewels service. * Add missing license --------- Co-authored-by: Tiago Silva --- api/client/client.go | 13 +- api/client/crownjewel/crownjewel.go | 106 ++++++ api/client/events.go | 8 + .../crownjewel/v1/crownjewel_service.pb.go | 348 +++++++++++------- .../v1/crownjewel_service_grpc.pb.go | 78 ++++ .../crownjewel/v1/crownjewel_service.proto | 18 +- api/types/constants.go | 3 +- lib/auth/accesspoint/accesspoint.go | 1 + lib/auth/auth.go | 14 + lib/auth/authclient/api.go | 7 + lib/auth/authclient/clt.go | 11 +- lib/auth/crownjewel/crownjewelv1/service.go | 234 ++++++++++++ .../crownjewel/crownjewelv1/service_test.go | 219 +++++++++++ lib/auth/crownjewel/object.go | 101 +++++ lib/auth/crownjewel/object_test.go | 224 +++++++++++ lib/auth/dbobject/dbobjectv1/service_test.go | 26 +- .../dbobjectimportrulev1/service_test.go | 26 +- lib/auth/grpcserver.go | 12 + lib/auth/init.go | 3 + lib/cache/cache.go | 40 +- lib/cache/cache_test.go | 85 ++++- lib/cache/collections.go | 61 +++ lib/events/api.go | 4 + lib/events/codes.go | 3 + lib/service/service.go | 6 +- lib/services/crown_jewel.go | 87 +++++ lib/services/crown_jewels_test.go | 125 +++++++ lib/services/discoveryconfig.go | 2 +- lib/services/local/crown_jewels.go | 85 +++++ lib/services/local/crown_jewels_test.go | 305 +++++++++++++++ lib/services/local/events.go | 31 ++ lib/services/presets.go | 1 + lib/services/resource.go | 2 + lib/services/services.go | 1 + lib/services/useracl.go | 4 + lib/utils/algorithms.go | 37 ++ lib/utils/algorithms_test.go | 31 ++ tool/tctl/common/collection.go | 34 ++ tool/tctl/common/resource_command.go | 60 +++ 39 files changed, 2272 insertions(+), 184 deletions(-) create mode 100644 api/client/crownjewel/crownjewel.go create mode 100644 lib/auth/crownjewel/crownjewelv1/service.go create mode 100644 lib/auth/crownjewel/crownjewelv1/service_test.go create mode 100644 lib/auth/crownjewel/object.go create mode 100644 lib/auth/crownjewel/object_test.go create mode 100644 lib/services/crown_jewel.go create mode 100644 lib/services/crown_jewels_test.go create mode 100644 lib/services/local/crown_jewels.go create mode 100644 lib/services/local/crown_jewels_test.go create mode 100644 lib/utils/algorithms.go create mode 100644 lib/utils/algorithms_test.go diff --git a/api/client/client.go b/api/client/client.go index 94afd1feef3ac..0ea0dbcb07b48 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -49,6 +49,8 @@ import ( "github.com/gravitational/teleport/api/breaker" "github.com/gravitational/teleport/api/client/accesslist" "github.com/gravitational/teleport/api/client/accessmonitoringrules" + "github.com/gravitational/teleport/api/client/crownjewel" + crownjewelapi "github.com/gravitational/teleport/api/client/crownjewel" "github.com/gravitational/teleport/api/client/discoveryconfig" "github.com/gravitational/teleport/api/client/externalauditstorage" kubewaitingcontainerclient "github.com/gravitational/teleport/api/client/kubewaitingcontainer" @@ -64,6 +66,7 @@ import ( accessmonitoringrulev1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1" auditlogpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/auditlog/v1" clusterconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/clusterconfig/v1" + 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" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" @@ -4892,8 +4895,16 @@ func (c *Client) DiscoveryConfigClient() *discoveryconfig.Client { return discoveryconfig.NewClient(discoveryconfigv1.NewDiscoveryConfigServiceClient(c.conn)) } +// CrownJewelServiceClient returns a CrownJewel client. +// Clients connecting to older Teleport versions, still get a CrownJewel client +// when calling this method, but all RPCs will return "not implemented" errors +// (as per the default gRPC behavior). +func (c *Client) CrownJewelServiceClient() *crownjewelapi.Client { + return crownjewel.NewClient(crownjewelv1.NewCrownJewelServiceClient(c.conn)) +} + // UserLoginStateClient returns a user login state client. -// Clients connecting to older Teleport versions, still get a user login state client +// Clients connecting to older Teleport versions, still get a user login state client // when calling this method, but all RPCs will return "not implemented" errors // (as per the default gRPC behavior). func (c *Client) UserLoginStateClient() *userloginstate.Client { diff --git a/api/client/crownjewel/crownjewel.go b/api/client/crownjewel/crownjewel.go new file mode 100644 index 0000000000000..710b5f1a66936 --- /dev/null +++ b/api/client/crownjewel/crownjewel.go @@ -0,0 +1,106 @@ +// Copyright 2024 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package crownjewel + +import ( + "context" + + "github.com/gravitational/trace" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" +) + +// Client is a client for the Crown Jewel API. +type Client struct { + grpcClient crownjewelv1.CrownJewelServiceClient +} + +// NewClient creates a new Discovery Config client. +func NewClient(grpcClient crownjewelv1.CrownJewelServiceClient) *Client { + return &Client{ + grpcClient: grpcClient, + } +} + +// ListCrownJewels returns a list of Crown Jewels. +func (c *Client) ListCrownJewels(ctx context.Context, pageSize int64, nextToken string) ([]*crownjewelv1.CrownJewel, string, error) { + resp, err := c.grpcClient.ListCrownJewels(ctx, &crownjewelv1.ListCrownJewelsRequest{ + PageSize: pageSize, + PageToken: nextToken, + }) + if err != nil { + return nil, "", trace.Wrap(err) + } + + return resp.CrownJewels, resp.NextPageToken, nil +} + +// CreateCrownJewel creates a new Crown Jewel. +func (c *Client) CreateCrownJewel(ctx context.Context, req *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) { + rsp, err := c.grpcClient.CreateCrownJewel(ctx, &crownjewelv1.CreateCrownJewelRequest{ + CrownJewels: req, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return rsp, nil +} + +// GetCrownJewel returns a Crown Jewel by name. +func (c *Client) GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.CrownJewel, error) { + rsp, err := c.grpcClient.GetCrownJewel(ctx, &crownjewelv1.GetCrownJewelRequest{ + Name: name, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return rsp, nil +} + +// UpdateCrownJewel updates an existing Crown Jewel. +func (c *Client) UpdateCrownJewel(ctx context.Context, req *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) { + rsp, err := c.grpcClient.UpdateCrownJewel(ctx, &crownjewelv1.UpdateCrownJewelRequest{ + CrownJewels: req, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return rsp, nil +} + +// UpsertCrownJewel upserts a Crown Jewel. +func (c *Client) UpsertCrownJewel(ctx context.Context, req *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) { + rsp, err := c.grpcClient.UpsertCrownJewel(ctx, &crownjewelv1.UpsertCrownJewelRequest{ + CrownJewels: req, + }) + if err != nil { + return nil, trace.Wrap(err) + } + return rsp, nil +} + +// DeleteCrownJewel deletes a Crown Jewel. +func (c *Client) DeleteCrownJewel(ctx context.Context, name string) error { + _, err := c.grpcClient.DeleteCrownJewel(ctx, &crownjewelv1.DeleteCrownJewelRequest{ + Name: name, + }) + return trace.Wrap(err) +} + +// DeleteAllCrownJewels deletes all Crown Jewels. +// Not implemented. Added to satisfy the interface. +func (c *Client) DeleteAllCrownJewels(_ context.Context) error { + return trace.NotImplemented("DeleteAllCrownJewels is not implemented") +} diff --git a/api/client/events.go b/api/client/events.go index f3910d5dad2cd..6b9d9240a723a 100644 --- a/api/client/events.go +++ b/api/client/events.go @@ -19,6 +19,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" accessmonitoringrulesv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1" + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" kubewaitingcontainerpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/kubewaitingcontainer/v1" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" @@ -61,6 +62,10 @@ func EventToGRPC(in types.Event) (*proto.Event, error) { out.Resource = &proto.Event_AccessMonitoringRule{ AccessMonitoringRule: r, } + case *crownjewelv1.CrownJewel: + out.Resource = &proto.Event_CrownJewel{ + CrownJewel: r, + } } case *types.ResourceHeader: out.Resource = &proto.Event_ResourceHeader{ @@ -478,6 +483,9 @@ func EventFromGRPC(in *proto.Event) (*types.Event, error) { } else if r := in.GetAccessMonitoringRule(); r != nil { out.Resource = types.Resource153ToLegacy(r) return &out, nil + } else if r := in.GetCrownJewel(); r != nil { + out.Resource = types.Resource153ToLegacy(r) + return &out, nil } else { return nil, trace.BadParameter("received unsupported resource %T", in.Resource) } diff --git a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go index 2f53ac3a959c9..338c16b88838d 100644 --- a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go +++ b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service.pb.go @@ -83,6 +83,55 @@ func (x *CreateCrownJewelRequest) GetCrownJewels() *CrownJewel { return nil } +// GetCrownJewelRequest is a request to get a CrownJewel by name. +type GetCrownJewelRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Name is the name of the CrownJewel to get. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *GetCrownJewelRequest) Reset() { + *x = GetCrownJewelRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCrownJewelRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCrownJewelRequest) ProtoMessage() {} + +func (x *GetCrownJewelRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCrownJewelRequest.ProtoReflect.Descriptor instead. +func (*GetCrownJewelRequest) Descriptor() ([]byte, []int) { + return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{1} +} + +func (x *GetCrownJewelRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + // ListCrownJewelsRequest is a request to get a list of CrownJewels. type ListCrownJewelsRequest struct { state protoimpl.MessageState @@ -99,7 +148,7 @@ type ListCrownJewelsRequest struct { func (x *ListCrownJewelsRequest) Reset() { *x = ListCrownJewelsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[1] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -112,7 +161,7 @@ func (x *ListCrownJewelsRequest) String() string { func (*ListCrownJewelsRequest) ProtoMessage() {} func (x *ListCrownJewelsRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[1] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -125,7 +174,7 @@ func (x *ListCrownJewelsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListCrownJewelsRequest.ProtoReflect.Descriptor instead. func (*ListCrownJewelsRequest) Descriptor() ([]byte, []int) { - return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{1} + return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{2} } func (x *ListCrownJewelsRequest) GetPageSize() int64 { @@ -157,7 +206,7 @@ type ListCrownJewelsResponse struct { func (x *ListCrownJewelsResponse) Reset() { *x = ListCrownJewelsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[2] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -170,7 +219,7 @@ func (x *ListCrownJewelsResponse) String() string { func (*ListCrownJewelsResponse) ProtoMessage() {} func (x *ListCrownJewelsResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[2] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -183,7 +232,7 @@ func (x *ListCrownJewelsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListCrownJewelsResponse.ProtoReflect.Descriptor instead. func (*ListCrownJewelsResponse) Descriptor() ([]byte, []int) { - return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{2} + return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{3} } func (x *ListCrownJewelsResponse) GetCrownJewels() []*CrownJewel { @@ -212,7 +261,7 @@ type UpdateCrownJewelRequest struct { func (x *UpdateCrownJewelRequest) Reset() { *x = UpdateCrownJewelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[3] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -225,7 +274,7 @@ func (x *UpdateCrownJewelRequest) String() string { func (*UpdateCrownJewelRequest) ProtoMessage() {} func (x *UpdateCrownJewelRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[3] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -238,7 +287,7 @@ func (x *UpdateCrownJewelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateCrownJewelRequest.ProtoReflect.Descriptor instead. func (*UpdateCrownJewelRequest) Descriptor() ([]byte, []int) { - return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{3} + return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{4} } func (x *UpdateCrownJewelRequest) GetCrownJewels() *CrownJewel { @@ -248,33 +297,32 @@ func (x *UpdateCrownJewelRequest) GetCrownJewels() *CrownJewel { return nil } -// DeleteCrownJewelRequest is a request to delete a CrownJewel. -type DeleteCrownJewelRequest struct { +// UpsertCrownJewelRequest is a request to upsert a CrownJewel. +type UpsertCrownJewelRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Name is the name of the CrownJewel to delete. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + CrownJewels *CrownJewel `protobuf:"bytes,1,opt,name=crown_jewels,json=crownJewels,proto3" json:"crown_jewels,omitempty"` } -func (x *DeleteCrownJewelRequest) Reset() { - *x = DeleteCrownJewelRequest{} +func (x *UpsertCrownJewelRequest) Reset() { + *x = UpsertCrownJewelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[4] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeleteCrownJewelRequest) String() string { +func (x *UpsertCrownJewelRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteCrownJewelRequest) ProtoMessage() {} +func (*UpsertCrownJewelRequest) ProtoMessage() {} -func (x *DeleteCrownJewelRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[4] +func (x *UpsertCrownJewelRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -285,42 +333,45 @@ func (x *DeleteCrownJewelRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteCrownJewelRequest.ProtoReflect.Descriptor instead. -func (*DeleteCrownJewelRequest) Descriptor() ([]byte, []int) { - return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{4} +// Deprecated: Use UpsertCrownJewelRequest.ProtoReflect.Descriptor instead. +func (*UpsertCrownJewelRequest) Descriptor() ([]byte, []int) { + return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{5} } -func (x *DeleteCrownJewelRequest) GetName() string { +func (x *UpsertCrownJewelRequest) GetCrownJewels() *CrownJewel { if x != nil { - return x.Name + return x.CrownJewels } - return "" + return nil } -// DeleteAllCrownJewelsRequest is a request to delete all CrownJewels. -type DeleteAllCrownJewelsRequest struct { +// DeleteCrownJewelRequest is a request to delete a CrownJewel. +type DeleteCrownJewelRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // Name is the name of the CrownJewel to delete. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } -func (x *DeleteAllCrownJewelsRequest) Reset() { - *x = DeleteAllCrownJewelsRequest{} +func (x *DeleteCrownJewelRequest) Reset() { + *x = DeleteCrownJewelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[5] + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *DeleteAllCrownJewelsRequest) String() string { +func (x *DeleteCrownJewelRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteAllCrownJewelsRequest) ProtoMessage() {} +func (*DeleteCrownJewelRequest) ProtoMessage() {} -func (x *DeleteAllCrownJewelsRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[5] +func (x *DeleteCrownJewelRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -331,9 +382,16 @@ func (x *DeleteAllCrownJewelsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteAllCrownJewelsRequest.ProtoReflect.Descriptor instead. -func (*DeleteAllCrownJewelsRequest) Descriptor() ([]byte, []int) { - return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{5} +// Deprecated: Use DeleteCrownJewelRequest.ProtoReflect.Descriptor instead. +func (*DeleteCrownJewelRequest) Descriptor() ([]byte, []int) { + return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP(), []int{6} +} + +func (x *DeleteCrownJewelRequest) GetName() string { + if x != nil { + return x.Name + } + return "" } var File_teleport_crownjewel_v1_crownjewel_service_proto protoreflect.FileDescriptor @@ -354,65 +412,85 @@ var file_teleport_crownjewel_v1_crownjewel_service_proto_rawDesc = []byte{ 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, - 0x73, 0x22, 0x54, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, - 0x77, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, - 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, - 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, - 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x88, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x5f, 0x6a, 0x65, 0x77, - 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x0b, 0x63, - 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, - 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x22, 0x60, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, - 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, - 0x0c, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x5f, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, - 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, - 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, - 0x77, 0x65, 0x6c, 0x73, 0x22, 0x2d, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, - 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0x1d, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, - 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x32, 0xb6, 0x03, 0x0a, 0x11, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, - 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x12, 0x2f, 0x2e, 0x74, + 0x73, 0x22, 0x2a, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x54, 0x0a, + 0x16, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, + 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x22, 0x88, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, + 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x45, 0x0a, 0x0c, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x5f, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x77, 0x6e, + 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, + 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x60, + 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x0c, 0x63, 0x72, 0x6f, + 0x77, 0x6e, 0x5f, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, + 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, + 0x77, 0x65, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, + 0x22, 0x60, 0x0a, 0x17, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, + 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x0c, 0x63, + 0x72, 0x6f, 0x77, 0x6e, 0x5f, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, + 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, + 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x0b, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, + 0x6c, 0x73, 0x22, 0x2d, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, + 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x32, 0x82, 0x05, 0x0a, 0x11, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x12, 0x2f, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, + 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, + 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, + 0x12, 0x61, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, + 0x6c, 0x12, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, + 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, + 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, + 0x77, 0x65, 0x6c, 0x12, 0x72, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, + 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x12, 0x2f, 0x2e, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, + 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, - 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, - 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, + 0x12, 0x67, 0x0a, 0x10, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, + 0x65, 0x77, 0x65, 0x6c, 0x12, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x12, 0x5b, 0x0a, 0x10, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x12, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, - 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, - 0x6c, 0x12, 0x72, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, - 0x77, 0x65, 0x6c, 0x73, 0x12, 0x2e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x12, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, - 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x12, 0x5b, - 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, - 0x65, 0x6c, 0x12, 0x2f, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x63, 0x72, - 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x72, 0x6f, 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x58, 0x5a, 0x56, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, - 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x63, 0x72, 0x6f, 0x77, 0x6e, - 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, - 0x77, 0x65, 0x6c, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x77, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x72, 0x6f, + 0x77, 0x6e, 0x4a, 0x65, 0x77, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x58, 0x5a, 0x56, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, + 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x72, 0x6f, 0x77, 0x6e, 0x6a, 0x65, 0x77, 0x65, 0x6c, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -427,34 +505,40 @@ func file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescGZIP() []byte { return file_teleport_crownjewel_v1_crownjewel_service_proto_rawDescData } -var file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_teleport_crownjewel_v1_crownjewel_service_proto_goTypes = []interface{}{ - (*CreateCrownJewelRequest)(nil), // 0: teleport.crownjewel.v1.CreateCrownJewelRequest - (*ListCrownJewelsRequest)(nil), // 1: teleport.crownjewel.v1.ListCrownJewelsRequest - (*ListCrownJewelsResponse)(nil), // 2: teleport.crownjewel.v1.ListCrownJewelsResponse - (*UpdateCrownJewelRequest)(nil), // 3: teleport.crownjewel.v1.UpdateCrownJewelRequest - (*DeleteCrownJewelRequest)(nil), // 4: teleport.crownjewel.v1.DeleteCrownJewelRequest - (*DeleteAllCrownJewelsRequest)(nil), // 5: teleport.crownjewel.v1.DeleteAllCrownJewelsRequest - (*CrownJewel)(nil), // 6: teleport.crownjewel.v1.CrownJewel - (*emptypb.Empty)(nil), // 7: google.protobuf.Empty + (*CreateCrownJewelRequest)(nil), // 0: teleport.crownjewel.v1.CreateCrownJewelRequest + (*GetCrownJewelRequest)(nil), // 1: teleport.crownjewel.v1.GetCrownJewelRequest + (*ListCrownJewelsRequest)(nil), // 2: teleport.crownjewel.v1.ListCrownJewelsRequest + (*ListCrownJewelsResponse)(nil), // 3: teleport.crownjewel.v1.ListCrownJewelsResponse + (*UpdateCrownJewelRequest)(nil), // 4: teleport.crownjewel.v1.UpdateCrownJewelRequest + (*UpsertCrownJewelRequest)(nil), // 5: teleport.crownjewel.v1.UpsertCrownJewelRequest + (*DeleteCrownJewelRequest)(nil), // 6: teleport.crownjewel.v1.DeleteCrownJewelRequest + (*CrownJewel)(nil), // 7: teleport.crownjewel.v1.CrownJewel + (*emptypb.Empty)(nil), // 8: google.protobuf.Empty } var file_teleport_crownjewel_v1_crownjewel_service_proto_depIdxs = []int32{ - 6, // 0: teleport.crownjewel.v1.CreateCrownJewelRequest.crown_jewels:type_name -> teleport.crownjewel.v1.CrownJewel - 6, // 1: teleport.crownjewel.v1.ListCrownJewelsResponse.crown_jewels:type_name -> teleport.crownjewel.v1.CrownJewel - 6, // 2: teleport.crownjewel.v1.UpdateCrownJewelRequest.crown_jewels:type_name -> teleport.crownjewel.v1.CrownJewel - 0, // 3: teleport.crownjewel.v1.CrownJewelService.CreateCrownJewel:input_type -> teleport.crownjewel.v1.CreateCrownJewelRequest - 1, // 4: teleport.crownjewel.v1.CrownJewelService.ListCrownJewels:input_type -> teleport.crownjewel.v1.ListCrownJewelsRequest - 3, // 5: teleport.crownjewel.v1.CrownJewelService.UpdateCrownJewel:input_type -> teleport.crownjewel.v1.UpdateCrownJewelRequest - 4, // 6: teleport.crownjewel.v1.CrownJewelService.DeleteCrownJewel:input_type -> teleport.crownjewel.v1.DeleteCrownJewelRequest - 6, // 7: teleport.crownjewel.v1.CrownJewelService.CreateCrownJewel:output_type -> teleport.crownjewel.v1.CrownJewel - 2, // 8: teleport.crownjewel.v1.CrownJewelService.ListCrownJewels:output_type -> teleport.crownjewel.v1.ListCrownJewelsResponse - 6, // 9: teleport.crownjewel.v1.CrownJewelService.UpdateCrownJewel:output_type -> teleport.crownjewel.v1.CrownJewel - 7, // 10: teleport.crownjewel.v1.CrownJewelService.DeleteCrownJewel:output_type -> google.protobuf.Empty - 7, // [7:11] is the sub-list for method output_type - 3, // [3:7] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 7, // 0: teleport.crownjewel.v1.CreateCrownJewelRequest.crown_jewels:type_name -> teleport.crownjewel.v1.CrownJewel + 7, // 1: teleport.crownjewel.v1.ListCrownJewelsResponse.crown_jewels:type_name -> teleport.crownjewel.v1.CrownJewel + 7, // 2: teleport.crownjewel.v1.UpdateCrownJewelRequest.crown_jewels:type_name -> teleport.crownjewel.v1.CrownJewel + 7, // 3: teleport.crownjewel.v1.UpsertCrownJewelRequest.crown_jewels:type_name -> teleport.crownjewel.v1.CrownJewel + 0, // 4: teleport.crownjewel.v1.CrownJewelService.CreateCrownJewel:input_type -> teleport.crownjewel.v1.CreateCrownJewelRequest + 1, // 5: teleport.crownjewel.v1.CrownJewelService.GetCrownJewel:input_type -> teleport.crownjewel.v1.GetCrownJewelRequest + 2, // 6: teleport.crownjewel.v1.CrownJewelService.ListCrownJewels:input_type -> teleport.crownjewel.v1.ListCrownJewelsRequest + 4, // 7: teleport.crownjewel.v1.CrownJewelService.UpdateCrownJewel:input_type -> teleport.crownjewel.v1.UpdateCrownJewelRequest + 5, // 8: teleport.crownjewel.v1.CrownJewelService.UpsertCrownJewel:input_type -> teleport.crownjewel.v1.UpsertCrownJewelRequest + 6, // 9: teleport.crownjewel.v1.CrownJewelService.DeleteCrownJewel:input_type -> teleport.crownjewel.v1.DeleteCrownJewelRequest + 7, // 10: teleport.crownjewel.v1.CrownJewelService.CreateCrownJewel:output_type -> teleport.crownjewel.v1.CrownJewel + 7, // 11: teleport.crownjewel.v1.CrownJewelService.GetCrownJewel:output_type -> teleport.crownjewel.v1.CrownJewel + 3, // 12: teleport.crownjewel.v1.CrownJewelService.ListCrownJewels:output_type -> teleport.crownjewel.v1.ListCrownJewelsResponse + 7, // 13: teleport.crownjewel.v1.CrownJewelService.UpdateCrownJewel:output_type -> teleport.crownjewel.v1.CrownJewel + 7, // 14: teleport.crownjewel.v1.CrownJewelService.UpsertCrownJewel:output_type -> teleport.crownjewel.v1.CrownJewel + 8, // 15: teleport.crownjewel.v1.CrownJewelService.DeleteCrownJewel:output_type -> google.protobuf.Empty + 10, // [10:16] is the sub-list for method output_type + 4, // [4:10] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_teleport_crownjewel_v1_crownjewel_service_proto_init() } @@ -477,7 +561,7 @@ func file_teleport_crownjewel_v1_crownjewel_service_proto_init() { } } file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListCrownJewelsRequest); i { + switch v := v.(*GetCrownJewelRequest); i { case 0: return &v.state case 1: @@ -489,7 +573,7 @@ func file_teleport_crownjewel_v1_crownjewel_service_proto_init() { } } file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListCrownJewelsResponse); i { + switch v := v.(*ListCrownJewelsRequest); i { case 0: return &v.state case 1: @@ -501,7 +585,7 @@ func file_teleport_crownjewel_v1_crownjewel_service_proto_init() { } } file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateCrownJewelRequest); i { + switch v := v.(*ListCrownJewelsResponse); i { case 0: return &v.state case 1: @@ -513,7 +597,7 @@ func file_teleport_crownjewel_v1_crownjewel_service_proto_init() { } } file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteCrownJewelRequest); i { + switch v := v.(*UpdateCrownJewelRequest); i { case 0: return &v.state case 1: @@ -525,7 +609,19 @@ func file_teleport_crownjewel_v1_crownjewel_service_proto_init() { } } file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteAllCrownJewelsRequest); i { + switch v := v.(*UpsertCrownJewelRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_crownjewel_v1_crownjewel_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteCrownJewelRequest); i { case 0: return &v.state case 1: @@ -543,7 +639,7 @@ func file_teleport_crownjewel_v1_crownjewel_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_crownjewel_v1_crownjewel_service_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go index 9571c35e239b9..50bf14c9e9c28 100644 --- a/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/crownjewel/v1/crownjewel_service_grpc.pb.go @@ -35,8 +35,10 @@ const _ = grpc.SupportPackageIsVersion7 const ( CrownJewelService_CreateCrownJewel_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/CreateCrownJewel" + CrownJewelService_GetCrownJewel_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/GetCrownJewel" CrownJewelService_ListCrownJewels_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/ListCrownJewels" CrownJewelService_UpdateCrownJewel_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/UpdateCrownJewel" + CrownJewelService_UpsertCrownJewel_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/UpsertCrownJewel" CrownJewelService_DeleteCrownJewel_FullMethodName = "/teleport.crownjewel.v1.CrownJewelService/DeleteCrownJewel" ) @@ -46,10 +48,14 @@ const ( type CrownJewelServiceClient interface { // CreateCrownJewel creates a new CrownJewel. CreateCrownJewel(ctx context.Context, in *CreateCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) + // GetCrownJewel gets a CrownJewel by name. + GetCrownJewel(ctx context.Context, in *GetCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) // ListCrownJewels returns a list of CrownJewels. It supports pagination. ListCrownJewels(ctx context.Context, in *ListCrownJewelsRequest, opts ...grpc.CallOption) (*ListCrownJewelsResponse, error) // UpdateCrownJewel updates an existing CrownJewel. UpdateCrownJewel(ctx context.Context, in *UpdateCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) + // UpsertCrownJewel upserts a CrownJewel. + UpsertCrownJewel(ctx context.Context, in *UpsertCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) // DeleteCrownJewel deletes a CrownJewel. DeleteCrownJewel(ctx context.Context, in *DeleteCrownJewelRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) } @@ -71,6 +77,15 @@ func (c *crownJewelServiceClient) CreateCrownJewel(ctx context.Context, in *Crea return out, nil } +func (c *crownJewelServiceClient) GetCrownJewel(ctx context.Context, in *GetCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) { + out := new(CrownJewel) + err := c.cc.Invoke(ctx, CrownJewelService_GetCrownJewel_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *crownJewelServiceClient) ListCrownJewels(ctx context.Context, in *ListCrownJewelsRequest, opts ...grpc.CallOption) (*ListCrownJewelsResponse, error) { out := new(ListCrownJewelsResponse) err := c.cc.Invoke(ctx, CrownJewelService_ListCrownJewels_FullMethodName, in, out, opts...) @@ -89,6 +104,15 @@ func (c *crownJewelServiceClient) UpdateCrownJewel(ctx context.Context, in *Upda return out, nil } +func (c *crownJewelServiceClient) UpsertCrownJewel(ctx context.Context, in *UpsertCrownJewelRequest, opts ...grpc.CallOption) (*CrownJewel, error) { + out := new(CrownJewel) + err := c.cc.Invoke(ctx, CrownJewelService_UpsertCrownJewel_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *crownJewelServiceClient) DeleteCrownJewel(ctx context.Context, in *DeleteCrownJewelRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { out := new(emptypb.Empty) err := c.cc.Invoke(ctx, CrownJewelService_DeleteCrownJewel_FullMethodName, in, out, opts...) @@ -104,10 +128,14 @@ func (c *crownJewelServiceClient) DeleteCrownJewel(ctx context.Context, in *Dele type CrownJewelServiceServer interface { // CreateCrownJewel creates a new CrownJewel. CreateCrownJewel(context.Context, *CreateCrownJewelRequest) (*CrownJewel, error) + // GetCrownJewel gets a CrownJewel by name. + GetCrownJewel(context.Context, *GetCrownJewelRequest) (*CrownJewel, error) // ListCrownJewels returns a list of CrownJewels. It supports pagination. ListCrownJewels(context.Context, *ListCrownJewelsRequest) (*ListCrownJewelsResponse, error) // UpdateCrownJewel updates an existing CrownJewel. UpdateCrownJewel(context.Context, *UpdateCrownJewelRequest) (*CrownJewel, error) + // UpsertCrownJewel upserts a CrownJewel. + UpsertCrownJewel(context.Context, *UpsertCrownJewelRequest) (*CrownJewel, error) // DeleteCrownJewel deletes a CrownJewel. DeleteCrownJewel(context.Context, *DeleteCrownJewelRequest) (*emptypb.Empty, error) mustEmbedUnimplementedCrownJewelServiceServer() @@ -120,12 +148,18 @@ type UnimplementedCrownJewelServiceServer struct { func (UnimplementedCrownJewelServiceServer) CreateCrownJewel(context.Context, *CreateCrownJewelRequest) (*CrownJewel, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateCrownJewel not implemented") } +func (UnimplementedCrownJewelServiceServer) GetCrownJewel(context.Context, *GetCrownJewelRequest) (*CrownJewel, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCrownJewel not implemented") +} func (UnimplementedCrownJewelServiceServer) ListCrownJewels(context.Context, *ListCrownJewelsRequest) (*ListCrownJewelsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListCrownJewels not implemented") } func (UnimplementedCrownJewelServiceServer) UpdateCrownJewel(context.Context, *UpdateCrownJewelRequest) (*CrownJewel, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateCrownJewel not implemented") } +func (UnimplementedCrownJewelServiceServer) UpsertCrownJewel(context.Context, *UpsertCrownJewelRequest) (*CrownJewel, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpsertCrownJewel not implemented") +} func (UnimplementedCrownJewelServiceServer) DeleteCrownJewel(context.Context, *DeleteCrownJewelRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteCrownJewel not implemented") } @@ -160,6 +194,24 @@ func _CrownJewelService_CreateCrownJewel_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _CrownJewelService_GetCrownJewel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCrownJewelRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CrownJewelServiceServer).GetCrownJewel(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CrownJewelService_GetCrownJewel_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CrownJewelServiceServer).GetCrownJewel(ctx, req.(*GetCrownJewelRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _CrownJewelService_ListCrownJewels_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListCrownJewelsRequest) if err := dec(in); err != nil { @@ -196,6 +248,24 @@ func _CrownJewelService_UpdateCrownJewel_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _CrownJewelService_UpsertCrownJewel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertCrownJewelRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CrownJewelServiceServer).UpsertCrownJewel(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CrownJewelService_UpsertCrownJewel_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CrownJewelServiceServer).UpsertCrownJewel(ctx, req.(*UpsertCrownJewelRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _CrownJewelService_DeleteCrownJewel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteCrownJewelRequest) if err := dec(in); err != nil { @@ -225,6 +295,10 @@ var CrownJewelService_ServiceDesc = grpc.ServiceDesc{ MethodName: "CreateCrownJewel", Handler: _CrownJewelService_CreateCrownJewel_Handler, }, + { + MethodName: "GetCrownJewel", + Handler: _CrownJewelService_GetCrownJewel_Handler, + }, { MethodName: "ListCrownJewels", Handler: _CrownJewelService_ListCrownJewels_Handler, @@ -233,6 +307,10 @@ var CrownJewelService_ServiceDesc = grpc.ServiceDesc{ MethodName: "UpdateCrownJewel", Handler: _CrownJewelService_UpdateCrownJewel_Handler, }, + { + MethodName: "UpsertCrownJewel", + Handler: _CrownJewelService_UpsertCrownJewel_Handler, + }, { MethodName: "DeleteCrownJewel", Handler: _CrownJewelService_DeleteCrownJewel_Handler, diff --git a/api/proto/teleport/crownjewel/v1/crownjewel_service.proto b/api/proto/teleport/crownjewel/v1/crownjewel_service.proto index 23fd32763dd04..9593437089556 100644 --- a/api/proto/teleport/crownjewel/v1/crownjewel_service.proto +++ b/api/proto/teleport/crownjewel/v1/crownjewel_service.proto @@ -25,10 +25,14 @@ option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport service CrownJewelService { // CreateCrownJewel creates a new CrownJewel. rpc CreateCrownJewel(CreateCrownJewelRequest) returns (CrownJewel); + // GetCrownJewel gets a CrownJewel by name. + rpc GetCrownJewel(GetCrownJewelRequest) returns (CrownJewel); // ListCrownJewels returns a list of CrownJewels. It supports pagination. rpc ListCrownJewels(ListCrownJewelsRequest) returns (ListCrownJewelsResponse); // UpdateCrownJewel updates an existing CrownJewel. rpc UpdateCrownJewel(UpdateCrownJewelRequest) returns (CrownJewel); + // UpsertCrownJewel upserts a CrownJewel. + rpc UpsertCrownJewel(UpsertCrownJewelRequest) returns (CrownJewel); // DeleteCrownJewel deletes a CrownJewel. rpc DeleteCrownJewel(DeleteCrownJewelRequest) returns (google.protobuf.Empty); } @@ -38,6 +42,12 @@ message CreateCrownJewelRequest { teleport.crownjewel.v1.CrownJewel crown_jewels = 1; } +// GetCrownJewelRequest is a request to get a CrownJewel by name. +message GetCrownJewelRequest { + // Name is the name of the CrownJewel to get. + string name = 1; +} + // ListCrownJewelsRequest is a request to get a list of CrownJewels. message ListCrownJewelsRequest { // page_size is the maximum number of items to return. @@ -61,11 +71,13 @@ message UpdateCrownJewelRequest { teleport.crownjewel.v1.CrownJewel crown_jewels = 1; } +// UpsertCrownJewelRequest is a request to upsert a CrownJewel. +message UpsertCrownJewelRequest { + teleport.crownjewel.v1.CrownJewel crown_jewels = 1; +} + // DeleteCrownJewelRequest is a request to delete a CrownJewel. message DeleteCrownJewelRequest { // Name is the name of the CrownJewel to delete. string name = 1; } - -// DeleteAllCrownJewelsRequest is a request to delete all CrownJewels. -message DeleteAllCrownJewelsRequest {} diff --git a/api/types/constants.go b/api/types/constants.go index 868a9fefcf830..dce05bb4ccc71 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -191,7 +191,8 @@ const ( // KindKubeServer is an kubernetes server resource. KindKubeServer = "kube_server" - + // KindCrownJewel is a crown jewel resource + KindCrownJewel = "crown_jewel" // KindKubernetesCluster is a Kubernetes cluster. KindKubernetesCluster = "kube_cluster" diff --git a/lib/auth/accesspoint/accesspoint.go b/lib/auth/accesspoint/accesspoint.go index 781e40bd8016b..d6a952b3d0d55 100644 --- a/lib/auth/accesspoint/accesspoint.go +++ b/lib/auth/accesspoint/accesspoint.go @@ -133,6 +133,7 @@ func NewAccessCache(cfg AccessCacheConfig) (*cache.Cache, error) { Restrictions: cfg.Services, Apps: cfg.Services, Kubernetes: cfg.Services, + CrownJewels: cfg.Services.CrownJewelClient(), DatabaseServices: cfg.Services, Databases: cfg.Services, AppSession: cfg.Services, diff --git a/lib/auth/auth.go b/lib/auth/auth.go index ad12f206548f3..45477c62638cd 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -237,6 +237,13 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { return nil, trace.Wrap(err) } } + + if cfg.CrownJewels == nil { + cfg.CrownJewels, err = local.NewCrownJewelsService(cfg.Backend) + if err != nil { + return nil, trace.Wrap(err) + } + } if cfg.ConnectionsDiagnostic == nil { cfg.ConnectionsDiagnostic = local.NewConnectionsDiagnosticService(cfg.Backend) } @@ -410,6 +417,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { KubeWaitingContainer: cfg.KubeWaitingContainers, Notifications: cfg.Notifications, AccessMonitoringRules: cfg.AccessMonitoringRules, + CrownJewels: cfg.CrownJewels, } as := Server{ @@ -587,6 +595,7 @@ type Services struct { services.SecReports services.KubeWaitingContainer services.AccessMonitoringRules + services.CrownJewels } // SecReportsClient returns the security reports client. @@ -638,6 +647,11 @@ func (r *Services) DiscoveryConfigClient() services.DiscoveryConfigs { return r } +// CrownJewelClient returns the CrownJewels client. +func (r *Services) CrownJewelClient() services.CrownJewels { + return r +} + // UserLoginStateClient returns the user login state client. func (r *Services) UserLoginStateClient() services.UserLoginStates { return r diff --git a/lib/auth/authclient/api.go b/lib/auth/authclient/api.go index 3b3eea996918c..d3d9a0aad6200 100644 --- a/lib/auth/authclient/api.go +++ b/lib/auth/authclient/api.go @@ -28,6 +28,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" accessmonitoringrules "github.com/gravitational/teleport/api/gen/proto/go/teleport/accessmonitoringrules/v1" + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" kubewaitingcontainerpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/kubewaitingcontainer/v1" userspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/users/v1" @@ -1153,6 +1154,12 @@ type Cache interface { // ListAccessListReviews will list access list reviews for a particular access list. ListAccessListReviews(ctx context.Context, accessList string, pageSize int, pageToken string) (reviews []*accesslist.Review, nextToken string, err error) + // ListCrownJewels returns a paginated list of crown jewels. + ListCrownJewels(ctx context.Context, pageSize int64, nextToken string) ([]*crownjewelv1.CrownJewel, string, error) + + // GetCrownJewel returns the specified crown jewel. + GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.CrownJewel, error) + // IntegrationsGetter defines read/list methods for integrations. services.IntegrationsGetter diff --git a/lib/auth/authclient/clt.go b/lib/auth/authclient/clt.go index 3d59c165fcb39..931b9bf5b5968 100644 --- a/lib/auth/authclient/clt.go +++ b/lib/auth/authclient/clt.go @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client" + "github.com/gravitational/teleport/api/client/crownjewel" "github.com/gravitational/teleport/api/client/externalauditstorage" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/client/secreport" @@ -582,6 +583,11 @@ func (c *Client) DiscoveryConfigClient() services.DiscoveryConfigWithStatusUpdat return c.APIClient.DiscoveryConfigClient() } +// CrownJewelsClient returns a client for managing Crown Jewel resources. +func (c *Client) CrownJewelsClient() services.CrownJewels { + return c.APIClient.CrownJewelServiceClient() +} + // DeleteStaticTokens deletes static tokens func (c *Client) DeleteStaticTokens() error { return trace.NotImplemented(notImplementedMessage) @@ -1535,7 +1541,7 @@ type ClientI interface { SecReportsClient() *secreport.Client // BotServiceClient returns a client for security reports. - // Clients connecting to older Teleport versions, still get a bot service client + // Clients connecting to older Teleport versions, still get a bot service client // when calling this method, but all RPCs will return "not implemented" errors // (as per the default gRPC behavior). BotServiceClient() machineidv1pb.BotServiceClient @@ -1552,6 +1558,9 @@ type ClientI interface { // (as per the default gRPC behavior). DiscoveryConfigClient() services.DiscoveryConfigWithStatusUpdater + // CrownJewelServiceClient returns a Crown Jewel service client. + CrownJewelServiceClient() *crownjewel.Client + // ResourceUsageClient returns a resource usage service client. // Clients connecting to non-Enterprise clusters, or older Teleport versions, // still get a client when calling this method, but all RPCs will return diff --git a/lib/auth/crownjewel/crownjewelv1/service.go b/lib/auth/crownjewel/crownjewelv1/service.go new file mode 100644 index 0000000000000..3b251c9b16b0d --- /dev/null +++ b/lib/auth/crownjewel/crownjewelv1/service.go @@ -0,0 +1,234 @@ +/* + * 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 crownjewelv1 + +import ( + "context" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/emptypb" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/auth/crownjewel" + "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/services" +) + +// ServiceConfig holds configuration options for the CrownJewel gRPC service. +type ServiceConfig struct { + // Authorizer is the authorizer to use. + Authorizer authz.Authorizer + + // Backend is the backend for storing CrownJewel. + Backend services.CrownJewels + + // Reader is the cache for storing CrownJewel. + Reader Reader +} + +// CheckAndSetDefaults checks the ServiceConfig fields and returns an error if +// a required param is not provided. +// Authorizer, Cache and Backend are required params +func (s *ServiceConfig) CheckAndSetDefaults() error { + if s.Authorizer == nil { + return trace.BadParameter("authorizer is required") + } + if s.Backend == nil { + return trace.BadParameter("backend is required") + } + if s.Reader == nil { + return trace.BadParameter("cache is required") + } + + return nil +} + +type Reader interface { + ListCrownJewels(ctx context.Context, pageSize int64, nextToken string) ([]*crownjewelv1.CrownJewel, string, error) + GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.CrownJewel, error) +} + +// Service implements the teleport.CrownJewel.v1.CrownJewelService RPC service. +type Service struct { + crownjewelv1.UnimplementedCrownJewelServiceServer + + authorizer authz.Authorizer + backend services.CrownJewels + reader Reader +} + +// NewService returns a new CrownJewel gRPC service. +func NewService(cfg ServiceConfig) (*Service, error) { + if err := cfg.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + + return &Service{ + authorizer: cfg.Authorizer, + backend: cfg.Backend, + reader: cfg.Reader, + }, nil +} + +// CreateCrownJewel creates crown jewel resource. +func (s *Service) CreateCrownJewel(ctx context.Context, req *crownjewelv1.CreateCrownJewelRequest) (*crownjewelv1.CrownJewel, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindCrownJewel, types.VerbCreate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminAction(); err != nil { + return nil, trace.Wrap(err) + } + + if err := crownjewel.ValidateCrownJewel(req.CrownJewels); err != nil { + return nil, trace.Wrap(err) + } + + rsp, err := s.backend.CreateCrownJewel(ctx, req.CrownJewels) + if err != nil { + return nil, trace.Wrap(err) + } + + return rsp, nil +} + +// ListCrownJewels returns a list of crown jewels. +func (s *Service) ListCrownJewels(ctx context.Context, req *crownjewelv1.ListCrownJewelsRequest) (*crownjewelv1.ListCrownJewelsResponse, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindCrownJewel, types.VerbRead, types.VerbList); err != nil { + return nil, trace.Wrap(err) + } + + rsp, nextToken, err := s.reader.ListCrownJewels(ctx, req.PageSize, req.PageToken) + if err != nil { + return nil, trace.Wrap(err) + } + + return &crownjewelv1.ListCrownJewelsResponse{ + CrownJewels: rsp, + NextPageToken: nextToken, + }, nil +} + +// GetCrownJewel returns crown jewel resource. +func (s *Service) GetCrownJewel(ctx context.Context, req *crownjewelv1.GetCrownJewelRequest) (*crownjewelv1.CrownJewel, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindCrownJewel, types.VerbRead); err != nil { + return nil, trace.Wrap(err) + } + + rsp, err := s.reader.GetCrownJewel(ctx, req.GetName()) + if err != nil { + return nil, trace.Wrap(err) + } + + return rsp, nil + +} + +// UpdateCrownJewel updates crown jewel resource. +func (s *Service) UpdateCrownJewel(ctx context.Context, req *crownjewelv1.UpdateCrownJewelRequest) (*crownjewelv1.CrownJewel, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindCrownJewel, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminAction(); err != nil { + return nil, trace.Wrap(err) + } + + if err := crownjewel.ValidateCrownJewel(req.CrownJewels); err != nil { + return nil, trace.Wrap(err) + } + + rsp, err := s.backend.UpdateCrownJewel(ctx, req.CrownJewels) + if err != nil { + return nil, trace.Wrap(err) + } + + return rsp, nil +} + +// UpsertCrownJewel upserts crown jewel resource. +func (s *Service) UpsertCrownJewel(ctx context.Context, req *crownjewelv1.UpsertCrownJewelRequest) (*crownjewelv1.CrownJewel, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindCrownJewel, types.VerbUpdate, types.VerbCreate); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminAction(); err != nil { + return nil, trace.Wrap(err) + } + + if err := crownjewel.ValidateCrownJewel(req.CrownJewels); err != nil { + return nil, trace.Wrap(err) + } + + rsp, err := s.backend.UpsertCrownJewel(ctx, req.CrownJewels) + if err != nil { + return nil, trace.Wrap(err) + } + + return rsp, nil + +} + +// DeleteCrownJewel deletes crown jewel resource. +func (s *Service) DeleteCrownJewel(ctx context.Context, req *crownjewelv1.DeleteCrownJewelRequest) (*emptypb.Empty, error) { + authCtx, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.CheckAccessToKind(types.KindCrownJewel, types.VerbDelete); err != nil { + return nil, trace.Wrap(err) + } + + if err := authCtx.AuthorizeAdminAction(); err != nil { + return nil, trace.Wrap(err) + } + + if err := s.backend.DeleteCrownJewel(ctx, req.GetName()); err != nil { + return nil, trace.Wrap(err) + } + + return &emptypb.Empty{}, nil +} diff --git a/lib/auth/crownjewel/crownjewelv1/service_test.go b/lib/auth/crownjewel/crownjewelv1/service_test.go new file mode 100644 index 0000000000000..d9c450746fd82 --- /dev/null +++ b/lib/auth/crownjewel/crownjewelv1/service_test.go @@ -0,0 +1,219 @@ +/* + * 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 crownjewelv1 + +import ( + "context" + "fmt" + "slices" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/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" +) + +func TestServiceAccess(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + allowedVerbs []string + allowedStates []authz.AdminActionAuthState + }{ + { + name: "CreateCrownJewel", + allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, + allowedVerbs: []string{types.VerbCreate}, + }, + { + name: "UpdateCrownJewel", + allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, + allowedVerbs: []string{types.VerbUpdate}, + }, + { + name: "DeleteCrownJewel", + allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, + allowedVerbs: []string{types.VerbDelete}, + }, + { + name: "UpsertCrownJewel", + allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, + allowedVerbs: []string{types.VerbCreate, types.VerbUpdate}, + }, + { + name: "ListCrownJewels", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthUnauthorized, authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbRead, types.VerbList}, + }, + { + name: "GetCrownJewel", + allowedStates: []authz.AdminActionAuthState{ + authz.AdminActionAuthUnauthorized, authz.AdminActionAuthNotRequired, + authz.AdminActionAuthMFAVerified, authz.AdminActionAuthMFAVerifiedWithReuse, + }, + allowedVerbs: []string{types.VerbRead}, + }, + } + + 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) + 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 crownjewelv1.CrownJewelService_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) + }) + } + }) +} + +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 CrownJewel service +func callMethod(t *testing.T, service *Service, method string) error { + for _, desc := range crownjewelv1.CrownJewelService_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") +} + +type fakeChecker struct { + allowedVerbs []string + services.AccessChecker +} + +func (f fakeChecker) CheckAccessToRule(_ services.RuleContext, _ string, resource string, verb string) error { + if resource == types.KindCrownJewel { + for slices.Contains(f.allowedVerbs, 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() + + b, err := memory.New(memory.Config{}) + require.NoError(t, err) + + backendService, err := local.NewCrownJewelsService(b) + require.NoError(t, err) + + authorizer := authz.AuthorizerFunc(func(ctx context.Context) (*authz.Context, error) { + user, err := types.NewUser("llama") + if err != nil { + return nil, err + } + return &authz.Context{ + User: user, + Checker: checker, + AdminActionAuthState: authState, + }, nil + }) + + service, err := NewService(ServiceConfig{ + Authorizer: authorizer, + Backend: backendService, + Reader: backendService, + }) + require.NoError(t, err) + return service +} diff --git a/lib/auth/crownjewel/object.go b/lib/auth/crownjewel/object.go new file mode 100644 index 0000000000000..b4075b26f8405 --- /dev/null +++ b/lib/auth/crownjewel/object.go @@ -0,0 +1,101 @@ +/* + * 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 crownjewel + +import ( + "github.com/gravitational/trace" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" +) + +// NewCrownJewel creates a new CrownJewel object. +// It validates the object before returning it. +func NewCrownJewel(name string, spec *crownjewelv1.CrownJewelSpec) (*crownjewelv1.CrownJewel, error) { + cj := &crownjewelv1.CrownJewel{ + Metadata: &headerv1.Metadata{ + Name: name, + }, + Spec: spec, + } + + if err := ValidateCrownJewel(cj); err != nil { + return nil, trace.Wrap(err) + } + + return cj, nil +} + +// ValidateCrownJewel validates the CrownJewel object without modifying it. +// Required fields: +// - Metadata.Name +// - Spec.TeleportMatchers or Spec.AwsMatchers +// - Matcher.Name or Matcher.Labels +func ValidateCrownJewel(jewel *crownjewelv1.CrownJewel) error { + switch { + case jewel == nil: + return trace.BadParameter("crown jewel is nil") + case jewel.Metadata == nil: + return trace.BadParameter("crown jewel metadata is nil") + case jewel.Metadata.Name == "": + return trace.BadParameter("crown jewel name is empty") + case jewel.Spec == nil: + return trace.BadParameter("crown jewel spec is nil") + case len(jewel.Spec.TeleportMatchers) == 0 && len(jewel.Spec.AwsMatchers) == 0: + return trace.BadParameter("crown jewel must have at least one matcher") + } + + if len(jewel.Spec.TeleportMatchers) > 0 { + for _, matcher := range jewel.Spec.TeleportMatchers { + if len(matcher.GetKinds()) == 0 { + return trace.BadParameter("teleport matcher kinds must be set") + } + + if matcher.Name == "" && len(matcher.GetLabels()) == 0 { + return trace.BadParameter("teleport matcher name or labels must be set") + } + + for _, label := range matcher.GetLabels() { + if label.Name == "" || len(label.Values) == 0 { + return trace.BadParameter("teleport matcher label name or value is empty") + } + } + } + } + + if len(jewel.Spec.AwsMatchers) > 0 { + for _, matcher := range jewel.Spec.AwsMatchers { + if len(matcher.GetTypes()) == 0 { + return trace.BadParameter("aws matcher type must be set") + } + + if matcher.GetArn() == "" && len(matcher.GetTags()) == 0 { + return trace.BadParameter("aws matcher arn or tags must be set") + } + + for _, label := range matcher.GetTags() { + if label.Key == "" || len(label.Values) == 0 { + return trace.BadParameter("aws matcher tag key or value is empty") + } + } + } + } + + return nil +} diff --git a/lib/auth/crownjewel/object_test.go b/lib/auth/crownjewel/object_test.go new file mode 100644 index 0000000000000..b985b078f4d17 --- /dev/null +++ b/lib/auth/crownjewel/object_test.go @@ -0,0 +1,224 @@ +/* + * 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 crownjewel_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/wrapperspb" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + labelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/label/v1" + "github.com/gravitational/teleport/lib/auth/crownjewel" +) + +func TestValidateCrownJewel(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + jewel *crownjewelv1.CrownJewel + wantErr require.ErrorAssertionFunc + }{ + { + name: "NilCrownJewel", + jewel: nil, + wantErr: require.Error, + }, + { + name: "ValidCrownJewel", + jewel: &crownjewelv1.CrownJewel{ + Metadata: &headerv1.Metadata{ + Name: "test", + }, + Spec: &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"kind1"}, + Name: "name1", + Labels: []*labelv1.Label{ + { + Name: "label1", + Values: []string{"value1"}, + }, + }, + }, + }, + AwsMatchers: []*crownjewelv1.AWSMatcher{ + { + Types: []string{"type1"}, + Arn: "arn1", + Tags: []*crownjewelv1.AWSTag{ + { + Key: "key1", + Values: []*wrapperspb.StringValue{wrapperspb.String("value1")}, + }, + }, + }, + }, + }, + }, + wantErr: require.NoError, + }, + { + name: "MissingMatchers", + jewel: &crownjewelv1.CrownJewel{ + Metadata: &headerv1.Metadata{ + Name: "test", + }, + Spec: &crownjewelv1.CrownJewelSpec{}, + }, + wantErr: require.Error, + }, + { + name: "MissingMetadata", + jewel: &crownjewelv1.CrownJewel{ + Spec: &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"kind1"}, + Name: "name1", + Labels: []*labelv1.Label{ + { + Name: "label1", + Values: []string{"value1"}, + }, + }, + }, + }, + }, + }, + wantErr: require.Error, + }, + { + name: "EmptyName", + jewel: &crownjewelv1.CrownJewel{ + Metadata: &headerv1.Metadata{ + Name: "", + }, + Spec: &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"kind1"}, + Name: "name1", + Labels: []*labelv1.Label{ + { + Name: "label1", + Values: []string{"value1"}, + }, + }, + }, + }, + AwsMatchers: []*crownjewelv1.AWSMatcher{ + { + Types: []string{"type1"}, + Arn: "arn1", + Tags: []*crownjewelv1.AWSTag{ + { + Key: "key1", + Values: []*wrapperspb.StringValue{wrapperspb.String("value1")}, + }, + }, + }, + }, + }, + }, + wantErr: require.Error, + }, + { + name: "EmptyTeleportMatcherKinds", + jewel: &crownjewelv1.CrownJewel{ + Metadata: &headerv1.Metadata{ + Name: "test", + }, + Spec: &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{}, + Name: "name1", + Labels: []*labelv1.Label{ + { + Name: "label1", + Values: []string{"value1"}, + }, + }, + }, + }, + AwsMatchers: []*crownjewelv1.AWSMatcher{ + { + Types: []string{"type1"}, + Arn: "arn1", + Tags: []*crownjewelv1.AWSTag{ + { + Key: "key1", + Values: []*wrapperspb.StringValue{wrapperspb.String("value1")}, + }, + }, + }, + }, + }, + }, + wantErr: require.Error, + }, + { + name: "EmptyAWSMatcherKinds", + jewel: &crownjewelv1.CrownJewel{ + Metadata: &headerv1.Metadata{ + Name: "test", + }, + Spec: &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"type2"}, + Name: "name1", + Labels: []*labelv1.Label{ + { + Name: "label1", + Values: []string{"value1"}, + }, + }, + }, + }, + AwsMatchers: []*crownjewelv1.AWSMatcher{ + { + Types: []string{}, + Arn: "arn1", + Tags: []*crownjewelv1.AWSTag{ + { + Key: "key1", + Values: []*wrapperspb.StringValue{wrapperspb.String("value1")}, + }, + }, + }, + }, + }, + }, + wantErr: require.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := crownjewel.ValidateCrownJewel(tt.jewel) + tt.wantErr(t, err) + }) + } +} diff --git a/lib/auth/dbobject/dbobjectv1/service_test.go b/lib/auth/dbobject/dbobjectv1/service_test.go index a84af1aa699e3..49a737e1b1986 100644 --- a/lib/auth/dbobject/dbobjectv1/service_test.go +++ b/lib/auth/dbobject/dbobjectv1/service_test.go @@ -31,6 +31,7 @@ import ( "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{ @@ -72,29 +73,6 @@ func callMethod(t *testing.T, service *DatabaseObjectService, method string) err panic("this line should never be reached: FailNow() should interrupt the test") } -// allCombinations yields all unique subslices of the input slice. -func allCombinations(verbs []string) [][]string { - var result [][]string - length := len(verbs) - - for i := 0; i < (1 << length); i++ { - subslice := make([]string, 0) - for j := 0; j < length; j++ { - if i&(1<. + */ + +package services + +import ( + "context" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" +) + +// CrownJewels is the interface for managing crown jewel resources. +type CrownJewels interface { + // ListCrownJewels returns the crown jewel resources. + ListCrownJewels(ctx context.Context, pageSize int64, nextToken string) ([]*crownjewelv1.CrownJewel, string, error) + // GetCrownJewel returns the crown jewel resource by name. + GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.CrownJewel, error) + // CreateCrownJewel creates a new crown jewel resource. + CreateCrownJewel(context.Context, *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) + // UpdateCrownJewel updates the crown jewel resource. + UpdateCrownJewel(context.Context, *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) + // UpsertCrownJewel creates or updates the crown jewel resource. + UpsertCrownJewel(context.Context, *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) + // DeleteCrownJewel deletes the crown jewel resource by name. + DeleteCrownJewel(context.Context, string) error + // DeleteAllCrownJewels deletes all crown jewel resources. + DeleteAllCrownJewels(context.Context) error +} + +// MarshalCrownJewel marshals the CrownJewel object into a JSON byte array. +func MarshalCrownJewel(object *crownjewelv1.CrownJewel, opts ...MarshalOption) ([]byte, error) { + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + if !cfg.PreserveResourceID { + object = proto.Clone(object).(*crownjewelv1.CrownJewel) + object.Metadata.Revision = "" + } + data, err := protojson.Marshal(object) + if err != nil { + return nil, trace.Wrap(err) + } + return data, nil +} + +// UnmarshalCrownJewel unmarshals the CrownJewel object from a JSON byte array. +func UnmarshalCrownJewel(data []byte, opts ...MarshalOption) (*crownjewelv1.CrownJewel, error) { + if len(data) == 0 { + return nil, trace.BadParameter("missing crown jewel data") + } + cfg, err := CollectOptions(opts) + if err != nil { + return nil, trace.Wrap(err) + } + var obj crownjewelv1.CrownJewel + if err := protojson.Unmarshal(data, &obj); err != nil { + return nil, trace.BadParameter(err.Error()) + } + if cfg.Revision != "" { + obj.Metadata.Revision = cfg.Revision + } + if !cfg.Expires.IsZero() { + obj.Metadata.Expires = timestamppb.New(cfg.Expires) + } + return &obj, nil +} diff --git a/lib/services/crown_jewels_test.go b/lib/services/crown_jewels_test.go new file mode 100644 index 0000000000000..62f579ea11f47 --- /dev/null +++ b/lib/services/crown_jewels_test.go @@ -0,0 +1,125 @@ +/* + * 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 services + +import ( + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/wrapperspb" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + labelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/label/v1" + "github.com/gravitational/teleport/lib/utils" +) + +func TestMarshalCrownJewelRoundTrip(t *testing.T) { + t.Parallel() + + spec := &crownjewelv1.CrownJewelSpec{} + obj := &crownjewelv1.CrownJewel{ + Metadata: &headerv1.Metadata{ + Name: "dummy-crown-jewel", + }, + Spec: spec, + } + + out, err := MarshalCrownJewel(obj) + require.NoError(t, err) + newObj, err := UnmarshalCrownJewel(out) + require.NoError(t, err) + require.True(t, proto.Equal(obj, newObj), "messages are not equal") +} + +func TestUnmarshalCrownJewel(t *testing.T) { + t.Parallel() + + data, err := utils.ToJSON([]byte(correctCrownJewelYAML)) + require.NoError(t, err) + + expected := &crownjewelv1.CrownJewel{ + Version: "v1", + Kind: "crown_jewel", + Metadata: &headerv1.Metadata{ + Name: "example-crown-jewel", + Labels: map[string]string{ + "env": "example", + }, + }, + Spec: &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"node"}, + Labels: []*labelv1.Label{ + { + Name: "abc", + Values: []string{"xyz"}, + }, + }, + }, + }, + AwsMatchers: []*crownjewelv1.AWSMatcher{ + { + Types: []string{"ec2"}, + Regions: []string{"us-west-1"}, + Tags: []*crownjewelv1.AWSTag{ + { + Key: "env", + Values: []*wrapperspb.StringValue{ + wrapperspb.String("prod"), + }, + }, + }, + }, + }, + }, + } + + obj, err := UnmarshalCrownJewel(data) + require.NoError(t, err) + require.True(t, proto.Equal(expected, obj), "CrownJewel objects are not equal") +} + +const correctCrownJewelYAML = ` +version: v1 +kind: crown_jewel +metadata: + labels: + env: example + name: example-crown-jewel +spec: + aws_matchers: + - regions: + - us-west-1 + tags: + - key: env + values: + - prod + types: + - ec2 + teleport_matchers: + - kinds: + - node + labels: + - name: abc + values: + - xyz +` diff --git a/lib/services/discoveryconfig.go b/lib/services/discoveryconfig.go index 5236c29a6b90a..bef4892ad0d0c 100644 --- a/lib/services/discoveryconfig.go +++ b/lib/services/discoveryconfig.go @@ -61,7 +61,7 @@ type DiscoveryConfigsGetter interface { GetDiscoveryConfig(ctx context.Context, name string) (*discoveryconfig.DiscoveryConfig, error) } -// MarshalDiscoveryConfig marshals the DiscoveryCOnfig resource to JSON. +// MarshalDiscoveryConfig marshals the DiscoveryConfig resource to JSON. func MarshalDiscoveryConfig(discoveryConfig *discoveryconfig.DiscoveryConfig, opts ...MarshalOption) ([]byte, error) { if err := discoveryConfig.CheckAndSetDefaults(); err != nil { return nil, trace.Wrap(err) diff --git a/lib/services/local/crown_jewels.go b/lib/services/local/crown_jewels.go new file mode 100644 index 0000000000000..68be1a0285a38 --- /dev/null +++ b/lib/services/local/crown_jewels.go @@ -0,0 +1,85 @@ +/* + * 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 local + +import ( + "context" + + "github.com/gravitational/trace" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/backend" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/services/local/generic" +) + +type CrownJewelsService struct { + service *generic.ServiceWrapper[*crownjewelv1.CrownJewel] +} + +const crownJewelsKey = "crown_jewels" + +// NewCrownJewelsService creates a new CrownJewelsService. +func NewCrownJewelsService(backend backend.Backend) (*CrownJewelsService, error) { + service, err := generic.NewServiceWrapper(backend, + types.KindCrownJewel, + crownJewelsKey, + services.MarshalCrownJewel, + services.UnmarshalCrownJewel) + if err != nil { + return nil, trace.Wrap(err) + } + return &CrownJewelsService{service: service}, nil +} + +func (s *CrownJewelsService) ListCrownJewels(ctx context.Context, pagesize int64, lastKey string) ([]*crownjewelv1.CrownJewel, string, error) { + r, nextToken, err := s.service.ListResources(ctx, int(pagesize), lastKey) + return r, nextToken, trace.Wrap(err) +} + +func (s *CrownJewelsService) GetCrownJewel(ctx context.Context, name string) (*crownjewelv1.CrownJewel, error) { + r, err := s.service.GetResource(ctx, name) + return r, trace.Wrap(err) +} + +func (s *CrownJewelsService) CreateCrownJewel(ctx context.Context, crownJewel *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) { + r, err := s.service.CreateResource(ctx, crownJewel) + return r, trace.Wrap(err) +} + +func (s *CrownJewelsService) UpdateCrownJewel(ctx context.Context, crownJewel *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) { + r, err := s.service.ConditionalUpdateResource(ctx, crownJewel) + return r, trace.Wrap(err) +} + +func (s *CrownJewelsService) UpsertCrownJewel(ctx context.Context, crownJewel *crownjewelv1.CrownJewel) (*crownjewelv1.CrownJewel, error) { + r, err := s.service.UpsertResource(ctx, crownJewel) + return r, trace.Wrap(err) +} + +func (s *CrownJewelsService) DeleteCrownJewel(ctx context.Context, name string) error { + err := s.service.DeleteResource(ctx, name) + return trace.Wrap(err) +} + +func (s *CrownJewelsService) DeleteAllCrownJewels(ctx context.Context) error { + err := s.service.DeleteAllResources(ctx) + return trace.Wrap(err) +} diff --git a/lib/services/local/crown_jewels_test.go b/lib/services/local/crown_jewels_test.go new file mode 100644 index 0000000000000..e90602789c6f9 --- /dev/null +++ b/lib/services/local/crown_jewels_test.go @@ -0,0 +1,305 @@ +/* + * 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 local_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/jonboulle/clockwork" + "github.com/mailgun/holster/v3/clock" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/timestamppb" + + crownjewelv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/crownjewel/v1" + headerv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/header/v1" + "github.com/gravitational/teleport/lib/auth/crownjewel" + "github.com/gravitational/teleport/lib/backend/memory" + "github.com/gravitational/teleport/lib/services" + "github.com/gravitational/teleport/lib/services/local" +) + +func TestCreateCrownJewel(t *testing.T) { + t.Parallel() + + ctx := context.Background() + service := getService(t) + + obj, err := crownjewel.NewCrownJewel("obj", &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"ssh"}, + Name: "test", + }, + }, + }) + require.NoError(t, err) + + // first attempt should succeed + objOut, err := service.CreateCrownJewel(ctx, obj) + require.NoError(t, err) + require.Equal(t, obj, objOut) + + // second attempt should fail, object already exists + _, err = service.CreateCrownJewel(ctx, obj) + require.Error(t, err) +} + +func TestUpsertCrownJewel(t *testing.T) { + t.Parallel() + + ctx := context.Background() + service := getService(t) + + obj, err := crownjewel.NewCrownJewel("obj", &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"ssh"}, + Name: "test", + }, + }, + }) + require.NoError(t, err) + + // the first attempt should succeed + objOut, err := service.UpsertCrownJewel(ctx, obj) + require.NoError(t, err) + require.Equal(t, obj, objOut) + + // the second attempt should also succeed + objOut, err = service.UpsertCrownJewel(ctx, obj) + require.NoError(t, err) + require.Equal(t, obj, objOut) +} + +func TestGetCrownJewel(t *testing.T) { + t.Parallel() + + ctx := context.Background() + service := getService(t) + prepopulate(t, service, 1) + + tests := []struct { + name string + key string + wantErr bool + wantObj *crownjewelv1.CrownJewel + }{ + { + name: "object does not exist", + key: "dummy", + wantErr: true, + wantObj: nil, + }, + { + name: "success", + key: getObject(t, 0).GetMetadata().GetName(), + wantErr: false, + wantObj: getObject(t, 0), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Fetch a specific object. + obj, err := service.GetCrownJewel(ctx, tt.key) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + cmpOpts := []cmp.Option{ + protocmp.IgnoreFields(&headerv1.Metadata{}, "id", "revision"), + protocmp.Transform(), + } + require.Equal(t, "", cmp.Diff(tt.wantObj, obj, cmpOpts...)) + }) + } +} + +func TestUpdateCrownJewel(t *testing.T) { + t.Parallel() + + ctx := context.Background() + service := getService(t) + prepopulate(t, service, 1) + + expiry := timestamppb.New(clock.Now().Add(30 * time.Minute)) + + // Fetch the object from the backend so the revision is populated. + obj, err := service.GetCrownJewel(ctx, getObject(t, 0).GetMetadata().GetName()) + require.NoError(t, err) + // update the expiry time + obj.Metadata.Expires = expiry + + objUpdated, err := service.UpdateCrownJewel(ctx, obj) + require.NoError(t, err) + require.Equal(t, expiry, objUpdated.Metadata.Expires) + + objFresh, err := service.GetCrownJewel(ctx, obj.Metadata.Name) + require.NoError(t, err) + require.Equal(t, expiry, objFresh.Metadata.Expires) +} + +func TestUpdateCrownJewelMissingRevision(t *testing.T) { + t.Parallel() + + ctx := context.Background() + service := getService(t) + prepopulate(t, service, 1) + + expiry := timestamppb.New(clock.Now().Add(30 * time.Minute)) + + obj := getObject(t, 0) + obj.Metadata.Expires = expiry + + // Update should be rejected as the revision is missing. + _, err := service.UpdateCrownJewel(ctx, obj) + require.Error(t, err) +} + +func TestDeleteCrownJewel(t *testing.T) { + t.Parallel() + + ctx := context.Background() + service := getService(t) + prepopulate(t, service, 1) + + tests := []struct { + name string + key string + wantErr bool + }{ + { + name: "object does not exist", + key: "dummy", + wantErr: true, + }, + { + name: "success", + key: getObject(t, 0).GetMetadata().GetName(), + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Fetch a specific object. + err := service.DeleteCrownJewel(ctx, tt.key) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestListCrownJewel(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + counts := []int{0, 1, 5, 10} + for _, count := range counts { + t.Run(fmt.Sprintf("count=%v", count), func(t *testing.T) { + service := getService(t) + prepopulate(t, service, count) + + t.Run("one page", func(t *testing.T) { + // Fetch all objects. + elements, nextToken, err := service.ListCrownJewels(ctx, 200, "") + require.NoError(t, err) + require.Empty(t, nextToken) + require.Len(t, elements, count) + + for i := 0; i < count; i++ { + cmpOpts := []cmp.Option{ + protocmp.IgnoreFields(&headerv1.Metadata{}, "id", "revision"), + protocmp.Transform(), + } + require.Equal(t, "", cmp.Diff(getObject(t, i), elements[i], cmpOpts...)) + } + }) + + t.Run("paginated", func(t *testing.T) { + // Fetch a paginated list of objects + elements := make([]*crownjewelv1.CrownJewel, 0) + nextToken := "" + for { + out, token, err := service.ListCrownJewels(ctx, 2, nextToken) + require.NoError(t, err) + nextToken = token + + elements = append(elements, out...) + if nextToken == "" { + break + } + } + + for i := 0; i < count; i++ { + cmpOpts := []cmp.Option{ + protocmp.IgnoreFields(&headerv1.Metadata{}, "id", "revision"), + protocmp.Transform(), + } + require.Equal(t, "", cmp.Diff(getObject(t, i), elements[i], cmpOpts...)) + } + }) + }) + } +} + +func getService(t *testing.T) services.CrownJewels { + backend, err := memory.New(memory.Config{ + Context: context.Background(), + Clock: clockwork.NewFakeClock(), + }) + require.NoError(t, err) + + service, err := local.NewCrownJewelsService(backend) + require.NoError(t, err) + return service +} + +func getObject(t *testing.T, index int) *crownjewelv1.CrownJewel { + name := fmt.Sprintf("obj%v", index) + obj, err := crownjewel.NewCrownJewel(name, &crownjewelv1.CrownJewelSpec{ + TeleportMatchers: []*crownjewelv1.TeleportMatcher{ + { + Kinds: []string{"ssh"}, + Name: "test", + }, + }, + }) + require.NoError(t, err) + + return obj +} + +func prepopulate(t *testing.T, service services.CrownJewels, count int) { + for i := 0; i < count; i++ { + _, err := service.CreateCrownJewel(context.Background(), getObject(t, i)) + require.NoError(t, err) + } +} diff --git a/lib/services/local/events.go b/lib/services/local/events.go index 5108241478156..ce62bafa9051d 100644 --- a/lib/services/local/events.go +++ b/lib/services/local/events.go @@ -156,6 +156,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type parser = newInstallerParser() case types.KindKubernetesCluster: parser = newKubeClusterParser() + case types.KindCrownJewel: + parser = newCrownJewelParser() case types.KindPlugin: parser = newPluginParser(kind.LoadSecrets) case types.KindSAMLIdPServiceProvider: @@ -1221,6 +1223,35 @@ func (p *kubeClusterParser) parse(event backend.Event) (types.Resource, error) { } } +func newCrownJewelParser() *crownJewelParser { + return &crownJewelParser{ + baseParser: newBaseParser(backend.Key(crownJewelsKey)), + } +} + +type crownJewelParser struct { + baseParser +} + +func (p *crownJewelParser) parse(event backend.Event) (types.Resource, error) { + switch event.Type { + case types.OpDelete: + return resourceHeader(event, types.KindCrownJewel, types.V1, 0) + case types.OpPut: + r, err := services.UnmarshalCrownJewel(event.Item.Value, + services.WithResourceID(event.Item.ID), + services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), + ) + if err != nil { + return nil, trace.Wrap(err) + } + return types.Resource153ToLegacy(r), nil + default: + return nil, trace.BadParameter("event %v is not supported", event.Type) + } +} + func newAppParser() *appParser { return &appParser{ baseParser: newBaseParser(backend.Key(appPrefix)), diff --git a/lib/services/presets.go b/lib/services/presets.go index 01bd32d5ca0fa..1d7992d1bea3a 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -130,6 +130,7 @@ func NewPresetEditorRole() types.Role { types.NewRule(types.KindUser, RW()), types.NewRule(types.KindRole, RW()), types.NewRule(types.KindBot, RW()), + types.NewRule(types.KindCrownJewel, RW()), types.NewRule(types.KindDatabaseObjectImportRule, RW()), types.NewRule(types.KindOIDC, RW()), types.NewRule(types.KindSAML, RW()), diff --git a/lib/services/resource.go b/lib/services/resource.go index 72ae52e6b8b56..aeba9adb6131a 100644 --- a/lib/services/resource.go +++ b/lib/services/resource.go @@ -240,6 +240,8 @@ func ParseShortcut(in string) (string, error) { return types.KindAccessMonitoringRule, nil case types.KindDatabaseObject, "database_object": return types.KindDatabaseObject, nil + case types.KindCrownJewel, "crown_jewels": + return types.KindCrownJewel, nil case types.KindAccessRequest, types.KindAccessRequest + "s", "accessrequest", "accessrequests": return types.KindAccessRequest, nil case types.KindPlugin, types.KindPlugin + "s": diff --git a/lib/services/services.go b/lib/services/services.go index 199431130b67c..870b826daa3e7 100644 --- a/lib/services/services.go +++ b/lib/services/services.go @@ -55,6 +55,7 @@ type Services interface { UserLoginStateClient() UserLoginStates DiscoveryConfigClient() DiscoveryConfigs SecReportsClient() *secreport.Client + CrownJewelClient() CrownJewels } // RotationGetter returns the rotation state. diff --git a/lib/services/useracl.go b/lib/services/useracl.go index 11a32219b1b1b..7443af2520ed1 100644 --- a/lib/services/useracl.go +++ b/lib/services/useracl.go @@ -108,6 +108,8 @@ type UserACL struct { Bots ResourceAccess `json:"bots"` // AccessMonitoringRule defines access to manage access monitoring rule resources. AccessMonitoringRule ResourceAccess `json:"accessMonitoringRule"` + // CrownJewel defines access to manage CrownJewel resources. + CrownJewel ResourceAccess `json:"crownJewel"` } func hasAccess(roleSet RoleSet, ctx *Context, kind string, verbs ...string) bool { @@ -196,6 +198,7 @@ func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, des accessListAccess := newAccess(userRoles, ctx, types.KindAccessList) externalAuditStorage := newAccess(userRoles, ctx, types.KindExternalAuditStorage) bots := newAccess(userRoles, ctx, types.KindBot) + crownJewelAccess := newAccess(userRoles, ctx, types.KindCrownJewel) var auditQuery ResourceAccess var securityReports ResourceAccess @@ -243,5 +246,6 @@ func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, des AccessGraph: accessGraphAccess, Bots: bots, AccessMonitoringRule: accessMonitoringRules, + CrownJewel: crownJewelAccess, } } diff --git a/lib/utils/algorithms.go b/lib/utils/algorithms.go new file mode 100644 index 0000000000000..944a38b04dd78 --- /dev/null +++ b/lib/utils/algorithms.go @@ -0,0 +1,37 @@ +/* + * 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 utils + +// Combinations yields all unique sub-slices of the input slice. +func Combinations(verbs []string) [][]string { + var result [][]string + length := len(verbs) + + for i := 0; i < (1 << length); i++ { + subslice := make([]string, 0) + for j := 0; j < length; j++ { + if i&(1<. + */ + +package utils + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAllCombinations(t *testing.T) { + require.Len(t, Combinations([]string{"a"}), 2) + require.Len(t, Combinations([]string{"a", "b", "c"}), 8) + require.Len(t, Combinations(make([]string, 5)), 32) +} diff --git a/tool/tctl/common/collection.go b/tool/tctl/common/collection.go index 69292c33a6aa4..3ed204084af06 100644 --- a/tool/tctl/common/collection.go +++ b/tool/tctl/common/collection.go @@ -30,6 +30,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/constants" + 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" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" @@ -917,6 +918,39 @@ func (c *kubeServerCollection) writeJSON(w io.Writer) error { return utils.WriteJSONArray(w, c.servers) } +type crownJewelCollection struct { + items []*crownjewelv1.CrownJewel +} + +func (c *crownJewelCollection) resources() []types.Resource { + r := make([]types.Resource, 0, len(c.items)) + for _, resource := range c.items { + r = append(r, types.Resource153ToLegacy(resource)) + } + return r +} + +// writeText formats the crown jewels into a table and writes them into w. +// If verbose is disabled, labels column can be truncated to fit into the console. +func (c *crownJewelCollection) writeText(w io.Writer, verbose bool) error { + var rows [][]string + for _, item := range c.items { + labels := common.FormatLabels(item.GetMetadata().GetLabels(), verbose) + rows = append(rows, []string{item.Metadata.GetName(), item.GetSpec().String(), labels}) + } + headers := []string{"Name", "Spec", "Labels"} + var t asciitable.Table + if verbose { + t = asciitable.MakeTable(headers, rows...) + } else { + t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels") + } + // stable sort by name. + t.SortRowsBy([]int{0}, true) + _, err := t.AsBuffer().WriteTo(w) + return trace.Wrap(err) +} + type kubeClusterCollection struct { clusters []types.KubeCluster } diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index 17d258e4b4ddd..bb80753f51c86 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -42,6 +42,7 @@ import ( apiclient "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/client/proto" apidefaults "github.com/gravitational/teleport/api/defaults" + 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" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" @@ -157,6 +158,7 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, config *servicec types.KindDatabaseObjectImportRule: rc.createDatabaseObjectImportRule, types.KindDatabaseObject: rc.createDatabaseObject, types.KindAccessMonitoringRule: rc.createAccessMonitoringRule, + types.KindCrownJewel: rc.createCrownJewel, types.KindPlugin: rc.createPlugin, } rc.UpdateHandlers = map[ResourceKind]ResourceCreateHandler{ @@ -169,6 +171,7 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, config *servicec types.KindClusterAuthPreference: rc.updateAuthPreference, types.KindSessionRecordingConfig: rc.updateSessionRecordingConfig, types.KindAccessMonitoringRule: rc.updateAccessMonitoringRule, + types.KindCrownJewel: rc.updateCrownJewel, types.KindPlugin: rc.updatePlugin, } rc.config = config @@ -931,6 +934,40 @@ func (rc *ResourceCommand) createKubeCluster(ctx context.Context, client *authcl return nil } +func (rc *ResourceCommand) createCrownJewel(ctx context.Context, client *auth.Client, raw services.UnknownResource) error { + crownJewel, err := services.UnmarshalCrownJewel(raw.Raw) + if err != nil { + return trace.Wrap(err) + } + + c := client.CrownJewelsClient() + if rc.force { + if _, err := c.UpsertCrownJewel(ctx, crownJewel); err != nil { + return trace.Wrap(err) + } + fmt.Printf("crown jewel %q has been updated\n", crownJewel.GetMetadata().GetName()) + } else { + if _, err := c.CreateCrownJewel(ctx, crownJewel); err != nil { + return trace.Wrap(err) + } + fmt.Printf("crown jewel %q has been created\n", crownJewel.GetMetadata().GetName()) + } + + return nil +} + +func (rc *ResourceCommand) updateCrownJewel(ctx context.Context, client *auth.Client, resource services.UnknownResource) error { + in, err := services.UnmarshalCrownJewel(resource.Raw) + if err != nil { + return trace.Wrap(err) + } + if _, err := client.CrownJewelsClient().UpdateCrownJewel(ctx, in); err != nil { + return trace.Wrap(err) + } + fmt.Printf("crown jewel %q has been updated\n", in.GetMetadata().GetName()) + return nil +} + func (rc *ResourceCommand) createDatabase(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error { database, err := services.UnmarshalDatabase(raw.Raw) if err != nil { @@ -1541,6 +1578,11 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client return trace.Wrap(err) } fmt.Printf("%s %q has been deleted\n", resDesc, name) + case types.KindCrownJewel: + if err := client.CrownJewelsClient().DeleteCrownJewel(ctx, rc.ref.Name); err != nil { + return trace.Wrap(err) + } + fmt.Printf("crown_jewel %q has been deleted\n", rc.ref.Name) case types.KindWindowsDesktopService: if err = client.DeleteWindowsDesktopService(ctx, rc.ref.Name); err != nil { return trace.Wrap(err) @@ -2243,6 +2285,24 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient return nil, trace.NotFound("kubernetes cluster %q not found", rc.ref.Name) } return &kubeClusterCollection{clusters: clusters}, nil + case types.KindCrownJewel: + cjClient := client.CrownJewelsClient() + var rules []*crownjewelv1.CrownJewel + nextToken := "" + for { + resp, token, err := cjClient.ListCrownJewels(ctx, 0 /* default size */, nextToken) + if err != nil { + return nil, trace.Wrap(err) + } + + rules = append(rules, resp...) + + if token == "" { + break + } + nextToken = token + } + return &crownJewelCollection{items: rules}, nil case types.KindWindowsDesktopService: services, err := client.GetWindowsDesktopServices(ctx) if err != nil {