Source code for maasserver.models.sshkey

# Copyright 2012 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

""":class:`SSHKey` and friends."""

from __future__ import (
    absolute_import,
    print_function,
    unicode_literals,
    )

str = None

__metaclass__ = type
__all__ = [
    'SSHKey',
    ]


from cgi import escape

from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db.models import (
    ForeignKey,
    Manager,
    TextField,
    )
from django.utils.safestring import mark_safe
from maasserver import (
    DefaultMeta,
    logger,
    )
from maasserver.models.cleansave import CleanSave
from maasserver.models.timestampedmodel import TimestampedModel
from twisted.conch.ssh.keys import Key


class SSHKeyManager(Manager):
    """A utility to manage the colletion of `SSHKey`s."""

    def get_keys_for_user(self, user):
        """Return the text of the ssh keys associated with a user."""
        return SSHKey.objects.filter(user=user).values_list('key', flat=True)


def validate_ssh_public_key(value):
    """Validate that the given value contains a valid SSH public key."""
    try:
        key = Key.fromString(value)
    except Exception:
        # twisted.conch.ssh.keys.Key.fromString raises all sorts of exceptions.
        # Here, we catch them all and return a ValidationError since this
        # method only aims at validating keys and not return the exact cause of
        # the failure.
        logger.exception("Invalid SSH public key")
        raise ValidationError("Invalid SSH public key.")
    else:
        if not key.isPublic():
            raise ValidationError(
                "Invalid SSH public key (this key is a private key).")


HELLIPSIS = '…'


def get_html_display_for_key(key, size):
    """Return a compact HTML representation of this key with a boundary on
    the size of the resulting string.

    A key typically looks like this: 'key_type key_string comment'.
    What we want here is display the key_type and, if possible (i.e. if it
    fits in the boundary that `size` gives us), the comment.  If possible we
    also want to display a truncated key_string.  If the comment is too big
    to fit in, we simply display a cropped version of the whole string.

    :param key: The key for which we want an HTML representation.
    :type name: unicode
    :param size: The maximum size of the representation.  This may not be
        met exactly.
    :type size: int
    :return: The HTML representation of this key.
    :rtype: unicode
    """
    key = key.strip()
    key_parts = key.split(' ', 2)

    if len(key_parts) == 3:
        key_type = key_parts[0]
        key_string = key_parts[1]
        comment = key_parts[2]
        room_for_key = (
            size - (len(key_type) + len(comment) + len(HELLIPSIS) + 2))
        if room_for_key > 0:
            return '%s %.*s%s %s' % (
                escape(key_type, quote=True),
                room_for_key,
                escape(key_string, quote=True),
                HELLIPSIS,
                escape(comment, quote=True))

    if len(key) > size:
        return '%.*s%s' % (
            size - len(HELLIPSIS),
            escape(key, quote=True),
            HELLIPSIS)
    else:
        return escape(key, quote=True)


MAX_KEY_DISPLAY = 50


[docs]class SSHKey(CleanSave, TimestampedModel): """An `SSHKey` represents a user public SSH key. Users will be able to access allocated nodes using any of their registered keys. :ivar user: The user which owns the key. :ivar key: The SSH public key. """ objects = SSHKeyManager() user = ForeignKey(User, null=False, editable=False) key = TextField( null=False, editable=True, validators=[validate_ssh_public_key]) class Meta(DefaultMeta): verbose_name = "SSH key" unique_together = ('user', 'key') def unique_error_message(self, model_class, unique_check): if unique_check == ('user', 'key'): return "This key has already been added for this user." return super( SSHKey, self).unique_error_message(model_class, unique_check) def __unicode__(self): return self.key
[docs] def display_html(self): """Return a compact HTML representation of this key. :return: The HTML representation of this key. :rtype: unicode """ return mark_safe(get_html_display_for_key(self.key, MAX_KEY_DISPLAY))
MAAS logo

MAAS

Metal As A Service.



Related Topics