예제 #1
0
    def get_service_accounts(self, project_id):
        """Get Service Accounts associated with a project.

        Args:
            project_id (str): The project ID to get Service Accounts for.

        Returns:
            list: List of service accounts associated with the project.

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        name = self.repository.projects_serviceaccounts.get_name(project_id)

        try:
            paged_results = self.repository.projects_serviceaccounts.list(name)
            flattened_results = api_helpers.flatten_list_results(paged_results,
                                                                 'accounts')
            LOGGER.debug('Getting service accounts associated with a project,'
                         ' project_id = %s, flattened_results = %s',
                         project_id, flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'serviceAccounts', e, 'name', name)
            LOGGER.exception(api_exception)
            raise api_exception
예제 #2
0
    def get_organization_roles(self, org_id):
        """Get information about custom organization roles.

        Args:
            org_id (str): The id of the organization.

        Returns:
            list: The response of retrieving the organization roles.

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        name = self.repository.organizations_roles.get_name(org_id)

        try:
            paged_results = self.repository.organizations_roles.list(
                name, view='FULL')
            flattened_results = api_helpers.flatten_list_results(paged_results,
                                                                 'roles')
            LOGGER.debug('Getting information about custom organization roles,'
                         ' org_id = %s, flattened_results = %s',
                         org_id, flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'organizations_roles', e, 'name', name)
            LOGGER.exception(api_exception)
            raise api_exception
예제 #3
0
    def get_service_account_iam_policy(self, name):
        """Get IAM policy associated with a service account.

        Args:
            name (str): The service account name to query, must be in the format
                projects/{PROJECT_ID}/serviceAccounts/{SERVICE_ACCOUNT_EMAIL}

        Returns:
            dict: The IAM policies for the service account.

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        try:
            results = self.repository.projects_serviceaccounts.get_iam_policy(
                name)
            LOGGER.debug('Getting the IAM Policy associated with the service'
                         ' account, name = %s, results = %s', name, results)
            return results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'serviceAccountIamPolicy', e, 'name', name)
            LOGGER.exception(api_exception)
            raise api_exception
예제 #4
0
    def get_curated_roles(self, parent=None):
        """Get information about organization roles

        Args:
            parent (str): An optional parent ID to query. If unset, defaults
                to returning the list of curated roles in GCP.

        Returns:
            list: The response of retrieving the curated roles.

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        try:
            paged_results = self.repository.roles.list(parent=parent,
                                                       view='FULL')
            flattened_results = api_helpers.flatten_list_results(paged_results,
                                                                 'roles')
            LOGGER.debug('Getting information about organization roles,'
                         ' parent = %s, flattened_results = %s',
                         parent, flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'project_roles', e, 'parent', parent)
            LOGGER.exception(api_exception)
            raise api_exception
예제 #5
0
    def create_finding(self, finding, source_id=None, finding_id=None):
        """Creates a finding in CSCC.

        Args:
            finding (dict): Forseti violation in CSCC format.
            source_id (str): Unique ID assigned by CSCC, to the organization
                that the violations are originating from.
            finding_id (str): id hash of the CSCC finding

        Returns:
            dict: An API response containing one page of results.
        """
        try:
            LOGGER.debug('Creating finding.')

            # patch() will also create findings for new violations.
            response = self.repository.findings.patch(
                '{}/findings/{}'.format(source_id, finding_id), finding)
            LOGGER.debug('Successfully created finding response: %s', response)
            return response
        except (errors.HttpError, HttpLib2Error) as e:
            raw_error = e.args[1]
            error = raw_error.decode('utf-8')
            formatted_error = json.loads(error)
            error_code = formatted_error['error']['code']
            if error_code == 409:
                LOGGER.debug(
                    'Unable to create finding. Finding already exists '
                    'in CSCC. %s', finding)
            else:
                LOGGER.exception('Unable to create CSCC finding: Resource: %s',
                                 finding)
                violation_data = (
                    finding.get('source_properties').get('violation_data'))
                raise api_errors.ApiExecutionError(violation_data, e)
