Edgewall Software

Ticket #2456: user_API_r4938.diff

File user_API_r4938.diff, 22.8 kB (added by Waldemar Kornewald <wkornewald>, 17 months ago)

resolved conflicts with most recent refactorings. also, we now use get_users_with_permission() which simplifies the code

  • trac/env.py

     
    2525from trac.core import Component, ComponentManager, implements, Interface, \ 
    2626                      ExtensionPoint, TracError 
    2727from trac.db import DatabaseManager 
     28from trac.user import UserManager, User 
    2829from trac.util import get_pkginfo 
    2930from trac.versioncontrol import RepositoryManager 
    3031from trac.web.href import Href 
     
    329330        self.log = logger_factory(logtype, logfile, self.log_level, self.path, 
    330331                                  format=format) 
    331332 
    332     def get_known_users(self, cnx=None): 
     333    def get_known_users(self): 
    333334        """Generator that yields information about all known users, i.e. users 
    334335        that have logged in to this Trac environment and possibly set their name 
    335336        and email. 
    336337 
    337338        This function generates one tuple for every user, of the form 
    338339        (username, name, email) ordered alpha-numerically by username. 
    339  
    340         @param cnx: the database connection; if ommitted, a new connection is 
    341                     retrieved 
    342340        """ 
    343         if not cnx: 
    344             cnx = self.get_db_cnx() 
    345         cursor = cnx.cursor() 
    346         cursor.execute("SELECT DISTINCT s.sid, n.value, e.value " 
    347                        "FROM session AS s " 
    348                        " LEFT JOIN session_attribute AS n ON (n.sid=s.sid " 
    349                        "  and n.authenticated=1 AND n.name = 'name') " 
    350                        " LEFT JOIN session_attribute AS e ON (e.sid=s.sid " 
    351                        "  AND e.authenticated=1 AND e.name = 'email') " 
    352                        "WHERE s.authenticated=1 ORDER BY s.sid") 
    353         for username,name,email in cursor: 
    354             yield username, name, email 
     341        for user in UserManager(self).get_all_users(): 
     342            yield user.username, user['name'], user['email'] 
    355343 
    356344    def backup(self, dest=None): 
    357345        """Simple SQLite-specific backup of the database. 
  • trac/prefs/web_ui.py

     
    2222 
    2323from trac.core import * 
    2424from trac.prefs.api import IPreferencePanelProvider 
     25from trac.user import UserManager, User 
    2526from trac.util.datefmt import all_timezones, get_timezone 
    2627from trac.web import HTTPNotFound, IRequestHandler 
    2728from trac.web.chrome import add_stylesheet, INavigationContributor, \ 
     
    9293                self._do_save(req) 
    9394            req.redirect(req.href.prefs(panel or None)) 
    9495 
     96        name = email = '' 
     97        if req.authname != 'anonymous': 
     98            user = UserManager(self.env).get_user(req.authname) 
     99            name, email = user['name'], user['email'] 
     100        else: 
     101            name = req.session.get('name') 
     102            email = req.session.get('email') 
    95103        return 'prefs_%s.html' % (panel or 'general'), { 
    96             'settings': {'session': req.session, 'session_id': req.session.sid}, 
     104            'settings': {'session': req.session, 'session_id': req.session.sid, 
     105                         'name': name, 'email': email}, 
    97106            'timezones': all_timezones, 'timezone': get_timezone 
    98107        } 
    99108 
     
    117126                elif field == 'newsid' and val: 
    118127                    req.session.change_sid(val) 
    119128                else: 
    120                     req.session[field] = val 
    121             elif field in req.args and field in req.session: 
    122                 del req.session[field] 
     129                    if req.authname != 'anonymous' and \ 
     130                            field in ('name', 'email'): 
     131                        user = UserManager(self.env).get_user(req.authname) 
     132                        user[field] = val 
     133                    else: 
     134                        req.session[field] = val 
     135            elif field in req.args: 
     136                if req.authname != 'anonymous' and \ 
     137                        field in ('name', 'email'): 
     138                    user = UserManager(self.env).get_user(req.authname) 
     139                    user[field] = '' 
     140                elif field in req.session: 
     141                    del req.session[field] 
    123142 
    124143    def _do_load(self, req): 
    125144        if req.authname == 'anonymous': 
  • trac/prefs/templates/prefs_general.html

     
    1414      <tr class="field"> 
    1515        <th><label for="name">Full name:</label></th> 
    1616        <td><input type="text" id="name" name="name" size="30" 
    17             value="${settings.session.name}" /></td> 
     17            value="${settings.name}" /></td> 
    1818      </tr> 
    1919      <tr class="field"> 
    2020        <th><label for="email">Email address:</label></th> 
    2121        <td><input type="text" id="email" name="email" size="30" 
    22             value="${settings.session.email}" /></td> 
     22            value="${settings.email}" /></td> 
    2323      </tr> 
    2424    </table> 
    2525    <p py:choose="" class="hint"> 
  • trac/ticket/admin.py

     
    9090 
    9191        if self.config.getbool('ticket', 'restrict_owner'): 
    9292            perm = PermissionSystem(self.env) 
    93             def valid_owner(username): 
    94                 return perm.get_user_permissions(username).get('TICKET_MODIFY') 
    95             data['owners'] = [username for username, name, email 
    96                               in self.env.get_known_users() 
    97                               if valid_owner(username)] 
     93            data['owners'] = perm.get_users_with_permission('TICKET_MODIFY') 
    9894 
    9995        return 'admin_components.html', data 
    10096 
  • trac/ticket/report.py

     
    2828from trac.core import * 
    2929from trac.db import get_column_names 
    3030from trac.perm import IPermissionRequestor 
     31from trac.user import UserManager 
    3132from trac.util import sorted 
    3233from trac.util.datefmt import format_datetime, format_time 
    3334from trac.util.text import to_unicode, unicode_urlencode 
     
    358359        # Get the email addresses of all known users 
    359360        email_map = {} 
    360361        if Chrome(self.env).show_email_addresses: 
    361             for username, name, email in self.env.get_known_users(): 
    362                 if email: 
    363                     email_map[username] = email 
     362            email_map = UserManager(self.env).get_attribute_mapper('email') 
    364363 
    365364        data.update({'header_groups': header_groups, 
    366365                     'row_groups': row_groups, 
  • trac/versioncontrol/web_ui/log.py

     
    2323from trac.context import Context 
    2424from trac.core import * 
    2525from trac.perm import IPermissionRequestor 
     26from trac.user import UserManager 
    2627from trac.util import Ranges 
    2728from trac.util.datefmt import http_date 
    2829from trac.util.html import html 
     
    192193        if format == 'rss': 
    193194            # Get the email addresses of all known users 
    194195            if Chrome(self.env).show_email_addresses: 
    195                 for username,name,email in self.env.get_known_users(): 
    196                     if email: 
    197                         email_map[username] = email 
     196                email_map = UserManager(self.env).get_attribute_mapper('email') 
    198197        elif format == 'changelog': 
    199198            for rev in revs: 
    200199                changeset = changes[rev] 
  • trac/perm.py

     
    2020 
    2121from trac.config import ExtensionOption 
    2222from trac.core import * 
     23from trac.user import UserManager 
    2324from trac.util.compat import set 
    2425 
    2526__all__ = ['IPermissionRequestor', 'IPermissionStore', 
     
    141142        db = self.env.get_db_cnx() 
    142143        cursor = db.cursor() 
    143144        groups = permissions 
    144         users = set([u[0] for u in self.env.get_known_users()]) 
     145        users = set([username for username 
     146                    in UserManager(self.env).get_usernames()]) 
    145147        result = set() 
    146148 
    147149        # First iteration finds all users and groups that have any of the 
  • trac/user.py

     
     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 
     32    def create_user(self, username, password): 
     33        """ 
     34        Creates a new user with the given username and password. 
     35 
     36        @return success 
     37        """ 
     38 
     39    def get_usernames(self): 
     40        """ 
     41        Generator that yields an ordered list of known usernames. 
     42 
     43        @return username 
     44        """ 
     45 
     46    def check_password(self, username, password): 
     47        """ 
     48        Checks if the password is correct for the given user. 
     49        """ 
     50 
     51    def change_password(self, username, password): 
     52        """ 
     53        Changes a user's password. 
     54 
     55        @return success 
     56        """ 
     57 
     58    def delete_user(self, username): 
     59        """ 
     60        Deletes a user. 
     61 
     62        @return success 
     63            Returns False if the user didn't exist. 
     64        """ 
     65 
     66 
     67class IUserAttributeProvider(Interface): 
     68    """ 
     69    Extension point interface for backends that store user attributes. 
     70    """ 
     71 
     72    def supports_attribute_operation(self, operation): 
     73        """ 
     74        Returns whether the operation (a method name) is supported. 
     75 
     76        @return supported 
     77        """ 
     78 
     79    def get_user_attribute(self, username, attribute): 
     80        """ 
     81        Returns a user attribute. 
     82 
     83        If the attribute is not set it returs an empty string. 
     84        If the attribute is not supported None is returned. 
     85        """ 
     86 
     87    def set_user_attribute(self, username, attribute, value): 
     88        """ 
     89        Sets a user attribute. 
     90 
     91        @return success 
     92            Returns False if setting the attribute is not supported. 
     93        """ 
     94 
     95    def delete_all_user_attributes(self, username): 
     96        """ 
     97        Deletes all of the given user's attributes. 
     98        """ 
     99 
     100class UserAttributeMapper(object): 
     101    """ 
     102    Dict that paps usernames to attributes and caches those values to 
     103    increase efficiency. 
     104    """ 
     105 
     106    _usernames_cache = None 
     107    _cache = {} 
     108 
     109    def __init__(self, attribute, manager): 
     110        self._attribute = attribute 
     111        self._manager = manager 
     112 
     113    def __getitem__(self, username): 
     114        if not self._usernames_cache: 
     115            self._usernames_cache = [username for username 
     116                                     in self._manager.get_usernames()] 
     117        if username not in self._usernames_cache: 
     118            return None 
     119        if username not in self._cache: 
     120            value = self._manager.get_user(username)[self._attribute] 
     121            if value: 
     122                self._cache[username] = value 
     123            return value 
     124        else: 
     125            return self._cache.get(username) 
     126 
     127    def __contains__(self, username): 
     128        value = self[username] 
     129        return value is not None and len(value) > 0 
     130 
     131    def __setitem__(self,key,value): 
     132        raise NotImplementedError, "dict is immutable" 
     133    def __delitem__(self,key): 
     134        raise NotImplementedError, "dict is immutable" 
     135    def clear(self): 
     136        raise NotImplementedError, "dict is immutable" 
     137    def setdefault(self,k,default=None): 
     138        raise NotImplementedError, "dict is immutable" 
     139    def popitem(self): 
     140        raise NotImplementedError, "dict is immutable" 
     141    def update(self,other): 
     142        raise NotImplementedError, "dict is immutable" 
     143 
     144 
     145class UserManager(Component): 
     146    """ 
     147    Component responsible for managing users and user attributes. 
     148    """ 
     149 
     150    store = ExtensionOption('users', 
     151        'store', IUserStore, 'SessionUserStore', 
     152        doc="""The user store that should be used for authentication 
     153            (''since 0.11'').""") 
     154    attribute_providers = OrderedExtensionsOption('users', 
     155        'attribute_providers', IUserAttributeProvider, 
     156        doc="""Ordered list of user attribute providers (''since 0.11'').""") 
     157 
     158    # public API 
     159 
     160    def supports_operation(self, operation): 
     161        if self.store.supports_user_operation(operation): 
     162            return True 
     163        for provider in self.attribute_providers: 
     164            if self.provider.supports_attribute_operation(operation): 
     165                return True 
     166        return False 
     167 
     168    # IUserStore methods 
     169 
     170    def create_user(self, username, password): 
     171        """ 
     172        Creates a new user with the given username and password. 
     173 
     174        @return User object or None if the user couldn't be created 
     175        """ 
     176        if not self.store.supports_user_operation('create_user'): 
     177            return None 
     178        if self.store.create_user(username, password): 
     179            return User(username, self.store, self.attribute_providers) 
     180 
     181    def get_user(self, username): 
     182        """ 
     183        Returns a User object for the username. 
     184 
     185        @return User object or None if the user doesn't exist 
     186        """ 
     187        return User(username, self.store, self.attribute_providers) 
     188 
     189    def get_all_users(self): 
     190        """ 
     191        Generator for User objects. 
     192        """ 
     193        if not self.store.supports_user_operation('get_usernames'): 
     194            return 
     195        for username in self.store.get_usernames(): 
     196            yield User(username, self.store, self.attribute_providers) 
     197 
     198    def get_usernames(self): 
     199        """ 
     200        Generator for usernames. 
     201        """ 
     202        if not self.store.supports_user_operation('get_usernames'): 
     203            return 
     204        return self.store.get_usernames() 
     205 
     206    def get_attribute_mapper(self, attribute): 
     207        return UserAttributeMapper(attribute, self) 
     208 
     209 
     210class User(object): 
     211    """ 
     212    Object representing a user. 
     213    """ 
     214 
     215    def __init__(self, username, store, attribute_providers): 
     216        self._username = username 
     217        self.store = store 
     218        self.attribute_providers = attribute_providers 
     219 
     220    # public API 
     221 
     222    @property 
     223    def username(self): 
     224        return self._username 
     225 
     226    def exists(self): 
     227        return self.username in [username for username 
     228                                 in self.store.get_usernames()] 
     229 
     230    # IUserStore methods 
     231 
     232    def check_password(self, password): 
     233        if not self.store.supports_user_operation('check_password'): 
     234            return False 
     235        return self.store.check_password(self.username, password) 
     236 
     237    def change_password(self, password): 
     238        if not self.store.supports_user_operation('change_password'): 
     239            return False 
     240        return self.store.change_password(self.username, password) 
     241 
     242    def delete(self): 
     243        if not self.store.supports_user_operation('delete_user'): 
     244            return False 
     245        self.delete_all_attributes() 
     246        return self.store.delete_user(self.username) 
     247 
     248    # IUserAttributeProvider methods 
     249 
     250    def __getitem__(self, attribute): 
     251        for provider in self.attribute_providers: 
     252            if not provider.supports_attribute_operation('get_user_attribute'): 
     253                continue 
     254            value = provider.get_user_attribute(self.username, attribute) 
     255            if value is not None: 
     256                return value 
     257        return None 
     258 
     259    def __setitem__(self, attribute, value): 
     260        for provider in self.attribute_providers: 
     261            if provider.supports_attribute_operation('set_user_attribute') \ 
     262                    and provider.set_user_attribute(self.username, attribute, 
     263                                                    value): 
     264                return True 
     265        return False 
     266 
     267    def delete_all_attributes(self): 
     268        for provider in self.attribute_providers: 
     269            if provider.supports_attribute_operation('delete_all_user_attributes'): 
     270                provider.delete_all_user_attributes(username) 
     271 
     272 
     273class SessionUserStore(Component): 
     274    """ 
     275    Component for managing authenticated users stored in sessions. 
     276    """ 
     277 
     278    implements(IUserStore) 
     279 
     280    def supports_user_operation(self, operation): 
     281        return hasattr(self, operation) 
     282 
     283    def get_usernames(self): 
     284        db = self.env.get_db_cnx() 
     285        cursor = db.cursor() 
     286        cursor.execute("SELECT sid FROM session " 
     287                       "WHERE authenticated=1 " 
     288                       "ORDER BY sid") 
     289        for row in cursor: 
     290            yield row[0] 
     291 
     292 
     293class SessionUserAttributeProvider(Component): 
     294    """ 
     295    Component for providing user attributes via Trac sessions. 
     296    """ 
     297 
     298    implements(IUserAttributeProvider) 
     299 
     300    def supports_attribute_operation(self, operation): 
     301        return hasattr(self, operation) 
     302 
     303    def get_user_attribute(self, username, attribute): 
     304        db = self.env.get_db_cnx() 
     305        cursor = db.cursor() 
     306        cursor.execute("SELECT value FROM session_attribute " 
     307                       "WHERE sid=%s AND name=%s AND authenticated=1", 
     308                       (username, attribute)) 
     309        row = cursor.fetchone() 
     310        if row: 
     311            return row[0] 
     312 
     313        # if the attribute doesn't exist we return an empty string to 
     314        # indicate that the attribute is supported, but not set 
     315        return '' 
     316 
     317    def set_user_attribute(self, username, attribute, value): 
     318        db = self.env.get_db_cnx() 
     319        cursor = db.cursor() 
     320 
     321        if not value or value == '': 
     322            cursor.execute("DELETE FROM session_attribute " 
     323                           "WHERE sid=%s AND name=%s AND authenticated=1", 
     324                          (username, attribute)) 
     325            db.commit() 
     326            return True 
     327 
     328        # check if attribute exists 
     329        cursor.execute("SELECT value FROM session_attribute " 
     330                       "WHERE sid=%s AND name=%s AND authenticated=1", 
     331                       (username, attribute)) 
     332        if cursor.fetchone(): 
     333            # update the attribute 
     334            cursor.execute("UPDATE session_attribute SET value=%s " 
     335                           "WHERE sid=%s AND name=%s AND authenticated=1", 
     336                           (value, username, attribute)) 
     337        else: 
     338            # create new attribute 
     339            cursor.execute("INSERT INTO session_attribute " 
     340                           "(sid,authenticated,name,value) " 
     341                           "VALUES(%s,1,%s,%s)", 
     342                           (username, attribute, value)) 
     343        db.commit() 
     344        return True 
     345 
     346    def delete_all_user_attributes(self, username): 
     347        db = self.env.get_db_cnx() 
     348        cursor = db.cursor() 
     349        cursor.execute("DELETE FROM session_attribute " 
     350                       "WHERE sid=%s AND authenticated=1", 
     351                      (username,)) 
     352        db.commit() 
  • trac/timeline/web_ui.py

     
    3030from trac.core import * 
    3131from trac.perm import IPermissionRequestor 
    3232from trac.timeline.api import ITimelineEventProvider, TimelineEvent 
     33from trac.user import UserManager 
    3334from trac.util.datefmt import format_date, format_datetime, parse_date, \ 
    3435                              to_timestamp, utc, pretty_timedelta 
    3536from trac.util.text import to_unicode 
     
    161162            # Get the email addresses of all known users 
    162163            email_map = {} 
    163164            if Chrome(self.env).show_email_addresses: 
    164                 for username, name, email in self.env.get_known_users(): 
    165                     if email: 
    166                         email_map[username] = email 
     165                email_map = UserManager(self.env).get_attribute_mapper('email') 
    167166            data['email_map'] = email_map 
    168167            return 'timeline.rss', data, 'application/rss+xml' 
    169168 
  • trac/test.py

     
    166166    def get_db_cnx(self): 
    167167        return self.db 
    168168 
    169     def get_known_users(self, db): 
     169    def get_known_users(self): 
    170170        return self.known_users 
    171171 
    172172 
  • trac/notification.py

     
    2222from trac import __version__ 
    2323from trac.config import BoolOption, IntOption, Option 
    2424from trac.core import * 
     25from trac.user import UserManager 
    2526from trac.util.text import CRLF 
    2627from trac.web.chrome import Chrome 
    2728 
     
    148149    from_email = 'trac+tickets@localhost' 
    149150    subject = '' 
    150151    server = None 
    151     email_map = None 
    152152    template_name = None 
    153153    addrfmt = r"[\w\d_\.\-\+=]+\@(([\w\d\-])+\.)+([\w\d]{2,4})+" 
    154154    shortaddr_re = re.compile(addrfmt) 
     
    161161 
    162162        self._use_tls = self.env.config.getbool('notification', 'use_tls') 
    163163        self._init_pref_encoding() 
    164         # Get the email addresses of all known users 
    165         self.email_map = {} 
    166         for username, name, email in self.env.get_known_users(self.db): 
    167             if email: 
    168                 self.email_map[username] = email 
    169                  
     164 
    170165    def _init_pref_encoding(self): 
    171166        from email.Charset import Charset, QP, BASE64 
    172167        self._charset = Charset() 
     
    241236        if address.find('@') == -1: 
    242237            if address == 'anonymous': 
    243238                return None 
    244             if self.email_map.has_key(address): 
    245                 address = self.email_map[address] 
     239             
     240            email = UserManager(self.env).get_user(address)['email'] 
     241            if email and len(email) > 0: 
     242                address = email 
    246243            elif NotifyEmail.nodomaddr_re.match(address): 
    247244                if self.config.getbool('notification', 'use_short_addr'): 
    248245                    return address