Merge lp:~canonical-isd-hackers/canonical-identity-provider/key-registration into lp:canonical-identity-provider/release

Proposed by Michael Foord
Status: Rejected
Rejected by: Natalia Bidart
Proposed branch: lp:~canonical-isd-hackers/canonical-identity-provider/key-registration
Merge into: lp:canonical-identity-provider/release
Diff against target: 434 lines (+247/-15)
9 files modified
identityprovider/forms.py (+33/-4)
identityprovider/models/__init__.py (+1/-0)
identityprovider/models/account.py (+4/-3)
identityprovider/models/yubi_key.py (+20/-0)
identityprovider/templates/account/delete_key.html (+34/-0)
identityprovider/templates/account/edit.html (+4/-0)
identityprovider/templates/account/keys.html (+82/-0)
identityprovider/urls.py (+3/-0)
identityprovider/views/account.py (+66/-8)
To merge this branch: bzr merge lp:~canonical-isd-hackers/canonical-identity-provider/key-registration
Reviewer Review Type Date Requested Status
Canonical ISD hackers Pending
Review via email: mp+60470@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Rejecting since this is pretty old.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'identityprovider/forms.py'
--- identityprovider/forms.py 2010-09-21 22:04:10 +0000
+++ identityprovider/forms.py 2011-05-11 08:50:23 +0000
@@ -9,7 +9,12 @@
9from django.utils.safestring import mark_safe9from django.utils.safestring import mark_safe
10from django.utils.translation import ugettext as _10from django.utils.translation import ugettext as _
1111
12from identityprovider.models import Account, EmailAddress, verify_token_string12from identityprovider.models import (
13 Account,
14 EmailAddress,
15 YubiKey,
16 verify_token_string,
17)
13from identityprovider.models.const import EmailStatus18from identityprovider.models.const import EmailStatus
14from identityprovider.utils import (19from identityprovider.utils import (
15 get_person_and_account_by_email, CannotResetPasswordException,20 get_person_and_account_by_email, CannotResetPasswordException,
@@ -182,6 +187,7 @@
182 'class': 'disableAutoComplete textType',187 'class': 'disableAutoComplete textType',
183 'size': '20'188 'size': '20'
184 }))189 }))
190 require_key = fields.BooleanField(required=False)
185191
186 def __init__(self, data=None, **kwargs):192 def __init__(self, data=None, **kwargs):
187 # These keyword arguments are required to render the form successfully193 # These keyword arguments are required to render the form successfully
@@ -189,7 +195,8 @@
189 self.account = kwargs.pop('account')195 self.account = kwargs.pop('account')
190196
191 if data is None:197 if data is None:
192 kwargs['initial'] = {'displayname': self.account.displayname}198 kwargs['initial'] = {'displayname': self.account.displayname,
199 'require_key':self.account.require_key}
193200
194 super(EditAccountForm, self).__init__(data, **kwargs)201 super(EditAccountForm, self).__init__(data, **kwargs)
195202
@@ -200,12 +207,13 @@
200207
201 # Override the field to set initial value (current preferred email)208 # Override the field to set initial value (current preferred email)
202 self.fields['preferred_email'] = PreferredEmailField(209 self.fields['preferred_email'] = PreferredEmailField(
203 queryset=EmailAddress.objects.filter(account=self.account).\210 queryset=EmailAddress.objects.filter(account=self.account).
204 exclude(status=EmailStatus.NEW).order_by('-status', 'email'),211 exclude(status=EmailStatus.NEW).order_by('-status', 'email'),
205 initial=preferredemail_id,212 initial=preferredemail_id,
206 widget=ROAwareSelect,213 widget=ROAwareSelect,
207 error_messages=email_errors,214 error_messages=email_errors,
208 empty_label=None)215 empty_label=None
216 )
209217
210 def clean_displayname(self):218 def clean_displayname(self):
211 name = self.cleaned_data['displayname'].strip()219 name = self.cleaned_data['displayname'].strip()
@@ -261,6 +269,7 @@
261 if self.is_valid():269 if self.is_valid():
262 self.account.displayname = self.cleaned_data['displayname']270 self.account.displayname = self.cleaned_data['displayname']
263 self.account.preferredemail = self.cleaned_data['preferred_email']271 self.account.preferredemail = self.cleaned_data['preferred_email']
272 self.account.require_key = self.cleaned_data['require_key']
264 self.account.save()273 self.account.save()
265 password = self.cleaned_data['password']274 password = self.cleaned_data['password']
266 if password:275 if password:
@@ -286,6 +295,26 @@
286 return data295 return data
287296
288297
298class NewKeyForm(forms.ModelForm):
299 newkey = fields.CharField()
300
301 def clean_newkey(self):
302 data = self.cleaned_data['newkey']
303 # Verify, if fail, then:
304 #raise forms.ValidationError(_("Invalid Yubi key."))
305 # 'cccccccitnicnibiidnjbrctldunhkiedndechgkjiku'[:12] = 'cccccccitnic'
306 return data
307
308 def clean(self):
309 if self.cleaned_data.has_key('newkey'):
310 self.cleaned_data['public_id'] = self.cleaned_data['newkey'][:12]
311 return self.cleaned_data
312
313 class Meta:
314 model = YubiKey
315 #exclude = ('public_id',)
316
317
289class PreAuthorizeForm(forms.Form):318class PreAuthorizeForm(forms.Form):
290 trust_root = forms.CharField(error_messages=default_errors)319 trust_root = forms.CharField(error_messages=default_errors)
291 callback = forms.CharField(error_messages=default_errors)320 callback = forms.CharField(error_messages=default_errors)
292321
=== modified file 'identityprovider/models/__init__.py'
--- identityprovider/models/__init__.py 2010-12-22 15:19:08 +0000
+++ identityprovider/models/__init__.py 2011-05-11 08:50:23 +0000
@@ -5,6 +5,7 @@
5from metamodels import *5from metamodels import *
6from person import *6from person import *
7from emailaddress import *7from emailaddress import *
8from yubi_key import *
8from authtoken import *9from authtoken import *
9from openidmodels import *10from openidmodels import *
10from team import *11from team import *
1112
=== modified file 'identityprovider/models/account.py'
--- identityprovider/models/account.py 2011-02-23 15:45:28 +0000
+++ identityprovider/models/account.py 2011-05-11 08:50:23 +0000
@@ -75,9 +75,9 @@
75class Account(models.Model):75class Account(models.Model):
76 date_created = models.DateTimeField(default=datetime.utcnow,76 date_created = models.DateTimeField(default=datetime.utcnow,
77 editable=False)77 editable=False)
78 creation_rationale = \78 creation_rationale = models.IntegerField(
79 models.IntegerField(79 choices=AccountCreationRationale._get_choices()
80 choices=AccountCreationRationale._get_choices())80 )
8181
82 status = models.IntegerField(choices=AccountStatus._get_choices())82 status = models.IntegerField(choices=AccountStatus._get_choices())
83 date_status_set = models.DateTimeField(default=datetime.utcnow)83 date_status_set = models.DateTimeField(default=datetime.utcnow)
@@ -86,6 +86,7 @@
86 status_comment = models.TextField(blank=True, null=True)86 status_comment = models.TextField(blank=True, null=True)
87 preferredlanguage = models.TextField(blank=True, null=True)87 preferredlanguage = models.TextField(blank=True, null=True)
88 old_openid_identifier = models.TextField(blank=True, null=True)88 old_openid_identifier = models.TextField(blank=True, null=True)
89 require_key = models.BooleanField(default=False)
8990
90 objects = AccountManager()91 objects = AccountManager()
9192
9293
=== added file 'identityprovider/models/yubi_key.py'
--- identityprovider/models/yubi_key.py 1970-01-01 00:00:00 +0000
+++ identityprovider/models/yubi_key.py 2011-05-11 08:50:23 +0000
@@ -0,0 +1,20 @@
1# Copyright 2011 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4from django.db import models
5
6from identityprovider.models import Account
7from django.utils.translation import ugettext_lazy as _
8
9
10__all__ = ['YubiKey']
11
12
13class YubiKey(models.Model):
14 public_id = models.CharField(primary_key=True, max_length=20, blank=True)
15 name = models.CharField(max_length=30)
16 account = models.ForeignKey(Account)
17
18 class Meta:
19 app_label = 'identityprovider'
20 db_table = u'yubi_key'
021
=== added file 'identityprovider/templates/account/delete_key.html'
--- identityprovider/templates/account/delete_key.html 1970-01-01 00:00:00 +0000
+++ identityprovider/templates/account/delete_key.html 2011-05-11 08:50:23 +0000
@@ -0,0 +1,34 @@
1{% extends "base.html" %}
2{% load i18n %}
3
4{% comment %}
5Copyright 2010 Canonical Ltd. This software is licensed under the
6GNU Affero General Public License version 3 (see the file LICENSE).
7{% endcomment %}
8
9{% block title %}
10 {% trans "Remove USB key" %}
11{% endblock %}
12
13{% block text_title %}
14 <h2 class="main">{% blocktrans %}Remove USB key?{% endblocktrans %}</h2>
15{% endblock %}
16
17{% block content_id %}auth{% endblock %}
18
19{% block content %}
20 <div class="info">
21 <p>{% blocktrans %}Are you sure you want to remove this USB key?{% endblocktrans %}</p>
22 </div>
23
24 <div class="actions">
25 <form action="" method="POST">
26 <p>
27 <button type="submit" class="btn" name="delete"><span><span>{% trans "Yes, remove" %}</span></span></button>
28 {% trans "or" %}
29 <a href="/keys">{% trans "cancel" %}</a>
30 </p>
31 </form>
32 </div>
33 <br style="clear: both" />
34{% endblock %}
035
=== modified file 'identityprovider/templates/account/edit.html'
--- identityprovider/templates/account/edit.html 2010-11-10 00:10:51 +0000
+++ identityprovider/templates/account/edit.html 2011-05-11 08:50:23 +0000
@@ -90,7 +90,11 @@
90 {{ form.preferred_email }}90 {{ form.preferred_email }}
91 </p>91 </p>
92 {% if not embedded %}92 {% if not embedded %}
93 <p><label class="formLabel" for="id_require_key">
94 {% trans "Require key authentication" %}{{ form.require_key }}
95 </p>
93 <p><a href="/+emails">{% trans "Manage email addresses" %}</a></p>96 <p><a href="/+emails">{% trans "Manage email addresses" %}</a></p>
97 <p><a href="/keys">{% trans "Manage USB keys" %}</a></p>
94 {% endif %}98 {% endif %}
95 </div>99 </div>
96 {% if not readonly %}100 {% if not readonly %}
97101
=== added file 'identityprovider/templates/account/keys.html'
--- identityprovider/templates/account/keys.html 1970-01-01 00:00:00 +0000
+++ identityprovider/templates/account/keys.html 2011-05-11 08:50:23 +0000
@@ -0,0 +1,82 @@
1{% extends "base.html" %}
2{% load i18n %}
3
4{% comment %}
5Copyright 2010 Canonical Ltd. This software is licensed under the
6GNU Affero General Public License version 3 (see the file LICENSE).
7{% endcomment %}
8
9{% block title %}
10 {% blocktrans %}{{ account_displayname }}'s USB keys{% endblocktrans %}
11{% endblock %}
12
13{% block extra_header_top %}
14<link rel="stylesheet" type="text/css" href="/assets/identityprovider/lazr-js/cssreset/reset-min.css"></link>
15<link rel="stylesheet" type="text/css" href="/assets/identityprovider/lazr-js/cssfonts/fonts-min.css"></link>
16<link rel="stylesheet" type="text/css" href="/assets/identityprovider/lazr-js/cssbase/base-min.css"></link>
17<style type="text/css">
18td.actions {
19 text-align: right;
20}
21</style>
22{% endblock %}
23
24{% block text_title %}
25 <h1 class="main">{% trans "Your email addresses" %}</h1>
26{% endblock %}
27
28{% block content %}
29
30{% if not yubi_keys %}
31 <p>{% blocktrans %}You have no USB keys associated with your account.{% endblocktrans %}</p>
32{% endif %}
33
34{% if yubi_keys %}
35
36 <h2>{% trans "USB keys" %}</h2>
37
38 <table class="listing hover">
39 {% for key in yubi_keys %}
40 <tr>
41 <td>{{ key.name }}</td>
42 <td>{{ key.public_id }}</td>
43 {% if not readonly %}
44 <td class="actions">
45 <a href="/remove-key/{{ key.public_id }}"><img src="/assets/identityprovider/trash-icon.gif" width="14" height="14"> {% trans "Remove" %}</a>
46 </td>
47 {% endif %}
48 </tr>
49 {% endfor %}
50 </table>
51
52{% endif %}
53
54{% if not readonly %}
55
56 <h2>{% trans "Add USB key" %}</h2>
57
58 <form action="new-key" method="post">
59 <p class="input-row">
60 <label for="id_name">{% trans "Name" %}</label><br>
61 {{ form.name }}
62 {% if form.name.errors %}
63 <span class="error">{{ form.name.errors|first }}</span>
64 {% endif %}
65 </p>
66 <p class="input-row">
67 <label for="id_newkey">{% trans "Key" %}</label><br>
68 {{ form.newkey }}
69 {% if form.newkey.errors %}
70 <span class="error">{{ form.newkey.errors|first }}</span>
71 {% endif %}
72 </p>
73 <p class="actions">
74 <button type="submit" class="btn" name="continue">
75 <span><span>{% trans "Add key" %}</span></span>
76 </button>
77 </p>
78 </form>
79
80{% endif %}
81
82{% endblock %}
083
=== modified file 'identityprovider/urls.py'
--- identityprovider/urls.py 2011-03-04 17:09:45 +0000
+++ identityprovider/urls.py 2011-05-11 08:50:23 +0000
@@ -65,12 +65,15 @@
65urlpatterns += patterns('identityprovider.views.account',65urlpatterns += patterns('identityprovider.views.account',
66 (r'^%(optional_token)s\+edit$' % repls, 'index'),66 (r'^%(optional_token)s\+edit$' % repls, 'index'),
67 (r'^\+emails$', 'account_emails'),67 (r'^\+emails$', 'account_emails'),
68 (r'^keys$', 'account_keys'),
68 (r'^$', 'index'),69 (r'^$', 'index'),
69 (r'^\+cookie$', 'cookie'),70 (r'^\+cookie$', 'cookie'),
70 (r'^%(optional_token)s\+index$' % repls, 'index'),71 (r'^%(optional_token)s\+index$' % repls, 'index'),
71 (r'^%(optional_token)s\+new-email$' % repls, 'new_email'),72 (r'^%(optional_token)s\+new-email$' % repls, 'new_email'),
73 (r'^new-key$' % repls, 'new_key'),
72 (r'^%(optional_token)s\+verify-email$' % repls, 'verify_email'),74 (r'^%(optional_token)s\+verify-email$' % repls, 'verify_email'),
73 (r'^%(optional_token)s\+remove-email$' % repls, 'delete_email'),75 (r'^%(optional_token)s\+remove-email$' % repls, 'delete_email'),
76 (r'^remove-key/(?P<public_id>.+)$' % repls, 'delete_key'),
74)77)
7578
76if settings.BRAND.lower() == 'ubuntu':79if settings.BRAND.lower() == 'ubuntu':
7780
=== modified file 'identityprovider/views/account.py'
--- identityprovider/views/account.py 2011-01-10 15:02:53 +0000
+++ identityprovider/views/account.py 2011-05-11 08:50:23 +0000
@@ -12,9 +12,17 @@
12from django.utils.translation import ugettext as _12from django.utils.translation import ugettext as _
1313
14from identityprovider.decorators import check_readonly14from identityprovider.decorators import check_readonly
15from identityprovider.forms import EditAccountForm, LoginForm, NewEmailForm15from identityprovider.forms import (
16from identityprovider.models import (send_validation_email_request,16 EditAccountForm,
17 EmailAddress)17 LoginForm,
18 NewEmailForm,
19 NewKeyForm,
20)
21from identityprovider.models import (
22 EmailAddress,
23 send_validation_email_request,
24 YubiKey,
25)
18from oauth_backend.models import Consumer, Token26from oauth_backend.models import Consumer, Token
19from identityprovider.models.const import EmailStatus27from identityprovider.models.const import EmailStatus
20from identityprovider.views.utils import (redirection_url_for_token,28from identityprovider.views.utils import (redirection_url_for_token,
@@ -55,7 +63,7 @@
55 if request.method == 'POST' and not settings.READ_ONLY_MODE:63 if request.method == 'POST' and not settings.READ_ONLY_MODE:
56 form = EditAccountForm(request.POST, account=account)64 form = EditAccountForm(request.POST, account=account)
57 if form.save_account():65 if form.save_account():
58 request.session['message'] = _("Your account details have been "\66 request.session['message'] = _("Your account details have been "
59 "successfully updated")67 "successfully updated")
60 return HttpResponseRedirect('.')68 return HttpResponseRedirect('.')
61 else:69 else:
@@ -93,6 +101,22 @@
93101
94102
95@login_required103@login_required
104def account_keys(request):
105 account = request.user
106 form = NewKeyForm()
107 context = RequestContext(request, {
108 'current_section': 'emails',
109 'account': account,
110 'account_displayname': account.displayname,
111 'yubi_keys': account.yubikey_set.all(),
112 'form': form,
113 'message': request.session.pop('message', None),
114 'message_style': 'informational'
115 })
116 return render_to_response('account/keys.html', context)
117
118
119@login_required
96def account_deactivate(request, token=None):120def account_deactivate(request, token=None):
97 request.token = token121 request.token = token
98 import django.contrib.auth as auth122 import django.contrib.auth as auth
@@ -117,7 +141,7 @@
117 # TODO: This makes use of `tokenid` if it's passed in, but the141 # TODO: This makes use of `tokenid` if it's passed in, but the
118 # call to `send_validation_email_request` always generates a new142 # call to `send_validation_email_request` always generates a new
119 # token. Is this right?143 # token. Is this right?
120 144
121 # If there are any unverified emails that match they should be deleted.145 # If there are any unverified emails that match they should be deleted.
122 EmailAddress.objects.filter(email__iexact=email,146 EmailAddress.objects.filter(email__iexact=email,
123 status=EmailStatus.NEW).delete()147 status=EmailStatus.NEW).delete()
@@ -163,6 +187,31 @@
163 return render_to_response('account/new_email.html', context)187 return render_to_response('account/new_email.html', context)
164188
165189
190
191@login_required
192@check_readonly
193def new_key(request, key=None):
194 #request.token = token
195
196 if request.method != 'POST' or settings.READ_ONLY_MODE:
197 return
198
199 data = request.POST.copy()
200 data['account'] = request.user.id
201 form = NewKeyForm(data)
202 if form.is_valid():
203 form.save()
204 # Send e-mail to notify user (so they can catch possible
205 # security breaches
206 return HttpResponseRedirect('/keys')
207
208 context = RequestContext(request, {
209 'account_displayname': request.user.displayname,
210 'form': form
211 })
212 return render_to_response('account/keys.html', context)
213
214
166@login_required215@login_required
167def verify_email(request, token=None):216def verify_email(request, token=None):
168 request.token = token217 request.token = token
@@ -195,6 +244,18 @@
195244
196245
197@login_required246@login_required
247def delete_key(request, public_id):
248 key = get_object_or_404(request.user.yubikey_set, public_id=public_id)
249 if request.method == 'POST':
250 key.delete()
251 request.session['message'] = _("The USB key was removed successfully")
252 return HttpResponseRedirect('/keys')
253 else:
254 context = RequestContext(request)
255 return render_to_response('account/delete_key.html', context)
256
257
258@login_required
198@check_readonly259@check_readonly
199def applications(request):260def applications(request):
200 if request.method == 'POST':261 if request.method == 'POST':
@@ -217,6 +278,3 @@
217 'message_style': 'informational',278 'message_style': 'informational',
218 })279 })
219 return render_to_response('account/applications.html', context)280 return render_to_response('account/applications.html', context)
220
221
222