Skip to content

Commit 858530c

Browse files
committed
Cleaned up the checking permission for a user to manage a group. Also, cleanly handle a set of group parent links did for a loop.
- Legacy-Id: 11082
1 parent 570107d commit 858530c

8 files changed

Lines changed: 76 additions & 40 deletions

File tree

ietf/doc/views_charter.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
derive_new_work_text )
2424
from ietf.doc.mails import email_state_changed, email_charter_internal_review
2525
from ietf.group.models import ChangeStateGroupEvent, MilestoneGroupEvent
26-
from ietf.group.utils import save_group_in_history, save_milestone_in_history, can_manage_group_type
26+
from ietf.group.utils import save_group_in_history, save_milestone_in_history, can_manage_group
2727
from ietf.ietfauth.utils import has_role, role_required
2828
from ietf.name.models import GroupStateName
2929
from ietf.person.models import Person
@@ -58,7 +58,7 @@ def change_state(request, name, option=None):
5858
charter = get_object_or_404(Document, type="charter", name=name)
5959
group = charter.group
6060

61-
if not can_manage_group_type(request.user, group.type_id):
61+
if not can_manage_group(request.user, group):
6262
return HttpResponseForbidden("You don't have permission to access this view")
6363

6464
chartering_type = get_chartering_type(charter)
@@ -246,7 +246,7 @@ def change_title(request, name, option=None):
246246
logging the title as a comment."""
247247
charter = get_object_or_404(Document, type="charter", name=name)
248248
group = charter.group
249-
if not can_manage_group_type(request.user, group.type_id):
249+
if not can_manage_group(request.user, group):
250250
return HttpResponseForbidden("You don't have permission to access this view")
251251
login = request.user.person
252252
if request.method == 'POST':
@@ -359,7 +359,7 @@ def submit(request, name=None, option=None):
359359
charter = get_object_or_404(Document, type="charter", name=name)
360360
group = charter.group
361361

362-
if not can_manage_group_type(request.user, group.type_id):
362+
if not can_manage_group(request.user, group):
363363
return HttpResponseForbidden("You don't have permission to access this view")
364364

365365
path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter.canonical_name(), charter.rev))

ietf/doc/views_doc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
get_initial_notify, make_notify_changed_event, crawl_history)
5353
from ietf.community.models import CommunityList
5454
from ietf.group.models import Role
55-
from ietf.group.utils import can_manage_group_type, can_manage_materials
55+
from ietf.group.utils import can_manage_group, can_manage_materials
5656
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person, role_required
5757
from ietf.name.models import StreamName, BallotPositionName
5858
from ietf.person.models import Email
@@ -445,7 +445,7 @@ def document_main(request, name, rev=None):
445445
if chartering and not snapshot:
446446
milestones = doc.group.groupmilestone_set.filter(state="charter")
447447

448-
can_manage = can_manage_group_type(request.user, doc.group.type_id)
448+
can_manage = can_manage_group(request.user, doc.group)
449449

450450
return render_to_response("doc/document_charter.html",
451451
dict(doc=doc,

ietf/group/edit.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from ietf.doc.utils_charter import charter_name_for_group
1717
from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStateName,
1818
GroupStateTransitions, GroupTypeName, GroupURL, ChangeStateGroupEvent )
19-
from ietf.group.utils import save_group_in_history, can_manage_group_type, can_manage_team_group
19+
from ietf.group.utils import save_group_in_history, can_manage_group
2020
from ietf.group.utils import get_group_or_404
2121
from ietf.ietfauth.utils import has_role
2222
from ietf.person.fields import SearchableEmailsField
@@ -186,7 +186,7 @@ def submit_initial_charter(request, group_type=None, acronym=None):
186186
# This is where we start ignoring the passed in group_type
187187
group_type = group.type_id
188188

189-
if not can_manage_group_type(request.user, group_type):
189+
if not can_manage_group(request.user, group):
190190
return HttpResponseForbidden("You don't have permission to access this view")
191191

192192
if not group.charter:
@@ -199,18 +199,6 @@ def submit_initial_charter(request, group_type=None, acronym=None):
199199
def edit(request, group_type=None, acronym=None, action="edit"):
200200
"""Edit or create a group, notifying parties as
201201
necessary and logging changes as group events."""
202-
group = get_group_or_404(acronym, group_type)
203-
if not group_type and group:
204-
group_type = group.type_id
205-
206-
if group_type == "team":
207-
can_edit = can_manage_team_group(request.user, group) or group.has_role(request.user, "chair")
208-
else:
209-
can_edit = can_manage_group_type(request.user, group_type)
210-
211-
if not can_edit:
212-
return HttpResponseForbidden("You don't have permission to access this view")
213-
214202
if action == "edit":
215203
new_group = False
216204
elif action in ("create","charter"):
@@ -219,6 +207,13 @@ def edit(request, group_type=None, acronym=None, action="edit"):
219207
else:
220208
raise Http404
221209

210+
if not new_group:
211+
group = get_group_or_404(acronym, group_type)
212+
if not group_type and group:
213+
group_type = group.type_id
214+
if not (can_manage_group(request.user, group) or group.has_role(request.user, "chair")):
215+
return HttpResponseForbidden("You don't have permission to access this view")
216+
222217
if request.method == 'POST':
223218
form = GroupForm(request.POST, group=group, group_type=group_type)
224219
if form.is_valid():
@@ -369,7 +364,7 @@ def conclude(request, acronym, group_type=None):
369364
"""Request the closing of group, prompting for instructions."""
370365
group = get_group_or_404(acronym, group_type)
371366

372-
if not can_manage_group_type(request.user, group.type_id):
367+
if not can_manage_group(request.user, group):
373368
return HttpResponseForbidden("You don't have permission to access this view")
374369

375370
if request.method == 'POST':

ietf/group/info.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
from ietf.doc.templatetags.ietf_filters import clean_whitespace
5656
from ietf.group.models import Group, Role, ChangeStateGroupEvent
5757
from ietf.name.models import GroupTypeName
58-
from ietf.group.utils import get_charter_text, can_manage_group_type, can_manage_team_group, milestone_reviewer_for_group_type, can_provide_status_update
58+
from ietf.group.utils import get_charter_text, can_manage_group_type, can_manage_group, milestone_reviewer_for_group_type, can_provide_status_update
5959
from ietf.group.utils import can_manage_materials, get_group_or_404
6060
from ietf.utils.pipe import pipe
6161
from ietf.utils.textupload import get_cleaned_text_file_content
@@ -365,9 +365,7 @@ def construct_group_menu_context(request, group, selected, group_type, others):
365365
actions = []
366366

367367
is_chair = group.has_role(request.user, "chair")
368-
can_manage = can_manage_group_type(request.user, group.type_id)
369-
if group.type_id == "team":
370-
can_manage = can_manage_team_group(request.user, group)
368+
can_manage = can_manage_group(request.user, group)
371369

372370
if group.features.has_milestones:
373371
if group.state_id != "proposed" and (is_chair or can_manage):
@@ -491,7 +489,7 @@ def group_about(request, acronym, group_type=None):
491489
e = group.latest_event(type__in=("changed_state", "requested_close",))
492490
requested_close = group.state_id != "conclude" and e and e.type == "requested_close"
493491

494-
can_manage = can_manage_group_type(request.user, group.type_id)
492+
can_manage = can_manage_group(request.user, group)
495493

496494
can_provide_update = can_provide_status_update(request.user, group)
497495
status_update = group.latest_event(type="status_update")

ietf/group/milestones.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ietf.doc.utils import get_chartering_type
1313
from ietf.doc.fields import SearchableDocumentsField
1414
from ietf.group.models import GroupMilestone, MilestoneGroupEvent
15-
from ietf.group.utils import (save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type,
15+
from ietf.group.utils import (save_milestone_in_history, can_manage_group, milestone_reviewer_for_group_type,
1616
get_group_or_404)
1717
from ietf.name.models import GroupMilestoneStateName
1818
from ietf.group.mails import email_milestones_changed
@@ -93,8 +93,8 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
9393
raise Http404
9494

9595
needs_review = False
96-
if not can_manage_group_type(request.user, group.type_id):
97-
if group.role_set.filter(name="chair", person__user=request.user):
96+
if not can_manage_group(request.user, group):
97+
if group.has_role(request.user, "chair"):
9898
if milestone_set == "current":
9999
needs_review = True
100100
else:
@@ -329,8 +329,9 @@ def reset_charter_milestones(request, group_type, acronym):
329329
if not group.features.has_milestones:
330330
raise Http404
331331

332-
if (not can_manage_group_type(request.user, group_type) and
333-
not group.role_set.filter(name="chair", person__user=request.user)):
332+
can_manage = can_manage_group(request.user, group)
333+
is_chair = group.has_role(request.user, "chair")
334+
if (not can_manage) and (not is_chair):
334335
return HttpResponseForbidden("You are not chair of this group.")
335336

336337
current_milestones = group.groupmilestone_set.filter(state="active")

ietf/group/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def has_role(self, user, role_names):
8282

8383
def is_decendant_of(self, sought_parent):
8484
p = self.parent
85-
while (p != None):
85+
while ((p != None) and (p != self)):
8686
if p.acronym == sought_parent:
8787
return True
8888
p = p.parent

ietf/group/tests_info.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,5 +1125,44 @@ def test_edit_status_update(self):
11251125
self.assertEqual(response.status_code, 302)
11261126
self.assertEqual(chair.group.latest_event(type='status_update').desc,'This came from a file.')
11271127

1128+
class GroupParentLoopTests(TestCase):
11281129

1129-
1130+
def test_group_parent_loop(self):
1131+
make_test_data()
1132+
mars = Group.objects.get(acronym="mars")
1133+
test1 = Group.objects.create(
1134+
type_id="team",
1135+
acronym="testteam1",
1136+
name="Test One",
1137+
description="The test team 1 is testing.",
1138+
state_id="active",
1139+
parent = mars,
1140+
)
1141+
test2 = Group.objects.create(
1142+
type_id="team",
1143+
acronym="testteam2",
1144+
name="Test Two",
1145+
description="The test team 2 is testing.",
1146+
state_id="active",
1147+
parent = test1,
1148+
)
1149+
# Change the parent of Mars to make a loop
1150+
mars.parent = test2
1151+
1152+
# In face of the loop in the parent links, the code should not loop forever
1153+
import signal
1154+
1155+
def timeout_handler(signum, frame):
1156+
raise Exception("Infinite loop in parent links is not handeled properly.")
1157+
1158+
signal.signal(signal.SIGALRM, timeout_handler)
1159+
signal.alarm(1) # One second
1160+
try:
1161+
test2.is_decendant_of("ietf")
1162+
except Exception:
1163+
raise
1164+
finally:
1165+
signal.alarm(0)
1166+
1167+
# If we get here, then there is not an infinite loop
1168+
return

ietf/group/utils.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,16 @@ def can_manage_group_type(user, group_type):
9191

9292
return has_role(user, 'Secretariat')
9393

94-
def can_manage_team_group(user, group):
95-
if group.type_id != "team":
96-
return False
97-
elif group.is_decendant_of("ietf") and has_role(user, ('Area Director', 'Secretariat')):
98-
return True
99-
elif group.is_decendant_of("irtf") and has_role(user, ('IRTF Chair', 'Secretariat')):
100-
return True
94+
def can_manage_group(user, group):
95+
if group.type_id == "rg":
96+
return has_role(user, ('IRTF Chair', 'Secretariat'))
97+
elif group.type_id == "wg":
98+
return has_role(user, ('Area Director', 'Secretariat'))
99+
elif group.type_id == "team":
100+
if group.is_decendant_of("ietf"):
101+
return has_role(user, ('Area Director', 'Secretariat'))
102+
elif group.is_decendant_of("irtf"):
103+
return has_role(user, ('IRTF Chair', 'Secretariat'))
101104
return has_role(user, ('Secretariat'))
102105

103106
def milestone_reviewer_for_group_type(group_type):

0 commit comments

Comments
 (0)