2019-05-21 15:53:48 +01:00
|
|
|
|
import uuid
|
2021-03-10 13:55:06 +00:00
|
|
|
|
from datetime import datetime, timedelta
|
2023-02-17 11:54:17 -05:00
|
|
|
|
from secrets import randbelow
|
2018-03-13 13:07:02 +00:00
|
|
|
|
|
2016-05-20 13:43:31 +01:00
|
|
|
|
from sqlalchemy import func
|
2018-03-13 13:07:02 +00:00
|
|
|
|
from sqlalchemy.orm import joinedload
|
|
|
|
|
|
|
2016-01-07 17:31:17 +00:00
|
|
|
|
from app import db
|
2021-04-14 07:11:01 +01:00
|
|
|
|
from app.dao.dao_utils import autocommit
|
2019-05-21 15:53:48 +01:00
|
|
|
|
from app.dao.permissions_dao import permission_dao
|
|
|
|
|
|
from app.dao.service_user_dao import dao_get_service_users_by_user_id
|
|
|
|
|
|
from app.errors import InvalidRequest
|
2021-03-10 13:55:06 +00:00
|
|
|
|
from app.models import EMAIL_AUTH_TYPE, User, VerifyCode
|
2020-05-22 09:36:07 +01:00
|
|
|
|
from app.utils import escape_special_characters, get_archived_db_column_value
|
2017-06-13 18:11:13 +01:00
|
|
|
|
|
2016-01-21 17:29:24 +00:00
|
|
|
|
|
2016-11-07 17:42:39 +00:00
|
|
|
|
def _remove_values_for_keys_if_present(dict, keys):
|
|
|
|
|
|
for key in keys:
|
|
|
|
|
|
dict.pop(key, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-02-17 11:54:17 -05:00
|
|
|
|
def create_secret_code(length=6):
|
|
|
|
|
|
random_number = randbelow(10 ** length)
|
2023-02-22 10:48:15 -05:00
|
|
|
|
return "{:0{length}d}".format(random_number, length=length)
|
2016-01-07 17:31:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
2020-12-22 15:46:31 +00:00
|
|
|
|
def save_user_attribute(usr, update_dict=None):
|
|
|
|
|
|
db.session.query(User).filter_by(id=usr.id).update(update_dict or {})
|
2016-11-07 17:42:39 +00:00
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-12-22 15:46:31 +00:00
|
|
|
|
def save_model_user(user, update_dict=None, password=None, validated_email_access=False):
|
2020-01-24 15:18:39 +00:00
|
|
|
|
if password:
|
|
|
|
|
|
user.password = password
|
|
|
|
|
|
user.password_changed_at = datetime.utcnow()
|
|
|
|
|
|
if validated_email_access:
|
|
|
|
|
|
user.email_access_validated_at = datetime.utcnow()
|
2016-01-11 17:19:06 +00:00
|
|
|
|
if update_dict:
|
2016-11-07 17:42:39 +00:00
|
|
|
|
_remove_values_for_keys_if_present(update_dict, ['id', 'password_changed_at'])
|
2020-12-22 15:46:31 +00:00
|
|
|
|
db.session.query(User).filter_by(id=user.id).update(update_dict or {})
|
2016-01-11 17:19:06 +00:00
|
|
|
|
else:
|
2020-01-24 15:18:39 +00:00
|
|
|
|
db.session.add(user)
|
2016-01-07 17:31:17 +00:00
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-21 17:29:24 +00:00
|
|
|
|
def create_user_code(user, code, code_type):
|
|
|
|
|
|
verify_code = VerifyCode(code_type=code_type,
|
2017-02-14 14:04:11 +00:00
|
|
|
|
expiry_datetime=datetime.utcnow() + timedelta(minutes=30),
|
2016-01-21 17:29:24 +00:00
|
|
|
|
user=user)
|
|
|
|
|
|
verify_code.code = code
|
|
|
|
|
|
db.session.add(verify_code)
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
return verify_code
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_user_code(user, code, code_type):
|
|
|
|
|
|
# Get the most recent codes to try and reduce the
|
|
|
|
|
|
# time searching for the correct code.
|
|
|
|
|
|
codes = VerifyCode.query.filter_by(
|
|
|
|
|
|
user=user, code_type=code_type).order_by(
|
2017-02-15 17:41:07 +00:00
|
|
|
|
VerifyCode.created_at.desc())
|
2017-11-03 09:51:50 +00:00
|
|
|
|
return next((x for x in codes if x.check_code(code)), None)
|
2016-01-21 17:29:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-03-09 17:46:01 +00:00
|
|
|
|
def delete_codes_older_created_more_than_a_day_ago():
|
|
|
|
|
|
deleted = db.session.query(VerifyCode).filter(
|
|
|
|
|
|
VerifyCode.created_at < datetime.utcnow() - timedelta(hours=24)
|
|
|
|
|
|
).delete()
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
return deleted
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-21 17:29:24 +00:00
|
|
|
|
def use_user_code(id):
|
|
|
|
|
|
verify_code = VerifyCode.query.get(id)
|
|
|
|
|
|
verify_code.code_used = True
|
|
|
|
|
|
db.session.add(verify_code)
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-12 10:39:49 +00:00
|
|
|
|
def delete_model_user(user):
|
|
|
|
|
|
db.session.delete(user)
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-05-06 11:07:11 +01:00
|
|
|
|
def delete_user_verify_codes(user):
|
|
|
|
|
|
VerifyCode.query.filter_by(user=user).delete()
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-15 16:18:05 +00:00
|
|
|
|
def count_user_verify_codes(user):
|
2017-02-16 12:44:40 +00:00
|
|
|
|
query = VerifyCode.query.filter(
|
|
|
|
|
|
VerifyCode.user == user,
|
|
|
|
|
|
VerifyCode.expiry_datetime > datetime.utcnow(),
|
|
|
|
|
|
VerifyCode.code_used.is_(False)
|
|
|
|
|
|
)
|
|
|
|
|
|
return query.count()
|
2017-02-15 16:18:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-11-07 17:42:39 +00:00
|
|
|
|
def get_user_by_id(user_id=None):
|
2016-01-07 17:31:17 +00:00
|
|
|
|
if user_id:
|
2016-03-11 12:39:55 +00:00
|
|
|
|
return User.query.filter_by(id=user_id).one()
|
2016-01-07 17:31:17 +00:00
|
|
|
|
return User.query.filter_by().all()
|
2016-01-25 11:14:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-02-23 11:03:59 +00:00
|
|
|
|
def get_user_by_email(email):
|
2016-05-20 13:43:31 +01:00
|
|
|
|
return User.query.filter(func.lower(User.email_address) == func.lower(email)).one()
|
2016-02-23 11:03:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
2018-07-09 17:25:13 +01:00
|
|
|
|
def get_users_by_partial_email(email):
|
2018-07-13 15:26:42 +01:00
|
|
|
|
email = escape_special_characters(email)
|
|
|
|
|
|
return User.query.filter(User.email_address.ilike("%{}%".format(email))).all()
|
2018-07-09 17:25:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-01-25 11:14:23 +00:00
|
|
|
|
def increment_failed_login_count(user):
|
|
|
|
|
|
user.failed_login_count += 1
|
|
|
|
|
|
db.session.add(user)
|
|
|
|
|
|
db.session.commit()
|
2016-01-28 11:32:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def reset_failed_login_count(user):
|
|
|
|
|
|
if user.failed_login_count > 0:
|
|
|
|
|
|
user.failed_login_count = 0
|
|
|
|
|
|
db.session.add(user)
|
|
|
|
|
|
db.session.commit()
|
2017-02-07 11:05:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
2021-08-17 16:59:51 +01:00
|
|
|
|
def update_user_password(user, password):
|
2017-02-16 15:20:30 +00:00
|
|
|
|
# reset failed login count - they've just reset their password so should be fine
|
2017-02-07 11:05:15 +00:00
|
|
|
|
user.password = password
|
|
|
|
|
|
user.password_changed_at = datetime.utcnow()
|
|
|
|
|
|
db.session.add(user)
|
|
|
|
|
|
db.session.commit()
|
2018-03-13 13:07:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_user_and_accounts(user_id):
|
|
|
|
|
|
return User.query.filter(
|
|
|
|
|
|
User.id == user_id
|
|
|
|
|
|
).options(
|
|
|
|
|
|
# eagerly load the user's services and organisations, and also the service's org and vice versa
|
|
|
|
|
|
# (so we can see if the user knows about it)
|
|
|
|
|
|
joinedload('services'),
|
|
|
|
|
|
joinedload('organisations'),
|
|
|
|
|
|
joinedload('organisations.services'),
|
|
|
|
|
|
joinedload('services.organisation'),
|
|
|
|
|
|
).one()
|
2019-05-21 15:53:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
2021-04-14 07:11:01 +01:00
|
|
|
|
@autocommit
|
2019-05-21 15:53:48 +01:00
|
|
|
|
def dao_archive_user(user):
|
|
|
|
|
|
if not user_can_be_archived(user):
|
|
|
|
|
|
msg = "User can’t be removed from a service - check all services have another team member with manage_settings"
|
|
|
|
|
|
raise InvalidRequest(msg, 400)
|
|
|
|
|
|
|
|
|
|
|
|
permission_dao.remove_user_service_permissions_for_all_services(user)
|
|
|
|
|
|
|
|
|
|
|
|
service_users = dao_get_service_users_by_user_id(user.id)
|
|
|
|
|
|
for service_user in service_users:
|
|
|
|
|
|
db.session.delete(service_user)
|
|
|
|
|
|
|
|
|
|
|
|
user.organisations = []
|
|
|
|
|
|
|
|
|
|
|
|
user.auth_type = EMAIL_AUTH_TYPE
|
2020-05-22 09:36:07 +01:00
|
|
|
|
user.email_address = get_archived_db_column_value(user.email_address)
|
2019-05-21 15:53:48 +01:00
|
|
|
|
user.mobile_number = None
|
|
|
|
|
|
user.password = str(uuid.uuid4())
|
|
|
|
|
|
# Changing the current_session_id signs the user out
|
|
|
|
|
|
user.current_session_id = '00000000-0000-0000-0000-000000000000'
|
|
|
|
|
|
user.state = 'inactive'
|
|
|
|
|
|
|
|
|
|
|
|
db.session.add(user)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def user_can_be_archived(user):
|
|
|
|
|
|
active_services = [x for x in user.services if x.active]
|
|
|
|
|
|
|
|
|
|
|
|
for service in active_services:
|
|
|
|
|
|
other_active_users = [x for x in service.users if x.state == 'active' and x != user]
|
|
|
|
|
|
|
|
|
|
|
|
if not other_active_users:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
if not any('manage_settings' in user.get_permissions(service.id) for user in other_active_users):
|
|
|
|
|
|
# no-one else has manage settings
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
return True
|