Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pass request to AXES_COOLOFF_TIME callback #1222

Conversation

browniebroke
Copy link
Member

@browniebroke browniebroke commented Aug 14, 2024

What does this PR do?

If the AXES_COOLOFF_TIME is a callable or path to a callable taking an argument, pass the username to it.

This should enable users to customize the cool off to be user dependant, and possibly implement a growing cool-off time:

  • First lockout cools off after 5 mins
  • Second one after 10 mins
  • etc...

Past references of this:

This PR should be backwards compatible with previous behaviour: if the AXES_COOLOFF_TIME is a callable that takes no argument, it will be called without parameters.

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you make sure to update the documentation with your changes?
  • Did you write any new necessary tests?

If the AXES_COOLOFF_TIME is a callable or path to a callable taking
an argument, pass the username to it.

This should enable users to customize the cool off to be user dependant,
and possibly implement a growing cool-off time:

- First lockout cools off after 5 mins
- Second one after 10 mins
- etc...
@browniebroke browniebroke force-pushed the cool-off-time-callback-username-argument branch from b22f022 to 982ebe3 Compare August 14, 2024 09:18
@browniebroke
Copy link
Member Author

Looks like the test failures are the same as another PR (#1221) and they don't look related to my change so I'm going to ignore them for this. Any idea how to fix them? I'll try to take a look if I find time

Copy link
Member

@aleksihakli aleksihakli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good but I think the implementation should just take the request if a parametrizable callback is implemented as that contains the username and other metadata that is useful for processing as well, offering users wanting to customize more flexibility for the same price.

axes/helpers.py Outdated
if cool_off is None:
return None
return int(cool_off.total_seconds())


def get_cool_off() -> Optional[timedelta]:
def get_cool_off(username: Optional[str] = None) -> Optional[timedelta]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to just supply the request object here so the user can use any field present in there? IP address or other metadata could be interesting as well. The request can also be used to carry attached attributes used in upstream / downstream processing in the middleware stack, if needed. username seems a bit limiting if we're implementing features here.

Adding the use of request object requires a few modifications in other places as well, but should be easy enough to test.

Copy link
Member Author

@browniebroke browniebroke Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That suggestion made a lot of sense when I first read it, but now that I'm trying to make the changes, I remembered why I went for this initial implementation...

The place where it's called, user_login_failed calls a function to resolve the username get_client_username, passing down some credentials. I can see that this function defaults to using request.data or request.POST.

I think I'm lacking a bit of context on the lifecycle where this is used, is that fair to assume that this will be called in a POST request where the username will be included in the body? It feels it may not always be there in some cases like SSO/OAuth flow. Are these kind of flows out of the scope of django-axes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, a good question on the authentication methods and flows, I initially missed this one. Sorry about that.

SSO flows such as SAML and OAuth are absolutely in scope of this plugin. Ideally django-axes should be login method agnostic, if possible.

If anyone else is checking this discussion it might be useful to see the Django authentication signals documentation at https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals for reference.

You are right in that the request object might not contain all the relevant information needed to dig up e.g. username or other identifying login data if the login is coming in from e.g. a SAML request, but request is the only persistent information, really, that we get from all the login flows i.e. going up and down the middleware stack and in all the login signals as described at e.g. https://django-axes.readthedocs.io/en/latest/7_architecture.html

At the current time we always expect a request for the get_lockout_response callable and credentials as an optional.

It actually might be nice to pass the credentials argument as well, as you pointed out.

If you're open to it I think it'd be OK to add that argument as well and bake a version 7.x from that. If you want to make a PR I'll be happy to make a new release for that.

axes/models.py Outdated Show resolved Hide resolved
@aleksihakli
Copy link
Member

Looks like the test failures are the same as another PR (#1221) and they don't look related to my change so I'm going to ignore them for this. Any idea how to fix them? I'll try to take a look if I find time

There was a failing test that was produced by a change in the Django upstream implementation that limits header sizes. I updated the test, if you rebase this PR changeset on top of jazzband/django-axes repository master branch the test should be fixed in this branch as well.

browniebroke and others added 5 commits October 1, 2024 08:32
As we pass down the whole request, we no longer need to extract the axes_attempt_time anymore.

This is a potential breaking change, but the impacted functions are not part of the documented API.
@browniebroke browniebroke changed the title feat: pass username to AXES_COOLOFF_TIME callback feat: pass request to AXES_COOLOFF_TIME callback Oct 1, 2024
@browniebroke
Copy link
Member Author

I've pushed more updates, trying to me more granular with my commits. I didn't rebase anything yet to help you keep track of the changes you already reviewed, and the new ones I've added. I can rebase them when/if you want me to.

Copy link
Member

@aleksihakli aleksihakli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @browniebroke the changeset looks good 👍 Do you think it would be feasible to drop the _maybe_partial implementation and just directly invoke the settings.AXES_COOLOFF_TIME callable without the functools.partial and inspect additions? I think that'd make for an easier-to-understand API in the long run and we can just bake a breaking release for this changeset with e.g. major release version 7 if needed, it's not a big effort. I can write up the changelog and bake a release after this has been merged.

axes/helpers.py Outdated Show resolved Hide resolved
axes/helpers.py Outdated Show resolved Hide resolved
axes/attempts.py Show resolved Hide resolved
@browniebroke
Copy link
Member Author

I think it's all done now. Let me know if I should update the changelog with some "unreleased changes".

@aleksihakli
Copy link
Member

Yeah looks good to me, thank you 👍 I'll merge and bake a changelog and a release for this.

@aleksihakli aleksihakli merged commit b54019f into jazzband:master Oct 2, 2024
16 checks passed
@aleksihakli
Copy link
Member

Hey @browniebroke I made a release version 7.0.0 with associated changelog and notes for upgrading, do let me know if there's anything missing.

I also missed your earlier comment regarding e.g. SSO logins, please check it out and let me know if you have any thoughts on that.

Cheers for the good PR 👍

@browniebroke browniebroke deleted the cool-off-time-callback-username-argument branch October 2, 2024 18:02
@browniebroke
Copy link
Member Author

Thanks a lot for the quick feedback and very prompt release!

@browniebroke browniebroke mentioned this pull request Oct 2, 2024
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants