users package
Subpackages
Submodules
users.admin module
users.apps module
- class users.apps.UsersConfig(app_name, app_module)[source]
Bases:
AppConfig- name = 'users'
users.backends module
- class users.backends.EmailBackend[source]
Bases:
ModelBackend- authenticate(request, email=None, password=None, **kwargs)[source]
users.email_utils module
- users.email_utils.send_verification_email(user)[source]
Send an email verification link to the given user via SendGrid.
Constructs an HTML email containing a verification link built from
settings.FRONTEND_URLand the user’semail_verification_token. The link routes to/verify-email?token=<token>on the frontend.In test mode (
settings.SEND_EMAILS = False) no email is sent; the verification link is logged atINFOlevel instead and the function returnsTrueto allow the rest of the registration flow to proceed unaffected.In production, a SendGrid API call is made using
settings.SENDGRID_API_KEY. Any non-2xx response or raised exception is caught, logged atERRORlevel, and returned asFalse— callers are responsible for deciding whether to surface this failure to the user.- Parameters:
user (users.models.User) – The user to whom the verification email should be sent. Must have a non-empty
email_verification_token; returnsFalseimmediately if the token is absent.- Returns:
Trueif the email was sent successfully (or skipped in test mode),Falseif the token is missing, SendGrid returns a non-2xx status, or any exception is raised during the API call.- Return type:
Required settings:
SEND_EMAILS—Falseto suppress sending in test/dev environments.FRONTEND_URL— Base URL used to construct the verification link.DEFAULT_FROM_EMAIL— Sender address for all outbound mail.SENDGRID_API_KEY— API key for the SendGrid client.
Example:
>>> token = user.generate_verification_token() >>> success = send_verification_email(user) >>> if not success: ... logger.warning('Verification email failed; user must request resend.')
- users.email_utils.send_password_reset_email(user)[source]
Send a password reset link to the given user via SendGrid.
Constructs an HTML email containing a reset link built from
settings.FRONTEND_URLand the user’spassword_reset_token. The link routes to/reset-password/<token>on the frontend.In test mode (
settings.SEND_EMAILS = False) no email is sent; the reset link is logged atINFOlevel instead and the function returnsTrueto allow the password reset flow to proceed unaffected.In production, a SendGrid API call is made using
settings.SENDGRID_API_KEY. Any non-2xx response or raised exception is caught, logged atERRORlevel, and returned asFalse— callers are responsible for deciding whether to surface this failure to the user.- Parameters:
user (users.models.User) – The user to whom the reset email should be sent. Must have a non-empty
password_reset_token; returnsFalseimmediately if the token is absent.- Returns:
Trueif the email was sent successfully (or skipped in test mode),Falseif the token is missing, SendGrid returns a non-2xx status, or any exception is raised during the API call.- Return type:
Required settings:
SEND_EMAILS—Falseto suppress sending in test/dev environments.FRONTEND_URL— Base URL used to construct the reset link.DEFAULT_FROM_EMAIL— Sender address for all outbound mail.SENDGRID_API_KEY— API key for the SendGrid client.
Example:
>>> token = user.generate_password_reset_token() >>> success = send_password_reset_email(user) >>> if not success: ... logger.warning('Reset email failed; user must request a new link.')
users.models module
- class users.models.UserManager(*args, **kwargs)[source]
Bases:
BaseUserManagerCustom manager for the
Usermodel.Provides helper methods for creating standard users and superusers, enforcing UCLA email validation and normalization on all accounts.
- create_user(email, password=None, **extra_fields)[source]
Create and persist a standard (non-superuser)
Userinstance.Normalizes the email to lowercase and strips whitespace before saving. Raises
ValueErrorif the email is missing or not a valid UCLA address.- Parameters:
- Returns:
The newly created and saved
Userinstance.- Return type:
User
- Raises:
ValueError – If
emailis empty or not a recognized UCLA domain.
Example:
>>> user = User.objects.create_user('jdoe@ucla.edu', 'secret123')
- create_superuser(email, password=None, **extra_fields)[source]
Create and persist a superuser
Userinstance with admin privileges.Defaults
is_staff,is_superuser, androleto their administrative values before delegating tocreate_user().- Parameters:
- Returns:
The newly created superuser
Userinstance.- Return type:
User
- Raises:
ValueError – If
emailis empty or not a recognized UCLA domain.
- class users.models.User(*args, **kwargs)[source]
Bases:
AbstractBaseUser,PermissionsMixinCustom user model for the BruinBridge platform.
Replaces Django’s default
Userwith a UCLA-email-gated account that supports mentor/mentee matching, soft deletion, email verification, and password reset workflows. Authentication is performed viaemailinstead ofusername.- userID
Immutable public identifier for the user.
- Type:
UUIDField
Unique UCLA email address; used as the login credential.
- Type:
EmailField
- firstName
User’s given name.
- Type:
CharField
- lastName
User’s family name.
- Type:
CharField
- role
One of
MENTEE,MENTOR, orADMIN(seeRole).- Type:
CharField
- profilePictureUrl
URL pointing to the user’s avatar image.
- Type:
URLField
- year
Academic year (0–5).
- Type:
IntegerField
- major
List of declared majors.
- Type:
JSONField
- minor
List of declared minors.
- Type:
JSONField
- international
Flag for international student status.
- Type:
BooleanField
- commuter
Flag for commuter status.
- Type:
BooleanField
- firstgen
Flag for first-generation college student status.
- Type:
BooleanField
- outofstate
Flag for out-of-state student status.
- Type:
BooleanField
- transfer
Flag for transfer student status.
- Type:
BooleanField
- otherBackground
Free-text description of additional background.
- Type:
CharField
- hobbies
User-provided list of hobbies.
- Type:
CharField
- clubs
User-provided list of club memberships.
- Type:
CharField
- goals
User-provided academic or career goals.
- Type:
CharField
- isMatched
Whether the user currently has an active match.
- Type:
BooleanField
- matchedMentorEmail
Email of the mentor currently matched to this mentee.
- Type:
EmailField
- blacklisted_mentors
List of mentor emails the mentee has blocked.
- Type:
JSONField
- current_mentees
List of mentee emails currently assigned to this mentor.
- Type:
JSONField
- is_deleted
Soft-deletion flag; account is hidden but not yet purged.
- Type:
BooleanField
- deletion_requested_date
When the soft-deletion was requested.
- Type:
DateTimeField
- permanent_deletion_date
When the account will be hard-deleted.
- Type:
DateTimeField
- is_verified
Whether the user’s email address has been verified.
- Type:
BooleanField
- email_verification_token
Active one-time email verification token.
- Type:
CharField
- email_verification_sent_at
When the verification email was dispatched.
- Type:
DateTimeField
- password_reset_token
Active one-time password reset token.
- Type:
CharField
- password_reset_sent_at
When the password reset email was dispatched.
- Type:
DateTimeField
- is_active
Django internal;
Falsedisables login.- Type:
BooleanField
- is_staff
Django internal; grants admin-site access.
- Type:
BooleanField
- date_joined
Timestamp of account creation.
- Type:
DateTimeField
- class Role(*values)[source]
Bases:
TextChoicesEnumeration of valid user roles on the platform.
- Variables:
MENTEE – A student seeking guidance from a mentor.
MENTOR – A student providing guidance to mentees.
ADMIN – A platform administrator with elevated privileges.
- MENTEE = 'MENTEE'
- MENTOR = 'MENTOR'
- ADMIN = 'ADMIN'
- userID
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- firstName
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- lastName
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- role
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- profilePictureUrl
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- year
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- major
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- minor
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- international
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- commuter
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- firstgen
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- outofstate
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- transfer
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- otherBackground
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- hobbies
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- clubs
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- goals
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- isMatched
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- matchedMentorEmail
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- blacklisted_mentors
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- current_mentees
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- is_deleted
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- deletion_requested_date
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- permanent_deletion_date
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- is_verified
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- email_verification_token
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- email_verification_sent_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- password_reset_token
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- password_reset_sent_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- is_active
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- is_staff
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- date_joined
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- objects = <users.models.UserManager object>
- USERNAME_FIELD = 'email'
- REQUIRED_FIELDS = []
- clean()[source]
Normalize and validate model fields before saving.
Strips whitespace and lowercases
email, then asserts it belongs to a recognized UCLA domain.- Raises:
ValidationError – If
emailis not a valid UCLA address.
- save(*args, **kwargs)[source]
Run full model validation then persist the instance.
Calls
full_clean()before every save to ensure email normalization and UCLA domain constraints are always enforced.- Parameters:
args – Positional arguments forwarded to
super().save().kwargs – Keyword arguments forwarded to
super().save().
- Raises:
ValidationError – If any field fails validation in
clean().
- SURVEY_FIELDS = ('firstName', 'lastName', 'year', 'major', 'minor', 'hobbies', 'clubs', 'goals', 'otherBackground', 'international', 'commuter', 'firstgen', 'outofstate', 'transfer', 'profilePictureUrl')
Fields that may be set via
update_profile()orcomplete_survey(). Any field absent from this tuple will be silently ignored, protecting sensitive fields (e.g.email,password) from bulk updates.
- SAFE_ROLES = {User.Role.MENTEE, User.Role.MENTOR}
Roles that a user is permitted to self-assign.
ADMINis intentionally excluded.
- update_profile(**kwargs)[source]
Update permitted profile fields with the supplied values.
Only fields listed in
SURVEY_FIELDSare written. Role changes are accepted only forSAFE_ROLES(MENTOR/MENTEE); any attempt to assignADMINis silently dropped and logged as a warning.- Parameters:
kwargs – Mapping of field names to new values.
- Raises:
ValidationError – Propagated from
save()if a written value fails model-level validation.
Example:
>>> user.update_profile(firstName='Jane', year=2)
- complete_survey(survey_data)[source]
Persist the user’s initial onboarding survey responses.
Delegates entirely to
update_profile(), applying the same field whitelist and role-safety rules.- Parameters:
survey_data (dict) – Dictionary of survey field names to their submitted values.
- Raises:
ValidationError – Propagated from
update_profile()if a value fails model-level validation.
Note
This method may be redundant if
update_profile()already satisfies all survey update requirements.
- change_role(new_role)[source]
Switch the user’s role between
MENTEEandMENTOR.Before applying the role change, all existing matches, compatibility scores, and chat messages are cleaned up to prevent stale data from persisting across roles.
- Parameters:
new_role (str) – The target role. Must be
'MENTEE'or'MENTOR'.- Raises:
ValueError – If
new_roleis notMENTEEorMENTOR, or if the user already holdsnew_role.
Example:
>>> user.change_role('MENTOR')
- blacklist_mentor(mentor_email)[source]
Add a mentor’s email to this mentee’s blacklist.
Prevents the blacklisted mentor from being matched to this mentee in future matching runs. Does nothing if the mentor is already blacklisted.
- Parameters:
mentor_email (str) – The email address of the mentor to blacklist.
Example:
>>> mentee.blacklist_mentor('mentor@ucla.edu')
- add_mentee(mentee_email)[source]
Assign a mentee to this mentor’s active roster.
Performs the following checks before adding:
Caller must have the
MENTORrole.Mentor roster must not already be at capacity (
MAX_MENTEES).Mentee must not already be on the roster.
Mentee must exist in the database.
This mentor must not be on the mentee’s blacklist.
On success, updates
isMatchedfor both parties. The mentor’sisMatchedflag is set toTrueonly when the roster reaches full capacity.- Parameters:
mentee_email (str) – Email address of the mentee to add.
- Returns:
Trueif the mentee was successfully added,Falseif any pre-condition was not met.- Return type:
Example:
>>> mentor.add_mentee('mentee@g.ucla.edu') True
- remove_mentee(mentee_email)[source]
Remove a mentee from this mentor’s active roster.
Clears
isMatchedfor both the mentor and the removed mentee. Does nothing ifmentee_emailis not currently on the roster.- Parameters:
mentee_email (str) – Email address of the mentee to remove.
Example:
>>> mentor.remove_mentee('mentee@g.ucla.edu')
- request_deletion()[source]
Initiate a soft-delete of the account with a configurable grace period.
Sets
is_deletedtoTrueand schedulespermanent_deletion_dateasnow + ACCOUNT_RECOVERY_GRACE_PERIODhours (sourced fromsettings.ACCOUNT_RECOVERY_GRACE_PERIOD). Also unlinks all active matches, removes compatibility scores, and deletes chat history.The account remains recoverable via
cancel_deletion()untilpermanent_deletion_dateis reached.
- cancel_deletion()[source]
Restore a soft-deleted account within the grace period.
Clears
is_deleted,deletion_requested_date, andpermanent_deletion_date. ReturnsFalsewithout making changes if the grace period has already elapsed.- Returns:
Trueif the account was successfully restored,Falseif the grace period has expired.- Return type:
Example:
>>> user.cancel_deletion() True
- static permanently_delete_expired_accounts()[source]
Hard-delete all accounts whose grace period has elapsed.
Queries for
Userrecords whereis_deleted=Trueandpermanent_deletion_dateis in the past, marks each asis_active=False, then performs a hard database delete.Intended to be called periodically by a scheduled task (e.g. a Celery beat job or management command).
- Returns:
The number of accounts permanently deleted.
- Return type:
Example:
>>> deleted = User.permanently_delete_expired_accounts() >>> print(f'{deleted} accounts purged.')
- generate_verification_token()[source]
Generate, persist, and return a fresh email verification token.
Creates a UUID4 token, records the current timestamp as
email_verification_sent_at, and writes both fields to the database without triggering a full model save.- Returns:
The newly generated verification token string.
- Return type:
Example:
>>> token = user.generate_verification_token() >>> send_verification_email(user.email, token)
- verify_email(token)[source]
Validate an email verification token and mark the account as verified.
Checks that
tokenmatchesemail_verification_tokenand that the token was issued within the window defined bysettings.EMAIL_VERIFICATION_TOKEN_EXPIRATION(hours). On success, setsis_verified=Trueand clears both token fields.- Parameters:
token (str) – The verification token submitted by the user.
- Returns:
Trueif verification succeeded,Falseif the token was missing, mismatched, or expired.- Return type:
Example:
>>> user.verify_email('3f2e1a...') True
- generate_password_reset_token()[source]
Generate, persist, and return a fresh password reset token.
Creates a UUID4 token, records the current timestamp as
password_reset_sent_at, and writes both fields to the database without triggering a full model save.- Returns:
The newly generated password reset token string.
- Return type:
Example:
>>> token = user.generate_password_reset_token() >>> send_reset_email(user.email, token)
- reset_password_with_token(token, new_password)[source]
Validate a password reset token and apply a new password.
Checks that
tokenmatchespassword_reset_tokenand that the token was issued within the window defined bysettings.PASSWORD_RESET_TOKEN_EXPIRATION(hours). On success, hashes and savesnew_password, then clears both token fields.- Parameters:
- Returns:
Trueif the password was successfully reset,Falseif the token was missing, mismatched, or expired.- Return type:
Example:
>>> user.reset_password_with_token('a1b2c3...', 'newSecurePass!') True
- exception DoesNotExist
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned
Bases:
MultipleObjectsReturned
- exception NotUpdated
Bases:
ObjectNotUpdated,DatabaseError
- get_next_by_date_joined(*, field=<django.db.models.fields.DateTimeField: date_joined>, is_next=True, **kwargs)
- get_previous_by_date_joined(*, field=<django.db.models.fields.DateTimeField: date_joined>, is_next=False, **kwargs)
- get_role_display(*, field=<django.db.models.fields.CharField: role>)
- groups
Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.
In the example:
class Pizza(Model): toppings = ManyToManyField(Topping, related_name='pizzas')
Pizza.toppingsandTopping.pizzasareManyToManyDescriptorinstances.Most of the implementation is delegated to a dynamically defined manager class built by
create_forward_many_to_many_manager()defined below.
- id
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- is_superuser
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- last_login
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- logentry_set
Accessor to the related objects manager on the reverse side of a many-to-one relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Parent.childrenis aReverseManyToOneDescriptorinstance.Most of the implementation is delegated to a dynamically defined manager class built by
create_reverse_many_to_one_manager()defined below.
- password
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- user_permissions
Accessor to the related objects manager on the forward and reverse sides of a many-to-many relation.
In the example:
class Pizza(Model): toppings = ManyToManyField(Topping, related_name='pizzas')
Pizza.toppingsandTopping.pizzasareManyToManyDescriptorinstances.Most of the implementation is delegated to a dynamically defined manager class built by
create_forward_many_to_many_manager()defined below.
users.serializers module
- users.serializers.full_name(user)[source]
Derive a display name from a user’s
firstNameandlastNamefields.Strips whitespace from each component before joining. Falls back to
'Not provided'if both fields are blank or absent, ensuring the return value is always a non-empty string suitable for display.- Parameters:
user (users.models.User) – The user whose name is being formatted.
- Returns:
Full name string, or
'Not provided'if both name fields are empty.- Return type:
Example:
>>> full_name(user) # both fields set 'Jane Doe' >>> full_name(empty_user) # both fields blank 'Not provided'
- users.serializers.serialize_user(user)[source]
Serialize a
Userinstance into a standard API response dict.Includes core identity, academic profile, background flags, and matching state. Excludes sensitive fields such as
password, verification tokens, and password reset tokens.Note
If the
Usermodel fields change, update this function andserialize_user_with_deletion()accordingly.- Parameters:
user (users.models.User) – The user instance to serialize.
- Returns:
Dictionary of serialized user data safe for API responses.
- Return type:
Response shape:
{ "email": "jdoe@ucla.edu", "firstName": "Jane", "lastName": "Doe", "role": "MENTEE", "year": 2, "major": ["Computer Science"], "minor": [], "hobbies": "chess, hiking", "clubs": "ACM", "goals": "Become a software engineer", "international": false, "commuter": false, "firstgen": true, "outofstate": false, "transfer": false, "otherBackground": "", "isMatched": false, "profilePictureUrl": "https://..." }
- users.serializers.serialize_user_with_deletion(user)[source]
Serialize a
Userinstance including soft-deletion metadata.Extends
serialize_user()withis_deletedandpermanent_deletion_date, intended for responses where the client needs to display a deletion warning or countdown — for example, the login response when an account is within its grace period.permanent_deletion_dateis serialized as an ISO 8601 string when present, ornullif the date has not been set.- Parameters:
user (users.models.User) – The soft-deleted (or deletion-pending) user instance to serialize.
- Returns:
Dictionary of serialized user data including deletion fields.
- Return type:
Additional fields beyond
serialize_user():{ "is_deleted": true, "permanent_deletion_date": "2024-01-15T10:30:00+00:00" }
Example:
>>> serialize_user_with_deletion(pending_user) { "email": "jdoe@ucla.edu", ..., "is_deleted": True, "permanent_deletion_date": "2024-01-15T10:30:00+00:00" }
users.services module
- exception users.services.UserServiceError(message, status=400)[source]
Bases:
ExceptionBase exception for all user service layer errors.
Carries an HTTP
statuscode alongside the message so that views can return the appropriate response status without additional branching logic.- Parameters:
Example:
raise UserServiceError('Something went wrong', status=400)
- exception users.services.NotFound(message='User not found')[source]
Bases:
UserServiceErrorRaised when a requested user account cannot be found.
Specialization of
UserServiceErrorthat always setsstatus=404.- Parameters:
message (str) – Human-readable description of the missing resource. Defaults to
'User not found'.
Example:
raise NotFound('User not found')
- exception users.services.Conflict(message)[source]
Bases:
UserServiceErrorRaised when a user operation conflicts with the current system state.
Specialization of
UserServiceErrorthat always setsstatus=409. Typical use case: attempting to register with an email address that is already in use.- Parameters:
message (str) – Human-readable description of the conflict.
Example:
raise Conflict('Email already exists')
- exception users.services.Unauthorized(message='Invalid credentials')[source]
Bases:
UserServiceErrorRaised when authentication credentials are missing or invalid.
Specialization of
UserServiceErrorthat always setsstatus=401.- Parameters:
message (str) – Human-readable description of the auth failure. Defaults to
'Invalid credentials'.
Example:
raise Unauthorized()
- exception users.services.Forbidden(message)[source]
Bases:
UserServiceErrorRaised when an authenticated user attempts an action they are not permitted to perform.
Specialization of
UserServiceErrorthat always setsstatus=403.- Parameters:
message (str) – Human-readable description of the authorization failure.
Example:
raise Forbidden('Email not verified')
- users.services.register_user(email, password, role='MENTEE')[source]
Create a new user account and dispatch a verification email.
Handles the following edge cases before creating the account:
If an account with
emailalready exists and its grace period has expired, it is hard-deleted and registration proceeds.If an account exists and is within its grace period (or is not soft-deleted at all), a
Conflictis raised.
In test mode (
settings.SEND_EMAILS = False) the account is automatically marked as verified and no email is sent. In production, a verification token is generated and dispatched viasend_verification_email(). A failed email send is logged as a warning but does not prevent the account from being created.- Parameters:
- Returns:
The newly created
Userinstance.- Return type:
users.models.User
- Raises:
Conflict – If a non-expired account already exists for
email(HTTP 409).ValueError – Propagated from
create_user()ifemailis not a valid UCLA address.
Example:
>>> user = register_user('jdoe@ucla.edu', 'secret123', role='MENTOR')
- users.services.authenticate_user(request, email, password)[source]
Verify credentials and return the authenticated user.
Delegates to Django’s
authenticate()backend, then enforces an additional email-verification check. Does not open a session — callers are responsible for callinglogin()if a session is required.- Parameters:
request (django.http.HttpRequest) – The current HTTP request, forwarded to Django’s auth backend.
email (str) – The user’s UCLA email address.
password (str) – The user’s plain-text password.
- Returns:
The authenticated
Userinstance.- Return type:
users.models.User
- Raises:
Unauthorized – If Django’s auth backend returns
None, indicating invalid credentials (HTTP 401).Forbidden – If the account exists but the email address has not yet been verified (HTTP 403).
Example:
>>> user = authenticate_user(request, 'jdoe@ucla.edu', 'secret123')
- users.services.complete_survey(user, survey_data)[source]
Apply onboarding survey responses to a user’s profile.
Thin delegation to
complete_survey(), which enforces the field whitelist and role-safety rules defined on the model.- Parameters:
user (users.models.User) – The user completing the survey.
survey_data (dict) – Dictionary of survey field names to their submitted values.
- Raises:
ValidationError – Propagated from the model if any submitted value fails field-level validation.
Example:
>>> complete_survey(user, {'firstName': 'Jane', 'year': 1})
- users.services.request_deletion(user, confirmation_text)[source]
Initiate a soft-delete of the user’s account after confirming intent.
Requires the caller to supply the user’s own email address as
confirmation_text. On success, delegates torequest_deletion(), which sets the grace period and cleans up matches, scores, and chat messages.- Parameters:
user (users.models.User) – The user requesting deletion.
confirmation_text (str) – Text submitted by the user to confirm intent. Must exactly match
user.email.
- Raises:
UserServiceError – If
confirmation_textdoes not matchuser.email(HTTP 400).
Example:
>>> request_deletion(user, 'jdoe@ucla.edu')
- users.services.cancel_deletion(user)[source]
Cancel a pending account deletion for an already-authenticated user.
Verifies that a deletion is actually pending before delegating to
cancel_deletion().- Parameters:
user (users.models.User) – The authenticated user cancelling their deletion request.
- Raises:
UserServiceError – If the account is not currently pending deletion (HTTP 400), or if the grace period has already elapsed (HTTP 409).
Example:
>>> cancel_deletion(request.user)
- users.services.cancel_deletion_by_credentials(request, email, password)[source]
Authenticate by credentials and cancel a pending account deletion.
Intended for users who have been logged out but want to recover their account within the grace period. Authenticates via
authenticate_user()then applies the same deletion-state checks ascancel_deletion().- Parameters:
request (django.http.HttpRequest) – The current HTTP request, forwarded to the auth backend.
email (str) – The user’s UCLA email address.
password (str) – The user’s plain-text password.
- Returns:
The recovered
Userinstance with deletion fields cleared.- Return type:
users.models.User
- Raises:
Unauthorized – If credentials are invalid (HTTP 401).
Forbidden – If the email address is unverified (HTTP 403).
UserServiceError – If no deletion is pending (HTTP 400), or if the grace period has already elapsed (HTTP 409).
Example:
>>> user = cancel_deletion_by_credentials(request, 'jdoe@ucla.edu', 'secret123')
users.test_integration module
Integration tests for user authentication and profile management flows. Tests multiple components working together: models, services, views, database.
Run with: python manage.py test users.test_integration
- class users.test_integration.AuthenticationFlowIntegrationTests(methodName='runTest')[source]
Bases:
TestCaseTest complete authentication flows from registration to login
- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_complete_registration_and_login_flow()[source]
Integration test: Register -> Auto-verify (test mode) -> Login -> Access profile Tests: services.py, models.py, views.py, authentication backend
- test_unverified_user_cannot_login()[source]
Integration test: Register -> Don’t verify -> Try login -> Blocked Tests: Email verification enforcement across services and views
- class users.test_integration.ProfileManagementIntegrationTests(methodName='runTest')[source]
Bases:
TestCaseTest profile CRUD operations across multiple layers
- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_complete_profile_update_flow()[source]
Integration test: Login -> Update profile -> Verify changes -> Check matching eligibility Tests: Authentication, profile updates, scoring triggers
- test_security_role_change_blocked_via_api()[source]
Integration test: Attempt ADMIN escalation via API -> Verify blocked Tests: End-to-end security from API to database
- test_security_role_change_blocked_via_api_2()[source]
Integration test: Attempt ADMIN escalation via API -> Verify blocked Tests: End-to-end security from API to database
- class users.test_integration.AccountDeletionIntegrationTests(methodName='runTest')[source]
Bases:
TestCaseTest complete account deletion workflows
- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_deletion_request_and_cancellation_flow()[source]
Integration test: Request deletion -> Cancel -> Verify restored Tests: Deletion service, API, database state management
- test_expired_account_cleanup_and_reregistration()[source]
Integration test: Delete account -> Expire -> Garbage collect -> Re-register Tests: Deletion lifecycle, cleanup service, registration validation
- class users.test_integration.RoleChangeIntegrationTests(methodName='runTest')[source]
Bases:
TestCaseTest role changes and their cascading effects
- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_role_change_cleans_up_relationships()[source]
Integration test: Create match -> Change role -> Verify cleanup Tests: Matching system, role change service, database cascades
users.tests module
- class users.tests.UserModelTests(methodName='runTest')[source]
Bases:
TestCase- test_create_user()[source]
Verifies user creation
- test_user_requires_email()[source]
Ensures email is required
- test_user_requires_valid_ucla_email()[source]
Validates UCLA email domain
- class users.tests.UserMatchingLogicTests(methodName='runTest')[source]
Bases:
TestCase- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_max_mentee_limit()[source]
Ensures a mentor cannot exceed the MAX_MENTEES limit
- test_blacklisted_mentor_rejection()[source]
Ensures a mentor cannot add a mentee who blacklisted them
- class users.tests.UserProfileSecurityTests(methodName='runTest')[source]
Bases:
TestCase- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_update_profile_ignores_protected_fields()[source]
Ensures users cannot overwrite protected fields like isMatched via profile updates
- test_complete_survey_has_same_protections()[source]
complete_survey() should have same security as update_profile()
- test_update_profile_allows_mentor_mentee_role_change()[source]
Users can switch between MENTOR and MENTEE
- test_change_role_blocks_same_role()[source]
change_role() prevents no-op changes
- class users.tests.AdminCreationTests(methodName='runTest')[source]
Bases:
TestCaseEnsure ADMIN users can only be created via safe methods
- test_admin_creation_via_create_superuser()[source]
Admins should be created via create_superuser
- test_cannot_become_admin_via_profile_update()[source]
Check whether there is a loophole to become ADMIN
- class users.tests.UserServiceTests(methodName='runTest')[source]
Bases:
TestCase- test_register_user_service_success()[source]
Verifies the service creates a user and hashes the password
- test_register_duplicate_email_raises_conflict()[source]
Ensures registering an existing email throws the correct Conflict error
- class users.tests.RegistrationTests(methodName='runTest')[source]
Bases:
TestCase- test_register_while_in_grace_period()[source]
Test that you can’t register during grace period
- test_register_past_grace_period()[source]
Test that you can re-register after account expiration
- class users.tests.UserAccountDeletionTests(methodName='runTest')[source]
Bases:
TestCase- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_request_deletion_flags_account()[source]
Ensures requesting deletion sets flags but doesn’t hard-delete immediately
- test_cancel_deletion_restores_account()[source]
Ensures a user can cancel their deletion within the grace period
- class users.tests.UserAPIClientTests(methodName='runTest')[source]
Bases:
TestCase- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_unauthenticated_user_access_denied()[source]
Ensures an unauthenticated browser cannot access the profile endpoint
- test_authenticated_user_access_granted()[source]
Ensures a logged-in user can access their own profile data
- class users.tests.UserRoleChangeTests(methodName='runTest')[source]
Bases:
TestCase- setUp()[source]
Hook method for setting up the test fixture before exercising it.
- test_change_role_wipes_matches()[source]
Ensures that changing a role completely cleans up existing relationships
users.urls module
users.views module
- users.views.register(request)[source]
Register a new user account.
Parses
email,password, and optionalrolefrom the request body, delegates creation toservices.register_user(), and returns the new account’s identifiers. A verification email is dispatched as a side-effect of the service call; the account cannot be used until the email address is confirmed.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with
email,password, and optionallyrole.- Returns:
JSON response with
userID,email,role, and a reminder to verify the account (HTTP 201), or an error payload (HTTP 400).- Return type:
- Raises:
UserServiceError – Caught internally; returns the error message and its associated HTTP status code.
Request body (JSON):
{ "email": "jdoe@ucla.edu", "password": "secret123", "role": "MENTEE" // optional, defaults to "MENTEE" }
Success response (HTTP 201):
{ "message": "Registration successful. Please check your email to verify account.", "userID": "550e8400-e29b-...", "email": "jdoe@ucla.edu", "role": "MENTEE", "note": "You must verify your email before logging in." }
- users.views.user_login(request)[source]
Authenticate a user and open a session.
Validates credentials via
services.authenticate_user(), enforces email-verification and soft-deletion guards, then calls Django’slogin()to establish a session. Also computesprofile_completebased on whether the user has filled in their core profile fields.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with
emailandpassword.- Returns:
HTTP 200 with serialized user data and
profile_completeflag on success.HTTP 200 with deletion metadata if the account is pending deletion but still within the grace period.
HTTP 403 if the email address has not yet been verified.
HTTP 404 if the account’s grace period has expired and it has been purged.
HTTP 400 for missing fields or unexpected errors.
- Return type:
- Raises:
UserServiceError – Caught internally; returns the error message and its associated HTTP status code.
Request body (JSON):
{ "email": "jdoe@ucla.edu", "password": "secret123" }
Success response (HTTP 200):
{ "message": "Login successful", "profile_complete": true, // ...serialized user fields }
- users.views.user_logout(request)[source]
Terminate the current user session.
Calls Django’s
logout()to flush the session, regardless of whether the caller is authenticated.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request.
- Returns:
JSON confirmation message (HTTP 200).
- Return type:
- users.views.get_current_user(request)[source]
Return the profile of the currently authenticated user.
Computes
profile_completebased on whether core profile fields have been populated. Returns deletion metadata instead of the standard payload when the account is pending deletion.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP GET request. Must have an authenticated session.
- Returns:
HTTP 200 with serialized user data and
profile_completeon success.HTTP 200 with deletion metadata if the account is pending deletion.
HTTP 401 if the request is unauthenticated.
- Return type:
Success response (HTTP 200):
{ "profile_complete": true, // ...serialized user fields }
- users.views.update_profile(request)[source]
Update the authenticated user’s profile fields.
Accepts any subset of the fields permitted by
User.SURVEY_FIELDS. Protected fields are silently ignored by the model layer.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP PUT request containing a JSON body with one or more profile field key-value pairs.
- Returns:
HTTP 200 with a success message on update.
HTTP 400 if the request body is not valid JSON.
HTTP 401 if the request is unauthenticated.
- Return type:
Request body (JSON):
{ "firstName": "Jane", "year": 2, "major": ["Computer Science"] }
- users.views.complete_survey(request)[source]
Submit the authenticated user’s initial onboarding survey.
Delegates to
services.complete_survey(), which applies the same field-whitelist rules asupdate_profile().- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with survey field key-value pairs.
- Returns:
HTTP 200 with a success message on completion.
HTTP 400 for unexpected errors.
HTTP 401 if the request is unauthenticated.
- Return type:
Request body (JSON):
{ "firstName": "Jane", "lastName": "Doe", "year": 1, "major": ["Biology"], "international": false }
- users.views.request_account_deletion(request)[source]
Initiate a soft-delete of the authenticated user’s account.
Requires the caller to supply a confirmation string in the request body. The account is not immediately removed; a grace period is applied during which the user may cancel via
cancel_account_deletion().- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with a
confirmationstring.- Returns:
HTTP 200 with
permanent_deletion_dateon success.HTTP 400 for unexpected errors.
HTTP 401 if the request is unauthenticated.
Appropriate error status from
UserServiceErrorif the confirmation text is invalid.
- Return type:
- Raises:
UserServiceError – Caught internally; returns the error message and its associated HTTP status code.
Request body (JSON):
{ "confirmation": "delete my account" }
Success response (HTTP 200):
{ "message": "Account deletion requested", "permanent_deletion_date": "2024-01-01T12:00:00+00:00", "note": "Your account will be permanently deleted in 1 hour. You can cancel before then." }
- users.views.cancel_account_deletion(request)[source]
Cancel a pending account deletion for the currently authenticated user.
Delegates to
services.cancel_deletion(). The request will fail if the grace period has already elapsed. Usecancel_account_deletion_public()instead when the caller does not have an active session.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request. No body is required.
- Returns:
HTTP 200 with a success message on cancellation.
HTTP 401 if the request is unauthenticated.
Appropriate error status from
UserServiceErrorif the grace period has expired.
- Return type:
- Raises:
UserServiceError – Caught internally; returns the error message and its associated HTTP status code.
- users.views.cancel_account_deletion_public(request)[source]
Cancel a pending account deletion without an active session.
Accepts credentials in the request body and authenticates the user before delegating to
services.cancel_deletion_by_credentials(). Intended for users who have been logged out but still want to recover their account within the grace period.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with
emailandpassword.- Returns:
HTTP 200 with
emailandis_activeon success.HTTP 400 if
emailorpasswordis missing, or for unexpected errors.Appropriate error status from
UserServiceErroron auth failure or expired grace period.
- Return type:
- Raises:
UserServiceError – Caught internally; returns the error message and its associated HTTP status code.
Request body (JSON):
{ "email": "jdoe@ucla.edu", "password": "secret123" }
- users.views.verify_email(request, token)[source]
Verify a user’s email address using a one-time token.
Looks up the
Userwhoseemail_verification_tokenmatches the suppliedtokenpath parameter, then delegates toUser.verify_email(). Returns an appropriate response if the account is already verified or if the token has expired.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP GET request.
token (str) – The UUID verification token embedded in the confirmation link.
- Returns:
HTTP 200 with a success message and
emailon successful verification.HTTP 200 with
'Email already verified'if the account was already confirmed.HTTP 400 if the token is invalid or has expired.
- Return type:
Success response (HTTP 200):
{ "message": "Email verified successfully. You can now log in.", "email": "jdoe@ucla.edu" }
- users.views.resend_verification(request)[source]
Resend the email verification link to a user.
Looks up the account by
emailand issues a new verification token viaUser.generate_verification_token(). To prevent email enumeration, the response is identical whether or not the address exists in the system.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with
email.- Returns:
HTTP 200 with
'Verification email sent'on dispatch.HTTP 200 with a non-committal message if the email is not found (prevents enumeration).
HTTP 200 with
'Email already verified'if the account is already confirmed.HTTP 400 if
emailis missing or an unexpected error occurs.
- Return type:
Request body (JSON):
{ "email": "jdoe@ucla.edu" }
- users.views.change_role(request)[source]
Switch the authenticated user’s role between
MENTEEandMENTOR.All existing matches, compatibility scores, and chat messages are cleared before the role transition is applied (delegated to
User.change_role()).- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with
new_role.- Returns:
HTTP 200 with a success message and serialized user data on success.
HTTP 400 if
new_roleis missing, the JSON is invalid, or the role transition is not permitted.HTTP 401 if the request is unauthenticated.
- Return type:
Request body (JSON):
{ "new_role": "MENTOR" }
- users.views.upload_profile_picture(request)[source]
Upload and store a profile picture for the authenticated user.
Sends the supplied file to Cloudinary with a 400×400 face-crop transformation and returns the resulting secure URL. The caller is responsible for subsequently persisting the URL to the user’s profile via
update_profile().- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request with a
multipart/form-databody containing afilefield.- Returns:
HTTP 200 with
urlpointing to the uploaded image on success.HTTP 400 if no file is provided.
HTTP 401 if the request is unauthenticated.
- Return type:
Success response (HTTP 200):
{ "url": "https://res.cloudinary.com/.../profile_pictures/abc123.jpg" }
- users.views.request_password_reset(request)[source]
Dispatch a password reset email to the specified address.
Generates a one-time token via
User.generate_password_reset_token()and sends it viasend_password_reset_email(). To prevent email enumeration, the response is identical whether or not the address exists.- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with
email.- Returns:
HTTP 200 with a non-committal message regardless of whether the email exists (prevents enumeration).
HTTP 400 if
emailis missing or an unexpected error occurs.
- Return type:
Request body (JSON):
{ "email": "jdoe@ucla.edu" }
- users.views.reset_password(request, token)[source]
Reset a user’s password using a one-time token.
Looks up the
Userwhosepassword_reset_tokenmatches the suppliedtokenpath parameter, then delegates toUser.reset_password_with_token().- Parameters:
request (django.http.HttpRequest) – The incoming HTTP POST request containing a JSON body with
new_password.token (str) – The UUID password reset token embedded in the reset link.
- Returns:
HTTP 200 with a success message on reset.
HTTP 400 if
new_passwordis missing, the token is invalid, the token has expired, or an unexpected error occurs.
- Return type:
Request body (JSON):
{ "new_password": "newSecurePass!" }
Success response (HTTP 200):
{ "message": "Password reset successfully. You can now log in." }
Module contents
- show-inheritance:
- undoc-members: