Refined the GUI for personal API endpoints so that endpoints for which one does not have the right Roles do not show in the GUI, and added a supporting method on Person objects. Updated tests accordingly.
- Legacy-Id: 17643
This commit is contained in:
parent
eedd48d455
commit
69a5d0817d
|
@ -21,7 +21,7 @@ from ietf.group.models import Group, Role, RoleName
|
||||||
from ietf.group.factories import GroupFactory, RoleFactory
|
from ietf.group.factories import GroupFactory, RoleFactory
|
||||||
from ietf.ietfauth.htpasswd import update_htpasswd_file
|
from ietf.ietfauth.htpasswd import update_htpasswd_file
|
||||||
from ietf.mailinglists.models import Subscribed
|
from ietf.mailinglists.models import Subscribed
|
||||||
from ietf.person.models import Person, Email, PersonalApiKey, PERSON_API_KEY_ENDPOINTS
|
from ietf.person.models import Person, Email, PersonalApiKey
|
||||||
from ietf.person.factories import PersonFactory, EmailFactory
|
from ietf.person.factories import PersonFactory, EmailFactory
|
||||||
from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
|
from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
|
||||||
from ietf.review.models import ReviewWish, UnavailablePeriod
|
from ietf.review.models import ReviewWish, UnavailablePeriod
|
||||||
|
@ -531,18 +531,19 @@ class IetfAuthTests(TestCase):
|
||||||
self.assertContains(r, 'Endpoint')
|
self.assertContains(r, 'Endpoint')
|
||||||
|
|
||||||
# Add 2 keys
|
# Add 2 keys
|
||||||
for endpoint, display in PERSON_API_KEY_ENDPOINTS:
|
endpoints = person.available_api_endpoints()
|
||||||
|
for endpoint, display in endpoints:
|
||||||
r = self.client.post(url, {'endpoint': endpoint})
|
r = self.client.post(url, {'endpoint': endpoint})
|
||||||
self.assertRedirects(r, urlreverse('ietf.ietfauth.views.apikey_index'))
|
self.assertRedirects(r, urlreverse('ietf.ietfauth.views.apikey_index'))
|
||||||
|
|
||||||
# Check api key list content
|
# Check api key list content
|
||||||
url = urlreverse('ietf.ietfauth.views.apikey_index')
|
url = urlreverse('ietf.ietfauth.views.apikey_index')
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
for endpoint, display in PERSON_API_KEY_ENDPOINTS:
|
for endpoint, display in endpoints:
|
||||||
self.assertContains(r, endpoint)
|
self.assertContains(r, endpoint)
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
self.assertEqual(len(q('td code')), len(PERSON_API_KEY_ENDPOINTS)) # hash
|
self.assertEqual(len(q('td code')), len(endpoints)) # hash
|
||||||
self.assertEqual(len(q('td a:contains("Disable")')), len(PERSON_API_KEY_ENDPOINTS))
|
self.assertEqual(len(q('td a:contains("Disable")')), len(endpoints))
|
||||||
|
|
||||||
# Get one of the keys
|
# Get one of the keys
|
||||||
key = person.apikeys.first()
|
key = person.apikeys.first()
|
||||||
|
@ -562,8 +563,8 @@ class IetfAuthTests(TestCase):
|
||||||
url = urlreverse('ietf.ietfauth.views.apikey_index')
|
url = urlreverse('ietf.ietfauth.views.apikey_index')
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
self.assertEqual(len(q('td code')), len(PERSON_API_KEY_ENDPOINTS)) # key hash
|
self.assertEqual(len(q('td code')), len(endpoints)) # key hash
|
||||||
self.assertEqual(len(q('td a:contains("Disable")')), len(PERSON_API_KEY_ENDPOINTS)-1)
|
self.assertEqual(len(q('td a:contains("Disable")')), len(endpoints)-1)
|
||||||
|
|
||||||
def test_apikey_errors(self):
|
def test_apikey_errors(self):
|
||||||
BAD_KEY = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
BAD_KEY = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
|
@ -577,7 +578,7 @@ class IetfAuthTests(TestCase):
|
||||||
login_testing_unauthorized(self, person.user.username, url)
|
login_testing_unauthorized(self, person.user.username, url)
|
||||||
|
|
||||||
# Add keys
|
# Add keys
|
||||||
for endpoint, display in PERSON_API_KEY_ENDPOINTS:
|
for endpoint, display in person.available_api_endpoints():
|
||||||
r = self.client.post(url, {'endpoint': endpoint})
|
r = self.client.post(url, {'endpoint': endpoint})
|
||||||
self.assertRedirects(r, urlreverse('ietf.ietfauth.views.apikey_index'))
|
self.assertRedirects(r, urlreverse('ietf.ietfauth.views.apikey_index'))
|
||||||
|
|
||||||
|
@ -620,7 +621,8 @@ class IetfAuthTests(TestCase):
|
||||||
login_testing_unauthorized(self, person.user.username, url)
|
login_testing_unauthorized(self, person.user.username, url)
|
||||||
|
|
||||||
# Add keys
|
# Add keys
|
||||||
for endpoint, display in PERSON_API_KEY_ENDPOINTS:
|
endpoints = person.available_api_endpoints()
|
||||||
|
for endpoint, display in endpoints:
|
||||||
r = self.client.post(url, {'endpoint': endpoint})
|
r = self.client.post(url, {'endpoint': endpoint})
|
||||||
self.assertRedirects(r, urlreverse('ietf.ietfauth.views.apikey_index'))
|
self.assertRedirects(r, urlreverse('ietf.ietfauth.views.apikey_index'))
|
||||||
|
|
||||||
|
@ -639,7 +641,7 @@ class IetfAuthTests(TestCase):
|
||||||
cmd = Command()
|
cmd = Command()
|
||||||
cmd.handle(verbosity=0, days=7)
|
cmd.handle(verbosity=0, days=7)
|
||||||
|
|
||||||
self.assertEqual(len(outbox), len(PERSON_API_KEY_ENDPOINTS))
|
self.assertEqual(len(outbox), len(endpoints))
|
||||||
for mail in outbox:
|
for mail in outbox:
|
||||||
body = mail.get_payload(decode=True).decode('utf-8')
|
body = mail.get_payload(decode=True).decode('utf-8')
|
||||||
self.assertIn("API key usage", mail['subject'])
|
self.assertIn("API key usage", mail['subject'])
|
||||||
|
|
|
@ -65,9 +65,9 @@ from ietf.ietfauth.forms import ( RegistrationForm, PasswordForm, ResetPasswordF
|
||||||
WhitelistForm, ChangePasswordForm, get_person_form, RoleEmailForm,
|
WhitelistForm, ChangePasswordForm, get_person_form, RoleEmailForm,
|
||||||
NewEmailForm, ChangeUsernameForm, PersonPasswordForm)
|
NewEmailForm, ChangeUsernameForm, PersonPasswordForm)
|
||||||
from ietf.ietfauth.htpasswd import update_htpasswd_file
|
from ietf.ietfauth.htpasswd import update_htpasswd_file
|
||||||
from ietf.ietfauth.utils import role_required
|
from ietf.ietfauth.utils import role_required, has_role
|
||||||
from ietf.mailinglists.models import Subscribed, Whitelisted
|
from ietf.mailinglists.models import Subscribed, Whitelisted
|
||||||
from ietf.person.models import Person, Email, Alias, PersonalApiKey
|
from ietf.person.models import Person, Email, Alias, PersonalApiKey, PERSON_API_KEY_VALUES
|
||||||
from ietf.review.models import ReviewerSettings, ReviewWish, ReviewAssignment
|
from ietf.review.models import ReviewerSettings, ReviewWish, ReviewAssignment
|
||||||
from ietf.review.utils import unavailable_periods_to_list, get_default_filter_re
|
from ietf.review.utils import unavailable_periods_to_list, get_default_filter_re
|
||||||
from ietf.doc.fields import SearchableDocumentField
|
from ietf.doc.fields import SearchableDocumentField
|
||||||
|
@ -650,7 +650,10 @@ def apikey_index(request):
|
||||||
@login_required
|
@login_required
|
||||||
@person_required
|
@person_required
|
||||||
def apikey_create(request):
|
def apikey_create(request):
|
||||||
|
endpoints = [('', '----------')] + [ (v, n) for (v, n, r) in PERSON_API_KEY_VALUES if r==None or has_role(request.user, r) ]
|
||||||
class ApiKeyForm(forms.ModelForm):
|
class ApiKeyForm(forms.ModelForm):
|
||||||
|
endpoint = forms.ChoiceField(choices=endpoints)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PersonalApiKey
|
model = PersonalApiKey
|
||||||
fields = ['endpoint']
|
fields = ['endpoint']
|
||||||
|
|
|
@ -234,6 +234,11 @@ class Person(models.Model):
|
||||||
ct1['ascii'] = self.ascii
|
ct1['ascii'] = self.ascii
|
||||||
return ct1
|
return ct1
|
||||||
|
|
||||||
|
def available_api_endpoints(self):
|
||||||
|
from ietf.ietfauth.utils import has_role
|
||||||
|
return [ (v, n) for (v, n, r) in PERSON_API_KEY_VALUES if r==None or has_role(self.user, r) ]
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Alias(models.Model):
|
class Alias(models.Model):
|
||||||
"""This is used for alternative forms of a name. This is the
|
"""This is used for alternative forms of a name. This is the
|
||||||
|
@ -328,13 +333,13 @@ def salt():
|
||||||
return uuid.uuid4().bytes[:12]
|
return uuid.uuid4().bytes[:12]
|
||||||
|
|
||||||
# Manual maintenance: List all endpoints that use @require_api_key here
|
# Manual maintenance: List all endpoints that use @require_api_key here
|
||||||
PERSON_API_KEY_ENDPOINTS = [
|
PERSON_API_KEY_VALUES = [
|
||||||
("/api/iesg/position", "/api/iesg/position"),
|
("/api/iesg/position", "/api/iesg/position", "Area Director"),
|
||||||
# This requires secretariat role, and need not be listed generally:
|
("/api/v2/person/person", "/api/v2/person/person", "Secretariat"),
|
||||||
# ("/api/v2/person/person", "/api/v2/person/person"),
|
("/api/meeting/session/video/url", "/api/meeting/session/video/url", "Recording Manager"),
|
||||||
("/api/meeting/session/video/url", "/api/meeting/session/video/url"),
|
("/api/person/access/meetecho", "/api/person/access/meetecho", None),
|
||||||
("/api/v2/person/access/meetecho", "/api/v2/person/access/meetecho"),
|
|
||||||
]
|
]
|
||||||
|
PERSON_API_KEY_ENDPOINTS = [ (v, n) for (v, n, r) in PERSON_API_KEY_VALUES ]
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class PersonalApiKey(models.Model):
|
class PersonalApiKey(models.Model):
|
||||||
|
@ -375,7 +380,7 @@ class PersonalApiKey(models.Model):
|
||||||
return self._cached_hash
|
return self._cached_hash
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s (%s): %s ..." % (self.endpoint, self.created.strftime("%Y-%m-%d %H:%M"), self.hash()[:16])
|
return "%s %-24s %-32s(%6d): %s ..." % (self.created.strftime("%Y-%m-%d %H:%M"), self.person.name, self.endpoint, self.count, self.hash()[:16])
|
||||||
|
|
||||||
PERSON_EVENT_CHOICES = [
|
PERSON_EVENT_CHOICES = [
|
||||||
("apikey_login", "API key login"),
|
("apikey_login", "API key login"),
|
||||||
|
|
Loading…
Reference in a new issue