Skip to content

Commit 4575d17

Browse files
authored
Merge pull request #774 from uw-it-aca/hotfix/missing-f-string-spec
Hotfix/missing f string spec
2 parents b3a93aa + 537f49e commit 4575d17

File tree

4 files changed

+134
-11
lines changed

4 files changed

+134
-11
lines changed

endorsement/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,7 @@ class EmptyDelegateRightsException(AccessRecordException):
8787

8888
class DelegateRightMismatchException(AccessRecordException):
8989
pass
90+
91+
92+
class DelegateParameterException(Exception):
93+
pass
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"TargetNetid": "jstaff",
3+
"Delegates": [{
4+
"User": "u_javerage_admin",
5+
"AccessRights": "FullAccess"
6+
}]
7+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright 2025 UW-IT, University of Washington
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
5+
from django.urls import reverse
6+
from django.test import Client
7+
from userservice.user import get_original_user
8+
from endorsement.models import (
9+
Accessee, Accessor, AccessRight, AccessRecord, AccessRecordConflict)
10+
from endorsement.test.views import require_url
11+
from endorsement.test.api import EndorsementApiTest
12+
13+
14+
@require_url('access_api', 'access url not configured')
15+
class TestAccess(EndorsementApiTest):
16+
fixtures = ['test_data/accessright.json',
17+
'test_data/accessee.json',
18+
'test_data/accessor.json']
19+
20+
def test_access_api(self):
21+
test_data = {
22+
'access_type': "FullAccess",
23+
'delegate': "u_javerage_admin",
24+
'mailbox': "jstaff"
25+
}
26+
27+
self.assertEqual(0, AccessRecord.objects.all().count())
28+
29+
self.set_user('jstaff')
30+
31+
url = reverse('access_api')
32+
response = self.client.post(
33+
url, test_data, content_type='application/json')
34+
35+
self.assertEqual(response.status_code, 200)
36+
self.assertEqual(1, AccessRecord.objects.all().count())
37+
38+
def test_access_api_missing_param(self):
39+
test_data = {
40+
'access_type': "FullAccess",
41+
'delegate': "u_javerage_admin",
42+
'mailbox': None
43+
}
44+
45+
self.assertEqual(0, AccessRecord.objects.all().count())
46+
47+
self.set_user('jstaff')
48+
49+
url = reverse('access_api')
50+
response = self.client.post(
51+
url, test_data, content_type='application/json')
52+
53+
self.assertEqual(response.status_code, 400)
54+
self.assertEqual(0, AccessRecord.objects.all().count())
55+
56+
def test_access_api_renew(self):
57+
test_data = {
58+
'action': 'renew',
59+
'access_type': "FullAccess",
60+
'delegate': "u_javerage_admin",
61+
'mailbox': "jstaff"
62+
}
63+
64+
self.assertEqual(0, AccessRecord.objects.all().count())
65+
66+
self.set_user('jstaff')
67+
68+
url = reverse('access_api')
69+
response = self.client.post(
70+
url, test_data, content_type='application/json')
71+
72+
self.assertEqual(response.status_code, 200)
73+
self.assertEqual(1, AccessRecord.objects.all().count())

endorsement/views/api/office/access.py

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from endorsement.dao.office import is_office_permitted, get_office_accessor
1212
from endorsement.views.rest_dispatch import (
1313
RESTDispatch, invalid_session, invalid_endorser, data_error)
14-
from endorsement.exceptions import UnrecognizedUWNetid, InvalidNetID
14+
from endorsement.exceptions import (
15+
UnrecognizedUWNetid, InvalidNetID, DelegateParameterException)
1516
from endorsement.util.auth import is_support_user
1617
from uw_msca.access_rights import get_access_rights
1718
from restclients_core.exceptions import DataFailureException
@@ -68,10 +69,16 @@ def post(self, request, *args, **kwargs):
6869
except InvalidNetID:
6970
return invalid_endorser(logger)
7071

