From bb854f0da76fccc865f2385603354b7c41ed73c8 Mon Sep 17 00:00:00 2001 From: Eric Pang Date: Sun, 24 Nov 2024 01:54:07 +0000 Subject: [PATCH 1/5] Add scaffolding --- mockgcp/mockprivateca/certificateauthority.go | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 mockgcp/mockprivateca/certificateauthority.go diff --git a/mockgcp/mockprivateca/certificateauthority.go b/mockgcp/mockprivateca/certificateauthority.go new file mode 100644 index 0000000000..68883b21af --- /dev/null +++ b/mockgcp/mockprivateca/certificateauthority.go @@ -0,0 +1,216 @@ +// Copyright 2022 Google LLC +// +// 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 mockprivateca + +import ( + "context" + "fmt" + "strings" + "time" + + "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/emptypb" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects" + pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/security/privateca/v1" +) + +type PrivateCAV1 struct { + *MockService + pb.UnimplementedCertificateAuthorityServiceServer +} + +func (s *PrivateCAV1) GetCaPool(ctx context.Context, req *pb.GetCaPoolRequest) (*pb.CaPool, error) { + name, err := s.parseCAPoolName(req.Name) + if err != nil { + return nil, err + } + + fqn := name.String() + + obj := &pb.CaPool{} + if err := s.storage.Get(ctx, fqn, obj); err != nil { + if status.Code(err) == codes.NotFound { + return nil, status.Errorf(codes.NotFound, "Resource '%s' was not found", fqn) + } + return nil, err + } + + return obj, nil +} + +func (s *PrivateCAV1) CreateCaPool(ctx context.Context, req *pb.CreateCaPoolRequest) (*longrunning.Operation, error) { + reqName := req.Parent + "/caPools/" + req.CaPoolId + name, err := s.parseCAPoolName(reqName) + if err != nil { + return nil, err + } + + now := time.Now() + + fqn := name.String() + + obj := proto.Clone(req.CaPool).(*pb.CaPool) + obj.Name = fqn + + // service seems to remove "zero" values + baseKeyUsage := obj.GetIssuancePolicy().GetBaselineValues().GetKeyUsage().GetBaseKeyUsage() + if baseKeyUsage != nil { + if proto.Equal(baseKeyUsage, &pb.KeyUsage_KeyUsageOptions{}) { + obj.IssuancePolicy.BaselineValues.KeyUsage.BaseKeyUsage = nil + } + } + + extendedKeyUsage := obj.GetIssuancePolicy().GetBaselineValues().GetKeyUsage().GetExtendedKeyUsage() + if extendedKeyUsage != nil { + if proto.Equal(extendedKeyUsage, &pb.KeyUsage_ExtendedKeyUsageOptions{}) { + obj.IssuancePolicy.BaselineValues.KeyUsage.ExtendedKeyUsage = nil + } + } + + if err := s.storage.Create(ctx, fqn, obj); err != nil { + return nil, err + } + + opMetadata := &pb.OperationMetadata{ + ApiVersion: "v1", + CreateTime: timestamppb.New(now), + Verb: "create", + RequestedCancellation: false, + Target: fqn, + } + opPrefix := fmt.Sprintf("projects/%s/locations/%s", name.Project.ID, name.Location) + return s.operations.StartLRO(ctx, opPrefix, opMetadata, func() (proto.Message, error) { + opMetadata.EndTime = timestamppb.Now() + return obj, nil + }) +} + +func (s *PrivateCAV1) UpdateCaPool(ctx context.Context, req *pb.UpdateCaPoolRequest) (*longrunning.Operation, error) { + reqName := req.GetCaPool().GetName() + + name, err := s.parseCAPoolName(reqName) + if err != nil { + return nil, err + } + + now := time.Now() + + fqn := name.String() + obj := &pb.CaPool{} + if err := s.storage.Get(ctx, fqn, obj); err != nil { + return nil, err + } + + // Required. A list of fields to be updated in this request. + paths := req.GetUpdateMask().GetPaths() + + // TODO: Some sort of helper for fieldmask? + for _, path := range paths { + switch path { + case "issuancePolicy": + obj.IssuancePolicy = req.GetCaPool().GetIssuancePolicy() + case "publishingOptions": + obj.PublishingOptions = req.GetCaPool().GetPublishingOptions() + case "labels": + obj.Labels = req.GetCaPool().GetLabels() + default: + return nil, status.Errorf(codes.InvalidArgument, "update_mask path %q not valid", path) + } + } + + if err := s.storage.Update(ctx, fqn, obj); err != nil { + return nil, err + } + + opMetadata := &pb.OperationMetadata{ + ApiVersion: "v1", + CreateTime: timestamppb.New(now), + Verb: "update", + RequestedCancellation: false, + Target: fqn, + } + opPrefix := fmt.Sprintf("projects/%s/locations/%s", name.Project.ID, name.Location) + return s.operations.StartLRO(ctx, opPrefix, opMetadata, func() (proto.Message, error) { + opMetadata.EndTime = timestamppb.Now() + return obj, nil + }) +} + +func (s *PrivateCAV1) DeleteCaPool(ctx context.Context, req *pb.DeleteCaPoolRequest) (*longrunning.Operation, error) { + name, err := s.parseCAPoolName(req.Name) + if err != nil { + return nil, err + } + + fqn := name.String() + + now := time.Now() + + oldObj := &pb.CaPool{} + if err := s.storage.Delete(ctx, fqn, oldObj); err != nil { + return nil, err + } + + opMetadata := &pb.OperationMetadata{ + ApiVersion: "v1", + CreateTime: timestamppb.New(now), + Verb: "delete", + RequestedCancellation: false, + Target: fqn, + } + opPrefix := fmt.Sprintf("projects/%s/locations/%s", name.Project.ID, name.Location) + return s.operations.StartLRO(ctx, opPrefix, opMetadata, func() (proto.Message, error) { + opMetadata.EndTime = timestamppb.Now() + return &emptypb.Empty{}, nil + }) +} + +type caPoolName struct { + Project *projects.ProjectData + Location string + CAPoolName string +} + +func (n *caPoolName) String() string { + return "projects/" + n.Project.ID + "/locations/" + n.Location + "/caPools/" + n.CAPoolName +} + +// parseCAPoolName parses a string into a caPoolName. +// The expected form is projects//locations//caPools/ +func (s *MockService) parseCAPoolName(name string) (*caPoolName, error) { + tokens := strings.Split(name, "/") + + if len(tokens) == 6 && tokens[0] == "projects" && tokens[2] == "locations" && tokens[4] == "caPools" { + project, err := s.Projects.GetProjectByID(tokens[1]) + if err != nil { + return nil, err + } + + name := &caPoolName{ + Project: project, + Location: tokens[3], + CAPoolName: tokens[5], + } + + return name, nil + } else { + return nil, status.Errorf(codes.InvalidArgument, "name %q is not valid", name) + } +} From 27d03b8d947e324480063178e0a2c8a8d4278c64 Mon Sep 17 00:00:00 2001 From: Eric Pang Date: Thu, 28 Nov 2024 15:29:17 +0000 Subject: [PATCH 2/5] mock ca skeleton --- mockgcp/mockprivateca/certificateauthority.go | 97 +------------------ 1 file changed, 3 insertions(+), 94 deletions(-) diff --git a/mockgcp/mockprivateca/certificateauthority.go b/mockgcp/mockprivateca/certificateauthority.go index 68883b21af..8fe98e321c 100644 --- a/mockgcp/mockprivateca/certificateauthority.go +++ b/mockgcp/mockprivateca/certificateauthority.go @@ -17,7 +17,6 @@ package mockprivateca import ( "context" "fmt" - "strings" "time" "google.golang.org/genproto/googleapis/longrunning" @@ -27,16 +26,10 @@ import ( "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects" pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/security/privateca/v1" ) -type PrivateCAV1 struct { - *MockService - pb.UnimplementedCertificateAuthorityServiceServer -} - -func (s *PrivateCAV1) GetCaPool(ctx context.Context, req *pb.GetCaPoolRequest) (*pb.CaPool, error) { +func (s *PrivateCAV1) GetCertificateAuthority(context.Context, *pb.GetCertificateAuthorityRequest) (*pb.CertificateAuthority, error) { name, err := s.parseCAPoolName(req.Name) if err != nil { return nil, err @@ -55,7 +48,7 @@ func (s *PrivateCAV1) GetCaPool(ctx context.Context, req *pb.GetCaPoolRequest) ( return obj, nil } -func (s *PrivateCAV1) CreateCaPool(ctx context.Context, req *pb.CreateCaPoolRequest) (*longrunning.Operation, error) { +func (s *PrivateCAV1) CreateCertificateAuthority(context.Context, *pb.CreateCertificateAuthorityRequest) (*longrunning.Operation, error) { reqName := req.Parent + "/caPools/" + req.CaPoolId name, err := s.parseCAPoolName(reqName) if err != nil { @@ -102,58 +95,7 @@ func (s *PrivateCAV1) CreateCaPool(ctx context.Context, req *pb.CreateCaPoolRequ }) } -func (s *PrivateCAV1) UpdateCaPool(ctx context.Context, req *pb.UpdateCaPoolRequest) (*longrunning.Operation, error) { - reqName := req.GetCaPool().GetName() - - name, err := s.parseCAPoolName(reqName) - if err != nil { - return nil, err - } - - now := time.Now() - - fqn := name.String() - obj := &pb.CaPool{} - if err := s.storage.Get(ctx, fqn, obj); err != nil { - return nil, err - } - - // Required. A list of fields to be updated in this request. - paths := req.GetUpdateMask().GetPaths() - - // TODO: Some sort of helper for fieldmask? - for _, path := range paths { - switch path { - case "issuancePolicy": - obj.IssuancePolicy = req.GetCaPool().GetIssuancePolicy() - case "publishingOptions": - obj.PublishingOptions = req.GetCaPool().GetPublishingOptions() - case "labels": - obj.Labels = req.GetCaPool().GetLabels() - default: - return nil, status.Errorf(codes.InvalidArgument, "update_mask path %q not valid", path) - } - } - - if err := s.storage.Update(ctx, fqn, obj); err != nil { - return nil, err - } - - opMetadata := &pb.OperationMetadata{ - ApiVersion: "v1", - CreateTime: timestamppb.New(now), - Verb: "update", - RequestedCancellation: false, - Target: fqn, - } - opPrefix := fmt.Sprintf("projects/%s/locations/%s", name.Project.ID, name.Location) - return s.operations.StartLRO(ctx, opPrefix, opMetadata, func() (proto.Message, error) { - opMetadata.EndTime = timestamppb.Now() - return obj, nil - }) -} - -func (s *PrivateCAV1) DeleteCaPool(ctx context.Context, req *pb.DeleteCaPoolRequest) (*longrunning.Operation, error) { +func (s *PrivateCAV1) DeleteCertificateAuthority(context.Context, *pb.DeleteCertificateAuthorityRequest) (*longrunning.Operation, error) { name, err := s.parseCAPoolName(req.Name) if err != nil { return nil, err @@ -181,36 +123,3 @@ func (s *PrivateCAV1) DeleteCaPool(ctx context.Context, req *pb.DeleteCaPoolRequ return &emptypb.Empty{}, nil }) } - -type caPoolName struct { - Project *projects.ProjectData - Location string - CAPoolName string -} - -func (n *caPoolName) String() string { - return "projects/" + n.Project.ID + "/locations/" + n.Location + "/caPools/" + n.CAPoolName -} - -// parseCAPoolName parses a string into a caPoolName. -// The expected form is projects//locations//caPools/ -func (s *MockService) parseCAPoolName(name string) (*caPoolName, error) { - tokens := strings.Split(name, "/") - - if len(tokens) == 6 && tokens[0] == "projects" && tokens[2] == "locations" && tokens[4] == "caPools" { - project, err := s.Projects.GetProjectByID(tokens[1]) - if err != nil { - return nil, err - } - - name := &caPoolName{ - Project: project, - Location: tokens[3], - CAPoolName: tokens[5], - } - - return name, nil - } else { - return nil, status.Errorf(codes.InvalidArgument, "name %q is not valid", name) - } -} From d825d84125cbea735c09e1d99aeed42a560c5c16 Mon Sep 17 00:00:00 2001 From: Eric Pang Date: Thu, 28 Nov 2024 15:42:03 +0000 Subject: [PATCH 3/5] Add mock ca --- mockgcp/mockprivateca/certificateauthority.go | 71 ++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/mockgcp/mockprivateca/certificateauthority.go b/mockgcp/mockprivateca/certificateauthority.go index 8fe98e321c..0b4b77fa8f 100644 --- a/mockgcp/mockprivateca/certificateauthority.go +++ b/mockgcp/mockprivateca/certificateauthority.go @@ -17,6 +17,7 @@ package mockprivateca import ( "context" "fmt" + "strings" "time" "google.golang.org/genproto/googleapis/longrunning" @@ -29,15 +30,15 @@ import ( pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/security/privateca/v1" ) -func (s *PrivateCAV1) GetCertificateAuthority(context.Context, *pb.GetCertificateAuthorityRequest) (*pb.CertificateAuthority, error) { - name, err := s.parseCAPoolName(req.Name) +func (s *PrivateCAV1) GetCertificateAuthority(ctx context.Context, req *pb.GetCertificateAuthorityRequest) (*pb.CertificateAuthority, error) { + name, err := s.parseCertificateAuthorityName(req.Name) if err != nil { return nil, err } fqn := name.String() - obj := &pb.CaPool{} + obj := &pb.CertificateAuthority{} if err := s.storage.Get(ctx, fqn, obj); err != nil { if status.Code(err) == codes.NotFound { return nil, status.Errorf(codes.NotFound, "Resource '%s' was not found", fqn) @@ -48,9 +49,9 @@ func (s *PrivateCAV1) GetCertificateAuthority(context.Context, *pb.GetCertificat return obj, nil } -func (s *PrivateCAV1) CreateCertificateAuthority(context.Context, *pb.CreateCertificateAuthorityRequest) (*longrunning.Operation, error) { - reqName := req.Parent + "/caPools/" + req.CaPoolId - name, err := s.parseCAPoolName(reqName) +func (s *PrivateCAV1) CreateCertificateAuthority(ctx context.Context, req *pb.CreateCertificateAuthorityRequest) (*longrunning.Operation, error) { + reqName := req.Parent + "/certificateAuthorities/" + req.CertificateAuthorityId + name, err := s.parseCertificateAuthorityName(reqName) if err != nil { return nil, err } @@ -59,24 +60,9 @@ func (s *PrivateCAV1) CreateCertificateAuthority(context.Context, *pb.CreateCert fqn := name.String() - obj := proto.Clone(req.CaPool).(*pb.CaPool) + obj := proto.Clone(req.CertificateAuthority).(*pb.CertificateAuthority) obj.Name = fqn - // service seems to remove "zero" values - baseKeyUsage := obj.GetIssuancePolicy().GetBaselineValues().GetKeyUsage().GetBaseKeyUsage() - if baseKeyUsage != nil { - if proto.Equal(baseKeyUsage, &pb.KeyUsage_KeyUsageOptions{}) { - obj.IssuancePolicy.BaselineValues.KeyUsage.BaseKeyUsage = nil - } - } - - extendedKeyUsage := obj.GetIssuancePolicy().GetBaselineValues().GetKeyUsage().GetExtendedKeyUsage() - if extendedKeyUsage != nil { - if proto.Equal(extendedKeyUsage, &pb.KeyUsage_ExtendedKeyUsageOptions{}) { - obj.IssuancePolicy.BaselineValues.KeyUsage.ExtendedKeyUsage = nil - } - } - if err := s.storage.Create(ctx, fqn, obj); err != nil { return nil, err } @@ -95,8 +81,8 @@ func (s *PrivateCAV1) CreateCertificateAuthority(context.Context, *pb.CreateCert }) } -func (s *PrivateCAV1) DeleteCertificateAuthority(context.Context, *pb.DeleteCertificateAuthorityRequest) (*longrunning.Operation, error) { - name, err := s.parseCAPoolName(req.Name) +func (s *PrivateCAV1) DeleteCertificateAuthority(ctx context.Context, req *pb.DeleteCertificateAuthorityRequest) (*longrunning.Operation, error) { + name, err := s.parseCertificateAuthorityName(req.Name) if err != nil { return nil, err } @@ -105,7 +91,7 @@ func (s *PrivateCAV1) DeleteCertificateAuthority(context.Context, *pb.DeleteCert now := time.Now() - oldObj := &pb.CaPool{} + oldObj := &pb.CertificateAuthority{} if err := s.storage.Delete(ctx, fqn, oldObj); err != nil { return nil, err } @@ -123,3 +109,38 @@ func (s *PrivateCAV1) DeleteCertificateAuthority(context.Context, *pb.DeleteCert return &emptypb.Empty{}, nil }) } + +type certificateAuthorityName struct { + caPoolName + CertificateAuthorityID string +} + +func (n *certificateAuthorityName) String() string { + return "projects/" + n.Project.ID + "/locations/" + n.Location + "/caPools/" + n.CAPoolName + "/certificateAuthorities" + n.CertificateAuthorityID +} + +// parseCertificateAuthorityName parses a string into a certificateAuthorityName. +// The expected form is projects//locations//caPools//certificateAuthorities/ +func (s *MockService) parseCertificateAuthorityName(name string) (*certificateAuthorityName, error) { + tokens := strings.Split(name, "/") + + if len(tokens) == 8 && tokens[0] == "projects" && tokens[2] == "locations" && tokens[4] == "caPools" && tokens[6] == "certificateAuthorities" { + project, err := s.Projects.GetProjectByID(tokens[1]) + if err != nil { + return nil, err + } + + name := &certificateAuthorityName{ + caPoolName: caPoolName{ + Project: project, + Location: tokens[3], + CAPoolName: tokens[5], + }, + CertificateAuthorityID: tokens[7], + } + + return name, nil + } else { + return nil, status.Errorf(codes.InvalidArgument, "name %q is not valid", name) + } +} From fad7180c256b149ecdcb87497afb1e2e1dbbe2e4 Mon Sep 17 00:00:00 2001 From: Eric Pang Date: Fri, 29 Nov 2024 04:46:47 +0000 Subject: [PATCH 4/5] Add to harness --- config/tests/samples/create/harness.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/tests/samples/create/harness.go b/config/tests/samples/create/harness.go index 9635ba233c..715e26fcc5 100644 --- a/config/tests/samples/create/harness.go +++ b/config/tests/samples/create/harness.go @@ -824,6 +824,7 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured case schema.GroupKind{Group: "networkservices.cnrm.cloud.google.com", Kind: "NetworkServicesMesh"}: case schema.GroupKind{Group: "privateca.cnrm.cloud.google.com", Kind: "PrivateCACAPool"}: + case schema.GroupKind{Group: "privateca.cnrm.cloud.google.com", Kind: "PrivateCACertificateAuthority"}: case schema.GroupKind{Group: "privilegedaccessmanager.cnrm.cloud.google.com", Kind: "PrivilegedAccessManagerEntitlement"}: From c20656fb80fabdb02c0de91d157657e8225cb552 Mon Sep 17 00:00:00 2001 From: Eric Pang Date: Fri, 29 Nov 2024 05:56:08 +0000 Subject: [PATCH 5/5] fix syntax error --- ..._privatecacertificateauthority.golden.yaml | 52 ++ .../privatecacertificateauthority/_http.log | 672 ++++++++++++++++++ .../privatecacertificateauthority/create.yaml | 4 +- .../privatecacertificateauthority/update.yaml | 4 +- 4 files changed, 728 insertions(+), 4 deletions(-) create mode 100644 pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_generated_object_privatecacertificateauthority.golden.yaml create mode 100644 pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_http.log diff --git a/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_generated_object_privatecacertificateauthority.golden.yaml b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_generated_object_privatecacertificateauthority.golden.yaml new file mode 100644 index 0000000000..177c7896f0 --- /dev/null +++ b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_generated_object_privatecacertificateauthority.golden.yaml @@ -0,0 +1,52 @@ +apiVersion: privateca.cnrm.cloud.google.com/v1beta1 +kind: PrivateCACertificateAuthority +metadata: + annotations: + cnrm.cloud.google.com/management-conflict-prevention-policy: none + cnrm.cloud.google.com/state-into-spec: absent + finalizers: + - cnrm.cloud.google.com/finalizer + - cnrm.cloud.google.com/deletion-defender + generation: 2 + labels: + cnrm-test: "true" + label-one: value-one + label-two: value-two + name: privatecacertificateauthority-${uniqueId} + namespace: ${uniqueId} +spec: + caPoolRef: + name: privatecacapool-${uniqueId} + config: + subjectConfig: + subject: + commonName: my-certificate-authority + organization: Example + subjectAltName: + dnsNames: + - example.com + x509Config: + caOptions: + isCa: true + keyUsage: + baseKeyUsage: + certSign: true + crlSign: true + extendedKeyUsage: + serverAuth: true + keySpec: + algorithm: RSA_PKCS1_4096_SHA256 + lifetime: 86400s + location: us-central1 + projectRef: + external: projects/${projectId} + resourceID: privatecacertificateauthority-${uniqueId} + type: SELF_SIGNED +status: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: The resource is up to date + reason: UpToDate + status: "True" + type: Ready + observedGeneration: 2 diff --git a/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_http.log b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_http.log new file mode 100644 index 0000000000..a728d1a34c --- /dev/null +++ b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/_http.log @@ -0,0 +1,672 @@ +GET https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}?alt=json +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +404 Not Found +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "error": { + "code": 404, + "message": "Resource 'projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}' was not found", + "status": "NOT_FOUND" + } +} + +--- + +POST https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/caPools?alt=json&caPoolId=privatecacapool-${uniqueId} +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +{ + "issuancePolicy": { + "allowedIssuanceModes": { + "allowConfigBasedIssuance": false, + "allowCsrBasedIssuance": true + }, + "allowedKeyTypes": [ + { + "rsa": { + "maxModulusSize": 128, + "minModulusSize": 64 + } + }, + { + "ellipticCurve": { + "signatureAlgorithm": "ECDSA_P384" + } + } + ], + "baselineValues": { + "additionalExtensions": [ + { + "critical": false, + "objectId": { + "objectIdPath": [ + 1, + 7 + ] + }, + "value": "c3RyaW5nCg==" + } + ], + "aiaOcspServers": [ + "string" + ], + "caOptions": { + "isCa": false, + "maxIssuerPathLength": 7 + }, + "keyUsage": { + "baseKeyUsage": { + "certSign": false, + "contentCommitment": false, + "crlSign": false, + "dataEncipherment": false, + "decipherOnly": false, + "digitalSignature": false, + "encipherOnly": false, + "keyAgreement": false, + "keyEncipherment": false + }, + "extendedKeyUsage": { + "clientAuth": false, + "codeSigning": false, + "emailProtection": false, + "ocspSigning": false, + "serverAuth": false, + "timeStamping": false + }, + "unknownExtendedKeyUsages": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ] + }, + "policyIds": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ] + }, + "identityConstraints": { + "allowSubjectAltNamesPassthrough": false, + "allowSubjectPassthrough": false, + "celExpression": { + "description": "Always false", + "expression": "false", + "location": "devops.ca_pool.json", + "title": "Sample expression" + } + }, + "maximumLifetime": "43200s", + "passthroughExtensions": { + "additionalExtensions": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ], + "knownExtensions": [ + "BASE_KEY_USAGE" + ] + } + }, + "labels": { + "cnrm-test": "true", + "label-two": "value-two", + "managed-by-cnrm": "true" + }, + "name": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}", + "tier": "ENTERPRISE" +} + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "metadata": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.OperationMetadata", + "apiVersion": "v1", + "createTime": "2024-04-01T12:34:56.123456Z", + "target": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}", + "verb": "create" + }, + "name": "projects/${projectId}/locations/us-central1/operations/${operationID}" +} + +--- + +GET https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/operations/${operationID}?alt=json +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "done": true, + "metadata": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.OperationMetadata", + "apiVersion": "v1", + "createTime": "2024-04-01T12:34:56.123456Z", + "endTime": "2024-04-01T12:34:56.123456Z", + "target": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}", + "verb": "create" + }, + "name": "projects/${projectId}/locations/us-central1/operations/${operationID}", + "response": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.CaPool", + "issuancePolicy": { + "allowedIssuanceModes": { + "allowCsrBasedIssuance": true + }, + "allowedKeyTypes": [ + { + "rsa": { + "maxModulusSize": "128", + "minModulusSize": "64" + } + }, + { + "ellipticCurve": { + "signatureAlgorithm": "ECDSA_P384" + } + } + ], + "baselineValues": { + "additionalExtensions": [ + { + "objectId": { + "objectIdPath": [ + 1, + 7 + ] + }, + "value": "c3RyaW5nCg==" + } + ], + "aiaOcspServers": [ + "string" + ], + "caOptions": { + "isCa": false, + "maxIssuerPathLength": 7 + }, + "keyUsage": { + "unknownExtendedKeyUsages": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ] + }, + "policyIds": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ] + }, + "identityConstraints": { + "allowSubjectAltNamesPassthrough": false, + "allowSubjectPassthrough": false, + "celExpression": { + "description": "Always false", + "expression": "false", + "location": "devops.ca_pool.json", + "title": "Sample expression" + } + }, + "maximumLifetime": "43200s", + "passthroughExtensions": { + "additionalExtensions": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ], + "knownExtensions": [ + "BASE_KEY_USAGE" + ] + } + }, + "labels": { + "cnrm-test": "true", + "label-two": "value-two", + "managed-by-cnrm": "true" + }, + "name": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}", + "tier": "ENTERPRISE" + } +} + +--- + +GET https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}?alt=json +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "issuancePolicy": { + "allowedIssuanceModes": { + "allowCsrBasedIssuance": true + }, + "allowedKeyTypes": [ + { + "rsa": { + "maxModulusSize": "128", + "minModulusSize": "64" + } + }, + { + "ellipticCurve": { + "signatureAlgorithm": "ECDSA_P384" + } + } + ], + "baselineValues": { + "additionalExtensions": [ + { + "objectId": { + "objectIdPath": [ + 1, + 7 + ] + }, + "value": "c3RyaW5nCg==" + } + ], + "aiaOcspServers": [ + "string" + ], + "caOptions": { + "isCa": false, + "maxIssuerPathLength": 7 + }, + "keyUsage": { + "unknownExtendedKeyUsages": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ] + }, + "policyIds": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ] + }, + "identityConstraints": { + "allowSubjectAltNamesPassthrough": false, + "allowSubjectPassthrough": false, + "celExpression": { + "description": "Always false", + "expression": "false", + "location": "devops.ca_pool.json", + "title": "Sample expression" + } + }, + "maximumLifetime": "43200s", + "passthroughExtensions": { + "additionalExtensions": [ + { + "objectIdPath": [ + 1, + 7 + ] + } + ], + "knownExtensions": [ + "BASE_KEY_USAGE" + ] + } + }, + "labels": { + "cnrm-test": "true", + "label-two": "value-two", + "managed-by-cnrm": "true" + }, + "name": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}", + "tier": "ENTERPRISE" +} + +--- + +GET https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthorities/privatecacertificateauthority-${uniqueId}?alt=json +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +404 Not Found +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "error": { + "code": 404, + "message": "Resource 'projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthoritiesprivatecacertificateauthority-${uniqueId}' was not found", + "status": "NOT_FOUND" + } +} + +--- + +POST https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthorities?alt=json&certificateAuthorityId=privatecacertificateauthority-${uniqueId} +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +{ + "config": { + "subjectConfig": { + "subject": { + "commonName": "my-certificate-authority", + "organization": "Example" + }, + "subjectAltName": { + "dnsNames": [ + "example.com" + ] + } + }, + "x509Config": { + "caOptions": { + "isCa": true + }, + "keyUsage": { + "baseKeyUsage": { + "certSign": true, + "crlSign": true + }, + "extendedKeyUsage": { + "serverAuth": true + } + } + } + }, + "keySpec": { + "algorithm": "RSA_PKCS1_4096_SHA256" + }, + "labels": { + "cnrm-test": "true", + "label-two": "value-two", + "managed-by-cnrm": "true" + }, + "lifetime": "86400s", + "name": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthorities/privatecacertificateauthority-${uniqueId}", + "type": "SELF_SIGNED" +} + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "metadata": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.OperationMetadata", + "apiVersion": "v1", + "createTime": "2024-04-01T12:34:56.123456Z", + "target": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthoritiesprivatecacertificateauthority-${uniqueId}", + "verb": "create" + }, + "name": "projects/${projectId}/locations/us-central1/operations/${operationID}" +} + +--- + +GET https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/operations/${operationID}?alt=json +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "done": true, + "metadata": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.OperationMetadata", + "apiVersion": "v1", + "createTime": "2024-04-01T12:34:56.123456Z", + "endTime": "2024-04-01T12:34:56.123456Z", + "target": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthoritiesprivatecacertificateauthority-${uniqueId}", + "verb": "create" + }, + "name": "projects/${projectId}/locations/us-central1/operations/${operationID}", + "response": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.CertificateAuthority", + "config": { + "subjectConfig": { + "subject": { + "commonName": "my-certificate-authority", + "organization": "Example" + }, + "subjectAltName": { + "dnsNames": [ + "example.com" + ] + } + }, + "x509Config": { + "caOptions": { + "isCa": true + }, + "keyUsage": { + "baseKeyUsage": { + "certSign": true, + "crlSign": true + }, + "extendedKeyUsage": { + "serverAuth": true + } + } + } + }, + "keySpec": { + "algorithm": "RSA_PKCS1_4096_SHA256" + }, + "labels": { + "cnrm-test": "true", + "label-two": "value-two", + "managed-by-cnrm": "true" + }, + "lifetime": "86400s", + "name": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthoritiesprivatecacertificateauthority-${uniqueId}", + "type": "SELF_SIGNED" + } +} + +--- + +GET https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthorities/privatecacertificateauthority-${uniqueId}?alt=json +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "config": { + "subjectConfig": { + "subject": { + "commonName": "my-certificate-authority", + "organization": "Example" + }, + "subjectAltName": { + "dnsNames": [ + "example.com" + ] + } + }, + "x509Config": { + "caOptions": { + "isCa": true + }, + "keyUsage": { + "baseKeyUsage": { + "certSign": true, + "crlSign": true + }, + "extendedKeyUsage": { + "serverAuth": true + } + } + } + }, + "keySpec": { + "algorithm": "RSA_PKCS1_4096_SHA256" + }, + "labels": { + "cnrm-test": "true", + "label-two": "value-two", + "managed-by-cnrm": "true" + }, + "lifetime": "86400s", + "name": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthoritiesprivatecacertificateauthority-${uniqueId}", + "type": "SELF_SIGNED" +} + +--- + +DELETE https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthorities/privatecacertificateauthority-${uniqueId}?alt=json&ignoreActiveCertificates=true +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "metadata": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.OperationMetadata", + "apiVersion": "v1", + "createTime": "2024-04-01T12:34:56.123456Z", + "target": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthoritiesprivatecacertificateauthority-${uniqueId}", + "verb": "delete" + }, + "name": "projects/${projectId}/locations/us-central1/operations/${operationID}" +} + +--- + +GET https://privateca.googleapis.com/v1/projects/${projectId}/locations/us-central1/operations/${operationID}?alt=json +Content-Type: application/json +User-Agent: kcc/controller-manager DeclarativeClientLib/0.0.1 + +200 OK +Cache-Control: private +Content-Type: application/json; charset=UTF-8 +Server: ESF +Vary: Origin +Vary: X-Origin +Vary: Referer +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Xss-Protection: 0 + +{ + "done": true, + "metadata": { + "@type": "type.googleapis.com/google.cloud.security.privateca.v1.OperationMetadata", + "apiVersion": "v1", + "createTime": "2024-04-01T12:34:56.123456Z", + "endTime": "2024-04-01T12:34:56.123456Z", + "target": "projects/${projectId}/locations/us-central1/caPools/privatecacapool-${uniqueId}/certificateAuthoritiesprivatecacertificateauthority-${uniqueId}", + "verb": "delete" + }, + "name": "projects/${projectId}/locations/us-central1/operations/${operationID}", + "response": { + "@type": "type.googleapis.com/google.protobuf.Empty" + } +} \ No newline at end of file diff --git a/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/create.yaml b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/create.yaml index 129678ddba..f26ea55a42 100644 --- a/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/create.yaml +++ b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/create.yaml @@ -41,7 +41,7 @@ spec: baseKeyUsage: certSign: true crlSign: true - extendedKeyUsage: - serverAuth: true + extendedKeyUsage: + serverAuth: true keySpec: algorithm: RSA_PKCS1_4096_SHA256 diff --git a/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/update.yaml b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/update.yaml index 4787f9a995..dbc5561b63 100644 --- a/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/update.yaml +++ b/pkg/test/resourcefixture/testdata/basic/privateca/v1beta1/privatecacertificateauthority/update.yaml @@ -41,7 +41,7 @@ spec: baseKeyUsage: certSign: true crlSign: true - extendedKeyUsage: - serverAuth: true + extendedKeyUsage: + serverAuth: true keySpec: algorithm: RSA_PKCS1_4096_SHA256