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
4 changes: 4 additions & 0 deletions ietf/ietfauth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ def has_role(user, role_names, *args, **kwargs):
group__type="sdo",
group__state="active",
),
"Liaison Coordinator": Q(
name="liaison_coordinator",
group__acronym="iab",
),
"Authorized Individual": Q(
name="auth",
group__type="sdo",
Expand Down
33 changes: 22 additions & 11 deletions ietf/liaisons/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ def internal_groups_for_person(person: Optional[Person]):
if has_role(
person.user,
(
"Secretariat",
"IETF Chair",
"IAB Chair",
"IAB Executive Director",
"Liaison Manager",
"Authorized Individual",
), # todo liaison coordinator as well
"Secretariat",
"IETF Chair",
"IAB Chair",
"IAB Executive Director",
"Liaison Manager",
"Liaison Coordinator",
"Authorized Individual",
),
):
return all_internal_groups()
# Interesting roles, as Group queries
Expand Down Expand Up @@ -491,8 +492,12 @@ def is_approved(self):
return True

def get_post_only(self):
from_groups = self.cleaned_data.get('from_groups')
if has_role(self.user, "Secretariat") or is_authorized_individual(self.user,from_groups):
from_groups = self.cleaned_data.get("from_groups")
if (
has_role(self.user, "Secretariat")
or has_role(self.user, "Liaison Coordinator")
or is_authorized_individual(self.user, from_groups)
):
return False
return True

Expand All @@ -505,8 +510,14 @@ def set_from_fields(self):
if len(qs) == 1:
self.fields['from_groups'].initial = qs

if not has_role(self.user, "Secretariat"):
self.fields["from_contact"].initial = self.person.role_set.filter(group=qs[0]).first().email.address
# Note that the IAB chair currently doesn't get to work with incoming liaison statements
if not (
has_role(self.user, "Secretariat")
or has_role(self.user, "Liaison Coordinator")
):
self.fields["from_contact"].initial = (
self.person.role_set.filter(group=qs[0]).first().email.address
)
self.fields["from_contact"].widget.attrs["disabled"] = True

def set_to_fields(self):
Expand Down
22 changes: 21 additions & 1 deletion ietf/liaisons/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,11 +462,12 @@ def test_edit_liaison(self):