71-
mailbox = request.data.get('mailbox', None)
72-
delegate = request.data.get('delegate', None)
73-
access_type = request.data.get('access_type', None)
74-
previous_access_type = request.data.get('previous_access_type', None)
72+
try:
73+
mailbox = self._validate_param(request.data, 'mailbox')
74+
delegate = self._validate_param(request.data, 'delegate')
75+
access_type = self._validate_param(request.data, 'access_type')
76+
previous_access_type = self._validate_param(
77+
request.data, 'previous_access_type', True)
78+
except DelegateParameterException as ex:
79+
message = f"Access.post parameter: {ex}"
80+
logger.error(message)
81+
return self.error_response(400, message=message)
7582

7683
if not is_office_permitted(mailbox):
7784
return invalid_endorser(logger)
@@ -89,7 +96,8 @@ def post(self, request, *args, **kwargs):
8996
accessee, accessor, access_type, acted_as)
9097
except DataFailureException as ex:
9198
logger.error(
92-
f"ERROR: set/revoke access {ex.url} ({ex.status}): {ex.msg}")
99+
f"Access.post {ex.url} FAILURE data: {request.data} "
100+
f"status: {ex.msg} ({ex.status})")
93101
return self.error_response(ex.status, message=ex.msg)
94102

95103
return self.json_response(access.json_data())
@@ -102,11 +110,16 @@ def patch(self, request, *args, **kwargs):
102110
except InvalidNetID:
103111
return invalid_endorser(logger)
104112

105-
action = request.data.get('action', None)
106-
mailbox = request.data.get('mailbox', None)
107-
delegate = request.data.get('delegate', None)
108-
access_type = request.data.get('access_type', None)
109-
previous_access_type = request.data.get('previous_access_type', None)
113+
try:
114+
action = self._validate_param(request.data, 'action', True)
115+
mailbox = self._validate_param(request.data, 'mailbox')
116+
delegate = self._validate_param(request.data, 'delegate')
117+
access_type = self._validate_param(request.data, 'access_type')
118+
previous_access_type = self._validate_param(
119+
request.data, 'previous_access_type', True)
120+
except DelegateParameterException as ex:
121+
logger.error(f"Access.patch parameter: {ex}")
122+
return self.error_response(400, message=str(ex))
110123

111124
if not is_office_permitted(mailbox):
112125
return invalid_endorser(logger)
@@ -123,10 +136,16 @@ def patch(self, request, *args, **kwargs):
123136
accessee, accessor, previous_access_type,
124137
access_type, acted_as)
125138
else:
139+
logger.error(
140+
f"Access.patch missing parameter: {request.data}")
126141
return self.error_response(404, message="Insufficient Data")
127142
except AccessRecord.DoesNotExist:
143+
logger.error(
144+
f"Access.patch missing access record: {request.data}")
128145
return self.error_response(404, message="Unknown Access Record")
129146
except DataFailureException as ex:
147+
logger.error(
148+
f"Access.patch request: '{ex.url}' ({ex.status}): {ex.msg}")
130149
return self.error_response(ex.status, message=ex.msg)
131150

132151
return self.json_response(access.json_data())
@@ -149,10 +168,30 @@ def delete(self, request, *args, **kwargs):
149168
try:
150169
access = revoke_access(accessee, accessor, access_type, acted_as)
151170
except DataFailureException as ex:
171+
logger.error(
172+
f"Access.delete request: {ex.url} ({ex.status}): {ex.msg}")
152173
return self.error_response(ex.status, message=ex.msg)
153174

154175
return self.json_response(access.json_data())
155176

177+
def _validate_param(self, data, field, nullable=False):
178+
try:
179+
value = data[field].strip()
180+
if not value and not nullable:
181+
raise DelegateParameterException(f"Empty {field} field")
182+
183+
return value
184+
except AttributeError as ex:
185+
if nullable:
186+
return None
187+
188+
raise DelegateParameterException(f"Null {field} field")
189+
except KeyError as ex:
190+
if nullable:
191+
return None
192+
193+
raise DelegateParameterException(f"Missing {field} field")
194+
156195
def _load_access_for_accessee(self, accessee):
157196
access = AccessRecord.objects.get_access_for_accessee(accessee)
158197
return [ar.json_data() for ar in access]

0 commit comments

Comments
 (0)