mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-18 13:39:41 -04:00
Email auth for inviting members and editing permissions
This commit is contained in:
@@ -63,6 +63,11 @@
|
||||
right: -135px;
|
||||
}
|
||||
|
||||
&-hint {
|
||||
color: #6F777B;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -186,6 +186,14 @@ class PermissionsForm(Form):
|
||||
manage_templates = BooleanField("Add and edit templates")
|
||||
manage_service = BooleanField("Modify this service and its team")
|
||||
manage_api_keys = BooleanField("Create and revoke API keys")
|
||||
login_authentication = RadioField(
|
||||
'Sign in using',
|
||||
choices=[
|
||||
('sms_auth', 'Text message code'),
|
||||
('email_auth', 'Email link'),
|
||||
],
|
||||
validators=[DataRequired()]
|
||||
)
|
||||
|
||||
|
||||
class InviteUserForm(PermissionsForm):
|
||||
|
||||
@@ -19,7 +19,7 @@ from app.main.forms import (
|
||||
InviteUserForm,
|
||||
PermissionsForm
|
||||
)
|
||||
from app import (user_api_client, service_api_client, invite_api_client)
|
||||
from app import (user_api_client, current_service, service_api_client, invite_api_client)
|
||||
from app.utils import user_has_permissions
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ def manage_users(service_id):
|
||||
users = user_api_client.get_users_for_service(service_id=service_id)
|
||||
invited_users = [invite for invite in invite_api_client.get_invites_for_service(service_id=service_id)
|
||||
if invite.status != 'accepted']
|
||||
|
||||
return render_template(
|
||||
'views/manage-users.html',
|
||||
users=users,
|
||||
@@ -51,7 +52,13 @@ def manage_users(service_id):
|
||||
@user_has_permissions('manage_users', admin_override=True)
|
||||
def invite_user(service_id):
|
||||
|
||||
form = InviteUserForm(invalid_email_address=current_user.email_address)
|
||||
form = InviteUserForm(
|
||||
invalid_email_address=current_user.email_address
|
||||
)
|
||||
|
||||
service_has_email_auth = 'email_auth' in current_service['permissions']
|
||||
if not service_has_email_auth:
|
||||
form.login_authentication.data = 'sms_auth'
|
||||
|
||||
if form.validate_on_submit():
|
||||
email_address = form.email_address.data
|
||||
@@ -60,7 +67,8 @@ def invite_user(service_id):
|
||||
current_user.id,
|
||||
service_id,
|
||||
email_address,
|
||||
permissions
|
||||
permissions,
|
||||
form.login_authentication.data
|
||||
)
|
||||
|
||||
flash('Invite sent to {}'.format(invited_user.email_address), 'default_with_tick')
|
||||
@@ -68,7 +76,8 @@ def invite_user(service_id):
|
||||
|
||||
return render_template(
|
||||
'views/invite-user.html',
|
||||
form=form
|
||||
form=form,
|
||||
service_has_email_auth=service_has_email_auth
|
||||
)
|
||||
|
||||
|
||||
@@ -76,26 +85,35 @@ def invite_user(service_id):
|
||||
@login_required
|
||||
@user_has_permissions('manage_users', admin_override=True)
|
||||
def edit_user_permissions(service_id, user_id):
|
||||
service_has_email_auth = 'email_auth' in current_service['permissions']
|
||||
# TODO we should probably using the service id here in the get user
|
||||
# call as well. eg. /user/<user_id>?&service=service_id
|
||||
user = user_api_client.get_user(user_id)
|
||||
# Need to make the email address read only, or a disabled field?
|
||||
# Do it through the template or the form class?
|
||||
form = PermissionsForm(**{
|
||||
role: user.has_permissions(permissions=permissions) for role, permissions in roles.items()
|
||||
})
|
||||
user_has_no_mobile_number = user.mobile_number is None
|
||||
|
||||
form = PermissionsForm(
|
||||
**{role: user.has_permissions(permissions=permissions) for role, permissions in roles.items()},
|
||||
login_authentication=user.auth_type
|
||||
)
|
||||
|
||||
if form.validate_on_submit():
|
||||
user_api_client.set_user_permissions(
|
||||
user_id, service_id,
|
||||
permissions=set(get_permissions_from_form(form)),
|
||||
)
|
||||
if service_has_email_auth:
|
||||
user_api_client.update_user_attribute(
|
||||
user_id,
|
||||
auth_type=form.login_authentication.data
|
||||
)
|
||||
return redirect(url_for('.manage_users', service_id=service_id))
|
||||
|
||||
return render_template(
|
||||
'views/edit-user-permissions.html',
|
||||
user=user,
|
||||
form=form
|
||||
form=form,
|
||||
service_has_email_auth=service_has_email_auth,
|
||||
user_has_no_mobile_number=user_has_no_mobile_number
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@ class InviteApiClient(NotifyAdminAPIClient):
|
||||
self.service_id = app.config['ADMIN_CLIENT_USER_NAME']
|
||||
self.api_key = app.config['ADMIN_CLIENT_SECRET']
|
||||
|
||||
def create_invite(self, invite_from_id, service_id, email_address, permissions):
|
||||
def create_invite(self, invite_from_id, service_id, email_address, permissions, auth_type):
|
||||
data = {
|
||||
'service': str(service_id),
|
||||
'email_address': email_address,
|
||||
'from_user': invite_from_id,
|
||||
'permissions': permissions
|
||||
'permissions': permissions,
|
||||
'auth_type': auth_type
|
||||
}
|
||||
data = _attach_current_user(data)
|
||||
resp = self.post(url='/service/{}/invite'.format(service_id), data=data)
|
||||
|
||||
@@ -10,6 +10,7 @@ class User(UserMixin):
|
||||
self._mobile_number = fields.get('mobile_number')
|
||||
self._password_changed_at = fields.get('password_changed_at')
|
||||
self._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
|
||||
@@ -108,6 +109,14 @@ class User(UserMixin):
|
||||
return set(self._permissions[service_id]) >= set(permissions)
|
||||
return False
|
||||
|
||||
@property
|
||||
def auth_type(self):
|
||||
return self._auth_type
|
||||
|
||||
@auth_type.setter
|
||||
def auth_type(self, auth_type):
|
||||
self._auth_type = auth_type
|
||||
|
||||
@property
|
||||
def failed_login_count(self):
|
||||
return self._failed_login_count
|
||||
@@ -155,6 +164,7 @@ class InvitedUser(object):
|
||||
self.permissions = []
|
||||
self.status = status
|
||||
self.created_at = created_at
|
||||
self.auth_type = auth_type
|
||||
|
||||
def has_permissions(self, permissions):
|
||||
return set(self.permissions) > set(permissions)
|
||||
@@ -164,10 +174,12 @@ class InvitedUser(object):
|
||||
self.service,
|
||||
self.from_user,
|
||||
self.email_address,
|
||||
self.auth_type,
|
||||
self.status) == (other.id,
|
||||
other.service,
|
||||
other.from_user,
|
||||
other.email_address,
|
||||
other.auth_type,
|
||||
other.status))
|
||||
|
||||
def serialize(self, permissions_as_string=False):
|
||||
@@ -176,7 +188,8 @@ class InvitedUser(object):
|
||||
'from_user': self.from_user,
|
||||
'email_address': self.email_address,
|
||||
'status': self.status,
|
||||
'created_at': str(self.created_at)
|
||||
'created_at': str(self.created_at),
|
||||
'auth_type': self.auth_type
|
||||
}
|
||||
if permissions_as_string:
|
||||
data['permissions'] = ','.join(self.permissions)
|
||||
|
||||
@@ -6,7 +6,8 @@ from app.notify_client.models import User
|
||||
ALLOWED_ATTRIBUTES = {
|
||||
'name',
|
||||
'email_address',
|
||||
'mobile_number'
|
||||
'mobile_number',
|
||||
'auth_type',
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -63,6 +63,15 @@
|
||||
user.has_permissions(permissions=['manage_api_keys']),
|
||||
'Access API keys'
|
||||
) }}
|
||||
{% if 'email_auth' in current_service['permissions'] %}
|
||||
<div class="tick-cross-list-hint">
|
||||
{% if user.auth_type == 'sms_auth' %}
|
||||
Signs in with a text message code
|
||||
{% else %}
|
||||
Signs in with an email link
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if current_user.has_permissions(['manage_users'], admin_override=True) %}
|
||||
{% if current_user.id != user.id %}
|
||||
@@ -104,6 +113,15 @@
|
||||
user.has_permissions(permissions=['manage_api_keys']),
|
||||
'Access API keys'
|
||||
) }}
|
||||
{% if 'email_auth' in current_service['permissions'] %}
|
||||
<div class="tick-cross-list-hint">
|
||||
{% if user.auth_type == 'sms_auth' %}
|
||||
Signs in with a text message code
|
||||
{% else %}
|
||||
Signs in with an email link
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<li class="tick-cross-list-edit-link">
|
||||
{% if user.status == 'pending' and current_user.has_permissions(['manage_users']) %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% from "components/checkbox.html" import checkbox %}
|
||||
{% from "components/radios.html" import radios %}
|
||||
|
||||
<fieldset class="form-group">
|
||||
<legend class="form-label">
|
||||
@@ -20,3 +21,7 @@
|
||||
<li>who the other team members are</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if service_has_email_auth %}
|
||||
{{ radios(form.login_authentication, disable=['sms_auth' if user_has_no_mobile_number]) }}
|
||||
{% endif %}
|
||||
Reference in New Issue
Block a user