From 8fa8394be4d34a5f5864869c29f7fde95b45ce6c Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Thu, 28 Sep 2017 21:30:56 +0200 Subject: [PATCH 1/4] add possibility to restrict all stations by wildcards in network/station code --- src/jane/stationxml/plugins.py | 19 +++++++++++++++++-- src/jane/waveforms/models.py | 14 +++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/jane/stationxml/plugins.py b/src/jane/stationxml/plugins.py index f216983..37642c9 100644 --- a/src/jane/stationxml/plugins.py +++ b/src/jane/stationxml/plugins.py @@ -65,8 +65,23 @@ def filter_queryset_user_does_not_have_permission(self, queryset, pass elif model_type == "index": for restriction in restrictions: - queryset = queryset.exclude(json__network=restriction.network, - json__station=restriction.station) + kwargs = {} + # XXX in principle this could be handled simply by using a + # regex field lookup on the json field below, but in Django < + # 1.11 there's a bug so the regex lookup doesn't work, see + # django/django#6929 + if restriction.network == '*' and restriction.station == '*': + # if both network and station are '*' then all stations are + # restricted + return queryset.none() + elif restriction.network == '*': + kwargs['json__station'] = restriction.station + elif restriction.station == '*': + kwargs['json__network'] = restriction.network + else: + kwargs['json__network'] = restriction.network + kwargs['json__station'] = restriction.station + queryset = queryset.exclude(**kwargs) else: raise NotImplementedError() return queryset diff --git a/src/jane/waveforms/models.py b/src/jane/waveforms/models.py index caae726..2849693 100644 --- a/src/jane/waveforms/models.py +++ b/src/jane/waveforms/models.py @@ -235,9 +235,17 @@ class Restriction(models.Model): Waveforms are generally seen as public if not listed here. """ - network = models.CharField(max_length=2, db_index=True) - station = models.CharField(max_length=5, db_index=True) - users = models.ManyToManyField(User, db_index=True) + help_text = ('Use a single asterisk/star (*) to affect all station ' + 'codes. Use a single asterisk/star in both fields, to ' + 'restrict all network/station code combinations.') + network = models.CharField(max_length=2, db_index=True, + help_text=help_text) + station = models.CharField(max_length=5, db_index=True, + help_text=help_text) + users = models.ManyToManyField( + User, db_index=True, + help_text='The restriction defined by network/station code above will ' + 'apply to all users that are not added here.') comment = models.TextField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True, editable=False) created_by = models.ForeignKey(User, null=True, editable=False, From e5e8286f836469f8345af233153c0271bccd4041 Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 29 Sep 2017 12:47:39 +0200 Subject: [PATCH 2/4] docs: add note on asterisk in station restrictions --- docs/docs/waveforms.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/docs/waveforms.md b/docs/docs/waveforms.md index e1d3173..a2f3bfe 100644 --- a/docs/docs/waveforms.md +++ b/docs/docs/waveforms.md @@ -145,6 +145,11 @@ per-station granularity. To do that, add a new restriction in the admin interface. As soon as a restriction has been added it will be considered protected and only users that are part of the restriction will still be able to access them. +Restrictions can also be defined with a single asterisk (`*`) in the station +(or network) code field, to make the restriction apply to all stations across a +specific network (to apply to all networks across a specific station code). Use +a single asterisk in *both* network and station code fields to add a +restriction on *all* stations. ![Add waveform restriction](./images/add_waveform_restriction.png) From 7ceb96414d661e30264bc742b91b9cd81bcbcb9f Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Fri, 29 Sep 2017 15:48:04 +0200 Subject: [PATCH 3/4] add test for wildcarded station restrictions --- src/jane/fdsnws/tests/test_station_1.py | 132 ++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/src/jane/fdsnws/tests/test_station_1.py b/src/jane/fdsnws/tests/test_station_1.py index 15b479b..b350d54 100644 --- a/src/jane/fdsnws/tests/test_station_1.py +++ b/src/jane/fdsnws/tests/test_station_1.py @@ -423,6 +423,138 @@ def test_restrictions(self): **auth_headers) self.assertEqual(response.status_code, 204) + def test_restrictions_asterisk_network_and_station(self): + """ + Tests if the waveform restrictions actually work as expected. + """ + # No restrictions currently apply - we should get something. + response = self.client.get('/fdsnws/station/1/query') + self.assertEqual(response.status_code, 200) + self.assertTrue('OK' in response.reason_phrase) + inv = obspy.read_inventory(io.BytesIO(response.getvalue())) + self.assertEqual(inv.get_contents()["stations"], + ["BW.ALTM (Beilngries, Bavaria, BW-Net)"]) + + # add restriction on all stations + r = Restriction.objects.get_or_create(network="*", station="*")[0] + r.users.add(User.objects.filter(username='random')[0]) + r.save() + + # Now the same query should no longer return something as the + # station has been restricted. + response = self.client.get('/fdsnws/station/1/query') + self.assertEqual(response.status_code, 204) + + # The correct user can still get the stations. + response = self.client.get('/fdsnws/station/1/queryauth', + **self.valid_auth_headers) + self.assertEqual(response.status_code, 200) + self.assertTrue('OK' in response.reason_phrase) + inv = obspy.read_inventory(io.BytesIO(response.getvalue())) + self.assertEqual(inv.get_contents()["stations"], + ["BW.ALTM (Beilngries, Bavaria, BW-Net)"]) + + # Make another user that has not been added to this restriction - he + # should not be able to retrieve it. + self.client.logout() + User.objects.get_or_create( + username='some_dude', password=make_password('some_dude'))[0] + credentials = base64.b64encode(b'some_dude:some_dude') + auth_headers = { + 'HTTP_AUTHORIZATION': 'Basic ' + credentials.decode("ISO-8859-1") + } + response = self.client.get('/fdsnws/station/1/queryauth', + **auth_headers) + self.assertEqual(response.status_code, 204) + + def test_restrictions_asterisk_network(self): + """ + Tests if the waveform restrictions actually work as expected. + """ + # No restrictions currently apply - we should get something. + response = self.client.get('/fdsnws/station/1/query') + self.assertEqual(response.status_code, 200) + self.assertTrue('OK' in response.reason_phrase) + inv = obspy.read_inventory(io.BytesIO(response.getvalue())) + self.assertEqual(inv.get_contents()["stations"], + ["BW.ALTM (Beilngries, Bavaria, BW-Net)"]) + + # add restriction on ALTM stations + r = Restriction.objects.get_or_create(network="*", station="ALTM")[0] + r.users.add(User.objects.filter(username='random')[0]) + r.save() + + # Now the same query should no longer return something as the + # station has been restricted. + response = self.client.get('/fdsnws/station/1/query') + self.assertEqual(response.status_code, 204) + + # The correct user can still get the stations. + response = self.client.get('/fdsnws/station/1/queryauth', + **self.valid_auth_headers) + self.assertEqual(response.status_code, 200) + self.assertTrue('OK' in response.reason_phrase) + inv = obspy.read_inventory(io.BytesIO(response.getvalue())) + self.assertEqual(inv.get_contents()["stations"], + ["BW.ALTM (Beilngries, Bavaria, BW-Net)"]) + + # Make another user that has not been added to this restriction - he + # should not be able to retrieve it. + self.client.logout() + User.objects.get_or_create( + username='some_dude', password=make_password('some_dude'))[0] + credentials = base64.b64encode(b'some_dude:some_dude') + auth_headers = { + 'HTTP_AUTHORIZATION': 'Basic ' + credentials.decode("ISO-8859-1") + } + response = self.client.get('/fdsnws/station/1/queryauth', + **auth_headers) + self.assertEqual(response.status_code, 204) + + def test_restrictions_asterisk_station(self): + """ + Tests if the waveform restrictions actually work as expected. + """ + # No restrictions currently apply - we should get something. + response = self.client.get('/fdsnws/station/1/query') + self.assertEqual(response.status_code, 200) + self.assertTrue('OK' in response.reason_phrase) + inv = obspy.read_inventory(io.BytesIO(response.getvalue())) + self.assertEqual(inv.get_contents()["stations"], + ["BW.ALTM (Beilngries, Bavaria, BW-Net)"]) + + # add restriction on all BW-network stations + r = Restriction.objects.get_or_create(network="BW", station="*")[0] + r.users.add(User.objects.filter(username='random')[0]) + r.save() + + # Now the same query should no longer return something as the + # station has been restricted. + response = self.client.get('/fdsnws/station/1/query') + self.assertEqual(response.status_code, 204) + + # The correct user can still get the stations. + response = self.client.get('/fdsnws/station/1/queryauth', + **self.valid_auth_headers) + self.assertEqual(response.status_code, 200) + self.assertTrue('OK' in response.reason_phrase) + inv = obspy.read_inventory(io.BytesIO(response.getvalue())) + self.assertEqual(inv.get_contents()["stations"], + ["BW.ALTM (Beilngries, Bavaria, BW-Net)"]) + + # Make another user that has not been added to this restriction - he + # should not be able to retrieve it. + self.client.logout() + User.objects.get_or_create( + username='some_dude', password=make_password('some_dude'))[0] + credentials = base64.b64encode(b'some_dude:some_dude') + auth_headers = { + 'HTTP_AUTHORIZATION': 'Basic ' + credentials.decode("ISO-8859-1") + } + response = self.client.get('/fdsnws/station/1/queryauth', + **auth_headers) + self.assertEqual(response.status_code, 204) + class Station1LiveServerTestCase(LiveServerTestCase): """ From 3862ae957f1609cf0b81a0e97598d92f85f6609f Mon Sep 17 00:00:00 2001 From: Tobias Megies Date: Mon, 10 Feb 2020 13:38:46 +0100 Subject: [PATCH 4/4] add migration for waveforms help text changes --- .../migrations/0003_auto_20200131_1056.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/jane/waveforms/migrations/0003_auto_20200131_1056.py diff --git a/src/jane/waveforms/migrations/0003_auto_20200131_1056.py b/src/jane/waveforms/migrations/0003_auto_20200131_1056.py new file mode 100644 index 0000000..407c0c4 --- /dev/null +++ b/src/jane/waveforms/migrations/0003_auto_20200131_1056.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2020-01-31 10:56 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('waveforms', '0002_auto_20160706_1508'), + ] + + operations = [ + migrations.AlterField( + model_name='restriction', + name='network', + field=models.CharField(db_index=True, help_text='Use a single asterisk/star (*) to affect all station codes. Use a single asterisk/star in both fields, to restrict all network/station code combinations.', max_length=2), + ), + migrations.AlterField( + model_name='restriction', + name='station', + field=models.CharField(db_index=True, help_text='Use a single asterisk/star (*) to affect all station codes. Use a single asterisk/star in both fields, to restrict all network/station code combinations.', max_length=5), + ), + migrations.AlterField( + model_name='restriction', + name='users', + field=models.ManyToManyField(db_index=True, help_text='The restriction defined by network/station code above will apply to all users that are not added here.', to=settings.AUTH_USER_MODEL), + ), + ]