Skip to content

Commit 3aa2ae3

Browse files
authored
Merge pull request #2956 from kobotoolbox/2648-add-manage-project-permission
Add new permission for managing sharing
2 parents 39863d7 + 2c8b2ed commit 3aa2ae3

31 files changed

+923
-455
lines changed

jsapp/js/actions.es6

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ permissionsActions.assignAssetPermission.failed.listen(() => {
255255
permissionsActions.removeAssetPermission.failed.listen(() => {
256256
notify(t('Failed to remove permissions'), 'error');
257257
});
258+
permissionsActions.bulkSetAssetPermissions.failed.listen(() => {
259+
notify(t('Failed to update permissions'), 'error');
260+
});
258261
permissionsActions.assignCollectionPermission.failed.listen(() => {
259262
notify(t('Failed to update permissions'), 'error');
260263
});

jsapp/js/components/permissions/permParser.es6

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
* @property {string} data.username - Who give permissions to.
2020
* @property {boolean} data.formView - Is able to view forms.
2121
* @property {boolean} data.formEdit - Is able to edit forms.
22+
* @property {boolean} data.formManage - Is able to change form permissions (and future stuff TBD).
2223
* @property {boolean} data.submissionsView - Is able to view submissions.
2324
* @property {boolean} data.submissionsViewPartial - If true, then able to view submissions only of some users.
2425
* @property {string[]} data.submissionsViewPartialUsers - Users mentioned in the above line.
@@ -60,6 +61,10 @@ function parseFormData(data) {
6061
parsed.push(buildBackendPerm(data.username, PERMISSIONS_CODENAMES.get('change_asset')));
6162
}
6263

64+
if (data.formManage) {
65+
parsed.push(buildBackendPerm(data.username, PERMISSIONS_CODENAMES.get('manage_asset')));
66+
}
67+
6368
if (data.submissionsViewPartial) {
6469
let permObj = buildBackendPerm(data.username, PERMISSIONS_CODENAMES.get('partial_submissions'));
6570
permObj.partial_permissions = [{
@@ -159,6 +164,9 @@ function buildFormData(permissions) {
159164
if (perm.permission === permConfig.getPermissionByCodename(PERMISSIONS_CODENAMES.get('change_asset')).url) {
160165
formData.formEdit = true;
161166
}
167+
if (perm.permission === permConfig.getPermissionByCodename(PERMISSIONS_CODENAMES.get('manage_asset')).url) {
168+
formData.formManage = true;
169+
}
162170
if (perm.permission === permConfig.getPermissionByCodename(PERMISSIONS_CODENAMES.get('partial_submissions')).url) {
163171
formData.submissionsView = true;
164172
formData.submissionsViewPartial = true;

jsapp/js/components/permissions/permValidator.es6

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ class PermValidator extends React.Component {
3838
});
3939

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

5459
let hasAnyContradictory = false;
5560
allContradictory.forEach((contradictory) => {

jsapp/js/components/permissions/permissionsMocks.es6

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
// /api/v2/permissions/
88
const permissions = {
9-
'count': 10,
9+
'count': 11,
1010
'next': null,
1111
'previous': null,
1212
'results': [
@@ -52,6 +52,23 @@ const permissions = {
5252
],
5353
'name': 'Can delete submitted data for asset'
5454
},
55+
{
56+
"url": "/api/v2/permissions/manage_asset.json",
57+
"codename": "manage_asset",
58+
"implied": [
59+
"/api/v2/permissions/delete_submissions/",
60+
"/api/v2/permissions/change_submissions/",
61+
"/api/v2/permissions/validate_submissions/",
62+
"/api/v2/permissions/view_asset/",
63+
"/api/v2/permissions/change_asset/",
64+
"/api/v2/permissions/view_submissions/",
65+
"/api/v2/permissions/add_submissions/"
66+
],
67+
'contradictory': [
68+
'/api/v2/permissions/partial_submissions/'
69+
],
70+
"name": "Can manage all aspects of asset"
71+
},
5572
{
5673
'url': '/api/v2/permissions/partial_submissions/',
5774
'codename': 'partial_submissions',

jsapp/js/components/permissions/userAssetPermsEditor.es6

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ class UserAssetPermsEditor extends React.Component {
4747
username: '',
4848
formView: false,
4949
formViewDisabled: false,
50+
formEditDisabled: false,
5051
formEdit: false,
5152
submissionsView: false,
5253
submissionsViewDisabled: false,
5354
submissionsViewPartial: false,
5455
submissionsViewPartialDisabled: false,
5556
submissionsViewPartialUsers: [],
5657
submissionsAdd: false,
58+
submissionsAddDisabled: false,
5759
submissionsEdit: false,
5860
submissionsEditDisabled: false,
5961
submissionsDelete: false,
@@ -137,8 +139,10 @@ class UserAssetPermsEditor extends React.Component {
137139
applyValidityRules(stateObj) {
138140
// reset disabling before checks
139141
stateObj.formViewDisabled = false;
142+
stateObj.formEditDisabled = false;
140143
stateObj.submissionsViewDisabled = false;
141144
stateObj.submissionsViewPartialDisabled = false;
145+
stateObj.submissionsAddDisabled = false;
142146
stateObj.submissionsDeleteDisabled = false;
143147
stateObj.submissionsEditDisabled = false;
144148
stateObj.submissionsValidateDisabled = false;
@@ -187,6 +191,26 @@ class UserAssetPermsEditor extends React.Component {
187191
stateObj.submissionsViewPartialUsers = [];
188192
}
189193

194+
// `formManage` implies every other permission (except partial permissions)
195+
if (stateObj.formManage) {
196+
stateObj.formView = true;
197+
stateObj.formEdit = true;
198+
stateObj.submissionsAdd = true;
199+
stateObj.submissionsView = true;
200+
stateObj.submissionsDelete = true;
201+
stateObj.submissionsEdit = true;
202+
stateObj.submissionsValidate = true;
203+
204+
stateObj.formViewDisabled = true;
205+
stateObj.formEditDisabled = true;
206+
stateObj.submissionsViewDisabled = true;
207+
stateObj.submissionsViewPartialDisabled = true;
208+
stateObj.submissionsAddDisabled = true;
209+
stateObj.submissionsDeleteDisabled = true;
210+
stateObj.submissionsEditDisabled = true;
211+
stateObj.submissionsValidateDisabled = true;
212+
}
213+
190214
return stateObj;
191215
}
192216

@@ -339,6 +363,7 @@ class UserAssetPermsEditor extends React.Component {
339363

340364
if (this.isAssignable('view_asset')) {output.formView = this.state.formView;}
341365
if (this.isAssignable('change_asset')) {output.formEdit = this.state.formEdit;}
366+
if (this.isAssignable('manage_asset')) {output.formManage = this.state.formManage;}
342367
if (this.isAssignable('add_submissions')) {output.submissionsAdd = this.state.submissionsAdd;}
343368
if (this.isAssignable('view_submissions')) {output.submissionsView = this.state.submissionsView;}
344369
if (this.isAssignable('partial_submissions')) {
@@ -421,6 +446,7 @@ class UserAssetPermsEditor extends React.Component {
421446
{this.isAssignable('change_asset') &&
422447
<Checkbox
423448
checked={this.state.formEdit}
449+
disabled={this.state.formEditDisabled}
424450
onChange={this.onCheckboxChange.bind(this, 'formEdit')}
425451
label={this.getLabel('change_asset')}
426452
/>
@@ -459,6 +485,7 @@ class UserAssetPermsEditor extends React.Component {
459485
{this.isAssignable('add_submissions') &&
460486
<Checkbox
461487
checked={this.state.submissionsAdd}
488+
disabled={this.state.submissionsAddDisabled}
462489
onChange={this.onCheckboxChange.bind(this, 'submissionsAdd')}
463490
label={this.getLabel('add_submissions')}
464491
/>
@@ -490,6 +517,14 @@ class UserAssetPermsEditor extends React.Component {
490517
label={this.getLabel('validate_submissions')}
491518
/>
492519
}
520+
521+
{this.isAssignable('manage_asset') &&
522+
<Checkbox
523+
checked={this.state.formManage}
524+
onChange={this.onCheckboxChange.bind(this, 'formManage')}
525+
label={this.getLabel('manage_asset')}
526+
/>
527+
}
493528
</div>
494529

495530
<div className='user-permissions-editor__row'>

jsapp/js/constants.es6

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const PERMISSIONS_CODENAMES = new Map();
2727
new Set([
2828
'view_asset',
2929
'change_asset',
30+
'manage_asset',
3031
'add_submissions',
3132
'view_submissions',
3233
'partial_submissions',

kpi/constants.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
# ASSIGNABLE_PERMISSIONS
5353
PERM_VIEW_ASSET = 'view_asset'
5454
PERM_CHANGE_ASSET = 'change_asset'
55+
PERM_MANAGE_ASSET = 'manage_asset'
5556
PERM_ADD_SUBMISSIONS = 'add_submissions'
5657
PERM_DELETE_SUBMISSIONS = 'delete_submissions'
5758
PERM_VIEW_SUBMISSIONS = 'view_submissions'
@@ -62,11 +63,8 @@
6263
PERM_CHANGE_COLLECTION = 'change_collection'
6364

6465
# CALCULATED_PERMISSIONS
65-
PERM_SHARE_ASSET = 'share_asset'
6666
PERM_DELETE_ASSET = 'delete_asset'
67-
PERM_SHARE_SUBMISSIONS = 'share_submissions'
6867
PERM_DELETE_SUBMISSIONS = 'delete_submissions'
69-
PERM_SHARE_COLLECTION = 'share_collection'
7068
PERM_DELETE_COLLECTION = 'delete_collection'
7169

7270
# KC INTERNAL

kpi/filters.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ def filter_queryset(self, request, queryset, view):
159159

160160

161161
class KpiAssignedObjectPermissionsFilter(filters.BaseFilterBackend):
162+
"""
163+
Used by kpi.views.v1.object_permission.ObjectPermissionViewSet only
164+
"""
165+
162166
def filter_queryset(self, request, queryset, view):
163167
# TODO: omit objects for which the user has only a deny permission
164168
user = request.user
@@ -171,10 +175,11 @@ def filter_queryset(self, request, queryset, view):
171175
# Hide permissions from anonymous users
172176
return queryset.none()
173177
"""
174-
A regular user sees permissions for objects to which they have access.
175-
For example, if Alana has view access to an object owned by Richard,
176-
she should see all permissions for that object, including those
177-
assigned to other users.
178+
A regular user sees their own permissions and the owner's permissions
179+
for objects to which they have access. For example, if Alana and John
180+
have view access to an object owned by Richard, John should see all of
181+
his own permissions and Richard's permissions, but not any of Alana's
182+
permissions.
178183
"""
179184
possible_content_types = ContentType.objects.get_for_models(
180185
*get_models_with_object_permissions()
@@ -187,11 +192,22 @@ def filter_queryset(self, request, queryset, view):
187192
user=user,
188193
)
189194
# Find all the objects associated with those permissions, and then
190-
# find all the permissions applied to all of those objects
191-
result |= ObjectPermission.objects.filter(
192-
content_type=content_type,
193-
object_id__in=permissions_assigned_to_user.values(
194-
'object_id'
195-
).distinct()
196-
)
195+
# find all the owners' permissions applied to all of those objects
196+
for object_id, owner_id in (
197+
content_type.model_class()
198+
.objects.filter(
199+
pk__in=permissions_assigned_to_user.values_list(
200+
'object_id', flat=True
201+
).distinct()
202+
)
203+
.values_list('pk', 'owner_id')
204+
):
205+
criteria = dict(
206+
content_type=content_type,
207+
object_id=object_id,
208+
)
209+
# The owner sees all permission assignments, but others don't
210+
if user.pk != owner_id:
211+
criteria['user_id__in'] = (user.pk, owner_id)
212+
result |= ObjectPermission.objects.filter(**criteria)
197213
return result

kpi/fixtures/conflicting_versions.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,6 @@
604604
}
605605
]
606606
},
607-
"editors_can_change_permissions": true,
608607
"owner": 2,
609608
"date_created": "2017-12-18T22:42:59.233Z",
610609
"asset_type": "survey",

kpi/fixtures/test_data.json

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,16 @@
3434
["add_collection", "kpi", "collection"],
3535
["change_collection", "kpi", "collection"],
3636
["delete_collection", "kpi", "collection"],
37-
["share_collection", "kpi", "collection"],
3837
["view_collection", "kpi", "collection"],
3938
["add_asset", "kpi", "asset"],
4039
["change_asset", "kpi", "asset"],
4140
["delete_asset", "kpi", "asset"],
42-
["share_asset", "kpi", "asset"],
41+
["manage_asset", "kpi", "asset"],
4342
["view_asset", "kpi", "asset"],
4443
["add_submissions", "kpi", "asset"],
4544
["change_submissions", "kpi", "asset"],
4645
["validate_submissions", "kpi", "asset"],
4746
["delete_submissions", "kpi", "asset"],
48-
["share_submissions", "kpi", "asset"],
4947
["view_submissions", "kpi", "asset"]
5048
]
5149
},
@@ -69,18 +67,16 @@
6967
["add_collection", "kpi", "collection"],
7068
["change_collection", "kpi", "collection"],
7169
["delete_collection", "kpi", "collection"],
72-
["share_collection", "kpi", "collection"],
7370
["view_collection", "kpi", "collection"],
7471
["add_asset", "kpi", "asset"],
7572
["change_asset", "kpi", "asset"],
7673
["delete_asset", "kpi", "asset"],
77-
["share_asset", "kpi", "asset"],
74+
["manage_asset", "kpi", "asset"],
7875
["view_asset", "kpi", "asset"],
7976
["add_submissions", "kpi", "asset"],
8077
["change_submissions", "kpi", "asset"],
8178
["validate_submissions", "kpi", "asset"],
8279
["delete_submissions", "kpi", "asset"],
83-
["share_submissions", "kpi", "asset"],
8480
["view_submissions", "kpi", "asset"]
8581
]
8682
},

0 commit comments

Comments
 (0)