Files
notifications-admin/app/utils/user.py
Chris Hill-Scott c3091223a9 Be strict about similar email addresses for alerts
We don’t want a single person to have two accounts on an emergency
alerts service because it would let them circumvent the two eyes
approval process.

We can go some way to mitigating against this by stopping people using
common methods that email providers use to alias email addresses. These
are:
- being case insensitive
- being insensitive to the position or number of dots in the local part
  of an email address
- using ‘plus addressing’

We already prevent the first one, this commit adds normalisation which
strip out the second two before doing the comparision with the current
user’s email address.
2021-07-15 13:55:50 +01:00

80 lines
2.2 KiB
Python

import os
from functools import wraps
from flask import abort, current_app
from flask_login import current_user, login_required
from app.notify_client.organisations_api_client import organisations_client
user_is_logged_in = login_required
with open('{}/email_domains.txt'.format(
os.path.dirname(os.path.realpath(__file__))
)) as email_domains:
GOVERNMENT_EMAIL_DOMAIN_NAMES = [line.strip() for line in email_domains]
def user_has_permissions(*permissions, **permission_kwargs):
def wrap(func):
@wraps(func)
def wrap_func(*args, **kwargs):
if not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
if not current_user.has_permissions(*permissions, **permission_kwargs):
abort(403)
return func(*args, **kwargs)
return wrap_func
return wrap
def user_is_gov_user(f):
@wraps(f)
def wrapped(*args, **kwargs):
if not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
if not current_user.is_gov_user:
abort(403)
return f(*args, **kwargs)
return wrapped
def user_is_platform_admin(f):
@wraps(f)
def wrapped(*args, **kwargs):
if not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
if not current_user.platform_admin:
abort(403)
return f(*args, **kwargs)
return wrapped
def is_gov_user(email_address):
return _email_address_ends_with(
email_address, GOVERNMENT_EMAIL_DOMAIN_NAMES
) or _email_address_ends_with(
email_address, organisations_client.get_domains()
)
def _email_address_ends_with(email_address, known_domains):
return any(
email_address.lower().endswith((
"@{}".format(known),
".{}".format(known),
))
for known in known_domains
)
def normalise_email_address_aliases(email_address):
local_part, domain = email_address.split('@')
local_part = local_part.split('+')[0].replace('.', '')
return f'{local_part}@{domain}'.lower()
def distinct_email_addresses(*args):
return len(args) == len(set(map(normalise_email_address_aliases, args)))