Sample create tenant itegration

This integration works with the happy path, we'll need to expand some fields and handle error states more coherently.
This commit is contained in:
tomdds 2020-01-09 17:29:34 -05:00
parent ba47053a1c
commit 2ac333e0b7
5 changed files with 111 additions and 13 deletions

View File

@ -33,6 +33,8 @@ azure-mgmt-authorization = "*"
azure-mgmt-managementgroups = "*" azure-mgmt-managementgroups = "*"
azure-mgmt-resource = "*" azure-mgmt-resource = "*"
transitions = "*" transitions = "*"
azure-mgmt-consumption = "*"
adal = "*"
[dev-packages] [dev-packages]
bandit = "*" bandit = "*"

11
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "63b8f9d203f306a6f0ff20514b024909aa7e64917e1befcc9ea79931b5b4bd34" "sha256": "a127b88e6c64842786f1868cb93bb1cdc828aa78040ea8ba4079bb3de0316dab"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -21,6 +21,7 @@
"sha256:5a7f1e037c6290c6d7609cab33a9e5e988c2fbec5c51d1c4c649ee3faff37eaf", "sha256:5a7f1e037c6290c6d7609cab33a9e5e988c2fbec5c51d1c4c649ee3faff37eaf",
"sha256:fd17e5661f60634ddf96a569b95d34ccb8a98de60593d729c28bdcfe360eaad1" "sha256:fd17e5661f60634ddf96a569b95d34ccb8a98de60593d729c28bdcfe360eaad1"
], ],
"index": "pypi",
"version": "==1.2.2" "version": "==1.2.2"
}, },
"alembic": { "alembic": {
@ -60,6 +61,14 @@
"index": "pypi", "index": "pypi",
"version": "==0.60.0" "version": "==0.60.0"
}, },
"azure-mgmt-consumption": {
"hashes": [
"sha256:035d4b74ca7c47e2683bea17105fd9014c27060336fb6255324ac86b27f70f5b",
"sha256:af319ad6e3ec162a7578563f149e3cdd7d833a62ec80761cfd93caf79467610b"
],
"index": "pypi",
"version": "==3.0.0"
},
"azure-mgmt-managementgroups": { "azure-mgmt-managementgroups": {
"hashes": [ "hashes": [
"sha256:3d5237947458dc94b4a392141174b1c1258d26611241ee104e9006d1d798f682", "sha256:3d5237947458dc94b4a392141174b1c1258d26611241ee104e9006d1d798f682",

View File

@ -156,12 +156,31 @@ class TenantCSPPayload(BaseCSPPayload):
country_code: str country_code: str
password_recovery_email_address: str password_recovery_email_address: str
class Config:
fields = {
"user_id": "userId",
"domain_name": "domainName",
"first_name": "firstName",
"last_name": "lastName",
"country_code": "countryCode",
"password_recovery_email_address": "passwordRecoveryEmailAddress",
}
allow_population_by_field_name = True
class TenantCSPResult(BaseModel): class TenantCSPResult(BaseModel):
user_id: str user_id: str
tenant_id: str tenant_id: str
user_object_id: str user_object_id: str
class Config:
allow_population_by_field_name = True
fields = {
"user_id": "userId",
"tenant_id": "tenantId",
"user_object_id": "objectId",
}
class BillingProfileAddress(BaseModel): class BillingProfileAddress(BaseModel):
address: Dict address: Dict
@ -558,11 +577,15 @@ class AzureSDKProvider(object):
import azure.graphrbac as graphrbac import azure.graphrbac as graphrbac
import azure.common.credentials as credentials import azure.common.credentials as credentials
from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD
import adal
import requests
self.subscription = subscription self.subscription = subscription
self.authorization = authorization self.authorization = authorization
self.adal = adal
self.graphrbac = graphrbac self.graphrbac = graphrbac
self.credentials = credentials self.credentials = credentials
self.requests = requests
# may change to a JEDI cloud # may change to a JEDI cloud
self.cloud = AZURE_PUBLIC_CLOUD self.cloud = AZURE_PUBLIC_CLOUD
@ -657,20 +680,31 @@ class AzureCloudProvider(CloudProviderInterface):
"role_name": role_assignment_id, "role_name": role_assignment_id,
} }
def create_tenant(self, payload): def create_tenant(self, payload: TenantCSPPayload):
# auth as SP that is allowed to create tenant? (tenant creation sp creds) sp_token = self._get_sp_token(payload.creds)
# create tenant with owner details (populated from portfolio point of contact, pw is generated) if sp_token is None:
raise AuthenticationException("Could not resolve token for tenant creation")
# return tenant id, tenant owner id and tenant owner object id from: create_tenant_body = payload.dict(by_alias=True)
response = {"tenantId": "string", "userId": "string", "objectId": "string"}
return self._ok( print(create_tenant_body)
{
"tenant_id": response["tenantId"], create_tenant_headers = {
"user_id": response["userId"], "Content-Type": "application/json",
"user_object_id": response["objectId"], "Authorization": f"Bearer {sp_token}",
} }
result = self.sdk.requests.post(
"https://management.azure.com/providers/Microsoft.SignUp/createTenant?api-version=2020-01-01-preview",
json=create_tenant_body,
headers=create_tenant_headers,
) )
if result.status_code == 200:
return self._ok(TenantCSPResult(**result.json()))
else:
return self._error(result.json())
def create_billing_owner(self, creds, tenant_admin_details): def create_billing_owner(self, creds, tenant_admin_details):
# authenticate as tenant_admin # authenticate as tenant_admin
# create billing owner identity # create billing owner identity
@ -838,6 +872,26 @@ class AzureCloudProvider(CloudProviderInterface):
if sub_id_match: if sub_id_match:
return sub_id_match.group(1) return sub_id_match.group(1)
def _get_sp_token(self, creds):
home_tenant_id = creds.get("home_tenant_id")
client_id = creds.get("client_id")
secret_key = creds.get("secret_key")
# TODO: Make endpoints consts or configs
authentication_endpoint = "https://login.microsoftonline.com/"
resource = "https://management.azure.com/"
context = self.sdk.adal.AuthenticationContext(
authentication_endpoint + home_tenant_id
)
# TODO: handle failure states here
token_response = context.acquire_token_with_client_credentials(
resource, client_id, secret_key
)
return token_response.get("accessToken", None)
def _get_credential_obj(self, creds, resource=None): def _get_credential_obj(self, creds, resource=None):
return self.sdk.credentials.ServicePrincipalCredentials( return self.sdk.credentials.ServicePrincipalCredentials(

View File

@ -3,7 +3,7 @@ from unittest.mock import Mock
from uuid import uuid4 from uuid import uuid4
from atst.domain.csp.cloud import AzureCloudProvider from atst.domain.csp.cloud import AzureCloudProvider, TenantCSPResult
from tests.mock_azure import mock_azure, AUTH_CREDENTIALS from tests.mock_azure import mock_azure, AUTH_CREDENTIALS
from tests.factories import EnvironmentFactory, ApplicationFactory from tests.factories import EnvironmentFactory, ApplicationFactory
@ -121,3 +121,22 @@ def test_create_policy_definition_succeeds(mock_azure: AzureCloudProvider):
policy_definition_name=properties.get("displayName"), policy_definition_name=properties.get("displayName"),
parameters=mock_policy_definition, parameters=mock_policy_definition,
) )
def test_create_tenant(mock_azure: AzureCloudProvider):
mock_azure.sdk.adal.AuthenticationContext.return_value.context.acquire_token_with_client_credentials.return_value = {
"accessToken": "TOKEN"
}
mock_result = Mock()
mock_result.json.return_value = {
"objectId": "0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d",
"tenantId": "60ff9d34-82bf-4f21-b565-308ef0533435",
"userId": "1153801116406515559",
}
mock_result.status_code = 200
mock_azure.sdk.requests.post.return_value = mock_result
result = mock_azure.create_tenant(None, suffix=2)
print(result)
body: TenantCSPResult = result.get("body")
assert body.tenant_id == "60ff9d34-82bf-4f21-b565-308ef0533435"

View File

@ -53,16 +53,30 @@ def mock_policy():
return Mock(spec=policy) return Mock(spec=policy)
def mock_adal():
import adal
return Mock(spec=adal)
def mock_requests():
import requests
return Mock(spec=requests)
class MockAzureSDK(object): class MockAzureSDK(object):
def __init__(self): def __init__(self):
from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD
self.subscription = mock_subscription() self.subscription = mock_subscription()
self.authorization = mock_authorization() self.authorization = mock_authorization()
self.adal = mock_adal()
self.managementgroups = mock_managementgroups() self.managementgroups = mock_managementgroups()
self.graphrbac = mock_graphrbac() self.graphrbac = mock_graphrbac()
self.credentials = mock_credentials() self.credentials = mock_credentials()
self.policy = mock_policy() self.policy = mock_policy()
self.requests = mock_requests()
# may change to a JEDI cloud # may change to a JEDI cloud
self.cloud = AZURE_PUBLIC_CLOUD self.cloud = AZURE_PUBLIC_CLOUD