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 docs enhancements #1399

Merged
merged 16 commits into from Nov 18, 2014
Jump to file or symbol
Failed to load files and symbols.
+209 −105
Diff settings

Always

Just for now

View
@@ -78,6 +78,13 @@ Bug Fixes
- Fix route generation for static view asset specifications having no path.
See https://github.com/Pylons/pyramid/pull/1377
Deprecations
------------
- Renamed the ``principal`` argument to ``pyramid.security.remember()`` to
``userid`` in order to clarify its intended purpose.
See https://github.com/Pylons/pyramid/pull/1399
Docs
----
@@ -88,6 +95,10 @@ Docs
- Clarify a previously-implied detail of the ``ISession.invalidate`` API
documentation.
- Improve and clarify the documentation on what Pyramid defines as a
``principal`` and a ``userid`` in its security APIs.
See https://github.com/Pylons/pyramid/pull/1399
Scaffolds
---------
View
@@ -125,7 +125,10 @@ Future
- 1.7: Change ``pyramid.authentication.AuthTktAuthenticationPolicy`` default
``hashalg`` to ``sha512``.
- 1.8 Remove set_request_property.
- 1.8: Remove set_request_property.
- 1.9: Remove extra code enabling ``pyramid.security.remember(principal=...)``
and force use of ``userid``.
Probably Bad Ideas
------------------
View
@@ -167,37 +167,40 @@
.. versionadded:: 1.5
A property which returns the userid of the currently authenticated user
or ``None`` if there is no :term:`authentication policy` in effect or
there is no currently authenticated user. This differs from
:attr:`~pyramid.request.Request.unauthenticated_userid`, because the
effective authentication policy will have ensured that a record
associated with the userid exists in persistent storage; if it has
not, this value will be ``None``.
A property which returns the :term:`userid` of the currently
authenticated user or ``None`` if there is no :term:`authentication
policy` in effect or there is no currently authenticated user. This
differs from :attr:`~pyramid.request.Request.unauthenticated_userid`,
because the effective authentication policy will have ensured that a
record associated with the :term:`userid` exists in persistent storage;
if it has not, this value will be ``None``.
.. attribute:: unauthenticated_userid
.. versionadded:: 1.5
A property which returns a value which represents the *claimed* (not
verified) user id of the credentials present in the request. ``None`` if
there is no :term:`authentication policy` in effect or there is no user
data associated with the current request. This differs from
:attr:`~pyramid.request.Request.authenticated_userid`, because the
effective authentication policy will not ensure that a record associated
with the userid exists in persistent storage. Even if the userid
does not exist in persistent storage, this value will be the value
of the userid *claimed* by the request data.
verified) :term:`userid` of the credentials present in the
request. ``None`` if there is no :term:`authentication policy` in effect
or there is no user data associated with the current request. This
differs from :attr:`~pyramid.request.Request.authenticated_userid`,
because the effective authentication policy will not ensure that a
record associated with the :term:`userid` exists in persistent storage.
Even if the :term:`userid` does not exist in persistent storage, this
value will be the value of the :term:`userid` *claimed* by the request
data.
.. attribute:: effective_principals
.. versionadded:: 1.5
A property which returns the list of 'effective' :term:`principal`
identifiers for this request. This will include the userid of the
currently authenticated user if a user is currently authenticated. If no
:term:`authentication policy` is in effect, this will return a sequence
containing only the :attr:`pyramid.security.Everyone` principal.
identifiers for this request. This list typically includes the
:term:`userid` of the currently authenticated user if a user is
currently authenticated, but this depends on the
:term:`authentication policy` in effect. If no :term:`authentication
policy` is in effect, this will return a sequence containing only the
:attr:`pyramid.security.Everyone` principal.
.. method:: invoke_subrequest(request, use_tweens=False)
View
@@ -16,7 +16,7 @@ Authentication API Functions
.. autofunction:: forget
.. autofunction:: remember
.. autofunction:: remember(request, userid, **kwargs)

This comment has been minimized.

@tseaver

tseaver Nov 10, 2014

Member

Redundant? autofunction already sniffs the signature, unless it is hidden by a decorator.

@tseaver

tseaver Nov 10, 2014

Member

Redundant? autofunction already sniffs the signature, unless it is hidden by a decorator.

This comment has been minimized.

@mmerickel

mmerickel Nov 10, 2014

Member

It's done here to hide the userid=_marker from the new signature in the docs. We do this in some other areas as well where a function has some extra arg used only for testing.

@mmerickel

mmerickel Nov 10, 2014

Member

It's done here to hide the userid=_marker from the new signature in the docs. We do this in some other areas as well where a function has some extra arg used only for testing.

