Edgewall Software

Ticket #2456: user.3.py

File user.3.py, 8.9 kB (added by wkornewald, 21 months ago)

now with User object, still completely untested

Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright 2006 Waldemar Kornewald, wkornewald@haiku-os.org
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://trac.edgewall.org/wiki/TracLicense.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://trac.edgewall.org/log/.
13#
14# Author: Waldemar Kornewald, wkornewald@haiku-os.org
15
16from trac.core import *
17from trac.config import *
18
19
20class IUserStore(Interface):
21    """
22    Extension point interface for backends that store known users.
23    """
24
25    def supports_user_operation(self, operation):
26        """
27        Returns whether the operation (a method name) is supported.
28
29        @return supported
30        """
31        return hasattr(self, operation)
32
33    def create_user(self, username, password):
34        """
35        Creates a new user with the given username and password.
36
37        @return success
38        """
39
40    def get_usernames(self):
41        """
42        Generator that yields an ordered list of known usernames.
43
44        @return username
45        """
46
47    def check_password(self, username, password):
48        """
49        Checks if the password is correct for the given user.
50        """
51
52    def change_password(self, username, password):
53        """
54        Changes a user's password.
55
56        @return success
57        """
58
59    def delete_user(self, username):
60        """
61        Deletes a user.
62
63        @return success
64            Returns False if the user didn't exist.
65        """
66
67
68class IUserAttributeProvider(Interface):
69    """
70    Extension point interface for backends that store user attributes.
71    """
72
73    def supports_attribute_operation(self, operation):
74        """
75        Returns whether the operation (a method name) is supported.
76
77        @return supported
78        """
79        return hasattr(self, operation)
80
81    def get_user_attribute(self, username, attribute):
82        """
83        Returns a user attribute.
84
85        If the attribute is not set it returs an empty string.
86        If the attribute is not supported None is returned.
87        """
88
89    def set_user_attribute(self, username, attribute, value):
90        """
91        Sets a user attribute.
92
93        @return success
94            Returns False if setting the attribute is not supported.
95        """
96
97    def delete_all_user_attributes(self, username):
98        """
99        Deletes all of the given user's attributes.
100
101        @return success
102        """
103
104
105class UserManager(Component):
106    """
107    Component responsible for managing users and user attributes.
108    """
109
110    store = ExtensionOption('users',
111        'store', IUserStore, 'SessionUserStore',
112        doc="""The user store that should be used for authentication
113            (''since 0.11'').""")
114    attribute_providers = OrderedExtensionsOption('users',
115        'attribute_providers', IUserAttributeProvider,
116        doc="""Ordered list of user attribute providers (''since 0.11'').""")
117
118    # public API
119
120    def supports_operation(self, operation):
121        if self.store.supports_user_operation(operation):
122            return True
123        for provider in self.attribute_providers:
124            if self.provider.supports_attribute_operation(operation):
125                return True
126        return False
127
128    # IUserStore methods
129
130    def create_user(self, username, password):
131        """
132        Creates a new user with the given username and password.
133
134        @return User object or None if the user couldn't be created
135        """
136        if not self.store.supports_user_operation('create_user'):
137            return None
138        if self.store.create_user(username, password):
139            return User(username, self.store, self.attribute_providers)
140
141    def get_user(self, username):
142        """
143        Returns a User object for the username.
144
145        @return User object or None if the user doesn't exist
146        """
147        if username in self.get_usernames():
148            return User(username, self.store, self.attribute_providers)
149        return None
150
151    def get_usernames(self):
152        if not self.store.supports_user_operation('get_usernames'):
153            return []
154        return self.store.get_usernames()
155
156
157
158class User(object):
159    """
160    Object representing a user.
161    """
162
163    def __init__(self, username, store, attribute_providers):
164        self._username = username
165        self.store = store
166        self.attribute_providers = attribute_providers
167
168    # public API
169
170    @property
171    def username():
172        return self._username
173
174    # IUserStore methods
175
176    def check_password(self, password):
177        if not self.store.supports_user_operation('check_password'):
178            return False
179        return self.store.check_password(self.username, password)
180
181    def change_password(self, password):
182        if not self.store.supports_user_operation('change_password'):
183            return False
184        return self.store.change_password(self.username, password)
185
186    def delete(self):
187        if not self.store.supports_user_operation('delete_user') or \
188                not self.delete_all_attributes(self.username):
189            return False
190        return self.store.delete_user(self.username)
191
192    # IUserAttributeProvider methods
193
194    def __getattr__(self, attribute):
195        for provider in self.attribute_providers:
196            if not provider.supports_attribute_operation('get_user_attribute'):
197                continue
198            value = provider.get_user_attribute(self.username, attribute)
199            if value is not None:
200                return value
201        return None
202
203    def __setattr__(self, attribute, value):
204        for provider in self.attribute_providers:
205            if not provider.supports_attribute_operation('set_user_attribute'):
206                continue
207            result = provider.set_user_attribute(self.username, attribute,
208                                                 value)
209            if result:
210                return True
211        return False
212
213    def delete_all_attributes(self):
214        for provider in self.attribute_providers:
215            if not provider.supports_attribute_operation('delete_all_user_attributes'):
216                continue
217            result = provider.delete_all_user_attributes(username)
218            if result:
219                return True
220        return False
221
222
223class SessionUserStore(Component):
224    """
225    Component for managing authenticated users stored in sessions.
226    """
227
228    implements(IUserStore)
229
230    def get_usernames(self):
231        db = self.env.get_db_cnx()
232        cursor = db.cursor()
233        cursor.execute("SELECT sid FROM session "
234                       "WHERE authenticated=1 "
235                       "ORDER BY sid")
236        for row in cursor:
237            yield row[0]
238
239
240class SessionUserAttributeProvider(Component):
241    """
242    Component for providing user attributes via Trac sessions.
243    """
244
245    implements(IUserAttributeProvider)
246
247    def get_user_attribute(self, username, attribute):
248        db = self.env.get_db_cnx()
249        cursor = db.cursor()
250        cursor.execute("SELECT value FROM session_attribute "
251                       "WHERE sid=%s AND name=%s AND authenticated=1",
252                       (username, attribute))
253        row = cursor.fetchone()
254        if row:
255            return row[0]
256
257        # if the attribute isn't set we return empty string to indicate
258        # that the attribute is supported, but empty
259        return ''
260
261    def set_user_attribute(self, username, attribute, value):
262        db = self.env.get_db_cnx()
263        cursor = db.cursor()
264
265        # check if attribute exists
266        cursor.execute("SELECT value FROM session_attribute "
267                       "WHERE sid=%s AND name=%s AND authenticated=1",
268                       (username, attribute))
269        if cursor.fetchone():
270            # update the attribute
271            cursor.execute("UPDATE session_attribute SET value=%s "
272                           "WHERE sid=%s AND name=%s AND authenticated=1",
273                           (username, attribute, value))
274        else:
275            # create new attribute
276            cursor.execute("INSERT INTO session_attribute "
277                           "(sid,authenticated,name,value) "
278                           "VALUES(%s,1,%s,%s)",
279                           (username, attribute, value))
280        db.commit()
281        return True
282
283    def delete_all_user_attributes(self, username):
284        db = self.env.get_db_cnx()
285        cursor = db.cursor()
286        cursor.execute("DELETE FROM session_attribute WHERE sid=%s",
287                      (username,))
288        db.commit()
289        return True