Merge ~bjornt/maas:bug-1604702-too-big-to-index into maas:master

Proposed by Björn Tillenius
Status: Merged
Approved by: Andres Rodriguez
Approved revision: 354dbbd83faed0fb3e4af410eb75d55fe0c0f2e3
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~bjornt/maas:bug-1604702-too-big-to-index
Merge into: maas:master
Diff against target: 161 lines (+65/-26)
4 files modified
src/maasserver/forms/__init__.py (+5/-1)
src/maasserver/migrations/builtin/maasserver/0156_drop_ssh_unique_key_index.py (+19/-0)
src/maasserver/models/sshkey.py (+17/-7)
src/maasserver/models/tests/test_sshkey.py (+24/-18)
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
MAAS Lander Approve
Review via email: mp+345748@code.launchpad.net

Commit message

LP: #1604702 - Unable to add a 16384-bit ssh key to my account from web UI

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug-1604702-too-big-to-index lp:~bjornt/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 354dbbd83faed0fb3e4af410eb75d55fe0c0f2e3

review: Approve
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/forms/__init__.py b/src/maasserver/forms/__init__.py
2index b3e640d..3a88e75 100644
3--- a/src/maasserver/forms/__init__.py
4+++ b/src/maasserver/forms/__init__.py
5@@ -1100,7 +1100,7 @@ class KeyForm(MAASModelForm):
6 self._errors.setdefault('key', self.error_class()).extend(error)
7
8
9-class SSHKeyForm(KeyForm):
10+class SSHKeyForm(MAASModelForm):
11 key = UnstrippedCharField(
12 label="Public key",
13 widget=forms.Textarea(attrs={'rows': '5', 'cols': '30'}),
14@@ -1110,6 +1110,10 @@ class SSHKeyForm(KeyForm):
15 model = SSHKey
16 fields = ["key"]
17
18+ def __init__(self, user, *args, **kwargs):
19+ super().__init__(*args, **kwargs)
20+ self.instance.user = user
21+
22 def save(self, endpoint, request):
23 sshkey = super(SSHKeyForm, self).save()
24 create_audit_event(
25diff --git a/src/maasserver/migrations/builtin/maasserver/0156_drop_ssh_unique_key_index.py b/src/maasserver/migrations/builtin/maasserver/0156_drop_ssh_unique_key_index.py
26new file mode 100644
27index 0000000..05e21d3
28--- /dev/null
29+++ b/src/maasserver/migrations/builtin/maasserver/0156_drop_ssh_unique_key_index.py
30@@ -0,0 +1,19 @@
31+# -*- coding: utf-8 -*-
32+# Generated by Django 1.11.11 on 2018-05-16 09:01
33+from __future__ import unicode_literals
34+
35+from django.db import migrations
36+
37+
38+class Migration(migrations.Migration):
39+
40+ dependencies = [
41+ ('maasserver', '0155_add_globaldefaults_model'),
42+ ]
43+
44+ operations = [
45+ migrations.AlterUniqueTogether(
46+ name='sshkey',
47+ unique_together=set([]),
48+ ),
49+ ]
50diff --git a/src/maasserver/models/sshkey.py b/src/maasserver/models/sshkey.py
51index 06862e4..f0dfdf3 100644
52--- a/src/maasserver/models/sshkey.py
53+++ b/src/maasserver/models/sshkey.py
54@@ -114,13 +114,6 @@ class SSHKey(CleanSave, TimestampedModel):
55
56 class Meta(DefaultMeta):
57 verbose_name = "SSH key"
58- unique_together = ('user', 'key', 'keysource')
59-
60- def unique_error_message(self, model_class, unique_check):
61- if unique_check == ('user', 'key', 'keysource'):
62- return "This key has already been added for this user."
63- return super(
64- SSHKey, self).unique_error_message(model_class, unique_check)
65
66 def __str__(self):
67 return self.key
68@@ -134,3 +127,20 @@ class SSHKey(CleanSave, TimestampedModel):
69 if key_size is None:
70 key_size = DEFAULT_KEY_DISPLAY
71 return mark_safe(get_html_display_for_key(self.key, key_size))
72+
73+ def clean(self, *args, **kwargs):
74+ """Make sure there are no duplicate keys.
75+
76+ Note that this could have been done with Meta.unique_together,
77+ but it doesn't work for big keys, since the long text strings
78+ can't be indexed.
79+ """
80+ super().clean(*args, **kwargs)
81+ if not self._state.has_changed('key'):
82+ return
83+
84+ existing_key = SSHKey.objects.filter(
85+ keysource=self.keysource, user=self.user, key=self.key)
86+ if existing_key.exists():
87+ raise ValidationError(
88+ 'This key has already been added for this user.')
89diff --git a/src/maasserver/models/tests/test_sshkey.py b/src/maasserver/models/tests/test_sshkey.py
90index cf55bea..03bde0e 100644
91--- a/src/maasserver/models/tests/test_sshkey.py
92+++ b/src/maasserver/models/tests/test_sshkey.py
93@@ -8,7 +8,6 @@ __all__ = []
94 import random
95
96 from django.core.exceptions import ValidationError
97-from django.db import IntegrityError
98 from django.utils.safestring import SafeString
99 from maasserver.enum import KEYS_PROTOCOL_TYPE
100 from maasserver.models import (
101@@ -267,7 +266,7 @@ class SSHKeyTest(MAASServerTestCase):
102 display = key.display_html()
103 self.assertIsInstance(display, SafeString)
104
105- def test_sshkey_user_and_key_unique_together(self):
106+ def test_sshkey_user_and_key_unique_together_create(self):
107 protocol = random.choice(
108 [KEYS_PROTOCOL_TYPE.LP, KEYS_PROTOCOL_TYPE.GH])
109 auth_id = factory.make_name('auth_id')
110@@ -280,28 +279,35 @@ class SSHKeyTest(MAASServerTestCase):
111 self.assertRaises(
112 ValidationError, key2.full_clean)
113
114- def test_sshkey_user_and_key_unique_together_db_level(self):
115- # Even if we hack our way around model-level checks, uniqueness
116- # of the user/key combination is enforced at the database level.
117+ def test_sshkey_user_and_key_unique_together_change_key(self):
118 protocol = random.choice(
119 [KEYS_PROTOCOL_TYPE.LP, KEYS_PROTOCOL_TYPE.GH])
120 auth_id = factory.make_name('auth_id')
121 keysource = factory.make_KeySource(protocol=protocol, auth_id=auth_id)
122- key_string = get_data('data/test_rsa0.pub')
123+ key_string1 = get_data('data/test_rsa1.pub')
124+ key_string2 = get_data('data/test_rsa2.pub')
125 user = factory.make_User()
126- existing_key = SSHKey(key=key_string, user=user, keysource=keysource)
127- existing_key.save()
128- # The trick to hack around the model-level checks: create a
129- # duplicate key for another user, then attach it to the same
130- # user as the existing key by updating it directly in the
131- # database.
132- redundant_key = SSHKey(
133- key=key_string, user=factory.make_User(), keysource=keysource)
134- redundant_key.save()
135+ key1 = SSHKey(key=key_string1, user=user, keysource=keysource)
136+ key1.save()
137+ key2 = SSHKey(key=key_string2, user=user, keysource=keysource)
138+ key2.save()
139+ key2.key = key1.key
140 self.assertRaises(
141- IntegrityError,
142- SSHKey.objects.filter(id=redundant_key.id).update,
143- user=user)
144+ ValidationError, key2.full_clean)
145+
146+ def test_sshkey_same_key_can_be_used_by_different_sources(self):
147+ auth_id = factory.make_name('auth_id')
148+ keysource1 = factory.make_KeySource(
149+ protocol=KEYS_PROTOCOL_TYPE.LP, auth_id=auth_id)
150+ keysource2 = factory.make_KeySource(
151+ protocol=KEYS_PROTOCOL_TYPE.GH, auth_id=auth_id)
152+ key_string = get_data('data/test_rsa0.pub')
153+ user = factory.make_User()
154+ key1 = SSHKey(key=key_string, user=user, keysource=keysource1)
155+ key1.save()
156+ key2 = SSHKey(key=key_string, user=user, keysource=keysource2)
157+ key2.save()
158+ self.assertIsNone(key2.full_clean())
159
160 def test_sshkey_same_key_can_be_used_by_different_users(self):
161 key_string = get_data('data/test_rsa0.pub')

Subscribers

People subscribed via source and target branches