Skip to content

Commit

Permalink
Move more ldapauth stuff to ldap.py
Browse files Browse the repository at this point in the history
  • Loading branch information
hmpf committed Nov 13, 2023
1 parent 0b86755 commit c182d0d
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 40 deletions.
39 changes: 5 additions & 34 deletions python/nav/web/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,33 +55,22 @@ def authenticate(username, password):
try:
account = Account.objects.get(login__iexact=username)
except Account.DoesNotExist:
if ldap.available:
ldap_user = ldap.authenticate(username, password)
# If we authenticated, store the user in database.
if ldap_user:
account = Account(
login=ldap_user.username,
name=ldap_user.get_real_name(),
ext_sync='ldap',
)
account = update_ldap_user(ldap_user, account, password)
# We're authenticated now
return account
# No account, bail out
return None
# account autocreated if username is authenticated
account = ldap.authenticate(username, password)
return account

Check warning on line 60 in python/nav/web/auth/__init__.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/__init__.py#L59-L60

Added lines #L59 - L60 were not covered by tests

if account.locked:
_logger.info("Locked user %s tried to log in", account.login)
return None

Check warning on line 64 in python/nav/web/auth/__init__.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/__init__.py#L64

Added line #L64 was not covered by tests

if account.ext_sync == 'ldap' and ldap.available:
try:
ldap_user = ldap.authenticate(username, password)
ldap_user = ldap.get_ldap_user(username, password)
except ldap.NoAnswerError:
pass

Check warning on line 70 in python/nav/web/auth/__init__.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/__init__.py#L70

Added line #L70 was not covered by tests
else:
if ldap_user:
account = update_ldap_user(ldap_user, account, password)
account = ldap.update_ldap_user(ldap_user, account, password)
return account
return None
# Fallback to stored password if ldap is unavailable
Expand All @@ -91,24 +80,6 @@ def authenticate(username, password):
return None


def update_ldap_user(ldap_user, account, password):
account.set_password(password)
account.save()
_handle_ldap_admin_status(ldap_user, account)
return account


def _handle_ldap_admin_status(ldap_user, nav_account):
is_admin = ldap_user.is_admin()
# Only modify admin status if an entitlement is configured in webfront.conf
if is_admin is not None:
admin_group = AccountGroup.objects.get(id=AccountGroup.ADMIN_GROUP)
if is_admin:
nav_account.groups.add(admin_group)
else:
nav_account.groups.remove(admin_group)


def get_login_url(request):
"""Calculate which login_url to use"""
path = parse.quote(request.get_full_path())
Expand Down
64 changes: 60 additions & 4 deletions python/nav/web/auth/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import nav.errors
from nav.config import NAVConfigParser
from nav.models.profiles import Account, AccountGroup

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -121,11 +122,66 @@ def open_ldap():
return lconn


def authenticate(login, password):
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
return None

Check warning on line 148 in python/nav/web/auth/ldap.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/ldap.py#L133-L148

Added lines #L133 - L148 were not covered by tests


def autocreate_ldap_user(ldap_user, password):
account = Account(

Check warning on line 152 in python/nav/web/auth/ldap.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/ldap.py#L152

Added line #L152 was not covered by tests
login=ldap_user.username,
name=ldap_user.get_real_name(),
ext_sync='ldap',
)
account = update_ldap_user(ldap_user, account, password)
return account

Check warning on line 158 in python/nav/web/auth/ldap.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/ldap.py#L157-L158

Added lines #L157 - L158 were not covered by tests


def update_ldap_user(ldap_user, account, password):
account.set_password(password)
account.save()
_handle_ldap_admin_status(ldap_user, account)
return account


def _handle_ldap_admin_status(ldap_user, nav_account):
is_admin = ldap_user.is_admin()
# Only modify admin status if an entitlement is configured in webfront.conf
if is_admin is not None:
admin_group = AccountGroup.objects.get(id=AccountGroup.ADMIN_GROUP)
if is_admin:
nav_account.groups.add(admin_group)

Check warning on line 174 in python/nav/web/auth/ldap.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/ldap.py#L172-L174

Added lines #L172 - L174 were not covered by tests
else:
nav_account.groups.remove(admin_group)

Check warning on line 176 in python/nav/web/auth/ldap.py

View check run for this annotation

Codecov / codecov/patch

python/nav/web/auth/ldap.py#L176

Added line #L176 was not covered by tests


def get_ldap_user(login, password):
"""
Fetch an LDAPUser from an ldap server if login and password matches.
Returns an autenticated LDAPUser of a specific group or with specific
entitlements, or False.
"""
lconn = open_ldap()
server = _config.get('ldap', 'server')
Expand Down
4 changes: 2 additions & 2 deletions tests/unittests/general/webfront_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ def test_authenticate_should_return_account_when_ldap_says_yes(self):
ldap_user = Mock()
ldap_user.is_admin.return_value = None # mock to avoid database access
with patch("nav.web.auth.ldap.available", new=True):
with patch("nav.web.auth.ldap.authenticate", return_value=ldap_user):
with patch("nav.web.auth.ldap.get_ldap_user", return_value=ldap_user):
assert auth.authenticate('knight', 'shrubbery') == LDAP_ACCOUNT

def test_authenticate_should_return_false_when_ldap_says_no(self):
with patch("nav.web.auth.ldap.available", new=True):
with patch("nav.web.auth.ldap.authenticate", return_value=False):
with patch("nav.web.auth.ldap.get_ldap_user", return_value=False):
assert not auth.authenticate('knight', 'shrubbery')

def test_authenticate_should_fallback_when_ldap_is_disabled(self):
Expand Down

0 comments on commit c182d0d

Please sign in to comment.