Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / userdata / models.py @ 19b2c29d

History | View | Annotate | Download (4.2 kB)

1 49f50673 Vangelis Koukis
#
2 49f50673 Vangelis Koukis
# Copyright 2011 GRNET S.A. All rights reserved.
3 49f50673 Vangelis Koukis
#
4 49f50673 Vangelis Koukis
# Redistribution and use in source and binary forms, with or
5 49f50673 Vangelis Koukis
# without modification, are permitted provided that the following
6 49f50673 Vangelis Koukis
# conditions are met:
7 49f50673 Vangelis Koukis
#
8 49f50673 Vangelis Koukis
#   1. Redistributions of source code must retain the above
9 49f50673 Vangelis Koukis
#      copyright notice, this list of conditions and the following
10 49f50673 Vangelis Koukis
#      disclaimer.
11 49f50673 Vangelis Koukis
#
12 49f50673 Vangelis Koukis
#   2. Redistributions in binary form must reproduce the above
13 49f50673 Vangelis Koukis
#      copyright notice, this list of conditions and the following
14 49f50673 Vangelis Koukis
#      disclaimer in the documentation and/or other materials
15 49f50673 Vangelis Koukis
#      provided with the distribution.
16 49f50673 Vangelis Koukis
#
17 49f50673 Vangelis Koukis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18 49f50673 Vangelis Koukis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 49f50673 Vangelis Koukis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 49f50673 Vangelis Koukis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21 49f50673 Vangelis Koukis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 49f50673 Vangelis Koukis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 49f50673 Vangelis Koukis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 49f50673 Vangelis Koukis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 49f50673 Vangelis Koukis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 49f50673 Vangelis Koukis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 49f50673 Vangelis Koukis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 49f50673 Vangelis Koukis
# POSSIBILITY OF SUCH DAMAGE.
29 49f50673 Vangelis Koukis
#
30 49f50673 Vangelis Koukis
# The views and conclusions contained in the software and
31 49f50673 Vangelis Koukis
# documentation are those of the authors and should not be
32 49f50673 Vangelis Koukis
# interpreted as representing official policies, either expressed
33 49f50673 Vangelis Koukis
# or implied, of GRNET S.A.
34 49f50673 Vangelis Koukis
35 7469ff7d Kostas Papadimitriou
import base64
36 7469ff7d Kostas Papadimitriou
import binascii
37 386481eb Kostas Papadimitriou
import re
38 7469ff7d Kostas Papadimitriou
39 3a8f50a1 Kostas Papadimitriou
from hashlib import md5
40 3a8f50a1 Kostas Papadimitriou
41 eee0487e Kostas Papadimitriou
from django.db import models
42 c72a830d Kostas Papadimitriou
from django.conf import settings
43 c72a830d Kostas Papadimitriou
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
44 bf18a788 Kostas Papadimitriou
from django.core.validators import MaxLengthValidator
45 7469ff7d Kostas Papadimitriou
from django.db.models.signals import pre_save
46 7469ff7d Kostas Papadimitriou
47 7469ff7d Kostas Papadimitriou
try:
48 7469ff7d Kostas Papadimitriou
    from paramiko import rsakey, dsskey, SSHException
49 7469ff7d Kostas Papadimitriou
except:
50 7469ff7d Kostas Papadimitriou
    pass
51 7469ff7d Kostas Papadimitriou
52 bf18a788 Kostas Papadimitriou
SSH_KEY_MAX_CONTENT_LENGTH = getattr(settings,
53 bf18a788 Kostas Papadimitriou
                                     'USERDATA_SSH_KEY_MAX_CONTENT_LENGTH',
54 bf18a788 Kostas Papadimitriou
                                     30000)
55 bf18a788 Kostas Papadimitriou
56 49f50673 Vangelis Koukis
57 eee0487e Kostas Papadimitriou
class ProfileModel(models.Model):
58 eee0487e Kostas Papadimitriou
    """
59 eee0487e Kostas Papadimitriou
    Abstract model, provides a basic interface for models that store
60 eee0487e Kostas Papadimitriou
    user specific information
61 eee0487e Kostas Papadimitriou
    """
62 eee0487e Kostas Papadimitriou
63 244c552b Giorgos Verigakis
    user = models.CharField(max_length=100)
64 eee0487e Kostas Papadimitriou
65 eee0487e Kostas Papadimitriou
    class Meta:
66 eee0487e Kostas Papadimitriou
        abstract = True
67 26bade45 Kostas Papadimitriou
        app_label = 'userdata'
68 eee0487e Kostas Papadimitriou
69 eee0487e Kostas Papadimitriou
70 eee0487e Kostas Papadimitriou
class PublicKeyPair(ProfileModel):
71 eee0487e Kostas Papadimitriou
    """
72 eee0487e Kostas Papadimitriou
    Public key model
73 eee0487e Kostas Papadimitriou
    """
