Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Deniallugo committed Sep 6, 2018
1 parent 8e1943f commit 68098b9
Show file tree
Hide file tree
Showing 27 changed files with 474 additions and 223 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ docs/_build/
# PyBuilder
target/

coverage
coverage
.pytest_cache

#IDE stuff
.idea/*
.vscode/*
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ env:

deploy:
provider: pypi
user: andrew.svetlov
user: aio-libs-bot
password:
secure: "JdBvuOBA/198ognVDOY/qZpIKGXfCx47725kyJo/SpQ3nP+x0GLZb3PMQkR0jfSWWkx6Sisk3vOCYsoWclPyPzp+o4ZpfM8yAjHNFmtbr+k+XJdUEApEiWb6/Y3g7DCyY2Qa/L8IYlyABPWrrJI/nld2sKm5kmhFpR/z3HfeFtINP6Ivp34dUOkeRP6kOvCi9d6GyWnvTRnhlybAnk/Ngrroh8XrbKHdDv0zkQkshF8+pmxVzwao4C6S5ld5cFXIYZHLBA9lNC3zgvOMuFeGPUEN9vab3q77MvaiMIuTC9QjcgIhfw3gabH2u7knqfFzqqzXMaVptx5z8o1JtsxMyYt5NVBqS4NPIljpZjaoS/CASHJlRxniJiYfjvjOtFEcfGMNtZj8ZYsGR0nuP2jwzgpEHHWIs4qL0Y8h9t7pGirxCuQcnY10sr+Y+JKaZNJsugNLgbqE2aaZUye5gjDcEj9WY8kKNZXucLP7c0McJuwPqplDEO4CQouMttcKSYkA0QoETmpAFqaXCaMs3p/glOoU2ZyHSH9mXWir69yo84ymb2NlGPMTAstXlv/g/oLmLMSq7lbl6cSUnO1/wxBGlyfv5AAq/75YUaqsgYofzN5CjUgA3m6NedvbWxLUJaxVQ7nduYGEQKDvGEBmzCNv6CdVRCjQ9J1xX3XzkVheQGc="
secure: "fHbpT6AuRM+K6hg0nWT7ov/qJAAFJ7N5Mot1Z02QVHv9+XXJsqSmzJHMyv4FNWSO+IG9ulJ/wrVpQ/wr5S1FiCVJYpMFJP/71fqT7MbrgUg+ovbGrs1AfJHHQtVc91Az9Yl2nP+wzJCplJIuxO8IVjKHS87QxupzMHapo97ItYM6yvCNzIP+3JjvZyl5/ocqdUpl4KiS/tzXbiaBSlVgmI/013EbD5U36wcz2AAszTcBzKTDJh0BF4wr4brHnVPKr4gRSZPZRYduZ7WXh0rJt/aGyNGm9siYkKhuE+pzd/6vIbN3keKEhAjafCl4+Z3a0eL0ACyt8CBCBHf9/n4KYm+KPwLe3NYWKkO6qCJpZ+bMNfQInKiEoWJx9KDaKjdCVivlKY+abaJiF/thO4udunn3PfPz2O8MlkZRoTVqASN1sP60cULTlxfLi8x0RVqMKIHejNQi/AN8/4poCPFfFOOia/WQqq1pD45vJh8pNxsc6IEAjhHUgvMDnK0DBkEs4i2catZKc2YPEjgAkvplvTE4tH8Tzyj5EvMwM56h2zfByeKs9ojkvzyhPLhWq7d8JTPWPAyj72FsrpGm12cLU/E9g9KKj6Hg5E3F0+V2Zs7wXc+fT1ovC5/NRL2WpT2+k1wF/9Q7ZrbQ9InunHXYU7GJCFzRE8XlcRsBGPcR3Ls="
distributions: "sdist bdist_wheel"
on:
tags: true
all_branches: true
python: 3.6
condition: $PYTHONASYNCIODEBUG = ""
15 changes: 14 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
@@ -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)
------------------
Expand Down
8 changes: 4 additions & 4 deletions aiohttp_security/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from .abc import (AbstractAuthorizationPolicy, AbstractIdentityPolicy,
AbstractAuthenticationPolicy)
from .api import (authorized_userid, forget, has_permission, is_anonymous,
login_required, permits, remember, setup, provide_user,
authenticate_user)
login_required, permits, remember,
authenticate_user, setup, check_authorized, check_permission)
from .cookies_identity import CookiesIdentityPolicy
from .session_identity import SessionIdentityPolicy
from .jwt_identity import JWTIdentityPolicy

__version__ = '0.2.1'
__version__ = '0.3.0'

__all__ = ('AbstractIdentityPolicy', 'AbstractAuthorizationPolicy',
'AbstractAuthenticationPolicy',
Expand All @@ -17,4 +17,4 @@
'authenticate_user',
'permits', 'setup', 'is_anonymous',
'login_required', 'has_permission',
'provide_user')
'check_authorized', 'check_permission')
13 changes: 13 additions & 0 deletions aiohttp_security/abc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import abc


# see http://plope.com/pyramid_auth_design_api_postmortem


Expand Down Expand Up @@ -48,3 +49,15 @@ async def authorized_userid(self, identity):
or 'None' if no user exists related to the identity.
"""
pass


class AbstractAuthenticationPolicy(metaclass=abc.ABCMeta):

@abc.abstractmethod
async def authenticate_user(self, context, credentials):
"""Retrieve authorized user.
Return the user by the user authentication credentials
or 'None' if no user exists related to provided credentials.
"""
pass
73 changes: 38 additions & 35 deletions aiohttp_security/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import enum
import warnings
from aiohttp import web
from aiohttp_security.abc import (AbstractIdentityPolicy,
AbstractAuthorizationPolicy,
Expand Down Expand Up @@ -61,13 +62,19 @@ async def authorized_userid(request):


async def authenticate_user(credentials, context):
app = context.get('app')
"""Authenticate user by credentials
:param credentials: Authenticate credentials for AuthenticationPolicy
:param context: context can be dict or object, you can send request like context
:return: User
"""
app = context.get('app') or getattr(context, 'app', None)
if app is None:
raise Exception('No app in context')
authenticate_policy = app.get(AUTH_KEY)
if authenticate_policy is None:
return None
user = await authenticate_policy.authentificate_user(credentials, context)
user = await authenticate_policy.authenticate_user(credentials, context)
return user


Expand Down Expand Up @@ -99,6 +106,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.
Expand All @@ -118,43 +134,34 @@ async def wrapped(*args, **kwargs):
"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, user=userid)
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


def provide_user(fn):
"""Decorator that add user to function with request
async def check_permission(request, permission, context=None):
"""Checker that passes only to authoraised users with given permission.
Decorator add extra argument "user"
If user is not authorized - raises HTTPUnauthorized,
if user is authorized and does not have permission -
raises HTTPForbidden.
"""

@wraps(fn)
async def wrapped(*args, **kwargs):
request = args[-1]
if not isinstance(request, web.BaseRequest):
msg = ("Incorrect decorator usage. "
"Expecting `def handler(request)` "
"or `def handler(self, request)`.")
raise RuntimeError(msg)

userid = await authorized_userid(request)
ret = await fn(*args, **kwargs, user=userid)
return ret

return wrapped
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,
Expand All @@ -174,18 +181,14 @@ async def wrapped(*args, **kwargs):
"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, user=userid)
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


Expand Down
2 changes: 1 addition & 1 deletion aiohttp_security/jwt_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async def identify(self, request):

identity = jwt.decode(token,
self.secret,
algorithm=self.algorithm)
algorithms=[self.algorithm])
return identity

async def remember(self, *args, **kwargs): # pragma: no cover
Expand Down
1 change: 1 addition & 0 deletions aiohttp_security/session_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

try:
from aiohttp_session import get_session

HAS_AIOHTTP_SESSION = True
except ImportError: # pragma: no cover
HAS_AIOHTTP_SESSION = False
Expand Down
12 changes: 6 additions & 6 deletions demo/database_auth/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from aiohttp_security import (
remember, forget, authorized_userid,
has_permission, login_required,
check_permission, check_authorized,
)

from .db_auth import check_credentials
Expand Down Expand Up @@ -45,25 +45,25 @@ async def login(self, request):
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

Expand Down
2 changes: 1 addition & 1 deletion demo/database_auth/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 4 additions & 6 deletions demo/dictionary_auth/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from aiohttp_security import (
remember, forget, authorized_userid,
has_permission, login_required,
check_permission, check_authorized,
)

from .authz import check_credentials
Expand Down Expand Up @@ -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',
Expand All @@ -65,19 +65,17 @@ 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',
)
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',
Expand Down
6 changes: 3 additions & 3 deletions demo/simple_example_auth.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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!")


Expand Down
Loading

0 comments on commit 68098b9

Please sign in to comment.