Merge pull request #763 from alphagov/api-key-types

Api key types
This commit is contained in:
Leo Hemsted
2016-07-07 14:58:31 +01:00
committed by GitHub
10 changed files with 109 additions and 22 deletions

View File

@@ -111,7 +111,12 @@
} }
.table-field-heading { .table-field-headings,
.table-field-headings-visible {
th {
@include bold-19;
}
.dashboard-table &-first { .dashboard-table &-first {
width: 52.5%; width: 52.5%;

View File

@@ -12,12 +12,14 @@ from wtforms import (
FileField, FileField,
BooleanField, BooleanField,
HiddenField, HiddenField,
IntegerField IntegerField,
RadioField
) )
from wtforms.fields.html5 import EmailField, TelField from wtforms.fields.html5 import EmailField, TelField
from wtforms.validators import (DataRequired, Email, Length, Regexp) from wtforms.validators import (DataRequired, Email, Length, Regexp)
from app.main.validators import (Blacklist, CsvFileValidator, ValidEmailDomainRegex, NoCommasInPlaceHolders) from app.main.validators import (Blacklist, CsvFileValidator, ValidEmailDomainRegex, NoCommasInPlaceHolders)
from app.notify_client.api_key_api_client import KEY_TYPE_NORMAL, KEY_TYPE_TEST, KEY_TYPE_TEAM
def email_address(label='Email address'): def email_address(label='Email address'):
@@ -291,6 +293,18 @@ class CreateKeyForm(Form):
self.existing_key_names = [x.lower() for x in existing_key_names] self.existing_key_names = [x.lower() for x in existing_key_names]
super(CreateKeyForm, self).__init__(*args, **kwargs) super(CreateKeyForm, self).__init__(*args, **kwargs)
key_type = RadioField(
'What should Notify do when you use this key?',
choices=[
(KEY_TYPE_NORMAL, 'Send messages to anyone'),
(KEY_TYPE_TEST, 'Simulate sending messages to anyone'),
(KEY_TYPE_TEAM, 'Only send messages to members of your team')
],
validators=[
DataRequired()
]
)
key_name = StringField(u'Description of key', validators=[ key_name = StringField(u'Description of key', validators=[
DataRequired(message='You need to give the key a name') DataRequired(message='You need to give the key a name')
]) ])

View File

@@ -25,12 +25,16 @@ def create_api_key(service_id):
] ]
form = CreateKeyForm(key_names) form = CreateKeyForm(key_names)
if form.validate_on_submit(): if form.validate_on_submit():
secret = api_key_api_client.create_api_key(service_id=service_id, key_name=form.key_name.data) secret = api_key_api_client.create_api_key(
service_id=service_id,
key_name=form.key_name.data,
key_type=form.key_type.data
)
return render_template('views/api-keys/show.html', secret=secret, return render_template('views/api-keys/show.html', secret=secret,
key_name=form.key_name.data) key_name=form.key_name.data)
return render_template( return render_template(
'views/api-keys/create.html', 'views/api-keys/create.html',
key_name=form.key_name form=form
) )

View File

@@ -5,6 +5,7 @@ from app.notify_client import _attach_current_user
# must match key types in notifications-api/app/models.py # must match key types in notifications-api/app/models.py
KEY_TYPE_NORMAL = 'normal' KEY_TYPE_NORMAL = 'normal'
KEY_TYPE_TEAM = 'team' KEY_TYPE_TEAM = 'team'
KEY_TYPE_TEST = 'test'
class ApiKeyApiClient(BaseAPIClient): class ApiKeyApiClient(BaseAPIClient):
@@ -24,10 +25,10 @@ class ApiKeyApiClient(BaseAPIClient):
else: else:
return self.get(url='/service/{}/api-keys'.format(service_id)) return self.get(url='/service/{}/api-keys'.format(service_id))
def create_api_key(self, service_id, key_name): def create_api_key(self, service_id, key_name, key_type):
data = { data = {
'name': key_name, 'name': key_name,
'key_type': KEY_TYPE_NORMAL 'key_type': key_type
} }
_attach_current_user(data) _attach_current_user(data)
key = self.post(url='/service/{}/api-key'.format(service_id), data=data) key = self.post(url='/service/{}/api-key'.format(service_id), data=data)

View File

@@ -0,0 +1,23 @@
{% macro radios(
field,
hint=None
) %}
<div class="form-group {% if field.errors %} error{% endif %}">
<fieldset>
<legend class="form-label">
{{ field.label }}
{% if field.errors %}
<span class="error-message">
{{ field.errors[0] }}
</span>
{% endif %}
</legend>
{% for option in field %}
<label class="block-label" for="{{ option.id }}">
{{ option }}
{{ option.label.text }}
</label>
{% endfor %}
</fieldset>
</div>
{% endmacro %}

View File

@@ -58,7 +58,7 @@
<li><a href="{{ url_for('.manage_users', service_id=current_service.id) }}">Team members</a></li> <li><a href="{{ url_for('.manage_users', service_id=current_service.id) }}">Team members</a></li>
{% endif %} {% endif %}
{% if current_user.has_permissions(['manage_api_keys']) %} {% if current_user.has_permissions(['manage_api_keys']) %}
<li><a href="{{ url_for('.api_keys', service_id=current_service.id) }}">API keys</a></li> <li><a href="{{ url_for('.api_keys', service_id=current_service.id) }}">API integration</a></li>
{% endif %} {% endif %}
</ul> </ul>
</nav> </nav>

View File

@@ -11,7 +11,7 @@
<div class="grid-row"> <div class="grid-row">
<div class="column-two-thirds"> <div class="column-two-thirds">
<h1 class="heading-large"> <h1 class="heading-large">
API keys API integration
</h1> </h1>
</div> </div>
<div class="column-one-third"> <div class="column-one-third">
@@ -23,15 +23,29 @@
keys, keys,
empty_message="You havent created any API keys yet", empty_message="You havent created any API keys yet",
caption="API keys", caption="API keys",
caption_visible=False, caption_visible=false,
field_headings=['Key name', hidden_field_heading('Action')] field_headings=[
'API keys',
hidden_field_heading('Action')
],
) %} ) %}
{% call field() %} {% call field() %}
{{ item.name }} <div class="file-list">
{{ item.name }}
<span class="file-list-hint">
{% if item.key_type == 'normal' %}
<span class="visually-hidden">Normal</span>
{% elif item.key_type == 'team' %}
Only sends to team members
{% elif item.key_type == 'test' %}
Simulates sending messages
{% endif %}
</span>
</div>
{% endcall %} {% endcall %}
{% if item.expiry_date %} {% if item.expiry_date %}
{% call field(align='right', status='default') %} {% call field(align='right') %}
Revoked {{ item.expiry_date|format_datetime }} Revoked {{ item.expiry_date|format_datetime_short }}
{% endcall %} {% endcall %}
{% else %} {% else %}
{% call field(align='right', status='error') %} {% call field(align='right', status='error') %}
@@ -44,10 +58,11 @@
{{ api_key(current_service.id, "Service ID", thing="service ID") }} {{ api_key(current_service.id, "Service ID", thing="service ID") }}
</div> </div>
<h2 class="heading-small">Documentation</h2>
<p> <p>
API usage is described in the See the
<a href="{{ url_for('.documentation', service_id=current_service.id) }}"> <a href="{{ url_for('.documentation') }}">
API documentation</a>. API documentation</a> for clients and specfications.
</p> </p>
{% endblock %} {% endblock %}

View File

@@ -1,6 +1,7 @@
{% extends "withnav_template.html" %} {% extends "withnav_template.html" %}
{% from "components/page-footer.html" import page_footer %} {% from "components/page-footer.html" import page_footer %}
{% from "components/textbox.html" import textbox %} {% from "components/textbox.html" import textbox %}
{% from "components/radios.html" import radios %}
{% block page_title %} {% block page_title %}
Add a new API key GOV.UK Notify Add a new API key GOV.UK Notify
@@ -9,11 +10,12 @@
{% block maincolumn_content %} {% block maincolumn_content %}
<h1 class="heading-large"> <h1 class="heading-large">
Add a new API key Create an API key
</h1> </h1>
<form method="post"> <form method="post">
{{ textbox(key_name, hint='eg CRM application') }} {{ radios(form.key_type) }}
{{ textbox(form.key_name, label='Name for this key') }}
{{ page_footer('Continue') }} {{ page_footer('Continue') }}
</form> </form>

View File

@@ -1,3 +1,5 @@
import pytest
from werkzeug.datastructures import MultiDict from werkzeug.datastructures import MultiDict
from app.main.forms import CreateKeyForm from app.main.forms import CreateKeyForm
@@ -11,4 +13,20 @@ def test_return_validation_error_when_key_name_exists(app_):
form = CreateKeyForm(_get_names(), form = CreateKeyForm(_get_names(),
formdata=MultiDict([('key_name', 'Some key')])) formdata=MultiDict([('key_name', 'Some key')]))
form.validate() form.validate()
assert {'key_name': ['A key with this name already exists']} == form.errors assert form.errors['key_name'] == ['A key with this name already exists']
@pytest.mark.parametrize(
'key_type, expected_error', [
('', 'This field is required.'),
('invalid', 'Not a valid choice')
]
)
def test_return_validation_error_when_key_type_not_chosen(app_, key_type, expected_error):
with app_.test_request_context():
form = CreateKeyForm(
[],
formdata=MultiDict([('key_name', 'Some key'), ('key_type', key_type)]))
form.validate()
assert form.errors['key_type'] == [expected_error]

View File

@@ -39,7 +39,7 @@ def test_should_show_api_keys_page(app_,
resp_data = response.get_data(as_text=True) resp_data = response.get_data(as_text=True)
assert 'some key name' in resp_data assert 'some key name' in resp_data
assert 'another key name' in resp_data assert 'another key name' in resp_data
assert 'Revoked Thursday 01 January 1970 at 01:00' in resp_data assert 'Revoked 1 January at 01:00' in resp_data
mock_get_api_keys.assert_called_once_with(service_id=fake_uuid) mock_get_api_keys.assert_called_once_with(service_id=fake_uuid)
@@ -72,8 +72,13 @@ def test_should_create_api_key_with_type_normal(app_,
with app_.test_request_context(), app_.test_client() as client: with app_.test_request_context(), app_.test_client() as client:
client.login(api_user_active) client.login(api_user_active)
response = client.post(url_for('main.create_api_key', service_id=service_id), response = client.post(
data={'key_name': 'some default key name'}) url_for('main.create_api_key', service_id=service_id),
data={
'key_name': 'some default key name',
'key_type': 'normal'
}
)
assert response.status_code == 200 assert response.status_code == 200
assert 'some default key name' in response.get_data(as_text=True) assert 'some default key name' in response.get_data(as_text=True)