Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #7 from andymckay/778225

proxy shouldn't have db or cache access
  • Loading branch information...
commit fcfeb2bc2c43d8d16423c377443ae3c7bc58ccd7 2 parents 51b42d4 + 6365895
@andymckay andymckay authored
Showing with 92 additions and 25 deletions.
  1. +67 −18 lib/services/resources.py
  2. +25 −7 lib/services/tests.py
View
85 lib/services/resources.py
@@ -11,6 +11,10 @@
from tastypie import http
from tastypie.exceptions import ImmediateHttpResponse
+import logging
+
+log = logging.getLogger('s.services')
+
class TestError(Exception):
pass
@@ -71,14 +75,66 @@ class StatusObject(object):
pk = 'status'
cache = False
db = False
+ settings = True
def __repr__(self):
- return '<Status: database: %s, cache: %s>' % (self.db, self.cache)
+ values = ['%s: %s' % (k, v) for k, v in self.checks]
+ return '<Status: %s>' % ', '.join(values)
+
+ @property
+ def checks(self):
+ return [(k, getattr(self, k)) for k in ['cache', 'db', 'settings']]
+
+ @property
+ def is_proxy(self):
+ return getattr(settings, 'SOLITUDE_PROXY', {})
+
+ def test_cache(self):
+ # caching fails silently so we have to read from it after writing.
+ cache.set('status', 'works')
+
+ if cache.get('status') == 'works':
+ self.cache = True
+
+ def test_db(self):
+ try:
+ # exists is one of the fastest queries one can run.
+ Seller.objects.exists()
+ self.db = True
+ except DatabaseError:
+ pass
+
+ def test_settings(self):
+ # Warn if the settings are confused and the proxy settings are
+ # mixed with non-proxy settings. At this time we can't tell if you
+ # are running just the database server or solitude all in one.
+ caches = getattr(settings, 'CACHES', {})
+ dbs = getattr(settings, 'DATABASES', {})
+
+ if self.is_proxy:
+ # As a proxy, we should not have database access.
+ for db in dbs.values():
+ engine = db.get('ENGINE', '')
+ if (db.get('ENGINE', '') not in ['',
+ 'django.db.backends.dummy']):
+ log.error('Proxy db set to: %s' % engine)
+ self.settings = False
+
+ # There could be an issue if you share a proxy with the database
+ # server, a local cache should be fine.
+ for cache in caches.values():
+ backend = cache.get('BACKEND', '')
+ if (backend not in ['',
+ 'django.core.cache.backends.dummy.DummyCache',
+ 'django.core.cache.backends.locmem.LocMemCache']):
+ log.error('Proxy cache set to: %s' % backend)
+ self.settings = False
class StatusResource(ServiceResource):
cache = fields.BooleanField(readonly=True, attribute='cache')
db = fields.BooleanField(readonly=True, attribute='db')
+ settings = fields.BooleanField(readonly=True, attribute='settings')
class Meta(ServiceResource.Meta):
list_allowed_methods = ['get']
@@ -87,26 +143,19 @@ class Meta(ServiceResource.Meta):
def obj_get(self, request, **kwargs):
obj = StatusObject()
- if getattr(settings, 'SOLITUDE_PROXY', False):
- return obj
-
- # caching fails silently so we have to read from it after writing.
- cache.set('status', 'works')
-
- if cache.get('status') == 'works':
- obj.cache = True
+ obj.test_cache()
+ obj.test_db()
+ obj.test_settings()
- try:
- # exists is one of the fastest queries one can run.
- Seller.objects.exists()
- obj.db = True
- except DatabaseError:
- pass
+ if obj.is_proxy:
+ if obj.settings and not (obj.db and obj.cache):
+ return obj
+ raise StatusError(str(obj))
- if all((obj.db, obj.cache)):
+ if obj.db and obj.cache:
return obj
- else:
- raise StatusError(str(obj))
+ raise StatusError(str(obj))
+
def obj_get_list(self, request=None, **kwargs):
return [self.obj_get(request, **kwargs)]
View
32 lib/services/tests.py
@@ -6,6 +6,7 @@
from nose.tools import eq_
from solitude.base import APITest
+from lib.services.resources import StatusObject
class TestStatus(APITest):
@@ -18,17 +19,34 @@ def test_working_status(self):
res = self.client.get(self.list_url)
eq_(res.status_code, 200)
- @patch.object(cache, 'get', lambda x: None)
- def test_failure_status(self):
- res = self.client.get(self.list_url)
+ def failed(self, res, on):
eq_(res.status_code, 500)
data = json.loads(res.content)
- eq_(data['error_message'], '<Status: database: True, cache: False>')
+ assert '%s: False' % on in data['error_message'], data
- def test_proxy(self):
- with self.settings(SOLITUDE_PROXY=True):
+ @patch.object(cache, 'get', lambda x: None)
+ def test_failure_status(self):
+ res = self.client.get(self.list_url)
+ self.failed(res, 'cache')
+
+ # Note that Django will use the values in the settings, altering
+ # CACHES right now will still work if your settings allow it. Urk.
+ @patch.object(StatusObject, 'test_cache')
+ @patch.object(StatusObject, 'test_db')
+ def test_proxy(self, test_db, test_cache):
+ with self.settings(SOLITUDE_PROXY=True,
+ DATABASES={'default': {'ENGINE': ''}},
+ CACHES={}):
res = self.client.get(self.list_url)
- eq_(res.status_code, 200)
+ eq_(res.status_code, 200, res.content)
+
+ @patch.object(StatusObject, 'test_cache')
+ @patch.object(StatusObject, 'test_db')
+ def test_proxy_db(self, test_db, test_cache):
+ with self.settings(SOLITUDE_PROXY=True,
+ DATABASES={'default': {'ENGINE': 'foo'}},
+ CACHES={}):
+ self.failed(self.client.get(self.list_url), 'settings')
class TestError(APITest):
Please sign in to comment.
Something went wrong with that request. Please try again.