summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2010-10-28 13:52:48 -0400
committerChris McDonough <chrism@plope.com>2010-10-28 13:52:48 -0400
commita62cc2264c2dda6a0588fddbc5712afea9d89837 (patch)
tree82fbc7798b3fd7ec56023eb7d25a65ca3e99bd9c
parentecbf957db6c1efbc4c90852d3a62ca4df669f5f4 (diff)
downloadpyramid-a62cc2264c2dda6a0588fddbc5712afea9d89837.tar.gz
pyramid-a62cc2264c2dda6a0588fddbc5712afea9d89837.tar.bz2
pyramid-a62cc2264c2dda6a0588fddbc5712afea9d89837.zip
merge static view bugfixes/features from bfg trunk
-rw-r--r--CHANGES.txt2
-rw-r--r--docs/api/configuration.rst2
-rw-r--r--docs/glossary.rst9
-rw-r--r--docs/zcml/static.rst14
-rw-r--r--pyramid/configuration.py15
-rw-r--r--pyramid/static.py2
-rw-r--r--pyramid/tests/test_static.py13
-rw-r--r--pyramid/tests/test_zcml.py41
-rw-r--r--pyramid/zcml.py14
9 files changed, 106 insertions, 6 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 663db55fc..905bd27f1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,7 +6,7 @@ Features (delta from BFG 1.3.X)
- Mako templating renderer supports resource specification format for
template lookups and within Mako templates. Absolute filenames must
- be used in Pyramid to avoid this lookup process..
+ be used in Pyramid to avoid this lookup process.
- Add ``pyramid.httpexceptions`` module, which is a facade for the
``webob.exc`` module.
diff --git a/docs/api/configuration.rst b/docs/api/configuration.rst
index f13fc48e5..5215bfb3c 100644
--- a/docs/api/configuration.rst
+++ b/docs/api/configuration.rst
@@ -34,7 +34,7 @@
.. automethod:: add_route
- .. automethod:: add_static_view(name, path, cache_max_age=3600)
+ .. automethod:: add_static_view(name, path, cache_max_age=3600, permission='__no_permission_required__')
.. automethod:: add_settings
diff --git a/docs/glossary.rst b/docs/glossary.rst
index dfc40c407..2e2b11aaa 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -204,6 +204,15 @@ Glossary
to do this". Examples of permissions: ``read``, or
``view_blog_entries``.
+ default permission
+ A :term:`permission` which is registered as the default for an
+ entire application. When a default permission is in effect,
+ every :term:`view configuration` registered with the system will
+ be effectively amended with a ``permission`` argument that will
+ require that the executing user possess the default permission in
+ order to successfully execute the associated :term:`view
+ callable` See also :ref:`setting_a_default_permission`.
+
ACE
An *access control entry*. An access control entry is one element
in an :term:`ACL`. An access control entry is a three-tuple that
diff --git a/docs/zcml/static.rst b/docs/zcml/static.rst
index 6c1c5b6d3..815e332c8 100644
--- a/docs/zcml/static.rst
+++ b/docs/zcml/static.rst
@@ -29,6 +29,20 @@ Attributes
``Cache-Control`` headers, when any static file is served from this
directive. This defaults to 3600 (5 minutes). Optional.
+``permission``
+ Used to specify the :term:`permission` required by a user to execute
+ this static view. This value defaults to the string
+ ``__no_permission_required__``. The ``__no_permission_required__``
+ string is a special sentinel which indicates that, even if a
+ :term:`default permission` exists for the current application, the
+ static view should be renderered to completely anonymous users.
+ This default value is permissive because, in most web apps, static
+ resources seldom need protection from viewing. You should use this
+ option only if you register a static view which points at a
+ directory that contains resources which should be shown only if the
+ calling user has (according to the :term:`authorization policy`) a
+ particular permission.
+
Examples
~~~~~~~~
diff --git a/pyramid/configuration.py b/pyramid/configuration.py
index 12c0386cb..b3ba7a20b 100644
--- a/pyramid/configuration.py
+++ b/pyramid/configuration.py
@@ -1865,7 +1865,20 @@ class Configurator(object):
The ``cache_max_age`` keyword argument is input to set the
``Expires`` and ``Cache-Control`` headers for static resources
served. Note that this argument has no effect when the
- ``name`` is a *url prefix*.
+ ``name`` is a *url prefix*. By default, this argument is
+ ``None``, meaning that no particular Expires or Cache-Control
+ headers are set in the response.
+
+ The ``permission`` keyword argument is used to specify the
+ :term:`permission` required by a user to execute the static
+ view. By default, it is the string
+ ``__no_permission_required__``. The
+ ``__no_permission_required__`` string is a special sentinel
+ which indicates that, even if a :term:`default permission`
+ exists for the current application, the static view should be
+ renderered to completely anonymous users. This default value
+ is permissive because, in most web apps, static resources
+ seldom need protection from viewing.
*Usage*
diff --git a/pyramid/static.py b/pyramid/static.py
index 00102561d..179729732 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -140,11 +140,13 @@ class StaticURLInfo(object):
cache_max_age = extra.pop('cache_max_age', None)
view = static_view(spec, cache_max_age=cache_max_age)
# register a route using this view
+ permission = extra.pop('permission', '__no_permission_required__')
self.config.add_route(
name,
"%s*subpath" % name, # name already ends with slash
view=view,
view_for=self.__class__,
+ view_permission=permission,
factory=lambda *x: self,
_info=_info
)
diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py
index bd910cb66..81350504c 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -315,10 +315,23 @@ class TestStaticURLInfo(unittest.TestCase):
self.assertEqual(config.kw['_info'], None)
self.assertEqual(config.kw['view_for'], self._getTargetClass())
self.assertEqual(config.kw['factory'](), inst)
+ self.assertEqual(config.kw['view_permission'],
+ '__no_permission_required__')
self.assertEqual(config.kw['view'].__class__, static_view)
self.assertEqual(config.kw['view'].app.cache_max_age, 1)
self.assertEqual(inst.registrations, expected)
+ def test_add_viewname_with_permission(self):
+ class Config:
+ def add_route(self, *arg, **kw):
+ self.arg = arg
+ self.kw = kw
+ config = Config()
+ inst = self._makeOne(config)
+ inst.add('view', 'anotherpackage:path', cache_max_age=1,
+ permission='abc')
+ self.assertEqual(config.kw['view_permission'], 'abc')
+
class DummyStartResponse:
def __call__(self, status, headerlist, exc_info=None):
self.status = status
diff --git a/pyramid/tests/test_zcml.py b/pyramid/tests/test_zcml.py
index 9ceec9953..789188666 100644
--- a/pyramid/tests/test_zcml.py
+++ b/pyramid/tests/test_zcml.py
@@ -718,6 +718,8 @@ class TestStaticDirective(unittest.TestCase):
return static(*arg, **kw)
def test_it_with_slash(self):
+ from pyramid import testing
+ testing.registerDummySecurityPolicy(permissive=False)
from pyramid.static import PackageURLParser
from pyramid.threadlocal import get_current_registry
from zope.interface import implementedBy
@@ -754,6 +756,45 @@ class TestStaticDirective(unittest.TestCase):
request = DummyRequest()
self.assertEqual(view(None, request).__class__, PackageURLParser)
+ def test_it_with_nondefault_permission(self):
+ from pyramid import testing
+ from pyramid.exceptions import Forbidden
+ testing.registerDummySecurityPolicy(permissive=False)
+ from pyramid.threadlocal import get_current_registry
+ from zope.interface import implementedBy
+ from pyramid.static import StaticURLInfo
+ from pyramid.interfaces import IView
+ from pyramid.interfaces import IViewClassifier
+ from pyramid.interfaces import IRouteRequest
+ from pyramid.interfaces import IRoutesMapper
+ context = DummyContext()
+ self._callFUT(context, 'name', 'fixtures/static', permission='aperm')
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ reg = get_current_registry()
+
+ route_action = actions[0]
+ discriminator = route_action['discriminator']
+ self.assertEqual(discriminator, ('static', 'name'))
+ route_action['callable'](*route_action['args'], **route_action['kw'])
+ mapper = reg.getUtility(IRoutesMapper)
+ routes = mapper.get_routes()
+ self.assertEqual(len(routes), 1)
+ self.assertEqual(routes[0].pattern, 'name/*subpath')
+ self.assertEqual(routes[0].name, 'name/')
+
+ view_action = actions[1]
+ discriminator = view_action['discriminator']
+ self.assertEqual(discriminator[:3], ('view', StaticURLInfo, ''))
+ self.assertEqual(discriminator[4], IView)
+ iface = implementedBy(StaticURLInfo)
+ request_type = reg.getUtility(IRouteRequest, 'name/')
+ view = reg.adapters.lookup(
+ (IViewClassifier, request_type, iface), IView, name='')
+ request = DummyRequest()
+ self.assertRaises(Forbidden, view, None, request)
+
class TestResourceDirective(unittest.TestCase):
def setUp(self):
testing.setUp()
diff --git a/pyramid/zcml.py b/pyramid/zcml.py
index 6d481bd8c..80f73a3a2 100644
--- a/pyramid/zcml.py
+++ b/pyramid/zcml.py
@@ -561,7 +561,7 @@ class IStaticDirective(Interface):
path = TextLine(
title=u'Path to the directory which contains resources',
description=u'May be package-relative by using a colon to '
- 'seperate package name and path relative to the package directory.',
+ 'separate package name and path relative to the package directory.',
required=True)
cache_max_age = Int(
@@ -569,7 +569,13 @@ class IStaticDirective(Interface):
required=False,
default=None)
-def static(_context, name, path, cache_max_age=3600):
+ permission = TextLine(
+ title=u'Permission string',
+ description = u'The permission string',
+ required = False)
+
+def static(_context, name, path, cache_max_age=3600,
+ permission='__no_permission_required__'):
""" Handle ``static`` ZCML directives
"""
path = path_spec(_context, path)
@@ -580,7 +586,9 @@ def static(_context, name, path, cache_max_age=3600):
discriminator=('static', name),
callable=config.add_static_view,
args = (name, path),
- kw = {'cache_max_age':cache_max_age, '_info':_context.info},
+ kw = {'cache_max_age':cache_max_age,
+ 'permission':permission,
+ '_info':_context.info},
)
if not '/' in name: