Passed
Push — main ( 7e9c56...8b0189 )
by Jochen
03:34
created

create_form()   A

Complexity

Conditions 3

Size

Total Lines 35
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 8.2077

Importance

Changes 0
Metric Value
cc 3
eloc 25
nop 1
dl 0
loc 35
ccs 3
cts 18
cp 0.1666
crap 8.2077
rs 9.28
c 0
b 0
f 0
1
"""
2
byceps.blueprints.common.user.creation.views
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2020 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9 1
from datetime import datetime
10 1
from typing import Optional, Set
11 1
from uuid import UUID
12
13 1
from flask import abort, g, request
14
15 1
from .....config import get_app_mode
16 1
from .....services.brand import settings_service as brand_settings_service
17 1
from .....services.consent import subject_service as consent_subject_service
18 1
from .....services.consent.transfer.models import Consent, Subject, SubjectID
19 1
from .....services.newsletter.transfer.models import (
20
    ListID as NewsletterListID,
21
    Subscription as NewsletterSubscription,
22
)
23 1
from .....services.site import (
24
    settings_service as site_settings_service,
25
    service as site_service,
26
)
27 1
from .....services.terms import document_service as terms_document_service
28 1
from .....services.terms import version_service as terms_version_service
29 1
from .....services.terms.transfer.models import DocumentID as TermsDocumentID
30 1
from .....services.user import creation_service as user_creation_service
31 1
from .....signals import user as user_signals
32 1
from .....util.framework.blueprint import create_blueprint
33 1
from .....util.framework.flash import flash_error, flash_success
34 1
from .....util.framework.templating import templated
35 1
from .....util.views import redirect_to
36
37 1
from .forms import assemble_user_create_form
38
39
40 1
blueprint = create_blueprint('user_creation', __name__)
41
42
43 1
@blueprint.route('/create')
44 1
@templated
45 1
def create_form(erroneous_form=None):
46
    """Show a form to create a user."""
47
    _abort_if_user_account_creation_disabled()
48
49
    terms_version = terms_version_service.find_current_version_for_brand(
50
        g.brand_id
51
    )
52
    required_consent_subjects = _get_required_consent_subjects()
53
    newsletter_list_id = _find_newsletter_list_for_brand()
54
55
    real_name_required = _is_real_name_required()
56
    terms_consent_required = terms_version is not None
57
    newsletter_offered = newsletter_list_id is not None
58
59
    if terms_consent_required:
60
        terms_version_id = terms_version.id
61
    else:
62
        terms_version_id = None
63
64
    if erroneous_form:
65
        form = erroneous_form
66
    else:
67
        UserCreateForm = assemble_user_create_form(
68
            real_name_required=real_name_required,
69
            terms_consent_required=terms_consent_required,
70
            required_consent_subjects=required_consent_subjects,
71
            newsletter_offered=newsletter_offered,
72
        )
73
        form = UserCreateForm(terms_version_id=terms_version_id)
74
75
    return {
76
        'form': form,
77
        'required_consent_subjects': required_consent_subjects,
78
    }
79
80
81 1
@blueprint.route('/', methods=['POST'])
82
def create():
83
    """Create a user."""
84 1
    _abort_if_user_account_creation_disabled()
85
86 1
    terms_document_id = terms_document_service.find_document_id_for_brand(
87
        g.brand_id
88
    )
89 1
    required_consent_subjects = _get_required_consent_subjects()
90 1
    newsletter_list_id = _find_newsletter_list_for_brand()
91
92 1
    real_name_required = _is_real_name_required()
93 1
    terms_consent_required = terms_document_id is not None
94 1
    newsletter_offered = newsletter_list_id is not None
95
96 1
    UserCreateForm = assemble_user_create_form(
97
        real_name_required=real_name_required,
98
        terms_consent_required=terms_consent_required,
99
        required_consent_subjects=required_consent_subjects,
100
        newsletter_offered=newsletter_offered,
101
    )
102 1
    form = UserCreateForm(request.form)
103
104 1
    if not form.validate():
105
        return create_form(form)
106
107 1
    screen_name = form.screen_name.data.strip()
108 1
    email_address = form.email_address.data.strip().lower()
109 1
    password = form.password.data
110
111 1
    now_utc = datetime.utcnow()
112
113 1
    if real_name_required:
114 1
        first_names = form.first_names.data.strip()
115 1
        last_name = form.last_name.data.strip()
116
    else:
117
        first_names = None
118
        last_name = None
119
120 1
    if terms_consent_required:
121 1
        terms_consent = _get_terms_consent(form, terms_document_id, now_utc)
122
    else:
123
        terms_consent = None
124
125 1
    consents = {
126
        _assemble_consent(subject.id, now_utc)
127
        for subject in required_consent_subjects
128
    }
129
130 1
    newsletter_subscription = _get_newsletter_subscription(
131
        newsletter_offered, form, newsletter_list_id, now_utc
132
    )
133
134 1
    try:
135 1
        user, event = user_creation_service.create_user(
136
            screen_name,
137
            email_address,
138
            password,
139
            first_names,
140
            last_name,
141
            g.site_id,
142
            terms_consent=terms_consent,
143
            consents=consents,
144
            newsletter_subscription=newsletter_subscription,
145
        )
146
    except user_creation_service.UserCreationFailed:
147
        flash_error(
148
            f'Das Benutzerkonto für "{screen_name}" konnte nicht angelegt werden.'
149
        )
150
        return create_form(form)
151
152 1
    flash_success(
153
        f'Das Benutzerkonto für "{user.screen_name}" wurde angelegt. '
154
        'Bevor du dich damit anmelden kannst, muss zunächst der Link in '
155
        'der an die angegebene Adresse verschickten E-Mail besucht werden.'
156
    )
157
158 1
    user_signals.account_created.send(None, event=event)
159
160 1
    return redirect_to('authentication.login_form')
161
162
163 1
def _abort_if_user_account_creation_disabled():
164 1
    if not _is_user_account_creation_enabled():
165
        flash_error('Das Erstellen von Benutzerkonten ist deaktiviert.')
166
        abort(403)
167
168
169 1
def _is_user_account_creation_enabled():
170 1
    if get_app_mode().is_admin():
171
        return False
172
173 1
    site = site_service.get_site(g.site_id)
174 1
    return site.user_account_creation_enabled
175
176
177 1
def _is_real_name_required() -> bool:
178
    """Return `True` if real name is required.
