From 1395359d653df5507146a44ccab6f0e2ab85ac65 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 12 Jan 2020 20:30:41 -0600 Subject: move acl helper apis from pyramid.security to pyramid.authorization --- src/pyramid/authorization.py | 42 +++++++++- src/pyramid/security.py | 181 ++++++++++++++++++++++++------------------- 2 files changed, 143 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/pyramid/authorization.py b/src/pyramid/authorization.py index b7c5834f9..4a040e9e4 100644 --- a/src/pyramid/authorization.py +++ b/src/pyramid/authorization.py @@ -1,10 +1,50 @@ +import warnings from zope.interface import implementer from pyramid.interfaces import IAuthorizationPolicy from pyramid.location import lineage -from pyramid.security import ACLAllowed, ACLDenied, Allow, Deny, Everyone from pyramid.util import is_nonstr_iter +# the simplest way to deprecate the attributes in security.py is to +# leave them defined there and then import/re-export them here because +# otherwise there is a difficult-to-resolve circular import between +# the two modules - in the future when we remove the deprecated code and +# move it to live here, we will be able to remove this +with warnings.catch_warnings(): + warnings.simplefilter('ignore') + from pyramid.security import ( + ACLAllowed as _ACLAllowed, + ACLDenied as _ACLDenied, + AllPermissionsList as _AllPermissionsList, + Allow, + Authenticated, + Deny, + Everyone, + ) + + +Everyone = Everyone # api +Authenticated = Authenticated # api +Allow = Allow # api +Deny = Deny # api + + +class AllPermissionsList(_AllPermissionsList): + pass + + +ALL_PERMISSIONS = AllPermissionsList() # api +DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) # api + +# subclass to fix __qualname__ +class ACLAllowed(_ACLAllowed): + pass + + +# subclass to fix __qualname__ +class ACLDenied(_ACLDenied): + pass + @implementer(IAuthorizationPolicy) class ACLAuthorizationPolicy(object): diff --git a/src/pyramid/security.py b/src/pyramid/security.py index 5e803aa0a..7b27c45f4 100644 --- a/src/pyramid/security.py +++ b/src/pyramid/security.py @@ -11,28 +11,6 @@ from pyramid.interfaces import ( ) from pyramid.threadlocal import get_current_registry -Everyone = 'system.Everyone' -Authenticated = 'system.Authenticated' -Allow = 'Allow' -Deny = 'Deny' - - -class AllPermissionsList(object): - """ Stand in 'permission list' to represent all permissions """ - - def __iter__(self): - return iter(()) - - def __contains__(self, other): - return True - - def __eq__(self, other): - return isinstance(other, self.__class__) - - -ALL_PERMISSIONS = AllPermissionsList() -DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) - NO_PERMISSION_REQUIRED = '__no_permission_required__' @@ -129,6 +107,8 @@ def principals_allowed_by_permission(context, permission): reg = get_current_registry() policy = reg.queryUtility(IAuthorizationPolicy) if policy is None: + from pyramid.authorization import Everyone # noqa: F811 + return [Everyone] return policy.principals_allowed_by_permission(context, permission) @@ -231,62 +211,6 @@ class Allowed(PermitsResult): boolval = 1 -class ACLPermitsResult(PermitsResult): - def __new__(cls, ace, acl, permission, principals, context): - """ - Create a new instance. - - :param ace: The :term:`ACE` that matched, triggering the result. - :param acl: The :term:`ACL` containing ``ace``. - :param permission: The required :term:`permission`. - :param principals: The list of :term:`principals ` provided. - :param context: The :term:`context` providing the :term:`lineage` - searched. - - """ - fmt = ( - '%s permission %r via ACE %r in ACL %r on context %r for ' - 'principals %r' - ) - inst = PermitsResult.__new__( - cls, fmt, cls.__name__, permission, ace, acl, context, principals - ) - inst.permission = permission - inst.ace = ace - inst.acl = acl - inst.principals = principals - inst.context = context - return inst - - -class ACLDenied(ACLPermitsResult, Denied): - """ - An instance of ``ACLDenied`` is a specialization of - :class:`pyramid.security.Denied` that represents that a security check - made explicitly against ACL was denied. It evaluates equal to all - boolean false types. It also has the following attributes: ``acl``, - ``ace``, ``permission``, ``principals``, and ``context``. These - attributes indicate the security values involved in the request. Its - ``__str__`` method prints a summary of these attributes for debugging - purposes. The same summary is available as the ``msg`` attribute. - - """ - - -class ACLAllowed(ACLPermitsResult, Allowed): - """ - An instance of ``ACLAllowed`` is a specialization of - :class:`pyramid.security.Allowed` that represents that a security check - made explicitly against ACL was allowed. It evaluates equal to all - boolean true types. It also has the following attributes: ``acl``, - ``ace``, ``permission``, ``principals``, and ``context``. These - attributes indicate the security values involved in the request. Its - ``__str__`` method prints a summary of these attributes for debugging - purposes. The same summary is available as the ``msg`` attribute. - - """ - - class SecurityAPIMixin: """ Mixin for Request class providing auth-related properties. """ @@ -398,9 +322,11 @@ class AuthenticationAPIMixin(object): Return the list of 'effective' :term:`principal` identifiers for the ``request``. If no :term:`authentication policy` is in effect, this will return a one-element list containing the - :data:`pyramid.security.Everyone` principal. + :data:`pyramid.authorization.Everyone` principal. """ + from pyramid.authorization import Everyone # noqa: F811 + security = _get_security_policy(self) if security is not None and isinstance(security, LegacySecurityPolicy): authn = security._get_authn_policy(self) @@ -456,3 +382,100 @@ class LegacySecurityPolicy: authz = self._get_authz_policy(request) principals = authn.effective_principals(request) return authz.permits(context, principals, permission) + + +Everyone = 'system.Everyone' +Authenticated = 'system.Authenticated' +Allow = 'Allow' +Deny = 'Deny' + + +class AllPermissionsList(object): + """ Stand in 'permission list' to represent all permissions """ + + def __iter__(self): + return iter(()) + + def __contains__(self, other): + return True + + def __eq__(self, other): + return isinstance(other, self.__class__) + + +ALL_PERMISSIONS = AllPermissionsList() +DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) + + +class ACLPermitsResult(PermitsResult): + def __new__(cls, ace, acl, permission, principals, context): + """ + Create a new instance. + + :param ace: The :term:`ACE` that matched, triggering the result. + :param acl: The :term:`ACL` containing ``ace``. + :param permission: The required :term:`permission`. + :param principals: The list of :term:`principals ` provided. + :param context: The :term:`context` providing the :term:`lineage` + searched. + + """ + fmt = ( + '%s permission %r via ACE %r in ACL %r on context %r for ' + 'principals %r' + ) + inst = PermitsResult.__new__( + cls, fmt, cls.__name__, permission, ace, acl, context, principals + ) + inst.permission = permission + inst.ace = ace + inst.acl = acl + inst.principals = principals + inst.context = context + return inst + + +class ACLDenied(ACLPermitsResult, Denied): + """ + An instance of ``ACLDenied`` is a specialization of + :class:`pyramid.security.Denied` that represents that a security check + made explicitly against ACL was denied. It evaluates equal to all + boolean false types. It also has the following attributes: ``acl``, + ``ace``, ``permission``, ``principals``, and ``context``. These + attributes indicate the security values involved in the request. Its + ``__str__`` method prints a summary of these attributes for debugging + purposes. The same summary is available as the ``msg`` attribute. + + """ + + +class ACLAllowed(ACLPermitsResult, Allowed): + """ + An instance of ``ACLAllowed`` is a specialization of + :class:`pyramid.security.Allowed` that represents that a security check + made explicitly against ACL was allowed. It evaluates equal to all + boolean true types. It also has the following attributes: ``acl``, + ``ace``, ``permission``, ``principals``, and ``context``. These + attributes indicate the security values involved in the request. Its + ``__str__`` method prints a summary of these attributes for debugging + purposes. The same summary is available as the ``msg`` attribute. + + """ + + +for attr in ( + 'ALL_PERMISSIONS', + 'DENY_ALL', + 'ACLAllowed', + 'ACLDenied', + 'AllPermissionsList', + 'Allow', + 'Authenticated', + 'Deny', + 'Everyone', +): + deprecated( + attr, + '"pyramid.security.{attr}" is deprecated in Pyramid 2.0. Adjust your ' + 'import to "pyramid.authorization.{attr}"'.format(attr=attr), + ) -- cgit v1.2.3 From 791730715832038c1666683e37fef8bb67830045 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 12 Jan 2020 20:49:35 -0600 Subject: move doc references from pyramid.security to pyramid.authorization --- src/pyramid/authentication.py | 16 +++++++++------- src/pyramid/authorization.py | 30 +++++++++++++++--------------- src/pyramid/config/routes.py | 2 +- src/pyramid/config/views.py | 2 +- src/pyramid/httpexceptions.py | 2 +- src/pyramid/interfaces.py | 4 ++-- src/pyramid/security.py | 2 +- 7 files changed, 30 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/pyramid/authentication.py b/src/pyramid/authentication.py index 0ccc646c3..8c6c0f981 100644 --- a/src/pyramid/authentication.py +++ b/src/pyramid/authentication.py @@ -11,7 +11,7 @@ from webob.cookies import CookieProfile from zope.interface import implementer from pyramid.interfaces import IAuthenticationPolicy, IDebugLogger -from pyramid.security import Authenticated, Everyone +from pyramid.authorization import Authenticated, Everyone from pyramid.util import ( SimpleSerializer, ascii_, @@ -98,7 +98,7 @@ class CallbackAuthenticationPolicy(object): """ A list of effective principals derived from request. This will return a list of principals including, at least, - :data:`pyramid.security.Everyone`. If there is no authenticated + :data:`pyramid.authorization.Everyone`. If there is no authenticated userid, or the ``callback`` returns ``None``, this will be the only principal: @@ -108,8 +108,9 @@ class CallbackAuthenticationPolicy(object): If the ``callback`` does not return ``None`` and an authenticated userid is found, then the principals will include - :data:`pyramid.security.Authenticated`, the ``authenticated_userid`` - and the list of principals returned by the ``callback``: + :data:`pyramid.authorization.Authenticated`, the + ``authenticated_userid`` and the list of principals returned by the + ``callback``: .. code-block:: python @@ -274,13 +275,14 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy): """ A list of effective principals derived from the identity. This will return a list of principals including, at least, - :data:`pyramid.security.Everyone`. If there is no identity, or + :data:`pyramid.authorization.Everyone`. If there is no identity, or the ``callback`` returns ``None``, this will be the only principal. If the ``callback`` does not return ``None`` and an identity is found, then the principals will include - :data:`pyramid.security.Authenticated`, the ``authenticated_userid`` - and the list of principals returned by the ``callback``. + :data:`pyramid.authorization.Authenticated`, the + ``authenticated_userid`` and the list of principals returned by the + ``callback``. """ effective_principals = [Everyone] diff --git a/src/pyramid/authorization.py b/src/pyramid/authorization.py index 4a040e9e4..87e6b8767 100644 --- a/src/pyramid/authorization.py +++ b/src/pyramid/authorization.py @@ -69,9 +69,9 @@ class ACLAuthorizationPolicy(object): def permits(self, context, principals, permission): """ Return an instance of - :class:`pyramid.security.ACLAllowed` instance if the policy + :class:`pyramid.authorization.ACLAllowed` instance if the policy permits access, return an instance of - :class:`pyramid.security.ACLDenied` if not.""" + :class:`pyramid.authorization.ACLDenied` if not.""" return self.helper.permits(context, principals, permission) def principals_allowed_by_permission(self, context, permission): @@ -94,9 +94,9 @@ class ACLHelper: """ def permits(self, context, principals, permission): - """ Return an instance of :class:`pyramid.security.ACLAllowed` if the - ACL allows access a user with the given principals, return an instance - of :class:`pyramid.security.ACLDenied` if not. + """ Return an instance of :class:`pyramid.authorization.ACLAllowed` if + the ACL allows access a user with the given principals, return an + instance of :class:`pyramid.authorization.ACLDenied` if not. When checking if principals are allowed, the security policy consults the ``context`` for an ACL first. If no ACL exists on the context, or @@ -105,18 +105,18 @@ class ACLHelper: so on, until the lineage is exhausted or we determine that the policy permits or denies. - During this processing, if any :data:`pyramid.security.Deny` + During this processing, if any :data:`pyramid.authorization.Deny` ACE is found matching any principal in ``principals``, stop processing by returning an - :class:`pyramid.security.ACLDenied` instance (equals + :class:`pyramid.authorization.ACLDenied` instance (equals ``False``) immediately. If any - :data:`pyramid.security.Allow` ACE is found matching any + :data:`pyramid.authorization.Allow` ACE is found matching any principal, stop processing by returning an - :class:`pyramid.security.ACLAllowed` instance (equals + :class:`pyramid.authorization.ACLAllowed` instance (equals ``True``) immediately. If we exhaust the context's :term:`lineage`, and no ACE has explicitly permitted or denied access, return an instance of - :class:`pyramid.security.ACLDenied` (equals ``False``). + :class:`pyramid.authorization.ACLDenied` (equals ``False``). """ acl = '' @@ -160,13 +160,13 @@ class ACLHelper: of principals that are explicitly granted the ``permission`` in the provided ``context``. We do this by walking 'up' the object graph *from the root* to the context. During this walking process, if we - find an explicit :data:`pyramid.security.Allow` ACE for a principal + find an explicit :data:`pyramid.authorization.Allow` ACE for a principal that matches the ``permission``, the principal is included in the allow list. However, if later in the walking process that principal is - mentioned in any :data:`pyramid.security.Deny` ACE for the permission, - the principal is removed from the allow list. If a - :data:`pyramid.security.Deny` to the principal - :data:`pyramid.security.Everyone` is encountered during the walking + mentioned in any :data:`pyramid.authorization.Deny` ACE for the + permission, the principal is removed from the allow list. If a + :data:`pyramid.authorization.Deny` to the principal + :data:`pyramid.authorization.Everyone` is encountered during the walking process that matches the ``permission``, the allow list is cleared for all principals encountered in previous ACLs. The walking process ends after we've processed the any ACL directly attached to ``context``; a diff --git a/src/pyramid/config/routes.py b/src/pyramid/config/routes.py index 44fbb9c46..219c67ddc 100644 --- a/src/pyramid/config/routes.py +++ b/src/pyramid/config/routes.py @@ -278,7 +278,7 @@ class RoutesConfiguratorMixin(object): indicates that every principal named in the argument list is present in the current request, this predicate will return True; otherwise it will return False. For example: - ``effective_principals=pyramid.security.Authenticated`` or + ``effective_principals=pyramid.authorization.Authenticated`` or ``effective_principals=('fred', 'group:admins')``. .. versionadded:: 1.4a4 diff --git a/src/pyramid/config/views.py b/src/pyramid/config/views.py index 2cc5e8144..e0e5d8d29 100644 --- a/src/pyramid/config/views.py +++ b/src/pyramid/config/views.py @@ -718,7 +718,7 @@ class ViewsConfiguratorMixin(object): indicates that every principal named in the argument list is present in the current request, this predicate will return True; otherwise it will return False. For example: - ``effective_principals=pyramid.security.Authenticated`` or + ``effective_principals=pyramid.authorization.Authenticated`` or ``effective_principals=('fred', 'group:admins')``. .. versionadded:: 1.4a4 diff --git a/src/pyramid/httpexceptions.py b/src/pyramid/httpexceptions.py index 51c2e90a0..76e28424a 100644 --- a/src/pyramid/httpexceptions.py +++ b/src/pyramid/httpexceptions.py @@ -755,7 +755,7 @@ class HTTPForbidden(HTTPClientError): argument, ``detail``, should be a string. The value of this string will be used as the ``message`` attribute of the exception object. The second special keyword argument, ``result`` is usually an instance of - :class:`pyramid.security.Denied` or :class:`pyramid.security.ACLDenied` + :class:`pyramid.security.Denied` or :class:`pyramid.authorization.ACLDenied` each of which indicates a reason for the forbidden error. However, ``result`` is also permitted to be just a plain boolean ``False`` object or ``None``. The ``result`` value will be used as the ``result`` diff --git a/src/pyramid/interfaces.py b/src/pyramid/interfaces.py index c4160cc2b..433ac0c9d 100644 --- a/src/pyramid/interfaces.py +++ b/src/pyramid/interfaces.py @@ -554,8 +554,8 @@ class IAuthenticationPolicy(Interface): """ Return a sequence representing the effective principals typically including the :term:`userid` and any groups belonged to by the current user, always including 'system' groups such - as ``pyramid.security.Everyone`` and - ``pyramid.security.Authenticated``. + as ``pyramid.authorization.Everyone`` and + ``pyramid.authorization.Authenticated``. """ diff --git a/src/pyramid/security.py b/src/pyramid/security.py index 7b27c45f4..a5b4ce442 100644 --- a/src/pyramid/security.py +++ b/src/pyramid/security.py @@ -92,7 +92,7 @@ def principals_allowed_by_permission(context, permission): in effect, return a sequence of :term:`principal` ids that possess the permission in the ``context``. If no authorization policy is in effect, this will return a sequence with the single value - :mod:`pyramid.security.Everyone` (the special principal + :mod:`pyramid.authorization.Everyone` (the special principal identifier representing all principals). .. note:: -- cgit v1.2.3 From 8b6efc3cfbf9accc6bf2a009e124dee2b3c04840 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 12 Jan 2020 20:50:59 -0600 Subject: fix lint --- src/pyramid/authentication.py | 2 +- src/pyramid/authorization.py | 29 +++++++++++++++-------------- src/pyramid/httpexceptions.py | 11 ++++++----- 3 files changed, 22 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/pyramid/authentication.py b/src/pyramid/authentication.py index 8c6c0f981..95c01bbea 100644 --- a/src/pyramid/authentication.py +++ b/src/pyramid/authentication.py @@ -10,8 +10,8 @@ import warnings from webob.cookies import CookieProfile from zope.interface import implementer -from pyramid.interfaces import IAuthenticationPolicy, IDebugLogger from pyramid.authorization import Authenticated, Everyone +from pyramid.interfaces import IAuthenticationPolicy, IDebugLogger from pyramid.util import ( SimpleSerializer, ascii_, diff --git a/src/pyramid/authorization.py b/src/pyramid/authorization.py index 87e6b8767..a0524a8cc 100644 --- a/src/pyramid/authorization.py +++ b/src/pyramid/authorization.py @@ -33,9 +33,6 @@ class AllPermissionsList(_AllPermissionsList): pass -ALL_PERMISSIONS = AllPermissionsList() # api -DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) # api - # subclass to fix __qualname__ class ACLAllowed(_ACLAllowed): pass @@ -46,6 +43,10 @@ class ACLDenied(_ACLDenied): pass +ALL_PERMISSIONS = AllPermissionsList() # api +DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) # api + + @implementer(IAuthorizationPolicy) class ACLAuthorizationPolicy(object): """ An :term:`authorization policy` which consults an :term:`ACL` @@ -160,17 +161,17 @@ class ACLHelper: of principals that are explicitly granted the ``permission`` in the provided ``context``. We do this by walking 'up' the object graph *from the root* to the context. During this walking process, if we - find an explicit :data:`pyramid.authorization.Allow` ACE for a principal - that matches the ``permission``, the principal is included in the allow - list. However, if later in the walking process that principal is - mentioned in any :data:`pyramid.authorization.Deny` ACE for the - permission, the principal is removed from the allow list. If a - :data:`pyramid.authorization.Deny` to the principal - :data:`pyramid.authorization.Everyone` is encountered during the walking - process that matches the ``permission``, the allow list is cleared for - all principals encountered in previous ACLs. The walking process ends - after we've processed the any ACL directly attached to ``context``; a - set of principals is returned. + find an explicit :data:`pyramid.authorization.Allow` ACE for a + principal that matches the ``permission``, the principal is included in + the allow list. However, if later in the walking process that + principal is mentioned in any :data:`pyramid.authorization.Deny` ACE + for the permission, the principal is removed from the allow list. If + a :data:`pyramid.authorization.Deny` to the principal + :data:`pyramid.authorization.Everyone` is encountered during the + walking process that matches the ``permission``, the allow list is + cleared for all principals encountered in previous ACLs. The walking + process ends after we've processed the any ACL directly attached to + ``context``; a set of principals is returned. """ allowed = set() diff --git a/src/pyramid/httpexceptions.py b/src/pyramid/httpexceptions.py index 76e28424a..dcf61b9e5 100644 --- a/src/pyramid/httpexceptions.py +++ b/src/pyramid/httpexceptions.py @@ -755,11 +755,12 @@ class HTTPForbidden(HTTPClientError): argument, ``detail``, should be a string. The value of this string will be used as the ``message`` attribute of the exception object. The second special keyword argument, ``result`` is usually an instance of - :class:`pyramid.security.Denied` or :class:`pyramid.authorization.ACLDenied` - each of which indicates a reason for the forbidden error. However, - ``result`` is also permitted to be just a plain boolean ``False`` object - or ``None``. The ``result`` value will be used as the ``result`` - attribute of the exception object. It defaults to ``None``. + :class:`pyramid.security.Denied` or + :class:`pyramid.authorization.ACLDenied` each of which indicates a reason + for the forbidden error. However, ``result`` is also permitted to be just + a plain boolean ``False`` object or ``None``. The ``result`` value will + be used as the ``result`` attribute of the exception object. + It defaults to ``None``. The :term:`Forbidden View` can use the attributes of a Forbidden exception as necessary to provide extended information in an error -- cgit v1.2.3 From eb7046c8eeb8c9b598260ae8c8976187a8f84953 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 14 Jan 2020 23:01:44 -0600 Subject: fix comment --- src/pyramid/authorization.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/pyramid/authorization.py b/src/pyramid/authorization.py index a0524a8cc..42171088f 100644 --- a/src/pyramid/authorization.py +++ b/src/pyramid/authorization.py @@ -29,16 +29,15 @@ Allow = Allow # api Deny = Deny # api +# subclasses to fix __module__ class AllPermissionsList(_AllPermissionsList): pass -# subclass to fix __qualname__ class ACLAllowed(_ACLAllowed): pass -# subclass to fix __qualname__ class ACLDenied(_ACLDenied): pass -- cgit v1.2.3