Skip to content

Commit d4a5e0e

Browse files
BLUEBUTTON-720 R29 patch (#698)
* Specify explicit migrations in order for Grant (#686) * Order grant migrations to work against r27 (#689) * Correct metrics view for data access grants (#693) * Recreate client_uri field to work with db (#692) * Recreate client_uri field to work with db * Remove Removal of client_uri migration * Make application info fields null-able * Document depreciation of client_uri (#694) * Trigger app_authorized signal on bene interaction (#695) * Trigger app_authorized signal on bene interaction * Correct migration order from r29 patch
1 parent 432fefd commit d4a5e0e

File tree

9 files changed

+117
-30
lines changed

9 files changed

+117
-30
lines changed

apps/authorization/migrations/0003_auto_20181203_1843.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ class Migration(migrations.Migration):
1414
atomic = False
1515

1616
dependencies = [
17-
('oauth2_provider', '__latest__'),
18-
('dot_ext', '__latest__'),
17+
('oauth2_provider', '0006_auto_20171214_2232'),
18+
('dot_ext', '0013_auto_20181221_2114'),
1919
('authorization', '0002_auto_20181203_1542'),
2020
]
2121

apps/authorization/signals.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
AccessToken = get_access_token_model()
99

1010

11-
def app_authorized_record_grant(sender, request, token, **kwargs):
11+
def app_authorized_record_grant(sender, request, token, application=None, **kwargs):
12+
bene = request.user
13+
if token is not None:
14+
bene = token.user
15+
application = token.application
16+
1217
DataAccessGrant.objects.get_or_create(
13-
beneficiary=token.user,
14-
application=token.application,
18+
beneficiary=bene,
19+
application=application,
1520
)
1621

1722

apps/dot_ext/migrations/0010_auto_20181219_2117.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ class Migration(migrations.Migration):
1212
]
1313

1414
operations = [
15-
migrations.RemoveField(
16-
model_name='application',
17-
name='client_uri',
18-
),
1915
migrations.AddField(
2016
model_name='application',
2117
name='website_uri',
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Generated by Django 2.1.2 on 2019-01-17 13:19
2+
3+
import apps.dot_ext.validators
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('dot_ext', '0013_auto_20181221_2114'),
13+
]
14+
15+
operations = [
16+
migrations.AlterField(
17+
model_name='application',
18+
name='client_uri',
19+
field=models.URLField(blank=True, default='', help_text='This is typically a home/download website for the application. For example, https://www.example.org or http://www.example.org .', max_length=512, null=True, verbose_name='Website URI'),
20+
),
21+
migrations.AlterField(
22+
model_name='application',
23+
name='description',
24+
field=models.TextField(blank=True, default='', help_text='This is plain-text up to 1000 characters in length.', max_length=1000, null=True, validators=[apps.dot_ext.validators.validate_notags], verbose_name='Application Description'),
25+
),
26+
migrations.AlterField(
27+
model_name='application',
28+
name='website_uri',
29+
field=models.URLField(blank=True, default='', help_text='This is typically a home/download website for the application. For example, https://www.example.org or http://www.example.org .', max_length=512, null=True, verbose_name='Website URI'),
30+
),
31+
]

apps/dot_ext/migrations/0014_auto_20190121_1345.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class Migration(migrations.Migration):
1010

1111
dependencies = [
12-
('dot_ext', '0013_auto_20181221_2114'),
12+
('dot_ext', '0014_auto_20190117_1319'),
1313
]
1414

1515
operations = [

apps/dot_ext/models.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ class Application(AbstractApplication):
3131
updated = models.DateTimeField(auto_now=True)
3232
op_tos_uri = models.CharField(default=settings.TOS_URI, blank=True, max_length=512)
3333
op_policy_uri = models.CharField(default="", blank=True, max_length=512)
34-
website_uri = models.URLField(default="", blank=True, max_length=512, verbose_name="Website URI",
34+
35+
# client_uri is depreciated but will continued to be referenced until it can be removed safely
36+
client_uri = models.URLField(default="", blank=True, null=True, max_length=512, verbose_name="Client URI",
37+
help_text="This is typically a home/download website for the application. "
38+
"For example, https://www.example.org or http://www.example.org .")
39+
40+
website_uri = models.URLField(default="", blank=True, null=True, max_length=512, verbose_name="Website URI",
3541
help_text="This is typically a home/download website for the application. "
3642
"For example, https://www.example.org or http://www.example.org .")
3743
help_text = _('Allowed redirect URIs. Space or new line separated.')
@@ -62,7 +68,7 @@ class Application(AbstractApplication):
6268
blank=True,
6369
null=True)
6470

65-
description = models.TextField(default="", blank=True, max_length=1000,
71+
description = models.TextField(default="", blank=True, null=True, max_length=1000,
6672
verbose_name="Application Description",
6773
help_text="This is plain-text up to 1000 characters in length.",
6874
validators=[validate_notags])

apps/dot_ext/tests/test_views.py

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from apps.test import BaseApiTest
1010
from ..models import Application
11+
from apps.authorization.models import DataAccessGrant
1112

1213

1314
class TestApplicationUpdateView(BaseApiTest):
@@ -91,24 +92,32 @@ def _create_test_token(self, user, application):
9192
'expires_in': 86400,
9293
'allow': True,
9394
}
94-
response = self.client.post(reverse('oauth2_provider:authorize'), data=payload)
95+
if application.authorization_grant_type == Application.GRANT_IMPLICIT:
96+
payload['response_type'] = 'token'
97+
response = self.client.post('/v1/o/authorize/', data=payload)
9598
self.client.logout()
99+
if response.status_code != 302:
100+
raise Exception(response.context_data)
96101
self.assertEqual(response.status_code, 302)
97102
# now extract the authorization code and use it to request an access_token
98-
query_dict = parse_qs(urlparse(response['Location']).query)
99-
authorization_code = query_dict.pop('code')
100-
token_request_data = {
101-
'grant_type': 'authorization_code',
102-
'code': authorization_code,
103-
'redirect_uri': application.redirect_uris,
104-
'client_id': application.client_id,
105-
'client_secret': application.client_secret,
106-
}
103+
if application.authorization_grant_type == Application.GRANT_IMPLICIT:
104+
fragment = parse_qs(urlparse(response['Location']).fragment)
105+
tkn = fragment.pop('access_token')[0]
106+
else:
107+
query_dict = parse_qs(urlparse(response['Location']).query)
108+
authorization_code = query_dict.pop('code')
109+
token_request_data = {
110+
'grant_type': 'authorization_code',
111+
'code': authorization_code,
112+
'redirect_uri': application.redirect_uris,
113+
'client_id': application.client_id,
114+
'client_secret': application.client_secret,
115+
}
107116

108-
response = self.client.post('/v1/o/token/', data=token_request_data)
109-
self.assertEqual(response.status_code, 200)
110-
# Now we have a token and refresh token
111-
tkn = response.json()['access_token']
117+
response = self.client.post('/v1/o/token/', data=token_request_data)
118+
self.assertEqual(response.status_code, 200)
119+
# Now we have a token and refresh token
120+
tkn = response.json()['access_token']
112121

113122
t = AccessToken.objects.get(token=tkn)
114123
return t
@@ -160,12 +169,29 @@ def test_delete_token_success(self):
160169
'an app', grant_type=Application.GRANT_AUTHORIZATION_CODE,
161170
redirect_uris='http://example.it')
162171
application.scope.add(capability_a)
172+
other_application = self._create_application(
173+
'another app', grant_type=Application.GRANT_IMPLICIT,
174+
client_type=Application.CLIENT_PUBLIC,
175+
redirect_uris='http://example.it',
176+
user=application.user,
177+
)
178+
other_application.scope.add(capability_a)
163179
tkn = self._create_test_token(anna, application)
180+
181+
self.assertTrue(DataAccessGrant.objects.filter(
182+
beneficiary=anna,
183+
application=application,
184+
).exists())
185+
164186
response = self.client.get('/v1/fhir/Patient',
165187
HTTP_AUTHORIZATION="Bearer " + tkn.token)
166188
self.assertEqual(response.status_code, 403)
167189

168-
bob_tkn = self._create_test_token(bob, application)
190+
bob_tkn = self._create_test_token(bob, other_application)
191+
self.assertTrue(DataAccessGrant.objects.filter(
192+
beneficiary=bob,
193+
application=other_application,
194+
).exists())
169195

170196
response = self.client.get(reverse('token_management:token-list'),
171197
HTTP_AUTHORIZATION=self._create_authorization_header(application.client_id,
@@ -186,11 +212,27 @@ def test_delete_token_success(self):
186212
response = self.client.get('/v1/fhir/Patient',
187213
HTTP_AUTHORIZATION="Bearer " + tkn.token)
188214
self.assertEqual(response.status_code, 401)
215+
216+
self.assertFalse(DataAccessGrant.objects.filter(
217+
beneficiary=anna,
218+
application=application,
219+
).exists())
220+
189221
response = self.client.get('/v1/fhir/Patient',
190222
HTTP_AUTHORIZATION="Bearer " + bob_tkn.token)
191223
# 403 is an expected response if the token is ok but the test can't reach the data server (which it shouldn't)
192224
self.assertEqual(response.status_code, 403)
193225

226+
next_tkn = self._create_test_token(anna, application)
227+
response = self.client.get('/v1/fhir/Patient',
228+
HTTP_AUTHORIZATION="Bearer " + next_tkn.token)
229+
self.assertEqual(response.status_code, 403)
230+
# self.assertEqual(next_tkn.token, tkn.token)
231+
self.assertTrue(DataAccessGrant.objects.filter(
232+
beneficiary=anna,
233+
application=application,
234+
).exists())
235+
194236
def test_create_token_fail(self):
195237
self._create_user(self.test_username, '123456')
196238

apps/dot_ext/views/authorization.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from oauth2_provider.views.base import AuthorizationView as DotAuthorizationView
33
from oauth2_provider.models import get_application_model
44
from oauth2_provider.exceptions import OAuthToolkitError
5+
from oauth2_provider.signals import app_authorized
56
from ..forms import SimpleAllowForm
67
from ..models import Approval
78

@@ -49,6 +50,12 @@ def form_valid(self, form):
4950
except OAuthToolkitError as error:
5051
return self.error_response(error, application)
5152

53+
app_authorized.send(
54+
sender=self,
55+
request=self.request,
56+
token=None,
57+
application=application)
58+
5259
self.success_url = uri
5360
log.debug("Success url for the request: {0}".format(self.success_url))
5461
return self.redirect(self.success_url, application)

apps/metrics/views.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,12 @@ class Meta:
246246

247247

248248
class ArchivedDataAccessGrantSerializer(ModelSerializer):
249-
user = UserSerializer(read_only=True)
249+
beneficiary = UserSerializer(read_only=True)
250250
application = ApplicationSerializer(read_only=True)
251251

252252
class Meta:
253253
model = ArchivedDataAccessGrant
254-
fields = ('beneficiary', 'application', 'created_at', 'archived_at', )
254+
fields = ('beneficiary', 'application', 'created_at', 'archived_at', 'id', )
255255

256256

257257
class ArchivedDataAccessGrantView(ListAPIView):
@@ -289,7 +289,7 @@ class DataAccessGrantSerializer(ModelSerializer):
289289

290290
class Meta:
291291
model = DataAccessGrant
292-
fields = ('beneficiary', 'application', 'created_at', )
292+
fields = ('beneficiary', 'application', 'created_at', 'id', )
293293

294294

295295
class DataAccessGrantView(ListAPIView):

0 commit comments

Comments
 (0)