Merge branch 'master' into becca-invite-users

This commit is contained in:
Rebecca Law
2018-02-27 10:13:40 +00:00
18 changed files with 5478 additions and 74 deletions

View File

@@ -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 {

View File

@@ -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';

View File

@@ -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

File diff suppressed because it is too large Load Diff

30
app/email_domains.yml Normal file
View 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

View File

@@ -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])

View File

@@ -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

View File

@@ -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)

View File

@@ -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">

View File

@@ -16,7 +16,7 @@
<h1 class="heading-large">Roadmap</h1>
<p>The GOV.UK Notify roadmap shows the things were 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 its fully operational and supported, but were 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 youd like Notify to offer.</p>
<h2 class="heading-medium">Sending and receiving messages</h2>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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()