Authorization API Functions
---------------------------
View
@@ -286,13 +286,22 @@ Glossary
:term:`authorization policy`.
principal
A *principal* is a string or unicode object representing a userid
or a group id. It is provided by an :term:`authentication
policy`. For example, if a user had the user id "bob", and Bob
was part of two groups named "group foo" and "group bar", the
request might have information attached to it that would
indicate that Bob was represented by three principals: "bob",
"group foo" and "group bar".
A *principal* is a string or unicode object representing an
entity, typically a user or group. Principals are provided by an
:term:`authentication policy`. For example, if a user had the
:term:`userid` `"bob"`, and was part of two groups named `"group foo"`
and "group bar", the request might have information attached to
it that would indicate that Bob was represented by three
principals: `"bob"`, `"group foo"` and `"group bar"`.
userid
A *userid* is a string or unicode object used to identify and
authenticate a real-world user (or client). A userid is
supplied to an :term:`authentication policy` in order to discover
the user's :term:`principals <principal>`. The default behavior
of the authentication policies :app:`Pyramid` provides is to
return the user's userid as a principal, but this is not strictly
necessary in custom policies that define their principals differently.
authorization policy
An authorization policy in :app:`Pyramid` terms is a bit of
View
@@ -6,13 +6,28 @@
Security
========
:app:`Pyramid` provides an optional declarative authorization system
that can prevent a :term:`view` from being invoked based on an
:app:`Pyramid` provides an optional, declarative, security system.
Security in :app:`Pyramid` is separated into authentication and
authorization. The two systems communicate via :term:`principal`
identifiers. Authentication is merely the mechanism by which credentials
provided in the :term:`request` are resolved to one or more
:term:`principal` identifiers. These identifiers represent the users and
groups that are in effect during the request. Authorization then determines
access based on the :term:`principal` identifiers, the requested
:term:`permission`, and a :term:`context`.
The :app:`Pyramid` authorization system
can prevent a :term:`view` from being invoked based on an
:term:`authorization policy`. Before a view is invoked, the
authorization system can use the credentials in the :term:`request`
along with the :term:`context` resource to determine if access will be
allowed. Here's how it works at a high level:
- A user may or may not have previously visited the application and
supplied authentication credentials, including a :term:`userid`. If
so, the application may have called
:func:`pyramid.security.remember` to remember these.
- A :term:`request` is generated when a user visits the application.
- Based on the request, a :term:`context` resource is located through
@@ -25,7 +40,9 @@ allowed. Here's how it works at a high level:
context as well as other attributes of the request.
- If an :term:`authentication policy` is in effect, it is passed the
request; it returns some number of :term:`principal` identifiers.
request. It will return some number of :term:`principal` identifiers.
To do this, the policy would need to determine the authenticated
:term:`userid` present in the request.
- If an :term:`authorization policy` is in effect and the :term:`view
configuration` associated with the view callable that was found has
@@ -41,15 +58,6 @@ allowed. Here's how it works at a high level:
- If the authorization policy denies access, the view callable is not
invoked; instead the :term:`forbidden view` is invoked.
Security in :app:`Pyramid`, unlike many systems, cleanly and explicitly
separates authentication and authorization. Authentication is merely the
mechanism by which credentials provided in the :term:`request` are
resolved to one or more :term:`principal` identifiers. These identifiers
represent the users and groups in effect during the request.
Authorization then determines access based on the :term:`principal`
identifiers, the :term:`view callable` being invoked, and the
:term:`context` resource.
Authorization is enabled by modifying your application to include an
:term:`authentication policy` and :term:`authorization policy`.
:app:`Pyramid` comes with a variety of implementations of these
@@ -104,7 +112,8 @@ For example:
The above configuration enables a policy which compares the value of an "auth
ticket" cookie passed in the request's environment which contains a reference
to a single :term:`principal` against the principals present in any
to a single :term:`userid` and matches that userid's
:term:`principals <principal>` against the principals present in any
:term:`ACL` found in the resource tree when attempting to call some
:term:`view`.
@@ -595,36 +604,53 @@ that implements the following interface:
""" An object representing a Pyramid authentication policy. """
def authenticated_userid(self, request):
""" Return the authenticated userid or ``None`` if no
authenticated userid can be found. This method of the policy
should ensure that a record exists in whatever persistent store is
used related to the user (the user should not have been deleted);
if a record associated with the current id does not exist in a
persistent store, it should return ``None``."""
""" Return the authenticated :term:`userid` or ``None`` if
no authenticated userid can be found. This method of the
policy should ensure that a record exists in whatever
persistent store is used related to the user (the user
should not have been deleted); if a record associated with
the current id does not exist in a persistent store, it
should return ``None``.
"""
def unauthenticated_userid(self, request):
""" Return the *unauthenticated* userid. This method performs the
same duty as ``authenticated_userid`` but is permitted to return the
userid based only on data present in the request; it needn't (and
shouldn't) check any persistent store to ensure that the user record
related to the request userid exists."""
""" Return the *unauthenticated* userid. This method
performs the same duty as ``authenticated_userid`` but is
permitted to return the userid based only on data present
in the request; it needn't (and shouldn't) check any
persistent store to ensure that the user record related to
the request userid exists.
This method is intended primarily a helper to assist the
``authenticated_userid`` method in pulling credentials out
of the request data, abstracting away the specific headers,
query strings, etc that are used to authenticate the request.
"""
def effective_principals(self, request):
""" Return a sequence representing the effective principals
including the userid and any groups belonged to by the current
user, including 'system' groups such as
``pyramid.security.Everyone`` and
``pyramid.security.Authenticated``. """
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``.
def remember(self, request, principal, **kw):
"""
def remember(self, request, userid, **kw):
""" Return a set of headers suitable for 'remembering' the
principal named ``principal`` when set in a response. An
individual authentication policy and its consumers can decide
on the composition and meaning of **kw. """
: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(self, request):
""" Return a set of headers suitable for 'forgetting' the
current user on subsequent requests. """
current user on subsequent requests.
"""
After you do so, you can pass an instance of such a class into the
:class:`~pyramid.config.Configurator.set_authentication_policy` method
@@ -53,10 +53,10 @@ Security
We'll eventually be adding security to our application. The components we'll
use to do this are below.
- USERS, a dictionary mapping usernames to their
- USERS, a dictionary mapping :term:`userids <userid>` to their
corresponding passwords.
- GROUPS, a dictionary mapping usernames to a
- GROUPS, a dictionary mapping :term:`userids <userid>` to a
list of groups to which they belong to.
- ``groupfinder``, an *authorization callback* that looks up
@@ -53,7 +53,8 @@ Security
We'll eventually be adding security to our application. The components we'll
use to do this are below.
- USERS, a dictionary mapping users names to their corresponding passwords.
- USERS, a dictionary mapping users names (the user's :term:`userids
<userid>`) to their corresponding passwords.
- GROUPS, a dictionary mapping user names to a list of groups they belong to.
View
@@ -335,11 +335,11 @@ def effective_principals(self, request):
effective_principals.extend(groups)
return effective_principals
def remember(self, request, principal, **kw):
""" Store the ``principal`` as ``repoze.who.userid``.
def remember(self, request, userid, **kw):
""" Store the ``userid`` as ``repoze.who.userid``.
The identity to authenticated to :mod:`repoze.who`
will contain the given principal as ``userid``, and
will contain the given userid as ``userid``, and
provide all keyword arguments as additional identity
keys. Useful keys could be ``max_age`` or ``userdata``.
"""
@@ -348,7 +348,7 @@ def remember(self, request, principal, **kw):
return []
environ = request.environ
identity = kw
identity['repoze.who.userid'] = principal
identity['repoze.who.userid'] = userid
return identifier.remember(environ, identity)
def forget(self, request):
@@ -404,7 +404,7 @@ def unauthenticated_userid(self, request):
""" The ``REMOTE_USER`` value found within the ``environ``."""
return request.environ.get(self.environ_key)
def remember(self, request, principal, **kw):
def remember(self, request, userid, **kw):
""" A no-op. The ``REMOTE_USER`` does not provide a protocol for
remembering the user. This will be application-specific and can
be done somewhere else or in a subclass."""
@@ -652,15 +652,15 @@ def unauthenticated_userid(self, request):
if result:
return result['userid']
def remember(self, request, principal, **kw):
def remember(self, request, userid, **kw):
""" Accepts the following kw args: ``max_age=<int-seconds>,
``tokens=<sequence-of-ascii-strings>``.
Return a list of headers which will set appropriate cookies on
the response.
"""
return self.cookie.remember(request, principal, **kw)
return self.cookie.remember(request, userid, **kw)
def forget(self, request):
""" A list of headers which will delete appropriate cookies."""
@@ -1061,13 +1061,13 @@ def __init__(self, prefix='auth.', callback=None, debug=False):
self.userid_key = prefix + 'userid'
self.debug = debug
def remember(self, request, principal, **kw):
""" Store a principal in the session."""
request.session[self.userid_key] = principal
def remember(self, request, userid, **kw):
""" Store a userid in the session."""
request.session[self.userid_key] = userid
return []
def forget(self, request):
""" Remove the stored principal from the session."""
""" Remove the stored userid from the session."""
if self.userid_key in request.session:
del request.session[self.userid_key]
return []
@@ -1132,7 +1132,7 @@ def unauthenticated_userid(self, request):
if credentials:
return credentials[0]
def remember(self, request, principal, **kw):
def remember(self, request, userid, **kw):
""" A no-op. Basic authentication does not provide a protocol for
remembering the user. Credentials are sent on every request.
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.