diff --git a/globals/prefixes.go b/globals/prefixes.go index afd706c871..e1b29c0aad 100644 --- a/globals/prefixes.go +++ b/globals/prefixes.go @@ -315,6 +315,11 @@ var prefixToResourceType = map[string]ResourceInfo{ Type: resource.SessionRecording, Subtype: UnknownSubtype, }, + + StoragePolicyPrefix: { + Type: resource.Policy, + Subtype: UnknownSubtype, + }, } var resourceTypeToPrefixes map[resource.Type][]string = func() map[resource.Type][]string { diff --git a/internal/daemon/controller/handler.go b/internal/daemon/controller/handler.go index f8b1f277fa..875d042b17 100644 --- a/internal/daemon/controller/handler.go +++ b/internal/daemon/controller/handler.go @@ -33,6 +33,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/handlers/host_sets" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/hosts" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/managed_groups" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/policies" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/scopes" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/session_recordings" @@ -222,6 +223,17 @@ func (c *Controller) registerGrpcServices(s *grpc.Server) error { } services.RegisterStorageBucketServiceServer(s, sbs) } + if _, ok := currentServices[services.PolicyService_ServiceDesc.ServiceName]; !ok { + ps, err := policies.NewServiceFn( + c.baseContext, + c.IamRepoFn, + c.ControllerExtension, + ) + if err != nil { + return fmt.Errorf("failed to create policy handler service: %w", err) + } + services.RegisterPolicyServiceServer(s, ps) + } if _, ok := currentServices[services.SessionRecordingService_ServiceDesc.ServiceName]; !ok { srs, err := session_recordings.NewServiceFn( c.baseContext, @@ -403,6 +415,9 @@ func registerGrpcGatewayEndpoints(ctx context.Context, gwMux *runtime.ServeMux, if err := services.RegisterSessionRecordingServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil { return fmt.Errorf("failed to register session recording handler: %w", err) } + if err := services.RegisterPolicyServiceHandlerFromEndpoint(ctx, gwMux, gatewayTarget, dialOptions); err != nil { + return fmt.Errorf("failed to register policy handler: %w", err) + } return nil } diff --git a/internal/daemon/controller/handlers/policies/policy_service.go b/internal/daemon/controller/handlers/policies/policy_service.go new file mode 100644 index 0000000000..4a73dfd393 --- /dev/null +++ b/internal/daemon/controller/handlers/policies/policy_service.go @@ -0,0 +1,75 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package policies + +import ( + "context" + + "github.com/hashicorp/boundary/internal/daemon/controller/common" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + internalglobals "github.com/hashicorp/boundary/internal/globals" + "github.com/hashicorp/boundary/internal/types/action" + "github.com/hashicorp/boundary/internal/types/resource" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var ( + _ pbs.PolicyServiceServer = (*Service)(nil) + + // idActions contains the set of actions that can be performed on individual + // resources. + idActions = action.NewActionSet( + action.NoOp, + action.Read, + action.Update, + action.Delete, + ) + // CollectionActions contains the set of actions that can be performed on + // this collection. + CollectionActions = action.NewActionSet( + action.Create, + action.List, + ) +) + +// NewServiceFn returns a policy service which is not implemented in OSS +var NewServiceFn = func(ctx context.Context, + iamRepoFn common.IamRepoFactory, + controllerExt internalglobals.ControllerExtension, +) (pbs.PolicyServiceServer, error) { + return &Service{}, nil +} + +func init() { + action.RegisterResource(resource.Policy, idActions, CollectionActions) +} + +type Service struct { + pbs.UnimplementedPolicyServiceServer +} + +// GetPolicy implements the interface pbs.PolicyServiceServer. +func (s *Service) GetPolicy(ctx context.Context, req *pbs.GetPolicyRequest) (*pbs.GetPolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "Policies are an Enterprise-only feature") +} + +// ListPolicies implements the interface pbs.PolicyServiceServer. +func (s *Service) ListPolicies(ctx context.Context, req *pbs.ListPoliciesRequest) (*pbs.ListPoliciesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "Policies are an Enterprise-only feature") +} + +// CreatePolicy implements the interface pbs.PolicyServiceServer. +func (s *Service) CreatePolicy(ctx context.Context, req *pbs.CreatePolicyRequest) (*pbs.CreatePolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "Policies are an Enterprise-only feature") +} + +// UpdatePolicy implements the interface pbs.PolicyServiceServer. +func (s *Service) UpdatePolicy(ctx context.Context, req *pbs.UpdatePolicyRequest) (*pbs.UpdatePolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "Policies are an Enterprise-only feature") +} + +func (s *Service) DeletePolicy(ctx context.Context, req *pbs.DeletePolicyRequest) (*pbs.DeletePolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "Policies are an Enterprise-only feature") +} diff --git a/internal/daemon/controller/handlers/policies/policy_service_test.go b/internal/daemon/controller/handlers/policies/policy_service_test.go new file mode 100644 index 0000000000..c2913d493e --- /dev/null +++ b/internal/daemon/controller/handlers/policies/policy_service_test.go @@ -0,0 +1,62 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package policies + +import ( + "context" + "testing" + + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestPolicyService(t *testing.T) { + t.Parallel() + ctx := context.Background() + assert, require := assert.New(t), require.New(t) + s := Service{} + + gotCreate, err := s.CreatePolicy(ctx, &pbs.CreatePolicyRequest{}) + require.Nil(gotCreate) + require.Error(err) + gotStatus, ok := status.FromError(err) + require.True(ok) + assert.Equal(gotStatus.Code(), codes.Unimplemented) + assert.Equal(gotStatus.Message(), "Policies are an Enterprise-only feature") + + gotUpdate, err := s.UpdatePolicy(ctx, &pbs.UpdatePolicyRequest{}) + require.Nil(gotUpdate) + require.Error(err) + gotStatus, ok = status.FromError(err) + require.True(ok) + assert.Equal(gotStatus.Code(), codes.Unimplemented) + assert.Equal(gotStatus.Message(), "Policies are an Enterprise-only feature") + + gotGet, err := s.GetPolicy(ctx, &pbs.GetPolicyRequest{}) + require.Nil(gotGet) + require.Error(err) + gotStatus, ok = status.FromError(err) + require.True(ok) + assert.Equal(gotStatus.Code(), codes.Unimplemented) + assert.Equal(gotStatus.Message(), "Policies are an Enterprise-only feature") + + gotDelete, err := s.DeletePolicy(ctx, &pbs.DeletePolicyRequest{}) + require.Nil(gotDelete) + require.Error(err) + gotStatus, ok = status.FromError(err) + require.True(ok) + assert.Equal(gotStatus.Code(), codes.Unimplemented) + assert.Equal(gotStatus.Message(), "Policies are an Enterprise-only feature") + + gotList, err := s.ListPolicies(ctx, &pbs.ListPoliciesRequest{}) + require.Nil(gotList) + require.Error(err) + gotStatus, ok = status.FromError(err) + require.True(ok) + assert.Equal(gotStatus.Code(), codes.Unimplemented) + assert.Equal(gotStatus.Message(), "Policies are an Enterprise-only feature") +} diff --git a/internal/daemon/controller/handlers/scopes/scope_service.go b/internal/daemon/controller/handlers/scopes/scope_service.go index 198f20d7ac..8c54fc4ee8 100644 --- a/internal/daemon/controller/handlers/scopes/scope_service.go +++ b/internal/daemon/controller/handlers/scopes/scope_service.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentialstores" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/groups" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/host_catalogs" + "github.com/hashicorp/boundary/internal/daemon/controller/handlers/policies" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/session_recordings" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/sessions" @@ -81,6 +82,7 @@ var ( resource.User: users.CollectionActions, resource.Worker: workers.CollectionActions, resource.SessionRecording: session_recordings.CollectionActions, + resource.Policy: policies.CollectionActions, }, scope.Org.String(): { @@ -92,6 +94,7 @@ var ( resource.Scope: CollectionActions, resource.User: users.CollectionActions, resource.SessionRecording: session_recordings.CollectionActions, + resource.Policy: policies.CollectionActions, }, scope.Project.String(): { diff --git a/internal/daemon/controller/handlers/scopes/scope_service_test.go b/internal/daemon/controller/handlers/scopes/scope_service_test.go index bf2d379573..d5eeafe637 100644 --- a/internal/daemon/controller/handlers/scopes/scope_service_test.go +++ b/internal/daemon/controller/handlers/scopes/scope_service_test.go @@ -93,6 +93,12 @@ var globalAuthorizedCollectionActions = map[string]*structpb.ListValue{ structpb.NewStringValue("list"), }, }, + "policies": { + Values: []*structpb.Value{ + structpb.NewStringValue("create"), + structpb.NewStringValue("list"), + }, + }, "roles": { Values: []*structpb.Value{ structpb.NewStringValue("create"), @@ -155,6 +161,12 @@ var orgAuthorizedCollectionActions = map[string]*structpb.ListValue{ structpb.NewStringValue("list"), }, }, + "policies": { + Values: []*structpb.Value{ + structpb.NewStringValue("create"), + structpb.NewStringValue("list"), + }, + }, "roles": { Values: []*structpb.Value{ structpb.NewStringValue("create"), diff --git a/internal/daemon/controller/rate_limiter_test.go b/internal/daemon/controller/rate_limiter_test.go index b0112858b4..f473410d58 100644 --- a/internal/daemon/controller/rate_limiter_test.go +++ b/internal/daemon/controller/rate_limiter_test.go @@ -63,7 +63,7 @@ func Test_newRateLimiterConfig(t *testing.T) { ratelimit.DefaultLimiterMaxQuotas(), false, &rateLimiterConfig{ - maxSize: 296148, + maxSize: 308154, configs: nil, disabled: false, limits: defaultLimits, diff --git a/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/defaults.json b/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/defaults.json index 69ac30a30e..3f79845377 100644 --- a/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/defaults.json +++ b/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/defaults.json @@ -3893,7 +3893,7 @@ ] } }, - "max_size": 296148, + "max_size": 308154, "msg": "controller api rate limiter" }, "op": "controller.(rateLimiterConfig).writeSysEvent", diff --git a/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/override.json b/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/override.json index c3ec2d340e..788d57e836 100644 --- a/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/override.json +++ b/internal/daemon/controller/testdata/Test_rateLimiterConfig_writeSysEvent/override.json @@ -3893,7 +3893,7 @@ ] } }, - "max_size": 296148, + "max_size": 308154, "msg": "controller api rate limiter" }, "op": "controller.(rateLimiterConfig).writeSysEvent", diff --git a/internal/perms/acl_test.go b/internal/perms/acl_test.go index f45c76385e..630a2c3a1b 100644 --- a/internal/perms/acl_test.go +++ b/internal/perms/acl_test.go @@ -928,7 +928,7 @@ func Test_AnonRestrictions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { require, assert := require.New(t), assert.New(t) - for i := resource.Type(1); i <= resource.StorageBucket; i++ { + for i := resource.Type(1); i <= resource.Policy; i++ { if i == resource.Controller || i == resource.Worker { continue } diff --git a/internal/perms/grants_test.go b/internal/perms/grants_test.go index 79498819ec..e5dbe13dff 100644 --- a/internal/perms/grants_test.go +++ b/internal/perms/grants_test.go @@ -119,7 +119,7 @@ func Test_ValidateType(t *testing.T) { t.Parallel() ctx := context.Background() var g Grant - for i := resource.Unknown; i <= resource.StorageBucket; i++ { + for i := resource.Unknown; i <= resource.Policy; i++ { g.typ = i if i == resource.Controller { assert.Error(t, g.validateType(ctx)) diff --git a/internal/policy/policy.go b/internal/policy/policy.go index 02cb4e4b55..7e48961289 100644 --- a/internal/policy/policy.go +++ b/internal/policy/policy.go @@ -5,6 +5,9 @@ package policy import "github.com/hashicorp/boundary/internal/boundary" +// Domain defines the domain for this package. +const Domain = "policy" + // Policy contains the common methods across all the different types of policies. type Policy interface { boundary.Resource diff --git a/internal/ratelimit/config.go b/internal/ratelimit/config.go index a5a85b9160..68e2eaceb6 100644 --- a/internal/ratelimit/config.go +++ b/internal/ratelimit/config.go @@ -28,6 +28,7 @@ import ( _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/host_sets" _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/hosts" _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/managed_groups" + _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/policies" _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles" _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/scopes" _ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/session_recordings" diff --git a/internal/types/resource/resource.go b/internal/types/resource/resource.go index 14106c3bcb..02f3589ef4 100644 --- a/internal/types/resource/resource.go +++ b/internal/types/resource/resource.go @@ -34,6 +34,7 @@ const ( CredentialLibrary Credential StorageBucket + Policy // NOTE: When adding a new type, be sure to update: // // * The Grant.validateType function and test @@ -72,6 +73,7 @@ func (r Type) String() string { "credential-library", "credential", "storage-bucket", + "policy", }[r] } @@ -79,6 +81,8 @@ func (r Type) PluralString() string { switch r { case CredentialLibrary: return "credential-libraries" + case Policy: + return "policies" default: return r.String() + "s" } @@ -88,6 +92,8 @@ func FromPlural(s string) (Type, bool) { switch s { case "credential-libraries": return CredentialLibrary, true + case "policies": + return Policy, true default: t, ok := Map[strings.TrimSuffix(s, "s")] return t, ok @@ -117,6 +123,7 @@ var Map = map[string]Type{ CredentialLibrary.String(): CredentialLibrary, Credential.String(): Credential, StorageBucket.String(): StorageBucket, + Policy.String(): Policy, } // Parent returns the parent type for a given type; if there is no parent, it @@ -159,6 +166,7 @@ func TopLevelType(typ Type) bool { Target, User, StorageBucket, + Policy, Worker: return true } diff --git a/internal/types/resource/resource_test.go b/internal/types/resource/resource_test.go index e8ef0ef8e0..f42d75e4df 100644 --- a/internal/types/resource/resource_test.go +++ b/internal/types/resource/resource_test.go @@ -122,6 +122,11 @@ func Test_Resource(t *testing.T) { want: Credential, parent: CredentialStore, }, + { + typeString: "policy", + want: Policy, + topLevelType: true, + }, } for _, tt := range tests { t.Run(tt.typeString, func(t *testing.T) {