Locations
Location Permissions
Normal Access
Location Types - Users who can edit apps on the domain can edit location types. Locations - There is an “edit_locations” and a “view_locations” permission.
Restricted Access and Whitelist
Many large projects have mid-level users who should have access to a subset of the project based on the organization’s hierarchy.
This is handled by a special permission called “Full Organization Access” which is enabled by default on all user roles. To restrict data access based on a user’s location, projects may create a user role with this permission disabled.
This is checked like so:
user.has_permission(domain, 'access_all_locations')
We have whitelisted portions of HQ that have been made to correctly handle these restricted users. Anything not explicitly whitelisted is inaccessible to restricted users.
How data is associated with locations
Restricted users only have access to their section of the hierarchy. Here’s a little about what that means conceptually, and how to implement these restrictions.
Locations: Restricted users should be able to see and edit their own locations
and any descendants of those locations, as well as access data at those locations. See
also user_can_access_location_id
Users: If a user is assigned to an accessible location, the user is also
accessible. See also user_can_access_other_user
Groups: Groups are never accessible.
Forms: Forms are associated with a location via the submitting user, so if that
user is currently accessible, so is the form. Note that this means that moving
a user will affect forms even retroactively. See also can_edit_form_location
Cases: Case accessibility is determined by case owner. If the owner is a user,
then the user must be accessible for the case to be accessible. If the owner is
a location, then it must be accessible. If the owner is a case-sharing group,
the case is not accessible to any restricted users. See also
user_can_access_case
The SQLLocation
queryset method accessible_to_user
is helpful when
implementing these restrictions. Also refer to the standard reports, which do
this sort of filtering in bulk.
Whitelist Implementation
There is LocationAccessMiddleware
which controls this whitelist. It
intercepts every request, checks if the user has restricted access to the
domain, and if so, only allows requests to whitelisted views. This middleware
also guarantees that restricted users have a location assigned. That is, if a
user should be restricted, but does not have an assigned location, they can’t
see anything. This is to prevent users from obtaining full access in the event
that their location is deleted or improperly assigned.
The other component of this is uitabs. The menu bar and the sidebar on HQ are composed of a bunch of links and names, essentially. We run the url for each of these links against the same check that the middleware uses to see if it should be visible to the user. In this way, they only see menu and sidebar links that are accessible.
To mark a view as location safe, you apply the @location_safe
decorator to
it. This can be applied directly to view functions, view classes, HQ report
classes, or tastypie resources (see implentation and existing usages for
examples).
UCR and Report Builder reports will be automatically marked as location safe if
the report contains a location choice provider. This is done using the
conditionally_location_safe
decorator, which is provided with a function that
in this case checks that the report has at least one location choice provider.
When marking a view as location safe, you must also check for restricted users
by using either request.can_access_all_locations
or
user.has_permission(domain, 'access_all_locations')
and limit the data
returned accordingly.
You should create a user who is restricted and click through the desired workflow to make sure it still makes sense, there could be for instance, ajax requests that must also be protected, or links to features the user shouldn’t see.