Skip to content

Commit 17b78fe

Browse files
authored
Merge pull request #1208 from bcgov/freeze-mail
freeze/unfreeze mails added
2 parents 3bce8b7 + 9c5a969 commit 17b78fe

File tree

18 files changed

+1429
-24
lines changed

18 files changed

+1429
-24
lines changed

queue_services/account-mailer/q_cli.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,15 @@
2828
import sys
2929
from datetime import datetime
3030

31+
from entity_queue_common.service_utils import error_cb, logger, signal_handler
32+
33+
from account_mailer.enums import MessageType
34+
35+
3136
from nats.aio.client import Client as NATS # noqa N814; by convention the name is NATS
3237
from stan.aio.client import Client as STAN # noqa N814; by convention the name is STAN
3338

34-
from entity_queue_common.service_utils import error_cb, logger, signal_handler
39+
3540

3641

3742
async def run(loop, mode, auth_account_id, auth_account_name, auth_user_id, bank_number, bank_branch_number,
@@ -119,6 +124,30 @@ def subscription_options():
119124
'transactionId': transaction_id
120125
}
121126
}
127+
elif mode == 'acc_lock':
128+
payload = {
129+
'specversion': '1.x-wip',
130+
'type': f'{MessageType.NSF_UNLOCK_ACCOUNT.value}',
131+
'source': 'https://api.pay.bcregistry.gov.bc.ca/v1/invoices/{invoice.id}',
132+
'id': auth_account_id,
133+
'datacontenttype': 'application/json',
134+
'data': {
135+
'accountId': auth_account_id,
136+
'accountName': auth_account_name
137+
}
138+
}
139+
elif mode == 'acc_unlock':
140+
payload = {
141+
'specversion': '1.x-wip',
142+
'type': f'{MessageType.NSF_LOCK_ACCOUNT.value}',
143+
'source': 'https://api.pay.bcregistry.gov.bc.ca/v1/invoices/{invoice.id}',
144+
'id': auth_account_id,
145+
'datacontenttype': 'application/json',
146+
'data': {
147+
'accountId': auth_account_id,
148+
'accountName': auth_account_name
149+
}
150+
}
122151

123152
await sc.publish(subject=subscription_options().get('subject'),
124153
payload=json.dumps(payload).encode('utf-8'))
@@ -145,7 +174,7 @@ def subscription_options():
145174
print('q_cli.py -o <old_identifier> -n <new_identifier>')
146175
sys.exit()
147176
elif opt in ("-m", "--mode"):
148-
mode = arg # pad confirmation - "pad", refund request - "refund"
177+
mode = arg # pad confirmation - "pad", refund request - "refund", acc suspended - "acc_suspend", acc restored - "acc_restore"
149178
elif opt in ("-i", "--id"):
150179
auth_account_id = arg
151180
elif opt in ("-n", "--name"):
@@ -172,3 +201,5 @@ def subscription_options():
172201

