From 3bfb6c9621901672be05c5e0fe9a150a28c6b6b9 Mon Sep 17 00:00:00 2001 From: dandds Date: Wed, 18 Dec 2019 17:31:04 -0500 Subject: [PATCH] Basic implementation for a policy wrapper. The implementation here is meant to wrap a library of JSON policy documents. Policies should be added to directories corresponding to where they will be defined (portfolio, application, environment). Functionality for parsing portfolio policy definitions is included. When the policies need to be defined on a management group, the AzureCloudProvider can iterate the appropriate tier of the policy manager and add those definitions. --- .secrets.baseline | 4 +- atst/domain/csp/cloud.py | 3 ++ atst/domain/csp/policy.py | 43 +++++++++++++++++-- config/base.ini | 1 + .../portfolios/allowed-resource-types.json | 2 +- policies/portfolios/region-restriction.json | 2 +- tests/domain/cloud/test_policy.py | 8 ++++ tests/mock_azure.py | 1 + 8 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 tests/domain/cloud/test_policy.py diff --git a/.secrets.baseline b/.secrets.baseline index ffa18c6e..857d311a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$|^.*pgsslrootcert.yml$", "lines": null }, - "generated_at": "2019-12-18T15:29:41Z", + "generated_at": "2019-12-18T22:26:52Z", "plugins_used": [ { "base64_limit": 4.5, @@ -98,7 +98,7 @@ "hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3", "is_secret": false, "is_verified": false, - "line_number": 29, + "line_number": 30, "type": "Secret Keyword" } ], diff --git a/atst/domain/csp/cloud.py b/atst/domain/csp/cloud.py index adb2260f..36c91c95 100644 --- a/atst/domain/csp/cloud.py +++ b/atst/domain/csp/cloud.py @@ -6,6 +6,7 @@ from atst.models.user import User from atst.models.application import Application from atst.models.environment import Environment from atst.models.environment_role import EnvironmentRole +from .policy import AzurePolicyManager class GeneralCSPException(Exception): @@ -429,6 +430,8 @@ class AzureCloudProvider(CloudProviderInterface): else: self.sdk = azure_sdk_provider + self.policy_manager = AzurePolicyManager(config["AZURE_POLICY_LOCATION"]) + def create_environment( self, auth_credentials: Dict, user: User, environment: Environment ): diff --git a/atst/domain/csp/policy.py b/atst/domain/csp/policy.py index 43b4e232..cfff9aae 100644 --- a/atst/domain/csp/policy.py +++ b/atst/domain/csp/policy.py @@ -1,12 +1,47 @@ +from glob import glob +import json +from dataclasses import dataclass +from os.path import join as path_join + + class AzurePolicyManager: def __init__(self, static_policy_location): self._static_policy_location = static_policy_location - def portfolio_definitions(): + @property + def portfolio_definitions(self): + if getattr(self, "_portfolio_definitions", None) is None: + portfolio_files = self._glob_json("portfolios") + self._portfolio_definitions = self._load_policies(portfolio_files) + + return self._portfolio_definitions + + @property + def application_definitions(self): pass - def application_definitions(): + @property + def environment_definitions(self): pass - def environment_definitions(): - pass + def _glob_json(self, path): + return glob(path_join(self._static_policy_location, "portfolios", "*.json")) + + def _load_policies(self, json_policies): + return [self._load_policy(pol) for pol in json_policies] + + def _load_policy(self, policy_file): + with open(policy_file, "r") as file_: + doc = json.loads(file_.read()) + return AzurePolicy( + definition_point=doc["definitionPoint"], + definition=doc["policyDefinition"], + parameters=doc["parameters"], + ) + + +@dataclass +class AzurePolicy: + definition_point: str + definition: dict + parameters: dict diff --git a/config/base.ini b/config/base.ini index 2cc8fd93..0feba51f 100644 --- a/config/base.ini +++ b/config/base.ini @@ -3,6 +3,7 @@ ASSETS_URL AZURE_ACCOUNT_NAME AZURE_STORAGE_KEY AZURE_TO_BUCKET_NAME +AZURE_POLICY_LOCATION=policies BLOB_STORAGE_URL=http://localhost:8000/ CAC_URL = http://localhost:8000/login-redirect CA_CHAIN = ssl/server-certs/ca-chain.pem diff --git a/policies/portfolios/allowed-resource-types.json b/policies/portfolios/allowed-resource-types.json index 7e4dcb7f..1358a9f4 100644 --- a/policies/portfolios/allowed-resource-types.json +++ b/policies/portfolios/allowed-resource-types.json @@ -1,5 +1,5 @@ { - "definitionPoint": "portfolio-parent", + "definitionPoint": "portfolio", "policyDefinition": { "properties": { "displayName": "Allowed resource types", diff --git a/policies/portfolios/region-restriction.json b/policies/portfolios/region-restriction.json index 9a7f57ba..a828c40e 100644 --- a/policies/portfolios/region-restriction.json +++ b/policies/portfolios/region-restriction.json @@ -1,5 +1,5 @@ { - "definitionPoint": "portfolio-parent", + "definitionPoint": "portfolio", "policyDefinition": { "properties": { "displayName": "Custom - Region Restriction", diff --git a/tests/domain/cloud/test_policy.py b/tests/domain/cloud/test_policy.py new file mode 100644 index 00000000..c0189262 --- /dev/null +++ b/tests/domain/cloud/test_policy.py @@ -0,0 +1,8 @@ +from atst.domain.csp.policy import AzurePolicyManager, AzurePolicy + + +def test_portfolio_definitions(): + manager = AzurePolicyManager("policies") + assert len(manager.portfolio_definitions) > 0 + policy = manager.portfolio_definitions[0] + assert isinstance(policy, AzurePolicy) diff --git a/tests/mock_azure.py b/tests/mock_azure.py index b8391811..417e69fb 100644 --- a/tests/mock_azure.py +++ b/tests/mock_azure.py @@ -7,6 +7,7 @@ AZURE_CONFIG = { "AZURE_CLIENT_ID": "MOCK", "AZURE_SECRET_KEY": "MOCK", "AZURE_TENANT_ID": "MOCK", + "AZURE_POLICY_LOCATION": "policies", } AUTH_CREDENTIALS = {