aio-libs / aiohttp-security Goto Github PK
View Code? Open in Web Editor NEWauth and permissions for aiohttp
License: Apache License 2.0
auth and permissions for aiohttp
License: Apache License 2.0
You have different versions of this package in PyPi and git with the same number of version
Hello. Simple question about your package: is his production-ready?
I mean, I would like to start new project with aiohttp, but aiohttp-security is official aio-libs package and hasn't basic examples and google results to solve newbie questions (I'm newbie on aiohttp, using Django all time). And your package looks very good, have more useful examples than aiohttp-security ๐
I am currently using aiohttp_security for our new server.
I think it would be nice to include decorator in aiohttp_security api.
This is method signature I currently have.
@aiohttp_security.authorize(required=True, redirect_url=None, permission=None)
def handler(request):
return web.Response(body=b'OK')
Can I create pull request?
Problem
The API functions don't support static typing well. For example:
context
parameter must be typed as Any
and default to None
to be compatible with the API. However, some implementations may require a specific type for context
and won't accept a default (e.g. aiohttp-admin). There's no way to validate this with a type checker.Solution
I'm thinking the best approach will be to remove the API functions (everything in api.py), and then use the IdentityPolicy and AuthorizationPolicy classes directly.
A few tweaks will need to be made, but this should make it much easier to subclass and implement these with precise types.
Most of these functions are just calling the methods anyway, so there's not really any extra complexity to just call the methods directly.
Hello,
Clearing the current session (e.g. on logout) is possible using forget
. Is there also a way to clear all the other sessions of the same user/identity at the same time? (e.g. logging out a user in all browsers at once)
Thanks,
Victor
Can you explain me something?
Method SessionIdentityPolicy.remember() has **kwargs as one of the parameters, but never use it to write data into user session dict. What's the point of this param?
For now in my project I have my own SessionPolicy implementation inherited from SessionIdentityPolicy, just in order to have a posibility of storing extended data in session (like different datetimes, uuids, IPs etc.).
So, my question is: was Session Policy designed to be self implemented by users or am I wrong and there is special way to write extra data into session?
I tried running demo/simple_example_auth.py
(without any changes). I accessed it through my web-browser, when I clicked Log_me_in
it wouldn't login.
I still get the same output even after clicking login several times:
Hello, I'm Jack, I'm NOT logged in.
Log me in
Log me out
Check my permissions, when i'm logged in and logged out.
Can I listen?
Can I speak?
Which also makes /listen
an unauthorized page.
Hi,
I would like to use aiohttp in a client for accessing a digest authenticated server. Do you have any suggestions on how i accomplish that? It's a requirement for us to use the Python 3.5 asyncio package.
We'd prefer if we were not forced to write the Digest implementation ourselves. If there is no other way, then we'd consider writing our own digest auth client.
Any and all suggestions are welcome.
Hello, friends.
Why response in forget and remember methods? I see it's need in cookies identity, but not for others.
Maybe add kwagrs params or set default none for it in api.py ?
I use session identity and pass response do nothing for me. Just not needed for it
We need to drop references to aioredis and migrate to redis.
The docs say "basic auth example" (or similar), so people looking for HTTP Basic Auth will be confused because it means "basic (auth example)", not "(basic auth) example". (Human language is not associative...)
Alternate terms:
"simple"
"tutorial"
"baseline"
"example"
"basic usage"
I.e. it would be best to avoid the word combination "basic auth" in the docs, even if readers are not meant to parse it that way.
Because aiohttp discourages the use of global singletons and encourages that such values be stored within the application or request themselves, I've got a bit of trouble with the current implementations of AbstractAuthorizationPolicy.permits and AbstractAuthorizationPolicy.authorized_userid. Neither provide access to the request or application scope, which I need because I'm creating/storing within the application, on startup, a connection object to my database. When those functions are called, I need to be able to access my connector in order to validate the user and check their permissions.
I could submit a pull request with changes so that those function's would receive request as their first argument, but that would break compatibility with anyone who has implemented an AbstractAuthorizationPolicy.
Can we discuss this?
Thanks.
Hi,
I'm trying to use aiohttp_security on my website, I'm testing the demo code with a custom Policy (as I don't have sql database)
below the code for my policy :
from aiohttp_session import setup as setup_session
from aiohttp_session.cookie_storage import EncryptedCookieStorage
from aiohttp_security import setup as setup_security
from aiohttp_security import SessionIdentityPolicy
from aiohttp_security.abc import AbstractAuthorizationPolicy
class TestAuthorizationPolicy(AbstractAuthorizationPolicy):
def __init__(self):
pass
def authorized_userid(self, identity):
return identity
def permits(self, identity, permission, context=None):
return True
app = web.Application()
setup_session(app, EncryptedCookieStorage(key))
setup_security(app, SessionIdentityPolicy(), TestAuthorizationPolicy())
def user_role(role):
'''
Decorator that checks if a user has been authenticated and have the good role.
'''
def decorator(func):
async def wrapper(*args, **kwargs):
request = args[1]
if not await permits(request, role):
raise web.HTTPForbidden()
return await func(*args, **kwargs)
return wrapper
return decorator
The stacktrace of the error :
======== Running on http://127.0.0.1:8500/ ========
(Press CTRL+C to quit)
Error handling request
Traceback (most recent call last):
File "/home/olivier/Test/Test/venv/lib/python3.5/site-packages/aiohttp/server.py", line 261, in start
yield from self.handle_request(message, payload)
File "/home/olivier/Test/Test/venv/lib/python3.5/site-packages/aiohttp/web.py", line 88, in handle_request
resp = yield from handler(request)
File "/home/olivier/Test/Test/venv/lib/python3.5/site-packages/aiohttp_session/__init__.py", line 129, in middleware
response = yield from handler(request)
File "/home/olivier/Test/Test/test/web/handlers.py", line 79, in wrapper
if not await permits(request, role):
File "/home/olivier/Test/Test/venv/lib/python3.5/site-packages/aiohttp_security/api.py", line 73, in permits
access = yield from autz_policy.permits(identity, permission, context)
TypeError: 'bool' object is not iterable
It seems that somewhere in your code you try to iterate over the boolean returned by permits method ... but I don't see where ?
Hi,
The SessionIdentityPolicys remember
method calls aiohttp_sessions get_session but the aiohttp_session docs state to always use new_session for logins - is that not what remember
is mostly be used in, login views?
What if more complex cases are needed?
Hello,
can you upload the latest version to Pypi ?
The latest release on github is 0.1.1 (2016-10-22)
The latest release on Pypi is 0.1.0 (2016-01-18).
This simplifies our requirements.txt !
Thank you
F
I try to launch demo/database_auth
example but I get an error:
Traceback (most recent call last):
File "demo/database_auth/main.py", line 9, in <module>
from aioredis import create_pool
ImportError: cannot import name 'create_pool' from 'aioredis' (/home/flow/.local/lib/python3.8/site-packages/aioredis/__init__.py)
I've tried changing create_pool
to create_redis_pool
in the example code, like in aiohttp_demos, but it doesn't help. I've also tried versions 2.0.0 and 2.0.1 of aioredis and there was no use. I've even tried installing aioredis v 1.3.0 but RuntimeError("aioredis<2.0 is not supported")
was raised.
System is Ubuntu 20.04 and python is 3.8.10
One weird moment for me is aioredis that is being installed in /home/flow/.local/lib/python3.8/site-packages/
though other libraries can be found in venv folder, e.g. /home/flow/aiohttp-security-master/venv/lib/python3.8/site-packages
or /home/flow/aiohttp-security-master/venv/lib64/python3.8/site-packages
...
References to aiopg need to be removed and replaced with sqlalchemy 2.
I was trying to run pip install -r requirements-dev.txt
and there is the log:
Obtaining file:///D:/Work/github/aiohttp-security (from -r requirements-dev.txt
(line 1))
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "D:\Work\github\aiohttp-security\setup.py", line 22, in <module>
version = re.findall(r"^__version__ = '([^']+)'$", fp.read(), re.M)[0]
IndexError: list index out of range
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "D:\Work\github\aiohttp-security\setup.py", line 24, in <module>
raise RuntimeError('Unable to determine version.')
RuntimeError: Unable to determine version.
----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in D:\Work\github\ai
ohttp-security\
pip version: 9.0.3
python version: 3.5.0
os: windows 7
Do like this:
REPO_NAME={{ YOUR_REPO_NAME }}
travis encrypt -r "aio-libs/${REPO_NAME}" --api-endpoint 'https://api.travis-ci.com/'
Ref: https://github.com/orgs/aio-libs/teams/admins/discussions/9
In #183 it is said that aiohttp-security is used for server-side only, but can you provide some best-practices for login from a client?
I guess it is best to store the password in the database using pbkdf2_sha256 from passlib.hash. I think I have to trust SSL and send the password in plain because if someone manages to hijack the connection/manages a MITM-attack he can inject all kind of JavaScript that just invalidates every kind of Client-side security I can come up with.
Extend async def check_permission(request, permission, context=None) -> bool
to return back additional information.
Method check_permission()
calls method async def permits(...) -> bool
declared in AuthPolicy
and defined in user-defined policies that inherit AuthPolicy
. We need to have a general and unified way to return back information from permits()
(and thus check_permission()
).
Use case:
check_permission
is called on a bunch of permissions and the calling code wants to know which exactly permission check was failed.
Possible solutions:
check_permissions() -> PermissionCheckResult
(in addition to permits() -> bool
) that returns a general dataclass (or json object) that will consolidate information on the permission check, for example:T = TypeVar('T')
@dataclass
class PermissionCheckResult:
success: bool
missing: Set[T]
async def check_permissions(...) -> PermissionCheckResult:
...
permits() -> bool
, but allow it to raise a pre-defined exception for providing additional information:class PermissionDeniedException(Exception):
def __init__(self, missing_permissions):
pass
I will now begin work on a PR to add support for restricting routes to multiple permissions like this:
async def protected_page(self, request):
await check_permission(request, ['protected', 'level1', 'level2'])
response = web.Response(body=b'You are on protected page')
return response
Discussion welcome.
I would suggest updating docs to clarify that in order to use EncryptedCookieStorage
one have to use SessionIdentityPolicy
, and not CoookieIdentyPolicy
(as it is not so intuitive).
I can make it in spare time.
Hey.
I propose to add middleware to the library.
middleware.py
import base64
from aiohttp import web
from aiohttp_security import authorized_userid, remember
async def check_credentials(db_engine, username, password):
async with db_engine.acquire() as conn:
where = and_(db.user.c.username == username,
db.user.c.is_active)
query = db.user.select().where(where)
ret = await conn.execute(query)
user = await ret.fetchone()
if user is not None:
hash_ = user[2]
return sha256_crypt.verify(password, hash_)
return False
def middleware_factory():
@web.middleware
async def authorization_middleware(request, handler):
username = await authorized_userid(request)
if not username:
if request.headers.get('Authorization', None):
authorization = request.headers.get('Authorization').split(' ')
basic = base64.b64decode(authorization[1]).decode('utf-8')
username, password = basic.split(':')
if await check_credentials(request.app['db'], username, password):
# await remember(request, web.HTTPFound(request.rel_url), username)
raise web.HTTPFound(request.rel_url)
else:
raise web.HTTPForbidden()
return await handler(request)
return authorization_middleware
Tnx.
The module-level function, remember()
asserts that the identity is a string:
assert isinstance(identity, str), identity
assert identity
Shouldn't that be the burden of the underlying implementation of an IdentityPolicy?
My use-case: I'm building a custom IdentityPolicy and associated AuthorizationPolicy that sends a JWT token back to the client with a custom header. In my user-session code I want to be able to do, e.g.:
def login(request):
# Get `username` and `password` from request data, validate, etc.
username, password = ...
# Using an underlying model, User, call a classmethod `login` which
# validates the credentials and returns a user instance on success; None on failure
user = User.login(username, password)
if user is None:
raise web.HTTPUnauthorized()
# I have a valid, logged in user, json-ify the object and set header
resp = web.json_response(user)
remember(request, resp, user) # <== BLOWS UP WITH ASSERTION ERROR
return resp
The implementation of my policies expects a User instance in all places an identity is passed around. When creating the JWT the state of the user instance dictates the claims made. I want this logic in the IdentityPolicy not in the caller.
It seems to me that at the API level identities should be opaque and leave serialization/validation up to the underlying policy implementations.
Hi there,
I ran into some version issues where I assumed that the "latest" version on the github releases is correct, and corresponds to the latest version of the documentation, which is incorrect.
Could you please update the "latest" tag to point to v0.4.0?
Thank you!
Hello,
Just wanted to ask, is there a way to apply a timeout to remember() (such that forget() is called after x seconds)? The only thing on timeouts I can see in the aiohttp docs is with ClientSession (and if that is the solution to be used with remember(), I'm not sure how to combine the two).
Thanks
Checking on the code, the decorator has_permission
option is being deprecated in favor of check_permission
which has no decorator option. Why?
My best guess is is that it should not be the responsibility of the package to implement such feature, but I could not find the documented reason.
I inserted some print()s to the api:
async def remember(self, request, response, identity, **kwargs):
session = await get_session(request)
session[self._session_key] = identity
print(dict(session))
async def identify(self, request):
session = await get_session(request)
print(dict(session))
return session.get(self._session_key)
Below are respective result:
{'AIOHTTP_SECURITY': 'username'}
{}
which mean the AIOHTTP_SECURITY
session is gone before identify()
is called. This seem to be a bug.
The documentation is a little confusing, as it mentions that identity should not be something like a user ID or login. However, both demos appear to ignore this advice and it seems that userid == login == identity.
If trying to get started quickly by going through the demos, then it appears you would end up following bad practices.
Please never use underscores in python-packages names.
Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.
We have a really problem. When our build system convert all dependencies to RPM (using FPM) yum is broken. We are can't use this package in our projects.
I don't feel like forking and doing a PR. I made this mysql code to replace the SA code (I hate SA). You should add it as an example:
Edit: Nevermind. Regular contributors say they don't want code suggestions in issues.
See the details @ https://github.com/orgs/aio-libs/discussions/26.
For this, we'll need somebody with Owner privileges to either give me access on PyPI (the username there is the same โ webknjaz
) or be invited to the org by me (for that I'd need the username of such an individual).
As a bonus, this will also allow us to set up secretless publishing from GHA to PyPI and get rid of the in-repo secrets.
cc @jettify
It would be cool to be able to read the documentation online. Do you planning to upload it to readthedocs?
P.S. Can I help you with that?
I am using this module to implement something like 'remember me'.
I understand that we should use a random string such as a uuid or hash
as identity to make it unguessable for attackers. My question is is it fine to use a identity that is immutable for a specific user?
If so, what if the token is identity is leaked?
If not, when should it be changed?
Thanks in advance!
Dependabot couldn't authenticate with https://pypi.python.org/simple/.
You can provide authentication details in your Dependabot dashboard by clicking into the account menu (in the top right) and selecting 'Config variables'.
I was wondering if authorized_userid() method in AbstractAuthorizationPolicy is necessary.
It seems like it duplicates functionality of identity() in AbstractIdentityPolicy.
@asyncio.coroutine
@abc.abstractmethod
def authorized_userid(self, identity):
"""Retrieve authorized user id.
Return the user_id of the user identified by the identity
or 'None' if no user exists related to the identity.
"""
pass
In demo code, it is used to get identities from database that are not disabled.
@asyncio.coroutine
def authorized_user_id(self, identity):
with (yield from self.dbengine) as conn:
where = [db.users.c.login == identity, not db.users.c.disabled]
query = db.users.count().where(sa.and_(*where))
ret = yield from conn.scalar(query)
if ret:
return identity
else:
return None
I believe this check can be done in identify() method in AbstractIdentityPolicy instead.
I could work on a PR if there is interest in adding this.
Hello. Is there a way to use login_required decorator with a class based view?
Dependabot couldn't authenticate with https://pypi.python.org/simple/.
You can provide authentication details in your Dependabot dashboard by clicking into the account menu (in the top right) and selecting 'Config variables'.
I'm thinking about opposite direction: deprecate and remove these decorators.
Please consider two snippets:
@has_permission('read')
async def handler(request):
return web.Response()
and
async def handler(request):
check_permission(request)
return web.Response()
check_permission
from second example works basically as existing has_perission
decorator.
It raises HTTPForbidden
if the user is not authorized.
The main advantage is debugging. In decorator approach basically there is no line to put a breakpoint for debugging permission checks for the handler. The decorator is executed before first handler's line. Setting a breakpoint to decorator itself leads to debugging all handlers with decorator applied, not the specific one.
Also, it solves the problem of class based views (and any other web handlers organization style). The check can be done in any place of code, it is pretty readable and straightforward.
Downloaded the demo, on a mac - tried safari and chrome.
I would like to offer some help going through these PRs and issues. This package is really important to me.
Example code in docs/example.rst
is not working with current code. There are multiple issues with syntax and non-existent objects (like @protect
decorator or DictionaryAuthorizationPolicy
).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.