173202
# pad cmd --> python3 q_cli.py -m pad -i 10 -n TestAccount -b 088 -t 00277 -a 12874890
174203
# refund cmd --> python3 q_cli.py -m refund -i 10 -o 67892 -p 25.33 -d 988
204+
# account suspended cmd --> python3 q_cli.py -m acc_lock -i 4 -n SomeAccount
205+
# account restored cmd --> python3 q_cli.py -m acc_unlock -i 4 -n SomeAccount
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright © 2019 Province of British Columbia
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Auth Utils.
15+
16+
Generic utils to help auth functions.
17+
"""
18+
19+
from auth_api.models import User as UserModel
20+
from auth_api.utils.enums import Status
21+
from flask import current_app
22+
23+
24+
def get_member_emails(org_id, roles):
25+
"""Get emails for the user role passed in."""
26+
member_list = UserModel.find_users_by_org_id_by_status_by_roles(org_id, roles, Status.ACTIVE.value)
27+
member_emails = ','.join([str(x.contacts[0].contact.email) for x in member_list if x.contacts])
28+
return member_emails
29+
30+
31+
def get_login_url():
32+
"""Get application login url."""
33+
origin = current_app.config.get('HTTP_ORIGIN')
34+
context_path = current_app.config.get('AUTH_WEB_TOKEN_CONFIRM_PATH')
35+
login_url = '{}/{}'.format(origin, context_path)
36+
return login_url

queue_services/account-mailer/src/account_mailer/config.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
from dotenv import find_dotenv, load_dotenv
2626

27-
2827
# this will load all the envars from a .env file located in the project root (api)
2928
load_dotenv(find_dotenv())
3029

@@ -116,6 +115,10 @@ class _Config(): # pylint: disable=too-few-public-methods
116115

117116
TEMPLATE_PATH = os.getenv('TEMPLATE_PATH', 'src/account_mailer/email_templates')
118117

118+
HTTP_ORIGIN = os.getenv('HTTP_ORIGIN', 'localhost')
119+
120+
AUTH_WEB_TOKEN_CONFIRM_PATH = os.getenv('AUTH_WEB_TOKEN_CONFIRM_PATH')
121+
119122
# JWT_OIDC Settings
120123
JWT_OIDC_ISSUER = os.getenv('JWT_OIDC_ISSUER')
121124

@@ -132,6 +135,9 @@ class _Config(): # pylint: disable=too-few-public-methods
132135
KEYCLOAK_SERVICE_ACCOUNT_ID = os.getenv('KEYCLOAK_SERVICE_ACCOUNT_ID')
133136
KEYCLOAK_SERVICE_ACCOUNT_SECRET = os.getenv('KEYCLOAK_SERVICE_ACCOUNT_SECRET')
134137

138+
# If any value is present in this flag, starts up a keycloak docker
139+
USE_TEST_KEYCLOAK_DOCKER = os.getenv('USE_TEST_KEYCLOAK_DOCKER', None)
140+
USE_DOCKER_MOCK = os.getenv('USE_DOCKER_MOCK', None)
135141

136142
class DevConfig(_Config): # pylint: disable=too-few-public-methods
137143
"""Creates the Development Config object."""
@@ -162,6 +168,11 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods
162168
)
163169

164170
STAN_CLUSTER_NAME = 'test-cluster'
171+
AUTH_WEB_TOKEN_CONFIRM_PATH = ''
172+
JWT_OIDC_ISSUER = os.getenv('JWT_OIDC_TEST_ISSUER')
173+
# Service account details
174+
KEYCLOAK_SERVICE_ACCOUNT_ID = os.getenv('KEYCLOAK_TEST_ADMIN_CLIENTID')
175+
KEYCLOAK_SERVICE_ACCOUNT_SECRET = os.getenv('KEYCLOAK_TEST_ADMIN_SECRET')
165176

166177

167178
class ProdConfig(_Config): # pylint: disable=too-few-public-methods
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright © 2019 Province of British Columbia
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""A Template for the account suspended email."""
15+
16+
from auth_api.models import Org as OrgModel
17+
from entity_queue_common.service_utils import logger
18+
from flask import current_app
19+
from jinja2 import Template
20+
21+
from account_mailer.auth_utils import get_login_url
22+
from account_mailer.email_processors import generate_template
23+
24+
25+
def process(org_id, recipients, template_name, subject) -> dict:
26+
"""Build the email for Account notification."""
27+
logger.debug('account notification: %s', org_id)
28+
29+
org: OrgModel = OrgModel.find_by_id(org_id)
30+
# fill in template
31+
filled_template = generate_template(current_app.config.get('TEMPLATE_PATH'), template_name, )
32+
33+
# render template with vars from email msg
34+
jnja_template = Template(filled_template, autoescape=True)
35+
html_out = jnja_template.render(
36+
account_name=org.name, url=get_login_url
37+
)
38+
return {
39+
'recipients': recipients,
40+
'content': {
41+
'subject': subject,
42+
'body': html_out,
43+
'attachments': []
44+
}
45+
}

queue_services/account-mailer/src/account_mailer/email_processors/refund_requested.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414
"""Process an email for a refund request."""
1515

