Send email to PPOC when portfolio is provisioned

When a portfolio state machine transitions to the COMPLETED state, an
email is sent to the PPOC letting them know it's ready, and provides
them with their username needed to create a password.
This commit is contained in:
graham-dds 2020-02-12 13:54:24 -05:00
parent c8991a95bf
commit 8f52443b5d
5 changed files with 78 additions and 8 deletions

View File

@ -3,7 +3,7 @@
"files": "^.secrets.baseline$|^.*pgsslrootcert.yml$",
"lines": null
},
"generated_at": "2020-02-10T21:40:38Z",
"generated_at": "2020-02-12T18:51:01Z",
"plugins_used": [
{
"base64_limit": 4.5,
@ -82,7 +82,7 @@
"hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3",
"is_secret": false,
"is_verified": false,
"line_number": 43,
"line_number": 44,
"type": "Secret Keyword"
}
],

View File

@ -219,6 +219,7 @@ To generate coverage reports for the Javascript tests:
- `ASSETS_URL`: URL to host which serves static assets (such as a CDN).
- `AZURE_ACCOUNT_NAME`: The name for the Azure blob storage account
- `AZURE_LOGIN_URL`: The URL used to login for an Azure instance.
- `AZURE_STORAGE_KEY`: A valid secret key for the Azure blob storage account
- `AZURE_TO_BUCKET_NAME`: The Azure blob storage container name for task order uploads
- `BLOB_STORAGE_URL`: URL to Azure blob storage container.

View File

@ -18,6 +18,7 @@ from atst.domain.environments import Environments
from atst.domain.environment_roles import EnvironmentRoles
from atst.domain.portfolios import Portfolios
from atst.models import CSPRole, JobFailure
from atst.models.mixins.state_machines import FSMStates
from atst.domain.task_orders import TaskOrders
from atst.models.utils import claim_for_update, claim_many_for_update
from atst.queue import celery
@ -177,10 +178,30 @@ def do_work(fn, task, csp, **kwargs):
raise task.retry(exc=e)
def send_PPOC_email(portfolio_dict):
ppoc_email = portfolio_dict.get("password_recovery_email_address")
user_id = portfolio_dict.get("user_id")
domain_name = portfolio_dict.get("domain_name")
send_mail(
recipients=[ppoc_email],
subject=translate("email.portfolio_ready.subject"),
body=translate(
"email.portfolio_ready.body",
{
"password_reset_address": app.config.get("AZURE_LOGIN_URL"),
"username": f"{user_id}@{domain_name}.onmicrosoft.com",
},
),
)
def do_provision_portfolio(csp: CloudProviderInterface, portfolio_id=None):
portfolio = Portfolios.get_for_update(portfolio_id)
fsm = Portfolios.get_or_create_state_machine(portfolio)
fsm.trigger_next_transition(csp_data=portfolio.to_dictionary())
if fsm.current_state == FSMStates.COMPLETED:
send_PPOC_email(portfolio.to_dictionary())
@celery.task(bind=True, base=RecordFailure)

View File

@ -4,6 +4,7 @@ AZURE_AADP_QTY=5
AZURE_ACCOUNT_NAME
AZURE_CLIENT_ID
AZURE_GRAPH_RESOURCE="https://graph.microsoft.com/"
AZURE_LOGIN_URL="https://portal.azure.com/"
AZURE_POLICY_LOCATION=policies
AZURE_POWERSHELL_CLIENT_ID
AZURE_ROLE_DEF_ID_BILLING_READER="fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64"

View File

@ -7,8 +7,7 @@ from azure.core.exceptions import AzureError
from atst.domain.csp.cloud import MockCloudProvider
from atst.domain.csp.cloud.models import UserRoleCSPResult
from atst.domain.portfolios import Portfolios
from atst.models import ApplicationRoleStatus
from atst.models import ApplicationRoleStatus, Portfolio, FSMStates
from atst.jobs import (
RecordFailure,
@ -24,6 +23,7 @@ from atst.jobs import (
do_create_environment,
do_create_environment_role,
do_create_application,
send_PPOC_email,
)
from tests.factories import (
ApplicationFactory,
@ -281,10 +281,57 @@ def test_dispatch_provision_portfolio(csp, monkeypatch):
mock.delay.assert_called_once_with(portfolio_id=portfolio.id)
def test_do_provision_portfolio(csp, session, portfolio):
do_provision_portfolio(csp=csp, portfolio_id=portfolio.id)
session.refresh(portfolio)
assert portfolio.state_machine
class TestDoProvisionPortfolio:
def test_portfolio_has_state_machine(self, csp, session, portfolio):
do_provision_portfolio(csp=csp, portfolio_id=portfolio.id)
session.refresh(portfolio)
assert portfolio.state_machine
def test_sends_email_to_PPOC_on_completion(
self, monkeypatch, csp, portfolio: Portfolio
):
mock = Mock()
monkeypatch.setattr("atst.jobs.send_PPOC_email", mock)
csp._authorize.return_value = None
csp._maybe_raise.return_value = None
sm: PortfolioStateMachine = PortfolioStateMachineFactory.create(
portfolio=portfolio
)
# The stage before "COMPLETED"
sm.state = FSMStates.BILLING_OWNER_CREATED
do_provision_portfolio(csp=csp, portfolio_id=portfolio.id)
# send_PPOC_email was called
assert mock.assert_called_once
def test_send_ppoc_email(monkeypatch, app):
mock = Mock()
monkeypatch.setattr("atst.jobs.send_mail", mock)
ppoc_email = "example@example.com"
user_id = "user_id"
domain_name = "domain"
send_PPOC_email(
{
"password_recovery_email_address": ppoc_email,
"user_id": user_id,
"domain_name": domain_name,
}
)
mock.assert_called_once_with(
recipients=[ppoc_email],
subject=translate("email.portfolio_ready.subject"),
body=translate(
"email.portfolio_ready.body",
{
"password_reset_address": app.config.get("AZURE_LOGIN_URL"),
"username": f"{user_id}@{domain_name}.onmicrosoft.com",
},
),
)
def test_provision_portfolio_create_tenant(