Skip to content

Commit

Permalink
Merge pull request #2956 from kobotoolbox/2648-add-manage-project-per…
Browse files Browse the repository at this point in the history
…mission

Add new permission for managing sharing
  • Loading branch information
jnm authored Jan 21, 2021
2 parents 39863d7 + 2c8b2ed commit 3aa2ae3
Show file tree
Hide file tree
Showing 31 changed files with 923 additions and 455 deletions.
3 changes: 3 additions & 0 deletions jsapp/js/actions.es6
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ permissionsActions.assignAssetPermission.failed.listen(() => {
permissionsActions.removeAssetPermission.failed.listen(() => {
notify(t('Failed to remove permissions'), 'error');
});
permissionsActions.bulkSetAssetPermissions.failed.listen(() => {
notify(t('Failed to update permissions'), 'error');
});
permissionsActions.assignCollectionPermission.failed.listen(() => {
notify(t('Failed to update permissions'), 'error');
});
Expand Down
8 changes: 8 additions & 0 deletions jsapp/js/components/permissions/permParser.es6
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
* @property {string} data.username - Who give permissions to.
* @property {boolean} data.formView - Is able to view forms.
* @property {boolean} data.formEdit - Is able to edit forms.
* @property {boolean} data.formManage - Is able to change form permissions (and future stuff TBD).
* @property {boolean} data.submissionsView - Is able to view submissions.
* @property {boolean} data.submissionsViewPartial - If true, then able to view submissions only of some users.
* @property {string[]} data.submissionsViewPartialUsers - Users mentioned in the above line.
Expand Down Expand Up @@ -60,6 +61,10 @@ function parseFormData(data) {
parsed.push(buildBackendPerm(data.username, PERMISSIONS_CODENAMES.get('change_asset')));
}

if (data.formManage) {
parsed.push(buildBackendPerm(data.username, PERMISSIONS_CODENAMES.get('manage_asset')));
}

if (data.submissionsViewPartial) {
let permObj = buildBackendPerm(data.username, PERMISSIONS_CODENAMES.get('partial_submissions'));
permObj.partial_permissions = [{
Expand Down Expand Up @@ -159,6 +164,9 @@ function buildFormData(permissions) {
if (perm.permission === permConfig.getPermissionByCodename(PERMISSIONS_CODENAMES.get('change_asset')).url) {
formData.formEdit = true;
}
if (perm.permission === permConfig.getPermissionByCodename(PERMISSIONS_CODENAMES.get('manage_asset')).url) {
formData.formManage = true;
}
if (perm.permission === permConfig.getPermissionByCodename(PERMISSIONS_CODENAMES.get('partial_submissions')).url) {
formData.submissionsView = true;
formData.submissionsViewPartial = true;
Expand Down
5 changes: 5 additions & 0 deletions jsapp/js/components/permissions/permValidator.es6
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class PermValidator extends React.Component {
});

let hasAllImplied = true;
// FIXME: `manage_asset` implies all the `*_submission` permissions, but
// those are assignable *only* when the asset type is 'survey'. We need to
// design a way to pass that nuance from the back end to the front end
/*
allImplied.forEach((implied) => {
let isFound = false;
permissionAssignments.forEach((assignment) => {
Expand All @@ -50,6 +54,7 @@ class PermValidator extends React.Component {
hasAllImplied = false;
}
});
*/

let hasAnyContradictory = false;
allContradictory.forEach((contradictory) => {
Expand Down
19 changes: 18 additions & 1 deletion jsapp/js/components/permissions/permissionsMocks.es6
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

// /api/v2/permissions/
const permissions = {
'count': 10,
'count': 11,
'next': null,
'previous': null,
'results': [
Expand Down Expand Up @@ -52,6 +52,23 @@ const permissions = {
],
'name': 'Can delete submitted data for asset'
},
{
"url": "/api/v2/permissions/manage_asset.json",
"codename": "manage_asset",
"implied": [
"/api/v2/permissions/delete_submissions/",
"/api/v2/permissions/change_submissions/",
"/api/v2/permissions/validate_submissions/",
"/api/v2/permissions/view_asset/",
"/api/v2/permissions/change_asset/",
"/api/v2/permissions/view_submissions/",
"/api/v2/permissions/add_submissions/"
],
'contradictory': [
'/api/v2/permissions/partial_submissions/'
],
"name": "Can manage all aspects of asset"
},
{
'url': '/api/v2/permissions/partial_submissions/',
'codename': 'partial_submissions',
Expand Down
35 changes: 35 additions & 0 deletions jsapp/js/components/permissions/userAssetPermsEditor.es6
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ class UserAssetPermsEditor extends React.Component {
username: '',
formView: false,
formViewDisabled: false,
formEditDisabled: false,
formEdit: false,
submissionsView: false,
submissionsViewDisabled: false,
submissionsViewPartial: false,
submissionsViewPartialDisabled: false,
submissionsViewPartialUsers: [],
submissionsAdd: false,
submissionsAddDisabled: false,
submissionsEdit: false,
submissionsEditDisabled: false,
submissionsDelete: false,
Expand Down Expand Up @@ -137,8 +139,10 @@ class UserAssetPermsEditor extends React.Component {
applyValidityRules(stateObj) {
// reset disabling before checks
stateObj.formViewDisabled = false;
stateObj.formEditDisabled = false;
stateObj.submissionsViewDisabled = false;
stateObj.submissionsViewPartialDisabled = false;
stateObj.submissionsAddDisabled = false;
stateObj.submissionsDeleteDisabled = false;
stateObj.submissionsEditDisabled = false;
stateObj.submissionsValidateDisabled = false;
Expand Down Expand Up @@ -187,6 +191,26 @@ class UserAssetPermsEditor extends React.Component {
stateObj.submissionsViewPartialUsers = [];
}

// `formManage` implies every other permission (except partial permissions)
if (stateObj.formManage) {
stateObj.formView = true;
stateObj.formEdit = true;
stateObj.submissionsAdd = true;
stateObj.submissionsView = true;
stateObj.submissionsDelete = true;
stateObj.submissionsEdit = true;
stateObj.submissionsValidate = true;

stateObj.formViewDisabled = true;
stateObj.formEditDisabled = true;
stateObj.submissionsViewDisabled = true;
stateObj.submissionsViewPartialDisabled = true;
stateObj.submissionsAddDisabled = true;
stateObj.submissionsDeleteDisabled = true;
stateObj.submissionsEditDisabled = true;
stateObj.submissionsValidateDisabled = true;
}

return stateObj;
}

Expand Down Expand Up @@ -339,6 +363,7 @@ class UserAssetPermsEditor extends React.Component {

if (this.isAssignable('view_asset')) {output.formView = this.state.formView;}
if (this.isAssignable('change_asset')) {output.formEdit = this.state.formEdit;}
if (this.isAssignable('manage_asset')) {output.formManage = this.state.formManage;}
if (this.isAssignable('add_submissions')) {output.submissionsAdd = this.state.submissionsAdd;}
if (this.isAssignable('view_submissions')) {output.submissionsView = this.state.submissionsView;}
if (this.isAssignable('partial_submissions')) {
Expand Down Expand Up @@ -421,6 +446,7 @@ class UserAssetPermsEditor extends React.Component {
{this.isAssignable('change_asset') &&
<Checkbox
checked={this.state.formEdit}
disabled={this.state.formEditDisabled}
onChange={this.onCheckboxChange.bind(this, 'formEdit')}
label={this.getLabel('change_asset')}
/>
Expand Down Expand Up @@ -459,6 +485,7 @@ class UserAssetPermsEditor extends React.Component {
{this.isAssignable('add_submissions') &&
<Checkbox
checked={this.state.submissionsAdd}
disabled={this.state.submissionsAddDisabled}
onChange={this.onCheckboxChange.bind(this, 'submissionsAdd')}
label={this.getLabel('add_submissions')}
/>
Expand Down Expand Up @@ -490,6 +517,14 @@ class UserAssetPermsEditor extends React.Component {
label={this.getLabel('validate_submissions')}
/>
}

{this.isAssignable('manage_asset') &&
<Checkbox
checked={this.state.formManage}
onChange={this.onCheckboxChange.bind(this, 'formManage')}
label={this.getLabel('manage_asset')}
/>
}
</div>

<div className='user-permissions-editor__row'>
Expand Down
1 change: 1 addition & 0 deletions jsapp/js/constants.es6
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const PERMISSIONS_CODENAMES = new Map();
new Set([
'view_asset',
'change_asset',
'manage_asset',
'add_submissions',
'view_submissions',
'partial_submissions',
Expand Down
4 changes: 1 addition & 3 deletions kpi/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
# ASSIGNABLE_PERMISSIONS
PERM_VIEW_ASSET = 'view_asset'
PERM_CHANGE_ASSET = 'change_asset'
PERM_MANAGE_ASSET = 'manage_asset'
PERM_ADD_SUBMISSIONS = 'add_submissions'
PERM_DELETE_SUBMISSIONS = 'delete_submissions'
PERM_VIEW_SUBMISSIONS = 'view_submissions'
Expand All @@ -62,11 +63,8 @@
PERM_CHANGE_COLLECTION = 'change_collection'

# CALCULATED_PERMISSIONS
PERM_SHARE_ASSET = 'share_asset'
PERM_DELETE_ASSET = 'delete_asset'
PERM_SHARE_SUBMISSIONS = 'share_submissions'
PERM_DELETE_SUBMISSIONS = 'delete_submissions'
PERM_SHARE_COLLECTION = 'share_collection'
PERM_DELETE_COLLECTION = 'delete_collection'

# KC INTERNAL
Expand Down
38 changes: 27 additions & 11 deletions kpi/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ def filter_queryset(self, request, queryset, view):


class KpiAssignedObjectPermissionsFilter(filters.BaseFilterBackend):
"""
Used by kpi.views.v1.object_permission.ObjectPermissionViewSet only
"""

def filter_queryset(self, request, queryset, view):
# TODO: omit objects for which the user has only a deny permission
user = request.user
Expand All @@ -171,10 +175,11 @@ def filter_queryset(self, request, queryset, view):
# Hide permissions from anonymous users
return queryset.none()
"""
A regular user sees permissions for objects to which they have access.
For example, if Alana has view access to an object owned by Richard,
she should see all permissions for that object, including those
assigned to other users.
A regular user sees their own permissions and the owner's permissions
for objects to which they have access. For example, if Alana and John
have view access to an object owned by Richard, John should see all of
his own permissions and Richard's permissions, but not any of Alana's
permissions.
"""
possible_content_types = ContentType.objects.get_for_models(
*get_models_with_object_permissions()
Expand All @@ -187,11 +192,22 @@ def filter_queryset(self, request, queryset, view):
user=user,
)
# Find all the objects associated with those permissions, and then
# find all the permissions applied to all of those objects
result |= ObjectPermission.objects.filter(
content_type=content_type,
object_id__in=permissions_assigned_to_user.values(
'object_id'
).distinct()
)
# find all the owners' permissions applied to all of those objects
for object_id, owner_id in (
content_type.model_class()
.objects.filter(
pk__in=permissions_assigned_to_user.values_list(
'object_id', flat=True
).distinct()
)
.values_list('pk', 'owner_id')
):
criteria = dict(
content_type=content_type,
object_id=object_id,
)
# The owner sees all permission assignments, but others don't
if user.pk != owner_id:
criteria['user_id__in'] = (user.pk, owner_id)
result |= ObjectPermission.objects.filter(**criteria)
return result
1 change: 0 additions & 1 deletion kpi/fixtures/conflicting_versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,6 @@
}
]
},
"editors_can_change_permissions": true,
"owner": 2,
"date_created": "2017-12-18T22:42:59.233Z",
"asset_type": "survey",
Expand Down
8 changes: 2 additions & 6 deletions kpi/fixtures/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,16 @@
["add_collection", "kpi", "collection"],
["change_collection", "kpi", "collection"],
["delete_collection", "kpi", "collection"],
["share_collection", "kpi", "collection"],
["view_collection", "kpi", "collection"],
["add_asset", "kpi", "asset"],
["change_asset", "kpi", "asset"],
["delete_asset", "kpi", "asset"],
["share_asset", "kpi", "asset"],
["manage_asset", "kpi", "asset"],
["view_asset", "kpi", "asset"],
["add_submissions", "kpi", "asset"],
["change_submissions", "kpi", "asset"],
["validate_submissions", "kpi", "asset"],
["delete_submissions", "kpi", "asset"],
["share_submissions", "kpi", "asset"],
["view_submissions", "kpi", "asset"]
]
},
Expand All @@ -69,18 +67,16 @@
["add_collection", "kpi", "collection"],
["change_collection", "kpi", "collection"],
["delete_collection", "kpi", "collection"],
["share_collection", "kpi", "collection"],
["view_collection", "kpi", "collection"],
["add_asset", "kpi", "asset"],
["change_asset", "kpi", "asset"],
["delete_asset", "kpi", "asset"],
["share_asset", "kpi", "asset"],
["manage_asset", "kpi", "asset"],
["view_asset", "kpi", "asset"],
["add_submissions", "kpi", "asset"],
["change_submissions", "kpi", "asset"],
["validate_submissions", "kpi", "asset"],
["delete_submissions", "kpi", "asset"],
["share_submissions", "kpi", "asset"],
["view_submissions", "kpi", "asset"]
]
},
Expand Down
57 changes: 57 additions & 0 deletions kpi/migrations/0027_add_manage_asset_permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('kpi', '0026_disable_editors_can_change_permissions'),
]

operations = [
migrations.AlterModelOptions(
name='asset',
options={
'default_permissions': ('add', 'change', 'delete'),
'ordering': ('-date_modified',),
'permissions': (
('view_asset', 'Can view asset'),
('manage_asset', 'Can manage all aspects of asset'),
('add_submissions', 'Can submit data to asset'),
('view_submissions', 'Can view submitted data for asset'),
(
'partial_submissions',
'Can make partial actions on submitted data for asset for specific users',
),
(
'change_submissions',
'Can modify submitted data for asset',
),
(
'delete_submissions',
'Can delete submitted data for asset',
),
(
'validate_submissions',
'Can validate submitted data asset',
),
('from_kc_only', 'INTERNAL USE ONLY; DO NOT ASSIGN'),
),
},
),
migrations.AlterModelOptions(
name='collection',
options={
'default_permissions': ('add', 'change', 'delete'),
'ordering': ('-date_modified',),
'permissions': (('view_collection', 'Can view collection'),),
},
),
migrations.RemoveField(
model_name='asset',
name='editors_can_change_permissions',
),
migrations.RemoveField(
model_name='collection',
name='editors_can_change_permissions',
),
]
Loading

0 comments on commit 3aa2ae3

Please sign in to comment.