Skip to content

Commit d19179b

Browse files
committed
Merged in [13181] from housley@vigilsec.com:
This completes a good chunk of the downref registry work requested in ticket ietf-tools#2069. The registry has been imported to the database and can be shown, and entries can be added to the registry. Addresses issue ietf-tools#2069. - Legacy-Id: 13190 Note: SVN reference [13181] has been migrated to Git commit f012563
2 parents 6bdf6ff + f012563 commit d19179b

12 files changed

Lines changed: 547 additions & 2 deletions

ietf/doc/forms.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
import datetime
1+
# Copyright The IETF Trust 2017, All Rights Reserved
2+
3+
from __future__ import unicode_literals
24

5+
import datetime
6+
import debug #pyflakes:ignore
37
from django import forms
48

9+
from ietf.doc.fields import SearchableDocAliasesField, SearchableDocAliasField
10+
from ietf.doc.models import RelatedDocument
511
from ietf.iesg.models import TelechatDate
612
from ietf.iesg.utils import telechat_page_count
713

@@ -45,3 +51,64 @@ class NotifyForm(forms.Form):
4551
def clean_notify(self):
4652
addrspecs = [x.strip() for x in self.cleaned_data["notify"].split(',')]
4753
return ', '.join(addrspecs)
54+
55+
IESG_APPROVED_STATE_LIST = ("ann", "rfcqueue", "pub")
56+
57+
class AddDownrefForm(forms.Form):
58+
rfc = SearchableDocAliasField(
59+
label="Referenced RFC",
60+
help_text="The RFC that is approved for downref",
61+
required=True)
62+
drafts = SearchableDocAliasesField(
63+
label="Internet-Drafts that makes the reference",
64+
help_text="The drafts that approve the downref in their Last Call",
65+
required=True)
66+
67+
def clean_rfc(self):
68+
if 'rfc' not in self.cleaned_data:
69+
raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft")
70+
71+
rfc = self.cleaned_data['rfc']
72+
if not rfc.document.is_rfc():
73+
raise forms.ValidationError("Cannot find the RFC: " + rfc.name)
74+
return rfc
75+
76+
def clean_drafts(self):
77+
if 'drafts' not in self.cleaned_data:
78+
raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft")
79+
80+
v_err_names = []
81+
drafts = self.cleaned_data['drafts']
82+
for da in drafts:
83+
state = da.document.get_state("draft-iesg")
84+
if not state or state.slug not in IESG_APPROVED_STATE_LIST:
85+
v_err_names.append(da.name)
86+
if v_err_names:
87+
raise forms.ValidationError("Draft is not yet approved: " + ", ".join(v_err_names))
88+
return drafts
89+
90+
def clean(self):
91+
if 'rfc' not in self.cleaned_data or 'drafts' not in self.cleaned_data:
92+
raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft")
93+
94+
v_err_pairs = []
95+
rfc = self.cleaned_data['rfc']
96+
drafts = self.cleaned_data['drafts']
97+
for da in drafts:
98+
if RelatedDocument.objects.filter(source=da.document, target=rfc, relationship_id='downref-approval'):
99+
v_err_pairs.append(da.name + " --> RFC " + rfc.document.rfc_number())
100+
if v_err_pairs:
101+
raise forms.ValidationError("Downref is already in the registry: " + ", ".join(v_err_pairs))
102+
103+
if 'save_downref_anyway' not in self.data:
104+
# this check is skipped if the save_downref_anyway button is used
105+
v_err_refnorm = ""
106+
for da in drafts:
107+
if not RelatedDocument.objects.filter(source=da.document, target=rfc, relationship_id='refnorm'):
108+
if v_err_refnorm:
109+
v_err_refnorm = v_err_refnorm + " or " + da.name
110+
else:
111+
v_err_refnorm = da.name
112+
if v_err_refnorm:
113+
v_err_refnorm_prefix = "There does not seem to be a normative reference to RFC " + rfc.document.rfc_number() + " by "
114+
raise forms.ValidationError(v_err_refnorm_prefix + v_err_refnorm)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Copyright The IETF Trust 2017, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
4+
from __future__ import unicode_literals
5+
6+
from django.db import migrations
7+
8+
downref_registry_from_wiki = [
9+
['rfc952', 'draft-hollenbeck-rfc4931bis'],
10+
['rfc952', 'draft-hollenbeck-rfc4932bis'],
11+
['rfc1094','draft-ietf-nfsv4-nfsdirect'],
12+
['rfc1321','rfc3967'],
13+
['rfc1813','draft-ietf-nfsv4-nfsdirect'],
14+
['rfc1951','draft-ietf-lemonade-compress'],
15+
['rfc1952','draft-sweet-rfc2911bis'],
16+
['rfc1977','draft-sweet-rfc2911bis'],
17+
['rfc2104','rfc3967'],
18+
['rfc2144','draft-ietf-secsh-newmodes'],
19+
['rfc2315','draft-eastlake-additional-xmlsec-uris'],
20+
['rfc2330','draft-ietf-ippm-metrictest'],
21+
['rfc2412','draft-ietf-cat-kerberos-pk-init'],
22+
['rfc2648','draft-ietf-simple-xcap-diff'],
23+
['rfc2683','draft-ietf-qresync-rfc5162bis'],
24+
['rfc2702','draft-ietf-isis-admin-tags'],
25+
['rfc2781','draft-ietf-appsawg-xml-mediatypes'],
26+
['rfc2818','draft-dusseault-caldav'],
27+
['rfc2898','draft-turner-asymmetrickeyformat-algs'],
28+
['rfc2966','draft-ietf-isis-admin-tags'],
29+
['rfc2985','rfc5750'],
30+
['rfc2986','rfc6487'],
31+
['rfc3032','draft-ietf-pals-rfc4447bis'],
32+
['rfc3174','draft-harris-ssh-rsa-kex'],
33+
['rfc3196','draft-sweet-rfc2911bis'],
34+
['rfc3217','draft-ietf-smime-cms-rsa-kem'],
35+
['rfc3272','draft-ietf-mpls-cosfield-def'],
36+
['rfc3280','rfc3852'],
37+
['rfc3281','rfc3852'],
38+
['rfc3394','draft-ietf-smime-cms-rsa-kem'],
39+
['rfc3447','draft-ietf-cat-kerberos-pk-init'],
40+
['rfc3469','draft-ietf-mpls-cosfield-def'],
41+
['rfc3548','draft-ietf-dnsext-dnssec-records'],
42+
['rfc3564','draft-ietf-mpls-cosfield-def'],
43+
['rfc3567','draft-ietf-pce-disco-proto-isis'],
44+
['rfc3610','rfc4309'],
45+
['rfc3843','rfc5953'],
46+
['rfc3579','draft-ietf-radext-rfc4590bis'],
47+
['rfc3618','draft-ietf-mboned-msdp-deploy'],
48+
['rfc3713','draft-kato-ipsec-ciph-camellia'],
49+
['rfc3784','draft-ietf-isis-admin-tags'],
50+
['rfc3985','draft-ietf-mpls-cosfield-def'],
51+
['rfc4050','draft-eastlake-additional-xmlsec-uris'],
52+
['rfc4082','draft-ietf-msec-srtp-tesla'],
53+
['rfc4226','draft-ietf-keyprov-pskc'],
54+
['rfc4269','draft-eastlake-additional-xmlsec-uris'],
55+
['rfc4291','draft-hollenbeck-rfc4932bis'],
56+
['rfc4347','rfc5953'],
57+
['rfc4357','draft-ietf-pkix-gost-cppk'],
58+
['rfc4366','rfc5953'],
59+
['rfc4492','draft-ietf-tls-chacha20-poly1305'],
60+
['rfc4493','draft-songlee-aes-cmac-96'],
61+
['rfc4627','draft-ietf-mediactrl-ivr-control-package'],
62+
['rfc4753','draft-ietf-ipsec-ike-auth-ecdsa'],
63+
['rfc4949','draft-ietf-oauth-v2'],
64+
['rfc5036','draft-ietf-pals-rfc4447bis'],
65+
['rfc5246','rfc5953'],
66+
['rfc5280','rfc5953'],
67+
['rfc5322','draft-hollenbeck-rfc4933bis'],
68+
['rfc5410','draft-arkko-mikey-iana'],
69+
['rfc5489','draft-ietf-tls-chacha20-poly1305'],
70+
['rfc5598','draft-ietf-dkim-mailinglists'],
71+
['rfc5649','draft-turner-asymmetrickeyformat-algs'],
72+
['rfc5753','draft-turner-cms-symmetrickeypackage-algs'],
73+
['rfc5781','draft-ietf-sidr-res-certs'],
74+
['rfc5869','draft-ietf-trill-channel-tunnel'],
75+
['rfc5890','draft-ietf-dkim-rfc4871bis'],
76+
['rfc5911','draft-turner-asymmetrickeyformat'],
77+
['rfc5912','draft-ietf-pkix-authorityclearanceconstraints'],
78+
['rfc5952','rfc5953'],
79+
['rfc6043','draft-arkko-mikey-iana'],
80+
['rfc6090','draft-turner-akf-algs-update'],
81+
['rfc6151','draft-ietf-netmod-system-mgmt'],
82+
['rfc6234','draft-schaad-pkix-rfc2875-bis'],
83+
['rfc6386','draft-ietf-rtcweb-video'],
84+
['rfc6480','rfc6485'],
85+
['rfc6480','rfc6489'],
86+
['rfc6480','rfc6491'],
87+
['rfc6480','rfc7935'],
88+
['rfc6707','draft-ietf-cdni-metadata'],
89+
['rfc6839','draft-ietf-appsawg-xml-mediatypes'],
90+
['rfc7251','rfc7252'],
91+
['rfc7358','draft-ietf-pals-rfc4447bis'],
92+
['rfc7539','draft-ietf-tls-chacha20-poly1305'],
93+
['rfc7612','draft-sweet-rfc2911bis'],
94+
['rfc7748','draft-ietf-jose-cfrg-curves'],
95+
['rfc8032','draft-ietf-jose-cfrg-curves'] ]
96+
97+
98+
def addDownrefRelationships(apps,schema_editor):
99+
DocAlias = apps.get_model('doc','DocAlias')
100+
RelatedDocument = apps.get_model('doc','RelatedDocument')
101+
102+
for [fn2, fn1] in downref_registry_from_wiki:
103+
da1 = DocAlias.objects.get(name=fn1)
104+
da2 = DocAlias.objects.get(name=fn2)
105+
RelatedDocument.objects.create(source=da1.document,
106+
target=da2, relationship_id='downref-approval')
107+
108+
109+
def removeDownrefRelationships(apps,schema_editor):
110+
DocAlias = apps.get_model('doc','DocAlias')
111+
RelatedDocument = apps.get_model('doc','RelatedDocument')
112+
113+
for [fn2, fn1] in downref_registry_from_wiki:
114+
da1 = DocAlias.objects.get(name=fn1)
115+
da2 = DocAlias.objects.get(name=fn2)
116+
RelatedDocument.objects.filter(source=da1.document,
117+
target=da2, relationship_id='downref-approval').delete()
118+
119+
120+
class Migration(migrations.Migration):
121+
122+
dependencies = [
123+
('name', '0019_add_docrelationshipname_downref_approval'),
124+
('doc', '0025_auto_20170307_0146'),
125+
]
126+
127+
operations = [
128+
migrations.RunPython(addDownrefRelationships,removeDownrefRelationships)
129+
]

