1# -*- coding: utf-8 -*- # 2# Copyright 2017 Google LLC. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Helpers for parsing flags and arguments.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import unicode_literals 20 21from googlecloudsdk.calliope import arg_parsers 22from googlecloudsdk.calliope import base 23from googlecloudsdk.command_lib.kms import maps 24from googlecloudsdk.command_lib.util import completers 25from googlecloudsdk.command_lib.util import parameter_info_lib 26from googlecloudsdk.core import properties 27from googlecloudsdk.core import resources 28from googlecloudsdk.core.util import times 29 30KEY_RING_COLLECTION = 'cloudkms.projects.locations.keyRings' 31LOCATION_COLLECTION = 'cloudkms.projects.locations' 32 33# Collection names. 34CRYPTO_KEY_COLLECTION = 'cloudkms.projects.locations.keyRings.cryptoKeys' 35CRYPTO_KEY_VERSION_COLLECTION = '%s.cryptoKeyVersions' % CRYPTO_KEY_COLLECTION 36IMPORT_JOB_COLLECTION = 'cloudkms.projects.locations.keyRings.importJobs' 37# list command aggregators 38 39 40class ListCommandParameterInfo(parameter_info_lib.ParameterInfoByConvention): 41 42 def GetFlag(self, 43 parameter_name, 44 parameter_value=None, 45 check_properties=True, 46 for_update=False): 47 return super(ListCommandParameterInfo, self).GetFlag( 48 parameter_name, 49 parameter_value=parameter_value, 50 check_properties=check_properties, 51 for_update=for_update, 52 ) 53 54 55class ListCommandCompleter(completers.ListCommandCompleter): 56 57 def ParameterInfo(self, parsed_args, argument): 58 return ListCommandParameterInfo( 59 parsed_args, 60 argument, 61 self.collection, 62 updaters=COMPLETERS_BY_CONVENTION, 63 ) 64 65 66# kms completers 67 68 69class LocationCompleter(ListCommandCompleter): 70 71 def __init__(self, **kwargs): 72 super(LocationCompleter, self).__init__( 73 collection=LOCATION_COLLECTION, 74 list_command='kms locations list --uri', 75 **kwargs) 76 77 78class KeyRingCompleter(ListCommandCompleter): 79 80 def __init__(self, **kwargs): 81 super(KeyRingCompleter, self).__init__( 82 collection=KEY_RING_COLLECTION, 83 list_command='kms keyrings list --uri', 84 flags=['location'], 85 **kwargs) 86 87 88class KeyCompleter(ListCommandCompleter): 89 90 def __init__(self, **kwargs): 91 super(KeyCompleter, self).__init__( 92 collection=CRYPTO_KEY_COLLECTION, 93 list_command='kms keys list --uri', 94 flags=['location', 'keyring'], 95 **kwargs) 96 97 98class KeyVersionCompleter(ListCommandCompleter): 99 100 def __init__(self, **kwargs): 101 super(KeyVersionCompleter, self).__init__( 102 collection=CRYPTO_KEY_VERSION_COLLECTION, 103 list_command='kms keys versions list --uri', 104 flags=['location', 'key', 'keyring'], 105 **kwargs) 106 107 108@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA) 109class ImportJobCompleter(ListCommandCompleter): 110 111 def __init__(self, **kwargs): 112 super(ImportJobCompleter, self).__init__( 113 collection=IMPORT_JOB_COLLECTION, 114 list_command='beta kms import-jobs list --uri', 115 flags=['location', 'keyring'], 116 **kwargs) 117 118 119# completers by parameter name convention 120 121COMPLETERS_BY_CONVENTION = { 122 'location': (LocationCompleter, False), 123 'keyring': (KeyRingCompleter, False), 124 'key': (KeyCompleter, False), 125 'import-jobs': (ImportJobCompleter, False), 126} 127 128 129# Flags. 130def AddLocationFlag(parser, resource='resource'): 131 parser.add_argument( 132 '--location', 133 completer=LocationCompleter, 134 help='Location of the {0}.'.format(resource)) 135 136 137def AddKeyRingFlag(parser, resource='resource'): 138 parser.add_argument( 139 '--keyring', 140 completer=KeyRingCompleter, 141 help='Key ring of the {0}.'.format(resource)) 142 143 144def AddCryptoKeyFlag(parser, help_text=None): 145 parser.add_argument( 146 '--key', completer=KeyCompleter, help=help_text or 'The containing key.') 147 148 149def AddKeyResourceFlags(parser, help_text=None): 150 AddLocationFlag(parser, 'keyring') 151 AddKeyRingFlag(parser, 'key') 152 AddCryptoKeyFlag(parser, help_text) 153 154 155def AddCryptoKeyVersionFlag(parser, help_action, required=False): 156 parser.add_argument( 157 '--version', 158 required=required, 159 completer=KeyVersionCompleter, 160 help='Version {0}.'.format(help_action)) 161 162 163def AddCryptoKeyPrimaryVersionFlag(parser, help_action, required=False): 164 parser.add_argument( 165 '--primary-version', 166 required=required, 167 completer=KeyVersionCompleter, 168 help='Primary version {0}.'.format(help_action)) 169 170 171def AddRotationPeriodFlag(parser): 172 parser.add_argument( 173 '--rotation-period', 174 type=arg_parsers.Duration(lower_bound='1d'), 175 help=('Automatic rotation period of the key. See ' 176 '$ gcloud topic datetimes for information on duration formats.')) 177 178 179def AddNextRotationTimeFlag(parser): 180 parser.add_argument( 181 '--next-rotation-time', 182 type=arg_parsers.Datetime.Parse, 183 help=('Next automatic rotation time of the key. See ' 184 '$ gcloud topic datetimes for information on time formats.')) 185 186 187def AddRemoveRotationScheduleFlag(parser): 188 parser.add_argument( 189 '--remove-rotation-schedule', 190 action='store_true', 191 help='Remove any existing rotation schedule on the key.') 192 193 194def AddSkipInitialVersionCreationFlag(parser): 195 parser.add_argument( 196 '--skip-initial-version-creation', 197 default=None, 198 action='store_true', 199 dest='skip_initial_version_creation', 200 help=('Skip creating the first version in a key and setting it as ' 201 'primary during creation.')) 202 203 204def AddPlaintextFileFlag(parser, help_action): 205 parser.add_argument( 206 '--plaintext-file', 207 help='File path of the plaintext file {0}.'.format(help_action), 208 required=True) 209 210 211def AddCiphertextFileFlag(parser, help_action): 212 parser.add_argument( 213 '--ciphertext-file', 214 help='File path of the ciphertext file {0}.'.format(help_action), 215 required=True) 216 217 218def AddSignatureFileFlag(parser, help_action): 219 parser.add_argument( 220 '--signature-file', 221 help='Path to the signature file {}.'.format(help_action), 222 required=True) 223 224 225def AddInputFileFlag(parser, help_action): 226 parser.add_argument( 227 '--input-file', 228 help='Path to the input file {}.'.format(help_action), 229 required=True) 230 231 232def AddRsaAesWrappedKeyFileFlag(parser, help_action): 233 parser.add_argument( 234 '--rsa-aes-wrapped-key-file', 235 help='Path to the wrapped RSA AES key file {}.'.format(help_action)) 236 237 238def AddOutputFileFlag(parser, help_action): 239 parser.add_argument( 240 '--output-file', help='Path to the output file {}.'.format(help_action)) 241 242 243def AddAadFileFlag(parser): 244 parser.add_argument( 245 '--additional-authenticated-data-file', 246 help='File path to the optional file containing the additional ' 247 'authenticated data.') 248 249 250def AddProtectionLevelFlag(parser): 251 parser.add_argument( 252 '--protection-level', 253 choices=['software', 'hsm', 'external'], 254 default='software', 255 help='Protection level of the key.') 256 257 258def AddRequiredProtectionLevelFlag(parser): 259 parser.add_argument( 260 '--protection-level', 261 choices=['software', 'hsm'], 262 help='Protection level of the import job.', 263 required=True) 264 265 266def AddAttestationFileFlag(parser): 267 parser.add_argument( 268 '--attestation-file', help='Path to the output attestation file.') 269 270 271def AddDefaultAlgorithmFlag(parser): 272 parser.add_argument( 273 '--default-algorithm', 274 choices=sorted(maps.ALL_ALGORITHMS), 275 help='The default algorithm for the crypto key. For more information ' 276 'about choosing an algorithm, see ' 277 'https://cloud.google.com/kms/docs/algorithms.') 278 279 280def AddRequiredImportMethodFlag(parser): 281 parser.add_argument( 282 '--import-method', 283 choices=sorted(maps.IMPORT_METHOD_MAPPER.choices)[1:], 284 help='The wrapping method to be used for incoming key material. For more ' 285 'information about choosing an import method, see ' 286 'https://cloud.google.com/kms/docs/key-wrapping.', 287 required=True) 288 289 290def AddOptionalPublicKeyFileArgument(parser): 291 parser.add_argument( 292 '--public-key-file', 293 help='Optional path to the public key of the ImportJob, used to wrap the ' 294 'key for import. If missing, the public key will be fetched on your ' 295 'behalf.') 296 297 298def AddOptionalTargetKeyFileArgument(parser): 299 parser.add_argument( 300 '--target-key-file', 301 help='Optional path to the unwrapped target key to import into a Cloud ' 302 'KMS key version. If specified, the key will be securely wrapped before ' 303 'transmission to Google.') 304 305 306def AddDigestAlgorithmFlag(parser, help_action): 307 parser.add_argument( 308 '--digest-algorithm', 309 choices=sorted(maps.DIGESTS), 310 help=help_action, 311 required=True) 312 313 314def AddImportedVersionAlgorithmFlag(parser): 315 parser.add_argument( 316 '--algorithm', 317 choices=sorted(maps.ALGORITHMS_FOR_IMPORT), 318 help='The algorithm to assign to the new key version. For more ' 319 'information about supported algorithms, see ' 320 'https://cloud.google.com/kms/docs/algorithms.', 321 required=True) 322 323 324def AddExternalKeyUriFlag(parser): 325 parser.add_argument( 326 '--external-key-uri', 327 suggestion_aliases=['--key-uri'], 328 help='The URI of the external key for keys with protection level' 329 ' "external".') 330 331 332def AddStateFlag(parser): 333 parser.add_argument('--state', dest='state', help='State of the key version.') 334 335 336def AddSkipIntegrityVerification(parser): 337 parser.add_argument( 338 '--skip-integrity-verification', 339 default=None, 340 action='store_true', 341 dest='skip_integrity_verification', 342 help=('Skip integrity verification on request and response API fields.')) 343 344 345# Arguments 346def AddKeyRingArgument(parser, help_action): 347 parser.add_argument( 348 'keyring', 349 completer=KeyRingCompleter, 350 help='Name of the key ring {0}.'.format(help_action)) 351 352 353def AddCryptoKeyArgument(parser, help_action): 354 parser.add_argument( 355 'key', 356 completer=KeyCompleter, 357 help='Name of the key {0}.'.format(help_action)) 358 359 360def AddKeyResourceArgument(parser, help_action): 361 AddLocationFlag(parser, 'key') 362 AddKeyRingFlag(parser, 'key') 363 AddCryptoKeyArgument(parser, help_action) 364 365 366def AddCryptoKeyVersionArgument(parser, help_action): 367 parser.add_argument( 368 'version', 369 completer=KeyVersionCompleter, 370 help='Name of the version {0}.'.format(help_action)) 371 372 373def AddKeyVersionResourceArgument(parser, help_action): 374 AddKeyResourceFlags(parser) 375 AddCryptoKeyVersionArgument(parser, help_action) 376 377 378def AddPositionalImportJobArgument(parser, help_action): 379 parser.add_argument( 380 'import_job', 381 completer=ImportJobCompleter, 382 help='Name of the import job {0}.'.format(help_action)) 383 384 385def AddRequiredImportJobArgument(parser, help_action): 386 parser.add_argument( 387 '--import-job', 388 completer=ImportJobCompleter, 389 help='Name of the import job {0}.'.format(help_action), 390 required=True) 391 392 393def AddCertificateChainFlag(parser): 394 parser.add_argument( 395 '--certificate-chain-type', 396 default='all', 397 choices=['all', 'cavium', 'google-card', 'google-partition'], 398 help='Certificate chain to retrieve.') 399 400 401# Parsing. 402def ParseLocationName(args): 403 return resources.REGISTRY.Parse( 404 args.location, 405 params={'projectsId': properties.VALUES.core.project.GetOrFail}, 406 collection=LOCATION_COLLECTION) 407 408 409def ParseKeyRingName(args): 410 return resources.REGISTRY.Parse( 411 args.keyring, 412 params={ 413 'projectsId': properties.VALUES.core.project.GetOrFail, 414 'locationsId': args.MakeGetOrRaise('--location'), 415 }, 416 collection=KEY_RING_COLLECTION) 417 418 419def ParseCryptoKeyName(args): 420 return resources.REGISTRY.Parse( 421 args.key, 422 params={ 423 'keyRingsId': args.MakeGetOrRaise('--keyring'), 424 'locationsId': args.MakeGetOrRaise('--location'), 425 'projectsId': properties.VALUES.core.project.GetOrFail, 426 }, 427 collection=CRYPTO_KEY_COLLECTION) 428 429 430def ParseCryptoKeyVersionName(args): 431 return resources.REGISTRY.Parse( 432 args.version, 433 params={ 434 'cryptoKeysId': args.MakeGetOrRaise('--key'), 435 'keyRingsId': args.MakeGetOrRaise('--keyring'), 436 'locationsId': args.MakeGetOrRaise('--location'), 437 'projectsId': properties.VALUES.core.project.GetOrFail, 438 }, 439 collection=CRYPTO_KEY_VERSION_COLLECTION) 440 441 442def ParseImportJobName(args): 443 return resources.REGISTRY.Parse( 444 args.import_job, 445 params={ 446 'keyRingsId': args.MakeGetOrRaise('--keyring'), 447 'locationsId': args.MakeGetOrRaise('--location'), 448 'projectsId': properties.VALUES.core.project.GetOrFail, 449 }, 450 collection=IMPORT_JOB_COLLECTION) 451 452 453# Get parent type Resource from output of Parse functions above. 454def ParseParentFromResource(resource_ref): 455 collection_list = resource_ref.Collection().split('.') 456 parent_collection = '.'.join(collection_list[:-1]) 457 params = resource_ref.AsDict() 458 del params[collection_list[-1] + 'Id'] 459 return resources.REGISTRY.Create(parent_collection, **params) 460 461 462# Set proto fields from flags. 463def SetRotationPeriod(args, crypto_key): 464 if args.rotation_period is not None: 465 crypto_key.rotationPeriod = '{0}s'.format(args.rotation_period) 466 467 468def SetNextRotationTime(args, crypto_key): 469 if args.next_rotation_time is not None: 470 crypto_key.nextRotationTime = times.FormatDateTime(args.next_rotation_time) 471