# -*- coding: utf-8 -*- # # Copyright 2020 Google LLC. All Rights Reserved. # # 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. """Update a new certificate template.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib.privateca import base as privateca_base from googlecloudsdk.api_lib.privateca import request_utils from googlecloudsdk.calliope import base from googlecloudsdk.command_lib.privateca import exceptions as privateca_exceptions from googlecloudsdk.command_lib.privateca import flags from googlecloudsdk.command_lib.privateca import operations from googlecloudsdk.command_lib.privateca import resource_args from googlecloudsdk.command_lib.util.args import labels_util from googlecloudsdk.core import log @base.ReleaseTracks(base.ReleaseTrack.GA) class Create(base.CreateCommand): r"""Update a certificate template.""" detailed_help = { 'DESCRIPTION': """Update a certificate template.""", 'EXAMPLES': """\ To update a template named "dns-restricted" with new default x509 extensions: $ {command} dns-restricted --location=us-west1 --predefined-values-file=x509_parameters.yaml To update a template named "dns-restricted" to allow requestors to specify subject: $ {command} dns-restricted --location=us-west1 --copy-subject To update a template named "dns-restricted" with allowed extension 'base-key-usage' to allow requestors to specify additional x509 extension 'extended-key-usage': $ {command} dns-restricted --location=us-west1 --copy-known-extensions=base-key-usage,extended-key-usage To update a template named "mtls-restricted" with allowed OID '1.1' to allow requestors to specify alternative OIDS '2.2,3.3': $ {command} mtls-restricted --location=us-west1 --copy-extensions-by-oid=2.2,3.3 """, } def _UpdateCertificateTemplateFromArgs(self, args, current_labels): """Creates a Certificate template object and update mask from Certificate template update flags. Requires that args has 'description', 'copy-sans', 'copy-subject', 'predefined-values-file', 'copy-known-extensions', 'copy-extensions-by-oid', and update labels flags registered. Args: args: The parser that contains the flag values. current_labels: The current set of labels for the Certificate Template. Returns: A tuple with the Certificate template object to update with and the list of strings representing the update mask, respectively. """ messages = privateca_base.GetMessagesModule('v1') template_to_update = messages.CertificateTemplate() update_mask = [] # We'll parse the identity constraints if any of the flags are specified, # but only include the paths in the update masks of the flags that were # explicitly specified. if ( args.IsSpecified('copy_sans') or args.IsSpecified('copy_subject') or args.IsSpecified('identity_cel_expression') ): template_to_update.identityConstraints = flags.ParseIdentityConstraints( args ) if args.IsSpecified('copy_sans'): update_mask.append( 'identity_constraints.allow_subject_alt_names_passthrough' ) if args.IsSpecified('copy_subject'): update_mask.append('identity_constraints.allow_subject_passthrough') if args.IsSpecified('identity_cel_expression'): update_mask.append('identity_constraints.cel_expression') if args.IsSpecified('predefined_values_file'): template_to_update.predefinedValues = flags.ParsePredefinedValues(args) update_mask.append('predefined_values') if args.IsSpecified('description'): template_to_update.description = args.description update_mask.append('description') known_exts_flags = args.IsSpecified( 'copy_known_extensions' ) or args.IsSpecified('drop_known_extensions') oid_exts_flags = args.IsSpecified( 'copy_extensions_by_oid' ) or args.IsSpecified('drop_oid_extensions') if known_exts_flags or oid_exts_flags: # Parse all extension flags into a CertificateExtensionConstraints # message. template_to_update.passthroughExtensions = ( flags.ParseExtensionConstraints(args) ) if known_exts_flags: update_mask.append('passthrough_extensions.known_extensions') if oid_exts_flags: update_mask.append('passthrough_extensions.additional_extensions') labels_diff = labels_util.Diff.FromUpdateArgs(args) labels_update = labels_diff.Apply( messages.CaPool.LabelsValue, current_labels ) if labels_update.needs_update: template_to_update.labels = labels_update.labels update_mask.append('labels') if not update_mask: raise privateca_exceptions.NoUpdateException( 'No updates found for the requested certificate template.' ) return template_to_update, update_mask @staticmethod def Args(parser): resource_args.AddCertificateTemplatePositionalResourceArg( parser, 'to update' ) base.Argument( '--description', help='A text description for the Certificate Template.' ).AddToParser(parser) flags.AddPredefinedValuesFileFlag(parser) flags.AddIdentityConstraintsFlags(parser, require_passthrough_flags=False) flags.AddExtensionConstraintsFlagsForUpdate(parser) labels_util.AddUpdateLabelsFlags(parser) def Run(self, args): client = privateca_base.GetClientInstance('v1') messages = privateca_base.GetMessagesModule('v1') cert_template_ref = args.CONCEPTS.certificate_template.Parse() template_name = cert_template_ref.RelativeName() current_cert_template = client.projects_locations_certificateTemplates.Get( messages.PrivatecaProjectsLocationsCertificateTemplatesGetRequest( name=template_name ) ) cert_template_to_update, update_mask = ( self._UpdateCertificateTemplateFromArgs( args, current_cert_template.labels ) ) # Confirm that the result of this update is intended to be identity # reflection, if applicable. flags.ValidateIdentityConstraints( args, existing_copy_subj=current_cert_template.identityConstraints.allowSubjectPassthrough, existing_copy_sans=current_cert_template.identityConstraints.allowSubjectAltNamesPassthrough, for_update=True, ) operation = client.projects_locations_certificateTemplates.Patch( messages.PrivatecaProjectsLocationsCertificateTemplatesPatchRequest( name=template_name, certificateTemplate=cert_template_to_update, updateMask=','.join(update_mask), requestId=request_utils.GenerateRequestId(), ) ) cert_template_response = operations.Await( operation, 'Updating Certificate Template.', api_version='v1' ) cert_template = operations.GetMessageFromResponse( cert_template_response, messages.CertificateTemplate ) log.status.Print( 'Updated Certificate Template [{}].'.format(cert_template.name) )