Update to 0.3.0
This commit is contained in:
parent
097f7ecc43
commit
9b1d08c661
|
@ -56,3 +56,4 @@ docs/_build/
|
|||
target/
|
||||
|
||||
coverage
|
||||
.pytest_cache
|
15
CHANGES.txt
15
CHANGES.txt
|
@ -1,10 +1,23 @@
|
|||
Changes
|
||||
=======
|
||||
|
||||
0.3.0 (2018-09-06)
|
||||
------------------
|
||||
|
||||
- Deprecate ``login_required`` and ``has_permission`` decorators.
|
||||
Use ``check_authorized`` and ``check_permission`` helper functions instead.
|
||||
|
||||
- Bump supported ``aiohttp`` version to 3.0+
|
||||
|
||||
- Enable strong warnings mode for test suite, clean-up all deprecation
|
||||
warnings.
|
||||
|
||||
- Polish documentation
|
||||
|
||||
0.2.0 (2017-11-17)
|
||||
------------------
|
||||
|
||||
- Add `is_anonymous`, `login_required`, `has_permission` helpers (#114)
|
||||
- Add ``is_anonymous``, ``login_required``, ``has_permission`` helpers (#114)
|
||||
|
||||
0.1.2 (2017-10-17)
|
||||
------------------
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from .abc import AbstractAuthorizationPolicy, AbstractIdentityPolicy
|
||||
from .api import (authorized_userid, forget, has_permission, is_anonymous,
|
||||
login_required, permits, remember, setup)
|
||||
from .api import (authorized_userid, forget, has_permission,
|
||||
is_anonymous, login_required, permits, remember,
|
||||
setup, check_authorized, check_permission)
|
||||
from .cookies_identity import CookiesIdentityPolicy
|
||||
from .session_identity import SessionIdentityPolicy
|
||||
from .jwt_identity import JWTIdentityPolicy
|
||||
|
||||
__version__ = '0.2.0'
|
||||
__version__ = '0.3.0'
|
||||
|
||||
|
||||
__all__ = ('AbstractIdentityPolicy', 'AbstractAuthorizationPolicy',
|
||||
|
@ -13,4 +14,5 @@ __all__ = ('AbstractIdentityPolicy', 'AbstractAuthorizationPolicy',
|
|||
'JWTIdentityPolicy',
|
||||
'remember', 'forget', 'authorized_userid',
|
||||
'permits', 'setup', 'is_anonymous',
|
||||
'login_required', 'has_permission')
|
||||
'login_required', 'has_permission',
|
||||
'check_authorized', 'check_permission')
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import enum
|
||||
import warnings
|
||||
from aiohttp import web
|
||||
from aiohttp_security.abc import (AbstractIdentityPolicy,
|
||||
AbstractAuthorizationPolicy)
|
||||
|
@ -86,6 +87,15 @@ async def is_anonymous(request):
|
|||
return False
|
||||
|
||||
|
||||
async def check_authorized(request):
|
||||
"""Checker that raises HTTPUnauthorized for anonymous users.
|
||||
"""
|
||||
userid = await authorized_userid(request)
|
||||
if userid is None:
|
||||
raise web.HTTPUnauthorized()
|
||||
return userid
|
||||
|
||||
|
||||
def login_required(fn):
|
||||
"""Decorator that restrict access only for authorized users.
|
||||
|
||||
|
@ -101,21 +111,34 @@ def login_required(fn):
|
|||
"or `def handler(self, request)`.")
|
||||
raise RuntimeError(msg)
|
||||
|
||||
userid = await authorized_userid(request)
|
||||
if userid is None:
|
||||
raise web.HTTPUnauthorized
|
||||
|
||||
ret = await fn(*args, **kwargs)
|
||||
return ret
|
||||
await check_authorized(request)
|
||||
return await fn(*args, **kwargs)
|
||||
|
||||
warnings.warn("login_required decorator is deprecated, "
|
||||
"use check_authorized instead",
|
||||
DeprecationWarning)
|
||||
return wrapped
|
||||
|
||||
|
||||
async def check_permission(request, permission, context=None):
|
||||
"""Checker that passes only to authoraised users with given permission.
|
||||
|
||||
If user is not authorized - raises HTTPUnauthorized,
|
||||
if user is authorized and does not have permission -
|
||||
raises HTTPForbidden.
|
||||
"""
|
||||
|
||||
await check_authorized(request)
|
||||
allowed = await permits(request, permission, context)
|
||||
if not allowed:
|
||||
raise web.HTTPForbidden()
|
||||
|
||||
|
||||
def has_permission(
|
||||
permission,
|
||||
context=None,
|
||||
):
|
||||
"""Decorator that restrict access only for authorized users
|
||||
"""Decorator that restricts access only for authorized users
|
||||
with correct permissions.
|
||||
|
||||
If user is not authorized - raises HTTPUnauthorized,
|
||||
|
@ -132,18 +155,14 @@ def has_permission(
|
|||
"or `def handler(self, request)`.")
|
||||
raise RuntimeError(msg)
|
||||
|
||||
userid = await authorized_userid(request)
|
||||
if userid is None:
|
||||
raise web.HTTPUnauthorized
|
||||
|
||||
allowed = await permits(request, permission, context)
|
||||
if not allowed:
|
||||
raise web.HTTPForbidden
|
||||
ret = await fn(*args, **kwargs)
|
||||
return ret
|
||||
await check_permission(request, permission, context)
|
||||
return await fn(*args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
warnings.warn("has_permission decorator is deprecated, "
|
||||
"use check_permission instead",
|
||||
DeprecationWarning)
|
||||
return wrapper
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class JWTIdentityPolicy(AbstractIdentityPolicy):
|
|||
|
||||
identity = jwt.decode(token,
|
||||
self.secret,
|
||||
algorithm=self.algorithm)
|
||||
algorithms=[self.algorithm])
|
||||
return identity
|
||||
|
||||
async def remember(self, *args, **kwargs): # pragma: no cover
|
||||
|
|
|
@ -4,7 +4,7 @@ from aiohttp import web
|
|||
|
||||
from aiohttp_security import (
|
||||
remember, forget, authorized_userid,
|
||||
has_permission, login_required,
|
||||
check_permission, check_authorized,
|
||||
)
|
||||
|
||||
from .db_auth import check_credentials
|
||||
|
@ -45,25 +45,25 @@ class Web(object):
|
|||
db_engine = request.app.db_engine
|
||||
if await check_credentials(db_engine, login, password):
|
||||
await remember(request, response, login)
|
||||
return response
|
||||
raise response
|
||||
|
||||
return web.HTTPUnauthorized(
|
||||
raise web.HTTPUnauthorized(
|
||||
body=b'Invalid username/password combination')
|
||||
|
||||
@login_required
|
||||
async def logout(self, request):
|
||||
await check_authorized(request)
|
||||
response = web.Response(body=b'You have been logged out')
|
||||
await forget(request, response)
|
||||
return response
|
||||
|
||||
@has_permission('public')
|
||||
async def internal_page(self, request):
|
||||
await check_permission(request, 'public')
|
||||
response = web.Response(
|
||||
body=b'This page is visible for all registered users')
|
||||
return response
|
||||
|
||||
@has_permission('protected')
|
||||
async def protected_page(self, request):
|
||||
await check_permission(request, 'protected')
|
||||
response = web.Response(body=b'You are on protected page')
|
||||
return response
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ async def init(loop):
|
|||
password='aiohttp_security',
|
||||
database='aiohttp_security',
|
||||
host='127.0.0.1')
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
app.db_engine = db_engine
|
||||
setup_session(app, RedisStorage(redis_pool))
|
||||
setup_security(app,
|
||||
|
|
|
@ -4,7 +4,7 @@ from aiohttp import web
|
|||
|
||||
from aiohttp_security import (
|
||||
remember, forget, authorized_userid,
|
||||
has_permission, login_required,
|
||||
check_permission, check_authorized,
|
||||
)
|
||||
|
||||
from .authz import check_credentials
|
||||
|
@ -55,8 +55,8 @@ async def login(request):
|
|||
return web.HTTPUnauthorized(body='Invalid username / password combination')
|
||||
|
||||
|
||||
@login_required
|
||||
async def logout(request):
|
||||
await check_authorized(request)
|
||||
response = web.Response(
|
||||
text='You have been logged out',
|
||||
content_type='text/html',
|
||||
|
@ -65,9 +65,8 @@ async def logout(request):
|
|||
return response
|
||||
|
||||
|
||||
@has_permission('public')
|
||||
async def internal_page(request):
|
||||
# pylint: disable=unused-argument
|
||||
await check_permission(request, 'public')
|
||||
response = web.Response(
|
||||
text='This page is visible for all registered users',
|
||||
content_type='text/html',
|
||||
|
@ -75,9 +74,8 @@ async def internal_page(request):
|
|||
return response
|
||||
|
||||
|
||||
@has_permission('protected')
|
||||
async def protected_page(request):
|
||||
# pylint: disable=unused-argument
|
||||
await check_permission(request, 'protected')
|
||||
response = web.Response(
|
||||
text='You are on protected page',
|
||||
content_type='text/html',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from aiohttp import web
|
||||
from aiohttp_session import SimpleCookieStorage, session_middleware
|
||||
from aiohttp_security import has_permission, \
|
||||
from aiohttp_security import check_permission, \
|
||||
is_anonymous, remember, forget, \
|
||||
setup as setup_security, SessionIdentityPolicy
|
||||
from aiohttp_security.abc import AbstractAuthorizationPolicy
|
||||
|
@ -54,13 +54,13 @@ async def handler_logout(request):
|
|||
raise redirect_response
|
||||
|
||||
|
||||
@has_permission('listen')
|
||||
async def handler_listen(request):
|
||||
await check_permission(request, 'listen')
|
||||
return web.Response(body="I can listen!")
|
||||
|
||||
|
||||
@has_permission('speak')
|
||||
async def handler_speak(request):
|
||||
await check_permission(request, 'speak')
|
||||
return web.Response(body="I can speak!")
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ Simple example::
|
|||
|
||||
from aiohttp import web
|
||||
from aiohttp_session import SimpleCookieStorage, session_middleware
|
||||
from aiohttp_security import has_permission, \
|
||||
from aiohttp_security import check_permission, \
|
||||
is_anonymous, remember, forget, \
|
||||
setup as setup_security, SessionIdentityPolicy
|
||||
from aiohttp_security.abc import AbstractAuthorizationPolicy
|
||||
|
@ -63,13 +63,13 @@ Simple example::
|
|||
raise redirect_response
|
||||
|
||||
|
||||
@has_permission('listen')
|
||||
async def handler_listen(request):
|
||||
await check_permission(request, 'listen')
|
||||
return web.Response(body="I can listen!")
|
||||
|
||||
|
||||
@has_permission('speak')
|
||||
async def handler_speak(request):
|
||||
await check_permission(request, 'speak')
|
||||
return web.Response(body="I can speak!")
|
||||
|
||||
|
||||
|
@ -85,11 +85,12 @@ Simple example::
|
|||
app = web.Application(middlewares=[middleware])
|
||||
|
||||
# add the routes
|
||||
app.router.add_route('GET', '/', handler_root)
|
||||
app.router.add_route('GET', '/login', handler_login_jack)
|
||||
app.router.add_route('GET', '/logout', handler_logout)
|
||||
app.router.add_route('GET', '/listen', handler_listen)
|
||||
app.router.add_route('GET', '/speak', handler_speak)
|
||||
app.add_routes([
|
||||
web.get('/', handler_root),
|
||||
web.get('/login', handler_login_jack),
|
||||
web.get('/logout', handler_logout),
|
||||
web.get('/listen', handler_listen),
|
||||
web.get('/speak', handler_speak)])
|
||||
|
||||
# set up policies
|
||||
policy = SessionIdentityPolicy()
|
||||
|
|
|
@ -133,7 +133,7 @@ Once we have all the code in place we can install it for our application::
|
|||
password='aiohttp_security',
|
||||
database='aiohttp_security',
|
||||
host='127.0.0.1')
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
setup_session(app, RedisStorage(redis_pool))
|
||||
setup_security(app,
|
||||
SessionIdentityPolicy(),
|
||||
|
@ -142,23 +142,23 @@ Once we have all the code in place we can install it for our application::
|
|||
|
||||
|
||||
Now we have authorization and can decorate every other view with access rights
|
||||
based on permissions. There are already implemented two decorators::
|
||||
based on permissions. There are already implemented two helpers::
|
||||
|
||||
from aiohttp_security import has_permission, login_required
|
||||
from aiohttp_security import check_authorized, check_permission
|
||||
|
||||
For each view you need to protect - just apply the decorator on it::
|
||||
|
||||
class Web:
|
||||
@has_permission('protected')
|
||||
async def protected_page(self, request):
|
||||
await check_permission(request, 'protected')
|
||||
response = web.Response(body=b'You are on protected page')
|
||||
return response
|
||||
|
||||
or::
|
||||
|
||||
class Web:
|
||||
@login_required
|
||||
async def logout(self, request):
|
||||
await check_authorized(request)
|
||||
response = web.Response(body=b'You have been logged out')
|
||||
await forget(request, response)
|
||||
return response
|
||||
|
|
|
@ -3,6 +3,7 @@ aiohttp_security
|
|||
|
||||
The library provides security for :ref:`aiohttp.web<aiohttp-web>`.
|
||||
|
||||
The current version is |version|
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
|
|
@ -13,6 +13,19 @@
|
|||
Public API functions
|
||||
====================
|
||||
|
||||
.. function:: setup(app, identity_policy, autz_policy)
|
||||
|
||||
Setup :mod:`aiohttp` application with security policies.
|
||||
|
||||
:param app: aiohttp :class:`aiohttp.web.Application` instance.
|
||||
|
||||
:param identity_policy: indentification policy, an
|
||||
:class:`AbstractIdentityPolicy` instance.
|
||||
|
||||
:param autz_policy: authorization policy, an
|
||||
:class:`AbstractAuthorizationPolicy` instance.
|
||||
|
||||
|
||||
.. coroutinefunction:: remember(request, response, identity, **kwargs)
|
||||
|
||||
Remember *identity* in *response*, e.g. by storing a cookie or
|
||||
|
@ -50,6 +63,41 @@ Public API functions
|
|||
descendants like :class:`aiohttp.web.Response`.
|
||||
|
||||
|
||||
.. coroutinefunction:: check_authorized(request)
|
||||
|
||||
Checker that doesn't pass if user is not authorized by *request*.
|
||||
|
||||
:param request: :class:`aiohttp.web.Request` object.
|
||||
|
||||
:return str: authorized user ID if success
|
||||
|
||||
:raise: :class:`aiohttp.web.HTTPUnauthorized` for anonymous users.
|
||||
|
||||
Usage::
|
||||
|
||||
async def handler(request):
|
||||
await check_authorized(request)
|
||||
# this line is never executed for anonymous users
|
||||
|
||||
|
||||
.. coroutinefunction:: check_permission(request, permission)
|
||||
|
||||
Checker that doesn't pass if user has no requested permission.
|
||||
|
||||
:param request: :class:`aiohttp.web.Request` object.
|
||||
|
||||
:raise: :class:`aiohttp.web.HTTPUnauthorized` for anonymous users.
|
||||
|
||||
:raise: :class:`aiohttp.web.HTTPForbidden` if user is
|
||||
authorized but has no access rights.
|
||||
|
||||
Usage::
|
||||
|
||||
async def handler(request):
|
||||
await check_permission(request, 'read')
|
||||
# this line is never executed if a user has no read permission
|
||||
|
||||
|
||||
.. coroutinefunction:: authorized_userid(request)
|
||||
|
||||
Retrieve :term:`userid`.
|
||||
|
@ -78,7 +126,8 @@ Public API functions
|
|||
|
||||
:param request: :class:`aiohttp.web.Request` object.
|
||||
|
||||
:param permission: Requested :term:`permission`. :class:`str` or :class:`enum.Enum` object.
|
||||
:param permission: Requested :term:`permission`. :class:`str` or
|
||||
:class:`enum.Enum` object.
|
||||
|
||||
:param context: additional object may be passed into
|
||||
:meth:`AbstractAuthorizationPolicy.permission`
|
||||
|
@ -92,7 +141,8 @@ Public API functions
|
|||
|
||||
Checks if user is anonymous user.
|
||||
|
||||
Return ``True`` if user is not remembered in request, otherwise returns ``False``.
|
||||
Return ``True`` if user is not remembered in request, otherwise
|
||||
returns ``False``.
|
||||
|
||||
:param request: :class:`aiohttp.web.Request` object.
|
||||
|
||||
|
@ -103,29 +153,27 @@ Public API functions
|
|||
|
||||
Raises :class:`aiohttp.web.HTTPUnauthorized` if user is not authorized.
|
||||
|
||||
.. deprecated:: 0.3
|
||||
|
||||
Use :func:`check_authorized` async function.
|
||||
|
||||
|
||||
.. decorator:: has_permission(permission)
|
||||
|
||||
Decorator for handlers that checks if user is authorized
|
||||
and has correct permission.
|
||||
|
||||
Raises :class:`aiohttp.web.HTTPUnauthorized` if user is not authorized.
|
||||
Raises :class:`aiohttp.web.HTTPForbidden` if user is authorized but has no access rights.
|
||||
Raises :class:`aiohttp.web.HTTPUnauthorized` if user is not
|
||||
authorized.
|
||||
|
||||
Raises :class:`aiohttp.web.HTTPForbidden` if user is
|
||||
authorized but has no access rights.
|
||||
|
||||
:param str permission: requested :term:`permission`.
|
||||
|
||||
.. deprecated:: 0.3
|
||||
|
||||
.. function:: setup(app, identity_policy, autz_policy)
|
||||
|
||||
Setup :mod:`aiohttp` application with security policies.
|
||||
|
||||
:param app: aiohttp :class:`aiohttp.web.Application` instance.
|
||||
|
||||
:param identity_policy: indentification policy, an
|
||||
:class:`AbstractIdentityPolicy` instance.
|
||||
|
||||
:param autz_policy: authorization policy, an
|
||||
:class:`AbstractAuthorizationPolicy` instance.
|
||||
Use :func:`check_authorized` async function.
|
||||
|
||||
|
||||
Abstract policies
|
||||
|
|
110
docs/usage.rst
110
docs/usage.rst
|
@ -11,22 +11,30 @@
|
|||
|
||||
First of all, what is *aiohttp_security* about?
|
||||
|
||||
*aiohttp_security* is a set of public API functions as well as a reference standard for implementation details for securing access to assets served by a wsgi server.
|
||||
Assets are secured using authentication and authorization as explained below. *aiohttp_security* is part of the *aio_libs* project which takes advantage of asynchronous
|
||||
processing using Python's asyncio library.
|
||||
*aiohttp-security* is a set of public API functions as well as a
|
||||
reference standard for implementation details for securing access to
|
||||
assets served by a wsgi server.
|
||||
|
||||
Assets are secured using authentication and authorization as explained
|
||||
below. *aiohttp-security* is part of the
|
||||
`aio-libs <https://github.com/aio-libs>`_ project which takes advantage
|
||||
of asynchronous processing using Python's asyncio library.
|
||||
|
||||
|
||||
Public API
|
||||
==========
|
||||
|
||||
The API is agnostic to the low level implementation details such that all client code only needs to implement the endpoints as provided by the API (instead of calling policy
|
||||
code directly (see explanation below)).
|
||||
The API is agnostic to the low level implementation details such that
|
||||
all client code only needs to implement the endpoints as provided by
|
||||
the API (instead of calling policy code directly (see explanation
|
||||
below)).
|
||||
|
||||
Via the API an application can:
|
||||
|
||||
(i) remember a user in a local session (:func:`remember`),
|
||||
(ii) forget a user in a local session (:func:`forget`),
|
||||
(iii) retrieve the :term:`userid` (:func:`authorized_userid`) of a remembered user from an :term:`identity` (discussed below), and
|
||||
(iii) retrieve the :term:`userid` (:func:`authorized_userid`) of a
|
||||
remembered user from an :term:`identity` (discussed below), and
|
||||
(iv) check the :term:`permission` of a remembered user (:func:`permits`).
|
||||
|
||||
The library internals are built on top of two concepts:
|
||||
|
@ -34,52 +42,100 @@ The library internals are built on top of two concepts:
|
|||
1) :term:`authentication`, and
|
||||
2) :term:`authorization`.
|
||||
|
||||
There are abstract base classes for both types as well as several pre-built implementations
|
||||
that are shipped with the library. However, the end user is free to build their own implementations.
|
||||
The library comes with two pre-built identity policies; one that uses cookies, and one that uses sessions [#f1]_.
|
||||
It is envisioned that in most use cases developers will use one of the provided identity policies (Cookie or Session) and
|
||||
implement their own authorization policy.
|
||||
There are abstract base classes for both types as well as several
|
||||
pre-built implementations that are shipped with the library. However,
|
||||
the end user is free to build their own implementations.
|
||||
|
||||
The library comes with two pre-built identity policies; one that uses
|
||||
cookies, and one that uses sessions [#f1]_. It is envisioned that in
|
||||
most use cases developers will use one of the provided identity
|
||||
policies (Cookie or Session) and implement their own authorization
|
||||
policy.
|
||||
|
||||
The workflow is as follows:
|
||||
|
||||
1) User is authenticated. This has to be implemented by the developer.
|
||||
2) Once user is authenticated an identity string has to be created for that user. This has to be implemented by the developer.
|
||||
3) The identity string is passed to the Identity Policy's remember method and the user is now remembered (Cookie or Session if using built-in). *Only once a user is remembered can the other API methods:* :func:`permits`, :func:`forget`, *and* :func:`authorized_userid` *be invoked* .
|
||||
4) If the user tries to access a restricted asset the :func:`permits` method is called. Usually assets are protected using the **@aiohttp_security.has_permission(**\ *permission*\ **)** decorator. This should return True if permission is granted.
|
||||
2) Once user is authenticated an identity string has to be created for
|
||||
that user. This has to be implemented by the developer.
|
||||
3) The identity string is passed to the Identity Policy's remember
|
||||
method and the user is now remembered (Cookie or Session if using
|
||||
built-in). *Only once a user is remembered can the other API
|
||||
methods:* :func:`permits`, :func:`forget`, *and*
|
||||
:func:`authorized_userid` *be invoked* .
|
||||
4) If the user tries to access a restricted asset the :func:`permits`
|
||||
method is called. Usually assets are protected using the
|
||||
:func:`check_permission` helper. This should return True if
|
||||
permission is granted.
|
||||
|
||||
The :func:`permits` method is implemented by the developer as part of the :class:`AbstractAuthorizationPolicy` and passed to the application at runtime via setup.
|
||||
In addition a :func:`@aiohttp_security.login_required decorator` also exists that requires no permissions (i.e. doesn't call :func:`permits` method) but only requires that the user is remembered (i.e. authenticated/logged in).
|
||||
The :func:`permits` method is implemented by the developer as part of
|
||||
the :class:`AbstractAuthorizationPolicy` and passed to the
|
||||
application at runtime via setup.
|
||||
|
||||
In addition a :func:`check_authorized` also
|
||||
exists that requires no permissions (i.e. doesn't call :func:`permits`
|
||||
method) but only requires that the user is remembered
|
||||
(i.e. authenticated/logged in).
|
||||
|
||||
|
||||
|
||||
|
||||
Authentication
|
||||
==============
|
||||
Authentication is the process where a user's identity is verified. It confirms who the user is. This is traditionally done using a user name and password (note: this is not the only way).
|
||||
A authenticated user has no access rights, rather an authenticated user merely confirms that the user exists and that the user is who they say they are.
|
||||
|
||||
In *aiohttp_security* the developer is responsible for their own authentication mechanism. *aiohttp_security* only requires that the authentication result in a identity string which
|
||||
corresponds to a user's id in the underlying system.
|
||||
Authentication is the process where a user's identity is verified. It
|
||||
confirms who the user is. This is traditionally done using a user name
|
||||
and password (note: this is not the only way).
|
||||
|
||||
*Note:* :term:`identity` is a string that is shared between the browser and the server. Therefore it is recommended that a random string such as a uuid or hash is used rather than things like a database primary key, user login/email, etc.
|
||||
A authenticated user has no access rights, rather an authenticated
|
||||
user merely confirms that the user exists and that the user is who
|
||||
they say they are.
|
||||
|
||||
In *aiohttp_security* the developer is responsible for their own
|
||||
authentication mechanism. *aiohttp_security* only requires that the
|
||||
authentication result in a identity string which corresponds to a
|
||||
user's id in the underlying system.
|
||||
|
||||
.. note::
|
||||
|
||||
:term:`identity` is a string that is shared between the browser and
|
||||
the server. Therefore it is recommended that a random string
|
||||
such as a uuid or hash is used rather than things like a
|
||||
database primary key, user login/email, etc.
|
||||
|
||||
Identity Policy
|
||||
==============
|
||||
===============
|
||||
|
||||
Once a user is authenticated the *aiohttp_security* API is invoked for storing, retrieving, and removing a user's :term:`identity`. This is accommplished via AbstractIdentityPolicy's :func:`remember`, :func:`identify`, and :func:`forget` methods. The Identity Policy is therefore the mechanism by which a authenticated user is persisted in the system.
|
||||
Once a user is authenticated the *aiohttp_security* API is invoked for
|
||||
storing, retrieving, and removing a user's :term:`identity`. This is
|
||||
accommplished via AbstractIdentityPolicy's :func:`remember`,
|
||||
:func:`identify`, and :func:`forget` methods. The Identity Policy is
|
||||
therefore the mechanism by which a authenticated user is persisted in
|
||||
the system.
|
||||
|
||||
*aiohttp_security* has two built in identity policy's for this purpose. :term:`CookiesIdentityPolicy` that uses cookies and :term:`SessionIdentityPolicy` that uses sessions via :term:`aiohttp.session` library.
|
||||
*aiohttp_security* has two built in identity policy's for this
|
||||
purpose. :class:`CookiesIdentityPolicy` that uses cookies and
|
||||
:class:`SessionIdentityPolicy` that uses sessions via
|
||||
``aiohttp-session`` library.
|
||||
|
||||
Authorization
|
||||
==============
|
||||
|
||||
Once a user is authenticated (see above) it means that the user has an :term:`identity`. This :term:`identity` can now be used for checking access rights or :term:`permission` using a :term:`authorization` policy.
|
||||
Once a user is authenticated (see above) it means that the user has an
|
||||
:term:`identity`. This :term:`identity` can now be used for checking
|
||||
access rights or :term:`permission` using a :term:`authorization`
|
||||
policy.
|
||||
|
||||
The authorization policy's :func:`permits()` method is used for this purpose.
|
||||
|
||||
|
||||
When :class:`aiohttp.web.Request` has an :term:`identity` it means the user has been authenticated and therefore has an :term:`identity` that can be checked by the :term:`authorization` policy.
|
||||
When :class:`aiohttp.web.Request` has an :term:`identity` it means the
|
||||
user has been authenticated and therefore has an :term:`identity` that
|
||||
can be checked by the :term:`authorization` policy.
|
||||
|
||||
As noted above, :term:`identity` is a string that is shared between the browser and the server. Therefore it is recommended that a random string such as a uuid or hash is used rather than things like a database primary key, user login/email, etc.
|
||||
As noted above, :term:`identity` is a string that is shared between
|
||||
the browser and the server. Therefore it is recommended that a
|
||||
random string such as a uuid or hash is used rather than things like
|
||||
a database primary key, user login/email, etc.
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
-e .
|
||||
flake8==3.5.0
|
||||
async-timeout==3.0
|
||||
pytest==3.7.4
|
||||
pytest-cov==2.5.1
|
||||
pytest-mock==1.10.0
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[tool:pytest]
|
||||
testpaths = tests
|
||||
filterwarnings=
|
||||
error
|
2
setup.py
2
setup.py
|
@ -27,7 +27,7 @@ def read(f):
|
|||
return open(os.path.join(os.path.dirname(__file__), f)).read().strip()
|
||||
|
||||
|
||||
install_requires = ['aiohttp>=0.18']
|
||||
install_requires = ['aiohttp>=3.0.0']
|
||||
tests_require = install_requires + ['pytest']
|
||||
extras_require = {'session': 'aiohttp-session'}
|
||||
|
||||
|
|
|
@ -15,23 +15,23 @@ class Autz(AbstractAuthorizationPolicy):
|
|||
pass
|
||||
|
||||
|
||||
async def test_remember(loop, test_client):
|
||||
async def test_remember(loop, aiohttp_client):
|
||||
|
||||
async def handler(request):
|
||||
response = web.Response()
|
||||
await remember(request, response, 'Andrew')
|
||||
return response
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', handler)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert 200 == resp.status
|
||||
assert 'Andrew' == resp.cookies['AIOHTTP_SECURITY'].value
|
||||
|
||||
|
||||
async def test_identify(loop, test_client):
|
||||
async def test_identify(loop, aiohttp_client):
|
||||
|
||||
async def create(request):
|
||||
response = web.Response()
|
||||
|
@ -44,11 +44,11 @@ async def test_identify(loop, test_client):
|
|||
assert 'Andrew' == user_id
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', check)
|
||||
app.router.add_route('POST', '/', create)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.post('/')
|
||||
assert 200 == resp.status
|
||||
await resp.release()
|
||||
|
@ -56,7 +56,7 @@ async def test_identify(loop, test_client):
|
|||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_forget(loop, test_client):
|
||||
async def test_forget(loop, aiohttp_client):
|
||||
|
||||
async def index(request):
|
||||
return web.Response()
|
||||
|
@ -64,19 +64,19 @@ async def test_forget(loop, test_client):
|
|||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'Andrew')
|
||||
return response
|
||||
raise response
|
||||
|
||||
async def logout(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await forget(request, response)
|
||||
return response
|
||||
raise response
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', index)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
app.router.add_route('POST', '/logout', logout)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.post('/login')
|
||||
assert 200 == resp.status
|
||||
assert str(resp.url).endswith('/')
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import enum
|
||||
import pytest
|
||||
|
||||
from aiohttp import web
|
||||
from aiohttp_security import setup as _setup
|
||||
from aiohttp_security import (AbstractAuthorizationPolicy, authorized_userid,
|
||||
forget, has_permission, is_anonymous,
|
||||
login_required, permits, remember)
|
||||
login_required, permits, remember,
|
||||
check_authorized, check_permission)
|
||||
from aiohttp_security.cookies_identity import CookiesIdentityPolicy
|
||||
|
||||
|
||||
|
@ -23,23 +25,23 @@ class Autz(AbstractAuthorizationPolicy):
|
|||
return None
|
||||
|
||||
|
||||
async def test_authorized_userid(loop, test_client):
|
||||
async def test_authorized_userid(loop, aiohttp_client):
|
||||
|
||||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'UserID')
|
||||
return response
|
||||
raise response
|
||||
|
||||
async def check(request):
|
||||
userid = await authorized_userid(request)
|
||||
assert 'Andrew' == userid
|
||||
return web.Response(text=userid)
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', check)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
resp = await client.post('/login')
|
||||
assert 200 == resp.status
|
||||
|
@ -47,23 +49,23 @@ async def test_authorized_userid(loop, test_client):
|
|||
assert 'Andrew' == txt
|
||||
|
||||
|
||||
async def test_authorized_userid_not_authorized(loop, test_client):
|
||||
async def test_authorized_userid_not_authorized(loop, aiohttp_client):
|
||||
|
||||
async def check(request):
|
||||
userid = await authorized_userid(request)
|
||||
assert userid is None
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', check)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
resp = await client.get('/')
|
||||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_permits_enum_permission(loop, test_client):
|
||||
async def test_permits_enum_permission(loop, aiohttp_client):
|
||||
class Permission(enum.Enum):
|
||||
READ = '101'
|
||||
WRITE = '102'
|
||||
|
@ -86,7 +88,7 @@ async def test_permits_enum_permission(loop, test_client):
|
|||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'UserID')
|
||||
return response
|
||||
raise response
|
||||
|
||||
async def check(request):
|
||||
ret = await permits(request, Permission.READ)
|
||||
|
@ -97,16 +99,16 @@ async def test_permits_enum_permission(loop, test_client):
|
|||
assert not ret
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', check)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.post('/login')
|
||||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_permits_unauthorized(loop, test_client):
|
||||
async def test_permits_unauthorized(loop, aiohttp_client):
|
||||
|
||||
async def check(request):
|
||||
ret = await permits(request, 'read')
|
||||
|
@ -117,38 +119,38 @@ async def test_permits_unauthorized(loop, test_client):
|
|||
assert not ret
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', check)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_is_anonymous(loop, test_client):
|
||||
async def test_is_anonymous(loop, aiohttp_client):
|
||||
|
||||
async def index(request):
|
||||
is_anon = await is_anonymous(request)
|
||||
if is_anon:
|
||||
return web.HTTPUnauthorized()
|
||||
return web.HTTPOk()
|
||||
raise web.HTTPUnauthorized()
|
||||
return web.Response()
|
||||
|
||||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'UserID')
|
||||
return response
|
||||
raise response
|
||||
|
||||
async def logout(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await forget(request, response)
|
||||
return response
|
||||
raise response
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', index)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
app.router.add_route('POST', '/logout', logout)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
|
@ -161,27 +163,30 @@ async def test_is_anonymous(loop, test_client):
|
|||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
|
||||
async def test_login_required(loop, test_client):
|
||||
async def test_login_required(loop, aiohttp_client):
|
||||
with pytest.raises(DeprecationWarning):
|
||||
|
||||
@login_required
|
||||
async def index(request):
|
||||
return web.HTTPOk()
|
||||
return web.Response()
|
||||
|
||||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'UserID')
|
||||
return response
|
||||
raise response
|
||||
|
||||
async def logout(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await forget(request, response)
|
||||
return response
|
||||
raise response
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', index)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
app.router.add_route('POST', '/logout', logout)
|
||||
client = await test_client(app)
|
||||
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
|
@ -194,19 +199,54 @@ async def test_login_required(loop, test_client):
|
|||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
|
||||
async def test_has_permission(loop, test_client):
|
||||
async def test_check_authorized(loop, aiohttp_client):
|
||||
async def index(request):
|
||||
await check_authorized(request)
|
||||
return web.Response()
|
||||
|
||||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'UserID')
|
||||
raise response
|
||||
|
||||
async def logout(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await forget(request, response)
|
||||
raise response
|
||||
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/', index)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
app.router.add_route('POST', '/logout', logout)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
await client.post('/login')
|
||||
resp = await client.get('/')
|
||||
assert web.HTTPOk.status_code == resp.status
|
||||
|
||||
await client.post('/logout')
|
||||
resp = await client.get('/')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
|
||||
async def test_has_permission(loop, aiohttp_client):
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
|
||||
@has_permission('read')
|
||||
async def index_read(request):
|
||||
return web.HTTPOk()
|
||||
return web.Response()
|
||||
|
||||
@has_permission('write')
|
||||
async def index_write(request):
|
||||
return web.HTTPOk()
|
||||
return web.Response()
|
||||
|
||||
@has_permission('forbid')
|
||||
async def index_forbid(request):
|
||||
return web.HTTPOk()
|
||||
return web.Response()
|
||||
|
||||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
|
@ -216,16 +256,73 @@ async def test_has_permission(loop, test_client):
|
|||
async def logout(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await forget(request, response)
|
||||
return response
|
||||
raise response
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/permission/read', index_read)
|
||||
app.router.add_route('GET', '/permission/write', index_write)
|
||||
app.router.add_route('GET', '/permission/forbid', index_forbid)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
app.router.add_route('POST', '/logout', logout)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
resp = await client.get('/permission/read')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
resp = await client.get('/permission/write')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
resp = await client.get('/permission/forbid')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
await client.post('/login')
|
||||
resp = await client.get('/permission/read')
|
||||
assert web.HTTPOk.status_code == resp.status
|
||||
resp = await client.get('/permission/write')
|
||||
assert web.HTTPOk.status_code == resp.status
|
||||
resp = await client.get('/permission/forbid')
|
||||
assert web.HTTPForbidden.status_code == resp.status
|
||||
|
||||
await client.post('/logout')
|
||||
resp = await client.get('/permission/read')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
resp = await client.get('/permission/write')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
resp = await client.get('/permission/forbid')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
||||
|
||||
async def test_check_permission(loop, aiohttp_client):
|
||||
|
||||
async def index_read(request):
|
||||
await check_permission(request, 'read')
|
||||
return web.Response()
|
||||
|
||||
async def index_write(request):
|
||||
await check_permission(request, 'write')
|
||||
return web.Response()
|
||||
|
||||
async def index_forbid(request):
|
||||
await check_permission(request, 'forbid')
|
||||
return web.Response()
|
||||
|
||||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'UserID')
|
||||
raise response
|
||||
|
||||
async def logout(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await forget(request, response)
|
||||
raise response
|
||||
|
||||
app = web.Application()
|
||||
_setup(app, CookiesIdentityPolicy(), Autz())
|
||||
app.router.add_route('GET', '/permission/read', index_read)
|
||||
app.router.add_route('GET', '/permission/write', index_write)
|
||||
app.router.add_route('GET', '/permission/forbid', index_forbid)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
app.router.add_route('POST', '/logout', logout)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
resp = await client.get('/permission/read')
|
||||
assert web.HTTPUnauthorized.status_code == resp.status
|
||||
|
|
|
@ -35,7 +35,7 @@ async def test_no_pyjwt_installed(mocker):
|
|||
JWTIdentityPolicy('secret')
|
||||
|
||||
|
||||
async def test_identify(loop, make_token, test_client):
|
||||
async def test_identify(loop, make_token, aiohttp_client):
|
||||
kwt_secret_key = 'Key'
|
||||
|
||||
token = make_token({'login': 'Andrew'}, kwt_secret_key)
|
||||
|
@ -46,17 +46,17 @@ async def test_identify(loop, make_token, test_client):
|
|||
assert 'Andrew' == identity['login']
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, JWTIdentityPolicy(kwt_secret_key), Autz())
|
||||
app.router.add_route('GET', '/', check)
|
||||
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
headers = {'Authorization': 'Bearer {}'.format(token.decode('utf-8'))}
|
||||
resp = await client.get('/', headers=headers)
|
||||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_identify_broken_scheme(loop, make_token, test_client):
|
||||
async def test_identify_broken_scheme(loop, make_token, aiohttp_client):
|
||||
kwt_secret_key = 'Key'
|
||||
|
||||
token = make_token({'login': 'Andrew'}, kwt_secret_key)
|
||||
|
@ -71,11 +71,11 @@ async def test_identify_broken_scheme(loop, make_token, test_client):
|
|||
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
_setup(app, JWTIdentityPolicy(kwt_secret_key), Autz())
|
||||
app.router.add_route('GET', '/', check)
|
||||
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
headers = {'Authorization': 'Token {}'.format(token.decode('utf-8'))}
|
||||
resp = await client.get('/', headers=headers)
|
||||
assert 400 == resp.status
|
||||
|
|
|
@ -2,21 +2,21 @@ from aiohttp import web
|
|||
from aiohttp_security import authorized_userid, permits
|
||||
|
||||
|
||||
async def test_authorized_userid(loop, test_client):
|
||||
async def test_authorized_userid(loop, aiohttp_client):
|
||||
|
||||
async def check(request):
|
||||
userid = await authorized_userid(request)
|
||||
assert userid is None
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
app.router.add_route('GET', '/', check)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_permits(loop, test_client):
|
||||
async def test_permits(loop, aiohttp_client):
|
||||
|
||||
async def check(request):
|
||||
ret = await permits(request, 'read')
|
||||
|
@ -27,8 +27,8 @@ async def test_permits(loop, test_client):
|
|||
assert ret
|
||||
return web.Response()
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
app.router.add_route('GET', '/', check)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert 200 == resp.status
|
||||
|
|
|
@ -2,15 +2,15 @@ from aiohttp import web
|
|||
from aiohttp_security import remember, forget
|
||||
|
||||
|
||||
async def test_remember(loop, test_client):
|
||||
async def test_remember(loop, aiohttp_client):
|
||||
|
||||
async def do_remember(request):
|
||||
response = web.Response()
|
||||
await remember(request, response, 'Andrew')
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
app.router.add_route('POST', '/', do_remember)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.post('/')
|
||||
assert 500 == resp.status
|
||||
assert (('Security subsystem is not initialized, '
|
||||
|
@ -18,15 +18,15 @@ async def test_remember(loop, test_client):
|
|||
resp.reason)
|
||||
|
||||
|
||||
async def test_forget(loop, test_client):
|
||||
async def test_forget(loop, aiohttp_client):
|
||||
|
||||
async def do_forget(request):
|
||||
response = web.Response()
|
||||
await forget(request, response)
|
||||
|
||||
app = web.Application(loop=loop)
|
||||
app = web.Application()
|
||||
app.router.add_route('POST', '/', do_forget)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.post('/')
|
||||
assert 500 == resp.status
|
||||
assert (('Security subsystem is not initialized, '
|
||||
|
|
|
@ -20,14 +20,14 @@ class Autz(AbstractAuthorizationPolicy):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def make_app(loop):
|
||||
app = web.Application(loop=loop)
|
||||
def make_app():
|
||||
app = web.Application()
|
||||
setup_session(app, SimpleCookieStorage())
|
||||
setup_security(app, SessionIdentityPolicy(), Autz())
|
||||
return app
|
||||
|
||||
|
||||
async def test_remember(make_app, test_client):
|
||||
async def test_remember(make_app, aiohttp_client):
|
||||
|
||||
async def handler(request):
|
||||
response = web.Response()
|
||||
|
@ -37,12 +37,12 @@ async def test_remember(make_app, test_client):
|
|||
async def check(request):
|
||||
session = await get_session(request)
|
||||
assert session['AIOHTTP_SECURITY'] == 'Andrew'
|
||||
return web.HTTPOk()
|
||||
return web.Response()
|
||||
|
||||
app = make_app()
|
||||
app.router.add_route('GET', '/', handler)
|
||||
app.router.add_route('GET', '/check', check)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get('/')
|
||||
assert 200 == resp.status
|
||||
|
||||
|
@ -50,7 +50,7 @@ async def test_remember(make_app, test_client):
|
|||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_identify(make_app, test_client):
|
||||
async def test_identify(make_app, aiohttp_client):
|
||||
|
||||
async def create(request):
|
||||
response = web.Response()
|
||||
|
@ -66,7 +66,7 @@ async def test_identify(make_app, test_client):
|
|||
app = make_app()
|
||||
app.router.add_route('GET', '/', check)
|
||||
app.router.add_route('POST', '/', create)
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.post('/')
|
||||
assert 200 == resp.status
|
||||
|
||||
|
@ -74,28 +74,28 @@ async def test_identify(make_app, test_client):
|
|||
assert 200 == resp.status
|
||||
|
||||
|
||||
async def test_forget(make_app, test_client):
|
||||
async def test_forget(make_app, aiohttp_client):
|
||||
|
||||
async def index(request):
|
||||
session = await get_session(request)
|
||||
return web.HTTPOk(text=session.get('AIOHTTP_SECURITY', ''))
|
||||
return web.Response(text=session.get('AIOHTTP_SECURITY', ''))
|
||||
|
||||
async def login(request):
|
||||
response = web.HTTPFound(location='/')
|
||||
await remember(request, response, 'Andrew')
|
||||
return response
|
||||
raise response
|
||||
|
||||
async def logout(request):
|
||||
response = web.HTTPFound('/')
|
||||
await forget(request, response)
|
||||
return response
|
||||
raise response
|
||||
|
||||
app = make_app()
|
||||
app.router.add_route('GET', '/', index)
|
||||
app.router.add_route('POST', '/login', login)
|
||||
app.router.add_route('POST', '/logout', logout)
|
||||
|
||||
client = await test_client(app)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
resp = await client.post('/login')
|
||||
assert 200 == resp.status
|
||||
|
|
Loading…
Reference in New Issue