Edgewall Software

Ticket #2456: user_API_r4501.diff

File user_API_r4501.diff, 23.4 kB (added by Waldemar Kornewald <wkornewald>, 19 months ago)

updated patch for show_email_addresses option. added a generic attribute mapper.

  • trac/env.py

     
    2323from trac.core import Component, ComponentManager, implements, Interface, \ 
    2424                      ExtensionPoint, TracError 
    2525from trac.db import DatabaseManager 
     26from trac.user import UserManager, User 
    2627from trac.util import get_pkginfo 
    2728from trac.versioncontrol import RepositoryManager 
    2829from trac.web.href import Href 
     
    283284            logfile = os.path.join(self.get_log_dir(), logfile) 
    284285        self.log = logger_factory(logtype, logfile, self.log_level, self.path) 
    285286 
    286     def get_known_users(self, cnx=None): 
     287    def get_known_users(self): 
    287288        """Generator that yields information about all known users, i.e. users 
    288289        that have logged in to this Trac environment and possibly set their name 
    289290        and email. 
    290291 
    291292        This function generates one tuple for every user, of the form 
    292293        (username, name, email) ordered alpha-numerically by username. 
    293  
    294         @param cnx: the database connection; if ommitted, a new connection is 
    295                     retrieved 
    296294        """ 
    297         if not cnx: 
    298             cnx = self.get_db_cnx() 
    299         cursor = cnx.cursor() 
    300         cursor.execute("SELECT DISTINCT s.sid, n.value, e.value " 
    301                        "FROM session AS s " 
    302                        " LEFT JOIN session_attribute AS n ON (n.sid=s.sid " 
    303                        "  and n.authenticated=1 AND n.name = 'name') " 
    304                        " LEFT JOIN session_attribute AS e ON (e.sid=s.sid " 
    305                        "  AND e.authenticated=1 AND e.name = 'email') " 
    306                        "WHERE s.authenticated=1 ORDER BY s.sid") 
    307         for username,name,email in cursor: 
    308             yield username, name, email 
     295        for user in UserManager(self).get_all_users(): 
     296            yield user.username, user['name'], user['email'] 
    309297 
    310298    def backup(self, dest=None): 
    311299        """Simple SQLite-specific backup of the database. 
  • trac/prefs/web_ui.py

     
    2121 
    2222from trac.core import * 
    2323from trac.prefs.api import IPreferencePanelProvider 
     24from trac.user import UserManager, User 
    2425from trac.util.datefmt import all_timezones, utc 
    2526from trac.web import HTTPNotFound, IRequestHandler 
    2627from trac.web.chrome import add_stylesheet, INavigationContributor 
     
    9091                self._do_save(req) 
    9192            req.redirect(req.href.prefs(panel or None)) 
    9293 
     94        name = email = '' 
     95        if req.authname != 'anonymous': 
     96            user = UserManager(self.env).get_user(req.authname) 
     97            name, email = user['name'], user['email'] 
     98        else: 
     99            name = req.session.get('name') 
     100            email = req.session.get('email') 
    93101        return 'prefs_%s.html' % (panel or 'general'), { 
    94             'settings': {'session': req.session, 'session_id': req.session.sid}, 
     102            'settings': {'session': req.session, 'session_id': req.session.sid, 
     103                         'name': name, 'email': email}, 
    95104            'timezones': all_timezones 
    96105        } 
    97106 
     
    107116                elif field == 'newsid' and val: 
    108117                    req.session.change_sid(val) 
    109118                else: 
    110                     req.session[field] = val 
    111             elif field in req.args and field in req.session: 
    112                 del req.session[field] 
     119                    if req.authname != 'anonymous' and \ 
     120                            field in ('name', 'email'): 
     121                        user = UserManager(self.env).get_user(req.authname) 
     122                        user[field] = val 
     123                    else: 
     124                        req.session[field] = val 
     125            elif field in req.args: 
     126                if req.authname != 'anonymous' and \ 
     127                        field in ('name', 'email'): 
     128                    user = UserManager(self.env).get_user(req.authname) 
     129                    user[field] = '' 
     130                elif field in req.session: 
     131                    del req.session[field] 
    113132 
    114133    def _do_load(self, req): 
    115134        if req.authname == 'anonymous': 
  • trac/ticket/admin.py

     
    1717from trac.core import * 
    1818from trac.perm import PermissionSystem 
    1919from trac.ticket import model 
     20from trac.user import UserManager 
    2021from trac.util import datefmt 
    2122from trac.web.chrome import add_link, add_script 
    2223 
     
    9192            perm = PermissionSystem(self.env) 
    9293            def valid_owner(username): 
    9394                return perm.get_user_permissions(username).get('TICKET_MODIFY') 
    94             data['owners'] = [username for username, name, email 
    95                               in self.env.get_known_users() 
     95            data['owners'] = [username for username 
     96                              in UserManager(self.env).get_usernames() 
    9697                              if valid_owner(username)] 
    9798 
    9899        return 'admin_components.html', data 
  • trac/ticket/api.py

     
    2020from trac.config import * 
    2121from trac.core import * 
    2222from trac.perm import IPermissionRequestor, PermissionSystem 
     23from trac.user import UserManager 
    2324from trac.util import Ranges 
    2425from trac.util.html import html 
    2526from trac.util.text import shorten_line 
     
    105106            perm = PermissionSystem(self.env) 
    106107            def valid_owner(username): 
    107108                return perm.get_user_permissions(username).get('TICKET_MODIFY') 
    108             field['options'] = [username for username, name, email 
    109                                 in self.env.get_known_users() 
     109            field['options'] = [username for username 
     110                                in UserManager(self.env).get_usernames() 
    110111                                if valid_owner(username)] 
    111112            field['optional'] = True 
    112113        else: 
  • trac/ticket/report.py

     
    2222from trac.core import * 
    2323from trac.db import get_column_names 
    2424from trac.perm import IPermissionRequestor 
     25from trac.user import UserManager 
    2526from trac.util import sorted 
    2627from trac.util.text import to_unicode, unicode_urlencode 
    2728from trac.util.html import html 
     
    324325        # Get the email addresses of all known users 
    325326        email_map = {} 
    326327        if Chrome(self.env).show_email_addresses: 
    327             for username, name, email in self.env.get_known_users(): 
    328                 if email: 
    329                     email_map[username] = email 
     328            email_map = UserManager(self.env).get_attribute_mapper('email') 
    330329 
    331330        data.update({'header_groups': header_groups, 
    332331                     'row_groups': row_groups, 
  • trac/versioncontrol/web_ui/log.py

     
    2121 
    2222from trac.core import * 
    2323from trac.perm import IPermissionRequestor 
     24from trac.user import UserManager 
    2425from trac.util import Ranges 
    2526from trac.util.datefmt import http_date 
    2627from trac.util.html import html 
     
    184185        if format == 'rss': 
    185186            # Get the email addresses of all known users 
    186187            if Chrome(self.env).show_email_addresses: 
    187                 for username,name,email in self.env.get_known_users(): 
    188                     if email: 
    189                         email_map[username] = email 
     188                email_map = UserManager(self.env).get_attribute_mapper('email') 
    190189        elif format == 'changelog': 
    191190            for rev in revs: 
    192191                changeset = changes[rev] 
  • 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

     
    2626from trac.core import * 
    2727from trac.perm import IPermissionRequestor 
    2828from trac.timeline.api import ITimelineEventProvider, TimelineEvent 
     29from trac.user import UserManager 
    2930from trac.util.datefmt import format_date, parse_date, to_timestamp, utc 
    3031from trac.util.html import html, Markup 
    3132from trac.util.text import to_unicode 
     
    141142            # Get the email addresses of all known users 
    142143            email_map = {} 
    143144            if Chrome(self.env).show_email_addresses: 
    144                 for username, name, email in self.env.get_known_users(): 
    145                     if email: 
    146                         email_map[username] = email 
     145                email_map = UserManager(self.env).get_attribute_mapper('email') 
    147146            data['email_map'] = email_map 
    148147            return 'timeline.rss', data, 'application/rss+xml' 
    149148 
  • trac/test.py

     
    174174    def get_db_cnx(self): 
    175175        return self.db 
    176176 
    177     def get_known_users(self, db): 
     177    def get_known_users(self): 
    178178        return self.known_users 
    179179 
    180180 
  • 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 
  • 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">