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

Simplify auth.authenticate #2728

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

Conversation

hmpf
Copy link
Contributor

@hmpf hmpf commented Nov 10, 2023

Greatly simplify the logic by using early returns.

@hmpf hmpf self-assigned this Nov 10, 2023
@hmpf hmpf marked this pull request as ready for review November 10, 2023 10:26
Copy link

github-actions bot commented Nov 10, 2023

Test results

     12 files       12 suites   11m 39s ⏱️
3 325 tests 3 325 ✔️ 0 💤 0
9 450 runs  9 450 ✔️ 0 💤 0

Results for commit 6c0d22c.

♻️ This comment has been updated with latest results.

Copy link

codecov bot commented Nov 14, 2023

Codecov Report

Attention: Patch coverage is 77.08333% with 11 lines in your changes are missing coverage. Please review.

Project coverage is 56.72%. Comparing base (f92920c) to head (6c0d22c).

Files Patch % Lines
python/nav/web/auth/ldap.py 80.00% 7 Missing ⚠️
python/nav/web/auth/__init__.py 69.23% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2728      +/-   ##
==========================================
+ Coverage   56.69%   56.72%   +0.02%     
==========================================
  Files         602      602              
  Lines       43971    43988      +17     
==========================================
+ Hits        24931    24950      +19     
+ Misses      19040    19038       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@hmpf hmpf force-pushed the simplify-authenticate branch 4 times, most recently from 6acbca8 to 1b0e2f7 Compare November 14, 2023 11:13
Comment on lines +145 to +147
if account.check_password(password):
account = update_ldap_user(ldap_user, account, password)
return account
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ldap_user can be False, and then update_ldap_user will raise an exception. It is unclear what the best solution is though:

Change the if to:

if ldap_user and account.check_password(password):

If ldap_user is False authenticate will return None.

Split the if into two:

if ldap_user:
    account = update_ldap_user(ldap_user, account, password)

if account.check_password(password):
    return account

Worse, the latter if might not be needed at all because if ldap_user is True, we have already been authenticated with ldap, so authenticating again is unneccessary.

A third way is:

if not ldap_user:
    return None

account = update_ldap_user(ldap_user, account, password)

and then again choose whether to check the password again...

@hmpf hmpf force-pushed the simplify-authenticate branch 2 times, most recently from 59468b8 to 8ad1121 Compare November 21, 2023 07:54
Copy link

sonarcloud bot commented Nov 21, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

Copy link
Member

@lunkwill42 lunkwill42 left a comment

Choose a reason for hiding this comment

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

Didn't we once discuss making a proper flowchart for NAV's login process? I think we need it...

Comment on lines +125 to +147
def authenticate(username, password):
"""
Attempt to authenticate the login name with password against the
configured LDAP server. If the user is authenticated, required
group memberships are also verified.
Authenticate the username and password against the configured LDAP server.

Required group memberships are also verified.

Returns an authenticated Account with updated groups, or None.
"""
if not available:
return None
ldap_user = get_ldap_user(username, password)
try:
account = Account.objects.get(login__iexact=username, ext_sync='ldap')
except Account.DoesNotExist:
if ldap_user:
account = autocreate_ldap_user(ldap_user, password)
return account
if account.locked:
_logger.info("Locked user %s tried to log in", account.login)
return None
if account.check_password(password):
account = update_ldap_user(ldap_user, account, password)
return account
Copy link
Member

Choose a reason for hiding this comment

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

This got confusing, real fast.

I read this as: If a re-visiting user is authenticated via LDAP, this additionally checks their entered password against the password stored in the DB. If their LDAP password changed at some point, this local verification will fail, and the user can no longer log in to NAV.

Then I realized that this function is actually only ever called for first-time users. The function contains code to handle the difference between new users and re-visiting users. It won't work well for re-visiting users, if I'm reading it correctly, but it will also never be called for re-visiting users, so what's the plan here?

Copy link
Contributor Author

@hmpf hmpf Nov 23, 2023

Choose a reason for hiding this comment

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

It is confusing: it's partially duplicating what nav.web.auth.authenticate is doing as a small step to make the latter independent of ldap.

It will be called for re-visiting users once we split up nav.web.auth.authenticate. Then it will also change.

  • If ldap is not on, return None and fallback to local user.
  • If ldap is on:
    • If ldap_user is False, return None and fallback to local user
    • If ldap_user is not False it is authenticated, get and/or update the account and return the account

See my comment above about the fact that what happens if ldap_user is False currently isn't covered and that there are multiple ways to do it.

The hope is that we will one day have a local.authenticate in addition to ldap.authenticate, and that in nav.web.auth we will have something (e.g. loop_through_available_authentication_methods) that runs through all methods that ask for username/password in order and stops on the first to find an account.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, is the change you want a flow-chart?

Copy link

sonarcloud bot commented Jan 3, 2024

Quality Gate Passed Quality Gate passed

Kudos, no new issues were introduced!

0 New issues
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

@hmpf hmpf force-pushed the simplify-authenticate branch 2 times, most recently from 1cdafc0 to 096ea0c Compare February 27, 2024 06:33
@hmpf hmpf force-pushed the simplify-authenticate branch 6 times, most recently from 9868366 to 99d3387 Compare March 7, 2024 08:59
Copy link

sonarcloud bot commented Mar 7, 2024

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

hmpf added 2 commits March 7, 2024 10:42
Greatly simplify the logic by using early returns.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants