Skip to content

Commit 9b00517

Browse files
committed
View to enter a private key that will be encrypted and saved into the user session.
Util functions to encrypt/decrypt the private key. Decorator to redirect to the private key input view if there is no key defined in the user session. Templatetag 'decrypt' that decrypts text using the private key provided to the templatetag. Fixes ietf-tools#947 - Legacy-Id: 5335
1 parent 20320ea commit 9b00517

8 files changed

Lines changed: 127 additions & 6 deletions

File tree

ietf/nomcom/decorators.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
from django.core.urlresolvers import reverse
2+
from django.http import HttpResponseRedirect
3+
from django.utils.http import urlquote
4+
15
from ietf.ietfauth.decorators import passes_test_decorator, has_role
26

37
from ietf.nomcom.utils import get_nomcom_by_year
@@ -16,3 +20,15 @@ def _is_nomcom_member(user, *args, **kwargs):
1620
return nomcom.group.is_member(user)
1721
return False
1822
return passes_test_decorator(_is_nomcom_member, 'Restricted to NomCom %s' % role)
23+
24+
25+
def private_key_required(view_func):
26+
def inner(request, *args, **kwargs):
27+
year = kwargs.get('year', None)
28+
if not year:
29+
raise Exception, 'View decorated with private_key_required must receive a year argument'
30+
if not 'NOMCOM_PRIVATE_KEY_%s' % year in request.session:
31+
return HttpResponseRedirect('%s?back_to=%s' % (reverse('nomcom_private_key', None, args=(year, )), urlquote(request.get_full_path())))
32+
else:
33+
return view_func(request, *args, **kwargs)
34+
return inner

ietf/nomcom/forms.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,10 @@ def __init__(self, *args, **kwargs):
440440
def save(self, *args, **kwargs):
441441
self.instance.nomcom = self.nomcom
442442
super(PositionForm, self).save(*args, **kwargs)
443+
444+
445+
class PrivateKeyForm(BaseNomcomForm, forms.Form):
446+
447+
key = forms.CharField(label='Private key', widget=forms.Textarea(), required=False)
448+
449+
fieldsets = [('Private key', ('key',))]

ietf/nomcom/templatetags/nomcom_tags.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import os
2+
import tempfile
3+
14
from django import template
5+
from django.conf import settings
26

37
from ietf.ietfauth.decorators import has_role
48
from ietf.nomcom.utils import get_nomcom_by_year
9+
from ietf.utils.pipe import pipe
510

611
register = template.Library()
712

@@ -14,3 +19,25 @@ def is_chair(user, year):
1419
if has_role(user, "Secretariat"):
1520
return True
1621
return nomcom.group.is_chair(user)
22+
23+
24+
@register.filter
25+
def decrypt(string, key=None):
26+
if not key:
27+
return '<-Encripted text [No private key provided]->'
28+
29+
encrypted_file = tempfile.NamedTemporaryFile(delete=False)
30+
encrypted_file.write(string)
31+
encrypted_file.close()
32+
33+
command = "%s smime -decrypt -in %s -inkey /dev/stdin"
34+
code, out, error = pipe(command % (settings.OPENSSL_COMMAND,
35+
encrypted_file.name), key)
36+
37+
38+
os.unlink(encrypted_file.name)
39+
40+
if error:
41+
return '<-Encripted text [Your private key is invalid]->'
42+
43+
return out

ietf/nomcom/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
urlpatterns = patterns('ietf.nomcom.views',
66
url(r'^(?P<year>\d{4})/private/$', 'private_index', name='nomcom_private_index'),
7+
url(r'^(?P<year>\d{4})/private/key/$', 'private_key', name='nomcom_private_key'),
78
url(r'^(?P<year>\d{4})/private/nominate/$', 'private_nominate', name='nomcom_private_nominate'),
89
url(r'^(?P<year>\d{4})/private/merge/$', 'private_merge', name='nomcom_private_merge'),
910
url(r'^(?P<year>\d{4})/private/edit-members/$', EditMembersFormPreview(EditMembersForm), name='nomcom_edit_members'),

ietf/nomcom/utils.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from django.shortcuts import get_object_or_404
1+
from django.conf import settings
22
from django.core.exceptions import PermissionDenied
3+
from django.shortcuts import get_object_or_404
34

4-
from ietf.person.models import Email
55
from ietf.dbtemplate.models import DBTemplate
6+
from ietf.person.models import Email
7+
from ietf.utils.pipe import pipe
68

79
MAIN_NOMCOM_TEMPLATE_PATH = '/nomcom/defaults/'
810
QUESTIONNAIRE_TEMPLATE = 'position/questionnaire.txt'
@@ -85,3 +87,27 @@ def initialize_requirements_for_position(position):
8587
variables=template.variables,
8688
type_id=template.type_id,
8789
content=template.content)
90+
91+
92+
def retrieve_nomcom_private_key(request, year):
93+
private_key = request.session.get('NOMCOM_PRIVATE_KEY_%s' % year, None)
94+
95+
if not private_key:
96+
return private_key
97+
98+
command = "%s bf -d -in /dev/stdin -k \"%s\" -a"
99+
code, out, error = pipe(command % (settings.OPENSSL_COMMAND,
100+
settings.SECRET_KEY), private_key)
101+
return out
102+
103+
104+
def store_nomcom_private_key(request, year, private_key):
105+
if not private_key:
106+
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = ''
107+
else:
108+
command = "%s bf -e -in /dev/stdin -k \"%s\" -a"
109+
code, out, error = pipe(command % (settings.OPENSSL_COMMAND,
110+
settings.SECRET_KEY), private_key)
111+
if error:
112+
out = ''
113+
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = out

