...
 
Commits (53)
......@@ -58,8 +58,6 @@ before_script:
libxml2-dev
swig
${PYTHON}-pip
# install voeventlib for python2
- if [[ "${PYTHON_MAJOR}" -eq 2 ]]; then apt-get -o dir::cache::archives="${APT_CACHE_DIR}" install -yqq python-voeventlib; fi
# install everything else from pip
- ${PYTHON} -m pip install -r requirements.txt
# create logs path required for tests
......
......@@ -28,7 +28,6 @@ RUN apt-get update && \
python2.7-dev \
python-libxml2 \
python-pip \
python-voeventlib \
procps \
shibboleth \
supervisor \
......
......@@ -21,6 +21,10 @@ def get_from_env(envvar, default_value=None, fail_if_not_found=True):
'Could not get environment variable {0}'.format(envvar))
return value
# Maintenance mode
MAINTENANCE_MODE = False
MAINTENANCE_MODE_MESSAGE = None
# Version ---------------------------------------------------------------------
PROJECT_VERSION = '2.6.3'
......@@ -307,6 +311,7 @@ AUTHENTICATION_BACKENDS = [
# List of middleware classes to use.
MIDDLEWARE = [
'core.middleware.maintenance.MaintenanceModeMiddleware',
'events.middleware.PerformanceMiddleware',
'core.middleware.accept.AcceptMiddleware',
'core.middleware.api.ClientVersionMiddleware',
......
......@@ -61,6 +61,18 @@ TWILIO_AUTH_TOKEN = os.environ.get('DJANGO_TWILIO_AUTH_TOKEN', None)
if TWILIO_AUTH_TOKEN is None:
raise ImproperlyConfigured('Could not get Twilio auth token from envvars.')
# Get maintenance mode settings from environment
maintenance_mode = get_from_env(
'DJANGO_MAINTENANCE_MODE_ACTIVE',
default_value=False,
fail_if_not_found=False
)
if (isinstance(maintenance_mode, str) and
maintenance_mode.lower() in ['true', 't']):
MAINTENANCE_MODE = True
MAINTENANCE_MODE_MESSAGE = \
get_from_env('DJANGO_MAINTENANCE_MODE_MESSAGE', fail_if_not_found=False)
# Get email settings from environment
EMAIL_BACKEND = 'django_ses.SESBackend'
AWS_SES_ACCESS_KEY_ID = get_from_env('AWS_SES_ACCESS_KEY_ID')
......
......@@ -33,6 +33,7 @@ ALLOWED_HOSTS += ['testserver']
# Home page stuff
INSTANCE_TITLE = 'GraceDB Playground'
INSTANCE_INFO = """
<h3>Playground instance</h3>
<p>
This GraceDB instance is designed for users to develop and test their own
applications. It mimics the production instance in all but the following ways:
......
......@@ -2,7 +2,7 @@ from django.conf import settings
from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.auth.views import logout
from django.contrib.auth.views import LogoutView
from django.views.generic import TemplateView
# Import feeds
......@@ -10,7 +10,9 @@ import core.views
from events.feeds import EventFeed, feedview
import events.reports
import events.views
from ligoauth.views import pre_login, post_login, shib_logout, manage_password
from ligoauth.views import (
manage_password, ShibLoginView, ShibPostLoginView
)
import search.views
# Django admin auto-discover
......@@ -49,9 +51,9 @@ urlpatterns = [
url(r'^search/$', search.views.search, name="mainsearch"),
# Authentication
url(r'^login/$', pre_login, name='login'),
url(r'^post-login/$', post_login, name='post-login'),
url(r'^logout/$', shib_logout, name='logout'),
url(r'^login/$', ShibLoginView.as_view(), name='login'),
url(r'^post-login/$', ShibPostLoginView.as_view(), name='post-login'),
url(r'^logout/$', LogoutView.as_view(), name='logout'),
# Password management
url('^manage-password/$', manage_password, name='manage-password'),
......
......@@ -8,8 +8,14 @@ Run ./check_shibboleth_status -h for help.
'''
# Imports
import argparse, urllib2, sys
import argparse
import sys
import xml.etree.ElementTree as ET
try:
from urllib.request import urlopen
from urllib.error import URLError
except ImportError: # python < 3
from urllib2 import (urlopen, URLError)
# Parameters - may need to be modified in the future
# if Shibboleth status pages change or new metadata
......@@ -48,12 +54,12 @@ metadata_feeds = args.feeds.split(",")
# Get XML data from URL.
host_url = host + "/" + urlpath
try:
response = urllib2.urlopen(host_url, timeout=timeout)
except urllib2.URLError:
print "Error opening Shibboleth status page (" + host_url + ")."
response = urlopen(host_url, timeout=timeout)
except URLError:
print("Error opening Shibboleth status page (" + host_url + ").")
sys.exit(2)
except:
print "Unknown error opening Shibboleth status page (" + host_url + ")."
print("Unknown error opening Shibboleth status page (" + host_url + ").")
sys.exit(3)
# Convert from string to ElementTree
......@@ -61,11 +67,11 @@ try:
status_tree = ET.fromstring(response.read())
except ET.ParseError:
# Error parsing response.
print "Error parsing response from server - not in XML format."
print("Error parsing response from server - not in XML format.")
sys.exit(2)
except:
# Error that is not ParseError.
print "Unknown error occurred when parsing response from server."
print("Unknown error occurred when parsing response from server.")
sys.exit(3)
response.close()
......@@ -75,12 +81,12 @@ response.close()
for tag in tags_to_check:
status_tag = status_tree.find(tag)
if (status_tag is None):
print "Error: tag \'" + tag + "\' not found."
print("Error: tag \'" + tag + "\' not found.")
sys.exit(2)
else:
status_OK = status_tag.find('OK')
if (status_OK is None):
print "Error: tag \'" + tag + "\' is not OK."
print("Error: tag \'" + tag + "\' is not OK.")
sys.exit(2)
# Check 2: make sure metadata feeds that we expect
......@@ -90,12 +96,12 @@ srcs = [element.attrib['source'] for element in metaprov_tags]
for feed in metadata_feeds:
feed_found = [src.lower().find(feed) >= 0 for src in srcs]
if (sum(feed_found) < 1):
print "MetadataProvider " + feed + " not found."
print("MetadataProvider " + feed + " not found.")
sys.exit(2)
elif (sum(feed_found) < 1):
print "MetadataProvider " + feed + "found in multiple elements."
print("MetadataProvider " + feed + "found in multiple elements.")
sys.exit(2)
# If we make it to this point, everything is OK.
print "All MetadataProviders found. Status and SessionCache are OK."
print("All MetadataProviders found. Status and SessionCache are OK.")
sys.exit(0)
......@@ -114,7 +114,7 @@ example, here is how to grant ``view`` permissions to ``public``::
group_name = 'public'
perm_shortname = 'view'
url = g.service_url + urllib.quote('events/%s/%s/%s' % (graceid, group_name, perm_codename))
url = g.service_url + urllib.parse.quote('events/%s/%s/%s' % (graceid, group_name, perm_codename))
r = g.put(url)
Templates
......
......@@ -62,7 +62,7 @@ in real life (from inside the Django shell)::
>>> u = User.objects.get(username='albert.einstein@LIGO.ORG')
>>> if p in u.user_permissions.all():
...: print "Albert can add events!"
...: print("Albert can add events!")
The Django ``User`` class has a convenience function ``has_perm`` to
make this easier::
......@@ -72,7 +72,7 @@ make this easier::
>>> u = User.objects.get(username='albert.einstein@LIGO.ORG')
>>> if u.has_perm('events.add_event'):
...: print "Albert can add events!"
...: print("Albert can add events!")
Again, notice that the ``has_perm`` function needs the codename to be scoped by
the app to which the model belongs. Both are required to fully specify the model.
......@@ -124,7 +124,7 @@ event data. Thus, we have added a custom ``view`` permission for the event model
>>> perms = Permission.objects.filter(codename__startswith='view')
>>> for p in perms:
...: print p.codename
...: print(p.codename)
...:
view_coincinspiralevent
view_event
......@@ -314,7 +314,7 @@ can be done by adding the permission by hand::
>>> u = User.objects.get(username='albert.einstein@LIGO.ORG')
>>> u.user_permissions.add(p):
...: print "Albert can add events!"
...: print("Albert can add events!")
Granting permission to populate a pipeline
------------------------------------------
......
......@@ -75,9 +75,9 @@ Shibbolized client as follows::
try:
r = client.ping()
except HTTPError, e:
print e.message
except HTTPError as e:
print(e.message)
print "Response code: %d" % r.status
print "Response content: %s" % r.json()
print("Response code: %d" % r.status)
print("Response content: %s" % r.json())
......@@ -76,11 +76,11 @@ client class is specially formulated for basic auth::
try:
r = client.ping()
except HTTPError, e:
print e.message
print "Response code: %d" % r.status
print "Response content: %s" % r.json()
except HTTPError as e:
print(e.message)
print("Response code: %d" % r.status)
print("Response content: %s" % r.json())
The only real difference is that the ``GraceDbBasic`` client class is used instead
of the ``GraceDb`` class (which assumes that X509 credentials are available).
......@@ -147,7 +147,7 @@ observation record consisting of three separate footprints::
decList, decWidthList, startTimeList, durationList, comment)
if r.status == 201: # 201 means 'Created'
print 'Success!'
print('Success!')
Note that the start times are always assumed to be in UTC. For users not
familiar with Python, there are several other options available for uploading
......
......@@ -321,7 +321,7 @@ Create a Python executable ``iReact.py`` and fill it with the following::
import sys
alert = json.loads(sys.stdin.read())
print 'uid : ' + alert['uid']
print('uid : ' + alert['uid'])
Don't forget to give this executable permissions with::
......@@ -369,7 +369,7 @@ Open ``iReact.py`` and modify it so it reads::
from ligo.gracedb.rest import GraceDb
alert = json.loads(sys.stdin.read())
print 'uid : ' + alert['uid']
print('uid : ' + alert['uid'])
gdb = GraceDb() ### instantiate a GraceDB object which connects to the default server
......@@ -411,15 +411,14 @@ more example for what ``iReact.py`` might look like::
FarThr = float(sys.argv[1])
alert = json.loads(sys.stdin.read())
print 'uid : '+alert['uid']
print('uid : '+alert['uid'])
gdb = GraceDb() ### instantiate a GraceDB object which connects to the default server
if alert['alert_type'] == 'new': ### the event was just created and this is the first announcment
if alert['far'] < FarThr:
file_obj = open("iReact.txt", "w")
print >> file_obj, "wow! this was a rare event! It had FAR = %.3e < %.3e, which was my threshold"%(alert['far'], FarThr)
file_obj.close()
with open("iReact.txt", "w") as file_obj:
print("wow! this was a rare event! It had FAR = %.3e < %.3e, which was my threshold"%(alert['far'], FarThr), file=file_obj)
gdb.writeLog( alert['uid'], message="user.name heard an alert about this new event!", filename="iReact.txt", tagname=["data_quality"] )
Try to figure out exactly what this version does. If you can
......
......@@ -70,11 +70,11 @@ instantiating the client, and then calling the desired method::
try:
r = client.ping()
except HTTPError, e:
print e.message
except HTTPError as e:
print(e.message)
print "Response code: %d" % r.status
print "Response content: %s" % r.json()
print("Response code: %d" % r.status)
print("Response content: %s" % r.json())
In the above example, we merely ping the GraceDB server and examine the
......@@ -319,10 +319,10 @@ in a ``while`` loop in the following way::
try:
r = gracedb.writeLog(graceid, "Hello, this is a log message.")
success = True
except HTTPError, e:
except HTTPError as e:
try:
rdict = json.loads(e.message)
if 'retry-after' in rdict.keys():
if 'retry-after' in rdict:
time.sleep(int(rdict['retry-after']))
continue
else:
......
......@@ -8,7 +8,8 @@ from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
from django.core.mail import EmailMessage
from django.db import models
from django.utils import timezone
from django.utils import six, timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.http import urlencode
from django_twilio.client import twilio_client
......@@ -55,7 +56,6 @@ class Contact(CleanSaveModel):
updated = models.DateTimeField(auto_now=True)
verified_time = models.DateTimeField(null=True, blank=True, editable=False)
def __str__(self):
return "{0}: {1}".format(self.user.username, self.description)
......@@ -168,6 +168,7 @@ class Contact(CleanSaveModel):
###############################################################################
# Notifications ###############################################################
###############################################################################
@python_2_unicode_compatible
class Notification(models.Model):
# Notification categories
NOTIFICATION_CATEGORY_EVENT = 'E'
......@@ -196,10 +197,12 @@ class Notification(models.Model):
pipelines = models.ManyToManyField('events.pipeline', blank=True)
searches = models.ManyToManyField('events.search', blank=True)
def __unicode__(self):
return (u"%s: %s") % (
self.user.username,
self.display()
def __str__(self):
return six.text_type(
"{username}: {display}".format(
username=self.user.username,
display=self.display()
)
)
def display(self):
......
try:
from functools import reduce
except ImportError: # python < 3
pass
from django.conf import settings
from django.db.models import Q
......
import mock
try:
from unittest import mock
except ImportError: # python < 3
import mock
from django.conf import settings
from django.contrib.auth.models import Group as AuthGroup
......
import mock
try:
from unittest import mock
except ImportError: # python < 3
import mock
from django.test import override_settings
......
import mock
try:
from unittest import mock
except ImportError: # python < 3
import mock
from django.conf import settings
from django.test import override_settings
......
import mock
try:
from unittest import mock
except ImportError: # python < 3
import mock
from django.test import override_settings
......
import mock
try:
from unittest import mock
except ImportError: # python < 3
import mock
import pytest
from django.conf import settings
......
......@@ -65,8 +65,7 @@ class CreateNotificationView(MultipleFormView):
return kw
def form_valid(self, form):
if form.cleaned_data.has_key('key_field'):
form.cleaned_data.pop('key_field')
form.cleaned_data.pop('key_field', None)
# Add user (from request) and category (stored on form class) to
# the form instance, then save
......@@ -169,8 +168,7 @@ class CreateContactView(MultipleFormView):
def form_valid(self, form):
# Remove key_field, add user, and save form
if form.cleaned_data.has_key('key_field'):
form.cleaned_data.pop('key_field')
form.cleaned_data.pop('key_field', None)
form.instance.user = self.request.user
form.save()
......
......@@ -13,7 +13,8 @@ def gracedb_exception_handler(exc, context):
if hasattr(exc, 'detail') and hasattr(exc.detail, 'values'):
# Combine values into one list
exc_out = [item for sublist in exc.detail.values() for item in sublist]
exc_out = [item for sublist in list(exc.detail.values())
for item in sublist]
# For only one exception, just print it rather than the list
if len(exc_out) == 1:
......
from base64 import b64encode
import mock
try:
from unittest import mock
except ImportError: # python < 3
import mock
from django.conf import settings
from django.urls import reverse
......@@ -31,9 +34,12 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
"""User can authenticate to API with correct password"""
# Set up and make request
url = api_reverse('api:root')
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password=self.password)) \
.decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password=self.password
).encode()
).decode("ascii")
headers = {
'HTTP_AUTHORIZATION': 'Basic {0}'.format(user_and_pass),
}
......@@ -53,16 +59,20 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
"""User can't authenticate with wrong password"""
# Set up and make request
url = api_reverse('api:root')
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password='b4d')).decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password='b4d'
).encode()
).decode("ascii")
headers = {
'HTTP_AUTHORIZATION': 'Basic {0}'.format(user_and_pass),
}
response = self.client.get(url, data=None, **headers)
# Check response
self.assertEqual(response.status_code, 403)
self.assertIn('Invalid username/password', response.content)
self.assertContains(response, 'Invalid username/password',
status_code=403)
def test_user_authenticate_to_api_with_expired_password(self):
"""User can't authenticate with expired password"""
......@@ -73,17 +83,20 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
# Set up and make request
url = api_reverse('api:root')
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password=self.password)) \
.decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password=self.password
).encode()
).decode("ascii")
headers = {
'HTTP_AUTHORIZATION': 'Basic {0}'.format(user_and_pass),
}
response = self.client.get(url, data=None, **headers)
# Check response
self.assertEqual(response.status_code, 403)
self.assertIn('Your password has expired', response.content)
self.assertContains(response, 'Your password has expired',
status_code=403)
class TestGraceDbX509Authentication(GraceDbApiTestBase):
......@@ -139,8 +152,8 @@ class TestGraceDbX509Authentication(GraceDbApiTestBase):
response = self.client.get(url, data=None, **headers)
# Check response
self.assertEqual(response.status_code, 401)
self.assertIn("Invalid certificate subject", response.content)
self.assertContains(response, 'Invalid certificate subject',
status_code=401)
def test_inactive_user_authenticate(self):
"""Inactive user can't authenticate"""
......@@ -156,8 +169,8 @@ class TestGraceDbX509Authentication(GraceDbApiTestBase):
response = self.client.get(url, data=None, **headers)
# Check response
self.assertEqual(response.status_code, 401)
self.assertIn("User inactive or deleted", response.content)
self.assertContains(response, 'User inactive or deleted',
status_code=401)
def test_authenticate_cert_with_proxy(self):
"""User can authenticate to API with proxied X509 certificate"""
......
......@@ -45,9 +45,12 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
"""User can authenticate to API with correct password"""
# Set up request
request = self.factory.get(api_reverse('api:root'))
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password=self.password)) \
.decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password=self.password
).encode()
).decode("ascii")
request.META['HTTP_AUTHORIZATION'] = 'Basic {0}'.format(user_and_pass)
# Authentication attempt
......@@ -60,8 +63,12 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
"""User can't authenticate with wrong password"""
# Set up request
request = self.factory.get(api_reverse('api:root'))
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password='b4d')).decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password='b4d'
).encode()
).decode("ascii")
request.META['HTTP_AUTHORIZATION'] = 'Basic {0}'.format(user_and_pass)
# Authentication attempt should fail
......@@ -77,9 +84,12 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
# Set up request
request = self.factory.get(api_reverse('api:root'))
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password=self.password)) \
.decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password=self.password
).encode()
).decode("ascii")
request.META['HTTP_AUTHORIZATION'] = 'Basic {0}'.format(user_and_pass)
# Authentication attempt should fail
......@@ -91,9 +101,12 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
"""User can't authenticate to a non-API URL path"""
# Set up request
request = self.factory.get(reverse('home'))
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password=self.password)) \
.decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password=self.password
).encode()
).decode("ascii")
request.META['HTTP_AUTHORIZATION'] = 'Basic {0}'.format(user_and_pass)
# Try to authenticate
......@@ -108,9 +121,12 @@ class TestGraceDbBasicAuthentication(GraceDbApiTestBase):
# Set up request
request = self.factory.get(api_reverse('api:root'))
user_and_pass = b64encode(b"{username}:{password}".format(
username=self.lvem_user.username, password=self.password)) \
.decode("ascii")
user_and_pass = b64encode(
"{username}:{password}".format(
username=self.lvem_user.username,
password=self.password
).encode()
).decode("ascii")
request.META['HTTP_AUTHORIZATION'] = 'Basic {0}'.format(user_and_pass)
# Authentication attempt should fail
......
import mock
try:
from unittest import mock
except ImportError: # python < 3
import mock
from django.conf import settings
from django.core.cache import caches
......@@ -24,5 +27,4 @@ class TestThrottling(GraceDbApiTestBase):
# Second response should get throttled
response = self.request_as_user(url, "GET")
self.assertEqual(response.status_code, 429)
self.assertIn('Request was throttled', response.content)
self.assertContains(response, 'Request was throttled', status_code=429)
from copy import deepcopy
import mock
try:
from functools import reduce
except ImportError: # python < 3
pass
try:
from unittest import mock
except ImportError: # python < 3
import mock
from django.conf import settings
from django.core.cache import caches
......
import logging
from rest_framework import permissions
# Set up logger
logger = logging.getLogger(__name__)
class CanUpdateGrbEvent(permissions.BasePermission):
def has_permission(self, request, view):
return request.user.has_perm('events.t90_grbevent')
This diff is collapsed.
try:
from unittest import mock
except ImportError: # python < 3
import mock
import pytest
from django.conf import settings
from django.db.models import Q
from django.urls import reverse
from rest_framework.test import APIRequestFactory as rf
from events.models import GrbEvent
from ..views import GrbEventPatchView
from ...settings import API_VERSION
def v_reverse(viewname, *args, **kwargs):
"""Easily customizable versioned API reverse for testing"""
viewname = 'api:{version}:'.format(version=API_VERSION) + viewname
return reverse(viewname, *args, **kwargs)
def test_access(mock_internal_user):
# Create a mock event
mock_event = mock.MagicMock(spec=GrbEvent, graceid='E1234')
mock_event.search.name = 'GRB'
mock_event.redshift = 3
# Get URL and set up request
url = v_reverse("events:update-grbevent", args=[mock_event.graceid])
data = {'redshift': 2}
request = rf().patch(url, data=data)
request.user = mock_internal_user
view = GrbEventPatchView.as_view()
with mock.patch('api.v1.events.views.EventAlertIssuer'), \
mock.patch('api.v1.events.views.Event.getByGraceid') as mock_get_event, \
mock.patch('api.v1.events.views.eventToDict'):
mock_get_event.return_value = mock_event
response = view(request, mock_event.graceid)
response.render()
assert mock_get_event.call_args[0] == (mock_event.graceid,)
......@@ -9,6 +9,8 @@ urlpatterns = [
url(r'^$', EventList.as_view(), name='event-list'),
url(r'^(?P<graceid>[GEHMT]\d+)$', EventDetail.as_view(),
name='event-detail'),
url(r'^(?P<graceid>[GEHMT]\d+)/update-grbevent/$',
GrbEventPatchView.as_view(), name='update-grbevent'),
# Event Log Resources
# events/{graceid}/logs/[{logid}]
......
This diff is collapsed.
......@@ -122,8 +122,8 @@ class GenericField(fields.Field):
return self.model.objects.get(**model_dict)
except self.model.DoesNotExist:
error_msg = '{model} with {lf}={data} does not exist' \
.format(model=self.model.__name__, lf=model_dict.keys()[0],
data=model_dict.values()[0])
.format(model=self.model.__name__, lf=list(model_dict)[0],
data=list(model_dict.values())[0])
raise exceptions.ValidationError(error_msg)
def get_model_dict(self, data):
......
......@@ -31,14 +31,18 @@ class TestPublicAccess(GraceDbApiTestBase):
"""Unauthenticated user can't access performance info"""
url = v_reverse('performance-info')
response = self.request_as_user(url, "GET")
self.assertEqual(response.status_code, 403)
self.assertIn("Authentication credentials were not provided",
response.content)
self.assertContains(
response,
'Authentication credentials were not provided',
status_code=403
)
def test_lvem_user_performance_info(self):
"""LV-EM user can't access performance info"""
url = v_reverse('performance-info')
response = self.request_as_user(url, "GET", self.lvem_user)
self.assertEqual(response.status_code, 403)
self.assertIn("Forbidden", response.content)
self.assertContains(
response,
'Forbidden',
status_code=403
)
......@@ -46,5 +46,5 @@ class TestUserInfoView(GraceDbApiTestBase):
self.assertEqual(response.status_code, 200)
# Test information
self.assertEqual(response.data.keys(), ['username'])
self.assertEqual(list(response.data), ['username'])
self.assertEqual(response.data['username'], 'AnonymousUser')
......@@ -103,6 +103,10 @@ class GracedbRoot(APIView):
signofflist = api_reverse("events:signoff-list", args=["G1200"], request=request)
signofflist = signofflist.replace("G1200", "{graceid}")
update_grbevent = api_reverse("events:update-grbevent", args=["G1200"],
request=request)
update_grbevent = update_grbevent.replace("G1200", "{graceid}")
# XXX Need a template for the tag list?
templates = {
......@@ -119,6 +123,7 @@ class GracedbRoot(APIView):
"tag-template" : tag,
"taglist-template" : taglist,
"signoff-list-template": signofflist,
"update-grbevent-template": update_grbevent,
}
# Get superevent templates
......@@ -180,7 +185,7 @@ class PerformanceInfo(InheritDefaultPermissionsMixin, APIView):
try:
performance_info = get_performance_info()
except Exception, e:
except Exception as e:
return Response(str(e),
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
......
from collections import OrderedDict
import logging
import urllib
from django.utils.http import urlencode
from rest_framework import pagination
from rest_framework.response import Response
......@@ -80,7 +81,7 @@ class CustomSupereventPagination(pagination.LimitOffsetPagination):
'start': last,
self.limit_query_param: self.limit,
}
last_uri = base_uri + '?' + urllib.urlencode(param_dict)
last_uri = base_uri + '?' + urlencode(param_dict)
output = OrderedDict([
('numRows', numRows),
......
......@@ -58,7 +58,7 @@ class SupereventOrderingFilter(filters.OrderingFilter):
for f in fields:
prefix = '-' if f.startswith('-') else ''
f_s = f.lstrip('-')
if f_s in self.field_map.keys():
if f_s in self.field_map:
mapped_fields = self.field_map[f_s]
if not isinstance(mapped_fields, list):
mapped_fields = [mapped_fields]
......
from collections import OrderedDict
import logging
import urllib
from django.utils.http import urlencode
from rest_framework import pagination
from rest_framework.response import Response
......@@ -26,7 +27,7 @@ class CustomSupereventPagination(pagination.LimitOffsetPagination):
'start': last,
self.limit_query_param: self.limit,
}
last_uri = base_uri + '?' + urllib.urlencode(param_dict)
last_uri = base_uri + '?' + urlencode(param_dict)
output = OrderedDict([
('numRows', numRows),
......
......@@ -783,7 +783,7 @@ class SupereventEMObservationSerializer(serializers.ModelSerializer):
list_length = len(ra_list)
all_lists = (ra_list, dec_list, ra_width_list, dec_width_list,
start_time_list, duration_list)
if not all(map(lambda l: len(l) == list_length, all_lists)):
if not all(list(map(lambda l: len(l) == list_length, all_lists))):
self.fail('list_lengths')
return data
......
......@@ -45,12 +45,12 @@ class TestSupereventSerializerViaWeb(SupereventSetup, GraceDbApiTestBase):
response = self.request_as_user(url, "GET", self.internal_user)
self.assertEqual(response.status_code, 200)
# Check data on page
response_keys = response.json().keys()
response_keys = list(response.json())
response_links = response.json()['links']
self.assertIn('preferred_event', response_keys)
self.assertIn('gw_events', response_keys)
self.assertIn('em_events', response_keys)
self.assertIn('events', response_links.keys())
self.assertIn('events', response_links)
def test_lvem_user_get_superevent_detail(self):
"""LV-EM user sees events link and all event graceids"""
......@@ -61,12 +61,12 @@ class TestSupereventSerializerViaWeb(SupereventSetup, GraceDbApiTestBase):
response = self.request_as_user(url, "GET", self.lvem_user)
self.assertEqual(response.status_code, 200)
# Check data on page
response_keys = response.json().keys()
response_keys = list(response.json())
response_links = response.json()['links']
self.assertIn('preferred_event', response_keys)
self.assertIn('gw_events', response_keys)
self.assertIn('em_events', response_keys)
self.assertIn('events', response_links.keys())
self.assertIn('events', response_links)
def test_public_user_get_superevent_detail(self):
"""Public user does not see events link or all event graceids"""
......@@ -77,9 +77,9 @@ class TestSupereventSerializerViaWeb(SupereventSetup, GraceDbApiTestBase):
response = self.request_as_user(url, "GET")
self.assertEqual(response.status_code, 200)
# Check data on page
response_keys = response.json().keys()
response_keys = list(response.json())
response_links = response.json()['links']
self.assertNotIn('preferred_event', response_keys)
self.assertNotIn('gw_events', response_keys)
self.assertNotIn('em_events', response_keys)
self.assertNotIn('events', response_links.keys())
self.assertNotIn('events', response_links)
......@@ -60,12 +60,12 @@ def construct_url_templates(request=None):
# keys are like '{view_name}-template'
# values are URLs with placeholder parameters
templates = {view_name + '-template': sr(view_name, args=args)
for view_name, args in views.iteritems()}
for view_name, args in views.items()}
# Replace URL placeholder parameters with string formatting placeholders
# Ex: replace 'G1234' with '{graceid}'
for k,v in templates.iteritems():
for pattern,placeholder in PH.iteritems():
for k,v in templates.items():
for pattern,placeholder in PH.items():
if placeholder in v:
v = v.replace(placeholder, "{{{0}}}".format(pattern))
templates[k] = v
......
......@@ -15,7 +15,7 @@ from rest_framework.views import APIView
from core.file_utils import get_file_list
from core.http import check_and_serve_file
from core.vfile import VersionedFile, FileVersionError, FileVersionNameError
from core.vfile import FileVersionError, FileVersionNameError
from events.models import Event, Label
from events.view_utils import reverse as gracedb_reverse
from ligoauth.utils import is_internal
......
try:
from unittest import mock
except ImportError: # python < 3
import mock
import pytest
from django.conf import settings
......@@ -5,6 +9,9 @@ from django.contrib.auth.models import (
Group, Permission, AnonymousUser,
)
from django_mock_queries.query import MockSet
# Groups ----------------------------------------------------------------------
@pytest.mark.django_db
@pytest.fixture
......@@ -89,6 +96,7 @@ def em_advocate_user(django_user_model, internal_group, em_advocates_group):
return user
# User lists ------------------------------------------------------------------
@pytest.fixture(params=['internal_user', 'public_user'])
def standard_user(request):
......@@ -97,3 +105,43 @@ def standard_user(request):
internal user, public user (LV-EM user to come?)
"""
return request.getfixturevalue(request.param)
# Attempts at mocking things away
## Mock groups ------------------------
@pytest.fixture
def mock_internal_group():
group = mock.MagicMock(spec=Group)
group.permissions = MockSet()
group.name = settings.LVC_GROUP
# Create mock permissions
perm_data = [
{'content_type.app.label': 'superevents', 'codename': 'add_labelling'},
{'content_type.app.label': 'superevents', 'codename': 'delete_labelling'},
{'content_type.app.label': 'superevents', 'codename': 'tag_log'},
{'content_type.app.label': 'superevents', 'codename': 'untag_log'},
{'content_type.app.label': 'superevents', 'codename': 'view_log'},
{'content_type.app.label': 'superevents', 'codename': 'add_test_superevent'},
{'content_type.app.label': 'superevents', 'codename': 'change_test_superevent'},
{'content_type.app.label': 'superevents', 'codename': 'confirm_gw_test_superevent'},
{'content_type.app.label': 'superevents', 'codename': 'annotate_superevent'},
{'content_type.app.label': 'superevents', 'codename': 'view_superevent'},
{'content_type.app.label': 'superevents', 'codename': 'add_voevent'},
{'content_type.app.label': 'superevents', 'codename': 'view_supereventgroupobjectpermission'},
{'content_type.app.label': 'superevents', 'codename': 'view_signoff'},
]
for perm in perm_data:
p = mock.MagicMock(spec=Permission, **perm)
group.permissions.add(p)
return group
## Mock user objects ------------------
@pytest.fixture
def mock_internal_user(django_user_model, mock_internal_group):
user = mock.MagicMock(spec=django_user_model)
user.groups = MockSet()
user.groups.add(mock_internal_group)
return user
from functools import wraps
def ignore_maintenance_mode(view):
@wraps(view)
def inner(request, *args, **kwargs):
return view(request, *args, **kwargs)
inner.__dict__['ignore_maintenance_mode'] = True
return inner
......@@ -37,8 +37,8 @@ class ModelFormUpdateMixin(forms.ModelForm):
# Insert instance data for missing fields only
instance_data = self.get_instance_data()
for key in self.fields.keys():
if not self.data.has_key(key) and instance_data[key]:
for key in self.fields:
if not key in self.data and instance_data[key]:
self.data[key] = instance_data[key]
......
from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render
from django.urls import resolve
import logging
# Set up logger
logger = logging.getLogger(__name__)
class MaintenanceModeMiddleware(object):
accept_header_name = 'HTTP_ACCEPT'
default_message = 'The site is temporarily down for maintenance.'
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Process request -----------------------------------------------------
if settings.MAINTENANCE_MODE is True:
# Check if the view specifies to ignore maintenance mode
ignore_maintenance = \
self.check_for_ignore_maintenance_mode(request)
if not ignore_maintenance:
# Get message to display
maintenance_message = self.get_message()
accept_header = request.META.get(self.accept_header_name, None)
if accept_header and 'text/html' in accept_header:
# Attempt to handle browsers
context = {'message': maintenance_message}
return render(request, 'maintenance.html', context=context,
status=503)
else:
# Anything else (likely client API requests)
return HttpResponse(maintenance_message, status=503)
# Otherwise, get response and return with no further processing -------
response = self.get_response(request)
return response
@staticmethod
def check_for_ignore_maintenance_mode(request):
resolver_match = resolve(request.path)
view_func = resolver_match.func
return view_func.__dict__.get('ignore_maintenance_mode', False)
def get_message(self):
message = settings.MAINTENANCE_MODE_MESSAGE
if message is None:
message = self.default_message
return message
......@@ -5,9 +5,14 @@
import sys
import os
import re
import hotshot, hotshot.stats
import tempfile
import StringIO
try:
from StringIO import StringIO
except ImportError: # python >= 3
from io import StringIO
import hotshot, hotshot.stats
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
......@@ -91,7 +96,7 @@ class ProfileMiddleware(MiddlewareMixin):
if (settings.DEBUG or request.user.is_superuser) and 'prof' in request.GET:
self.prof.close()
out = StringIO.StringIO()
out = StringIO()
old_stdout = sys.stdout
sys.stdout = out
......
......@@ -11,7 +11,7 @@ class XForwardedForMiddleware(object):
def __call__(self, request):
# Process request -----------------------------------------------------
if request.META.has_key('HTTP_X_FORWARDED_FOR'):
if 'HTTP_X_FORWARDED_FOR' in request.META:
request.META['REMOTE_ADDR'] = \
request.META['HTTP_X_FORWARDED_FOR'].split(",")[0].strip()
......
......@@ -115,7 +115,7 @@ class AutoIncrementModel(models.Model):
qn = compiler.quote_name_unless_alias
# Compile multiple constraints with AND
constraint_fields = map(meta.get_field, self.AUTO_CONSTRAINTS)
constraint_fields = list(map(meta.get_field, self.AUTO_CONSTRAINTS))
constraint_list = ["{0}=%s".format(qn(f.column))
for f in constraint_fields]
constraint_values = [f.get_db_prep_value(getattr(self, f.column),
......
......@@ -14,7 +14,7 @@ import calendar
gpsEpoch = calendar.timegm((1980, 1, 6, 0, 0, 0, 0, 0, 0))
leapSeconds = map(calendar.timegm, [
leapSeconds = list(map(calendar.timegm, [
(1981, 7, 0, 0, 0, 0, 0, 0, 0),
(1982, 7, 0, 0, 0, 0, 0, 0, 0),
(1983, 7, 0, 0, 0, 0, 0, 0, 0),
......@@ -33,7 +33,7 @@ leapSeconds = map(calendar.timegm, [
(2012, 7, 0, 0, 0, 0, 0, 0, 0),
(2015, 7, 0, 0, 0, 0, 0, 0, 0),
(2017, 1, 0, 0, 0, 0, 0, 0, 0),
])
]))
def gpsToPosixTime(gpsTime):
if gpsTime is None:
......
......@@ -19,7 +19,7 @@ def int_to_letters(num, positive_only=True):
"""
# Argument checking
if not isinstance(num, (int, long)):
if not isinstance(num, int):
# Coerce to int
logger.warning('Coercing argument of type {0} to int'.format(
type(num)))
......
......@@ -24,7 +24,7 @@ class FileVersionNameError(Exception):
pass
class VersionedFile(file):
class VersionedFile(object):
"""
Open a versioned file.
......@@ -74,7 +74,7 @@ class VersionedFile(file):
# one scoped inside of this __init__). But I'm reluctant to mess with
# Brian's code too much.
self.version = version
file.__init__(self, actual_name, *args, **kwargs)
self.file = open(actual_name, *args, **kwargs)
# Otherwise...
......@@ -122,13 +122,13 @@ class VersionedFile(file):
# os.O_EXCL causes the open to fail if the file already exists.
fd = os.open(actual_name,
os.O_WRONLY | os.O_CREAT | os.O_EXCL,
0644)
0o644)
# re-open
file.__init__(self, actual_name, *args, **kwargs)
self.file = open(actual_name, *args, **kwargs)
# lose fd we used to ensure file creation.
os.close(fd)
break
except OSError, e:
except OSError as e:
if e.errno != errno.EEXIST:
raise
version += 1
......@@ -183,7 +183,7 @@ class VersionedFile(file):
try:
# XXX Another race condition. File will not exist for a very brief time.
os.unlink(self.fullname)
except OSError, e:
except OSError as e:
# Do not care if file does not exist, otherwise raise exception.
if e.errno != errno.ENOENT:
raise
......@@ -208,6 +208,13 @@ class VersionedFile(file):
return [int(f.split(',')[1])
for f in os.listdir(d) if f.startswith(name + ',')]
def write(self, s):
self.file.write(s)
@property
def closed(self):
return self.file.closed
def close(self):
if self.writing:
# no need to update symlink if we were only reading.
...