mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-07 10:38:25 -04:00
Merge branch 'master' into becca-invite-users
This commit is contained in:
@@ -45,12 +45,6 @@
|
||||
|
||||
}
|
||||
|
||||
.beta-badge {
|
||||
@include phase-tag();
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
|
||||
@include media(desktop) {
|
||||
#proposition-menu {
|
||||
float: right;
|
||||
@@ -172,11 +166,6 @@ details summary {
|
||||
|
||||
}
|
||||
|
||||
.phase-banner-beta {
|
||||
border: 0;
|
||||
margin-bottom: -$gutter + 2px;
|
||||
}
|
||||
|
||||
.body-copy-table {
|
||||
|
||||
table {
|
||||
|
||||
@@ -13,7 +13,6 @@ $path: '/static/images/';
|
||||
@import 'helpers';
|
||||
@import 'url-helpers';
|
||||
@import 'design-patterns/buttons';
|
||||
@import 'design-patterns/alpha-beta';
|
||||
|
||||
// Dependencies from GOV.UK Elements
|
||||
// https://github.com/alphagov/govuk_elements
|
||||
@@ -28,7 +27,6 @@ $path: '/static/images/';
|
||||
@import 'elements/layout';
|
||||
@import 'elements/lists';
|
||||
@import 'elements/panels';
|
||||
@import 'elements/phase-banner';
|
||||
@import 'elements/tables';
|
||||
|
||||
|
||||
|
||||
@@ -69,39 +69,6 @@ class Config(object):
|
||||
STATSD_HOST = "statsd.hostedgraphite.com"
|
||||
STATSD_PORT = 8125
|
||||
NOTIFY_ENVIRONMENT = 'development'
|
||||
|
||||
EMAIL_DOMAIN_REGEXES = [
|
||||
r"gov\.uk",
|
||||
r"mod\.uk",
|
||||
r"mil\.uk",
|
||||
r"ddc-mod\.org",
|
||||
r"slc\.co\.uk",
|
||||
r"gov\.scot",
|
||||
r"parliament\.uk",
|
||||
r"nhs\.uk",
|
||||
r"nhs\.net",
|
||||
r"police\.uk",
|
||||
r"dclgdatamart\.co\.uk",
|
||||
r"ucds\.email",
|
||||
r"naturalengland\.org\.uk",
|
||||
r"hmcts\.net",
|
||||
r"scotent\.co\.uk",
|
||||
r"assembly\.wales",
|
||||
r"cjsm\.net",
|
||||
r"cqc\.org\.uk",
|
||||
r"bl\.uk",
|
||||
r"stfc\.ac\.uk",
|
||||
r"wmfs\.net",
|
||||
r"bbsrc\.ac\.uk",
|
||||
r"acas\.org\.uk",
|
||||
r"gov\.wales",
|
||||
r"biglotteryfund\.org\.uk",
|
||||
r"marinemanagement\.org\.uk",
|
||||
r"britishmuseum\.org",
|
||||
r"derrystrabane\.com",
|
||||
r"highwaysengland\.co\.uk",
|
||||
]
|
||||
|
||||
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-local'
|
||||
ROUTE_SECRET_KEY_1 = os.environ.get('ROUTE_SECRET_KEY_1', '')
|
||||
ROUTE_SECRET_KEY_2 = os.environ.get('ROUTE_SECRET_KEY_2', '')
|
||||
|
||||
5086
app/domains.yml
Normal file
5086
app/domains.yml
Normal file
File diff suppressed because it is too large
Load Diff
30
app/email_domains.yml
Normal file
30
app/email_domains.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
- gov.uk
|
||||
- mod.uk
|
||||
- mil.uk
|
||||
- ddc-mod.org
|
||||
- slc.co.uk
|
||||
- gov.scot
|
||||
- parliament.uk
|
||||
- nhs.uk
|
||||
- nhs.net
|
||||
- police.uk
|
||||
- dclgdatamart.co.uk
|
||||
- ucds.email
|
||||
- naturalengland.org.uk
|
||||
- hmcts.net
|
||||
- scotent.co.uk
|
||||
- assembly.wales
|
||||
- cjsm.net
|
||||
- cqc.org.uk
|
||||
- bl.uk
|
||||
- stfc.ac.uk
|
||||
- wmfs.net
|
||||
- bbsrc.ac.uk
|
||||
- acas.org.uk
|
||||
- gov.wales
|
||||
- biglotteryfund.org.uk
|
||||
- marinemanagement.org.uk
|
||||
- britishmuseum.org
|
||||
- derrystrabane.com
|
||||
- highwaysengland.co.uk
|
||||
@@ -1,6 +1,6 @@
|
||||
from itertools import chain
|
||||
from flask_login import UserMixin, AnonymousUserMixin
|
||||
from flask import session
|
||||
from flask import request, session
|
||||
|
||||
|
||||
roles = {
|
||||
@@ -13,6 +13,10 @@ roles = {
|
||||
all_permissions = set(chain.from_iterable(roles.values())) | {'view_activity'}
|
||||
|
||||
|
||||
def _get_service_id_from_view_args():
|
||||
return request.view_args.get('service_id', None)
|
||||
|
||||
|
||||
class User(UserMixin):
|
||||
def __init__(self, fields, max_failed_login_count=3):
|
||||
self._id = fields.get('id')
|
||||
@@ -27,7 +31,7 @@ class User(UserMixin):
|
||||
self.max_failed_login_count = max_failed_login_count
|
||||
self.platform_admin = fields.get('platform_admin')
|
||||
self.current_session_id = fields.get('current_session_id')
|
||||
self.organisations = fields.get('organisations', [])
|
||||
self._organisations = fields.get('organisations', [])
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
@@ -118,9 +122,8 @@ class User(UserMixin):
|
||||
if admin_override and not permissions:
|
||||
return False
|
||||
|
||||
from flask import request
|
||||
# Service id is always set on the request for service specific views.
|
||||
service_id = request.view_args.get('service_id', None)
|
||||
service_id = _get_service_id_from_view_args()
|
||||
if service_id in self._permissions:
|
||||
if any_:
|
||||
return any([x in self._permissions[service_id] for x in permissions])
|
||||
|
||||
@@ -226,6 +226,16 @@ class ServiceAPIClient(NotifyAdminAPIClient):
|
||||
service_id=service_id)
|
||||
return self.get(endpoint, *params)
|
||||
|
||||
def count_service_templates(self, service_id, template_type=None):
|
||||
return len([
|
||||
template for template in
|
||||
self.get_service_templates(service_id)['data']
|
||||
if (
|
||||
not template_type
|
||||
or template['template_type'] == template_type
|
||||
)
|
||||
])
|
||||
|
||||
def delete_service_template(self, service_id, template_id):
|
||||
"""
|
||||
Set a service template's archived flag to True
|
||||
|
||||
@@ -131,6 +131,12 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
resp = self.get(endpoint)
|
||||
return [User(data) for data in resp['data']]
|
||||
|
||||
def get_count_of_users_with_permission(self, service_id, permission):
|
||||
return len([
|
||||
user for user in self.get_users_for_service(service_id)
|
||||
if user.has_permissions(permission, any_=True)
|
||||
])
|
||||
|
||||
def get_users_for_organisation(self, org_id):
|
||||
endpoint = '/organisations/{}/users'.format(org_id)
|
||||
resp = self.get(endpoint)
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block inside_header %}
|
||||
<strong class='beta-badge'>Beta</strong>
|
||||
{% endblock %}
|
||||
{% block header_class %}with-proposition{% endblock %}
|
||||
{% block proposition_header %}
|
||||
<div class="header-proposition">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<h1 class="heading-large">Roadmap</h1>
|
||||
<p>The GOV.UK Notify roadmap shows the things we’re working on and when we hope to have them ready for you to use.</p>
|
||||
<p class="panel panel-border-wide">This roadmap is a only a guide and things might change.</p>
|
||||
<p>Notify is in public beta. This means it’s fully operational and supported, but we’re regularly adding new features. The roadmap is a guide to what we have planned, but some things might change.</p>
|
||||
<p>You can <a href="{{url_for('.feedback', ticket_type='ask-question-give-feedback')}}">contact us</a> for more detail about these features, or to suggest something else you’d like Notify to offer.</p>
|
||||
|
||||
<h2 class="heading-medium">Sending and receiving messages</h2>
|
||||
|
||||
@@ -227,9 +227,14 @@
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
To remove these restrictions
|
||||
<a href="{{ url_for('.request_to_go_live', service_id=current_service.id) }}">request to go live</a>.
|
||||
</p>
|
||||
{% if current_user.has_permissions('manage_settings') %}
|
||||
To remove these restrictions
|
||||
<a href="{{ url_for('.request_to_go_live', service_id=current_service.id) }}">request to go live</a>.
|
||||
{% else %}
|
||||
Your service manager can ask to have these restrictions removed.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% else %}
|
||||
<h2 class="heading-medium">Your service is live</h2>
|
||||
|
||||
@@ -328,7 +333,7 @@
|
||||
<a href="{{ url_for('.service_switch_can_send_precompiled_letter', service_id=current_service.id) }}" class="button">
|
||||
{{ 'Stop sending precompiled letters' if 'precompiled_letter' in current_service.permissions else 'Allow to send precompiled letters' }}
|
||||
</a>
|
||||
</li>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="bottom-gutter">
|
||||
<a href="{{ url_for('.service_switch_email_auth', service_id=current_service.id) }}" class="button">
|
||||
|
||||
@@ -120,12 +120,12 @@
|
||||
<div class="grid-row bottom-gutter">
|
||||
<div class="column-half">
|
||||
<h3 class="visually-hidden">Services</h3>
|
||||
<div class="product-page-big-number">160</div>
|
||||
<div class="product-page-big-number">166</div>
|
||||
services
|
||||
</div>
|
||||
<div class="column-half">
|
||||
<h3 class="visually-hidden">Organisations</h3>
|
||||
<div class="product-page-big-number">65</div>
|
||||
<div class="product-page-big-number">66</div>
|
||||
organisations
|
||||
</div>
|
||||
</div>
|
||||
|
||||
84
app/utils.py
84
app/utils.py
@@ -1,5 +1,8 @@
|
||||
import os
|
||||
import re
|
||||
import csv
|
||||
import yaml
|
||||
|
||||
from itertools import chain
|
||||
|
||||
import pytz
|
||||
@@ -280,9 +283,11 @@ def get_help_argument():
|
||||
|
||||
|
||||
def is_gov_user(email_address):
|
||||
valid_domains = current_app.config['EMAIL_DOMAIN_REGEXES']
|
||||
email_regex = (r"[\.|@]({})$".format("|".join(valid_domains)))
|
||||
return bool(re.search(email_regex, email_address.lower()))
|
||||
try:
|
||||
GovernmentEmailDomain(email_address)
|
||||
return True
|
||||
except NotGovernmentEmailDomain:
|
||||
return False
|
||||
|
||||
|
||||
def get_template(
|
||||
@@ -428,3 +433,76 @@ def set_status_filters(filter_args):
|
||||
SENDING_STATUSES if 'sending' in status_filters else [],
|
||||
FAILURE_STATUSES if 'failed' in status_filters else []
|
||||
)))
|
||||
|
||||
|
||||
_dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
|
||||
class GovernmentDomain:
|
||||
|
||||
with open('{}/domains.yml'.format(_dir_path)) as domains:
|
||||
domains = yaml.safe_load(domains)
|
||||
domain_names = sorted(domains.keys(), key=len, reverse=True)
|
||||
|
||||
def __init__(self, email_address_or_domain):
|
||||
|
||||
self._match = next(filter(
|
||||
self.get_matching_function(email_address_or_domain),
|
||||
self.domain_names,
|
||||
), None)
|
||||
|
||||
(
|
||||
self.owner,
|
||||
self.crown_status,
|
||||
self.agreement_signed
|
||||
) = self._get_details_of_domain()
|
||||
|
||||
@staticmethod
|
||||
def get_matching_function(email_address_or_domain):
|
||||
|
||||
email_address_or_domain = email_address_or_domain.lower()
|
||||
|
||||
def fn(domain):
|
||||
|
||||
return (
|
||||
email_address_or_domain == domain
|
||||
) or (
|
||||
email_address_or_domain.endswith("@{}".format(domain))
|
||||
) or (
|
||||
email_address_or_domain.endswith(".{}".format(domain))
|
||||
)
|
||||
|
||||
return fn
|
||||
|
||||
def _get_details_of_domain(self):
|
||||
|
||||
details = self.domains.get(self._match) or {}
|
||||
|
||||
if isinstance(details, str):
|
||||
return GovernmentDomain(details)._get_details_of_domain()
|
||||
|
||||
elif isinstance(details, dict):
|
||||
return(
|
||||
details.get("owner"),
|
||||
details.get("crown"),
|
||||
details.get("agreement_signed"),
|
||||
)
|
||||
|
||||
|
||||
class NotGovernmentEmailDomain(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class GovernmentEmailDomain(GovernmentDomain):
|
||||
|
||||
with open('{}/email_domains.yml'.format(_dir_path)) as email_domains:
|
||||
domain_names = yaml.safe_load(email_domains)
|
||||
|
||||
def __init__(self, email_address_or_domain):
|
||||
try:
|
||||
self._match = next(filter(
|
||||
self.get_matching_function(email_address_or_domain),
|
||||
self.domain_names,
|
||||
))
|
||||
except StopIteration:
|
||||
raise NotGovernmentEmailDomain()
|
||||
|
||||
Reference in New Issue
Block a user