Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions ietf/group/migrations/0056_dir_chair_groupman_role.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 2.2.28 on 2022-06-14 13:14

from django.db import migrations


def forward(apps, schema_editor):
GroupFeatures = apps.get_model('group', 'GroupFeatures')
features = GroupFeatures.objects.get(type_id='dir')
if 'chair' not in features.groupman_roles:
features.groupman_roles.append('chair')
features.save()


def reverse(apps, schema_editor):
GroupFeatures = apps.get_model('group', 'GroupFeatures')
features = GroupFeatures.objects.get(type_id='dir')
if 'chair' in features.groupman_roles:
features.groupman_roles.remove('chair')
features.save()


class Migration(migrations.Migration):

dependencies = [
('group', '0055_editorial_stream'),
]

operations = [
migrations.RunPython(forward, reverse),
]
4 changes: 4 additions & 0 deletions ietf/group/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ def active_wgs(self):
def closed_wgs(self):
return self.wgs().exclude(state__in=Group.ACTIVE_STATE_IDS)

def with_meetings(self):
return self.get_queryset().filter(type__features__has_meetings=True)


class Group(GroupInfo):
objects = GroupManager()

Expand Down
22 changes: 22 additions & 0 deletions ietf/group/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,28 @@ def can_manage_group(user, group):
return True
return group.has_role(user, group.features.groupman_roles)

def groups_managed_by(user, group_queryset=None):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very easy to read!

"""Find groups user can manage"""
if group_queryset is None:
group_queryset = Group.objects.all()
query_terms = Q(pk__in=[]) # ensure empty set is returned if no other terms are added
if user.is_authenticated or user.person:
# find the GroupTypes entirely managed by this user based on groupman_authroles
types_can_manage = []
for type_id, groupman_authroles in GroupFeatures.objects.values_list('type_id', 'groupman_authroles'):
if has_role(user, groupman_authroles):
types_can_manage.append(type_id)
query_terms |= Q(type_id__in=types_can_manage)
# find the Groups managed by this user based on groupman_roles
groups_can_manage = []
for group_id, role_name, groupman_roles in user.person.role_set.values_list(
'group_id', 'name_id', 'group__type__features__groupman_roles'
):
if role_name in groupman_roles:
groups_can_manage.append(group_id)
query_terms |= Q(pk__in=groups_can_manage)
return group_queryset.filter(query_terms)

def milestone_reviewer_for_group_type(group_type):
if group_type == "rg":
return "IRTF Chair"
Expand Down
34 changes: 10 additions & 24 deletions ietf/meeting/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@
from django.conf import settings
from django.core import validators
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.forms import BaseInlineFormSet
from django.utils.functional import cached_property

import debug # pyflakes:ignore

from ietf.doc.models import Document, DocAlias, State, NewRevisionDocEvent
from ietf.group.models import Group, GroupFeatures
from ietf.ietfauth.utils import has_role
from ietf.group.models import Group
from ietf.group.utils import groups_managed_by
from ietf.meeting.models import Session, Meeting, Schedule, countries, timezones, TimeSlot, Room
from ietf.meeting.helpers import get_next_interim_number, make_materials_directories
from ietf.meeting.helpers import is_interim_meeting_approved, get_next_agenda_name
Expand Down Expand Up @@ -106,10 +105,7 @@ def clean(self):

