models.py 3.01 KB
Newer Older
Scott Duckworth committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright 2013 Scott Duckworth
#
# This file is part of django-sshkey.
#
# django-sshkey is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# django-sshkey is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with django-sshkey.  If not, see <http://www.gnu.org/licenses/>.

18 19
from django.db import models
from django.contrib.auth.models import User
20
from django.core.exceptions import ValidationError
21 22 23 24 25 26 27 28 29 30
import base64
import hashlib
import re

sshkey_re = re.compile(r'(?P<type>[\w-]+)\s+(?P<b64key>\S+)(?:\s+(?P<comment>\S+))?$')

def sshkey_fingerprint(b64key):
  key = base64.b64decode(b64key)
  fp_plain = hashlib.md5(key).hexdigest()
  return ':'.join(a+b for a,b in zip(fp_plain[::2], fp_plain[1::2]))
31 32 33

class UserKey(models.Model):
  user = models.ForeignKey(User, db_index=True)
34
  name = models.CharField(max_length=50, blank=True)
35
  key = models.TextField(max_length=2000)
36
  fingerprint = models.CharField(max_length=47, blank=True, db_index=True)
37 38
  created = models.DateTimeField(auto_now_add=True, null=True)
  last_modified = models.DateTimeField(auto_now=True, null=True)
39 40 41 42 43 44 45 46 47

  class Meta:
    unique_together = [
      ('user', 'name'),
    ]

  def __unicode__(self):
    return unicode(self.user) + u': ' + self.name

48 49 50 51
  def clean_fields(self, exclude=None):
    if not exclude or 'key' not in exclude:
      self.key = self.key.strip()

52
  def clean(self):
53 54 55 56
    m = sshkey_re.match(self.key)
    errmsg = 'Key is not a valid SSH protocol 2 base64-encoded key'
    if not m:
      raise ValidationError(errmsg)
57
    try:
58 59 60
      self.fingerprint = sshkey_fingerprint(m.group('b64key'))
    except TypeError:
      raise ValidationError(errmsg)
61 62 63 64 65
    if not self.name:
      comment = m.group('comment')
      if not comment:
        raise ValidationError('Name or key comment required')
      self.name = comment
66 67 68 69 70 71 72 73

  def validate_unique(self, exclude=None):
    if self.pk is None:
      objects = type(self).objects
    else:
      objects = type(self).objects.exclude(pk=self.pk)
    if exclude is None or 'name' not in exclude:
      if objects.filter(user=self.user, name=self.name).count():
74 75
        message = 'You already have a key with that name'
        raise ValidationError({'name': [message]})
76 77 78 79 80 81 82 83 84 85
    if exclude is None or 'key' not in exclude:
      try:
        other = objects.get(fingerprint=self.fingerprint, key=self.key)
        if self.user == other.user:
          message = 'You already have that key on file (%s)' % other.name
        else:
          message = 'Somebody else already has that key on file'
        raise ValidationError({'key': [message]})
      except type(self).DoesNotExist:
        pass