Edgewall Software

Ticket #2456: user_API_r5898.diff

File user_API_r5898.diff, 22.5 kB (added by Morris, 11 months ago)

patch for r5898 -- I have lots of other mods, so here's hoping that I filtered them all out of this patch...

  • 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/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 
     
    158159    smtp_port = 25 
    159160    from_email = 'trac+tickets@localhost' 
    160161    subject = '' 
     162    server = None 
    161163    template_name = None 
    162164    nodomaddr_re = re.compile(r'[\w\d_\.\-]+') 
    163165    addrsep_re = re.compile(r'[;\s,]+') 
     
    177179        self.longaddr_re = re.compile(r'^\s*(.*)\s+<(%s)>\s*$' % addrfmt); 
    178180        self._use_tls = self.env.config.getbool('notification', 'use_tls') 
    179181        self._init_pref_encoding() 
    180         domains = self.env.config.get('notification', 'ignore_domains', '') 
    181         self._ignore_domains = [x.strip() for x in domains.lower().split(',')] 
    182         # Get the email addresses of all known users 
    183         self.email_map = {} 
    184         for username, name, email in self.env.get_known_users(self.db): 
    185             if email: 
    186                 self.email_map[username] = email 
    187                  
     182 
    188183    def _init_pref_encoding(self): 
    189184        from email.Charset import Charset, QP, BASE64 
    190185        self._charset = Charset() 
     
    270265        if not is_email(address): 
    271266            if address == 'anonymous': 
    272267                return None 
    273             if self.email_map.has_key(address): 
    274                 address = self.email_map[address] 
     268             
     269            email = UserManager(self.env).get_user(address)['email'] 
     270            if email and len(email) > 0: 
     271                address = email 
    275272            elif NotifyEmail.nodomaddr_re.match(address): 
    276273                if self.config.getbool('notification', 'use_short_addr'): 
    277274                    return address 
  • trac/perm.py

     
    2020 
    2121from trac.config import ExtensionOption, OrderedExtensionsOption 
    2222from trac.core import * 
     23from trac.user import UserManager 
    2324from trac.util.compat import set 
    2425from trac.util.translation import _ 
    2526 
     
    157158        db = self.env.get_db_cnx() 
    158159        cursor = db.cursor() 
    159160        result = set() 
    160         users = set([u[0] for u in self.env.get_known_users()]) 
     161        users = set([username for username 
     162                    in UserManager(self.env).get_usernames()]) 
    161163        for user in users: 
    162164            userperms = self.get_user_permissions(user) 
    163165            for group in permissions: 
  • 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.util.translation import _ 
    2728from trac.web import HTTPNotFound, IRequestHandler 
     
    9394                self._do_save(req) 
    9495            req.redirect(req.href.prefs(panel or None)) 
    9596 
     97        name = email = '' 
     98        if req.authname != 'anonymous': 
     99            user = UserManager(self.env).get_user(req.authname) 
     100            name, email = user['name'], user['email'] 
     101        else: 
     102            name = req.session.get('name') 
     103            email = req.session.get('email') 
    96104        return 'prefs_%s.html' % (panel or 'general'), { 
    97             'settings': {'session': req.session, 'session_id': req.session.sid}, 
     105            'settings': {'session': req.session, 'session_id': req.session.sid, 
     106                         'name': name, 'email': email}, 
    98107            'timezones': all_timezones, 'timezone': get_timezone 
    99108        } 
    100109 
     
    118127                elif field == 'newsid' and val: 
    119128                    req.session.change_sid(val) 
    120129                else: 
    121                     req.session[field] = val 
    122             elif field in req.args and field in req.session: 
    123                 del req.session[field] 
     130                    if req.authname != 'anonymous' and \ 
     131                            field in ('name', 'email'): 
     132                        user = UserManager(self.env).get_user(req.authname) 
     133                        user[field] = val 
     134                    else: 
     135                        req.session[field] = val 
     136            elif field in req.args: 
     137                if req.authname != 'anonymous' and \ 
     138                        field in ('name', 'email'): 
     139                    user = UserManager(self.env).get_user(req.authname) 
     140                    user[field] = '' 
     141                elif field in req.session: 
     142                    del req.session[field] 
    124143 
    125144    def _do_load(self, req): 
    126145        if req.authname == 'anonymous': 
  • trac/test.py

     
    187187    def get_db_cnx(self): 
    188188        return self.db 
    189189 
    190     def get_known_users(self, db): 
     190    def get_known_users(self): 
    191191        return self.known_users 
    192192 
    193193 
  • trac/ticket/admin.py

     
    107107 
    108108        if self.config.getbool('ticket', 'restrict_owner'): 
    109109            perm = PermissionSystem(self.env) 
    110             def valid_owner(username): 
    111                 return perm.get_user_permissions(username).get('TICKET_MODIFY') 
    112             data['owners'] = [username for username, name, email 
    113                               in self.env.get_known_users() 
    114                               if valid_owner(username)] 
     110            data['owners'] = perm.get_users_with_permission('TICKET_MODIFY') 
    115111        else: 
    116112            data['owners'] = None 
    117113 
  • trac/ticket/report.py

     
    2727from trac.core import * 
    2828from trac.db import get_column_names 
    2929from trac.perm import IPermissionRequestor 
     30from trac.user import UserManager 
    3031from trac.util import sorted 
    3132from trac.util.datefmt import format_datetime, format_time 
    3233from trac.util.text import to_unicode, unicode_urlencode 
     
    369370        # Get the email addresses of all known users 
    370371        email_map = {} 
    371372        if Chrome(self.env).show_email_addresses: 
    372             for username, name, email in self.env.get_known_users(): 
    373                 if email: 
    374                     email_map[username] = email 
     373            email_map = UserManager(self.env).get_attribute_mapper('email') 
    375374 
    376375        data.update({'header_groups': header_groups, 
    377376                     'row_groups': row_groups, 
  • trac/timeline/web_ui.py

     
    3030from trac.perm import IPermissionRequestor 
    3131from trac.timeline.api import ITimelineEventProvider, TimelineEvent 
    3232from trac.util.compat import sorted 
     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 
     
    167168            # Get the email addresses of all known users 
    168169            email_map = {} 
    169170            if Chrome(self.env).show_email_addresses: 
    170                 for username, name, email in self.env.get_known_users(): 
    171                     if email: 
    172                         email_map[username] = email 
     171                email_map = UserManager(self.env).get_attribute_mapper('email') 
    173172            data['email_map'] = email_map 
    174173            return 'timeline.rss', data, 'application/rss+xml' 
    175174 
  • 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/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 
     
    193194        if format == 'rss': 
    194195            # Get the email addresses of all known users 
    195196            if Chrome(self.env).show_email_addresses: 
    196                 for username,name,email in self.env.get_known_users(): 
    197                     if email: 
    198                         email_map[username] = email 
     197                email_map = UserManager(self.env).get_attribute_mapper('email') 
    199198        elif format == 'changelog': 
    200199            for rev in revs: 
    201200                changeset = changes[rev]