179
180
    By default, real name is required. It can be disabled by configuring
181
    the string `false` for the site setting `real_name_required`.
182
    """
183 1
    value = _find_site_setting_value('real_name_required')
184
185 1
    return value != 'false'
186
187
188 1
def _get_required_consent_subjects() -> Set[Subject]:
189
    """Return the consent subjects required for this brand."""
190 1
    subject_ids = set()
191
192 1
    privacy_policy_consent_subject_id = (
193
        _find_privacy_policy_consent_subject_id()
194
    )
195 1
    if privacy_policy_consent_subject_id is not None:
196 1
        subject_ids.add(privacy_policy_consent_subject_id)
197
198 1
    return consent_subject_service.get_subjects(subject_ids)
199
200
201 1
def _find_privacy_policy_consent_subject_id() -> Optional[SubjectID]:
202
    """Return the privacy policy consent subject ID configured for this
203
    brand, or `None` if none is configured.
204
    """
205 1
    value = _find_brand_setting_value('privacy_policy_consent_subject_id')
206
207 1
    if not value:
208
        return None
209
210 1
    return UUID(value)
211
212
213 1
def _find_newsletter_list_for_brand() -> Optional[NewsletterListID]:
214
    """Return the newsletter list configured for this brand, or `None`
215
    if none is configured.
216
    """
217 1
    value = _find_brand_setting_value('newsletter_list_id')
218
219 1
    if not value:
220
        return None
221
222 1
    return NewsletterListID(value)
223
224
225 1
def _find_brand_setting_value(setting_name: str) -> Optional[str]:
226
    """Return the value configured for this brand and the given setting
227
    name, or `None` if not configured.
228
    """
229 1
    return brand_settings_service.find_setting_value(g.brand_id, setting_name)
230
231
232 1
def _find_site_setting_value(setting_name: str) -> Optional[str]:
233
    """Return the value configured for this site and the given setting
234
    name, or `None` if not configured.
235
    """
236 1
    return site_settings_service.find_setting_value(g.site_id, setting_name)
237
238
239 1
def _get_terms_consent(
240
    form, terms_document_id: TermsDocumentID, expressed_at: datetime,
241
) -> Consent:
242 1
    terms_version_id = form.terms_version_id.data
243 1
    consent_to_terms = form.consent_to_terms.data
244
245 1
    terms_version = terms_version_service.find_version(terms_version_id)
246 1
    if terms_version.document_id != terms_document_id:
247
        abort(400, 'Die AGB-Version gehört nicht zu dieser Veranstaltung.')
248
249 1
    return _assemble_consent(terms_version.consent_subject_id, expressed_at)
250
251
252 1
def _assemble_consent(subject_id: SubjectID, expressed_at: datetime) -> Consent:
253 1
    return Consent(
254
        user_id=None,  # not available at this point
255
        subject_id=subject_id,
256
        expressed_at=expressed_at,
257
    )
258
259
260 1
def _get_newsletter_subscription(
261
    newsletter_offered: bool,
262
    form,
263
    newsletter_list_id: NewsletterListID,
264
    expressed_at: datetime,
265
) -> Optional[NewsletterSubscription]:
266 1
    if not newsletter_offered:
267
        return None
268
269 1
    subscribe_to_newsletter = form.subscribe_to_newsletter.data
270 1
    if not subscribe_to_newsletter:
271 1
        return None
272
273 1
    return _assemble_newsletter_subscription(newsletter_list_id, expressed_at)
274
275
276 1
def _assemble_newsletter_subscription(
277
    newsletter_list_id: NewsletterListID, expressed_at: datetime
278
) -> NewsletterSubscription:
279 1
    return NewsletterSubscription(
280
        user_id=None,  # not available at this point
281
        list_id=newsletter_list_id,
282
        expressed_at=expressed_at,
283
    )
284