File: //snap/google-cloud-cli/current/lib/googlecloudsdk/command_lib/accesscontextmanager/policies.py
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Command line processing utilities for access policies."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.accesscontextmanager import policies as policies_api
from googlecloudsdk.api_lib.cloudresourcemanager import organizations
from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.calliope.concepts import deps
from googlecloudsdk.command_lib.meta import cache_util as meta_cache_util
from googlecloudsdk.command_lib.util import cache_util
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
class DefaultPolicyResolutionError(exceptions.Error):
pass
def ValidateAccessPolicyArg(ref, args, req=None):
"""Add the particular service filter message based on specified args."""
del ref # unused
if args.IsSpecified('policy'):
properties.AccessPolicyValidator(args.policy)
return req
def GetAttributeConfig():
property_ = properties.VALUES.access_context_manager.policy
return concepts.ResourceParameterAttributeConfig(
name='policy',
help_text='The ID of the access policy.',
fallthroughs=[deps.PropertyFallthrough(property_)])
def GetResourceSpec():
return concepts.ResourceSpec(
'accesscontextmanager.accessPolicies',
resource_name='policy',
accessPoliciesId=GetAttributeConfig())
def AddResourceArg(parser, verb):
"""Add a resource argument for an access policy.
NOTE: Must be used only if it's the only resource arg in the command.
Args:
parser: the parser for the command.
verb: str, the verb to describe the resource, such as 'to update'.
"""
concept_parsers.ConceptParser.ForResource(
'policy',
GetResourceSpec(),
'The access policy {}.'.format(verb),
required=True).AddToParser(parser)
@cache_util.CacheResource('organizations-by-domain', 10)
def _GetOrganization(domain):
"""Get the organization for the given domain.
The current user must have permission to list the organization.
Args:
domain: str, the domain (e.g. 'example.com') to look up the organization of,
or None to just list the organizations for the current account.
Returns:
resources.Resource, a reference to a cloudresourcemanager.organizations
resource
Raises:
DefaultPolicyResolutionError: if the number of organizations matching the
given domain is not exactly 1, or searching organizations fails.
"""
filter_ = 'domain:' + domain
try:
orgs = list(organizations.Client().List(filter_=filter_, limit=2))
except Exception as err:
raise DefaultPolicyResolutionError(
'Unable to resolve organization for domain [{}]: {}'.format(
domain, err))
if not orgs:
raise DefaultPolicyResolutionError(
'No matching organizations found for domain [{}].'.format(domain))
elif len(orgs) > 1:
raise DefaultPolicyResolutionError(
'Found more than one organization for domain [{}].\n{}'.format(
domain, orgs))
return resources.REGISTRY.Parse(
orgs[0].name, collection='cloudresourcemanager.organizations')
@cache_util.CacheResource('policies-by-organization', 10)
def _GetPolicy(organization_ref):
"""Get the access policy for the given organization.
The current user must have permission to list the policies for the
organization.
Args:
organization_ref: resources.Resource, a reference to a
cloudresourcemanager.organizations resource to look up the policy for.
Returns:
resources.Resource, a reference to an accesscontextmanager.accessPolicies
resource
Raises:
DefaultPolicyResolutionError: if the number of policies matching the
given organization is not exactly 1, or listing policies fails.
"""
try:
policies = list(policies_api.Client().List(organization_ref, limit=2))
except Exception as err:
raise DefaultPolicyResolutionError(
'Unable to resolve policy for organization [{}]: {}'.format(
organization_ref.Name(), err))
if not policies:
raise DefaultPolicyResolutionError(
'No matching policies found for organization [{}]'.format(
organization_ref.Name()))
elif len(policies) > 1:
raise DefaultPolicyResolutionError(
'Found more than one access policy for organization [{}]:\n{}'.format(
organization_ref.Name(), policies))
policy_ref = resources.REGISTRY.Parse(
policies[0].name, collection='accesscontextmanager.accessPolicies')
return policy_ref
_IAM_SUFFIX = '.iam.gserviceaccount.com'
_DEVELOPER_DOMAIN = 'developer.gserviceaccount.com'
def _GetDomain(account):
_, _, host = account.partition('@')
if host.endswith(_IAM_SUFFIX) or host == _DEVELOPER_DOMAIN:
return None
return host
def GetDefaultPolicy():
"""Gets the ID of the default policy for the current account."""
account = properties.VALUES.core.account.Get()
if not account:
log.info('Unable to automatically resolve policy since account property '
'is not set.')
return None
domain = _GetDomain(account)
if not domain:
log.info('Unable to resolve domain for account [%s]', account)
return None
with meta_cache_util.GetCache('resource://', create=True) as cache:
try:
# pylint: disable=too-many-function-args
organization_ref = _GetOrganization(cache, domain)
policy_ref = _GetPolicy(cache, organization_ref.RelativeName(),
(organization_ref,))
except DefaultPolicyResolutionError as err:
log.info('Unable to automatically resolve policy: %s', err)
return None
return policy_ref.Name()