16+
1617
from entity_queue_common.service_utils import logger
1718
from flask import current_app
1819
from jinja2 import Template
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<base href="/">
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<meta name="referrer" content="origin-when-cross-origin"/>
8+
<meta name="author" content="BC Registries and Online Services">
9+
<title>Account Restored Email Notification</title>
10+
[[style.html]]
11+
</head>
12+
<body>
13+
<div style="font-family: Verdana, Arial, sans-serif; font-size: 14px; padding: 16px;">
14+
<div style="margin-left: -6px;">
15+
[[logo.html]]
16+
</div>
17+
<h1 style="margin: 0; line-height: 34px; letter-spacing: -1px; font-size: 24px; font-weight: 700;">
18+
Your BC Registries and Online Services account has been reactivated.
19+
</h1>
20+
<p style="margin-top: 20px; margin-bottom: 0; line-height: 24px; font-size: 16px;">
21+
The following account has been reactivated and is now accessible.
22+
</p>
23+
<p style="margin-top: 20px; margin-bottom: 0; line-height: 24px; font-size: 16px; font-weight: 700;">
24+
{{ account_name }}
25+
</p>
26+
<p style="margin-top: 20px; margin-bottom: 0; line-height: 24px; font-size: 16px;">
27+
Log in to BC Registries and Online Services to access your account.
28+
</p>
29+
<div style="margin-top: 45px;">
30+
<!--[if mso]> <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="{{ url }}" style="height:55px; v-text-anchor:middle; width:250px;" arcsize="10%" stroke="f" fillcolor="#003366" alias="CTA1">Join Team<w:anchorlock/> <center> <![endif]-->
31+
<a style="display: block; width: 250px; padding: 15px 0; color: #ffffff; border: none; border-radius: 6px; background-color: #003366; text-align: center; text-decoration: none; font-size: 16px; font-weight: bold;"
32+
href="{{ url }}">Log in
33+
</a>
34+
<!--[if mso]> </center> </v:roundrect> <![endif]-->
35+
</div>
36+
</div>
37+
</body>
38+
</html>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<base href="/">
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<meta name="referrer" content="origin-when-cross-origin"/>
8+
<meta name="author" content="BC Registries and Online Services">
9+
<title>Account Suspended Email Notification</title>
10+
[[style.html]]
11+
</head>
12+
<body>
13+
<div style="font-family: Verdana, Arial, sans-serif; font-size: 14px; padding: 16px;">
14+
<div style="margin-left: -6px;">
15+
[[logo.html]]
16+
</div>
17+
<h1 style="margin: 0; line-height: 34px; letter-spacing: -1px; font-size: 24px; font-weight: 700;">
18+
Your BC Registries and Online Services account has been suspended.
19+
</h1>
20+
<p style="margin-top: 20px; margin-bottom: 0; line-height: 24px; font-size: 16px;">
21+
The following account has been suspended due to a failed payment.
22+
</p>
23+
<p style="margin-top: 20px; margin-bottom: 0; line-height: 24px; font-size: 16px; font-weight: 700;">
24+
{{ account_name }}
25+
</p>
26+
<p style="margin-top: 20px; margin-bottom: 0; line-height: 24px; font-size: 16px;">
27+
Log in to BC Registries and Online Services to reactivate your account.
28+
</p>
29+
<div style="margin-top: 45px;">
30+
<!--[if mso]> <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="{{ url }}" style="height:55px; v-text-anchor:middle; width:250px;" arcsize="10%" stroke="f" fillcolor="#003366" alias="CTA1">Join Team<w:anchorlock/> <center> <![endif]-->
31+
<a style="display: block; width: 250px; padding: 15px 0; color: #ffffff; border: none; border-radius: 6px; background-color: #003366; text-align: center; text-decoration: none; font-size: 16px; font-weight: bold;"
32+
href="{{ url }}">Log in
33+
</a>
34+
<!--[if mso]> </center> </v:roundrect> <![endif]-->
35+
</div>
36+
</div>
37+
</body>
38+
</html>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright © 2019 Province of British Columbia
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the 'License');
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an 'AS IS' BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Enum definitions."""
15+
from enum import Enum
16+
17+
18+
class MessageType(Enum):
19+
"""Event Types."""
20+
21+
REFUND_REQUEST = 'bc.registry.payment.refundRequest'
22+
PAD_ACCOUNT_CREATE = 'bc.registry.payment.padAccountCreate'
23+
NSF_LOCK_ACCOUNT = 'bc.registry.payment.lockAccount'
24+
NSF_UNLOCK_ACCOUNT = 'bc.registry.payment.unlockAccount'
25+
26+
27+
class SubjectType(Enum):
28+
"""Event Types."""
29+
30+
NSF_LOCK_ACCOUNT_SUBJECT = 'Your account has been suspended.'
31+
NSF_UNLOCK_ACCOUNT_SUBJECT = 'Your account has been reactivated.'
32+
33+
34+
class TemplateType(Enum):
35+
"""Template Types."""
36+
37+
NSF_LOCK_ACCOUNT_TEMPLATE_NAME = 'account_suspended_email'
38+
NSF_UNLOCK_ACCOUNT_TEMPLATE_NAME = 'account_restored_email'

queue_services/account-mailer/src/account_mailer/worker.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@
3131
import nats
3232
from auth_api.models import db
3333
from auth_api.services.rest_service import RestService
34+
from auth_api.utils.roles import ADMIN, COORDINATOR
3435
from entity_queue_common.service import QueueServiceManager
3536
from entity_queue_common.service_utils import QueueException, logger
3637
from flask import Flask # pylint: disable=wrong-import-order
3738

3839
from account_mailer import config # pylint: disable=wrong-import-order
40+
from account_mailer.auth_utils import get_member_emails
41+
from account_mailer.email_processors import common_mailer # pylint: disable=wrong-import-order
3942
from account_mailer.email_processors import pad_confirmation # pylint: disable=wrong-import-order
4043
from account_mailer.email_processors import payment_completed # pylint: disable=wrong-import-order
4144
from account_mailer.email_processors import refund_requested # pylint: disable=wrong-import-order
45+
from account_mailer.enums import MessageType, SubjectType, TemplateType
4246
from account_mailer.services import notification_service # pylint: disable=wrong-import-order
4347

4448

@@ -62,12 +66,26 @@ async def process_event(event_message: dict, flask_app):
6266
if message_type == 'account.mailer':
6367
email_msg = json.loads(event_message.get('data'))
6468
email_dict = payment_completed.process(email_msg)
65-
elif message_type == 'bc.registry.payment.refundRequest':
69+
elif message_type == MessageType.REFUND_REQUEST.value:
6670
email_msg = event_message.get('data')
6771
email_dict = refund_requested.process(email_msg)
68-
elif event_message.get('type', None) == 'bc.registry.payment.padAccountCreate':
72+
elif message_type == MessageType.PAD_ACCOUNT_CREATE.value:
6973
email_msg = event_message.get('data')
7074
email_dict = pad_confirmation.process(email_msg, token)
75+
elif message_type == MessageType.NSF_LOCK_ACCOUNT.value:
76+
email_msg = event_message.get('data')
77+
template_name = TemplateType.NSF_LOCK_ACCOUNT_TEMPLATE_NAME.value
78+
org_id = email_msg.get('accountId')
79+
admin_coordinator_emails = get_member_emails(org_id, (ADMIN, COORDINATOR))
80+
subject = SubjectType.NSF_LOCK_ACCOUNT_SUBJECT.value
81+
email_dict = common_mailer.process(org_id, admin_coordinator_emails, template_name, subject)
82+
elif message_type == MessageType.NSF_UNLOCK_ACCOUNT.value:
83+
email_msg = event_message.get('data')
84+
template_name = TemplateType.NSF_UNLOCK_ACCOUNT_TEMPLATE_NAME.value
85+
org_id = email_msg.get('accountId')
86+
admin_coordinator_emails = get_member_emails(org_id, (ADMIN, COORDINATOR))
87+
subject = SubjectType.NSF_UNLOCK_ACCOUNT_SUBJECT.value
88+
email_dict = common_mailer.process(org_id, admin_coordinator_emails, template_name, subject)
7189

7290
logger.debug('Extracted email msg: %s', email_dict)
7391
process_email(email_dict, FLASK_APP, token)

0 commit comments

Comments
 (0)