ietf/doc/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,9 @@ class DocReminder(models.Model):
888888
("requested_review", "Requested review"),
889889
("assigned_review_request", "Assigned review request"),
890890
("closed_review_request", "Closed review request"),
891+
892+
# downref
893+
("downref_approved", "Downref approved"),
891894
]
892895

893896
class DocEvent(models.Model):

ietf/doc/tests_downref.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright The IETF Trust 2017, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
4+
from __future__ import unicode_literals
5+
6+
from django.core.urlresolvers import reverse as urlreverse
7+
8+
import debug # pyflakes:ignore
9+
10+
from ietf.doc.models import Document, DocAlias, RelatedDocument, State
11+
from ietf.utils.test_utils import TestCase
12+
from ietf.utils.test_data import make_test_data, make_downref_test_data
13+
from ietf.utils.test_utils import login_testing_unauthorized, unicontent
14+
15+
class Downref(TestCase):
16+
def test_downref_registry(self):
17+
url = urlreverse('ietf.doc.views_downref.downref_registry')
18+
19+
# normal - get the table without the "Add downref" button
20+
self.client.login(username="plain", password="plain+password")
21+
r = self.client.get(url)
22+
self.assertEqual(r.status_code, 200)
23+
content = unicontent(r)
24+
self.assertTrue('<h1>Downref registry</h1>' in content)
25+
self.assertFalse('Add downref' in content)
26+
27+
# secretariat - get the table with the "Add downref" button
28+
self.client.login(username='secretary', password='secretary+password')
29+
r = self.client.get(url)
30+
self.assertEqual(r.status_code, 200)
31+
content = unicontent(r)
32+
self.assertTrue('<h1>Downref registry</h1>' in content)
33+
self.assertTrue('Add downref' in content)
34+
35+
# area director - get the table with the "Add downref" button
36+
self.client.login(username='ad', password='ad+password')
37+
r = self.client.get(url)
38+
self.assertEqual(r.status_code, 200)
39+
content = unicontent(r)
40+
self.assertTrue('<h1>Downref registry</h1>' in content)
41+
self.assertTrue('Add downref' in content)
42+
43+
def test_downref_registry_add(self):
44+
url = urlreverse('ietf.doc.views_downref.downref_registry_add')
45+
login_testing_unauthorized(self, "plain", url)
46+
47+
# secretariat - get the form to add entries to the registry
48+
self.client.login(username='secretary', password='secretary+password')
49+
r = self.client.get(url)
50+
self.assertEqual(r.status_code, 200)
51+
content = unicontent(r)
52+
self.assertTrue('<h1>Add entry to the downref registry</h1>' in content)
53+
self.assertTrue('Save downref' in content)
54+
55+
# area director - get the form to add entries to the registry
56+
self.client.login(username='ad', password='ad+password')
57+
r = self.client.get(url)
58+
self.assertEqual(r.status_code, 200)
59+
content = unicontent(r)
60+
self.assertTrue('<h1>Add entry to the downref registry</h1>' in content)
61+
self.assertTrue('Save downref' in content)
62+
63+
# error - already in the downref registry
64+
r = self.client.post(url, dict(rfc='rfc9998', drafts=('draft-ietf-mars-approved-document', )))
65+
self.assertEqual(r.status_code, 200)
66+
content = unicontent(r)
67+
self.assertTrue('Downref is already in the registry' in content)
68+
69+
# error - source is not in an approved state
70+
r = self.client.get(url)
71+
self.assertEqual(r.status_code, 200)
72+
r = self.client.post(url, dict(rfc='rfc9998', drafts=('draft-ietf-mars-test', )))
73+
self.assertEqual(r.status_code, 200)
74+
content = unicontent(r)
75+
self.assertTrue('Draft is not yet approved' in content)
76+
77+
# error - the target is not a normative reference of the source
78+
draft = Document.objects.get(name="draft-ietf-mars-test")
79+
draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="pub"))
80+
r = self.client.get(url)
81+
self.assertEqual(r.status_code, 200)
82+
r = self.client.post(url, dict(rfc='rfc9998', drafts=('draft-ietf-mars-test', )))
83+
self.assertEqual(r.status_code, 200)
84+
content = unicontent(r)
85+
self.assertTrue('There does not seem to be a normative reference to RFC' in content)
86+
self.assertTrue('Save downref anyway' in content)
87+
88+
# normal - approve the document so the downref is now okay
89+
rfc = DocAlias.objects.get(name="rfc9998")
90+
RelatedDocument.objects.create(source=draft, target=rfc, relationship_id='refnorm')
91+
draft_de_count_before = draft.docevent_set.count()
92+
rfc_de_count_before = rfc.document.docevent_set.count()
93+
94+
r = self.client.get(url)
95+
self.assertEqual(r.status_code, 200)
96+
r = self.client.post(url, dict(rfc='rfc9998', drafts=('draft-ietf-mars-test', )))
97+
self.assertEqual(r.status_code, 302)
98+
newurl = urlreverse('ietf.doc.views_downref.downref_registry')
99+
r = self.client.get(newurl)
100+
self.assertEqual(r.status_code, 200)
101+
content = unicontent(r)
102+
self.assertTrue('<a href="/doc/draft-ietf-mars-test' in content)
103+
self.assertTrue(RelatedDocument.objects.filter(source=draft, target=rfc, relationship_id='downref-approval'))
104+
self.assertEqual(draft.docevent_set.count(), draft_de_count_before + 1)
105+
self.assertEqual(rfc.document.docevent_set.count(), rfc_de_count_before + 1)
106+
107+
def setUp(self):
108+
make_test_data()
109+
make_downref_test_data()

