15.2.172. camcops_server.cc_modules.cc_user

camcops_server/cc_modules/cc_user.py


Copyright (C) 2012, University of Cambridge, Department of Psychiatry. Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

This file is part of CamCOPS.

CamCOPS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

CamCOPS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.


CamCOPS users.

class camcops_server.cc_modules.cc_user.SecurityAccountLockout(**kwargs)[source]

Represents an account “lockout”.

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

classmethod delete_old_account_lockouts(req: CamcopsRequest) None[source]

Delete all expired account lockouts.

classmethod is_user_locked_out(req: CamcopsRequest, username: str) bool[source]

Is the specified user locked out?

Parameters
classmethod lock_user_out(req: CamcopsRequest, username: str, lockout_minutes: int) None[source]

Lock user out for a specified number of minutes.

Parameters
classmethod unlock_user(req: CamcopsRequest, username: str) None[source]

Unlock a user.

Parameters
classmethod user_locked_out_until(req: CamcopsRequest, username: str) Optional[pendulum.datetime.DateTime][source]

When is the user locked out until?

Parameters
Returns

Pendulum datetime in local timezone (or None if not locked out).

class camcops_server.cc_modules.cc_user.SecurityLoginFailure(**kwargs)[source]

Represents a record of a failed login.

Too many failed logins lead to a lockout; see SecurityAccountLockout.

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

classmethod act_on_login_failure(req: CamcopsRequest, username: str) None[source]

Record login failure and lock out user if necessary.

Parameters
classmethod clear_dummy_login_failures_if_necessary(req: CamcopsRequest) None[source]

Clear dummy login failures if we haven’t done so for a while.

Not too often! See CLEAR_DUMMY_LOGIN_PERIOD.

Parameters

reqcamcops_server.cc_modules.cc_request.CamcopsRequest

classmethod clear_login_failures(req: CamcopsRequest, username: str) None[source]

Clear login failures for a user.

Parameters
classmethod clear_login_failures_for_nonexistent_users(req: CamcopsRequest) None[source]

Clear login failures for nonexistent users.

Login failues are recorded for nonexistent users to mimic the lockout seen for real users, i.e. to reduce the potential for username discovery.

Parameters

reqcamcops_server.cc_modules.cc_request.CamcopsRequest

classmethod enable_user(req: CamcopsRequest, username: str) None[source]

Unlock user and clear login failures.

Parameters
classmethod how_many_login_failures(req: CamcopsRequest, username: str) int[source]

How many times has the user tried and failed to log in (recently)?

Parameters
classmethod record_login_failure(req: CamcopsRequest, username: str) None[source]

Record that a user has failed to log in.

Parameters
class camcops_server.cc_modules.cc_user.User(**kwargs)[source]

Class representing a user.

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

agree_terms(req: CamcopsRequest) None[source]

Mark the user as having agreed to the terms/conditions of use now.

property authorized_as_groupadmin: bool

Is the user authorized as a group administrator for any group (either by being specifically set as a group administrator, or by being a superuser)?

property authorized_for_reports: bool

Is the user authorized to run reports (for some group)?

authorized_to_add_special_note(group_id: int) bool[source]

Is this user authorized to add special notes for the group identified by group_id?

property authorized_to_dump: bool

Is the user authorized to dump data (for some group)?

property authorized_to_email_patients: bool

Is the user authorized to send emails to patients (for some group)?

authorized_to_erase_tasks(group_id: int) bool[source]

Is this user authorized to erase tasks for the group identified by group_id?

property authorized_to_manage_patients: bool

Is the user authorized to manage patients (for some group)?

clear_login_failures(req: CamcopsRequest) None[source]

Clear login failures.

classmethod create_superuser(req: CamcopsRequest, username: str, password: str) bool[source]

Creates a superuser.

Will fail if the user already exists.

Parameters
Returns

success?

enable(req: CamcopsRequest) None[source]

Re-enables the user, unlocking them and clearing login failures.

ensure_mfa_info() None[source]

If for some reason we have lost aspects of our MFA information, reset it. This step also ensures that anything erroneous in the database is cleaned to a valid value.

force_password_change() None[source]

Make the user change their password at next login.

classmethod get_system_user(dbsession: sqlalchemy.orm.session.Session) camcops_server.cc_modules.cc_user.User[source]

Returns a user representing “command-line access”.

classmethod get_user_by_id(dbsession: sqlalchemy.orm.session.Session, user_id: Optional[int]) Optional[camcops_server.cc_modules.cc_user.User][source]

Returns a User from their integer ID, or None.

classmethod get_user_by_name(dbsession: sqlalchemy.orm.session.Session, username: str) Optional[camcops_server.cc_modules.cc_user.User][source]

Returns a User from their username, or None.

classmethod get_user_from_username_password(req: CamcopsRequest, username: str, password: str, take_time_for_nonexistent_user: bool = True) Optional[User][source]

Retrieve a User object from the supplied username, if the password is correct; otherwise, return None.

Parameters
  • reqcamcops_server.cc_modules.cc_request.CamcopsRequest

  • username – the username

  • password – the password attempt

  • take_time_for_nonexistent_user – if True (the default), then even if the user doesn’t exist, we take some time to mimic the time we spend doing deliberately wasteful password encryption (to prevent attackers from discovering real usernames via timing attacks).

classmethod get_username_from_id(req: CamcopsRequest, user_id: int) Optional[str][source]

Looks up a user from their integer ID and returns their name, if found.

property group_ids: List[int]

Return a list of group IDs for all the groups that the user is a member of.

group_ids_nonsuperuser_may_see_when_unfiltered() List[int][source]

Which group IDs may this user see all patients for, when unfiltered?

property group_names: List[str]

Returns a list of group names for all the groups that the user is a member of.

property groups_user_is_admin_for: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user is an administrator for.

Less efficient than the group ID version; for visual display (see view_own_user_info.mako).

property groups_user_may_add_special_notes: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user can add special notes to.

For visual display (see view_own_user_info.mako).

property groups_user_may_dump: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user can dump.

For security notes, see ids_of_groups_user_may_dump().

Less efficient than the group ID version (see ids_of_groups_user_may_dump()). This version is for visual display (see view_own_user_info.mako).

property groups_user_may_email_patients_in: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user may send emails to patients in.

property groups_user_may_manage_patients_in: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user may manage patients in.

property groups_user_may_report_on: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user can report on.

For security notes, see ids_of_groups_user_may_report_on().

Less efficient than the group ID version (see ids_of_groups_user_may_report_on()). This version is for visual display (see view_own_user_info.mako).

property groups_user_may_see: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user can see.

Less efficient than the group ID version; for visual display (see view_own_user_info.mako).

property groups_user_may_see_all_pts_when_unfiltered: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user can see all patients when unfiltered.

For visual display (see view_own_user_info.mako).

property groups_user_may_upload_into: List[camcops_server.cc_modules.cc_group.Group]

Returns a list of camcops_server.cc_modules.cc_group.Group objects for groups the user can upload into.

For visual display (see view_own_user_info.mako).

property ids_of_groups_user_is_admin_for: List[int]

Returns a list of group IDs for groups that the user is an administrator for.

property ids_of_groups_user_may_dump: List[int]

Return a list of group IDs for groups that the user may dump data from.

See also groups_user_may_dump().

This does not give “second-hand authority” to dump. For example, if group G1 can “see” G2, and user U has authority to dump G1, that authority does not extend to G2.

property ids_of_groups_user_may_email_patients_in: List[int]

Returns a list of group IDs for groups that the user may send emails to patients in

property ids_of_groups_user_may_manage_patients_in: List[int]

Returns a list of group IDs for groups that the user may add/edit/delete patients in

property ids_of_groups_user_may_report_on: List[int]

Returns a list of group IDs for groups that the user may run reports on.

This does not give “second-hand authority” to dump. For example, if group G1 can “see” G2, and user U has authority to report on G1, that authority does not extend to G2.

property ids_of_groups_user_may_see: List[int]

Return a list of group IDs for groups that the user may see data from. (That means the groups the user is in, plus any other groups that the user’s groups are authorized to see.)

property is_a_groupadmin: bool

Is the user a specifically defined group administrator (for any group)?

is_locked_out(req: CamcopsRequest) bool[source]

Is the user locked out because of multiple login failures?

is_password_correct(password: str) bool[source]

Is the supplied password valid for this user?

static is_username_permissible(username: str) bool[source]

Is this a permissible username?

locked_out_until(req: CamcopsRequest) Optional[pendulum.datetime.DateTime][source]

When is the user locked out until?

Returns a Pendulum datetime in local timezone (or None if the user isn’t locked out).

login(req: CamcopsRequest) None[source]

Called when the framework has determined a successful login.

Clears any login failures. Requires the user to change their password if policies say they should.

managed_users() Optional[sqlalchemy.orm.query.Query][source]

Return a query for all users managed by this user.

LOGIC SHOULD MATCH may_edit_user().

may_administer_group(group_id: int) bool[source]

May this user administer the group identified by group_id?

may_edit_user(req: CamcopsRequest, other: User) Tuple[bool, str][source]

May the self user edit the other user?

Parameters
Returns

may_edit (bool), reason_why_not (str)

Return type

tuple

LOGIC SHOULD MATCH managed_users().

may_email_patients_in_group(group_id: int) bool[source]

May this user send emails to patients in the group identified by group_id?

property may_login_as_tablet: bool

May the user login via the client (tablet) API?

may_manage_patients_in_group(group_id: int) bool[source]

May this user manage patients in the group identified by group_id?

property may_register_devices: bool

May this user register devices?

You can register a device if your chosen upload groups allow you to do so. (You have to have a chosen group – even for superusers – because the tablet wants group ID policies at the moment of registration, so we have to know which group.)

property may_upload: bool

May this user upload to the group that is set as their upload group?

may_upload_to_group(group_id: int) bool[source]

May this user upload to the specified group?

property may_use_webviewer: bool

May this user log in to the web front end?

property may_view_all_patients_when_unfiltered: bool

May the user view all patients when no filters are applied (for all groups that the user is a member of)?

property may_view_no_patients_when_unfiltered: bool

May the user view no patients when no filters are applied?

membership_for_group_id(group_id: int) camcops_server.cc_modules.cc_membership.UserGroupMembership[source]

Returns the UserGroupMembership object relating this user to the group identified by group_id.

property must_agree_terms: bool

Does the user still need to agree the terms/conditions of use?

must_set_mfa_method(req: CamcopsRequest) bool[source]

Does the user still need to select a (valid) multi-factor authentication method? We are happy if the user has selected a method that is approved in the current config.

property names_of_groups_user_is_admin_for: List[str]

Returns a list of group names for groups that the user is an administrator for.

property names_of_groups_user_is_admin_for_csv: str

Returns a list of group names for groups that the user is an administrator for.

property partial_email: str

Returns a partially obscured version of the user’s e-mail address.

There doesn’t seem to be an agreed way of doing this. Here we show the first and last letter of the “local-part” (see https://en.wikipedia.org/wiki/Email_address), separated by asterisks. If the local part is a single letter, it’s shown twice.

property partial_phone_number: str

Returns a partially obscured version of the user’s phone number.

There doesn’t seem to be an agreed way of doing this either. https://www.karansaini.com/fuzzing-obfuscated-phone-numbers/

property raw_phone_number: str

Returns the user’s phone number in E164 format: https://en.wikipedia.org/wiki/E.164

set_group_ids(group_ids: List[int]) None[source]

Set the user’s groups to the groups whose integer IDs are in the group_ids list, and remove the user from any other groups.

set_mfa_method(mfa_method: str) None[source]

Resets the multi-factor authentication (MFA) method.

set_password(req: CamcopsRequest, new_password: str) None[source]

Set a user’s password.

set_password_change_flag_if_necessary(req: CamcopsRequest) None[source]

If we’re requiring users to change their passwords, then check to see if they must do so now.

static take_some_time_mimicking_password_encryption() None[source]

Waste some time. We use this when an attempt has been made to log in with a nonexistent user; we know the user doesn’t exist very quickly, but we mimic the time it takes to check a real user’s password.

classmethod user_exists(req: CamcopsRequest, username: str) bool[source]

Does a user exist with this username?

verify_one_time_password(one_time_password: str) bool[source]

Determines whether the supplied one-time password is valid for the multi-factor authentication (MFA) currently selected.

Returns False if no MFA method is selected.

camcops_server.cc_modules.cc_user.set_password_directly(req: CamcopsRequest, username: str, password: str) bool[source]

If the user exists, set its password. Returns Boolean success. Used from the command line.