1
# Copyright 2012 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
""":class:`SSHKey` and friends."""
6
from __future__ import (
19
from cgi import escape
21
from django.contrib.auth.models import User
22
from django.core.exceptions import ValidationError
23
from django.db.models import (
28
from django.utils.safestring import mark_safe
29
from maasserver import DefaultMeta
30
from maasserver.models.cleansave import CleanSave
31
from maasserver.models.timestampedmodel import TimestampedModel
32
from twisted.conch.ssh.keys import (
38
class SSHKeyManager(Manager):
39
"""A utility to manage the colletion of `SSHKey`s."""
41
def get_keys_for_user(self, user):
42
"""Return the text of the ssh keys associated with a user."""
43
return SSHKey.objects.filter(user=user).values_list('key', flat=True)
46
def validate_ssh_public_key(value):
47
"""Validate that the given value contains a valid SSH public key."""
49
key = Key.fromString(value)
50
if not key.isPublic():
51
raise ValidationError(
52
"Invalid SSH public key (this key is a private key).")
53
except (BadKeyError, binascii.Error):
54
raise ValidationError("Invalid SSH public key.")
57
HELLIPSIS = '…'
60
def get_html_display_for_key(key, size):
61
"""Return a compact HTML representation of this key with a boundary on
62
the size of the resulting string.
64
A key typically looks like this: 'key_type key_string comment'.
65
What we want here is display the key_type and, if possible (i.e. if it
66
fits in the boundary that `size` gives us), the comment. If possible we
67
also want to display a truncated key_string. If the comment is too big
68
to fit in, we simply display a cropped version of the whole string.
70
:param key: The key for which we want an HTML representation.
71
:type name: basestring
72
:param size: The maximum size of the representation. This may not be
75
:return: The HTML representation of this key.
79
key_parts = key.split(' ', 2)
81
if len(key_parts) == 3:
82
key_type = key_parts[0]
83
key_string = key_parts[1]
84
comment = key_parts[2]
86
size - (len(key_type) + len(comment) + len(HELLIPSIS) + 2))
88
return '%s %.*s%s %s' % (
89
escape(key_type, quote=True),
91
escape(key_string, quote=True),
93
escape(comment, quote=True))
97
size - len(HELLIPSIS),
98
escape(key, quote=True),
101
return escape(key, quote=True)
107
class SSHKey(CleanSave, TimestampedModel):
108
"""A `SSHKey` represents a user public SSH key.
110
Users will be able to access `Node`s using any of their registered keys.
112
:ivar user: The user which owns the key.
113
:ivar key: The ssh public key.
116
objects = SSHKeyManager()
118
user = ForeignKey(User, null=False, editable=False)
121
null=False, editable=True, validators=[validate_ssh_public_key])
123
class Meta(DefaultMeta):
124
verbose_name = "SSH key"
125
unique_together = ('user', 'key')
127
def unique_error_message(self, model_class, unique_check):
128
if unique_check == ('user', 'key'):
129
return "This key has already been added for this user."
131
SSHKey, self).unique_error_message(model_class, unique_check)
133
def __unicode__(self):
136
def display_html(self):
137
"""Return a compact HTML representation of this key.
139
:return: The HTML representation of this key.
142
return mark_safe(get_html_display_for_key(self.key, MAX_KEY_DISPLAY))