Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions auth-api/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions auth-api/src/auth_api/resources/v1/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ def put_task(task_id):
task = TaskService(TaskModel.find_by_task_id(task_id))
if task:
# Update task and its relationships
origin = request.environ.get("HTTP_ORIGIN", "localhost")
task_dict = task.update_task(task_info=request_json, origin_url=origin).as_dict()
task_dict = task.update_task(task_info=request_json).as_dict()
# ProductService uses TaskService already. So, we need to avoid circular import.
if task_dict["relationship_type"] == TaskRelationshipType.PRODUCT.value:
ProductService.update_org_product_keycloak_groups(task_dict["account_id"])
Expand Down
1 change: 1 addition & 0 deletions auth-api/src/auth_api/services/membership.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ def send_notification_to_member(self, origin_url, notification_type):
"orgName": org_name,
"role": self._model.membership_type.code,
"label": self._model.membership_type.label,
"loginSource": self._model.user.login_source,
}
elif notification_type == NotificationType.MEMBERSHIP_APPROVED.value:
# TODO how to check properly if user is bceid user
Expand Down
48 changes: 40 additions & 8 deletions auth-api/src/auth_api/services/org.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ def create_org(org_info: dict, user_id):

if is_staff_review_needed:
Org._create_staff_review_task(org, UserModel.find_by_jwt_token())
else:
Org._send_account_created_notification(org, UserModel.find_by_jwt_token())

org.commit()

Expand Down Expand Up @@ -200,6 +202,30 @@ def _create_staff_review_task(org: OrgModel, user: UserModel):
TaskService.create_task(task_info=task_info, do_commit=False)
Org.send_staff_review_account_reminder(relationship_id=org.id)

@staticmethod
def _send_account_created_notification(org: OrgModel, user: UserModel):
"""Send account created notification to the user."""
current_app.logger.debug("<_send_account_created_notification")
app_url = current_app.config.get("WEB_APP_URL")
recipients = UserService.get_admin_emails_for_org(org.id)
login_source = user.login_source
if not recipients:
current_app.logger.warning(f"No recipient found for org {org.id}")
return

data = {
"accountId": org.id,
"orgName": org.name,
"emailAddresses": recipients,
"contextUrl": app_url,
"loginSource": login_source,
}
try:
publish_to_mailer(QueueMessageTypes.ACCOUNT_CREATED_NOTIFICATION.value, data=data)
current_app.logger.debug("_send_account_created_notification>")
except Exception as e: # noqa: B901
current_app.logger.warning(f"_send_account_created_notification failed: {e}")

@staticmethod
@user_context
def create_membership(org, user_id, **kwargs):
Expand Down Expand Up @@ -954,7 +980,7 @@ def change_org_status(self, status_code, suspension_reason_code):
return Org(org_model)