74 eee0487e Kostas Papadimitriou
    name = models.CharField(max_length=255, null=False, blank=False)
75 bf18a788 Kostas Papadimitriou
    content = models.TextField(validators=[
76 bf18a788 Kostas Papadimitriou
        MaxLengthValidator(SSH_KEY_MAX_CONTENT_LENGTH)])
77 386481eb Kostas Papadimitriou
    fingerprint = models.CharField(max_length=100, null=False, blank=True)
78 eee0487e Kostas Papadimitriou
79 26bade45 Kostas Papadimitriou
    class Meta:
80 26bade45 Kostas Papadimitriou
        app_label = 'userdata'
81 c72a830d Kostas Papadimitriou
82 7469ff7d Kostas Papadimitriou
    def full_clean(self, *args, **kwargs):
83 7469ff7d Kostas Papadimitriou
        # update fingerprint before clean
84 7469ff7d Kostas Papadimitriou
        self.update_fingerprint()
85 7469ff7d Kostas Papadimitriou
        super(PublicKeyPair, self).full_clean(*args, **kwargs)
86 7469ff7d Kostas Papadimitriou
87 7469ff7d Kostas Papadimitriou
    def key_data(self):
88 7469ff7d Kostas Papadimitriou
        return self.content.split(" ", 1)
89 7469ff7d Kostas Papadimitriou
90 7469ff7d Kostas Papadimitriou
    def get_key_object(self):
91 7469ff7d Kostas Papadimitriou
        """
92 7469ff7d Kostas Papadimitriou
        Identify key contents and return appropriate paramiko public key object
93 7469ff7d Kostas Papadimitriou
        """
94 7469ff7d Kostas Papadimitriou
        key_type, data = self.key_data()
95 7469ff7d Kostas Papadimitriou
        data = base64.b64decode(data)
96 7469ff7d Kostas Papadimitriou
97 7469ff7d Kostas Papadimitriou
        if key_type == "ssh-rsa":
98 386481eb Kostas Papadimitriou
            key = rsakey.RSAKey(data=data)
99 7469ff7d Kostas Papadimitriou
        elif key_type == "ssh-dss":
100 386481eb Kostas Papadimitriou
            key = dsskey.DSSKey(data=data)
101 7469ff7d Kostas Papadimitriou
        else:
102 7469ff7d Kostas Papadimitriou
            raise Exception("Invalid key type")
103 7469ff7d Kostas Papadimitriou
104 386481eb Kostas Papadimitriou
        return key
105 386481eb Kostas Papadimitriou
106 7469ff7d Kostas Papadimitriou
    def clean_key(self):
107 7469ff7d Kostas Papadimitriou
        key = None
108 7469ff7d Kostas Papadimitriou
        try:
109 7469ff7d Kostas Papadimitriou
            key = self.get_key_object()
110 7469ff7d Kostas Papadimitriou
        except:
111 7469ff7d Kostas Papadimitriou
            raise ValidationError("Invalid SSH key")
112 7469ff7d Kostas Papadimitriou
113 c72a830d Kostas Papadimitriou
    def clean(self):
114 c72a830d Kostas Papadimitriou
        if PublicKeyPair.user_limit_exceeded(self.user):
115 c72a830d Kostas Papadimitriou
            raise ValidationError("SSH keys limit exceeded.")
116 c72a830d Kostas Papadimitriou
117 7469ff7d Kostas Papadimitriou
    def update_fingerprint(self):
118 7469ff7d Kostas Papadimitriou
        try:
119 386481eb Kostas Papadimitriou
            fp = binascii.hexlify(self.get_key_object().get_fingerprint())
120 386481eb Kostas Papadimitriou
            self.fingerprint = ":".join(re.findall(r"..", fp))
121 7469ff7d Kostas Papadimitriou
        except:
122 7469ff7d Kostas Papadimitriou
            self.fingerprint = "unknown fingerprint"
123 7469ff7d Kostas Papadimitriou
124 386481eb Kostas Papadimitriou
    def save(self, *args, **kwargs):
125 386481eb Kostas Papadimitriou
        self.update_fingerprint()
126 386481eb Kostas Papadimitriou
        super(PublicKeyPair, self).save(*args, **kwargs)
127 386481eb Kostas Papadimitriou
128 c72a830d Kostas Papadimitriou
    @classmethod
129 c72a830d Kostas Papadimitriou
    def user_limit_exceeded(cls, user):
130 49f50673 Vangelis Koukis
        return (PublicKeyPair.objects.filter(user=user).count() >=
131 49f50673 Vangelis Koukis
                settings.USERDATA_MAX_SSH_KEYS_PER_USER)