def test_incoming_access(self):
'''Ensure only Secretariat, Liaison Managers, and Authorized Individuals
'''Ensure only Secretariat, Liaison Managers, Liaison Coordinators, and Authorized Individuals
have access to incoming liaisons.
'''
sdo = RoleFactory(name_id='liaiman',group__type_id='sdo', person__user__username='ulm-liaiman').group
RoleFactory(name_id='auth',group=sdo,person__user__username='ulm-auth')
RoleFactory(name_id='liaison_coordinator', group__acronym='iab', person__user__username='liaison-coordinator')
stmt = LiaisonStatementFactory(from_groups=[sdo,])
LiaisonStatementEventFactory(statement=stmt,type_id='posted')
RoleFactory(name_id='chair',person__user__username='marschairman',group__acronym='mars')
Expand Down Expand Up @@ -499,6 +500,15 @@ def test_incoming_access(self):
r = self.client.get(addurl)
self.assertEqual(r.status_code, 200)

# Liaison Coordinator has access
self.client.login(username="liaison-coordinator", password="liaison-coordinator+password")
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('a.btn:contains("New incoming liaison")')), 1)
r = self.client.get(addurl)
self.assertEqual(r.status_code, 200)

# Authorized Individual has access
self.client.login(username="ulm-auth", password="ulm-auth+password")
r = self.client.get(url)
Expand All @@ -521,6 +531,7 @@ def test_outgoing_access(self):

sdo = RoleFactory(name_id='liaiman',group__type_id='sdo', person__user__username='ulm-liaiman').group
RoleFactory(name_id='auth',group=sdo,person__user__username='ulm-auth')
RoleFactory(name_id='liaison_coordinator', group__acronym='iab', person__user__username='liaison-coordinator')
mars = RoleFactory(name_id='chair',person__user__username='marschairman',group__acronym='mars').group
RoleFactory(name_id='secr',group=mars,person__user__username='mars-secr')
RoleFactory(name_id='execdir',group=Group.objects.get(acronym='iab'),person__user__username='iab-execdir')
Expand Down Expand Up @@ -599,6 +610,15 @@ def test_outgoing_access(self):
r = self.client.get(addurl)
self.assertEqual(r.status_code, 200)

# Liaison Coordinator has access
self.assertTrue(self.client.login(username="liaison-coordinator", password="liaison-coordinator+password"))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('a.btn:contains("New outgoing liaison")')), 1)
r = self.client.get(addurl)
self.assertEqual(r.status_code, 200)

# Authorized Individual has no access
self.assertTrue(self.client.login(username="ulm-auth", password="ulm-auth+password"))
r = self.client.get(url)
Expand Down
2 changes: 2 additions & 0 deletions ietf/liaisons/tests_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ def test_external_groups_for_person(self):
group=Group.objects.get(acronym="iab"),
person__user__username="iab-execdir",
)
RoleFactory(name_id="liaison_coordinator", group__acronym="iab", person__user__username="liaison-coordinator")
the_sdo = GroupFactory(type_id="sdo", acronym="the-sdo")
liaison_manager = RoleFactory(name_id="liaiman", group=the_sdo).person
authperson = RoleFactory(name_id="auth", group=the_sdo).person
Expand All @@ -166,6 +167,7 @@ def test_external_groups_for_person(self):
"ietf-chair",
"iab-chair",
"iab-execdir",
"liaison-coordinator",
"ad",
"sopschairman",
"sopssecretary",
Expand Down
3 changes: 2 additions & 1 deletion ietf/liaisons/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
"IAB Executive Director",
"IETF Chair",
"Liaison Manager",
"Liaison Coordinator",
"Secretariat",
"WG Chair",
"WG Secretary",
]

# Roles allowed to create and manage incoming liaison statements.
INCOMING_LIAISON_ROLES = ["Authorized Individual", "Liaison Manager", "Secretariat"]
INCOMING_LIAISON_ROLES = ["Authorized Individual", "Liaison Manager", "Liaison Coordinator", "Secretariat"]

can_submit_liaison_required = passes_test_decorator(
lambda u, *args, **kwargs: can_add_liaison(u),
Expand Down
8 changes: 7 additions & 1 deletion ietf/liaisons/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,13 @@ def post_only(group,person):
- Authorized Individuals have full access for the group they are associated with
- Liaison Managers can post only
'''
if group.type_id == 'sdo' and ( not(has_role(person.user,"Secretariat") or group.role_set.filter(name='auth',person=person)) ):
if group.type_id == "sdo" and (
not (
has_role(person.user, "Secretariat")
or has_role(person.user, "Liaison Coordinator")
or group.role_set.filter(name="auth", person=person)
)
):
return True
else:
return False
Expand Down
40 changes: 38 additions & 2 deletions ietf/name/fixtures/names.json
Original file line number Diff line number Diff line change
Expand Up @@ -5392,6 +5392,21 @@
"model": "mailtrigger.mailtrigger",
"pk": "review_completed_opsdir_telechat"
},
{
"fields": {
"cc": [
"ietf_last_call",
"review_doc_all_parties",
"review_doc_group_mail_list"
],
"desc": "Recipients when a perfmetrdir Telechat review is completed",
"to": [
"review_team_mail_list"
]
},
"model": "mailtrigger.mailtrigger",
"pk": "review_completed_perfmetrdir_telechat"
},
{
"fields": {
"cc": [
Expand Down Expand Up @@ -11330,6 +11345,17 @@
"model": "name.extresourcename",
"pk": "mailing_list_archive"
},
{
"fields": {
"desc": "ORCID",
"name": "ORCID",
"order": 0,
"type": "url",
"used": true
},
"model": "name.extresourcename",
"pk": "orcid"
},
{
"fields": {
"desc": "Related Implementations",
Expand Down Expand Up @@ -13562,7 +13588,7 @@
"desc": "",
"name": "Liaison CC Contact",
"order": 9,
"used": true
"used": false
},
"model": "name.rolename",
"pk": "liaison_cc_contact"
Expand All @@ -13572,11 +13598,21 @@
"desc": "",
"name": "Liaison Contact",
"order": 8,
"used": true
"used": false
},
"model": "name.rolename",
"pk": "liaison_contact"
},
{
"fields": {
"desc": "Coordinates liaison handling for the IAB",
"name": "Liaison Coordinator",
"order": 0,
"used": true
},
"model": "name.rolename",
"pk": "liaison_coordinator"
},
{
"fields": {
"desc": "",
Expand Down
33 changes: 33 additions & 0 deletions ietf/name/migrations/0018_alter_liaisonrolenames.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright The IETF Trust 2025, All Rights Reserved# Generated by Django 4.2.21 on 2025-05-30 16:35

from django.db import migrations


def forward(apps, schema_editor):
RoleName = apps.get_model("name", "RoleName")
RoleName.objects.filter(slug__in=["liaison_contact", "liaison_cc_contact"]).update(
used=False
)
RoleName.objects.get_or_create(
slug="liaison_coordinator",
defaults={
"name": "Liaison Coordinator",
"desc": "Coordinates liaison handling for the IAB",
},
)


def reverse(apps, schema_editor):
RoleName = apps.get_model("name", "RoleName")
RoleName.objects.filter(slug__in=["liaison_contact", "liaison_cc_contact"]).update(
used=True
)
RoleName.objects.filter(slug="liaison_coordinator").delete()


class Migration(migrations.Migration):
dependencies = [
("name", "0017_populate_new_reg_names"),
]

operations = [migrations.RunPython(forward, reverse)]