Skip to content

Commit 51080e7

Browse files
committed
fix(controller): use key fingerprint instead of id for uniqueness
1 parent 86b3b09 commit 51080e7

6 files changed

Lines changed: 524 additions & 4 deletions

controller/api/models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,14 +873,19 @@ class Key(UuidAuditedModel):
873873
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
874874
id = models.CharField(max_length=128)
875875
public = models.TextField(unique=True, validators=[validate_base64])
876+
fingerprint = models.CharField(max_length=128)
876877

877878
class Meta:
878879
verbose_name = 'SSH Key'
879-
unique_together = (('owner', 'id'))
880+
unique_together = (('owner', 'fingerprint'))
880881

881882
def __str__(self):
882883
return "{}...{}".format(self.public[:18], self.public[-31:])
883884

885+
def save(self, *args, **kwargs):
886+
self.fingerprint = fingerprint(self.public)
887+
return super(Key, self).save(*args, **kwargs)
888+
884889

885890
# define update/delete callbacks for synchronizing
886891
# models with the configuration management backend

controller/api/serializers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class KeySerializer(ModelSerializer):
215215
"""Serialize a :class:`~api.models.Key` model."""
216216

217217
owner = serializers.ReadOnlyField(source='owner.username')
218+
fingerprint = serializers.CharField(read_only=True)
218219
created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
219220
updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
220221

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# -*- coding: utf-8 -*-
2+
from south.utils import datetime_utils as datetime
3+
from south.db import db
4+
from south.v2 import SchemaMigration
5+
from django.db import models
6+
7+
8+
class Migration(SchemaMigration):
9+
10+
def forwards(self, orm):
11+
# Adding field 'Key.fingerprint'
12+
db.add_column(u'api_key', 'fingerprint',
13+
self.gf('django.db.models.fields.CharField')(max_length=128, null=True),
14+
keep_default=False)
15+
16+
17+
def backwards(self, orm):
18+
# Deleting field 'Key.fingerprint'
19+
db.delete_column(u'api_key', 'fingerprint')
20+
21+
22+
models = {
23+
u'api.app': {
24+
'Meta': {'object_name': 'App'},
25+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
26+
'id': ('django.db.models.fields.SlugField', [], {'default': "'rustic-valkyrie'", 'unique': 'True', 'max_length': '64'}),
27+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
28+
'structure': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
29+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
30+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
31+
},
32+
u'api.build': {
33+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Build'},
34+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
35+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
36+
'dockerfile': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
37+
'image': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
38+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
39+
'procfile': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
40+
'sha': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}),
41+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
42+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
43+
},
44+
u'api.certificate': {
45+
'Meta': {'object_name': 'Certificate'},
46+
'certificate': ('django.db.models.fields.TextField', [], {}),
47+
'common_name': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
48+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
49+
'expires': ('django.db.models.fields.DateTimeField', [], {}),
50+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
51+
'key': ('django.db.models.fields.TextField', [], {}),
52+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
53+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
54+
},
55+
u'api.config': {
56+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Config'},
57+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
58+
'cpu': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
59+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
60+
'memory': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
61+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
62+
'tags': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
63+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
64+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'}),
65+
'values': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'})
66+
},
67+
u'api.container': {
68+
'Meta': {'ordering': "[u'created']", 'object_name': 'Container'},
69+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
70+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
71+
'num': ('django.db.models.fields.PositiveIntegerField', [], {}),
72+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
73+
'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Release']"}),
74+
'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
75+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
76+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
77+
},
78+
u'api.domain': {
79+
'Meta': {'object_name': 'Domain'},
80+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
81+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
82+
'domain': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
83+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
84+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
85+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
86+
},
87+
u'api.key': {
88+
'Meta': {'unique_together': "((u'owner', u'id'),)", 'object_name': 'Key'},
89+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
90+
'fingerprint': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
91+
'id': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
92+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
93+
'public': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
94+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
95+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
96+
},
97+
u'api.push': {
98+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Push'},
99+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
100+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
101+
'fingerprint': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
102+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
103+
'receive_repo': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
104+
'receive_user': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
105+
'sha': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
106+
'ssh_connection': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
107+
'ssh_original_command': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
108+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
109+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
110+
},
111+
u'api.release': {
112+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'version'),)", 'object_name': 'Release'},
113+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
114+
'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Build']", 'null': 'True'}),
115+
'config': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Config']"}),
116+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
117+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
118+
'summary': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
119+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
120+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'}),
121+
'version': ('django.db.models.fields.PositiveIntegerField', [], {})
122+
},
123+
u'auth.group': {
124+
'Meta': {'object_name': 'Group'},
125+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126+
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
127+
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
128+
},
129+
u'auth.permission': {
130+
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
131+
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
132+
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
133+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
134+
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
135+
},
136+
u'auth.user': {
137+
'Meta': {'object_name': 'User'},
138+
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
139+
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
140+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
141+
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
142+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
143+
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
144+
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
145+
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
146+
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
147+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
148+
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
149+
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
150+
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
151+
},
152+
u'contenttypes.contenttype': {
153+
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
154+
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
155+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
156+
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
157+
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
158+
}
159+
}
160+
161+
complete_apps = ['api']

0 commit comments

Comments
 (0)