Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Security policy implementation #3465

Merged
merged 46 commits into from
Oct 1, 2019
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
753d596
Add ISecurityPolicy interface.
luhn Feb 17, 2019
e47e7f4
Add `security policy` and `identity` to glossary.
luhn Feb 17, 2019
a6234e4
Implement setting ISecurityPolicy in the configurator.
luhn Feb 17, 2019
4c3c826
Implement legacy security policy.
luhn Feb 18, 2019
839bfb4
Set legacy policy when using authn/authz policies.
luhn Feb 18, 2019
f1709eb
Sign CONTRIBUTORS.txt.
luhn Feb 18, 2019
aae1c51
Add `request.identity`.
luhn Feb 27, 2019
140fdbb
Implement bw-compat authenticated_userid and unauthenticated_userid
luhn Mar 2, 2019
5abdd1d
Implement new request.has_permission.
luhn Mar 3, 2019
edf7ef0
Implement secured view deriver.
luhn Mar 9, 2019
027d3cb
Revamp tests for EffectivePrincipalsPredicate.
luhn Mar 9, 2019
7cdaabd
Reimplement remember and forget.
luhn Mar 3, 2019
94a16a7
Implement new dummy security policy.
luhn Mar 9, 2019
8a1f8d4
Get integration tests working again.
luhn Mar 9, 2019
282a59b
Clean up configurator methods.
luhn Mar 17, 2019
31998bc
Implement pyramid.security.ACLHelper
luhn Mar 30, 2019
6aba89d
Add SessionAuthenticationHelper.
luhn Mar 30, 2019
05b24d4
Merge branch 'master' into security-policy
luhn Mar 30, 2019
3d9c5c5
Fix formatting.
luhn Mar 30, 2019
9f267dd
Migrate AuthTktCookieHelper to pyramid.security.
luhn Mar 30, 2019
b05b66e
Merge branch 'master' into security-policy
luhn Mar 30, 2019
8536f3a
Register security policy in phase 2.
luhn Apr 16, 2019
47f8935
Stringify identity in legacy authenticated_userid.
luhn Apr 16, 2019
f4c6c99
Revert "Migrate AuthTktCookieHelper to pyramid.security."
luhn Apr 16, 2019
d6e543b
Move SessionAuthenticationHelper to pyramid.authentication.
luhn Apr 16, 2019
600ffe2
Use SessionAuthenticationHelper in SessionAuthenticationPolicy.
luhn Apr 16, 2019
5497c0f
Move ACLHelper to pyramid.authorizations.
luhn Apr 16, 2019
ad611d2
Add simple integration tests for security.
luhn Apr 27, 2019
08d5edd
Rename request.identity to request.authenticated_identity.
luhn May 12, 2019
4c95ccd
Narrative docs WIP
luhn May 12, 2019
c544a22
First draft of narrative docs.
luhn May 26, 2019
f9fba4f
API docs.
luhn May 26, 2019
ecfd8b7
Beginnings of upgrade docs.
luhn Jun 8, 2019
3d34d65
Fix bw-compat for `unauthenticated_userid`.
luhn Jun 8, 2019
b111ba7
First draft of upgrade docs.
luhn Jun 8, 2019
f54cae0
Add a whatsnew-2.0 doc.
luhn Jun 23, 2019
f2315b9
Use a table for policy => helper list.
luhn Jun 23, 2019
5384cc6
Act on @stevepiercy's suggestions
luhn Jun 23, 2019
5d79e3f
Make sure Configator.set_security_policy is in docs.
luhn Jun 23, 2019
14d569e
Deprecation notices.
luhn Jun 23, 2019
514f75f
Add deprecation warnings.
luhn Jun 23, 2019
c699947
Improve authn/authz API docs.
luhn Jul 13, 2019
0996092
Improve security docs.
luhn Jul 13, 2019
d2d20b9
Un-deprecate authenticated_userid.
luhn Jul 21, 2019
1cfd72e
Doc fix via @mmerickel
luhn Aug 26, 2019
cdb2661
Doc fix via @mmerickel
luhn Aug 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,5 @@ Contributors
- Colin Dunklau, 2018/09/19

- Alexandre Yukio Harano, 2018/10/05

- Theron Luhn, 2019/02/17
18 changes: 18 additions & 0 deletions docs/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -306,18 +306,36 @@ Glossary
a principal, but this is not strictly necessary in custom policies that
define their principals differently.

identity
An identity is an opaque identifier of the user associated with the
current request.

security policy
A security policy in :app:`Pyramid` terms is a bit of code which has an
API which identifies the user associated with the current request (perhaps
via a cookie or ``Authorization`` header) and determines whether or not
that user is permitted to access the requested resource.