class InterimMeetingModelForm(forms.ModelForm):
group = GroupModelChoiceField(
queryset=Group.objects.filter(
type_id__in=GroupFeatures.objects.filter(
has_meetings=True
).values_list('type_id',flat=True),
queryset=Group.objects.with_meetings().filter(
state__in=('active', 'proposed', 'bof')
).order_by('acronym'),
required=False,
Expand Down Expand Up @@ -187,24 +183,14 @@ def is_virtual(self):
return True

def set_group_options(self):
'''Set group options based on user accessing the form'''
if has_role(self.user, "Secretariat"):
return # don't reduce group options
q_objects = Q()
if has_role(self.user, "Area Director"):
q_objects.add(Q(type__in=["wg", "ag", "team"], state__in=("active", "proposed", "bof")), Q.OR)
if has_role(self.user, "IRTF Chair"):
q_objects.add(Q(type__in=["rg", "rag"], state__in=("active", "proposed")), Q.OR)
if has_role(self.user, "WG Chair"):
q_objects.add(Q(type="wg", state__in=("active", "proposed", "bof"), role__person=self.person, role__name="chair"), Q.OR)
if has_role(self.user, "RG Chair"):
q_objects.add(Q(type="rg", state__in=("active", "proposed"), role__person=self.person, role__name="chair"), Q.OR)
if has_role(self.user, "Program Lead") or has_role(self.user, "Program Chair"):
q_objects.add(Q(type="program", state__in=("active", "proposed"), role__person=self.person, role__name__in=["chair", "lead"]), Q.OR)

queryset = Group.objects.filter(q_objects).distinct().order_by('acronym')
"""Set group options based on user accessing the form"""
queryset = groups_managed_by(
self.user,
Group.objects.with_meetings(),
).filter(
state_id__in=['active', 'proposed', 'bof']
).order_by('acronym')
self.fields['group'].queryset = queryset

# if there's only one possibility make it the default
if len(queryset) == 1:
self.fields['group'].initial = queryset[0]
Expand Down
18 changes: 16 additions & 2 deletions ietf/meeting/tests_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
"""Tests of forms in the Meeting application"""
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import override_settings
from django.test import override_settings, RequestFactory

from ietf.meeting.forms import FileUploadForm, ApplyToAllFileUploadForm, InterimSessionModelForm
from ietf.group.factories import GroupFactory
from ietf.meeting.forms import (FileUploadForm, ApplyToAllFileUploadForm, InterimSessionModelForm,
InterimMeetingModelForm)
from ietf.person.factories import PersonFactory
from ietf.utils.test_utils import TestCase


Expand Down Expand Up @@ -119,3 +122,14 @@ def test_remote_participation_options(self):
choice_vals = [choice[0] for choice in form.fields['remote_participation'].choices]
self.assertNotIn('meetecho', choice_vals)
self.assertIn('manual', choice_vals)


class InterimMeetingModelFormTests(TestCase):
def test_enforces_authroles(self):
"""User can only request sessions for groups they can manage"""
GroupFactory(type_id='wg', state_id='active')
request = RequestFactory().get('/some/url')
request.user = PersonFactory().user
form = InterimMeetingModelForm(request)
self.assertEqual(form.fields['group'].queryset.count(), 0,
'person with no roles cannot request interims for any group')
3 changes: 1 addition & 2 deletions ietf/meeting/tests_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4612,8 +4612,7 @@ def test_interim_request_options(self):
r = self.client.get("/meeting/interim/request/")
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
Group.objects.filter(type_id__in=GroupFeatures.objects.filter(has_meetings=True).values_list('type_id',flat=True), state__in=('active', 'proposed', 'bof'))
self.assertEqual(Group.objects.filter(type_id__in=GroupFeatures.objects.filter(has_meetings=True).values_list('type_id',flat=True), state__in=('active', 'proposed', 'bof')).count(),
self.assertEqual(Group.objects.with_meetings().filter(state__in=('active', 'proposed', 'bof')).count(),
len(q("#id_group option")) - 1) # -1 for options placeholder
self.client.logout()

Expand Down
2 changes: 1 addition & 1 deletion ietf/name/fixtures/names.json
Original file line number Diff line number Diff line change
Expand Up @@ -2757,7 +2757,7 @@
"default_used_roles": "[\n \"ad\",\n \"chair\",\n \"reviewer\",\n \"secr\",\n \"delegate\"\n]",
"docman_roles": "[\n \"chair\"\n]",
"groupman_authroles": "[\n \"Secretariat\"\n]",
"groupman_roles": "[\n \"ad\",\n \"secr\",\n \"delegate\"\n]",
"groupman_roles": "[\n \"ad\",\n \"secr\",\n \"delegate\",\n \"chair\"\n]",
"has_chartering_process": false,
"has_default_jabber": false,
"has_documents": false,
Expand Down