Source code for azureenergylabelerlib.datamodels

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# File: datamodels.py
#
# Copyright 2022 Sayantan Khanra
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to
#  deal in the Software without restriction, including without limitation the
#  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
#  sell copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#  DEALINGS IN THE SOFTWARE.
#

"""
Main code for datamodels.

.. _Google Python Style Guide:
   https://google.github.io/styleguide/pyguide.html

"""

import logging
import json

__author__ = '''Sayantan Khanra <skhanra@schubergphilis.com>'''
__docformat__ = '''google'''
__date__ = '''22-04-2022'''
__copyright__ = '''Copyright 2022, Sayantan Khanra'''
__credits__ = ["Sayantan Khanra"]
__license__ = '''MIT'''
__maintainer__ = '''Sayantan Khanra'''
__email__ = '''<skhanra@schubergphilis.com>'''
__status__ = '''Development'''  # "Prototype", "Development", "Production".

# This is the main prefix used for logging
LOGGER_BASENAME = '''datamodels'''
LOGGER = logging.getLogger(LOGGER_BASENAME)
LOGGER.addHandler(logging.NullHandler())


[docs]class TenantEnergyLabelingData: """Models the data for energy labeling to export.""" def __init__(self, # pylint: disable= too-many-arguments filename, id, # pylint: disable= redefined-builtin energy_label, labeled_subscriptions, defender_for_cloud_findings): self.filename = filename self._id = id self._energy_label = energy_label self._labeled_subscriptions = labeled_subscriptions self._defender_for_cloud_findings = defender_for_cloud_findings @property def json(self): """Data to json.""" subscription_metrics = [] for subscription in self._labeled_subscriptions: energy_label = subscription.get_energy_label(self._defender_for_cloud_findings) subscription_metrics.append({ 'Subscription ID': subscription.subscription_id, 'Subscription Display Name': subscription.display_name, 'Number of high findings': energy_label.number_of_high_findings, 'Number of medium findings': energy_label.number_of_medium_findings, 'Number of low findings': energy_label.number_of_low_findings, 'Number of exempted findings': len(subscription.exempted_policies), 'Number of maximum days open': energy_label.max_days_open, 'Energy Label': energy_label.label }) return json.dumps( [{ 'Tenant ID': self._id, 'Tenant Energy Label': self._energy_label, 'Labeled subscriptions': subscription_metrics }], indent=2, default=str)
[docs]class DefenderForCloudFindingsData: """Models the data for energy labeling to export.""" def __init__(self, filename, defender_for_cloud_findings): self.filename = filename self._defender_for_cloud_findings = defender_for_cloud_findings @property def json(self): """Data to json.""" return json.dumps([{'Compliance Standard ID': finding.compliance_standard_id, 'Compliance Control ID': finding.compliance_control_id, 'Compliance State': finding.compliance_state, 'Subscription ID': finding.subscription_id, 'Resource Group': finding.resource_group, 'Resource Type': finding.resource_type, 'Resource Name': finding.resource_name, 'Resource ID': finding.resource_id, 'Severity': finding.severity, 'State': finding.state, 'Recommendation ID': finding.recommendation_id, 'Recommendation Name': finding.recommendation_name, 'Recommendation Display Name': finding.recommendation_display_name, 'Description': finding.description, 'Remediation Steps': finding.remediation_steps, 'Azure Portal Recommendation Link': finding.azure_portal_recommendation_link, 'Control Name': finding.control_name, 'Days Open': finding.days_open } for finding in self._defender_for_cloud_findings if not finding.is_skipped], indent=2, default=str)
[docs]class SubscriptionExemptedPolicies: """Models the data for exempted policies to export.""" def __init__(self, filename, labeled_subscriptions): self.filename = filename self._labeled_subscriptions = labeled_subscriptions @property def data(self): """Data of an subscription exempted policies to export.""" exempted_policies = [] for subscription in self._labeled_subscriptions: for exempted_policy in subscription.exempted_policies: exempted_policies.append({'Subscription ID': subscription.subscription_id, 'Created At': exempted_policy.system_data.created_at, 'Created By': exempted_policy.system_data.created_by, 'Description': exempted_policy.description, 'Display Name': exempted_policy.display_name, 'Exemption Category': exempted_policy.exemption_category, 'Last Modified By': exempted_policy.system_data.last_modified_by, 'Last Modified At': exempted_policy.system_data.last_modified_at, 'Name': exempted_policy.name, 'Expires On': exempted_policy.expires_on }) return exempted_policies @property def json(self): """Data to json.""" return json.dumps(self.data, indent=2, default=str)
[docs]class LabeledSubscriptionData: """Models the data for energy labeling to export.""" def __init__(self, filename, labeled_subscription, defender_for_cloud_findings): self.filename = filename self._labeled_subscription = labeled_subscription self._defender_for_cloud_findings = defender_for_cloud_findings @property def data(self): """Data of an subscription to export.""" energy_label = self._labeled_subscription.get_energy_label(self._defender_for_cloud_findings) return {'Subscription ID': self._labeled_subscription.subscription_id, 'Subscription Display Name': self._labeled_subscription.display_name, 'Number of high findings': energy_label.number_of_high_findings, 'Number of medium findings': energy_label.number_of_medium_findings, 'Number of low findings': energy_label.number_of_low_findings, 'Number of exempted findings': len(self._labeled_subscription.exempted_policies), 'Number of maximum days open': energy_label.max_days_open, 'Energy Label': energy_label.label} @property def json(self): """Data to json.""" return json.dumps(self.data, indent=2, default=str)
[docs]class LabeledResourceGroupData: """Models the data for energy labeling to export.""" def __init__(self, filename, labeled_resource_group_data, defender_for_cloud_findings): self.filename = filename self._subscription_id = labeled_resource_group_data.get('subscription_id') self._labeled_resource_group = labeled_resource_group_data.get('labeled_resource_group') self._defender_for_cloud_findings = defender_for_cloud_findings @property def data(self): """Data of an subscription to export.""" energy_label = self._labeled_resource_group.get_energy_label(self._defender_for_cloud_findings) return {'Subscription ID': self._subscription_id, 'ResourceGroup Name': self._labeled_resource_group.name, 'Number of high findings': energy_label.number_of_high_findings, 'Number of medium findings': energy_label.number_of_medium_findings, 'Number of low findings': energy_label.number_of_low_findings, 'Number of maximum days open': energy_label.max_days_open, 'Energy Label': energy_label.label} @property def json(self): """Data to json.""" return json.dumps(self.data, indent=2, default=str)
[docs]class LabeledResourceGroupsData: """Models the data for energy labeling to export.""" def __init__(self, filename, labeled_subscriptions, defender_for_cloud_findings): self.filename = filename self._labeled_subscriptions = labeled_subscriptions self._defender_for_cloud_findings = defender_for_cloud_findings @property def json(self): """Data to json.""" labeled_resource_groups = [] for subscription in self._labeled_subscriptions: for resource_group in subscription.resource_groups: labeled_resource_groups.append({ 'subscription_id': subscription.subscription_id, 'labeled_resource_group': resource_group }) return json.dumps([LabeledResourceGroupData(self.filename, resource_group, self._defender_for_cloud_findings).data for resource_group in labeled_resource_groups], indent=2, default=str)
[docs]class LabeledSubscriptionsData: """Models the data for energy labeling to export.""" def __init__(self, filename, labeled_subscriptions, defender_for_cloud_findings): self.filename = filename self._labeled_subscriptions = labeled_subscriptions self.defender_for_cloud_findings = defender_for_cloud_findings @property def json(self): """Data to json.""" return json.dumps([LabeledSubscriptionData(self.filename, subscription, self.defender_for_cloud_findings).data for subscription in self._labeled_subscriptions], indent=2, default=str)