authorization policy
An authorization policy in :app:`Pyramid` terms is a bit of
code which has an API which determines whether or not the
principals associated with the request can perform an action
associated with a permission, based on the information found on the
:term:`context` resource.

.. deprecated:: 2.0
Authorization policies have been deprecated in favor of a
:term:`security policy`.

authentication policy
An authentication policy in :app:`Pyramid` terms is a bit of
code which has an API which determines the current
:term:`principal` (or principals) associated with a request.

.. deprecated:: 2.0
Authentication policies have been deprecated in favor of a
:term:`security policy`.

mmerickel marked this conversation as resolved.
Show resolved Hide resolved
WSGI
`Web Server Gateway Interface <https://wsgi.readthedocs.io/en/latest/>`_.
This is a Python standard for connecting web applications to web servers,
Expand Down
14 changes: 12 additions & 2 deletions src/pyramid/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,17 @@ class Configurator(
:term:`dotted Python name` to the same. If it is ``None``, a default
root factory will be used.

If ``security_policy`` is passed, it should be an instance of a
:term:`security policy` or a :term:`dotted Python name` to the same.

If ``authentication_policy`` is passed, it should be an instance
of an :term:`authentication policy` or a :term:`dotted Python
name` to the same.
name` to the same. (Deprecated as of Pyramid 2.0 in favor of
``security_policy``.)

If ``authorization_policy`` is passed, it should be an instance of
an :term:`authorization policy` or a :term:`dotted Python name` to
the same.
the same. (Deprecated as of Pyramid 2.0 in favor of ``security_policy``.)
luhn marked this conversation as resolved.
Show resolved Hide resolved

.. note:: A ``ConfigurationError`` will be raised when an
authorization policy is supplied without also supplying an
Expand Down Expand Up @@ -278,6 +282,7 @@ def __init__(
package=None,
settings=None,
root_factory=None,
security_policy=None,
authentication_policy=None,
authorization_policy=None,
renderers=None,
Expand Down Expand Up @@ -315,6 +320,7 @@ def __init__(
root_factory=root_factory,
authentication_policy=authentication_policy,
authorization_policy=authorization_policy,
security_policy=security_policy,
renderers=renderers,
debug_logger=debug_logger,
locale_negotiator=locale_negotiator,
Expand All @@ -330,6 +336,7 @@ def setup_registry(
self,
settings=None,
root_factory=None,
security_policy=None,
authentication_policy=None,
authorization_policy=None,
renderers=None,
Expand Down Expand Up @@ -415,6 +422,9 @@ def setup_registry(
if authentication_policy:
self.set_authentication_policy(authentication_policy)

if security_policy:
self.set_security_policy(security_policy)

if default_view_mapper is not None:
self.set_view_mapper(default_view_mapper)

Expand Down
44 changes: 44 additions & 0 deletions src/pyramid/config/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
ICSRFStoragePolicy,
IDefaultCSRFOptions,
IDefaultPermission,
ISecurityPolicy,
PHASE1_CONFIG,
PHASE2_CONFIG,
)

from pyramid.csrf import LegacySessionCSRFStoragePolicy
from pyramid.exceptions import ConfigurationError
from pyramid.util import as_sorted_tuple
from pyramid.security import LegacySecurityPolicy

from pyramid.config.actions import action_method

Expand All @@ -21,6 +23,38 @@ class SecurityConfiguratorMixin(object):
def add_default_security(self):
self.set_csrf_storage_policy(LegacySessionCSRFStoragePolicy())

@action_method
def set_security_policy(self, policy):
""" Override the :app:`Pyramid` :term:`security policy` in the current
configuration. The ``policy`` argument must be an instance
of a security policy or a :term:`dotted Python name`
that points at an instance of a security policy.

.. note::

Using the ``security_policy`` argument to the
:class:`pyramid.config.Configurator` constructor can be used to
achieve the same purpose.

"""

def register():
self._set_security_policy(policy)

intr = self.introspectable(
'security policy',
None,
self.object_description(policy),
'security policy',
)
intr['policy'] = policy
# authentication policy used by view config (phase 3)
self.action(ISecurityPolicy, register, introspectables=(intr,))
luhn marked this conversation as resolved.
Show resolved Hide resolved

def _set_security_policy(self, policy):
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, ISecurityPolicy)
luhn marked this conversation as resolved.
Show resolved Hide resolved

@action_method
def set_authentication_policy(self, policy):
""" Override the :app:`Pyramid` :term:`authentication policy` in the
Expand All @@ -44,6 +78,7 @@ def register():
'also configuring an authorization policy '
'(use the set_authorization_policy method)'
)
self._set_legacy_policy()

intr = self.introspectable(
'authentication policy',
Expand All @@ -64,6 +99,15 @@ def _set_authentication_policy(self, policy):
policy = self.maybe_dotted(policy)
self.registry.registerUtility(policy, IAuthenticationPolicy)

def _set_legacy_policy(self):
if self.registry.queryUtility(ISecurityPolicy) is not None:
raise ConfigurationError(
'Cannot configure an authentication and authorization policy '
'with a configured security policy.'
)
policy = LegacySecurityPolicy()
self.registry.registerUtility(policy, ISecurityPolicy)
luhn marked this conversation as resolved.
Show resolved Hide resolved

@action_method
def set_authorization_policy(self, policy):
""" Override the :app:`Pyramid` :term:`authorization policy` in the
Expand Down
15 changes: 4 additions & 11 deletions src/pyramid/config/testing.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
from zope.interface import Interface

from pyramid.interfaces import (
ITraverser,
IAuthorizationPolicy,
IAuthenticationPolicy,
IRendererFactory,
)
from pyramid.interfaces import ITraverser, ISecurityPolicy, IRendererFactory

from pyramid.renderers import RendererHelper

Expand All @@ -18,8 +13,7 @@ class TestingConfiguratorMixin(object):
# testing API
def testing_securitypolicy(
self,
userid=None,
groupids=(),
identity=None,
permissive=True,
remember_result=None,
forget_result=None,
Expand Down Expand Up @@ -69,10 +63,9 @@ def testing_securitypolicy(
from pyramid.testing import DummySecurityPolicy

policy = DummySecurityPolicy(
userid, groupids, permissive, remember_result, forget_result
identity, permissive, remember_result, forget_result
)
self.registry.registerUtility(policy, IAuthorizationPolicy)
self.registry.registerUtility(policy, IAuthenticationPolicy)
self.registry.registerUtility(policy, ISecurityPolicy)
return policy

def testing_resources(self, resources):
Expand Down
42 changes: 40 additions & 2 deletions src/pyramid/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,40 @@ def __call__(self, **kw):
"""


class ISecurityPolicy(Interface):
def identify(request):
""" Return an object identifying a trusted and verified user. """

def permits(request, context, identity, permission):
""" Return an instance of :class:`pyramid.security.Allowed` if a user
of the given identity is allowed the ``permission`` in the current
``context``, else return an instance of
:class:`pyramid.security.Denied`.
"""

def remember(request, userid, **kw):
""" Return a set of headers suitable for 'remembering' the
:term:`userid` named ``userid`` when set in a response. An
individual authentication policy and its consumers can
decide on the composition and meaning of ``**kw``.

"""

def forget(request):
""" Return a set of headers suitable for 'forgetting' the
current user on subsequent requests.

"""


class IAuthenticationPolicy(Interface):
""" An object representing a Pyramid authentication policy. """
""" An object representing a Pyramid authentication policy.

.. deprecated:: 2.0

Use :class:`ISecurityPolicy`.
luhn marked this conversation as resolved.
Show resolved Hide resolved
luhn marked this conversation as resolved.
Show resolved Hide resolved

"""

def authenticated_userid(request):
""" Return the authenticated :term:`userid` or ``None`` if
Expand Down Expand Up @@ -536,7 +568,13 @@ def forget(request):


class IAuthorizationPolicy(Interface):
""" An object representing a Pyramid authorization policy. """
""" An object representing a Pyramid authorization policy.

.. deprecated:: 2.0

Use :class:`ISecurityPolicy`.

"""

def permits(context, principals, permission):
""" Return an instance of :class:`pyramid.security.Allowed` if any
Expand Down
7 changes: 7 additions & 0 deletions src/pyramid/predicates.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,13 @@ def __call__(self, context, request):


class EffectivePrincipalsPredicate(object):
"""
.. deprecated:: 2.0

No longer applicable with the new :term:`security policy`.

"""

def __init__(self, val, config):
if is_nonstr_iter(val):
self.val = set(val)
Expand Down
4 changes: 2 additions & 2 deletions src/pyramid/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from pyramid.decorator import reify
from pyramid.i18n import LocalizerRequestMixin
from pyramid.response import Response, _get_response_factory
from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin
from pyramid.security import SecurityAPIMixin, AuthenticationAPIMixin
from pyramid.url import URLMethodsMixin
from pyramid.util import (
InstancePropertyHelper,
Expand Down Expand Up @@ -147,8 +147,8 @@ class Request(
CallbackMethodsMixin,
InstancePropertyMixin,
LocalizerRequestMixin,
SecurityAPIMixin,
AuthenticationAPIMixin,
AuthorizationAPIMixin,
ViewMethodsMixin,
):
"""
Expand Down