ietf/nomcom/views.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99

1010
from ietf.dbtemplate.models import DBTemplate
1111
from ietf.dbtemplate.views import template_edit
12-
from ietf.nomcom.decorators import member_required
12+
from ietf.nomcom.decorators import member_required, private_key_required
1313
from ietf.nomcom.forms import (EditPublicKeyForm, NominateForm, MergeForm,
14-
NomComTemplateForm, PositionForm)
14+
NomComTemplateForm, PositionForm, PrivateKeyForm)
1515
from ietf.nomcom.models import Position
16-
from ietf.nomcom.utils import get_nomcom_by_year, HOME_TEMPLATE
16+
from ietf.nomcom.utils import (get_nomcom_by_year, HOME_TEMPLATE,
17+
retrieve_nomcom_private_key,
18+
store_nomcom_private_key)
1719

1820

1921
def index(request, year):
@@ -27,6 +29,28 @@ def index(request, year):
2729
'template': template}, RequestContext(request))
2830

2931

32+
@member_required(role='member')
33+
def private_key(request, year):
34+
nomcom = get_nomcom_by_year(year)
35+
private_key = retrieve_nomcom_private_key(request, year)
36+
37+
back_url = request.GET.get('back_to', reverse('nomcom_private_index', None, args=(year, )))
38+
if request.method == 'POST':
39+
form = PrivateKeyForm(data=request.POST)
40+
if form.is_valid():
41+
store_nomcom_private_key(request, year, form.cleaned_data.get('key', ''))
42+
return HttpResponseRedirect(back_url)
43+
else:
44+
form = PrivateKeyForm(initial={'key': private_key})
45+
return render_to_response('nomcom/private_key.html',
46+
{'nomcom': nomcom,
47+
'year': year,
48+
'back_url': back_url,
49+
'form': form,
50+
'private_key': private_key,
51+
'selected': 'private_key'}, RequestContext(request))
52+
53+
3054
@member_required(role='member')
3155
def private_index(request, year):
3256
nomcom = get_nomcom_by_year(year)

ietf/templates/nomcom/nomcom_private_base.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ <h1>Nomcom {{ year }} Private Area</h1>
99
<div class="ietf-navset">
1010
{% if selected == "index" %}<span class="selected">List of nominees</span>{% else %}<a href="{% url nomcom_private_index year %}">List of nominees</a>{% endif %} |
1111
{% if selected == "nominate" %}<span class="selected">Nominate</span>{% else %}<a href="{% url nomcom_private_nominate year %}">Nominate</a>{% endif %} |
12-
{% if user|is_chair:year %}
12+
{% if selected == "private_key" %}<span class="selected">Private key</span>{% else %}<a href="{% url nomcom_private_key year %}">Private key</a>{% endif %}
13+
{% if user|is_chair:year %} |
1314
{% if selected == "merge" %}<span class="selected">Merge nominee email addr</span>{% else %}<a href="{% url nomcom_private_merge year %}">Merge nominee email addr</a>{% endif %} |
1415
{% if selected == "edit_members" %}<span class="selected">Nomcom members</span>{% else %}<a href="{% url nomcom_edit_members year %}">Nomcom members</a>{% endif %} |
1516
{% if selected == "edit_publickey" %}<span class="selected">Public key</span>{% else %}<a href="{% url nomcom_edit_publickey year %}">Public key</a>{% endif %} |
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{% extends "nomcom/nomcom_private_base.html" %}
2+
3+
{% block subtitle %}- Enter private key{% endblock %}
4+
5+
{% block nomcom_content %}
6+
<h2>Enter private key</h2>
7+
8+
<p>In order to access the {{ nomcom.group }} data you have to enter your private key. Please paste it in the text area below.</p>
9+
10+
<p>If you don't have a private key, please contact the group chair. You can leave the key empty and continue navigation without access to the encrypted data.</p>
11+
12+
<form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
13+
<table>
14+
{{ form }}
15+
</table>
16+
<p><input type="submit" value="Enter key" /></p>
17+
</form>
18+
19+
{% endblock %}

0 commit comments

Comments
 (0)