Skip to content

Assign picture password when user's last role is deleted#14566

Merged
LianaHarris360 merged 2 commits intolearningequality:developfrom
rtibblesbot:issue-14556-65c860
Apr 13, 2026
Merged

Assign picture password when user's last role is deleted#14566
LianaHarris360 merged 2 commits intolearningequality:developfrom
rtibblesbot:issue-14556-65c860

Conversation

@rtibblesbot
Copy link
Copy Markdown
Contributor

@rtibblesbot rtibblesbot commented Apr 9, 2026

Summary

After a user's last role is deleted (reverting them to learner status), if picture login is enabled for their facility and they don't yet have a picture_password, one is now automatically assigned. This mirrors the assignment that happens at new-learner creation and the clearing that happens at role creation (#14549).

The change adds a post-delete check to Role.delete(). NoAvailableSequences is caught silently (capacity-exceeded case), following the same pattern as FacilityUserSerializer.create(). Inline imports are used inside the method body to avoid a circular import (picture_passwords.py imports Facility/FacilityUser from models.py).

References

Closes #14556

Related: #14549 (clears picture password on role creation — this PR handles the inverse path)

Reviewer guidance

Five new unit tests in RoleDeletePicturePasswordTestCase (kolibri/core/auth/test/test_api.py) cover all acceptance criteria: last role deleted assigns password, remaining role prevents assignment, existing password not overwritten, at learner limit no assignment/no exception, NoAvailableSequences caught silently.

Area worth checking: the inline imports inside Role.delete() — this is an intentional workaround for the circular import described above.

AI usage

Implemented with Claude Code following a pre-written implementation plan using TDD. Tests were written and confirmed failing before the implementation was added. I reviewed the code, ran the full auth test suite (pytest kolibri/core/auth/test/), and verified all tests pass with no regressions.


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?
  • Ran pre-flight CI checks (lint, format, tests) and verified all pass
  • Rebased onto the target branch and resolved any conflicts
  • Reorganized commit history into clean, logical commits
  • Audited the diff to ensure only issue-relevant files are changed
  • Built PR body from the repository's PR template with evidence blocks

status

@github-actions github-actions bot added DEV: backend Python, databases, networking, filesystem... SIZE: medium labels Apr 9, 2026
@rtibblesbot rtibblesbot marked this pull request as ready for review April 9, 2026 18:58
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

@rtibblesbot rtibblesbot force-pushed the issue-14556-65c860 branch 4 times, most recently from 384d2f9 to a543c38 Compare April 9, 2026 23:00
Copy link
Copy Markdown
Member

@LianaHarris360 LianaHarris360 left a comment

Choose a reason for hiding this comment

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

The code changes align with the issue specced, but an unrelated change to AllPasswordsPage.spec.js should be removed, and a regression test for when a user's last role is deleted in a facility where picture login isn't enable should be added for more thorough testing.

import { picturePasswordStrings } from 'kolibri-common/strings/picturePasswords';
import AllPasswordsPage from '../AllPasswordsPage.vue';

jest.mock('kolibri/composables/useNav');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test file does not need to be updated with this mock for this change, this should be removed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — removed the line and dropped the entire AllPasswordsPage spec commit. The file is no longer in the diff.


user.refresh_from_db()
self.assertIsNone(user.picture_password)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be worth it to include a test case for when a user's last role is deleted in a facility where picture_password_settings is None to confirm that they are not assigned a picture_password.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added — new test test_last_role_deleted_no_assignment_when_picture_password_settings_null covers this case. It temporarily disables picture password settings, deletes the user's last role, and asserts picture_password remains None.

@LianaHarris360 LianaHarris360 requested a review from bjester April 10, 2026 20:04
Move the no-picture-password-settings test out of RoleDeletePicturePasswordTestCase
into a separate class with its own fixture data, avoiding the need to temporarily
mutate shared state and restore it in a finally block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@LianaHarris360 LianaHarris360 merged commit 3ace0ab into learningequality:develop Apr 13, 2026
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DEV: backend Python, databases, networking, filesystem... SIZE: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assign picture password when a user's last role is removed

2 participants