Merge pull request #4235 from normanjaeckel/SubmitterPermissions

Added new flag for states to hide motions from submitters.
This commit is contained in:
Emanuel Schütze 2019-02-01 16:08:51 +01:00 committed by GitHub
commit 448ea6df28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 42 deletions

View File

@ -38,6 +38,7 @@ Motions:
recommendations [#4037, #4132].
- Added timestampes for motions [#4134].
- New config option to set reason as required field [#4232]
- Added new flag to motion state to control access for different users [#4235].
User:
- Added new admin group which grants all permissions. Users of existing group

View File

@ -37,13 +37,17 @@ class MotionAccessPermissions(BaseAccessPermissions):
is_submitter = False
# Check see permission for this motion.
required_permission_to_see = full["state_required_permission_to_see"]
permission = (
not required_permission_to_see
or await async_has_perm(user_id, required_permission_to_see)
or await async_has_perm(user_id, "motions.can_manage")
or is_submitter
)
from .models import State
if await async_has_perm(user_id, "motions.can_manage"):
level = State.MANAGERS_ONLY
elif await async_has_perm(user_id, "motions.can_manage_metadata"):
level = State.EXTENDED_MANAGERS
elif is_submitter:
level = State.EXTENDED_MANAGERS_AND_SUBMITTER
else:
level = State.ALL
permission = level >= full["state_access_level"]
# Parse single motion.
if permission:
@ -57,7 +61,6 @@ class MotionAccessPermissions(BaseAccessPermissions):
data.append(full_copy)
else:
data = []
return data

View File

@ -0,0 +1,43 @@
# Generated by Django 2.1.5 on 2019-02-01 11:58
from django.db import migrations, models
def transform_required_permission_to_see_field(apps, schema_editor):
"""
Sets new access_level of states to EXTENDED_MANAGERS_AND_SUBMITTER
if required_permission_to_see is given
"""
# We get the model from the versioned app registry;
# if we directly import it, it will be the wrong version.
State = apps.get_model("motions", "State")
for state in State.objects.all():
if state.required_permission_to_see:
state.access_level = 1
state.save(skip_autoupdate=True)
class Migration(migrations.Migration):
dependencies = [("motions", "0020_auto_20190119_1425")]
operations = [
migrations.AddField(
model_name="state",
name="access_level",
field=models.IntegerField(
choices=[
(0, "All users with permission to see motions"),
(
1,
"Submitters, managers and users with permission to manage metadata",
),
(2, "Only managers and users with permission to manage metadata"),
(3, "Only managers"),
],
default=0,
),
),
migrations.RunPython(transform_required_permission_to_see_field),
migrations.RemoveField(model_name="state", name="required_permission_to_see"),
]

View File

@ -1054,6 +1054,24 @@ class State(RESTModelMixin, models.Model):
state.
"""
ALL = 0
EXTENDED_MANAGERS_AND_SUBMITTER = 1
EXTENDED_MANAGERS = 2
MANAGERS_ONLY = 3
ACCESS_LEVELS = (
(ALL, "All users with permission to see motions"),
(
EXTENDED_MANAGERS_AND_SUBMITTER,
"Submitters, managers and users with permission to manage metadata",
),
(
EXTENDED_MANAGERS,
"Only managers and users with permission to manage metadata",
),
(MANAGERS_ONLY, "Only managers"),
)
name = models.CharField(max_length=255)
"""A string representing the state."""
@ -1075,14 +1093,10 @@ class State(RESTModelMixin, models.Model):
Default value is 'primary' (blue).
"""
required_permission_to_see = models.CharField(max_length=255, blank=True)
access_level = models.IntegerField(choices=ACCESS_LEVELS, default=0)
"""
A permission string. If not empty, the user has to have this permission to
see a motion in this state.
To use this feature change the database entry of a state object and add
your favourite permission string. You can do this e. g. by editing the
definitions in create_builtin_workflows() in openslides/motions/signals.py.
Defines which users may see motions in this state e. g. only managers,
users with permission to manage metadata and submitters.
"""
allow_support = models.BooleanField(default=False)

View File

@ -102,7 +102,7 @@ class StateSerializer(ModelSerializer):
"name",
"recommendation_label",
"css_class",
"required_permission_to_see",
"access_level",
"allow_support",
"allow_create_poll",
"allow_submitter_edit",
@ -389,7 +389,7 @@ class MotionSerializer(ModelSerializer):
polls = MotionPollSerializer(many=True, read_only=True)
modified_final_version = CharField(allow_blank=True, required=False)
reason = CharField(allow_blank=True, required=False)
state_required_permission_to_see = SerializerMethodField()
state_access_level = SerializerMethodField()
text = CharField(allow_blank=True)
title = CharField(max_length=255)
amendment_paragraphs = AmendmentParagraphsJSONSerializerField(required=False)
@ -424,7 +424,7 @@ class MotionSerializer(ModelSerializer):
"supporters",
"state",
"state_extension",
"state_required_permission_to_see",
"state_access_level",
"statute_paragraph",
"workflow_id",
"recommendation",
@ -531,12 +531,9 @@ class MotionSerializer(ModelSerializer):
return result
def get_state_required_permission_to_see(self, motion):
def get_state_access_level(self, motion):
"""
Returns the permission (as string) that is required for non
managers that are not submitters to see this motion in this state.
Hint: Most states have and empty string here so this restriction is
disabled.
Returns the access level of this state. The default is 0 so everybody
with permission to see motions can see this motion.
"""
return motion.state.required_permission_to_see
return motion.state.access_level

View File

@ -479,13 +479,11 @@ class RetrieveMotion(TestCase):
username=f"user_{index}", password="password"
)
def test_guest_state_with_required_permission_to_see(self):
def test_guest_state_with_access_level(self):
config["general_system_enable_anonymous"] = True
guest_client = APIClient()
state = self.motion.state
state.required_permission_to_see = (
"permission_that_the_user_does_not_have_leeceiz9hi7iuta4ahY2"
)
state.access_level = State.MANAGERS_ONLY
state.save()
# The cache has to be cleared, see:
# https://github.com/OpenSlides/OpenSlides/issues/3396
@ -494,20 +492,17 @@ class RetrieveMotion(TestCase):
response = guest_client.get(reverse("motion-detail", args=[self.motion.pk]))
self.assertEqual(response.status_code, 404)
def test_admin_state_with_required_permission_to_see(self):
def test_admin_state_with_access_level(self):
state = self.motion.state
state.required_permission_to_see = (
"permission_that_the_user_does_not_have_coo1Iewu8Eing2xahfoo"
)
state.access_level = State.MANAGERS_ONLY
state.save()
response = self.client.get(reverse("motion-detail", args=[self.motion.pk]))
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_submitter_state_with_required_permission_to_see(self):
def test_submitter_state_with_access_level(self):
state = self.motion.state
state.required_permission_to_see = (
"permission_that_the_user_does_not_have_eiW8af9caizoh1thaece"
)
state.access_level = State.EXTENDED_MANAGERS_AND_SUBMITTER
state.save()
user = get_user_model().objects.create_user(
username="username_ohS2opheikaSa5theijo",

View File

@ -29,7 +29,7 @@ def all_data():
"supporters_id": [],
"state_id": 1,
"state_extension": None,
"state_required_permission_to_see": "",
"state_access_level": 0,
"statute_paragraph_id": None,
"workflow_id": 1,
"recommendation_id": None,
@ -86,7 +86,7 @@ def all_data():
"name": "submitted",
"recommendation_label": None,
"css_class": "primary",
"required_permission_to_see": "",
"access_level": 0,
"allow_support": True,
"allow_create_poll": True,
"allow_submitter_edit": True,
@ -102,7 +102,7 @@ def all_data():
"name": "accepted",
"recommendation_label": "Acceptance",
"css_class": "success",
"required_permission_to_see": "",
"access_level": 0,
"allow_support": False,
"allow_create_poll": False,
"allow_submitter_edit": False,
@ -118,7 +118,7 @@ def all_data():
"name": "rejected",
"recommendation_label": "Rejection",
"css_class": "danger",
"required_permission_to_see": "",
"access_level": 0,
"allow_support": False,
"allow_create_poll": False,
"allow_submitter_edit": False,
@ -134,7 +134,7 @@ def all_data():
"name": "not decided",
"recommendation_label": "No decision",
"css_class": "default",
"required_permission_to_see": "",
"access_level": 0,
"allow_support": False,
"allow_create_poll": False,
"allow_submitter_edit": False,