Skip to content

Commit a08ec2d

Browse files
committed
feat: allow exempting service accounts from PREVENT_CONCURRENT_LOGINS
1 parent fe6b64b commit a08ec2d

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

  • common/djangoapps/student/models

common/djangoapps/student/models/user.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,31 @@ def log_successful_logout(sender, request, user, **kwargs): # pylint: disable=u
13381338
segment.track(request.user.id, 'edx.bi.user.account.logout')
13391339

13401340

1341+
def _is_single_login_exempt(user):
1342+
"""
1343+
Return True if ``user`` is exempt from single-login enforcement.
1344+
1345+
``PREVENT_CONCURRENT_LOGINS`` keeps a single active session per user and
1346+
deletes the previously registered session on each login. That is correct for
1347+
human accounts, but it breaks shared service/automation accounts (for
1348+
example the xqueue-watcher grader account) whose many concurrent workers all
1349+
authenticate as one user and would otherwise continually evict each other's
1350+
sessions.
1351+
1352+
Exemptions are opt-in and default to empty, so behaviour is unchanged unless
1353+
configured:
1354+
1355+
* ``SINGLE_LOGIN_EXEMPT_USERNAMES`` -- iterable of exact usernames.
1356+
* ``SINGLE_LOGIN_EXEMPT_GROUPS`` -- iterable of group names; a user in any of
1357+
these groups is exempt.
1358+
"""
1359+
exempt_usernames = getattr(settings, 'SINGLE_LOGIN_EXEMPT_USERNAMES', None) or ()
1360+
if user.username in exempt_usernames:
1361+
return True
1362+
exempt_groups = getattr(settings, 'SINGLE_LOGIN_EXEMPT_GROUPS', None) or ()
1363+
return bool(exempt_groups) and user.groups.filter(name__in=exempt_groups).exists()
1364+
1365+
13411366
@receiver(user_logged_in)
13421367
@receiver(user_logged_out)
13431368
def enforce_single_login(sender, request, user, signal, **kwargs): # pylint: disable=unused-argument
@@ -1346,6 +1371,9 @@ def enforce_single_login(sender, request, user, signal, **kwargs): # pylint: di
13461371
to prevent concurrent logins.
13471372
"""
13481373
if settings.FEATURES.get('PREVENT_CONCURRENT_LOGINS', False):
1374+
if user and _is_single_login_exempt(user):
1375+
# Shared service/automation accounts may hold concurrent sessions.
1376+
return
13491377
if signal == user_logged_in:
13501378
key = request.session.session_key
13511379
else:

0 commit comments

Comments
 (0)