@staticmethod
def approve_or_reject(org_id: int, is_approved: bool, origin_url: str = None, task_action: str = None):
def approve_or_reject(org_id: int, is_approved: bool, task_action: str = None):
"""Mark the affidavit as approved or rejected."""
current_app.logger.debug("<find_affidavit_by_org_id ")
# Get the org and check what's the current status
Expand All @@ -981,10 +1007,10 @@ def approve_or_reject(org_id: int, is_approved: bool, origin_url: str = None, ta
admin_emails = UserService.get_admin_emails_for_org(org_id)
if admin_emails != "":
if org.access_type in (AccessType.EXTRA_PROVINCIAL.value, AccessType.REGULAR_BCEID.value):
Org.send_approved_rejected_notification(admin_emails, org.name, org.id, org.status_code, origin_url)
Org.send_approved_rejected_notification(admin_emails, org.name, org.id, org.status_code, user)
elif org.access_type in (AccessType.GOVM.value, AccessType.GOVN.value):
Org.send_approved_rejected_govm_govn_notification(
admin_emails, org.name, org.id, org.status_code, origin_url
admin_emails, org.name, org.id, org.status_code, user
)
else:
# continue but log error
Expand Down Expand Up @@ -1025,7 +1051,7 @@ def send_staff_review_account_reminder(relationship_id, task_relationship_type=T
raise BusinessException(Error.FAILED_NOTIFICATION, None) from e

@staticmethod
def send_approved_rejected_notification(receipt_admin_emails, org_name, org_id, org_status: OrgStatus, origin_url):
def send_approved_rejected_notification(receipt_admin_emails, org_name, org_id, org_status: OrgStatus, user: UserModel = None):
"""Send Approved/Rejected notification to the user."""
current_app.logger.debug("<send_approved_rejected_notification")

Expand All @@ -1035,8 +1061,14 @@ def send_approved_rejected_notification(receipt_admin_emails, org_name, org_id,
notification_type = QueueMessageTypes.NON_BCSC_ORG_REJECTED_NOTIFICATION.value
else:
return # Don't send mail for any other status change
app_url = f"{origin_url}/"
data = {"accountId": org_id, "emailAddresses": receipt_admin_emails, "contextUrl": app_url, "orgName": org_name}
app_url = current_app.config.get("WEB_APP_URL")
data = {
"accountId": org_id,
"emailAddresses": receipt_admin_emails,
"contextUrl": app_url,
"orgName": org_name,
"loginSource": user.login_source
}
try:
publish_to_mailer(notification_type, data=data)
current_app.logger.debug("<send_approved_rejected_notification")
Expand All @@ -1046,7 +1078,7 @@ def send_approved_rejected_notification(receipt_admin_emails, org_name, org_id,

@staticmethod
def send_approved_rejected_govm_govn_notification(
receipt_admin_email, org_name, org_id, org_status: OrgStatus, origin_url
receipt_admin_email, org_name, org_id, org_status: OrgStatus, origin_url, user: UserModel
):
"""Send Approved govm notification to the user."""
current_app.logger.debug("<send_approved_rejected_govm_govn_notification")
Expand All @@ -1058,7 +1090,7 @@ def send_approved_rejected_govm_govn_notification(
else:
return # Don't send mail for any other status change
app_url = f"{origin_url}/"
data = {"accountId": org_id, "emailAddresses": receipt_admin_email, "contextUrl": app_url, "orgName": org_name}
data = {"accountId": org_id, "emailAddresses": receipt_admin_email, "contextUrl": app_url, "orgName": org_name, "loginSource": user.login_source}
try:
publish_to_mailer(notification_type, data=data)
current_app.logger.debug("send_approved_rejected_govm_govn_notification>")
Expand Down
14 changes: 7 additions & 7 deletions auth-api/src/auth_api/services/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def close_task(task_id, remarks: [] = None, do_commit: bool = True):
if do_commit:
db.session.commit()

def update_task(self, task_info: dict = None, origin_url: str = None):
def update_task(self, task_info: dict = None):
"""Update a task record."""
current_app.logger.debug("<update_task ")
task_model: TaskModel = self._model
Expand All @@ -104,12 +104,12 @@ def update_task(self, task_info: dict = None, origin_url: str = None):
task_model.relationship_status = task_relationship_status
task_model.flush()

self._update_relationship(origin_url=origin_url)
self._update_relationship()
db.session.commit()
current_app.logger.debug(">update_task ")
return Task(task_model)

def _update_relationship(self, origin_url: str = None):
def _update_relationship(self):
"""Retrieve the relationship record and update the status."""
task_model: TaskModel = self._model
current_app.logger.debug("<update_task_relationship ")
Expand All @@ -121,7 +121,7 @@ def _update_relationship(self, origin_url: str = None):
org_id = task_model.relationship_id
if not is_hold:
self._update_org(
is_approved=is_approved, org_id=org_id, origin_url=origin_url, task_action=task_model.action
is_approved=is_approved, org_id=org_id, task_action=task_model.action
)
else:
# Task with ACCOUNT_REVIEW action cannot be put on hold
Expand Down Expand Up @@ -211,7 +211,7 @@ def _notify_admin_about_hold(
"applicationDate": f"{task_model.created.strftime('%m/%d/%Y')}",
"accountId": account_id,
"emailAddresses": admin_emails,
"contextUrl": f"{current_app.config.get('WEB_APP_URL')}"
"contextUrl": f"{current_app.config.get("WEB_APP_URL")}"
f"/{current_app.config.get('BCEID_SIGNIN_ROUTE')}/"
f"{create_account_signin_route}",
}
Expand All @@ -223,14 +223,14 @@ def _notify_admin_about_hold(
raise BusinessException(Error.FAILED_NOTIFICATION, None) from e

@staticmethod
def _update_org(is_approved: bool, org_id: int, origin_url: str = None, task_action: str = None):
def _update_org(is_approved: bool, org_id: int, task_action: str = None):
"""Approve/Reject Affidavit and Org."""
from auth_api.services import Org as OrgService # pylint:disable=cyclic-import, import-outside-toplevel

current_app.logger.debug("<update_task_org ")

OrgService.approve_or_reject(
org_id=org_id, is_approved=is_approved, origin_url=origin_url, task_action=task_action
org_id=org_id, is_approved=is_approved, task_action=task_action
)

current_app.logger.debug(">update_task_org ")
Expand Down
32 changes: 32 additions & 0 deletions auth-api/tests/unit/api/test_org.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import pytest
import pytz
from faker import Faker
from sbc_common_components.utils.enums import QueueMessageTypes

import auth_api.utils.account_mailer
from auth_api.exceptions import BusinessException
from auth_api.exceptions.errors import Error
from auth_api.models import Affidavit as AffidavitModel
Expand Down Expand Up @@ -81,6 +83,7 @@
patch_pay_account_delete_error,
patch_pay_account_fees,
patch_pay_account_post,
patch_pay_account_put,
)

FAKE = Faker()
Expand Down Expand Up @@ -854,6 +857,35 @@ def test_add_org_invalid_returns_exception(client, jwt, session): # pylint:disa
assert schema_utils.validate(rv.json, "exception")[0]


@pytest.mark.parametrize(
"route,org_data_factory",
[
("/api/v1/orgs", lambda: TestOrgInfo.org_govm), # GOVM: is_staff_review_needed=False
("/api/v2/orgs", lambda: {**TestOrgInfo.org_govm, "contact": TestContactInfo.contact1}),
],
)
@patch.object(UserService, "get_admin_emails_for_org", return_value="test@test.com")
@patch.object(auth_api.services.org, "publish_to_mailer")
def test_add_org_sends_account_created_notification(
mock_mailer, _mock_admin_emails, client, jwt, session, keycloak_mock, monkeypatch, route, org_data_factory
): # pylint:disable=unused-argument
"""Assert that POST org (V1 and V2) sends account created notification."""
patch_pay_account_post(monkeypatch)
patch_pay_account_put(monkeypatch)
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.staff_admin_role)
client.post("/api/v1/users", headers=headers, content_type="application/json")

org_data = org_data_factory()
rv = client.post(route, data=json.dumps(org_data), headers=headers, content_type="application/json")
assert rv.status_code == HTTPStatus.CREATED
mock_mailer.assert_called_once()
call_args = mock_mailer.call_args
assert call_args[0][0] == QueueMessageTypes.ACCOUNT_CREATED_NOTIFICATION.value
assert call_args[1]["data"]["emailAddresses"]
assert call_args[1]["data"]["accountId"] == rv.json["id"]
assert "orgName" in call_args[1]["data"]


def test_get_org(client, jwt, session, keycloak_mock): # pylint:disable=unused-argument
"""Assert that an org can be retrieved via GET."""
headers = factory_auth_header(jwt=jwt, claims=TestJwtClaims.public_user_role)
Expand Down
7 changes: 4 additions & 3 deletions auth-api/tests/unit/services/test_invitation.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,10 @@ def test_accept_invitation_for_govm(session, auth_mock, keycloak_mock, monkeypat
with patch.object(InvitationService, "send_invitation", return_value=None):
with patch.object(auth, "check_auth", return_value=True):
with patch.object(InvitationService, "notify_admin", return_value=None):
user_with_token = dict(TestUserInfo.user_staff_admin)
user_with_token["keycloak_guid"] = TestJwtClaims.public_user_role["sub"]
user = factory_user_model(user_with_token)
staff_creator_with_token = dict(TestUserInfo.user_staff_admin)
staff_creator_with_token["keycloak_guid"] = TestJwtClaims.staff_admin_role["sub"]
staff_creator_with_token["idp_userid"] = TestJwtClaims.staff_admin_role["idp_userid"]
user = factory_user_model(staff_creator_with_token)

patch_token_info(TestJwtClaims.staff_admin_role, monkeypatch)

Expand Down
7 changes: 5 additions & 2 deletions auth-api/tests/unit/services/test_invitation_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,11 @@ def test_change_authentication_non_govm(session, auth_mock, keycloak_mock, monke
@mock.patch("auth_api.services.affiliation_invitation.RestService.get_service_account_token", mock_token)
def test_invitation_govm(session, auth_mock, keycloak_mock, monkeypatch):
"""Assert that government ministry organization invites can be accepted by IDIR only."""
# Users setup
staff_user = factory_user_model(TestUserInfo.user_staff_admin)
# Users setup - staff_user must align with token (sub and idp_userid) for find_by_jwt_token
staff_creator_with_token = dict(TestUserInfo.user_staff_admin)
staff_creator_with_token["keycloak_guid"] = TestJwtClaims.staff_admin_role["sub"]
staff_creator_with_token["idp_userid"] = TestJwtClaims.staff_admin_role["idp_userid"]
staff_user = factory_user_model(staff_creator_with_token)
staff_invitee_user = factory_user_model(TestUserInfo.user1)
invitee_bcsc_user = factory_user_model(TestUserInfo.user2)
invitee_bceid_user = factory_user_model(TestUserInfo.user3)
Expand Down
5 changes: 4 additions & 1 deletion auth-api/tests/unit/services/test_org.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,10 @@ def test_create_basic_org_assert_pay_request_is_govm(session, keycloak_mock, sta
"""Assert that while org creation , pay-api gets called with proper data for basic accounts."""
user = factory_user_model()
token_info = TestJwtClaims.get_test_user(
sub=user.keycloak_guid, source=LoginSource.STAFF.value, roles=["create_accounts"]
sub=user.keycloak_guid,
source=LoginSource.STAFF.value,
roles=["create_accounts"],
idp_userid=user.idp_userid,
)
with patch.object(RestService, "post") as mock_post:
patch_token_info(token_info, monkeypatch)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Your BC Registries and Online Services account has been approved.

Account Number: {{ account_number }}
Account Name: {{ account_name_with_branch }}
Login Method: {{ login_source }}

[Log in to your account]({{ context_url }})

---

**BC Registries and Online Services**
Toll Free: 1-877-526-1526
Victoria Office: 250-387-7848
Email: [BCRegistries@gov.bc.ca](BCRegistries@gov.bc.ca)
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# Your BC Registries account **{{ account_number }}: {{ account_name_with_branch }}** has been approved.
# Your BC Registries and Online Services account has been approved.

BC Registries staff have approved your organization's BC Registries account.
Account Number: {{ account_number }}
Account Name: {{ account_name_with_branch }}
Login Method: IDIR

You are the account administrator.
[Log in to your account]({{ context_url }})

To activate your BC Registries account, click the link below and login to your account. After logging in, you can manage your team members in account settings.
---

[Access Account]({{ context_url }})
**BC Registries and Online Services**
Toll Free: 1-877-526-1526
Victoria Office: 250-387-7848
Email: [BCRegistries@gov.bc.ca](BCRegistries@gov.bc.ca)
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# Your BC Registries account **{{ account_number }}: {{ account_name_with_branch }}** has been rejected.
# Your BC Registries and Online Services account has been declined.

BC Registries staff have rejected your organization's BC Registries account.
BC Registries staff have reviewed and declined your account request. If you have questions, please contact us to discuss further actions.

You are the account administrator.
Account Name: {{ account_name_with_branch }}
Login Method: {{ login_source }}

Contact BC Registries staff at **[bconline@gov.bc.ca](bconline@gov.bc.ca)** to discuss further actions.
---

**BC Registries and Online Services**
Toll Free: 1-877-526-1526
Victoria Office: 250-387-7848
Email: [BCRegistries@gov.bc.ca](BCRegistries@gov.bc.ca)
Loading