Passed
Push — main ( 5a1833...1f94b4 )
by Jochen
05:05
created

byceps.services.user.email_address_verification_service   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Test Coverage

Coverage 82.5%

Importance

Changes 0
Metric Value
eloc 142
dl 0
loc 239
ccs 66
cts 80
cp 0.825
rs 10
c 0
b 0
f 0
wmc 15

7 Functions

Rating   Name   Duplication   Size   Complexity  
B invalidate_email_address() 0 38 5
A send_email_address_confirmation_email() 0 25 1
A send_email_address_confirmation_email_for_site() 0 17 1
A confirm_email_address() 0 38 4
A send_email_address_change_email_for_site() 0 17 1
A send_email_address_change_email() 0 27 1
A change_email_address() 0 19 2
1
"""
2
byceps.services.user.email_address_verification_service
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2021 Jochen Kupperschmidt
6
:License: Revised BSD (see `LICENSE` file for details)
7
"""
8
9 1
from typing import Optional
10
11 1
from ...database import db
12 1
from ...events.user import (
13
    UserEmailAddressChanged,
14
    UserEmailAddressConfirmed,
15
    UserEmailAddressInvalidated,
16
)
17 1
from ...typing import UserID
18
19 1
from ..email import service as email_service
20 1
from ..email.transfer.models import NameAndAddress
21 1
from ..site import service as site_service
22 1
from ..site.transfer.models import SiteID
23 1
from ..user import (
24
    command_service as user_command_service,
25
    service as user_service,
26
)
27 1
from ..verification_token import service as verification_token_service
28 1
from ..verification_token.transfer.models import Token
29
30 1
from . import event_service as user_event_service
31 1
from .transfer.models import User
32
33
34 1
def send_email_address_confirmation_email_for_site(
35
    recipient_email_address: str,
36
    recipient_screen_name: str,
37
    user_id: UserID,
38
    site_id: SiteID,
39
) -> None:
40 1
    site = site_service.get_site(site_id)
41
42 1
    email_config = email_service.get_config(site.brand_id)
43 1
    sender = email_config.sender
44
45 1
    send_email_address_confirmation_email(
46
        recipient_email_address,
47
        recipient_screen_name,
48
        user_id,
49
        site.server_name,
50
        sender,
51
    )
52
53
54 1
def send_email_address_confirmation_email(
55
    recipient_email_address: str,
56
    recipient_screen_name: str,
57
    user_id: UserID,
58
    server_name: str,
59
    sender: NameAndAddress,
60
) -> None:
61 1
    verification_token = (
62
        verification_token_service.create_for_email_address_confirmation(
63
            user_id, recipient_email_address
64
        )
65
    )
66 1
    confirmation_url = (
67
        f'https://{server_name}/users/email_address/'
68
        f'confirmation/{verification_token.token}'
69
    )
70
71 1
    subject = f'{recipient_screen_name}, bitte bestätige deine E-Mail-Adresse'
72 1
    body = (
73
        f'Hallo {recipient_screen_name},\n\n'
74
        f'bitte bestätige deine E-Mail-Adresse, indem du diese URL abrufst: {confirmation_url}'
75
    )
76 1
    recipients = [recipient_email_address]
77
78 1
    email_service.enqueue_email(sender, recipients, subject, body)
79
80
81 1
class EmailAddressConfirmationFailed(Exception):
82 1
    pass
83
84
85 1
def confirm_email_address(
86
    verification_token: Token,
87
) -> UserEmailAddressConfirmed:
88
    """Confirm the email address of the user account assigned with that
89
    verification token.
90
    """
91 1
    user = user_service.get_db_user(verification_token.user_id)
92
93 1
    if user.email_address is None:
94 1
        raise EmailAddressConfirmationFailed(
95
            'Account has no email address assigned.'
96
        )
97
98 1
    token_email_address = verification_token.data.get('email_address')
99 1
    if not token_email_address:
100
        raise EmailAddressConfirmationFailed('Token contains no email address.')
101
102 1
    if user.email_address != token_email_address:
103 1
        raise EmailAddressConfirmationFailed('Email addresses do not match.')
104
105 1
    user.email_address_verified = True
106
107 1
    event_data = {'email_address': token_email_address}
