#23155 closed New feature (fixed)
Add request attr to user_login_failed signal
Reported by: | anonymous | Owned by: | nobody |
---|---|---|---|
Component: | contrib.auth | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | brian@…, moritz.sichert@…, gavinwahl+gh@…, Aleksej Manaev, m.levental@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
It would be convenient to request instance in the signal user_login_failed.
This will get the users data and make limitations, such as IP address.
What do you think?
Change History (23)
comment:1 by , 10 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:2 by , 10 years ago
Cc: | added |
---|
Implementing this looks not-trivial.
The authenticate() method at django.contrib.auth takes username and password parameters, but does not take a request parameter.
https://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.authenticate
This function sends the user_login_failed signal. Fixing this may require changing the API of authenticate().
comment:3 by , 10 years ago
Adding optional request attr to authenticate() function and according to Authentication backends would give more options and flexibility.
I believe that the framework should be improved and made more flexible.
comment:4 by , 10 years ago
Simply passing request
to authenticate
is backwards incompatible: every third-party authentication backend will be silently skipped because the arguments don't match.
comment:5 by , 10 years ago
Version: | 1.7-rc-2 → master |
---|
comment:6 by , 10 years ago
Aymeric, I think you mixed django.contrib.auth.authenticate()
and the authenticate()
method of each backend. We wouldn't have to pass request
to the auth backends because the signal is sent in the former so it might be feasible.
comment:7 by , 10 years ago
The caller of django.contrib.auth.authenticate()
would have to be updated to take this new parameter. That could be code outside Django.
We could make it an optional parameter, however then you don't actually get the benefits unless it is called correctly. New code that uses the new request parameter will get confused if used with older Django versions as the request parameter will become part of **credentials
, and this will get passed through to every backend, like it or not.
The current code is:
def authenticate(**credentials): for backend in get_backends(): try: user = backend.authenticate(**credentials) [...]
comment:9 by , 9 years ago
One way I thought of to fix this is to add an optional _request
kwarg to django.contrib.auth.authenticate. If provided, it would be passed into the call to user_login_failed. This would be backwards incompatible if anyone were using an authentication backend that used _request
, which seems unlikely. Would a patch implementing this approach be accepted?
As it is user_login_failed isn't useful for what seems like its primary purpose -- logging suspected attacks, which would need the IP address to be useful at all.
comment:10 by , 9 years ago
I don't see any obvious problem with that. The underscore seems unnecessary to me unless you know of a case where there would be a collision.
comment:11 by , 9 years ago
It seems reasonable that someone has an auth backend that does already take the whole request as a credential, and I wouldn't want to break that. I'll send a pull request soon.
comment:13 by , 9 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:14 by , 9 years ago
I think the underscore is not really necessary.
It might be reasonable that some people already pass the request to authenticate()
but I don't think it's likely that anyone would use the request keyword for anything else than the actual request.
Another disadvantage for using the underscore is that if you have a backend that expects a request you'd have to write something like authenticate(request=request, _request=request)
.
I propose the request argument to be handled just like any other keyword argument, i.e. passing it along to the backend.
This has following advantages:
- the
authenticate(request=request, _request=request)
syntax is not needed - there won't be any backwards compatibilities regarding authentication backends because if you passed the request to
authenticate()
before, it will still be passed to the backend, and if you didn't, the backend won't get any unexpected arguments - it is still possible to provide a backwards compatibility path, if needed, by first trying to call the backend with the request and if that fails because of a
TypeError
try again without it
What do you think?
comment:15 by , 9 years ago
Patch needs improvement: | set |
---|---|
Triage Stage: | Ready for checkin → Accepted |
A deprecation path toward making request
a required argument of backend.authenticate()
makes sense to me and does seem cleaner in the long run. I guess that should be a separate ticket that precedes this one though.
comment:16 by , 9 years ago
Cc: | added |
---|
comment:18 by , 9 years ago
Cc: | added |
---|
comment:19 by , 9 years ago
What I meant about a separate ticket for a deprecation to make request
a required argument for authentication backends is something like this in authenticate()
:
try: inspect.getcallargs(backend.authenticate, request, **credentials) except TypeError: try: inspect.getcallargs(backend.authenticate, **credentials) except TypeError: # This backend doesn't accept these credentials as arguments. Try the next one. continue else: warnings.warn( "Update authentication backend %s to accept a " "positional `request` argument." % backend RemovedInDjango20Warning )
comment:22 by , 8 years ago
Cc: | added |
---|
comment:23 by , 8 years ago
Cc: | added |
---|
The user_logged_in and user_logged_out signals both have the request parameter, so having request for user_login_failed would make sense IMHO.