users.py 4.87 KB
Newer Older
Romain Bignon's avatar
Romain Bignon committed
1 2
# -*- coding: utf-8 -*-

3
# Copyright (C) 2011 Romain Bignon, Laurent Bachelier
Romain Bignon's avatar
Romain Bignon committed
4
#
Romain Bignon's avatar
Romain Bignon committed
5
# This file is part of assnet.
6
#
Romain Bignon's avatar
Romain Bignon committed
7
# assnet is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU Affero General Public License as published by
9 10
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
Romain Bignon's avatar
Romain Bignon committed
11
#
Romain Bignon's avatar
Romain Bignon committed
12
# assnet is distributed in the hope that it will be useful,
Romain Bignon's avatar
Romain Bignon committed
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Affero General Public License for more details.
Romain Bignon's avatar
Romain Bignon committed
16
#
17
# You should have received a copy of the GNU Affero General Public License
Romain Bignon's avatar
Romain Bignon committed
18
# along with assnet. If not, see <http://www.gnu.org/licenses/>.
Romain Bignon's avatar
Romain Bignon committed
19

Romain Bignon's avatar
Romain Bignon committed
20 21 22 23

__all__ = ['Group', 'IUser', 'User', 'Anonymous']


24
from .obj import IObject
25
from .mail import Mail
26
from .security import new_salt, new_user_key
Laurent Bachelier's avatar
Laurent Bachelier committed
27

28
import os
29
import hashlib
30

Laurent Bachelier's avatar
Laurent Bachelier committed
31

32 33 34
class Group(object):
    def __init__(self, name):
        self.name = name
Romain Bignon's avatar
Romain Bignon committed
35
        self.description = u''
36
        self.users = []
Romain Bignon's avatar
Romain Bignon committed
37

38 39 40 41 42 43
    def __str__(self):
        return self.name

    def __lt__(self, g):
        return self.name < g.name

Laurent Bachelier's avatar
Laurent Bachelier committed
44

Romain Bignon's avatar
Romain Bignon committed
45
class IUser(object):
Romain Bignon's avatar
Romain Bignon committed
46
    def has_perms(self, f, perm):
Romain Bignon's avatar
Romain Bignon committed
47 48
        raise NotImplementedError()

49 50 51
    def __str__(self):
        return self.name

Laurent Bachelier's avatar
Laurent Bachelier committed
52

53
class User(IUser, IObject):
Romain Bignon's avatar
Romain Bignon committed
54 55 56 57
    def __init__(self, storage, name):
        self.name = name
        self.email = None
        self.realname = None
Romain Bignon's avatar
Romain Bignon committed
58
        self.password = None
59
        self.key = None
Romain Bignon's avatar
Romain Bignon committed
60
        self.groups = []
61
        IObject.__init__(self, storage)
Romain Bignon's avatar
Romain Bignon committed
62

Romain Bignon's avatar
Romain Bignon committed
63
    def has_perms(self, f, perm):
64
        f_perms = f.get_user_perms(self.name)
65 66
        if f_perms is not None:
            return f_perms & perm
Romain Bignon's avatar
Romain Bignon committed
67 68 69

        for group in self.groups:
            f_perms = f.get_group_perms(group)
70 71
            if f_perms is not None:
                return f.get_group_perms(group) & perm
Romain Bignon's avatar
Romain Bignon committed
72

Romain Bignon's avatar
Romain Bignon committed
73 74 75 76
        f_perms = f.get_auth_perms()
        if f_perms is not None:
            return f_perms & perm

Romain Bignon's avatar
Romain Bignon committed
77 78
        f_perms = f.get_all_perms()
        if f_perms is not None:
Romain Bignon's avatar
Romain Bignon committed
79
            return f_perms & perm
Romain Bignon's avatar
Romain Bignon committed
80 81 82 83

        f_parent = f.parent()
        return f_parent and self.has_perms(f_parent, perm)

84 85
    def new_mail(self, template, subject):
        config = self.storage.get_config()
Romain Bignon's avatar
Romain Bignon committed
86
        sender = config.data['mail'].get('sender', 'assnet')
87 88 89 90 91 92
        recipient = '%s <%s>' % (self.realname, self.email)
        smtp = config.data['mail'].get('smtp', 'localhost')

        mail = Mail(self.storage, template, sender, recipient, subject, smtp)
        return mail

93
    def gen_key(self):
94
        self.key = new_user_key()
95

96 97 98 99 100 101
    def _get_confname(self):
        return os.path.join('users', self.name)

    def _postread(self):
        self.email = self.data['info'].get('email')
        self.realname = self.data['info'].get('realname')
102 103
        self.password = len(self.data['auth'].get('password', '') \
                            + self.data['auth'].get('salt', '')) > 0
104
        self.key = self.data['auth'].get('key')
Romain Bignon's avatar
Romain Bignon committed
105
        for group in self.storage.get_groupscfg().itervalues():
Romain Bignon's avatar
Romain Bignon committed
106 107
            if self.name in group.users:
                self.groups.append(group.name)
108 109 110 111

    def _prewrite(self):
        self.data['info']['email'] = self.email if self.email else None
        self.data['info']['realname'] = self.realname if self.realname else None
112
        self.data['auth']['key'] = self.key if self.key else None
113 114
        # only update password when set
        if isinstance(self.password, basestring):
115
            salt = new_salt()
116 117 118 119 120 121 122 123 124
            version = 1
            hpwd = self.hash_password(self.password, salt, version)
            self.data['auth']['password'] = hpwd
            self.data['auth']['salt'] = salt
            self.data['auth']['version'] = version
            self.password = True

    @staticmethod
    def hash_password(password, salt, version):
Laurent Bachelier's avatar
Laurent Bachelier committed
125
        assert version == 1  # the only one supported for now
126 127 128 129 130 131 132 133 134 135 136 137
        return hashlib.sha512(password + salt).hexdigest()

    def is_valid_password(self, password):
        assert isinstance(password, basestring)
        if isinstance(self.password, basestring):
            return self.password == password
        if self.password is True:
            hpwd = self.hash_password(password, \
                    self.data['auth']['salt'], \
                    int(self.data['auth']['version']))
            return hpwd == self.data['auth']['password']

138

Romain Bignon's avatar
Romain Bignon committed
139 140 141
class Anonymous(IUser):
    name = '<anonymous>'
    email = None
142 143
    realname = 'Anonymous'
    password = None
Romain Bignon's avatar
Romain Bignon committed
144
    groups = []
145
    exists = False
146 147
    key = None
    groups = []
Romain Bignon's avatar
Romain Bignon committed
148

149 150 151 152
    def __init__(self, fake_name=None):
        if fake_name:
            self.name = fake_name

Romain Bignon's avatar
Romain Bignon committed
153 154 155 156 157 158 159
    def has_perms(self, f, perm):
        f_perms = f.get_all_perms()
        if f_perms is not None:
            return f_perms & perm

        f_parent = f.parent()
        return f_parent and self.has_perms(f_parent, perm)
160 161 162

    def is_valid_password(self, password):
        return False