513 lines
17 KiB
Python
513 lines
17 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2019 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.
|
|
"""Shared utilities to access the Google Secret Manager API."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
from apitools.base.py import exceptions as apitools_exceptions
|
|
from apitools.base.py import list_pager
|
|
from googlecloudsdk.api_lib.util import apis
|
|
from googlecloudsdk.calliope import base
|
|
from googlecloudsdk.command_lib.iam import iam_util
|
|
|
|
|
|
def GetClient(version=None):
|
|
"""Get the default client."""
|
|
return apis.GetClientInstance('secretmanager', version or
|
|
apis.ResolveVersion('secretmanager'))
|
|
|
|
|
|
def GetMessages(version=None):
|
|
"""Get the default messages module."""
|
|
return apis.GetMessagesModule('secretmanager', version or
|
|
apis.ResolveVersion('secretmanager'))
|
|
|
|
|
|
def _FormatUpdateMask(update_mask):
|
|
return ','.join(update_mask)
|
|
|
|
|
|
def _MakeReplicationMessage(messages, policy, locations, keys):
|
|
"""Create a replication message from its components."""
|
|
if not policy:
|
|
return None
|
|
replication = messages.Replication(automatic=messages.Automatic())
|
|
if policy == 'automatic' and keys:
|
|
replication = messages.Replication(
|
|
automatic=messages.Automatic(
|
|
customerManagedEncryption=messages.CustomerManagedEncryption(
|
|
kmsKeyName=keys[0])))
|
|
if policy == 'user-managed':
|
|
replicas = []
|
|
for i, location in enumerate(locations):
|
|
if i < len(keys):
|
|
replicas.append(
|
|
messages.Replica(
|
|
location=location,
|
|
customerManagedEncryption=messages.CustomerManagedEncryption(
|
|
kmsKeyName=keys[i])))
|
|
else:
|
|
replicas.append(messages.Replica(location=locations[i]))
|
|
|
|
replication = messages.Replication(
|
|
userManaged=messages.UserManaged(replicas=replicas))
|
|
return replication
|
|
|
|
|
|
class Client(object):
|
|
"""Base class for all clients."""
|
|
|
|
def __init__(self, client=None, messages=None, api_version=None):
|
|
self.client = client or GetClient(version=api_version)
|
|
self.messages = messages or self.client.MESSAGES_MODULE
|
|
|
|
|
|
class Locations(Client):
|
|
"""High-level client for locations."""
|
|
|
|
def __init__(self, client=None, messages=None, api_version=None):
|
|
super(Locations, self).__init__(client, messages, api_version)
|
|
self.service = self.client.projects_locations
|
|
|
|
def Get(self, location_ref):
|
|
"""Get the location with the given name."""
|
|
return self.service.Get(
|
|
self.messages.SecretmanagerProjectsLocationsGetRequest(
|
|
name=location_ref.RelativeName()))
|
|
|
|
def ListWithPager(self, project_ref, limit):
|
|
"""List secrets returning a pager object."""
|
|
request = self.messages.SecretmanagerProjectsLocationsListRequest(
|
|
name=project_ref.RelativeName())
|
|
|
|
return list_pager.YieldFromList(
|
|
service=self.service,
|
|
request=request,
|
|
field='locations',
|
|
limit=limit,
|
|
batch_size_attribute='pageSize')
|
|
|
|
|
|
class Secrets(Client):
|
|
"""High-level client for secrets."""
|
|
|
|
def __init__(self, client=None, messages=None, api_version=None):
|
|
client = client or GetClient(version=api_version)
|
|
super(Secrets, self).__init__(client, messages)
|
|
self.service = self.client.projects_secrets
|
|
|
|
def Create(
|
|
self,
|
|
secret_ref,
|
|
policy,
|
|
locations,
|
|
labels,
|
|
tags,
|
|
expire_time=None,
|
|
ttl=None,
|
|
keys=None,
|
|
next_rotation_time=None,
|
|
rotation_period=None,
|
|
topics=None,
|
|
annotations=None,
|
|
regional_kms_key_name=None,
|
|
version_destroy_ttl=None,
|
|
secret_location=None,
|
|
):
|
|
"""Create a secret."""
|
|
keys = keys or []
|
|
replication = _MakeReplicationMessage(self.messages, policy, locations,
|
|
keys)
|
|
topics_message_list = []
|
|
if topics:
|
|
for topic in topics:
|
|
topics_message_list.append(self.messages.Topic(name=topic))
|
|
new_annotations = self.messages.Secret.AnnotationsValue(
|
|
additionalProperties=[])
|
|
if annotations:
|
|
for annotation_pair in annotations:
|
|
new_annotations.additionalProperties.append(annotation_pair)
|
|
|
|
rotation = None
|
|
if next_rotation_time or rotation_period:
|
|
rotation = self.messages.Rotation(
|
|
nextRotationTime=next_rotation_time, rotationPeriod=rotation_period
|
|
)
|
|
|
|
customer_managed_encryption = None
|
|
if regional_kms_key_name:
|
|
customer_managed_encryption = self.messages.CustomerManagedEncryption(
|
|
kmsKeyName=regional_kms_key_name
|
|
)
|
|
|
|
# For regional requests, replication should not be there.
|
|
replication = None
|
|
return self.service.Create(
|
|
self.messages.SecretmanagerProjectsSecretsCreateRequest(
|
|
parent=GetParentRelativeNameForSecret(secret_ref, secret_location),
|
|
secretId=secret_ref.Name(),
|
|
secret=self.messages.Secret(
|
|
labels=labels,
|
|
tags=tags,
|
|
replication=replication,
|
|
expireTime=expire_time,
|
|
ttl=ttl,
|
|
topics=topics_message_list,
|
|
annotations=new_annotations,
|
|
rotation=rotation,
|
|
customerManagedEncryption=customer_managed_encryption,
|
|
versionDestroyTtl=version_destroy_ttl,
|
|
),
|
|
)
|
|
)
|
|
|
|
def Delete(self, secret_ref, etag=None, secret_location=None):
|
|
"""Delete a secret."""
|
|
return self.service.Delete(
|
|
self.messages.SecretmanagerProjectsSecretsDeleteRequest(
|
|
etag=etag, name=GetRelativeName(secret_ref, secret_location)
|
|
)
|
|
)
|
|
|
|
def Get(self, secret_ref, secret_location=None):
|
|
"""Get the secret with the given name."""
|
|
return self.service.Get(
|
|
self.messages.SecretmanagerProjectsSecretsGetRequest(
|
|
name=GetRelativeName(secret_ref, secret_location)
|
|
)
|
|
)
|
|
|
|
def GetOrNone(self, secret_ref, secret_location=None):
|
|
"""Attempt to get the secret, returning None if the secret does not exist."""
|
|
try:
|
|
return self.Get(secret_ref=secret_ref, secret_location=secret_location)
|
|
except apitools_exceptions.HttpNotFoundError:
|
|
return None
|
|
|
|
def ListWithPager(self, project_ref, limit, request_filter=None):
|
|
"""List secrets returning a pager object."""
|
|
request = self.messages.SecretmanagerProjectsSecretsListRequest(
|
|
parent=project_ref.RelativeName(), filter=request_filter)
|
|
|
|
return list_pager.YieldFromList(
|
|
service=self.service,
|
|
request=request,
|
|
field='secrets',
|
|
limit=limit,
|
|
batch_size_attribute='pageSize')
|
|
|
|
def AddVersion(self, secret_ref, data, data_crc32c, secret_location=None):
|
|
"""Adds a new version of an existing secret."""
|
|
request = self.messages.SecretmanagerProjectsSecretsAddVersionRequest(
|
|
parent=GetRelativeName(secret_ref, secret_location),
|
|
addSecretVersionRequest=self.messages.AddSecretVersionRequest(
|
|
payload=self.messages.SecretPayload(
|
|
data=data, dataCrc32c=data_crc32c
|
|
)
|
|
),
|
|
)
|
|
return self.service.AddVersion(request)
|
|
|
|
def Update(
|
|
self,
|
|
secret_ref,
|
|
labels,
|
|
update_mask,
|
|
etag=None,
|
|
expire_time=None,
|
|
ttl=None,
|
|
topics=None,
|
|
version_aliases=None,
|
|
annotations=None,
|
|
next_rotation_time=None,
|
|
rotation_period=None,
|
|
version_destroy_ttl=None,
|
|
secret_location=None,
|
|
regional_kms_key_name=None,
|
|
):
|
|
"""Update a secret."""
|
|
|
|
rotation = None
|
|
if next_rotation_time or rotation_period:
|
|
rotation = self.messages.Rotation(
|
|
nextRotationTime=next_rotation_time, rotationPeriod=rotation_period)
|
|
|
|
topics_message_list = []
|
|
|
|
if topics:
|
|
for topic in topics:
|
|
topics_message_list.append(self.messages.Topic(name=topic))
|
|
new_version_aliases = self.messages.Secret.VersionAliasesValue(
|
|
additionalProperties=[])
|
|
if version_aliases:
|
|
for version_alias_pair in version_aliases:
|
|
new_version_aliases.additionalProperties.append(version_alias_pair)
|
|
new_annotations = self.messages.Secret.AnnotationsValue(
|
|
additionalProperties=[])
|
|
if annotations:
|
|
for annotation_pair in annotations:
|
|
new_annotations.additionalProperties.append(annotation_pair)
|
|
|
|
customer_managed_encryption = None
|
|
if regional_kms_key_name:
|
|
customer_managed_encryption = self.messages.CustomerManagedEncryption(
|
|
kmsKeyName=regional_kms_key_name
|
|
)
|
|
return self.service.Patch(
|
|
self.messages.SecretmanagerProjectsSecretsPatchRequest(
|
|
name=GetRelativeName(secret_ref, secret_location),
|
|
secret=self.messages.Secret(
|
|
labels=labels,
|
|
versionAliases=new_version_aliases,
|
|
annotations=new_annotations,
|
|
etag=etag,
|
|
expireTime=expire_time,
|
|
ttl=ttl,
|
|
topics=topics_message_list,
|
|
rotation=rotation,
|
|
versionDestroyTtl=version_destroy_ttl,
|
|
customerManagedEncryption=customer_managed_encryption,
|
|
),
|
|
updateMask=_FormatUpdateMask(update_mask),
|
|
)
|
|
)
|
|
|
|
def SetReplication(self, secret_ref, policy, locations, keys):
|
|
"""Set the replication policy on an existing secret.."""
|
|
replication = _MakeReplicationMessage(
|
|
self.messages, policy, locations, keys
|
|
)
|
|
return self.service.Patch(
|
|
self.messages.SecretmanagerProjectsSecretsPatchRequest(
|
|
name=secret_ref.RelativeName(),
|
|
secret=self.messages.Secret(replication=replication),
|
|
updateMask=_FormatUpdateMask(['replication']),
|
|
)
|
|
)
|
|
|
|
def GetIamPolicy(self, resource_ref, secret_location=None):
|
|
"""Get iam policy request.
|
|
|
|
Args:
|
|
resource_ref: Multitype resource (regional or global secret resource)
|
|
secret_location: location of the secret, None if global
|
|
|
|
Returns:
|
|
Operation response
|
|
"""
|
|
# check the secret type
|
|
if secret_location:
|
|
self.service = self.client.projects_locations_secrets
|
|
req = self.messages.SecretmanagerProjectsLocationsSecretsGetIamPolicyRequest(
|
|
resource=GetRelativeName(resource_ref, secret_location),
|
|
options_requestedPolicyVersion=iam_util.MAX_LIBRARY_IAM_SUPPORTED_VERSION,
|
|
)
|
|
else:
|
|
self.service = self.client.projects_secrets
|
|
req = self.messages.SecretmanagerProjectsSecretsGetIamPolicyRequest(
|
|
options_requestedPolicyVersion=iam_util.MAX_LIBRARY_IAM_SUPPORTED_VERSION,
|
|
resource=GetRelativeName(resource_ref, secret_location),
|
|
)
|
|
return self.service.GetIamPolicy(req)
|
|
|
|
def SetIamPolicy(
|
|
self, secret_ref, policy, update_mask=None, secret_location=None
|
|
):
|
|
"""Set iam policy request.
|
|
|
|
Args:
|
|
secret_ref: secret resource
|
|
policy: policy to be set
|
|
update_mask: update mask
|
|
secret_location: location of the secret, None if global
|
|
|
|
Returns:
|
|
Operation response
|
|
"""
|
|
# check the secret type
|
|
if secret_location:
|
|
self.service = self.client.projects_locations_secrets
|
|
req = self.messages.SecretmanagerProjectsLocationsSecretsSetIamPolicyRequest(
|
|
resource=GetRelativeName(secret_ref, secret_location),
|
|
setIamPolicyRequest=self.messages.SetIamPolicyRequest(
|
|
policy=policy, updateMask=update_mask
|
|
),
|
|
)
|
|
else:
|
|
self.service = self.client.projects_secrets
|
|
req = self.messages.SecretmanagerProjectsSecretsSetIamPolicyRequest(
|
|
resource=GetRelativeName(secret_ref, secret_location),
|
|
setIamPolicyRequest=self.messages.SetIamPolicyRequest(
|
|
policy=policy, updateMask=update_mask
|
|
),
|
|
)
|
|
return self.service.SetIamPolicy(req)
|
|
|
|
def AddIamPolicyBinding(
|
|
self, resorce_ref, member, role, condition=None, secret_location=None
|
|
):
|
|
"""Add iam policy binding request."""
|
|
policy = self.GetIamPolicy(resorce_ref, secret_location=secret_location)
|
|
policy.version = iam_util.MAX_LIBRARY_IAM_SUPPORTED_VERSION
|
|
iam_util.AddBindingToIamPolicyWithCondition(
|
|
self.messages.Binding,
|
|
self.messages.Expr,
|
|
policy,
|
|
member,
|
|
role,
|
|
condition=condition,
|
|
)
|
|
return self.SetIamPolicy(
|
|
resorce_ref, policy, secret_location=secret_location
|
|
)
|
|
|
|
def RemoveIamPolicyBinding(
|
|
self, resorce_ref, member, role, condition=None, secret_location=None
|
|
):
|
|
"""Remove iam policy binding request."""
|
|
policy = self.GetIamPolicy(resorce_ref, secret_location=secret_location)
|
|
policy.version = iam_util.MAX_LIBRARY_IAM_SUPPORTED_VERSION
|
|
iam_util.RemoveBindingFromIamPolicyWithCondition(
|
|
policy,
|
|
member,
|
|
role,
|
|
condition=condition,
|
|
)
|
|
return self.SetIamPolicy(
|
|
resorce_ref, policy, secret_location=secret_location
|
|
)
|
|
|
|
|
|
class SecretsLatest(Client):
|
|
"""High-level client for latest secrets."""
|
|
|
|
def __init__(self, client=None, messages=None, api_versions=None):
|
|
super(SecretsLatest, self).__init__(client, messages, api_versions)
|
|
self.service = self.client.projects_secrets_latest
|
|
|
|
def Access(self, secret_ref, secret_location=None):
|
|
"""Access the latest version of a secret."""
|
|
return self.service.Access(
|
|
self.messages.SecretmanagerProjectsSecretsLatestAccessRequest(
|
|
name=GetRelativeName(secret_ref, secret_location)
|
|
)
|
|
)
|
|
|
|
|
|
class Versions(Client):
|
|
"""High-level client for secret versions."""
|
|
|
|
def __init__(self, client=None, messages=None, api_version=None):
|
|
super(Versions, self).__init__(client, messages, api_version)
|
|
self.service = self.client.projects_secrets_versions
|
|
|
|
def Access(self, version_ref, secret_location=None):
|
|
"""Access a specific version of a secret."""
|
|
return self.service.Access(
|
|
self.messages.SecretmanagerProjectsSecretsVersionsAccessRequest(
|
|
name=GetRelativeName(version_ref, secret_location)
|
|
)
|
|
)
|
|
|
|
def Destroy(self, version_ref, etag=None, secret_location=None):
|
|
"""Destroy a secret version."""
|
|
destroy_secret_version_request = self.messages.DestroySecretVersionRequest(
|
|
etag=etag)
|
|
return self.service.Destroy(
|
|
self.messages.SecretmanagerProjectsSecretsVersionsDestroyRequest(
|
|
destroySecretVersionRequest=destroy_secret_version_request,
|
|
name=GetRelativeName(version_ref, secret_location),
|
|
)
|
|
)
|
|
|
|
def Disable(self, version_ref, etag=None, secret_location=None):
|
|
"""Disable a secret version."""
|
|
disable_secret_version_request = self.messages.DisableSecretVersionRequest(
|
|
etag=etag)
|
|
return self.service.Disable(
|
|
self.messages.SecretmanagerProjectsSecretsVersionsDisableRequest(
|
|
disableSecretVersionRequest=disable_secret_version_request,
|
|
name=GetRelativeName(version_ref, secret_location),
|
|
)
|
|
)
|
|
|
|
def Enable(self, version_ref, etag=None, secret_location=None):
|
|
"""Enable a secret version."""
|
|
enable_secret_version_request = self.messages.EnableSecretVersionRequest(
|
|
etag=etag)
|
|
return self.service.Enable(
|
|
self.messages.SecretmanagerProjectsSecretsVersionsEnableRequest(
|
|
enableSecretVersionRequest=enable_secret_version_request,
|
|
name=GetRelativeName(version_ref, secret_location),
|
|
)
|
|
)
|
|
|
|
def Get(self, version_ref, secret_location=None):
|
|
"""Get the secret version with the given name."""
|
|
return self.service.Get(
|
|
self.messages.SecretmanagerProjectsSecretsVersionsGetRequest(
|
|
name=GetRelativeName(version_ref, secret_location)
|
|
)
|
|
)
|
|
|
|
def List(self, secret_ref, limit, secret_location=None):
|
|
"""List secrets and return an array."""
|
|
request = self.messages.SecretmanagerProjectsSecretsVersionsListRequest(
|
|
parent=GetRelativeName(secret_ref, secret_location), pageSize=limit
|
|
)
|
|
return self.service.List(request)
|
|
|
|
def ListWithPager(
|
|
self, secret_ref, limit, request_filter=None, secret_location=None
|
|
):
|
|
"""List secrets returning a pager object."""
|
|
request = self.messages.SecretmanagerProjectsSecretsVersionsListRequest(
|
|
parent=GetRelativeName(secret_ref, secret_location),
|
|
filter=request_filter,
|
|
pageSize=0,
|
|
)
|
|
return list_pager.YieldFromList(
|
|
service=self.service,
|
|
request=request,
|
|
field='versions',
|
|
limit=limit,
|
|
batch_size=0,
|
|
batch_size_attribute='pageSize')
|
|
|
|
|
|
def GetApiFromTrack(track):
|
|
"""Returns api version based on the track."""
|
|
if track == base.ReleaseTrack.BETA:
|
|
return 'v1beta2'
|
|
elif track == base.ReleaseTrack.GA:
|
|
return 'v1'
|
|
|
|
|
|
def GetRelativeName(resource, location):
|
|
if location:
|
|
resource_uri = resource.RelativeName()
|
|
split = resource_uri.split('/')
|
|
return '/'.join(split[:2]) + f'/locations/{location}/' + '/'.join(split[2:])
|
|
return resource.RelativeName()
|
|
|
|
|
|
def GetParentRelativeNameForSecret(resource, location):
|
|
if location:
|
|
resource_uri = resource.Parent().RelativeName()
|
|
return resource_uri + f'/locations/{location}'
|
|
return resource.Parent().RelativeName()
|