예제 #6
0
    def get_buckets(self, project_id):
        """Gets all GCS buckets for a project.

        Args:
            project_id (int): The project id for a GCP project.

        Returns:
            list: a list of bucket resource dicts.
            https://cloud.google.com/storage/docs/json_api/v1/buckets

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails
        """
        try:
            paged_results = self.repository.buckets.list(project_id,
                                                         projection='full')
            flattened_results = api_helpers.flatten_list_results(paged_results,
                                                                 'items')
            LOGGER.debug('Getting all GCS buckets for a project, project_id ='
                         ' %s, flattened_results = %s',
                         project_id, flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'buckets', e, 'project_id', project_id)
            LOGGER.exception(api_exception)
            raise api_exception
예제 #7
0
    def list_instances(self, project_id, service_id, version_id):
        """Lists instances of a given service and version.

        Args:
            project_id (str): The id of the project.
            service_id (str): The id of the service to query.
            version_id (str): The id of the version to query.

        Returns:
            list: A list of Instance resource dicts for a given Version.
        """
        try:
            paged_results = self.repository.version_instances.list(
                project_id, services_id=service_id, versions_id=version_id)
            flattened_results = api_helpers.flatten_list_results(
                paged_results, 'instances')
            LOGGER.debug(
                'Listing instances of a given service and version,'
                ' project_id = %s, service_id = %s, version_id = %s,'
                ' flattened_results = %s', project_id, service_id, version_id,
                flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            if e.resp.status == 501:
                LOGGER.debug(e)
                return []
            if _is_status_not_found(e):
                return []
            raise api_errors.ApiExecutionError(project_id, e)
    def get_instances(self, project_id):
        """Gets all CloudSQL instances for a project.

        Args:
            project_id (int): The project id for a GCP project.

        Returns:
            list: A list of database Instance resource dicts for a project_id.
            https://cloud.google.com/sql/docs/mysql/admin-api/v1beta4/instances

            [{"kind": "sql#instance", "name": "sql_instance1", ...}
             {"kind": "sql#instance", "name": "sql_instance2", ...},
             {...}]

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP ClodSQL API fails
        """

        try:
            paged_results = self.repository.instances.list(project_id)
            flattened_results = api_helpers.flatten_list_results(
                paged_results, 'items')
            LOGGER.debug(
                'Getting all the cloudsql instances of a project,'
                ' project_id = %s, flattened_results = %s', project_id,
                flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'instances', e, 'project_id', project_id)
            LOGGER.exception(api_exception)
            raise api_exception
예제 #9
0
    def get_instance(self, project_id, service_id, version_id, instances_id):
        """Gets information about a specific instance of a service.

        Args:
            project_id (str): The id of the project.
            service_id (str): The id of the service to query.
            version_id (str): The id of the version to query.
            instances_id (str): The id of the instance to query.

        Returns:
            dict: An Instance resource dict for a given project_id,
            service_id and version_id.
        """
        try:
            results = self.repository.version_instances.get(
                project_id,
                target=instances_id,
                services_id=service_id,
                versions_id=version_id)
            LOGGER.debug(
                'Getting information about a specific instance of'
                ' a service, project_id = %s, service_id = %s,'
                ' version_id = %s, instance_id = %s, results = %s', project_id,
                service_id, version_id, instances_id, results)
            return results
        except (errors.HttpError, HttpLib2Error) as e:
            if _is_status_not_found(e):
                return {}
            raise api_errors.ApiExecutionError(project_id, e)
예제 #10
0
    def get_users(self, customer_id='my_customer'):
        """Get all the users for a given customer_id.

        A note on customer_id='my_customer'. This is a magic string instead
        of using the real customer id. See:

        https://developers.google.com/admin-sdk/directory/v1/guides/manage-groups#get_all_domain_groups

        Args:
            customer_id (str): The customer id to scope the request to.

        Returns:
            list: A list of user objects returned from the API.

        Raises:
            api_errors.ApiExecutionError: If groups retrieval fails.
            RefreshError: If the authentication fails.
        """
        try:
            paged_results = self.repository.users.list(customer=customer_id,
                                                       viewType='admin_view')
            flattened_results = api_helpers.flatten_list_results(
                paged_results, 'users')
            LOGGER.debug(
                'Getting all the users for customer_id = %s,'
                ' flattened_results = %s', customer_id, flattened_results)
            return flattened_results
        except RefreshError as e:
            # Authentication failed, log before raise.
            LOGGER.exception(GSUITE_AUTH_FAILURE_MESSAGE)
            raise e
        except (errors.HttpError, HttpLib2Error) as e:
            raise api_errors.ApiExecutionError('users', e)
예제 #11
0
    def test_enforce_policy_error_listing_networks(self, mock_logger):
        """Forces an error when listing project networks.

        Setup:
          * Set the networks.list API call to return an error.
          * Set the firewalls.list API call to return the current firewall
            rules.

        Expected Results:
          A ProjectResult proto showing status=ERROR and the correct reason
          string.
        """
        err = api_errors.ApiExecutionError(self.project, self.error_403)
        self.gce_api_client.get_networks.side_effect = err

        self.gce_api_client.get_firewall_rules.return_value = (
            self.expected_rules)

        result = self.enforcer.enforce_firewall_policy(self.policy)

        self.expected_proto.status = project_enforcer.STATUS_ERROR
        self.expected_proto.status_reason = (
            'error getting current networks from API: <HttpError 403 '
            '"Failed">')
        self.expected_proto.ClearField('networks')

        self.validate_results(self.expected_proto, result)
        self.assertTrue(mock_logger.exception.called)
예제 #12
0
    def get_produced_apis(self, project_id):
        """Gets the APIs produced by a project.

        Args:
            project_id (str): The project id for a GCP project.

        Returns:
            list: A list of ManagedService resource dicts.
            https://cloud.google.com/service-management/reference/rest/v1/services#ManagedService

            {
              "serviceName": string,
              "producerProjectId": string,
            }
        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        try:
            paged_results = self.repository.services.list(
                producerProjectId=project_id,
                max_results=self.DEFAULT_MAX_RESULTS)
            flattened_results = api_helpers.flatten_list_results(
                paged_results, 'services')
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'name', e, 'project_id', project_id)
            LOGGER.exception(api_exception)
            raise api_exception

        LOGGER.debug(
            'Getting the APIs produced by a project, project_id = %s, '
            'flattened_results = %s', project_id, flattened_results)
        return flattened_results
예제 #13
0
    def get_billing_acct_iam_policies(self, account_id):
        """Gets the IAM policies for the given billing account.

        Args:
            account_id (str): The billing account id.

        Returns:
            dict: An IAM Policy resource.
            https://cloud.google.com/billing/reference/rest/v1/Policy

            {
              "bindings": list,
              "auditConfigs": list,
              "etag": string,
            }

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        name = self.repository.billing_accounts.get_name(account_id)

        try:
            results = self.repository.billing_accounts.get_iam_policy(
                name, include_body=False)
            LOGGER.debug(
                'Getting IAM policies for a given billing account,'
                ' account_id = %s, results = %s', account_id, results)
            return results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(account_id, e)
            LOGGER.exception(api_exception)
            raise api_exception
    def get_project_sinks(self, project_id):
        """Get information about project sinks.
        Args:
            project_id (str): The id of the project.
        Returns:
            list: The response of retrieving the project sinks.
        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        name = self.repository.projects_sinks.get_name(project_id)

        try:
            paged_results = self.repository.projects_sinks.list(name)
            flattened_results = api_helpers.flatten_list_results(paged_results,
                                                                 'sinks')
            LOGGER.debug('Getting information about project sinks,'
                         ' project_id = %s, flattened_results = %s',
                         project_id, flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'projects_sinks', e, 'name', name)
            LOGGER.exception(api_exception)
            raise api_exception
    def create_finding(self, finding, source_id=None, finding_id=None):
        """Creates a finding in CSCC.

        Args:
            finding (dict): Forseti violation in CSCC format.
            source_id (str): Unique ID assigned by CSCC, to the organization
                that the violations are originating from.
            finding_id (str): id hash of the CSCC finding

        Returns:
            dict: An API response containing one page of results.
        """
        try:
            LOGGER.debug('Creating finding.')

            # patch() will also create findings for new violations.
            response = self.repository.findings.patch(
                '{}/findings/{}'.format(source_id, finding_id), finding)
            LOGGER.debug('Created finding response: %s', response)
            return response
        except (errors.HttpError, HttpLib2Error) as e:
            LOGGER.exception('Unable to create CSCC finding: Resource: %s',
                             finding)
            violation_data = (
                finding.get('source_properties').get('violation_data'))
            raise api_errors.ApiExecutionError(violation_data, e)
예제 #16
0
    def get_datasets_for_projectid(self, project_id):
        """Return BigQuery datasets stored in the requested project_id.

        Args:
            project_id (str): String representing the project id.

        Returns:
            list: A list of datasetReference objects for a given project_id

        An example return value:

            [{'datasetId': 'dataset-id',
              'projectId': 'project-id'},
             {...}]
        """
        try:
            results = self.repository.datasets.list(
                resource=project_id, all=True)
            flattened_results = api_helpers.flatten_list_results(
                results, 'datasets')
            LOGGER.debug('Getting bigquery datasets for a given project,'
                         ' project_id = %s, flattened_results = %s',
                         project_id, flattened_results)
            return flattened_results
        except (errors.HttpError, HttpLib2Error) as e:
            raise api_errors.ApiExecutionError(project_id, e)
예제 #17
0
    def get_dataset_access(self, project_id, dataset_id):
        """Return the access portion of the dataset resource object.

        Args:
            project_id (str): String representing the project id.
            dataset_id (str): String representing the dataset id.

        Returns:
            list: A list of access lists for a given project_id and dataset_id.

        An example return value:

            [
                {'role': 'WRITER', 'specialGroup': 'projectWriters'},
                {'role': 'OWNER', 'specialGroup': 'projectOwners'},
                {'role': 'OWNER', 'userByEmail': '*****@*****.**'},
                {'role': 'READER', 'specialGroup': 'projectReaders'}
            ]
        """
        try:
            results = self.repository.datasets.get(resource=project_id,
                                                   target=dataset_id,
                                                   fields='access')
            access = results.get('access', [])
            LOGGER.debug('Geting the access portion of the dataset'
                         ' resource object, project_id = %s, dataset_id = %s,'
                         ' results = %s', project_id, dataset_id, access)
            return access
        except (errors.HttpError, HttpLib2Error) as e:
            raise api_errors.ApiExecutionError(project_id, e)
예제 #18
0
    def get_bigquery_projectids(self):
        """Request and page through bigquery projectids.

        Returns:
            list: A list of project_ids enabled for bigquery.

            If there are no project_ids enabled for bigquery an empty list will
            be returned.

        An example return value:

            ['project-id',
             'project-id',
             '...']
        """
        try:
            results = self.repository.projects.list(
                fields='nextPageToken,projects/id')
            flattened_results = api_helpers.flatten_list_results(
                results, 'projects')
            LOGGER.debug('Request and page through bigquery '
                         ' projectids, flattened_results = %s',
                         flattened_results)
        except (errors.HttpError, HttpLib2Error) as e:
            raise api_errors.ApiExecutionError('bigquery', e)

        project_ids = [result.get('id') for result in flattened_results
                       if 'id' in result]
        return project_ids
예제 #19
0
    def test_enforce_policy_firewall_enforcer_deleted_400(self):
        """Verifies that a deleted project returns a status=PROJECT_DELETED.

        Setup:
          * Switch the ListFirewalls response to be a 400 error with the reason
            string set to unknown project.

        Expected Result:
          A ProjectResult proto showing status=PROJECT_DELETED and the correct
          reason string.
        """
        deleted_400 = httplib2.Response({
            'status': '400',
            'content-type': 'application/json'
        })
        deleted_400.reason = 'Invalid value for project: %s' % self.project
        error_deleted_400 = errors.HttpError(deleted_400, ''.encode(), uri='')
        err = api_errors.ApiExecutionError(self.project, error_deleted_400)

        self.gce_api_client.get_firewall_rules.side_effect = err
        result = self.enforcer.enforce_firewall_policy(self.policy)

        self.expected_proto.status = project_enforcer.STATUS_DELETED

        # Match first part of error reason string
        self.assertStartsWith(result.status_reason,
                              'Project scheduled for deletion')

        # Copy reason string into expected proto. The reason includes a long
        # error message, which would be ugly to replicate in the test.
        self.expected_proto.status_reason = result.status_reason

        self.validate_results(self.expected_proto, result)
예제 #20
0
    def test_enforce_policy_error_listing_firewalls(self):
        """Forces an error when listing project firewall rules.

        Setup:
          * Set the firewalls.list API call to return an error.

        Expected Results:
          A ProjectResult proto showing status=ERROR and the correct reason
          string.
        """
        err = api_errors.ApiExecutionError(self.project, self.error_403)
        self.gce_api_client.get_firewall_rules.side_effect = err

        result = self.enforcer.enforce_firewall_policy(self.policy)

        self.expected_proto.status = project_enforcer.STATUS_ERROR

        # Match first part of error reason string
        self.assertStartsWith(
            result.status_reason,
            'error getting current firewall rules from API:')

        # Copy reason string into expected proto. The reason includes a long
        # error message, which would be ugly to replicate in the test.
        self.expected_proto.status_reason = result.status_reason

        self.validate_results(self.expected_proto, result)
    def get_groups_settings(self, group_email):
        """Get the group settings for a given group.


        https://developers.google.com/admin-sdk/groups-settings/v1/reference/groups/get

        Args:
            group_email (str): The gsuite group email to scope the request to.

        Returns:
            dict:group settings for given group_email.

        Raises:
            api_errors.ApiExecutionError: If groups retrieval fails.
            RefreshError: If the authentication fails.
        """
        try:
            result = self.repository.groups.get(group_email)
            LOGGER.debug('Getting group settings information for group id = %s,'
                         ' result = %s',
                         group_email, result)
            return result
        except RefreshError as e:
            # Authentication failed, log before raise.
            LOGGER.exception(GSUITE_AUTH_FAILURE_MESSAGE)
            raise e
        except (errors.HttpError, HttpLib2Error) as e:
            raise api_errors.ApiExecutionError('groups', e)
예제 #22
0
    def get_bucket_iam_policy(self, bucket, user_project=None):
        """Gets the IAM policy for a bucket.

        Args:
            bucket (str): The bucket to fetch the policy for.
            user_project (str): The user project to bill the bucket access to,
                for requester pays buckets.

        Returns:
            dict: The IAM policies for the bucket.

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails
        """
        try:
            kwargs = {}
            if user_project:
                kwargs['userProject'] = user_project
            results = self.repository.buckets.get_iam_policy(bucket, **kwargs)
            LOGGER.debug('Getting the IAM policy for a bucket, bucket = %s,'
                         ' results = %s', bucket, results)
            return results
        except (errors.HttpError, HttpLib2Error) as e:
            if not user_project and _user_project_missing_error(e):
                if self._user_project:
                    LOGGER.info('User project required for bucket %s, '
                                'retrying.', bucket)
                    return self.get_bucket_iam_policy(bucket,
                                                      self._user_project)

            api_exception = api_errors.ApiExecutionError(
                'bucketIamPolicy', e, 'bucket', bucket)
            LOGGER.exception(api_exception)
            raise api_exception
예제 #23
0
    def get_all_apis(self):
        """Gets all APIs that can be enabled (based on caller's permissions).

        Returns:
            list: A list of ManagedService resource dicts.
            https://cloud.google.com/service-management/reference/rest/v1/services#ManagedService

            {
              "serviceName": string,
              "producerProjectId": string,
            }
        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        try:
            paged_results = self.repository.services.list(
                max_results=self.DEFAULT_MAX_RESULTS)
            flattened_results = api_helpers.flatten_list_results(
                paged_results, 'services')
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError('', e)
            LOGGER.exception(api_exception)
            raise api_exception

        LOGGER.debug('Getting all visible APIs, flattened_results = %s',
                     flattened_results)
        return flattened_results
예제 #24
0
    def update_finding(self, finding, finding_id, source_id=None):
        """Updates a finding in CSCC.

        Args:
            finding (dict): Forseti violation in CSCC format.
            finding_id (str): id hash of the CSCC finding.
            source_id (str): Unique ID assigned by CSCC, to the organization
                that the violations are originating from.

        Returns:
            dict: An API response containing one page of results.
        """
        try:
            LOGGER.debug('Updating finding.')

            # patch() will set the state of outdated findings to INACTIVE
            response = self.repository.findings.patch(
                '{}/findings/{}'.format(source_id, finding_id),
                finding,
                updateMask='state,event_time')
            LOGGER.debug('Successfully updated finding in CSCC:\n%s', finding)
            return response
        except (errors.HttpError, HttpLib2Error) as e:
            LOGGER.exception('Unable to update CSCC finding: Resource: %s',
                             finding)
            violation_data = (
                finding.get('source_properties').get('violation_data'))
            raise api_errors.ApiExecutionError(violation_data, e)
예제 #25
0
    def get_full_api_configuration(self, service_name):
        """Gets the full Service Configuration associated with a service.

        Args:
            service_name (str): The service name to query.

        Returns:
            dict: A single Service resource dict.
            https://cloud.google.com/service-infrastructure/docs/service-management/reference/rest/v1/services.configs#Service

        Raises:
            ApiExecutionError: ApiExecutionError is raised if the call to the
                GCP API fails.
        """
        try:
            result = self.repository.services.get_config(service_name)
        except (errors.HttpError, HttpLib2Error) as e:
            api_exception = api_errors.ApiExecutionError(
                'serviceConfig', e, 'serviceName', service_name)
            LOGGER.exception(api_exception)
            raise api_exception

        LOGGER.debug(
            'Getting Service Config for a service, service_name = %s, '
            'result = %s', service_name, result)

        return result
예제 #26
0
    def get_operation(self, operation_name):
        """Get the Operations Status.

        Args:
            operation_name (str): The name of the operation to get.

        Returns:
            dict: Operation status and info.

        Raises:
            ApiExecutionError: Returns if there is an error in the API response.
            ValueError: Raised on invalid parent resource name.
        """
        if not (operation_name.startswith('folders/')
                or operation_name.startswith('organizations/')
                or operation_name.startswith('projects/')):
            raise ValueError('operation_name must start with folders/, '
                             'projects/, or organizations/')

        repository = self.repository.operations
        try:
            results = repository.get(operation_name)
            LOGGER.debug(
                'Getting the operation status, operation_name = %s, '
                'results = %s', operation_name, results)
        except (errors.HttpError, HttpLib2Error) as e:
            raise api_errors.ApiExecutionError(operation_name, e)

        return results
예제 #27
0
 def _mock_permission_denied(parentid):
     response = httplib2.Response({
         'status': '403',
         'content-type': 'application/json'
     })
     content = results.GCP_PERMISSION_DENIED_TEMPLATE.format(id=parentid).\
         encode()
     error_403 = errors.HttpError(response, content)
     raise api_errors.ApiExecutionError(parentid, error_403)
예제 #28
0
    def export_assets(self, parent, destination_object, content_type=None,
                      asset_types=None, blocking=False, timeout=0):
        """Export assets under a parent resource to the destination GCS object.

        Args:
            parent (str): The name of the parent resource to export assests
                under.
            destination_object (str): The GCS path and file name to store the
                results in. The bucket must be in the same project that has the
                Cloud Asset API enabled.
            content_type (str): The specific content type to export, currently
                supports "RESOURCE" and "IAM_POLICY". If not specified only the
                CAI metadata for assets are included.
            asset_types (list): The list of asset types to filter the results
                to, if not specified, exports all assets known to CAI.
            blocking (bool): If true, don't return until the async operation
                completes on the backend or timeout seconds pass.
            timeout (float): If greater than 0 and blocking is True, then raise
                an exception if timeout seconds pass before the operation
                completes.

        Returns:
            dict: Operation status and info.

        Raises:
            ApiExecutionError: Returns if there is an error in the API response.
            OperationTimeoutError: Raised if the operation times out.
            ValueError: Raised on invalid parent resource name.
        """
        if not (parent.startswith('folders/') or
                parent.startswith('organizations/') or
                parent.startswith('projects/')):
            raise ValueError('parent must start with folders/, projects/, or '
                             'organizations/')

        repository = self.repository.top_level
        try:
            results = repository.export_assets(
                parent, destination_object, content_type=content_type,
                asset_types=asset_types)
            if blocking:
                results = self.wait_for_completion(parent, results,
                                                   timeout=timeout)
        except (errors.HttpError, HttpLib2Error) as e:
            LOGGER.error('Error exporting assets for parent %s: %s', parent, e)
            raise api_errors.ApiExecutionError(parent, e)
        except api_errors.OperationTimeoutError as e:
            LOGGER.warn('Timeout exporting assets for parent %s: %s', parent, e)
            raise

        LOGGER.info('Exporting assets for parent %s. Result: %s',
                    parent, results)
        return results
예제 #29
0
    def test_enforce_policy_error_fetching_updated_rules(self, mock_logger):
        """Forces an error when requesting firewall rules after enforcement.

        Setup:
          * Create a new set of rules that is a copy of the expected rules.
            - Modify the first rule so it will have to be updated.

          * Set API call to return the current firewall rules on the first call,
            and an error on the second call.

        Expected Results:
          A ProjectResult proto showing status=ERROR, the correct reason string,
          the number of rules changed in an audit_log, and a copy of the
          previous firewall rules only.
        """
        # Make a deep copy of the expected rules
        current_fw_rules = copy.deepcopy(self.expected_rules)

        # Make a change to one of the rules
        current_fw_rules[0]['sourceRanges'].append('10.0.0.0/8')

        err = api_errors.ApiExecutionError(self.project, self.error_403)

        self.gce_api_client.get_firewall_rules.side_effect = [
            current_fw_rules,
            err,
            err,
        ]

        result = self.enforcer.enforce_firewall_policy(self.policy)

        self.expected_proto.status = project_enforcer.STATUS_ERROR
        updated = get_rule_names(current_fw_rules[:1])  # First rule updated
        self.set_expected_audit_log(updated=updated)

        # Match first part of error reason string
        self.assertStartsWith(
            result.status_reason,
            'error getting current firewall rules from API:')

        # Copy reason string into expected proto. The reason includes a long
        # error message, which would be ugly to replicate in the test.
        self.expected_proto.status_reason = result.status_reason

        # Verify rules after json is an empty string
        self.assertEqual(
            self.expected_proto.gce_firewall_enforcement.rules_after.json, '')

        self.validate_results(self.expected_proto,
                              result,
                              expect_rules_before=True,
                              expect_rules_after=False)
        self.assertTrue(mock_logger.error.called)
예제 #30
0
    def test_load_cloudasset_data_cai_apierror(self):
        """Validate load_cloud_asset handles an API error from CAI."""
        response = httplib2.Response(
            {'status': '403', 'content-type': 'application/json'})
        content = PERMISSION_DENIED.encode()
        error_403 = errors.HttpError(response, content)

        self.mock_export_assets.side_effect = (
            api_errors.ApiExecutionError('organizations/987654321', error_403)
        )
        results = cloudasset.load_cloudasset_data(self.session,
                                                  self.inventory_config)
        self.assertIsNone(results)
        self.assertFalse(self.mock_copy_file_from_gcs.called)
        self.validate_no_data_in_table()