diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pyramid/config/actions.py | 2 | ||||
| -rw-r--r-- | src/pyramid/config/security.py | 25 | ||||
| -rw-r--r-- | src/pyramid/config/tweens.py | 2 | ||||
| -rw-r--r-- | src/pyramid/config/views.py | 4 | ||||
| -rw-r--r-- | src/pyramid/csrf.py | 18 | ||||
| -rw-r--r-- | src/pyramid/httpexceptions.py | 8 | ||||
| -rw-r--r-- | src/pyramid/interfaces.py | 10 | ||||
| -rw-r--r-- | src/pyramid/router.py | 5 | ||||
| -rw-r--r-- | src/pyramid/scripts/prequest.py | 2 | ||||
| -rw-r--r-- | src/pyramid/scripts/proutes.py | 10 | ||||
| -rw-r--r-- | src/pyramid/testing.py | 2 | ||||
| -rw-r--r-- | src/pyramid/view.py | 18 | ||||
| -rw-r--r-- | src/pyramid/viewderivers.py | 6 |
13 files changed, 77 insertions, 35 deletions
diff --git a/src/pyramid/config/actions.py b/src/pyramid/config/actions.py index 4a526e242..29d06d716 100644 --- a/src/pyramid/config/actions.py +++ b/src/pyramid/config/actions.py @@ -168,7 +168,7 @@ class ActionState(object): Return True if processing is needed and False otherwise. If the callable needs to be processed, it will be marked as - processed, assuming that the caller will procces the callable if + processed, assuming that the caller will process the callable if it needs to be processed. """ if spec in self._seen_files: diff --git a/src/pyramid/config/security.py b/src/pyramid/config/security.py index 8f0a108c5..32b4db03c 100644 --- a/src/pyramid/config/security.py +++ b/src/pyramid/config/security.py @@ -254,6 +254,7 @@ class SecurityConfiguratorMixin(object): token='csrf_token', header='X-CSRF-Token', safe_methods=('GET', 'HEAD', 'OPTIONS', 'TRACE'), + allow_no_origin=False, callback=None, ): """ @@ -278,6 +279,9 @@ class SecurityConfiguratorMixin(object): never be automatically checked for CSRF tokens. Default: ``('GET', 'HEAD', 'OPTIONS', TRACE')``. + ``allow_no_origin`` is a boolean. If false, a request lacking both an + ``Origin`` and ``Referer`` header will fail the CSRF check. + If ``callback`` is set, it must be a callable accepting ``(request)`` and returning ``True`` if the request should be checked for a valid CSRF token. This callback allows an application to support @@ -293,9 +297,17 @@ class SecurityConfiguratorMixin(object): .. versionchanged:: 1.8 Added the ``callback`` option. + .. versionchanged:: 2.0 + Added the ``allow_no_origin`` option. + """ options = DefaultCSRFOptions( - require_csrf, token, header, safe_methods, callback + require_csrf=require_csrf, + token=token, + header=header, + safe_methods=safe_methods, + allow_no_origin=allow_no_origin, + callback=callback, ) def register(): @@ -344,9 +356,18 @@ class SecurityConfiguratorMixin(object): @implementer(IDefaultCSRFOptions) class DefaultCSRFOptions(object): - def __init__(self, require_csrf, token, header, safe_methods, callback): + def __init__( + self, + require_csrf, + token, + header, + safe_methods, + allow_no_origin, + callback, + ): self.require_csrf = require_csrf self.token = token self.header = header self.safe_methods = frozenset(safe_methods) + self.allow_no_origin = allow_no_origin self.callback = callback diff --git a/src/pyramid/config/tweens.py b/src/pyramid/config/tweens.py index c85639d14..feb4a3230 100644 --- a/src/pyramid/config/tweens.py +++ b/src/pyramid/config/tweens.py @@ -73,7 +73,7 @@ class TweensConfiguratorMixin(object): If all options for ``under`` (or ``over``) cannot be found in the current configuration, it is an error. If some options are specified - purely for compatibilty with other tweens, just add a fallback of + purely for compatibility with other tweens, just add a fallback of MAIN or INGRESS. For example, ``under=('mypkg.someothertween', 'mypkg.someothertween2', INGRESS)``. This constraint will require the tween to be located under both the 'mypkg.someothertween' tween, diff --git a/src/pyramid/config/views.py b/src/pyramid/config/views.py index ac531ecb2..1abff0579 100644 --- a/src/pyramid/config/views.py +++ b/src/pyramid/config/views.py @@ -751,7 +751,7 @@ class ViewsConfiguratorMixin(object): It's useful when you want to always potentially show a view when some object is traversed to, but you can't be sure about what kind of object it will be, so you can't use the ``context`` predicate. The - individual path elements inbetween slash characters or in tuple + individual path elements in between slash characters or in tuple elements should be the Unicode representation of the name of the resource and should not be encoded in any way. @@ -859,7 +859,7 @@ class ViewsConfiguratorMixin(object): else: raise ConfigurationError( - '"view" was not specified and ' 'no "renderer" specified' + '"view" was not specified and no "renderer" specified' ) if request_type is not None: diff --git a/src/pyramid/csrf.py b/src/pyramid/csrf.py index 26c628acc..b352ada71 100644 --- a/src/pyramid/csrf.py +++ b/src/pyramid/csrf.py @@ -97,9 +97,9 @@ class SessionCSRFStoragePolicy(object): class CookieCSRFStoragePolicy(object): """ An alternative CSRF implementation that stores its information in unauthenticated cookies, known as the 'Double Submit Cookie' method in the - `OWASP CSRF guidelines <https://www.owasp.org/index.php/ - Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet# - Double_Submit_Cookie>`_. This gives some additional flexibility with + `OWASP CSRF guidelines + <https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie>`_. + This gives some additional flexibility with regards to scaling as the tokens can be generated and verified by a front-end server. @@ -247,7 +247,9 @@ def check_csrf_token( return True -def check_csrf_origin(request, trusted_origins=None, raises=True): +def check_csrf_origin( + request, trusted_origins=None, allow_no_origin=False, raises=True +): """ Check the ``Origin`` of the request to see if it is a cross site request or not. @@ -302,9 +304,13 @@ def check_csrf_origin(request, trusted_origins=None, raises=True): if origin is None: origin = request.referrer - # Fail if we were not able to locate an origin at all + # If we can't find an origin, fail or pass immediately depending on + # ``allow_no_origin`` if not origin: - return _fail("Origin checking failed - no Origin or Referer.") + if allow_no_origin: + return True + else: + return _fail("Origin checking failed - no Origin or Referer.") # Parse our origin so we we can extract the required information from # it. diff --git a/src/pyramid/httpexceptions.py b/src/pyramid/httpexceptions.py index 56797dc88..c9fdfe04b 100644 --- a/src/pyramid/httpexceptions.py +++ b/src/pyramid/httpexceptions.py @@ -367,7 +367,7 @@ class HTTPRedirection(HTTPException): This is an abstract base class for 3xx redirection. It indicates that further action needs to be taken by the user agent in order - to fulfill the request. It does not necessarly signal an error + to fulfill the request. It does not necessarily signal an error condition. """ @@ -914,9 +914,7 @@ class HTTPConflict(HTTPClientError): code = 409 title = 'Conflict' - explanation = ( - 'There was a conflict when trying to complete ' 'your request.' - ) + explanation = 'There was a conflict when trying to complete your request.' class HTTPGone(HTTPClientError): @@ -1040,7 +1038,7 @@ class HTTPExpectationFailed(HTTPClientError): """ subclass of :class:`~HTTPClientError` - This indidcates that the expectation given in an Expect + This indicates that the expectation given in an Expect request-header field could not be met by this server. code: 417, title: Expectation Failed diff --git a/src/pyramid/interfaces.py b/src/pyramid/interfaces.py index d97c3811b..2d8b1ac40 100644 --- a/src/pyramid/interfaces.py +++ b/src/pyramid/interfaces.py @@ -426,7 +426,7 @@ class IRendererInfo(Interface): ) type = Attribute('The renderer type name') registry = Attribute( - 'The "current" application registry when the ' 'renderer was created' + 'The "current" application registry when the renderer was created' ) settings = Attribute( 'The deployment settings dictionary related ' @@ -1100,6 +1100,10 @@ class IDefaultCSRFOptions(Interface): header = Attribute('The header to be matched with the CSRF token.') safe_methods = Attribute('A set of safe methods that skip CSRF checks.') callback = Attribute('A callback to disable CSRF checks per-request.') + allow_no_origin = Attribute( + 'Boolean. If false, a request lacking both an ``Origin`` and ' + '``Referer`` header will fail the CSRF check.' + ) class ISessionFactory(Interface): @@ -1319,7 +1323,7 @@ class IIntrospectable(Interface): ) category_name = Attribute('introspection category name') discriminator = Attribute( - 'introspectable discriminator (within category) ' '(must be hashable)' + 'introspectable discriminator (within category) (must be hashable)' ) discriminator_hash = Attribute('an integer hash of the discriminator') action_info = Attribute( @@ -1460,7 +1464,7 @@ class IViewDeriverInfo(Interface): :term:`view deriver` during configuration.""" registry = Attribute( - 'The "current" application registry where the ' 'view was created' + 'The "current" application registry where the view was created' ) package = Attribute( 'The "current package" where the view ' diff --git a/src/pyramid/router.py b/src/pyramid/router.py index 19641aecd..fa1a9ebf7 100644 --- a/src/pyramid/router.py +++ b/src/pyramid/router.py @@ -273,7 +273,4 @@ class Router(object): def default_execution_policy(environ, router): with router.request_context(environ) as request: - try: - return router.invoke_request(request) - except Exception: - return request.invoke_exception_view(reraise=True) + return router.invoke_request(request) diff --git a/src/pyramid/scripts/prequest.py b/src/pyramid/scripts/prequest.py index eb2032419..759978936 100644 --- a/src/pyramid/scripts/prequest.py +++ b/src/pyramid/scripts/prequest.py @@ -18,7 +18,7 @@ class PRequestCommand(object): description = """\ Submit a HTTP request to a web application. - This command makes an artifical request to a web application that uses a + This command makes an artificial request to a web application that uses a PasteDeploy (.ini) configuration file for the server and application. Use "prequest config.ini /path" to request "/path". diff --git a/src/pyramid/scripts/proutes.py b/src/pyramid/scripts/proutes.py index 78c2295d5..09b550cef 100644 --- a/src/pyramid/scripts/proutes.py +++ b/src/pyramid/scripts/proutes.py @@ -268,9 +268,11 @@ class PRoutesCommand(object): 'config_vars', nargs='*', default=(), - help="Variables required by the config file. For example, " - "`http_port=%%(http_port)s` would expect `http_port=8080` to be " - "passed here.", + help=( + "Variables required by the config file. For example, " + "`http_port=%%(http_port)s` would expect `http_port=8080` to be " + "passed here." + ), ) def __init__(self, argv, quiet=False): @@ -285,7 +287,7 @@ class PRoutesCommand(object): if fmt not in self.available_formats: invalid_formats.append(fmt) - msg = 'You provided invalid formats %s, ' 'Available formats are %s' + msg = 'You provided invalid formats %s. Available formats are %s' if invalid_formats: msg = msg % (invalid_formats, self.available_formats) diff --git a/src/pyramid/testing.py b/src/pyramid/testing.py index 4bf6d281f..3bf3f1898 100644 --- a/src/pyramid/testing.py +++ b/src/pyramid/testing.py @@ -154,7 +154,7 @@ class DummyResource: should be an interface object or tuple of interface objects that will be attached to the resulting resource via :func:`zope.interface.alsoProvides`. Any extra keywords passed - in the ``kw`` argumnent will be set as direct attributes of + in the ``kw`` argument will be set as direct attributes of the resource object. .. note:: For backwards compatibility purposes, this class can also diff --git a/src/pyramid/view.py b/src/pyramid/view.py index 944ad93ea..7e54a40f6 100644 --- a/src/pyramid/view.py +++ b/src/pyramid/view.py @@ -1,5 +1,6 @@ import itertools import sys +import inspect import venusian @@ -216,6 +217,14 @@ class view_config(object): if settings.get('context') is None: settings['context'] = settings['for_'] self.__dict__.update(settings) + self._get_info() + + def _get_info(self): + depth = self.__dict__.get('_depth', 0) + frame = sys._getframe(depth + 2) + frameinfo = inspect.getframeinfo(frame) + sourceline = frameinfo[3][0].strip() + self._info = frameinfo[0], frameinfo[1], frameinfo[2], sourceline def __call__(self, wrapped): settings = self.__dict__.copy() @@ -237,14 +246,13 @@ class view_config(object): if settings.get('attr') is None: settings['attr'] = wrapped.__name__ - settings['_info'] = info.codeinfo # fbo "action_method" return wrapped bfg_view = view_config # bw compat (forever) -class view_defaults(view_config): +def view_defaults(**settings): """ A class :term:`decorator` which, when applied to a class, will provide defaults for all view configurations that use the class. This decorator accepts all the arguments accepted by @@ -253,10 +261,12 @@ class view_defaults(view_config): See :ref:`view_defaults` for more information. """ - def __call__(self, wrapped): - wrapped.__view_defaults__ = self.__dict__.copy() + def wrap(wrapped): + wrapped.__view_defaults__ = settings return wrapped + return wrap + class AppendSlashNotFoundViewFactory(object): """ There can only be one :term:`Not Found view` in any diff --git a/src/pyramid/viewderivers.py b/src/pyramid/viewderivers.py index 22659d2a3..95c223e61 100644 --- a/src/pyramid/viewderivers.py +++ b/src/pyramid/viewderivers.py @@ -484,12 +484,14 @@ def csrf_view(view, info): token = 'csrf_token' header = 'X-CSRF-Token' safe_methods = frozenset(["GET", "HEAD", "OPTIONS", "TRACE"]) + allow_no_origin = False callback = None else: default_val = defaults.require_csrf token = defaults.token header = defaults.header safe_methods = defaults.safe_methods + allow_no_origin = defaults.allow_no_origin callback = defaults.callback enabled = ( @@ -508,7 +510,9 @@ def csrf_view(view, info): if request.method not in safe_methods and ( callback is None or callback(request) ): - check_csrf_origin(request, raises=True) + check_csrf_origin( + request, raises=True, allow_no_origin=allow_no_origin + ) check_csrf_token(request, token, header, raises=True) return view(context, request) |
