Contribution levels and record access#
This page lists the classes, functions and querysets that turn a contribution level into a permission answer on a project, dataset, sample or measurement: ContributionLevel, REQUIRED_LEVEL, RecordAccess, RecordLevelBackend, the queryset methods with_level, accessible_to and visible_to, and ManagerOnlyFieldsMixin. It does not explain why access works this way, which is in How access to a record is decided and in ADR 0025, or the methods that change a level, which are in Crediting.
ContributionLevel#
ContributionLevel is an IntegerChoices in fairdm.contrib.contributors.choices. Each level includes the ones before it, and the values are integers so that “at least this level” is a comparison. A level is stored in Contribution.level.
Name |
Value |
Label |
Lets its holder |
|---|---|---|---|
|
1 |
Can view |
Open the record |
|
2 |
Can edit |
Also change the record and the data in it |
|
3 |
Can manage |
Also change its contributors and their levels, change its visibility and delete it |
Contribution.level is empty for an organization, which holds no level, and may be empty for a person credited without one. A level on a project applies to its datasets. A level on a dataset applies to its samples and measurements. A measurement follows its own dataset, not its sample’s.
REQUIRED_LEVEL#
fairdm.contrib.contributors.access.REQUIRED_LEVEL maps each permission codename that a core record type declares to the level that carries it. A codename that is not in the mapping is refused.
Level |
Codenames |
|---|---|
|
|
|
|
|
|
A registered type’s own default permissions, such as plants.view_plantsample, are read as the core model’s. A permission that your own record type adds in Meta.permissions must be added to REQUIRED_LEVEL with the level that carries it, or it is refused.
RecordAccess#
RecordAccess(record) answers what people may do on one record from the levels on its contributions. record is a project, dataset, sample or measurement of any registered type. It is in fairdm.contrib.contributors.access.
Member |
Returns |
What it does |
|---|---|---|
|
|
Whether |
|
|
Whether |
|
|
The pair that names a record on a contribution. A polymorphic record is resolved to its real type first |
|
Property, model class |
The core model the record belongs to, whatever its registered type |
|
Property, |
The verbose name of that model: |
|
Cached property, |
The records this one takes levels from, nearest first. A sample’s list is its dataset and that dataset’s project |
|
|
The level |
|
|
The highest level the user holds on the record or any record above it. |
|
|
Every level the person holds on the record or above it |
|
|
The |
|
|
The level the person holds from being listed on this record. Pass the |
|
|
The highest level the person holds from a record above, and that record. |
|
|
Everyone who holds a level from a record above, at the highest they hold, ordered by name |
|
|
The people who can sign in and hold the manage level on the record or above it. A holder of a portal role does not count |
|
|
Whether the user holds the manage level on the record or above it, or holds |
|
|
Raises |
|
|
The contributions with a level on the record and the records above it, or on the records above alone. Used by the methods above |
Dataset.clean, Sample.clean and Measurement.clean call refuse_move_without_manager.
from fairdm.contrib.contributors.access import RecordAccess
from fairdm.contrib.contributors.choices import ContributionLevel
access = RecordAccess(sample)
access.above # [dataset, project]
access.level_of(request.user) # ContributionLevel.EDIT, for example, or None
access.can_manage(request.user)
access.required_level("sample.change_sample") # ContributionLevel.EDIT
level = RecordAccess(dataset).level_of(request.user)
may_edit = level is not None and level >= ContributionLevel.EDIT
Permission backends#
The AUTHENTICATION_BACKENDS that fairdm.setup() provides, in order:
Backend |
Answers |
|---|---|
|
Model-level permissions that were granted to the user or their groups |
|
Sign-in by email address |
|
Stored django-guardian rows for models other than the four core ones. It normalises a polymorphic instance to its base before the check. For a core record it answers |
|
|
|
Every |
|
Object-level questions for a permission held through a shipped portal role. See Portal roles in code |
RecordLevelBackend grants a permission when the user’s level on the record, or on a record above it, is at least the level REQUIRED_LEVEL gives the permission. It refuses an inactive user and a visitor. For an object that is not a core record, or a question with no object, it returns False and the other backends answer.
user.has_perm("dataset.change_dataset", dataset) # edit level or above
user.has_perm("plants.delete_plantsample", plant_sample) # manage level or above
A permission stored with django-guardian for a project, dataset, sample or measurement grants nothing. For other models the stored rows work as usual. See How to grant object permissions on a polymorphic model.
Queryset methods#
The querysets of the four core models take their level methods from fairdm.core.managers.RecordLevelMixin. ProjectQuerySet, DatasetQuerySet, SampleQuerySet and MeasurementQuerySet mix it in.
Method |
Returns |
What it does |
|---|---|---|
|
Queryset |
The records on which the user holds at least |
|
Queryset |
|
|
Queryset |
On the sample and measurement querysets only. The records of released datasets (public and published), and the records the user holds |
Two more methods keep a public listing to public records:
Method |
On |
Keeps |
|---|---|---|
|
|
Projects whose visibility is public |
|
|
Public datasets that are not inside a private project |
Dataset.objects leaves out private datasets. Use Dataset.all_objects when a query must reach them.
from fairdm.contrib.contributors.choices import ContributionLevel
from fairdm.core.dataset.models import Dataset
from fairdm.core.sample.models import Sample
Dataset.all_objects.with_level(request.user, ContributionLevel.EDIT)
Dataset.all_objects.accessible_to(request.user, ContributionLevel.EDIT)
Sample.objects.visible_to(request.user)
Use these querysets in your own views and filters in place of django-guardian’s get_objects_for_user, which finds nothing for the four core models.
ManagerOnlyFieldsMixin#
fairdm.core.forms.ManagerOnlyFieldsMixin leaves the fields that decide who gets into a record out of a form for anyone who cannot manage the record. A form for a record that does not exist yet leaves nothing out.
Member |
Type |
What it does |
|---|---|---|
|
|
The fields to leave out |
|
Method |
Removes those fields from |
Call withhold_manager_only_fields once the form’s fields exist. The shipped forms set:
Form |
|
|---|---|
|
|
|
|
|
|
|
|
ProjectForm and DatasetForm take the request as a request keyword. SampleFormMixin and MeasurementFormMixin do the same for dataset. See Form and filter mixins.