level12 / keg-auth Goto Github PK
View Code? Open in Web Editor NEWRobust authentication system for Keg
Robust authentication system for Keg
It seems better then forcing every view to inherit from something like AuthenticatedView.
@classmethod
def testing_create(cls, **kwargs):
kwargs['password'] = kwargs.get('password', randchars())
obj = super().testing_create(**kwargs)
obj._plaintext_pass = kwargs['password']
return obj
Used when logging in a user.
The class decoration in place currently assumes a keg BaseView with check_auth
method, where we can insert our own method and chain the original afterward.
Automatic syncing of permissions doesn't seem to like it when you use a pool_pre_ping
on the engine.
Traceback (most recent call last):
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/sqlalchemy/util/_collections.py", line 988, in __call__
return self.registry[key]
KeyError: 4348003776
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
... clipped a bunch of click stuff
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/keg/cli.py", line 44, in list_commands
info.load_app()
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/flask/cli.py", line 229, in load_app
rv = self.create_app(self)
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/keg/cli.py", line 332, in create_app
return self.appcls().init(**init_kwargs)
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/keg/app.py", line 98, in init
self.on_init_complete()
File "/Users/nzac/j/level12/clients/racebetter/src/racebetter/app.py", line 55, in on_init_complete
rbext.auth_manager.init_app(self)
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/keg_auth/core.py", line 78, in init_app
self.init_permissions(app)
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/keg_auth/core.py", line 199, in init_permissions
sync_permissions()
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/keg_auth/core.py", line 158, in sync_permissions
db_permissions = db.session.query(Permission).all()
... clip a bunch of sa stuff
File "/Users/nzac/.pyenv/versions/3.5.2/envs/racebetter/lib/python3.5/site-packages/sqlalchemy/engine/strategies.py", line 160, in create
engineclass.__name__))
TypeError: Invalid argument(s) 'pool_pre_ping' sent to create_engine(), using configuration PGDialect_psycopg2/QueuePool/Engine. Please check that the keyword arguments are appropriate for this combination of components.
We forked Flask-Session:
https://github.com/level12/flask-session/
and offered to help upstream:
But none of that went anywhere. What do we want to do?
Right now, we have to do something like:
client = flask_webtest.TestApp(flask.current_app)
user = ents.User.testing_create()
with client.session_transaction() as sess:
sess['user_id'] = user.id
resp = client.get('protected-resource', status=200)
So the with
is needed for each request. We can do better than that.
All auth is currently done via decorators on blueprints/classes/methods. We should probably add the possibility of having a global request hook.
Rather than letting the user set their API token, generate it for them and let them see it when generated. But after that, we can only re-generate it. Like AWS does their access token generation.
When using flask-login, we have the capability of using a "next" get parameter on the login route, which provides the path we should redirect to after a successful login. If the parameter is not provided, the after-login route may be used.
Example, user wants to get to /protect-all-the-things
. They should get redirected to /login?next=protect-all-the-things
. Once logged in, get them back to /protect-all-the-things
.
Currently, if a Keg view implements a check_auth
method, that method can be used to determine if the user should have access to the view (ie, should a 403 be returned).
Unfortunately, the nav system doesn't seem to have any way of accessing the check_auth
instance method. As a result, the nav system can include links that return a 403 when clicked.
I don't currently see any way to resolve this with the check_auth
method; permissions must instead be specified using the require_permissions
decorator.
If using check_auth
can't be resolved, we should add an "Upgrading to keg-auth" section to the readme which specifies that check_auth
should not be used.
CSS and JS
Newer versions of flake8 are more strict and that is causing CI to fail where it was not failing previously.
If a user's verification email expires or for some reason does not work, there is no way to verify the user unless you delete them and add them again.
An example of this functionality can be found in level12/atech-imp#282.
Instead of storing the reset token creation time in token_created_utc
perhaps we could use the timestamp signer from itsdangerous to sign the token we send and verify it for expiration.
Can we have better CLI integration in the Keg ecosystem with:
To make it clear that the method is only to be used for testing:
Line 94 in d712c13
Avoids confusion when subclassing and you want a method called request_loader()
.
See https://github.com/level12/racebetter/issues/651 for an example.
When creating a user through the web UI right now, no email is sent for the user to setup their password and to verify their account. If we used the AuthManager's create user method instead of just adding the user to the database this would fix the problem.
Get this project on AppVeyor for Windows testing.
Rate limit attempts on the following views:
Log attempts in these views in the database and limit attempts that are allowed in a configurable time span.
Include a CLI command that can be run from a cron job to purge old records
In these two functions we are caching the permitted sub_nodes and is_permitted flags for the nav items. The first construction of the navbar will work fine. If another user logs in and has the same web server instance they will see the navbar items that the first user is permitted to see.
keg-auth/keg_auth/libs/navigation.py
Line 163 in da1a75b
keg-auth/keg_auth/libs/navigation.py
Line 175 in da1a75b
Removing the if self._is_permitted is None:
and if self._permitted_sub_nodes is None:
fixed this issue for me.
https://github.com/level12/keg-auth/blob/master/keg_auth/grids.py#L76
Webgrid actually expects edit_link
instead of edit-link
, and the same for deletion. But, delete classes are configurable, and the edit/view classes should be as well.
Anything good here? https://pythonhosted.org/Flask-Principal/
Example:
@requires_user
class PrivateBlueprint(flask.Blueprint):
def on_authentication_failure(self):
print('never gets here')
private_bp = PrivateBlueprint('private', __name__)
class BaseView(KegBaseView):
blueprint = private_bp
After discussion we decided to remove updated_utc
and session_key
from the salt. We are going to add a field to the users table last_login_utc
and add that field to the salt. This field will get updated on flask_login's login event.
it's viral.
Might as well add our own license while we are at it.
The authenticators are not using lazy evaluation for the flash values, and so the flashes are not being translated.
# Required for Keg Auth when generating URL in create-user CLI command.
SERVER_NAME = '{{ nginx_vhost_server_name }}'
# This is important so that generated auth related URLS are secure. We have an SSL redirect
# but by the time that would fire, the key would have already been sent in the URL.
PREFERRED_URL_SCHEME = 'https'
KEGAUTH_EMAIL_SITE_NAME = 'Some Thing'
KEGAUTH_EMAIL_SITE_ABBR = 'Some Thing'
make_blueprint
should be able to accept a custom Blueprint class to set up the auth views.
Copy from internal project:
Hindsight is 20/20, but it's now quite clear that having a mapping between endpoints and required permissions would have been a huge win. Literally every place we use url_for, we must wrap it in a conditional to only show the link (or make it clickable, etc.) if the user has permission to access that endpoint. But since there is no mapping dictionary, we must hard code the mapping at every call site. Effectively not having the mapping means there is a ton of code duplication and a truckload of room for mistakes. In fact, there are whole swaths of links that are not properly checked for this condition. It's not a security issue, but a usability issue, since the links are clickable but will inevitably result in 403s for some users. Equally as bad is the difficulty in building abstractions around endpoints such that they are "permission-aware." Without the mapping, there is no generic way to write a form of url_for that considers permissions.
CrudView.delete
uses init_object
to control access on a per-object basis. But, sometimes edit vs delete should yield different results. Currently, in that case, it means more has to be overridden.
init_object_delete
method that passes through to init_object
I think it's time.
Add support for permissions. Permissions should be assignable in the following ways:
Bundle
assigned to a userGroup
assigned to a userBundle
assigned to a Group
assigned to a userIn applying permissions to views:
and
and or
relationships that would allow more complex conditions so you could do something like permisisons_required = and_('foo', or_('bar', 'baz'))
sortof sqlalchemy style@flask_login.login_required
on all of those viewsThe current assumption is that all user IDs are email addresses.
The dev can overrides things, for everything from the user ID being an email address, to user activation by email, etc. We will likely need to be able to use this in environments where email won't be happening, and users may not in fact be associated with an email at all.
Email could be an all-or-nothing idea, rather than having settings for each email op. I.e. have a feature-flag kind of setting that enables email functionality, and then the user object gets an email field, maybe default the user ID to the same field, turn on activations, links in the templates, etc.
In that case, if we don't have email ops turned on, we'd be restricted to having an admin reset any passwords that are forgotten, etc.
There is a separate setting for what field in the model is the user identifier for login (KEGAUTH_USER_IDENT_FIELD).
Pieces that would be affected:
CrudView should assign more template arguments out of the box.
The following are rendered by crud-list.html
but are not assigned by CrudView:
object_name
page_heading
page_heading
also appears in form-base.html
but is not assigned when rendering forms in CrudView.
Do we need to set the value to something other than the default? If so, is there a better way to set/enforce this than to have the apps have to remember to set it in their config?
PASSLIB_CRYPTCONTEXT_KWARGS = {
'schemes': ['bcrypt']
}
Right now, if you have a user model that adds something like name, and you require that name in the model, the only way to make the CLI command require it is to override the CLI implementation.
A user's current permission set at login will get stored in the session, so we don't have to run expensive queries constantly. The tradeoff to that is when permissions change for the user or related groups/bundles, that session will have to update, lest the user have access to something now denied, etc.
We've done this at the app level before in the blaze world, where we had a known session backend (beaker). We should see if flask has an API to access sessions and invalidate them according to whether they have particular data (e.g. the user ID) in them. If not, we would need to create plugins for our most-used session storage backends.
Anything of value here? https://developer.okta.com/documentation/
In some projects we have the ability to set a date at which the user becomes disabled, which has been useful over time. The logic becomes, the user is active if:
At present, the current user can obtained from flask_login.current_user
. This is somewhat of an internal detail, however, and future authenticators might not use the flask_login
system.
Add a method which provides access to the current user.
Per @guruofgentoo, simply returning flask_login.current_user
should be sufficient for now.
Remove the 'webgrid.git#41-multilingual' reference in setup.py
, and change it to just use 'webgrid[i18n]'.
Refs level12/webgrid#41
https://github.com/level12/keg-auth/blob/master/keg_auth/testing.py#L488
Needs to assert each token is part of the set of permissions given to the auth manager.
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.