Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / userdata / models.py @ 0c09b1c0

History | View | Annotate | Download (3.9 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 7469ff7d Kostas Papadimitriou
from django.db.models.signals import pre_save
45 7469ff7d Kostas Papadimitriou
46 7469ff7d Kostas Papadimitriou
try:
47 7469ff7d Kostas Papadimitriou
    from paramiko import rsakey, dsskey, SSHException
48 7469ff7d Kostas Papadimitriou
except:
49 7469ff7d Kostas Papadimitriou
    pass
50 7469ff7d Kostas Papadimitriou
51 49f50673 Vangelis Koukis
52 eee0487e Kostas Papadimitriou
class ProfileModel(models.Model):
53 eee0487e Kostas Papadimitriou
    """
54 eee0487e Kostas Papadimitriou
    Abstract model, provides a basic interface for models that store
55 eee0487e Kostas Papadimitriou
    user specific information
56 eee0487e Kostas Papadimitriou
    """
57 eee0487e Kostas Papadimitriou
58 244c552b Giorgos Verigakis
    user = models.CharField(max_length=100)
59 eee0487e Kostas Papadimitriou
60 eee0487e Kostas Papadimitriou
    class Meta:
61 eee0487e Kostas Papadimitriou
        abstract = True
62 26bade45 Kostas Papadimitriou
        app_label = 'userdata'
63 eee0487e Kostas Papadimitriou
64 eee0487e Kostas Papadimitriou
65 eee0487e Kostas Papadimitriou
class PublicKeyPair(ProfileModel):
66 eee0487e Kostas Papadimitriou
    """
67 eee0487e Kostas Papadimitriou
    Public key model
68 eee0487e Kostas Papadimitriou
    """
69 eee0487e Kostas Papadimitriou
    name = models.CharField(max_length=255, null=False, blank=False)
70 eee0487e Kostas Papadimitriou
    content = models.TextField()
71 386481eb Kostas Papadimitriou
    fingerprint = models.CharField(max_length=100, null=False, blank=True)
72 eee0487e Kostas Papadimitriou
73 26bade45 Kostas Papadimitriou
    class Meta:
74 26bade45 Kostas Papadimitriou
        app_label = 'userdata'
75 c72a830d Kostas Papadimitriou
76 7469ff7d Kostas Papadimitriou
    def full_clean(self, *args, **kwargs):
77 7469ff7d Kostas Papadimitriou
        # update fingerprint before clean
78 7469ff7d Kostas Papadimitriou
        self.update_fingerprint()
79 7469ff7d Kostas Papadimitriou
        super(PublicKeyPair, self).full_clean(*args, **kwargs)
80 7469ff7d Kostas Papadimitriou
81 7469ff7d Kostas Papadimitriou
    def key_data(self):
82 7469ff7d Kostas Papadimitriou
        return self.content.split(" ", 1)
83 7469ff7d Kostas Papadimitriou
84 7469ff7d Kostas Papadimitriou
    def get_key_object(self):
85 7469ff7d Kostas Papadimitriou
        """
86 7469ff7d Kostas Papadimitriou
        Identify key contents and return appropriate paramiko public key object
87 7469ff7d Kostas Papadimitriou
        """
88 7469ff7d Kostas Papadimitriou
        key_type, data = self.key_data()
89 7469ff7d Kostas Papadimitriou
        data = base64.b64decode(data)
90 7469ff7d Kostas Papadimitriou
91 7469ff7d Kostas Papadimitriou
        if key_type == "ssh-rsa":
92 386481eb Kostas Papadimitriou
            key = rsakey.RSAKey(data=data)
93 7469ff7d Kostas Papadimitriou
        elif key_type == "ssh-dss":
94 386481eb Kostas Papadimitriou
            key = dsskey.DSSKey(data=data)
95 7469ff7d Kostas Papadimitriou
        else:
96 7469ff7d Kostas Papadimitriou
            raise Exception("Invalid key type")
97 7469ff7d Kostas Papadimitriou
98 386481eb Kostas Papadimitriou
        return key
99 386481eb Kostas Papadimitriou
100 7469ff7d Kostas Papadimitriou
    def clean_key(self):
101 7469ff7d Kostas Papadimitriou
        key = None
102 7469ff7d Kostas Papadimitriou
        try:
103 7469ff7d Kostas Papadimitriou
            key = self.get_key_object()
104 7469ff7d Kostas Papadimitriou
        except:
105 7469ff7d Kostas Papadimitriou
            raise ValidationError("Invalid SSH key")
106 7469ff7d Kostas Papadimitriou
107 c72a830d Kostas Papadimitriou
    def clean(self):
108 c72a830d Kostas Papadimitriou
        if PublicKeyPair.user_limit_exceeded(self.user):
109 c72a830d Kostas Papadimitriou
            raise ValidationError("SSH keys limit exceeded.")
110 c72a830d Kostas Papadimitriou
111 7469ff7d Kostas Papadimitriou
    def update_fingerprint(self):
112 7469ff7d Kostas Papadimitriou
        try:
113 386481eb Kostas Papadimitriou
            fp = binascii.hexlify(self.get_key_object().get_fingerprint())
114 386481eb Kostas Papadimitriou
            self.fingerprint = ":".join(re.findall(r"..", fp))
115 7469ff7d Kostas Papadimitriou
        except:
116 7469ff7d Kostas Papadimitriou
            self.fingerprint = "unknown fingerprint"
117 7469ff7d Kostas Papadimitriou
118 386481eb Kostas Papadimitriou
    def save(self, *args, **kwargs):
119 386481eb Kostas Papadimitriou
        self.update_fingerprint()
120 386481eb Kostas Papadimitriou
        super(PublicKeyPair, self).save(*args, **kwargs)
121 386481eb Kostas Papadimitriou
122 c72a830d Kostas Papadimitriou
    @classmethod
123 c72a830d Kostas Papadimitriou
    def user_limit_exceeded(cls, user):
124 49f50673 Vangelis Koukis
        return (PublicKeyPair.objects.filter(user=user).count() >=
125 49f50673 Vangelis Koukis
                settings.USERDATA_MAX_SSH_KEYS_PER_USER)