mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-23 07:46:23 -04:00
Merge pull request #2991 from alphagov/user-api-client-return-json
Make user API client return JSON, not a model
This commit is contained in:
@@ -48,7 +48,7 @@ from app.extensions import (
|
||||
)
|
||||
from app.models.organisation import Organisation
|
||||
from app.models.service import Service
|
||||
from app.models.user import AnonymousUser
|
||||
from app.models.user import AnonymousUser, User
|
||||
from app.navigation import (
|
||||
CaseworkNavigation,
|
||||
HeaderNavigation,
|
||||
@@ -463,7 +463,7 @@ def nl2br(value):
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return user_api_client.get_user(user_id)
|
||||
return User.from_id(user_id)
|
||||
|
||||
|
||||
def load_service_before_request():
|
||||
|
||||
@@ -44,7 +44,7 @@ from app.main.validators import (
|
||||
ValidEmail,
|
||||
ValidGovEmail,
|
||||
)
|
||||
from app.models.user import permissions, roles
|
||||
from app.models.roles_and_permissions import permissions, roles
|
||||
from app.utils import guess_name_from_email_address
|
||||
|
||||
|
||||
@@ -373,11 +373,11 @@ class RegisterUserForm(StripWhitespaceForm):
|
||||
class RegisterUserFromInviteForm(RegisterUserForm):
|
||||
def __init__(self, invited_user):
|
||||
super().__init__(
|
||||
service=invited_user['service'],
|
||||
email_address=invited_user['email_address'],
|
||||
auth_type=invited_user['auth_type'],
|
||||
service=invited_user.service,
|
||||
email_address=invited_user.email_address,
|
||||
auth_type=invited_user.auth_type,
|
||||
name=guess_name_from_email_address(
|
||||
invited_user['email_address']
|
||||
invited_user.email_address
|
||||
),
|
||||
)
|
||||
|
||||
@@ -394,8 +394,8 @@ class RegisterUserFromInviteForm(RegisterUserForm):
|
||||
class RegisterUserFromOrgInviteForm(StripWhitespaceForm):
|
||||
def __init__(self, invited_org_user):
|
||||
super().__init__(
|
||||
organisation=invited_org_user['organisation'],
|
||||
email_address=invited_org_user['email_address'],
|
||||
organisation=invited_org_user.organisation,
|
||||
email_address=invited_org_user.email_address,
|
||||
)
|
||||
|
||||
name = StringField(
|
||||
|
||||
@@ -3,21 +3,22 @@ from flask import redirect, render_template, session, url_for
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import TextNotReceivedForm
|
||||
from app.models.user import User
|
||||
from app.utils import redirect_to_sign_in
|
||||
|
||||
|
||||
@main.route('/resend-email-verification')
|
||||
@redirect_to_sign_in
|
||||
def resend_email_verification():
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
user_api_client.send_verify_email(user.id, user.email_address)
|
||||
user = User.from_email_address(session['user_details']['email'])
|
||||
user.send_verify_email()
|
||||
return render_template('views/resend-email-verification.html', email=user.email_address)
|
||||
|
||||
|
||||
@main.route('/text-not-received', methods=['GET', 'POST'])
|
||||
@redirect_to_sign_in
|
||||
def check_and_resend_text_code():
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
user = User.from_email_address(session['user_details']['email'])
|
||||
|
||||
if user.state == 'active':
|
||||
# this is a verified user and therefore redirect to page to request resend without edit mobile
|
||||
@@ -25,8 +26,8 @@ def check_and_resend_text_code():
|
||||
|
||||
form = TextNotReceivedForm(mobile_number=user.mobile_number)
|
||||
if form.validate_on_submit():
|
||||
user_api_client.send_verify_code(user.id, 'sms', to=form.mobile_number.data)
|
||||
user = user_api_client.update_user_attribute(user.id, mobile_number=form.mobile_number.data)
|
||||
user.send_verify_code(to=form.mobile_number.data)
|
||||
user.update(mobile_number=form.mobile_number.data)
|
||||
return redirect(url_for('.verify'))
|
||||
|
||||
return render_template('views/text-not-received.html', form=form)
|
||||
@@ -35,8 +36,8 @@ def check_and_resend_text_code():
|
||||
@main.route('/send-new-code', methods=['GET'])
|
||||
@redirect_to_sign_in
|
||||
def check_and_resend_verification_code():
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user = User.from_email_address(session['user_details']['email'])
|
||||
user.send_verify_code()
|
||||
if user.state == 'pending':
|
||||
return redirect(url_for('main.verify'))
|
||||
else:
|
||||
|
||||
@@ -4,6 +4,7 @@ from flask_login import login_required
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import SearchUsersByEmailForm
|
||||
from app.models.user import User
|
||||
from app.utils import user_is_platform_admin
|
||||
|
||||
|
||||
@@ -29,7 +30,7 @@ def find_users_by_email():
|
||||
@login_required
|
||||
@user_is_platform_admin
|
||||
def user_information(user_id):
|
||||
user = user_api_client.get_user(user_id)
|
||||
user = User.from_id(user_id)
|
||||
services = user_api_client.get_services_for_user(user)
|
||||
return render_template(
|
||||
'views/find-users/user-information.html',
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
from flask import abort, flash, redirect, render_template, session, url_for
|
||||
from flask_login import current_user
|
||||
from markupsafe import Markup
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app import (
|
||||
invite_api_client,
|
||||
org_invite_api_client,
|
||||
organisations_client,
|
||||
service_api_client,
|
||||
user_api_client,
|
||||
)
|
||||
from app.main import main
|
||||
from app.models.organisation import Organisation
|
||||
from app.models.service import Service
|
||||
from app.models.user import (
|
||||
InvitedOrgUser,
|
||||
InvitedUser,
|
||||
OrganisationUsers,
|
||||
User,
|
||||
Users,
|
||||
)
|
||||
from app.notify_client import InviteTokenError
|
||||
|
||||
|
||||
@main.route("/invitation/<token>")
|
||||
def accept_invite(token):
|
||||
try:
|
||||
invited_user = invite_api_client.check_token(token)
|
||||
except HTTPError as e:
|
||||
if e.status_code == 400 and 'invitation' in e.message:
|
||||
flash(e.message['invitation'])
|
||||
return redirect(url_for('main.sign_in'))
|
||||
else:
|
||||
raise e
|
||||
invited_user = InvitedUser.from_token(token)
|
||||
except InviteTokenError as exception:
|
||||
flash(str(exception))
|
||||
return redirect(url_for('main.sign_in'))
|
||||
|
||||
if not current_user.is_anonymous and current_user.email_address.lower() != invited_user.email_address.lower():
|
||||
message = Markup("""
|
||||
@@ -38,11 +37,10 @@ def accept_invite(token):
|
||||
abort(403)
|
||||
|
||||
if invited_user.status == 'cancelled':
|
||||
from_user = user_api_client.get_user(invited_user.from_user)
|
||||
service = service_api_client.get_service(invited_user.service)['data']
|
||||
service = Service.from_id(invited_user.service)
|
||||
return render_template('views/cancelled-invitation.html',
|
||||
from_user=from_user.name,
|
||||
service_name=service['name'])
|
||||
from_user=invited_user.from_user.name,
|
||||
service_name=service.name)
|
||||
|
||||
if invited_user.status == 'accepted':
|
||||
session.pop('invited_user', None)
|
||||
@@ -50,36 +48,32 @@ def accept_invite(token):
|
||||
|
||||
session['invited_user'] = invited_user.serialize()
|
||||
|
||||
existing_user = user_api_client.get_user_by_email_or_none(invited_user.email_address)
|
||||
service_users = user_api_client.get_users_for_service(invited_user.service)
|
||||
existing_user = User.from_email_address_or_none(invited_user.email_address)
|
||||
|
||||
if existing_user:
|
||||
invite_api_client.accept_invite(invited_user.service, invited_user.id)
|
||||
if existing_user in service_users:
|
||||
invited_user.accept_invite()
|
||||
if existing_user in Users(invited_user.service):
|
||||
return redirect(url_for('main.service_dashboard', service_id=invited_user.service))
|
||||
else:
|
||||
service = service_api_client.get_service(invited_user.service)['data']
|
||||
service = Service.from_id(invited_user.service)
|
||||
# if the service you're being added to can modify auth type, then check if this is relevant
|
||||
if 'email_auth' in service['permissions'] and (
|
||||
# they have a phone number, we want them to start using it. if they dont have a mobile we just
|
||||
# ignore that option of the invite
|
||||
(existing_user.mobile_number and invited_user.auth_type == 'sms_auth') or
|
||||
# we want them to start sending emails. it's always valid, so lets always update
|
||||
invited_user.auth_type == 'email_auth'
|
||||
if service.has_permission('email_auth') and (
|
||||
# they have a phone number, we want them to start using it. if they dont have a mobile we just
|
||||
# ignore that option of the invite
|
||||
(existing_user.mobile_number and invited_user.auth_type == 'sms_auth') or
|
||||
# we want them to start sending emails. it's always valid, so lets always update
|
||||
invited_user.auth_type == 'email_auth'
|
||||
):
|
||||
user_api_client.update_user_attribute(existing_user.id, auth_type=invited_user.auth_type)
|
||||
user_api_client.add_user_to_service(invited_user.service,
|
||||
existing_user.id,
|
||||
invited_user.permissions,
|
||||
invited_user.folder_permissions)
|
||||
return redirect(url_for('main.service_dashboard', service_id=invited_user.service))
|
||||
existing_user.update(auth_type=invited_user.auth_type)
|
||||
invited_user.add_to_service()
|
||||
return redirect(url_for('main.service_dashboard', service_id=service.id))
|
||||
else:
|
||||
return redirect(url_for('main.register_from_invite'))
|
||||
|
||||
|
||||
@main.route("/organisation-invitation/<token>")
|
||||
def accept_org_invite(token):
|
||||
invited_org_user = org_invite_api_client.check_token(token)
|
||||
invited_org_user = InvitedOrgUser.from_token(token)
|
||||
if not current_user.is_anonymous and current_user.email_address.lower() != invited_org_user.email_address.lower():
|
||||
message = Markup("""
|
||||
You’re signed in as {}.
|
||||
@@ -94,11 +88,10 @@ def accept_org_invite(token):
|
||||
abort(403)
|
||||
|
||||
if invited_org_user.status == 'cancelled':
|
||||
invited_by = user_api_client.get_user(invited_org_user.invited_by)
|
||||
organisation = organisations_client.get_organisation(invited_org_user.organisation)
|
||||
organisation = Organisation.from_id(invited_org_user.organisation)
|
||||
return render_template('views/cancelled-invitation.html',
|
||||
from_user=invited_by.name,
|
||||
organisation_name=organisation['name'])
|
||||
from_user=invited_org_user.invited_by.name,
|
||||
organisation_name=organisation.name)
|
||||
|
||||
if invited_org_user.status == 'accepted':
|
||||
session.pop('invited_org_user', None)
|
||||
@@ -106,13 +99,13 @@ def accept_org_invite(token):
|
||||
|
||||
session['invited_org_user'] = invited_org_user.serialize()
|
||||
|
||||
existing_user = user_api_client.get_user_by_email_or_none(invited_org_user.email_address)
|
||||
organisation_users = user_api_client.get_users_for_organisation(invited_org_user.organisation)
|
||||
existing_user = User.from_email_address_or_none(invited_org_user.email_address)
|
||||
organisation_users = OrganisationUsers(invited_org_user.organisation)
|
||||
|
||||
if existing_user:
|
||||
org_invite_api_client.accept_invite(invited_org_user.organisation, invited_org_user.id)
|
||||
invited_org_user.accept_invite()
|
||||
if existing_user not in organisation_users:
|
||||
user_api_client.add_user_to_organisation(invited_org_user.organisation, existing_user.id)
|
||||
invited_org_user.add_to_organisation()
|
||||
return redirect(url_for('main.organisation_dashboard', org_id=invited_org_user.organisation))
|
||||
else:
|
||||
return redirect(url_for('main.register_from_org_invite'))
|
||||
|
||||
@@ -10,12 +10,7 @@ from flask import (
|
||||
from flask_login import current_user, login_required
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app import (
|
||||
current_service,
|
||||
invite_api_client,
|
||||
service_api_client,
|
||||
user_api_client,
|
||||
)
|
||||
from app import current_service, service_api_client
|
||||
from app.event_handlers import (
|
||||
create_email_change_event,
|
||||
create_mobile_number_change_event,
|
||||
@@ -29,7 +24,8 @@ from app.main.forms import (
|
||||
PermissionsForm,
|
||||
SearchUsersForm,
|
||||
)
|
||||
from app.models.user import permissions
|
||||
from app.models.roles_and_permissions import permissions
|
||||
from app.models.user import InvitedUser, User
|
||||
from app.utils import is_gov_user, redact_mobile_number, user_has_permissions
|
||||
|
||||
|
||||
@@ -64,7 +60,7 @@ def invite_user(service_id):
|
||||
|
||||
if form.validate_on_submit():
|
||||
email_address = form.email_address.data
|
||||
invited_user = invite_api_client.create_invite(
|
||||
invited_user = InvitedUser.create(
|
||||
current_user.id,
|
||||
service_id,
|
||||
email_address,
|
||||
@@ -106,13 +102,13 @@ def edit_user_permissions(service_id, user_id):
|
||||
)
|
||||
|
||||
if form.validate_on_submit():
|
||||
user_api_client.set_user_permissions(
|
||||
user_id, service_id,
|
||||
user.set_permissions(
|
||||
service_id,
|
||||
permissions=form.permissions,
|
||||
folder_permissions=form.folder_permissions.data,
|
||||
)
|
||||
if service_has_email_auth:
|
||||
user_api_client.update_user_attribute(user_id, auth_type=form.login_authentication.data)
|
||||
user.update(auth_type=form.login_authentication.data)
|
||||
return redirect(url_for('.manage_users', service_id=service_id))
|
||||
|
||||
return render_template(
|
||||
@@ -154,13 +150,10 @@ def edit_user_email(service_id, user_id):
|
||||
user = current_service.get_team_member(user_id)
|
||||
user_email = user.email_address
|
||||
|
||||
def _is_email_already_in_use(email):
|
||||
return user_api_client.is_email_already_in_use(email)
|
||||
|
||||
if is_gov_user(user_email):
|
||||
form = ChangeEmailForm(_is_email_already_in_use, email_address=user_email)
|
||||
form = ChangeEmailForm(User.already_registered, email_address=user_email)
|
||||
else:
|
||||
form = ChangeNonGovEmailForm(_is_email_already_in_use, email_address=user_email)
|
||||
form = ChangeNonGovEmailForm(User.already_registered, email_address=user_email)
|
||||
|
||||
if request.form.get('email_address', '').strip() == user_email:
|
||||
return redirect(url_for('.manage_users', service_id=current_service.id))
|
||||
@@ -193,7 +186,7 @@ def confirm_edit_user_email(service_id, user_id):
|
||||
))
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
user_api_client.update_user_attribute(str(user_id), email_address=new_email, updated_by=current_user.id)
|
||||
user.update(email_address=new_email, updated_by=current_user.id)
|
||||
except HTTPError as e:
|
||||
abort(500, e)
|
||||
else:
|
||||
@@ -253,7 +246,7 @@ def confirm_edit_user_mobile_number(service_id, user_id):
|
||||
))
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
user_api_client.update_user_attribute(str(user_id), mobile_number=new_number, updated_by=current_user.id)
|
||||
user.update(mobile_number=new_number, updated_by=current_user.id)
|
||||
except HTTPError as e:
|
||||
abort(500, e)
|
||||
else:
|
||||
|
||||
@@ -12,10 +12,10 @@ from flask import (
|
||||
from itsdangerous import SignatureExpired
|
||||
from notifications_utils.url_safe_token import check_token
|
||||
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import NewPasswordForm
|
||||
from app.main.views.two_factor import log_in_user
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
@main.route('/new-password/<path:token>', methods=['GET', 'POST'])
|
||||
@@ -28,7 +28,7 @@ def new_password(token):
|
||||
return redirect(url_for('.forgot_password'))
|
||||
|
||||
email_address = json.loads(token_data)['email']
|
||||
user = user_api_client.get_user_by_email(email_address)
|
||||
user = User.from_email_address(email_address)
|
||||
if user.password_changed_at and datetime.strptime(user.password_changed_at, '%Y-%m-%d %H:%M:%S.%f') > \
|
||||
datetime.strptime(json.loads(token_data)['created_at'], '%Y-%m-%d %H:%M:%S.%f'):
|
||||
flash('The link in the email has already been used')
|
||||
@@ -37,7 +37,7 @@ def new_password(token):
|
||||
form = NewPasswordForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
user_api_client.reset_failed_login_count(user.id)
|
||||
user.reset_failed_login_count()
|
||||
session['user_details'] = {
|
||||
'id': user.id,
|
||||
'email': user.email_address,
|
||||
@@ -47,7 +47,7 @@ def new_password(token):
|
||||
return log_in_user(user.id)
|
||||
else:
|
||||
# send user a 2fa sms code
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user.send_verify_code()
|
||||
return redirect(url_for('main.two_factor'))
|
||||
else:
|
||||
return render_template('views/new-password.html', token=token, form=form, user=user)
|
||||
|
||||
@@ -31,6 +31,7 @@ from app.main.forms import (
|
||||
SetLetterBranding,
|
||||
)
|
||||
from app.main.views.service_settings import get_branding_as_value_and_label
|
||||
from app.models.user import InvitedOrgUser, User
|
||||
from app.utils import user_has_permissions, user_is_platform_admin
|
||||
|
||||
|
||||
@@ -112,7 +113,7 @@ def invite_org_user(org_id):
|
||||
)
|
||||
if form.validate_on_submit():
|
||||
email_address = form.email_address.data
|
||||
invited_org_user = org_invite_api_client.create_invite(
|
||||
invited_org_user = InvitedOrgUser.create(
|
||||
current_user.id,
|
||||
org_id,
|
||||
email_address
|
||||
@@ -131,11 +132,9 @@ def invite_org_user(org_id):
|
||||
@login_required
|
||||
@user_has_permissions()
|
||||
def edit_user_org_permissions(org_id, user_id):
|
||||
user = user_api_client.get_user(user_id)
|
||||
|
||||
return render_template(
|
||||
'views/organisations/organisation/users/user/index.html',
|
||||
user=user
|
||||
user=User.from_id(user_id)
|
||||
)
|
||||
|
||||
|
||||
@@ -143,7 +142,7 @@ def edit_user_org_permissions(org_id, user_id):
|
||||
@login_required
|
||||
@user_has_permissions()
|
||||
def remove_user_from_organisation(org_id, user_id):
|
||||
user = user_api_client.get_user(user_id)
|
||||
user = User.from_id(user_id)
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
organisations_client.remove_user_from_organisation(org_id, user_id)
|
||||
|
||||
@@ -3,7 +3,6 @@ from datetime import datetime, timedelta
|
||||
from flask import abort, redirect, render_template, session, url_for
|
||||
from flask_login import current_user
|
||||
|
||||
from app import invite_api_client, org_invite_api_client, user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import (
|
||||
RegisterUserForm,
|
||||
@@ -11,6 +10,7 @@ from app.main.forms import (
|
||||
RegisterUserFromOrgInviteForm,
|
||||
)
|
||||
from app.main.views.verify import activate_user
|
||||
from app.models.user import InvitedOrgUser, InvitedUser, User
|
||||
|
||||
|
||||
@main.route('/register', methods=['GET', 'POST'])
|
||||
@@ -28,20 +28,18 @@ def register():
|
||||
|
||||
@main.route('/register-from-invite', methods=['GET', 'POST'])
|
||||
def register_from_invite():
|
||||
invited_user = session.get('invited_user')
|
||||
invited_user = InvitedUser.from_session()
|
||||
if not invited_user:
|
||||
abort(404)
|
||||
|
||||
is_sms_auth = invited_user['auth_type'] == 'sms_auth'
|
||||
|
||||
form = RegisterUserFromInviteForm(invited_user)
|
||||
|
||||
if form.validate_on_submit():
|
||||
if form.service.data != invited_user['service'] or form.email_address.data != invited_user['email_address']:
|
||||
if form.service.data != invited_user.service or form.email_address.data != invited_user.email_address:
|
||||
abort(400)
|
||||
_do_registration(form, send_email=False, send_sms=is_sms_auth)
|
||||
invite_api_client.accept_invite(invited_user['service'], invited_user['id'])
|
||||
if is_sms_auth:
|
||||
_do_registration(form, send_email=False, send_sms=invited_user.sms_auth)
|
||||
invited_user.accept_invite()
|
||||
if invited_user.sms_auth:
|
||||
return redirect(url_for('main.verify'))
|
||||
else:
|
||||
# we've already proven this user has email because they clicked the invite link,
|
||||
@@ -53,7 +51,7 @@ def register_from_invite():
|
||||
|
||||
@main.route('/register-from-org-invite', methods=['GET', 'POST'])
|
||||
def register_from_org_invite():
|
||||
invited_org_user = session.get('invited_org_user')
|
||||
invited_org_user = InvitedOrgUser.from_session()
|
||||
if not invited_org_user:
|
||||
abort(404)
|
||||
|
||||
@@ -63,34 +61,37 @@ def register_from_org_invite():
|
||||
form.auth_type.data = 'sms_auth'
|
||||
|
||||
if form.validate_on_submit():
|
||||
if (form.organisation.data != invited_org_user['organisation'] or
|
||||
form.email_address.data != invited_org_user['email_address']):
|
||||
if (form.organisation.data != invited_org_user.organisation or
|
||||
form.email_address.data != invited_org_user.email_address):
|
||||
abort(400)
|
||||
_do_registration(form, send_email=False, send_sms=True, organisation_id=invited_org_user['organisation'])
|
||||
org_invite_api_client.accept_invite(invited_org_user['organisation'], invited_org_user['id'])
|
||||
_do_registration(form, send_email=False, send_sms=True, organisation_id=invited_org_user.organisation)
|
||||
invited_org_user.accept_invite()
|
||||
|
||||
return redirect(url_for('main.verify'))
|
||||
return render_template('views/register-from-org-invite.html', invited_org_user=invited_org_user, form=form)
|
||||
|
||||
|
||||
def _do_registration(form, send_sms=True, send_email=True, organisation_id=None):
|
||||
if user_api_client.is_email_already_in_use(form.email_address.data):
|
||||
user = user_api_client.get_user_by_email(form.email_address.data)
|
||||
user = User.from_email_address_or_none(form.email_address.data)
|
||||
if user:
|
||||
if send_email:
|
||||
user_api_client.send_already_registered_email(user.id, user.email_address)
|
||||
user.send_already_registered_email()
|
||||
session['expiry_date'] = str(datetime.utcnow() + timedelta(hours=1))
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
else:
|
||||
user = user_api_client.register_user(form.name.data,
|
||||
form.email_address.data,
|
||||
form.mobile_number.data or None,
|
||||
form.password.data,
|
||||
form.auth_type.data)
|
||||
user = User.register(
|
||||
name=form.name.data,
|
||||
email_address=form.email_address.data,
|
||||
mobile_number=form.mobile_number.data,
|
||||
password=form.password.data,
|
||||
auth_type=form.auth_type.data,
|
||||
)
|
||||
|
||||
if send_email:
|
||||
user_api_client.send_verify_email(user.id, user.email_address)
|
||||
user.send_verify_email()
|
||||
|
||||
if send_sms:
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user.send_verify_code()
|
||||
session['expiry_date'] = str(datetime.utcnow() + timedelta(hours=1))
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
if organisation_id:
|
||||
|
||||
@@ -32,7 +32,6 @@ from app import (
|
||||
job_api_client,
|
||||
notification_api_client,
|
||||
service_api_client,
|
||||
user_api_client,
|
||||
)
|
||||
from app.main import main
|
||||
from app.main.forms import (
|
||||
@@ -41,6 +40,7 @@ from app.main.forms import (
|
||||
SetSenderForm,
|
||||
get_placeholder_form_instance,
|
||||
)
|
||||
from app.models.user import Users
|
||||
from app.s3_client.s3_csv_client import (
|
||||
s3download,
|
||||
s3upload,
|
||||
@@ -520,8 +520,6 @@ def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_
|
||||
if e.status_code != 404:
|
||||
raise
|
||||
|
||||
users = user_api_client.get_users_for_service(service_id=service_id)
|
||||
|
||||
statistics = service_api_client.get_service_statistics(service_id, today_only=True)
|
||||
remaining_messages = (current_service.message_limit - sum(stat['requested'] for stat in statistics.values()))
|
||||
|
||||
@@ -559,7 +557,7 @@ def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_
|
||||
max_initial_rows_shown=50,
|
||||
max_errors_shown=50,
|
||||
whitelist=itertools.chain.from_iterable(
|
||||
[user.name, user.mobile_number, user.email_address] for user in users
|
||||
[user.name, user.mobile_number, user.email_address] for user in Users(service_id)
|
||||
) if current_service.trial_mode else None,
|
||||
remaining_messages=remaining_messages,
|
||||
international_sms=current_service.has_permission('international_sms'),
|
||||
|
||||
@@ -10,9 +10,10 @@ from flask import (
|
||||
)
|
||||
from flask_login import current_user
|
||||
|
||||
from app import invite_api_client, login_manager, user_api_client
|
||||
from app import login_manager
|
||||
from app.main import main
|
||||
from app.main.forms import LoginForm
|
||||
from app.models.user import InvitedUser, User
|
||||
|
||||
|
||||
@main.route('/sign-in', methods=(['GET', 'POST']))
|
||||
@@ -24,26 +25,26 @@ def sign_in():
|
||||
|
||||
if form.validate_on_submit():
|
||||
|
||||
user = user_api_client.get_user_by_email_or_none(form.email_address.data)
|
||||
user = _get_and_verify_user(user, form.password.data)
|
||||
user = User.from_email_address_and_password_or_none(
|
||||
form.email_address.data, form.password.data
|
||||
)
|
||||
|
||||
if user and user.state == 'pending':
|
||||
return redirect(url_for('main.resend_email_verification'))
|
||||
|
||||
if user and session.get('invited_user'):
|
||||
invited_user = session.get('invited_user')
|
||||
if user.email_address.lower() != invited_user['email_address'].lower():
|
||||
invited_user = InvitedUser.from_session()
|
||||
if user.email_address.lower() != invited_user.email_address.lower():
|
||||
flash("You can't accept an invite for another person.")
|
||||
session.pop('invited_user', None)
|
||||
abort(403)
|
||||
else:
|
||||
invite_api_client.accept_invite(invited_user['service'], invited_user['id'])
|
||||
if user:
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
if user.is_active:
|
||||
if user.auth_type == "email_auth":
|
||||
return sign_in_email(user.id, user.email_address)
|
||||
else:
|
||||
return sign_in_sms(user.id, user.mobile_number)
|
||||
invited_user.accept_invite()
|
||||
if user and user.sign_in():
|
||||
if user.sms_auth:
|
||||
return redirect(url_for('.two_factor', next=request.args.get('next')))
|
||||
if user.email_auth:
|
||||
return redirect(url_for('.two_factor_email_sent'))
|
||||
|
||||
# Vague error message for login in case of user not known, locked, inactive or password not verified
|
||||
flash(Markup(
|
||||
@@ -62,35 +63,8 @@ def sign_in():
|
||||
)
|
||||
|
||||
|
||||
def sign_in_email(user_id, to):
|
||||
if request.args.get('next'):
|
||||
user_api_client.send_verify_code(user_id, 'email', None, request.args.get('next'))
|
||||
else:
|
||||
user_api_client.send_verify_code(user_id, 'email', None)
|
||||
return redirect(url_for('.two_factor_email_sent'))
|
||||
|
||||
|
||||
def sign_in_sms(user_id, to):
|
||||
user_api_client.send_verify_code(user_id, 'sms', to)
|
||||
if request.args.get('next'):
|
||||
return redirect(url_for('.two_factor', next=request.args.get('next')))
|
||||
else:
|
||||
return redirect(url_for('.two_factor'))
|
||||
|
||||
|
||||
@login_manager.unauthorized_handler
|
||||
def sign_in_again():
|
||||
return redirect(
|
||||
url_for('main.sign_in', next=request.path)
|
||||
)
|
||||
|
||||
|
||||
def _get_and_verify_user(user, password):
|
||||
if not user:
|
||||
return None
|
||||
elif user.is_locked():
|
||||
return None
|
||||
elif not user_api_client.verify_password(user.id, password):
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
|
||||
@@ -8,13 +8,14 @@ from flask import (
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import current_user, login_user
|
||||
from flask_login import current_user
|
||||
from itsdangerous import SignatureExpired
|
||||
from notifications_utils.url_safe_token import check_token
|
||||
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import TwoFactorForm
|
||||
from app.models.user import User
|
||||
from app.utils import redirect_to_sign_in
|
||||
|
||||
|
||||
@@ -30,7 +31,7 @@ def two_factor_email_sent():
|
||||
@main.route('/email-auth/<token>', methods=['GET'])
|
||||
def two_factor_email(token):
|
||||
if current_user.is_authenticated:
|
||||
return redirect_when_logged_in(current_user.id)
|
||||
return redirect_when_logged_in(platform_admin=current_user.platform_admin)
|
||||
|
||||
# checks url is valid, and hasn't timed out
|
||||
try:
|
||||
@@ -79,27 +80,27 @@ def _is_safe_redirect_url(target):
|
||||
|
||||
def log_in_user(user_id):
|
||||
try:
|
||||
user = user_api_client.get_user(user_id)
|
||||
user = User.from_id(user_id)
|
||||
# the user will have a new current_session_id set by the API - store it in the cookie for future requests
|
||||
session['current_session_id'] = user.current_session_id
|
||||
# Check if coming from new password page
|
||||
if 'password' in session.get('user_details', {}):
|
||||
user = user_api_client.update_password(user.id, password=session['user_details']['password'])
|
||||
activated_user = user_api_client.activate_user(user)
|
||||
login_user(activated_user)
|
||||
user.update_password(session['user_details']['password'])
|
||||
user.activate()
|
||||
user.login()
|
||||
finally:
|
||||
# get rid of anything in the session that we don't expect to have been set during register/sign in flow
|
||||
session.pop("user_details", None)
|
||||
session.pop("file_uploads", None)
|
||||
|
||||
return redirect_when_logged_in(user_id)
|
||||
return redirect_when_logged_in(platform_admin=user.platform_admin)
|
||||
|
||||
|
||||
def redirect_when_logged_in(user_id):
|
||||
def redirect_when_logged_in(platform_admin):
|
||||
next_url = request.args.get('next')
|
||||
if next_url and _is_safe_redirect_url(next_url):
|
||||
return redirect(next_url)
|
||||
if current_user.platform_admin:
|
||||
if platform_admin:
|
||||
return redirect(url_for('main.platform_admin'))
|
||||
|
||||
return redirect(url_for('main.show_accounts_or_dashboard'))
|
||||
|
||||
@@ -14,6 +14,7 @@ from app.main.forms import (
|
||||
ConfirmPasswordForm,
|
||||
TwoFactorForm,
|
||||
)
|
||||
from app.models.user import User
|
||||
from app.utils import user_is_gov_user
|
||||
|
||||
NEW_EMAIL = 'new-email'
|
||||
@@ -37,7 +38,7 @@ def user_profile_name():
|
||||
form = ChangeNameForm(new_name=current_user.name)
|
||||
|
||||
if form.validate_on_submit():
|
||||
user_api_client.update_user_attribute(current_user.id, name=form.new_name.data)
|
||||
current_user.update(name=form.new_name.data)
|
||||
return redirect(url_for('.user_profile'))
|
||||
|
||||
return render_template(
|
||||
@@ -52,9 +53,7 @@ def user_profile_name():
|
||||
@user_is_gov_user
|
||||
def user_profile_email():
|
||||
|
||||
def _is_email_already_in_use(email):
|
||||
return user_api_client.is_email_already_in_use(email)
|
||||
form = ChangeEmailForm(_is_email_already_in_use,
|
||||
form = ChangeEmailForm(User.already_registered,
|
||||
email_address=current_user.email_address)
|
||||
|
||||
if form.validate_on_submit():
|
||||
@@ -99,9 +98,8 @@ def user_profile_email_confirm(token):
|
||||
current_app.config['DANGEROUS_SALT'],
|
||||
current_app.config['EMAIL_EXPIRY_SECONDS'])
|
||||
token_data = json.loads(token_data)
|
||||
user_id = token_data['user_id']
|
||||
new_email = token_data['email']
|
||||
user_api_client.update_user_attribute(user_id, email_address=new_email)
|
||||
user = User.from_id(token_data['user_id'])
|
||||
user.update(email_address=token_data['email'])
|
||||
session.pop(NEW_EMAIL, None)
|
||||
|
||||
return redirect(url_for('.user_profile'))
|
||||
@@ -138,7 +136,7 @@ def user_profile_mobile_number_authenticate():
|
||||
|
||||
if form.validate_on_submit():
|
||||
session[NEW_MOBILE_PASSWORD_CONFIRMED] = True
|
||||
user_api_client.send_verify_code(current_user.id, 'sms', session[NEW_MOBILE])
|
||||
current_user.send_verify_code(to=session[NEW_MOBILE])
|
||||
return redirect(url_for('.user_profile_mobile_number_confirm'))
|
||||
|
||||
return render_template(
|
||||
@@ -163,13 +161,11 @@ def user_profile_mobile_number_confirm():
|
||||
form = TwoFactorForm(_check_code)
|
||||
|
||||
if form.validate_on_submit():
|
||||
user = user_api_client.get_user(current_user.id)
|
||||
# the user will have a new current_session_id set by the API - store it in the cookie for future requests
|
||||
session['current_session_id'] = user.current_session_id
|
||||
current_user.refresh_session_id()
|
||||
mobile_number = session[NEW_MOBILE]
|
||||
del session[NEW_MOBILE]
|
||||
del session[NEW_MOBILE_PASSWORD_CONFIRMED]
|
||||
user_api_client.update_user_attribute(current_user.id, mobile_number=mobile_number)
|
||||
current_user.update(mobile_number=mobile_number)
|
||||
return redirect(url_for('.user_profile'))
|
||||
|
||||
return render_template(
|
||||
|
||||
@@ -9,14 +9,13 @@ from flask import (
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import login_user
|
||||
from itsdangerous import SignatureExpired
|
||||
from notifications_utils.url_safe_token import check_token
|
||||
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.forms import TwoFactorForm
|
||||
from app.models.user import InvitedUser
|
||||
from app.models.user import InvitedUser, User
|
||||
from app.utils import redirect_to_sign_in
|
||||
|
||||
|
||||
@@ -54,7 +53,7 @@ def verify_email(token):
|
||||
|
||||
# token contains json blob of format: {'user_id': '...', 'secret_code': '...'} (secret_code is unused)
|
||||
token_data = json.loads(token_data)
|
||||
user = user_api_client.get_user(token_data['user_id'])
|
||||
user = User.from_id(token_data['user_id'])
|
||||
if not user:
|
||||
abort(404)
|
||||
|
||||
@@ -63,17 +62,17 @@ def verify_email(token):
|
||||
return redirect(url_for('main.sign_in'))
|
||||
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user.send_verify_code()
|
||||
return redirect(url_for('main.verify'))
|
||||
|
||||
|
||||
def activate_user(user_id):
|
||||
user = user_api_client.get_user(user_id)
|
||||
user = User.from_id(user_id)
|
||||
# the user will have a new current_session_id set by the API - store it in the cookie for future requests
|
||||
session['current_session_id'] = user.current_session_id
|
||||
organisation_id = session.get('organisation_id')
|
||||
activated_user = user_api_client.activate_user(user)
|
||||
login_user(activated_user)
|
||||
activated_user = user.activate()
|
||||
activated_user.login()
|
||||
|
||||
invited_user = session.get('invited_user')
|
||||
if invited_user:
|
||||
@@ -91,8 +90,8 @@ def activate_user(user_id):
|
||||
|
||||
|
||||
def _add_invited_user_to_service(invited_user):
|
||||
invitation = InvitedUser(**invited_user)
|
||||
user = user_api_client.get_user(session['user_id'])
|
||||
invitation = InvitedUser(invited_user)
|
||||
user = User.from_id(session['user_id'])
|
||||
service_id = invited_user['service']
|
||||
user_api_client.add_user_to_service(service_id, user.id, invitation.permissions, invitation.folder_permissions)
|
||||
return service_id
|
||||
|
||||
@@ -15,6 +15,7 @@ class JSONModel():
|
||||
def __getattr__(self, attr):
|
||||
if attr in self.ALLOWED_PROPERTIES:
|
||||
return self._dict[attr]
|
||||
return
|
||||
raise AttributeError('`{}` is not a {} attribute'.format(
|
||||
attr,
|
||||
self.__class__.__name__.lower(),
|
||||
@@ -25,3 +26,7 @@ class JSONModel():
|
||||
return next(thing for thing in things if thing['id'] == str(id))
|
||||
except StopIteration:
|
||||
abort(404)
|
||||
|
||||
|
||||
class InviteTokenError(Exception):
|
||||
pass
|
||||
|
||||
@@ -131,20 +131,18 @@ class Organisation(JSONModel):
|
||||
return [s for s in self.services if not s['active'] or s['restricted']]
|
||||
|
||||
@cached_property
|
||||
def active_users(self):
|
||||
# need to put this here to prevent cyclical import
|
||||
from app.notify_client.user_api_client import user_api_client
|
||||
return user_api_client.get_users_for_organisation(org_id=self.id)
|
||||
def invited_users(self):
|
||||
from app.models.user import OrganisationInvitedUsers
|
||||
return OrganisationInvitedUsers(self.id)
|
||||
|
||||
@cached_property
|
||||
def invited_users(self):
|
||||
# need to put this here to prevent cyclical import
|
||||
from app.notify_client.org_invite_api_client import org_invite_api_client
|
||||
return org_invite_api_client.get_invites_for_organisation(org_id=self.id)
|
||||
def active_users(self):
|
||||
from app.models.user import OrganisationUsers
|
||||
return OrganisationUsers(self.id)
|
||||
|
||||
@cached_property
|
||||
def team_members(self):
|
||||
return sorted(
|
||||
self.active_users + [i for i in self.invited_users if i.status != 'accepted'],
|
||||
key=lambda user: user.email_address,
|
||||
self.invited_users + self.active_users,
|
||||
key=lambda user: user.email_address.lower(),
|
||||
)
|
||||
|
||||
44
app/models/roles_and_permissions.py
Normal file
44
app/models/roles_and_permissions.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from itertools import chain
|
||||
|
||||
roles = {
|
||||
'send_messages': ['send_texts', 'send_emails', 'send_letters'],
|
||||
'manage_templates': ['manage_templates'],
|
||||
'manage_service': ['manage_users', 'manage_settings'],
|
||||
'manage_api_keys': ['manage_api_keys'],
|
||||
'view_activity': ['view_activity'],
|
||||
}
|
||||
|
||||
# same dict as above, but flipped round
|
||||
roles_by_permission = {
|
||||
permission: next(
|
||||
role for role, permissions in roles.items() if permission in permissions
|
||||
) for permission in chain(*list(roles.values()))
|
||||
}
|
||||
|
||||
all_permissions = set(roles_by_permission.values())
|
||||
|
||||
permissions = (
|
||||
('view_activity', 'See dashboard'),
|
||||
('send_messages', 'Send messages'),
|
||||
('manage_templates', 'Add and edit templates'),
|
||||
('manage_service', 'Manage settings, team and usage'),
|
||||
('manage_api_keys', 'Manage API integration'),
|
||||
)
|
||||
|
||||
|
||||
def translate_permissions_from_db_to_admin_roles(permissions):
|
||||
"""
|
||||
Given a list of database permissions, return a set of roles
|
||||
|
||||
look them up in roles_by_permission, falling back to just passing through from the api if they aren't in the dict
|
||||
"""
|
||||
return {roles_by_permission.get(permission, permission) for permission in permissions}
|
||||
|
||||
|
||||
def translate_permissions_from_admin_roles_to_db(permissions):
|
||||
"""
|
||||
Given a list of admin roles (ie: checkboxes on a permissions edit page for example), return a set of db permissions
|
||||
|
||||
Looks them up in the roles dict, falling back to just passing through if they're not recognised.
|
||||
"""
|
||||
return set(chain.from_iterable(roles.get(permission, [permission]) for permission in permissions))
|
||||
@@ -4,6 +4,7 @@ from werkzeug.utils import cached_property
|
||||
|
||||
from app.models import JSONModel
|
||||
from app.models.organisation import Organisation
|
||||
from app.models.user import InvitedUsers, User, Users
|
||||
from app.notify_client.api_key_api_client import api_key_api_client
|
||||
from app.notify_client.billing_api_client import billing_api_client
|
||||
from app.notify_client.email_branding_client import email_branding_client
|
||||
@@ -16,7 +17,6 @@ from app.notify_client.service_api_client import service_api_client
|
||||
from app.notify_client.template_folder_api_client import (
|
||||
template_folder_api_client,
|
||||
)
|
||||
from app.notify_client.user_api_client import user_api_client
|
||||
from app.utils import get_default_sms_sender
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ class Service(JSONModel):
|
||||
if 'permissions' not in self._dict:
|
||||
self.permissions = {'email', 'sms', 'letter'}
|
||||
|
||||
@classmethod
|
||||
def from_id(cls, service_id):
|
||||
return cls(service_api_client.get_service(service_id)['data'])
|
||||
|
||||
def update(self, **kwargs):
|
||||
return service_api_client.update_service(self.id, **kwargs)
|
||||
|
||||
@@ -99,11 +103,11 @@ class Service(JSONModel):
|
||||
|
||||
@cached_property
|
||||
def invited_users(self):
|
||||
return invite_api_client.get_invites_for_service(service_id=self.id)
|
||||
return InvitedUsers(self.id)
|
||||
|
||||
@cached_property
|
||||
def active_users(self):
|
||||
return user_api_client.get_users_for_service(service_id=self.id)
|
||||
return Users(self.id)
|
||||
|
||||
@cached_property
|
||||
def team_members(self):
|
||||
@@ -114,16 +118,12 @@ class Service(JSONModel):
|
||||
|
||||
@cached_property
|
||||
def has_team_members(self):
|
||||
return (
|
||||
user_api_client.get_count_of_users_with_permission(
|
||||
self.id, 'manage_service'
|
||||
) + invite_api_client.get_count_of_invites_with_permission(
|
||||
self.id, 'manage_service'
|
||||
)
|
||||
) > 1
|
||||
return len([
|
||||
user for user in self.team_members
|
||||
if user.has_permission_for_service(self.id, 'manage_service')
|
||||
]) > 1
|
||||
|
||||
def cancel_invite(self, invited_user_id):
|
||||
|
||||
if str(invited_user_id) not in {user.id for user in self.invited_users}:
|
||||
abort(404)
|
||||
|
||||
@@ -137,7 +137,7 @@ class Service(JSONModel):
|
||||
if str(user_id) not in {user.id for user in self.active_users}:
|
||||
abort(404)
|
||||
|
||||
return user_api_client.get_user(user_id)
|
||||
return User.from_id(user_id)
|
||||
|
||||
@cached_property
|
||||
def all_templates(self):
|
||||
|
||||
@@ -1,37 +1,22 @@
|
||||
from itertools import chain
|
||||
from collections.abc import Sequence
|
||||
|
||||
from flask import abort, request, session
|
||||
from flask_login import AnonymousUserMixin, UserMixin
|
||||
from flask import abort, current_app, request, session
|
||||
from flask_login import AnonymousUserMixin, UserMixin, login_user
|
||||
from notifications_python_client.errors import HTTPError
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
from app.models import JSONModel
|
||||
from app.models.organisation import Organisation
|
||||
from app.notify_client.organisations_api_client import organisations_client
|
||||
from app.utils import is_gov_user
|
||||
|
||||
roles = {
|
||||
'send_messages': ['send_texts', 'send_emails', 'send_letters'],
|
||||
'manage_templates': ['manage_templates'],
|
||||
'manage_service': ['manage_users', 'manage_settings'],
|
||||
'manage_api_keys': ['manage_api_keys'],
|
||||
'view_activity': ['view_activity'],
|
||||
}
|
||||
|
||||
# same dict as above, but flipped round
|
||||
roles_by_permission = {
|
||||
permission: next(
|
||||
role for role, permissions in roles.items() if permission in permissions
|
||||
) for permission in chain(*list(roles.values()))
|
||||
}
|
||||
|
||||
all_permissions = set(roles_by_permission.values())
|
||||
|
||||
permissions = (
|
||||
('view_activity', 'See dashboard'),
|
||||
('send_messages', 'Send messages'),
|
||||
('manage_templates', 'Add and edit templates'),
|
||||
('manage_service', 'Manage settings, team and usage'),
|
||||
('manage_api_keys', 'Manage API integration'),
|
||||
from app.models.roles_and_permissions import (
|
||||
all_permissions,
|
||||
translate_permissions_from_db_to_admin_roles,
|
||||
)
|
||||
from app.notify_client import InviteTokenError
|
||||
from app.notify_client.invite_api_client import invite_api_client
|
||||
from app.notify_client.org_invite_api_client import org_invite_api_client
|
||||
from app.notify_client.organisations_api_client import organisations_client
|
||||
from app.notify_client.user_api_client import user_api_client
|
||||
from app.utils import is_gov_user
|
||||
|
||||
|
||||
def _get_service_id_from_view_args():
|
||||
@@ -42,43 +27,66 @@ def _get_org_id_from_view_args():
|
||||
return str(request.view_args.get('org_id', '')) or None
|
||||
|
||||
|
||||
def translate_permissions_from_db_to_admin_roles(permissions):
|
||||
"""
|
||||
Given a list of database permissions, return a set of roles
|
||||
class User(JSONModel, UserMixin):
|
||||
|
||||
look them up in roles_by_permission, falling back to just passing through from the api if they aren't in the dict
|
||||
"""
|
||||
return {roles_by_permission.get(permission, permission) for permission in permissions}
|
||||
ALLOWED_PROPERTIES = {
|
||||
'id',
|
||||
'name',
|
||||
'email_address',
|
||||
'auth_type',
|
||||
'current_session_id',
|
||||
'failed_login_count',
|
||||
'logged_in_at',
|
||||
'mobile_number',
|
||||
'organisations',
|
||||
'password_changed_at',
|
||||
'permissions',
|
||||
'platform_admin',
|
||||
'services',
|
||||
'state',
|
||||
}
|
||||
|
||||
def __init__(self, _dict):
|
||||
super().__init__(_dict)
|
||||
self.permissions = _dict.get('permissions', {})
|
||||
self.max_failed_login_count = current_app.config['MAX_FAILED_LOGIN_COUNT']
|
||||
|
||||
def translate_permissions_from_admin_roles_to_db(permissions):
|
||||
"""
|
||||
Given a list of admin roles (ie: checkboxes on a permissions edit page for example), return a set of db permissions
|
||||
@classmethod
|
||||
def from_id(cls, user_id):
|
||||
return cls(user_api_client.get_user(user_id))
|
||||
|
||||
Looks them up in the roles dict, falling back to just passing through if they're not recognised.
|
||||
"""
|
||||
return set(chain.from_iterable(roles.get(permission, [permission]) for permission in permissions))
|
||||
@classmethod
|
||||
def from_email_address(cls, email_address):
|
||||
return cls(user_api_client.get_user_by_email(email_address))
|
||||
|
||||
@classmethod
|
||||
def from_email_address_or_none(cls, email_address):
|
||||
response = user_api_client.get_user_by_email_or_none(email_address)
|
||||
if response:
|
||||
return cls(response)
|
||||
return None
|
||||
|
||||
class User(UserMixin):
|
||||
def __init__(self, fields, max_failed_login_count=3):
|
||||
self.id = fields.get('id')
|
||||
self.name = fields.get('name')
|
||||
self.email_address = fields.get('email_address')
|
||||
self.mobile_number = fields.get('mobile_number')
|
||||
self.password_changed_at = fields.get('password_changed_at')
|
||||
self._set_permissions(fields.get('permissions', {}))
|
||||
self.auth_type = fields.get('auth_type')
|
||||
self.failed_login_count = fields.get('failed_login_count')
|
||||
self.state = fields.get('state')
|
||||
self.max_failed_login_count = max_failed_login_count
|
||||
self.logged_in_at = fields.get('logged_in_at')
|
||||
self.platform_admin = fields.get('platform_admin')
|
||||
self.current_session_id = fields.get('current_session_id')
|
||||
self.services = fields.get('services', [])
|
||||
self.organisations = fields.get('organisations', [])
|
||||
@staticmethod
|
||||
def already_registered(email_address):
|
||||
return bool(User.from_email_address_or_none(email_address))
|
||||
|
||||
def _set_permissions(self, permissions_by_service):
|
||||
@classmethod
|
||||
def from_email_address_and_password_or_none(cls, email_address, password):
|
||||
user = cls.from_email_address_or_none(email_address)
|
||||
if not user:
|
||||
return None
|
||||
if user.locked:
|
||||
return None
|
||||
if not user_api_client.verify_password(user.id, password):
|
||||
return None
|
||||
return user
|
||||
|
||||
@property
|
||||
def permissions(self):
|
||||
return self._permissions
|
||||
|
||||
@permissions.setter
|
||||
def permissions(self, permissions_by_service):
|
||||
"""
|
||||
Permissions is a dict {'service_id': ['permission a', 'permission b', 'permission c']}
|
||||
|
||||
@@ -96,13 +104,61 @@ class User(UserMixin):
|
||||
in permissions_by_service.items()
|
||||
}
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
def update(self, **kwargs):
|
||||
response = user_api_client.update_user_attribute(self.id, **kwargs)
|
||||
self.__init__(response)
|
||||
|
||||
def update_password(self, password):
|
||||
response = user_api_client.update_password(self.id, password)
|
||||
self.__init__(response)
|
||||
|
||||
def set_permissions(self, service_id, permissions, folder_permissions):
|
||||
user_api_client.set_user_permissions(
|
||||
self.id,
|
||||
service_id,
|
||||
permissions=permissions,
|
||||
folder_permissions=folder_permissions,
|
||||
)
|
||||
|
||||
def logged_in_elsewhere(self):
|
||||
# if the current user (ie: db object) has no session, they've never logged in before
|
||||
return self.current_session_id is not None and session.get('current_session_id') != self.current_session_id
|
||||
|
||||
def activate(self):
|
||||
if self.state == 'pending':
|
||||
user_data = user_api_client.activate_user(self.id)
|
||||
return self.__class__(user_data['data'])
|
||||
else:
|
||||
return self
|
||||
|
||||
def login(self):
|
||||
login_user(self)
|
||||
|
||||
def sign_in(self):
|
||||
|
||||
session['user_details'] = {"email": self.email_address, "id": self.id}
|
||||
|
||||
if not self.is_active:
|
||||
return False
|
||||
|
||||
if self.email_auth:
|
||||
user_api_client.send_verify_code(self.id, 'email', None, request.args.get('next'))
|
||||
if self.sms_auth:
|
||||
user_api_client.send_verify_code(self.id, 'sms', self.mobile_number)
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def sms_auth(self):
|
||||
return self.auth_type == 'sms_auth'
|
||||
|
||||
@property
|
||||
def email_auth(self):
|
||||
return self.auth_type == 'email_auth'
|
||||
|
||||
def reset_failed_login_count(self):
|
||||
user_api_client.reset_failed_login_count(self.id)
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.state == 'active'
|
||||
@@ -118,14 +174,6 @@ class User(UserMixin):
|
||||
super(User, self).is_authenticated
|
||||
)
|
||||
|
||||
@property
|
||||
def permissions(self):
|
||||
return self._permissions
|
||||
|
||||
@permissions.setter
|
||||
def permissions(self, permissions):
|
||||
raise AttributeError("Read only property")
|
||||
|
||||
def has_permissions(self, *permissions, restrict_admin_usage=False):
|
||||
unknown_permissions = set(permissions) - all_permissions
|
||||
if unknown_permissions:
|
||||
@@ -181,7 +229,8 @@ class User(UserMixin):
|
||||
if not self.belongs_to_service(service_id):
|
||||
abort(403)
|
||||
|
||||
def is_locked(self):
|
||||
@property
|
||||
def locked(self):
|
||||
return self.failed_login_count >= self.max_failed_login_count
|
||||
|
||||
@property
|
||||
@@ -225,38 +274,126 @@ class User(UserMixin):
|
||||
dct['password'] = self._password
|
||||
return dct
|
||||
|
||||
@classmethod
|
||||
def register(
|
||||
cls,
|
||||
name,
|
||||
email_address,
|
||||
mobile_number,
|
||||
password,
|
||||
auth_type,
|
||||
):
|
||||
return cls(user_api_client.register_user(
|
||||
name,
|
||||
email_address,
|
||||
mobile_number or None,
|
||||
password,
|
||||
auth_type,
|
||||
))
|
||||
|
||||
def set_password(self, pwd):
|
||||
self._password = pwd
|
||||
|
||||
def send_verify_email(self):
|
||||
user_api_client.send_verify_email(self.id, self.email_address)
|
||||
|
||||
class InvitedUser(object):
|
||||
def send_verify_code(self, to=None):
|
||||
user_api_client.send_verify_code(self.id, 'sms', to or self.mobile_number)
|
||||
|
||||
def __init__(self,
|
||||
id,
|
||||
service,
|
||||
from_user,
|
||||
email_address,
|
||||
permissions,
|
||||
status,
|
||||
created_at,
|
||||
auth_type,
|
||||
folder_permissions):
|
||||
self.id = id
|
||||
self.service = str(service)
|
||||
self.from_user = from_user
|
||||
self.email_address = email_address
|
||||
def send_already_registered_email(self):
|
||||
user_api_client.send_already_registered_email(self.id, self.email_address)
|
||||
|
||||
def refresh_session_id(self):
|
||||
self.current_session_id = user_api_client.get_user(self.id).get('current_session_id')
|
||||
session['current_session_id'] = self.current_session_id
|
||||
|
||||
|
||||
class InvitedUser(JSONModel):
|
||||
|
||||
ALLOWED_PROPERTIES = {
|
||||
'id',
|
||||
'service',
|
||||
'email_address',
|
||||
'permissions',
|
||||
'status',
|
||||
'created_at',
|
||||
'auth_type',
|
||||
'folder_permissions',
|
||||
}
|
||||
|
||||
def __init__(self, _dict):
|
||||
super().__init__(_dict)
|
||||
self.permissions = _dict.get('permissions') or []
|
||||
self._from_user = _dict['from_user']
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
invite_from_id,
|
||||
service_id,
|
||||
email_address,
|
||||
permissions,
|
||||
auth_type,
|
||||
folder_permissions,
|
||||
):
|
||||
return cls(invite_api_client.create_invite(
|
||||
invite_from_id,
|
||||
service_id,
|
||||
email_address,
|
||||
permissions,
|
||||
auth_type,
|
||||
folder_permissions,
|
||||
))
|
||||
|
||||
def accept_invite(self):
|
||||
invite_api_client.accept_invite(self.service, self.id)
|
||||
|
||||
def add_to_service(self):
|
||||
user_api_client.add_user_to_service(
|
||||
self.service,
|
||||
self.id,
|
||||
self.permissions,
|
||||
self.folder_permissions,
|
||||
)
|
||||
|
||||
@property
|
||||
def permissions(self):
|
||||
return self._permissions
|
||||
|
||||
@permissions.setter
|
||||
def permissions(self, permissions):
|
||||
if isinstance(permissions, list):
|
||||
self.permissions = permissions
|
||||
self._permissions = permissions
|
||||
else:
|
||||
if permissions:
|
||||
self.permissions = permissions.split(',')
|
||||
self._permissions = permissions.split(',')
|
||||
self._permissions = translate_permissions_from_db_to_admin_roles(self.permissions)
|
||||
|
||||
@property
|
||||
def from_user(self):
|
||||
return User.from_id(self._from_user)
|
||||
|
||||
@property
|
||||
def sms_auth(self):
|
||||
return self.auth_type == 'sms_auth'
|
||||
|
||||
@property
|
||||
def email_auth(self):
|
||||
return self.auth_type == 'email_auth'
|
||||
|
||||
@classmethod
|
||||
def from_token(cls, token):
|
||||
try:
|
||||
return cls(invite_api_client.check_token(token))
|
||||
except HTTPError as exception:
|
||||
if exception.status_code == 400 and 'invitation' in exception.message:
|
||||
raise InviteTokenError(exception.message['invitation'])
|
||||
else:
|
||||
self.permissions = []
|
||||
self.status = status
|
||||
self.created_at = created_at
|
||||
self.auth_type = auth_type
|
||||
self.permissions = translate_permissions_from_db_to_admin_roles(self.permissions)
|
||||
self.folder_permissions = folder_permissions
|
||||
raise exception
|
||||
|
||||
@classmethod
|
||||
def from_session(cls):
|
||||
invited_user = session.get('invited_user')
|
||||
return cls(invited_user) if invited_user else None
|
||||
|
||||
def has_permissions(self, *permissions):
|
||||
if self.status == 'cancelled':
|
||||
@@ -271,12 +408,12 @@ class InvitedUser(object):
|
||||
def __eq__(self, other):
|
||||
return ((self.id,
|
||||
self.service,
|
||||
self.from_user,
|
||||
self._from_user,
|
||||
self.email_address,
|
||||
self.auth_type,
|
||||
self.status) == (other.id,
|
||||
other.service,
|
||||
other.from_user,
|
||||
other._from_user,
|
||||
other.email_address,
|
||||
other.auth_type,
|
||||
other.status))
|
||||
@@ -284,7 +421,7 @@ class InvitedUser(object):
|
||||
def serialize(self, permissions_as_string=False):
|
||||
data = {'id': self.id,
|
||||
'service': self.service,
|
||||
'from_user': self.from_user,
|
||||
'from_user': self._from_user,
|
||||
'email_address': self.email_address,
|
||||
'status': self.status,
|
||||
'created_at': str(self.created_at),
|
||||
@@ -302,43 +439,118 @@ class InvitedUser(object):
|
||||
return [{'id': x} for x in self.folder_permissions]
|
||||
|
||||
|
||||
class InvitedOrgUser(object):
|
||||
class InvitedOrgUser(JSONModel):
|
||||
|
||||
def __init__(self, id, organisation, invited_by, email_address, status, created_at):
|
||||
self.id = id
|
||||
self.organisation = str(organisation)
|
||||
self.invited_by = invited_by
|
||||
self.email_address = email_address
|
||||
self.status = status
|
||||
self.created_at = created_at
|
||||
ALLOWED_PROPERTIES = {
|
||||
'id',
|
||||
'organisation',
|
||||
'email_address',
|
||||
'status',
|
||||
'created_at',
|
||||
}
|
||||
|
||||
def __init__(self, _dict):
|
||||
super().__init__(_dict)
|
||||
self._invited_by = _dict['invited_by']
|
||||
|
||||
def __eq__(self, other):
|
||||
return ((self.id,
|
||||
self.organisation,
|
||||
self.invited_by,
|
||||
self._invited_by,
|
||||
self.email_address,
|
||||
self.status) == (other.id,
|
||||
other.organisation,
|
||||
other.invited_by,
|
||||
other._invited_by,
|
||||
other.email_address,
|
||||
other.status))
|
||||
|
||||
@classmethod
|
||||
def create(cls, invite_from_id, org_id, email_address):
|
||||
return cls(org_invite_api_client.create_invite(
|
||||
invite_from_id, org_id, email_address
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def from_session(cls):
|
||||
invited_org_user = session.get('invited_org_user')
|
||||
return cls(invited_org_user) if invited_org_user else None
|
||||
|
||||
def serialize(self, permissions_as_string=False):
|
||||
data = {'id': self.id,
|
||||
'organisation': self.organisation,
|
||||
'invited_by': self.invited_by,
|
||||
'invited_by': self._invited_by,
|
||||
'email_address': self.email_address,
|
||||
'status': self.status,
|
||||
'created_at': str(self.created_at)
|
||||
}
|
||||
return data
|
||||
|
||||
@property
|
||||
def invited_by(self):
|
||||
return User.from_id(self._invited_by)
|
||||
|
||||
@classmethod
|
||||
def from_token(cls, token):
|
||||
try:
|
||||
return cls(org_invite_api_client.check_token(token))
|
||||
except HTTPError as exception:
|
||||
if exception.status_code == 400 and 'invitation' in exception.message:
|
||||
raise InviteTokenError(exception.message['invitation'])
|
||||
else:
|
||||
raise exception
|
||||
|
||||
def accept_invite(self):
|
||||
org_invite_api_client.accept_invite(self.organisation, self.id)
|
||||
|
||||
def add_to_organisation(self):
|
||||
user_api_client.add_user_to_organisation(self.organisation, self.id)
|
||||
|
||||
|
||||
class AnonymousUser(AnonymousUserMixin):
|
||||
# set the anonymous user so that if a new browser hits us we don't error http://stackoverflow.com/a/19275188
|
||||
|
||||
def logged_in_elsewhere(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def default_organisation(self):
|
||||
return Organisation(None)
|
||||
|
||||
|
||||
class Users(Sequence):
|
||||
|
||||
client = user_api_client.get_users_for_service
|
||||
model = User
|
||||
|
||||
def __init__(self, service_id):
|
||||
self.users = self.client(service_id)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.model(self.users[index])
|
||||
|
||||
def __len__(self):
|
||||
return len(self.users)
|
||||
|
||||
def __add__(self, other):
|
||||
return list(self) + list(other)
|
||||
|
||||
|
||||
class OrganisationUsers(Users):
|
||||
client = user_api_client.get_users_for_organisation
|
||||
|
||||
|
||||
class InvitedUsers(Users):
|
||||
|
||||
client = invite_api_client.get_invites_for_service
|
||||
model = InvitedUser
|
||||
|
||||
def __init__(self, service_id):
|
||||
self.users = [
|
||||
user for user in self.client(service_id)
|
||||
if user['status'] != 'accepted'
|
||||
]
|
||||
|
||||
|
||||
class OrganisationInvitedUsers(InvitedUsers):
|
||||
client = org_invite_api_client.get_invites_for_organisation
|
||||
model = InvitedOrgUser
|
||||
|
||||
@@ -60,3 +60,7 @@ class NotifyAdminAPIClient(BaseAPIClient):
|
||||
def delete(self, *args, **kwargs):
|
||||
self.check_inactive_service()
|
||||
return super().delete(*args, **kwargs)
|
||||
|
||||
|
||||
class InviteTokenError(Exception):
|
||||
pass
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from app.models.user import (
|
||||
InvitedUser,
|
||||
from app.models.roles_and_permissions import (
|
||||
roles,
|
||||
translate_permissions_from_admin_roles_to_db,
|
||||
)
|
||||
@@ -31,16 +30,9 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
}
|
||||
data = _attach_current_user(data)
|
||||
resp = self.post(url='/service/{}/invite'.format(service_id), data=data)
|
||||
return InvitedUser(**resp['data'])
|
||||
return resp['data']
|
||||
|
||||
def get_invites_for_service(self, service_id):
|
||||
return [
|
||||
InvitedUser(**invite)
|
||||
for invite in self._get_invites_for_service(service_id)
|
||||
if invite['status'] != 'accepted'
|
||||
]
|
||||
|
||||
def _get_invites_for_service(self, service_id):
|
||||
return self.get(
|
||||
'/service/{}/invite'.format(service_id)
|
||||
)['data']
|
||||
@@ -54,8 +46,7 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
])
|
||||
|
||||
def check_token(self, token):
|
||||
resp = self.get(url='/invite/service/{}'.format(token))
|
||||
return InvitedUser(**resp['data'])
|
||||
return self.get(url='/invite/service/{}'.format(token))['data']
|
||||
|
||||
def cancel_invited_user(self, service_id, invited_user_id):
|
||||
data = {'status': 'cancelled'}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from app.models.user import InvitedOrgUser
|
||||
from app.notify_client import NotifyAdminAPIClient, _attach_current_user
|
||||
|
||||
|
||||
@@ -17,18 +16,16 @@ class OrgInviteApiClient(NotifyAdminAPIClient):
|
||||
}
|
||||
data = _attach_current_user(data)
|
||||
resp = self.post(url='/organisation/{}/invite'.format(org_id), data=data)
|
||||
return InvitedOrgUser(**resp['data'])
|
||||
return resp['data']
|
||||
|
||||
def get_invites_for_organisation(self, org_id):
|
||||
endpoint = '/organisation/{}/invite'.format(org_id)
|
||||
resp = self.get(endpoint)
|
||||
invites = resp['data']
|
||||
invited_users = self._get_invited_org_users(invites)
|
||||
return invited_users
|
||||
return resp['data']
|
||||
|
||||
def check_token(self, token):
|
||||
resp = self.get(url='/invite/organisation/{}'.format(token))
|
||||
return InvitedOrgUser(**resp['data'])
|
||||
return resp['data']
|
||||
|
||||
def cancel_invited_user(self, org_id, invited_user_id):
|
||||
data = {'status': 'cancelled'}
|
||||
@@ -41,12 +38,5 @@ class OrgInviteApiClient(NotifyAdminAPIClient):
|
||||
self.post(url='/organisation/{0}/invite/{1}'.format(org_id, invited_user_id),
|
||||
data=data)
|
||||
|
||||
def _get_invited_org_users(self, invites):
|
||||
invited_users = []
|
||||
for invite in invites:
|
||||
invited_user = InvitedOrgUser(**invite)
|
||||
invited_users.append(invited_user)
|
||||
return invited_users
|
||||
|
||||
|
||||
org_invite_api_client = OrgInviteApiClient()
|
||||
|
||||
@@ -2,9 +2,7 @@ from itertools import chain
|
||||
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app.models.user import (
|
||||
User,
|
||||
roles,
|
||||
from app.models.roles_and_permissions import (
|
||||
translate_permissions_from_admin_roles_to_db,
|
||||
)
|
||||
from app.notify_client import NotifyAdminAPIClient, cache
|
||||
@@ -22,8 +20,6 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
|
||||
def init_app(self, app):
|
||||
super().init_app(app)
|
||||
|
||||
self.max_failed_login_count = app.config["MAX_FAILED_LOGIN_COUNT"]
|
||||
self.admin_url = app.config['ADMIN_BASE_URL']
|
||||
|
||||
def register_user(self, name, email_address, mobile_number, password, auth_type):
|
||||
@@ -35,10 +31,10 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
"auth_type": auth_type
|
||||
}
|
||||
user_data = self.post("/user", data)
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
return user_data['data']
|
||||
|
||||
def get_user(self, user_id):
|
||||
return User(self._get_user(user_id)['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
return self._get_user(user_id)['data']
|
||||
|
||||
@cache.set('user-{user_id}')
|
||||
def _get_user(self, user_id):
|
||||
@@ -46,7 +42,7 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
|
||||
def get_user_by_email(self, email_address):
|
||||
user_data = self.get('/user/email', params={'email': email_address})
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
return user_data['data']
|
||||
|
||||
def get_user_by_email_or_none(self, email_address):
|
||||
try:
|
||||
@@ -54,13 +50,7 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
except HTTPError as e:
|
||||
if e.status_code == 404:
|
||||
return None
|
||||
|
||||
def get_users(self):
|
||||
users_data = self.get("/user")['data']
|
||||
users = []
|
||||
for user in users_data:
|
||||
users.append(User(user, max_failed_login_count=self.max_failed_login_count))
|
||||
return users
|
||||
raise e
|
||||
|
||||
@cache.delete('user-{user_id}')
|
||||
def update_user_attribute(self, user_id, **kwargs):
|
||||
@@ -73,20 +63,20 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
|
||||
url = "/user/{}".format(user_id)
|
||||
user_data = self.post(url, data=data)
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
return user_data['data']
|
||||
|
||||
@cache.delete('user-{user_id}')
|
||||
def reset_failed_login_count(self, user_id):
|
||||
url = "/user/{}/reset-failed-login-count".format(user_id)
|
||||
user_data = self.post(url, data={})
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
return user_data['data']
|
||||
|
||||
@cache.delete('user-{user_id}')
|
||||
def update_password(self, user_id, password):
|
||||
data = {"_password": password}
|
||||
url = "/user/{}/update-password".format(user_id)
|
||||
user_data = self.post(url, data=data)
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
return user_data['data']
|
||||
|
||||
@cache.delete('user-{user_id}')
|
||||
def verify_password(self, user_id, password):
|
||||
@@ -132,21 +122,11 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
|
||||
def get_users_for_service(self, service_id):
|
||||
endpoint = '/service/{}/users'.format(service_id)
|
||||
resp = self.get(endpoint)
|
||||
return [User(data) for data in resp['data']]
|
||||
|
||||
def get_count_of_users_with_permission(self, service_id, permission):
|
||||
if permission not in roles.keys():
|
||||
raise TypeError('{} is not a valid permission'.format(permission))
|
||||
return len([
|
||||
user for user in self.get_users_for_service(service_id)
|
||||
if user.has_permission_for_service(service_id, permission)
|
||||
])
|
||||
return self.get(endpoint)['data']
|
||||
|
||||
def get_users_for_organisation(self, org_id):
|
||||
endpoint = '/organisations/{}/users'.format(org_id)
|
||||
resp = self.get(endpoint)
|
||||
return [User(data) for data in resp['data']]
|
||||
return self.get(endpoint)['data']
|
||||
|
||||
@cache.delete('service-{service_id}')
|
||||
@cache.delete('service-{service_id}-template-folders')
|
||||
@@ -164,7 +144,7 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
@cache.delete('user-{user_id}')
|
||||
def add_user_to_organisation(self, org_id, user_id):
|
||||
resp = self.post('/organisations/{}/users/{}'.format(org_id, user_id), data={})
|
||||
return User(resp['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
return resp['data']
|
||||
|
||||
@cache.delete('service-{service_id}-template-folders')
|
||||
@cache.delete('user-{user_id}')
|
||||
@@ -191,20 +171,8 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
users = self.post(endpoint, data=data)
|
||||
return users
|
||||
|
||||
def is_email_already_in_use(self, email_address):
|
||||
if self.get_user_by_email_or_none(email_address):
|
||||
return True
|
||||
return False
|
||||
|
||||
def activate_user(self, user):
|
||||
if user.state == 'pending':
|
||||
user_data = self._activate_user(user.id)
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
else:
|
||||
return user
|
||||
|
||||
@cache.delete('user-{user_id}')
|
||||
def _activate_user(self, user_id):
|
||||
def activate_user(self, user_id):
|
||||
return self.post("/user/{}/activate".format(user_id), data=None)
|
||||
|
||||
def send_change_email_verification(self, user_id, new_email):
|
||||
|
||||
Reference in New Issue
Block a user