ietf/doc/urls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from django.views.generic import RedirectView
3535
from django.conf import settings
3636

37-
from ietf.doc import views_search, views_draft, views_ballot, views_status_change, views_doc, views_stats, views_help
37+
from ietf.doc import views_search, views_draft, views_ballot, views_status_change, views_doc, views_downref, views_stats, views_help
3838
from ietf.utils.urls import url
3939

4040
session_patterns = [
@@ -54,6 +54,8 @@
5454
url(r'^start-rfc-status-change/(?:%(name)s/)?$' % settings.URL_REGEXPS, views_status_change.start_rfc_status_change),
5555
url(r'^iesg/(?P<last_call_only>[A-Za-z0-9.-]+/)?$', views_search.drafts_in_iesg_process),
5656
url(r'^email-aliases/$', views_doc.email_aliases),
57+
url(r'^downref/$', views_downref.downref_registry),
58+
url(r'^downref/add/?$', views_downref.downref_registry_add),
5759
url(r'^stats/newrevisiondocevent/?$', views_stats.chart_newrevisiondocevent),
5860
url(r'^stats/newrevisiondocevent/conf/?$', views_stats.chart_conf_newrevisiondocevent),
5961
url(r'^stats/newrevisiondocevent/data/?$', views_stats.chart_data_newrevisiondocevent),

0 commit comments

Comments
 (0)