108 1
    event = user_event_service.build_event(
109
        'user-email-address-confirmed', user.id, event_data
110
    )
111 1
    db.session.add(event)
112
113 1
    db.session.commit()
114
115 1
    verification_token_service.delete_token(verification_token.token)
116
117 1
    return UserEmailAddressConfirmed(
118
        occurred_at=event.occurred_at,
119
        initiator_id=user.id,
120
        initiator_screen_name=user.screen_name,
121
        user_id=user.id,
122
        user_screen_name=user.screen_name,
123
    )
124
125
126 1
def invalidate_email_address(
127
    user_id: UserID, reason: str, *, initiator_id: Optional[UserID] = None
128
) -> UserEmailAddressInvalidated:
129
    """Invalidate the user's email address by marking it as unverified.
130
131
    This might be appropriate if an email to the user's address bounced
132
    because of a permanent issue (unknown mailbox, unknown domain, etc.)
133
    but not a temporary one (for example: mailbox full).
134
    """
135 1
    user = user_service.get_db_user(user_id)
136
137
    initiator: Optional[User]
138 1
    if initiator_id is not None:
139
        initiator = user_service.get_user(initiator_id)
140
    else:
141 1
        initiator = None
142
143 1
    user.email_address_verified = False
144
145 1
    event_data = {
146
        'email_address': user.email_address,
147
        'reason': reason,
148
    }
149 1
    if initiator:
150
        event_data['initiator_id'] = str(initiator.id)
151 1
    event = user_event_service.build_event(
152
        'user-email-address-invalidated', user.id, event_data
153
    )
154 1
    db.session.add(event)
155
156 1
    db.session.commit()
157
158 1
    return UserEmailAddressInvalidated(
159
        occurred_at=event.occurred_at,
160
        initiator_id=initiator.id if initiator else None,
161
        initiator_screen_name=initiator.screen_name if initiator else None,
162
        user_id=user.id,
163
        user_screen_name=user.screen_name,
164
    )
165
166
167 1
def send_email_address_change_email_for_site(
168
    new_email_address: str,
169
    recipient_screen_name: str,
170
    user_id: UserID,
171
    site_id: SiteID,
172
) -> None:
173
    site = site_service.get_site(site_id)
174
175
    email_config = email_service.get_config(site.brand_id)
176
    sender = email_config.sender
177
178
    send_email_address_change_email(
179
        new_email_address,
180
        recipient_screen_name,
181
        user_id,
182
        site.server_name,
183
        sender,
184
    )
185
186
187 1
def send_email_address_change_email(
188
    new_email_address: str,
189
    recipient_screen_name: str,
190
    user_id: UserID,
191
    server_name: str,
192
    sender: NameAndAddress,
193
) -> None:
194
    verification_token = (
195
        verification_token_service.create_for_email_address_change(
196
            user_id, new_email_address
197
        )
198
    )
199
    confirmation_url = (
200
        f'https://{server_name}/users/email_address/'
201
        f'change/{verification_token.token}'
202
    )
203
204
    subject = (
205
        f'{recipient_screen_name}, bitte bestätige deine neue E-Mail-Adresse'
206
    )
207
    body = (
208
        f'Hallo {recipient_screen_name},\n\n'
209
        f'bitte bestätige deine neue E-Mail-Adresse, indem du diese URL abrufst: {confirmation_url}'
210
    )
211
    recipients = [new_email_address]
212
213
    email_service.enqueue_email(sender, recipients, subject, body)
214
215
216 1
class EmailAddressChangeFailed(Exception):
217 1
    pass
218
219
220 1
def change_email_address(verification_token: Token) -> UserEmailAddressChanged:
221
    """Change the email address of the user account assigned with that
222
    verification token.
223
    """
224 1
    new_email_address = verification_token.data.get('new_email_address')
225 1
    if not new_email_address:
226
        raise EmailAddressChangeFailed('Token contains no email address.')
227
228 1
    user = user_service.get_db_user(verification_token.user_id)
229 1
    verified = True
230 1
    initiator = user
231
232 1
    event = user_command_service.change_email_address(
233
        user.id, new_email_address, verified, initiator.id
234
    )
235
236 1
    verification_token_service.delete_token(verification_token.token)
237
238
    return event
239