Ticket #2456: user_API.2.diff
| File user_API.2.diff, 27.0 KB (added by Waldemar Kornewald <wkornewald>, 5 years ago) |
|---|
-
trac/env.py
23 23 from trac.core import Component, ComponentManager, implements, Interface, \ 24 24 ExtensionPoint, TracError 25 25 from trac.db import DatabaseManager 26 from trac.user import UserManager, User 26 27 from trac.util import get_pkginfo 27 28 from trac.versioncontrol import RepositoryManager 28 29 from trac.web.href import Href … … 283 284 logfile = os.path.join(self.get_log_dir(), logfile) 284 285 self.log = logger_factory(logtype, logfile, self.log_level, self.path) 285 286 286 def get_known_users(self , cnx=None):287 def get_known_users(self): 287 288 """Generator that yields information about all known users, i.e. users 288 289 that have logged in to this Trac environment and possibly set their name 289 290 and email. 290 291 291 292 This function generates one tuple for every user, of the form 292 293 (username, name, email) ordered alpha-numerically by username. 293 294 @param cnx: the database connection; if ommitted, a new connection is295 retrieved296 294 """ 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'] 309 297 310 298 def backup(self, dest=None): 311 299 """Simple SQLite-specific backup of the database. -
trac/prefs/web_ui.py
21 21 22 22 from trac.core import * 23 23 from trac.prefs.api import IPreferencePanelProvider 24 from trac.user import UserManager, User 24 25 from trac.util.datefmt import all_timezones, utc 25 26 from trac.web import HTTPNotFound, IRequestHandler 26 27 from trac.web.chrome import add_stylesheet, INavigationContributor … … 90 91 self._do_save(req) 91 92 req.redirect(req.href.prefs(panel or None)) 92 93 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') 93 101 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}, 95 104 'timezones': all_timezones 96 105 } 97 106 … … 107 116 elif field == 'newsid' and val: 108 117 req.session.change_sid(val) 109 118 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] 113 132 114 133 def _do_load(self, req): 115 134 if req.authname == 'anonymous': -
trac/ticket/admin.py
17 17 from trac.core import * 18 18 from trac.perm import PermissionSystem 19 19 from trac.ticket import model 20 from trac.user import UserManager 20 21 from trac.util import datefmt 21 22 from trac.web.chrome import add_link, add_script 22 23 … … 91 92 perm = PermissionSystem(self.env) 92 93 def valid_owner(username): 93 94 return perm.get_user_permissions(username).get('TICKET_MODIFY') 94 data['owners'] = [username for username , name, email95 in self.env.get_known_users()95 data['owners'] = [username for username 96 in UserManager(self.env).get_usernames() 96 97 if valid_owner(username)] 97 98 98 99 return 'admin_components.html', data -
trac/ticket/api.py
20 20 from trac.config import * 21 21 from trac.core import * 22 22 from trac.perm import IPermissionRequestor, PermissionSystem 23 from trac.user import UserManager 23 24 from trac.util import Ranges 24 25 from trac.util.html import html 25 26 from trac.util.text import shorten_line … … 102 103 field = {'name': 'owner', 'label': 'Owner'} 103 104 if self.restrict_owner: 104 105 field['type'] = 'select' 105 users = [''] # for clearing assignment106 106 perm = PermissionSystem(self.env) 107 for username, name, email in self.env.get_known_users(db): 108 if perm.get_user_permissions(username).get('TICKET_MODIFY'): 109 users.append(username) 110 field['options'] = users 107 def valid_owner(username): 108 return perm.get_user_permissions(username).get('TICKET_MODIFY') 109 field['options'] = [username for username 110 in UserManager(self.env).get_usernames() 111 if valid_owner(username)] 111 112 field['optional'] = True 112 113 else: 113 114 field['type'] = 'text' -
trac/ticket/report.py
22 22 from trac.core import * 23 23 from trac.db import get_column_names 24 24 from trac.perm import IPermissionRequestor 25 from trac.user import UserManager 25 26 from trac.util import sorted 26 27 from trac.util.text import to_unicode, unicode_urlencode 27 28 from trac.util.html import html … … 276 277 header_groups.append([]) 277 278 header_group.append(header) 278 279 279 # Get the email addresses of all known users280 email_map = {}281 for username, name, email in self.env.get_known_users():282 if email:283 email_map[username] = email284 285 280 # Structure the rows and cells: 286 281 # - group rows according to __group__ value, if defined 287 282 # - group cells the same way headers are grouped … … 313 308 # Special casing based on column name 314 309 col = col.strip('_') 315 310 if col == 'reporter': 316 if '@' in value: 317 cell['author'] = value 318 elif value in email_map: 319 cell['author'] = email_map[value] 311 cell['author'] = value 320 312 elif col == 'resource': 321 313 resource = value 322 314 cell_group.append(cell) -
trac/ticket/query.py
690 690 query.verbose = True 691 691 db = self.env.get_db_cnx() 692 692 results = query.execute(req, db) 693 for result in results:694 if result['reporter'].find('@') == -1:695 result['reporter'] = ''696 693 query_href = req.abs_href.query(group=query.group, 697 694 groupdesc=query.groupdesc and 1 or None, 698 695 verbose=query.verbose and 1 or None, -
trac/versioncontrol/web_ui/log.py
21 21 22 22 from trac.core import * 23 23 from trac.perm import IPermissionRequestor 24 from trac.user import UserManager 24 25 from trac.util import Ranges 25 26 from trac.util.datefmt import http_date 26 27 from trac.util.html import html … … 178 179 revs = [i['rev'] for i in info] 179 180 changes = get_changes(repos, revs) 180 181 extra_changes = {} 181 email_map = {} 182 if format == 'rss': 183 # Get the email addresses of all known users 184 email_map = {} 185 for username,name,email in self.env.get_known_users(): 186 if email: 187 email_map[username] = email 188 elif format == 'changelog': 182 if format == 'changelog': 189 183 for rev in revs: 190 184 changeset = changes[rev] 191 185 cs = {} … … 206 200 'mode': mode, 'verbose': verbose, 207 201 'path_links': path_links, 208 202 'items': info, 'changes': changes, 209 'e mail_map': email_map, 'extra_changes': extra_changes,203 'extra_changes': extra_changes, 210 204 'wiki_format_messages': 211 205 self.config['changeset'].getbool('wiki_format_messages') 212 206 } -
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 16 from trac.core import * 17 from trac.config import * 18 19 20 class 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 67 class 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 100 101 class UserManager(Component): 102 """ 103 Component responsible for managing users and user attributes. 104 """ 105 106 store = ExtensionOption('users', 107 'store', IUserStore, 'SessionUserStore', 108 doc="""The user store that should be used for authentication 109 (''since 0.11'').""") 110 attribute_providers = OrderedExtensionsOption('users', 111 'attribute_providers', IUserAttributeProvider, 112 doc="""Ordered list of user attribute providers (''since 0.11'').""") 113 114 # public API 115 116 def supports_operation(self, operation): 117 if self.store.supports_user_operation(operation): 118 return True 119 for provider in self.attribute_providers: 120 if self.provider.supports_attribute_operation(operation): 121 return True 122 return False 123 124 # IUserStore methods 125 126 def create_user(self, username, password): 127 """ 128 Creates a new user with the given username and password. 129 130 @return User object or None if the user couldn't be created 131 """ 132 if not self.store.supports_user_operation('create_user'): 133 return None 134 if self.store.create_user(username, password): 135 return User(username, self.store, self.attribute_providers) 136 137 def get_user(self, username): 138 """ 139 Returns a User object for the username. 140 141 @return User object or None if the user doesn't exist 142 """ 143 if username in self.get_usernames(): 144 return User(username, self.store, self.attribute_providers) 145 return None 146 147 def get_all_users(self): 148 """ 149 Generator for User objects. 150 """ 151 if not self.store.supports_user_operation('get_usernames'): 152 return 153 for username in self.store.get_usernames(): 154 yield User(username, self.store, self.attribute_providers) 155 156 def get_usernames(self): 157 """ 158 Generator for usernames. 159 """ 160 if not self.store.supports_user_operation('get_usernames'): 161 return 162 return self.store.get_usernames() 163 164 165 class User(object): 166 """ 167 Object representing a user. 168 """ 169 170 def __init__(self, username, store, attribute_providers): 171 self._username = username 172 self.store = store 173 self.attribute_providers = attribute_providers 174 175 # public API 176 177 @property 178 def username(self): 179 return self._username 180 181 # IUserStore methods 182 183 def check_password(self, password): 184 if not self.store.supports_user_operation('check_password'): 185 return False 186 return self.store.check_password(self.username, password) 187 188 def change_password(self, password): 189 if not self.store.supports_user_operation('change_password'): 190 return False 191 return self.store.change_password(self.username, password) 192 193 def delete(self): 194 if not self.store.supports_user_operation('delete_user'): 195 return False 196 self.delete_all_attributes() 197 return self.store.delete_user(self.username) 198 199 # IUserAttributeProvider methods 200 201 def __getitem__(self, attribute): 202 for provider in self.attribute_providers: 203 if not provider.supports_attribute_operation('get_user_attribute'): 204 continue 205 value = provider.get_user_attribute(self.username, attribute) 206 if value is not None: 207 return value 208 return None 209 210 def __setitem__(self, attribute, value): 211 for provider in self.attribute_providers: 212 if provider.supports_attribute_operation('set_user_attribute') \ 213 and provider.set_user_attribute(self.username, attribute, 214 value): 215 return True 216 return False 217 218 def delete_all_attributes(self): 219 for provider in self.attribute_providers: 220 if provider.supports_attribute_operation('delete_all_user_attributes'): 221 provider.delete_all_user_attributes(username) 222 223 224 class SessionUserStore(Component): 225 """ 226 Component for managing authenticated users stored in sessions. 227 """ 228 229 implements(IUserStore) 230 231 def supports_user_operation(self, operation): 232 return hasattr(self, operation) 233 234 def get_usernames(self): 235 db = self.env.get_db_cnx() 236 cursor = db.cursor() 237 cursor.execute("SELECT sid FROM session " 238 "WHERE authenticated=1 " 239 "ORDER BY sid") 240 for row in cursor: 241 yield row[0] 242 243 244 class SessionUserAttributeProvider(Component): 245 """ 246 Component for providing user attributes via Trac sessions. 247 """ 248 249 implements(IUserAttributeProvider) 250 251 def supports_attribute_operation(self, operation): 252 return hasattr(self, operation) 253 254 def get_user_attribute(self, username, attribute): 255 db = self.env.get_db_cnx() 256 cursor = db.cursor() 257 cursor.execute("SELECT value FROM session_attribute " 258 "WHERE sid=%s AND name=%s AND authenticated=1", 259 (username, attribute)) 260 row = cursor.fetchone() 261 if row: 262 return row[0] 263 264 # if the attribute doesn't exist we return an empty string to 265 # indicate that the attribute is supported, but not set 266 return '' 267 268 def set_user_attribute(self, username, attribute, value): 269 db = self.env.get_db_cnx() 270 cursor = db.cursor() 271 272 if not value or value == '': 273 cursor.execute("DELETE FROM session_attribute " 274 "WHERE sid=%s AND name=%s AND authenticated=1", 275 (username, attribute)) 276 db.commit() 277 return True 278 279 # check if attribute exists 280 cursor.execute("SELECT value FROM session_attribute " 281 "WHERE sid=%s AND name=%s AND authenticated=1", 282 (username, attribute)) 283 if cursor.fetchone(): 284 # update the attribute 285 cursor.execute("UPDATE session_attribute SET value=%s " 286 "WHERE sid=%s AND name=%s AND authenticated=1", 287 (value, username, attribute)) 288 else: 289 # create new attribute 290 cursor.execute("INSERT INTO session_attribute " 291 "(sid,authenticated,name,value) " 292 "VALUES(%s,1,%s,%s)", 293 (username, attribute, value)) 294 db.commit() 295 return True 296 297 def delete_all_user_attributes(self, username): 298 db = self.env.get_db_cnx() 299 cursor = db.cursor() 300 cursor.execute("DELETE FROM session_attribute " 301 "WHERE sid=%s AND authenticated=1", 302 (username,)) 303 db.commit() -
trac/timeline/web_ui.py
26 26 from trac.core import * 27 27 from trac.perm import IPermissionRequestor 28 28 from trac.timeline.api import ITimelineEventProvider, TimelineEvent 29 from trac.user import UserManager 29 30 from trac.util.datefmt import format_date, parse_date, to_timestamp, utc 30 31 from trac.util.html import html, Markup 31 32 from trac.util.text import to_unicode … … 137 138 break 138 139 139 140 if format == 'rss': 140 # Get the email addresses of all known users141 email_map = {}142 for username, name, email in self.env.get_known_users():143 if email:144 email_map[username] = email145 data['email_map'] = email_map146 141 return 'timeline.rss', data, 'application/rss+xml' 147 142 148 143 add_stylesheet(req, 'common/css/timeline.css') -
trac/test.py
174 174 def get_db_cnx(self): 175 175 return self.db 176 176 177 def get_known_users(self , db):177 def get_known_users(self): 178 178 return self.known_users 179 179 180 180 -
trac/notification.py
22 22 from trac import __version__ 23 23 from trac.config import BoolOption, IntOption, Option 24 24 from trac.core import * 25 from trac.user import UserManager 25 26 from trac.util.text import CRLF 26 27 from trac.web.chrome import Chrome 27 28 … … 148 149 from_email = 'trac+tickets@localhost' 149 150 subject = '' 150 151 server = None 151 email_map = None152 152 template_name = None 153 153 addrfmt = r"[\w\d_\.\-\+=]+\@(([\w\d\-])+\.)+([\w\d]{2,4})+" 154 154 shortaddr_re = re.compile(addrfmt) … … 161 161 162 162 self._use_tls = self.env.config.getbool('notification', 'use_tls') 163 163 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 170 165 def _init_pref_encoding(self): 171 166 from email.Charset import Charset, QP, BASE64 172 167 self._charset = Charset() … … 241 236 if address.find('@') == -1: 242 237 if address == 'anonymous': 243 238 return None 244 if self.email_map.has_key(address): 245 address = self.email_map[address] 239 user = UserManager(self.env).get_user(address) 240 email = user and user['email'] 241 if user and email: 242 address = email 246 243 elif NotifyEmail.nodomaddr_re.match(address): 247 244 if self.config.getbool('notification', 'use_short_addr'): 248 245 return address -
templates/prefs_general.html
14 14 <tr class="field"> 15 15 <th><label for="name">Full name:</label></th> 16 16 <td><input type="text" id="name" name="name" size="30" 17 value="${settings. session.name}" /></td>17 value="${settings.name}" /></td> 18 18 </tr> 19 19 <tr class="field"> 20 20 <th><label for="email">Email address:</label></th> 21 21 <td><input type="text" id="email" name="email" size="30" 22 value="${settings. session.email}" /></td>22 value="${settings.email}" /></td> 23 23 </tr> 24 24 </table> 25 25 <p py:choose="" class="hint"> -
templates/report.rss
17 17 <py:with vars="col = cell.header.col.strip('_')"> 18 18 <py:choose> 19 19 <py:when test="col == 'reporter'"> 20 < author py:if="cell.author">$cell.author</author>20 <dc:creator py:if="cell.author">$cell.author</cd:creator> 21 21 </py:when> 22 22 <py:when test="col in ('time', 'changetime', 'created', 'modified')"> 23 23 <!-- FIXME: we end up with multiple pubDate --> -
templates/revisionlog.rss
13 13 </image> 14 14 15 15 <item py:for="item in items" py:with="change = changes[item.rev]; item_context = context('changeset', change.rev, abs_urls=True)"> 16 < author py:if="change.author" py:with="a = change.author">${a and '@' in a and a or email_map.get(a)}</author>16 <dc:creator py:if="change.author">${change.author}</dc:creator> 17 17 <pubDate>${http_date(change.date)}</pubDate> 18 18 <title>Revision $item.rev: ${shorten_line(change.message)}</title> 19 19 <link>${abs_href.changeset(rev, path)}</link> -
templates/timeline.rss
14 14 15 15 <item py:for="event in events"> 16 16 <title>${plaintext(event.title, keeplinebreaks=False)}</title> 17 <py:with vars="author=event.author; author = author and '@' in author and author or email_map.get(author)"> 18 <author py:if="author">$author</author> 19 </py:with> 17 <dc:creator py:if="event.author">${event.author}</dc:creator> 20 18 <pubDate>${http_date(event.date)}</pubDate> 21 19 <link>${event.abs_href}</link> 22 20 <guid isPermaLink="false">${event.abs_href}/${event.dateuid()}</guid> -
templates/ticket.rss
13 13 <generator>Trac $trac.version</generator> 14 14 15 15 <item py:for="change in changes"> 16 < author py:if="change.author">$change.author</author>16 <dc:creator py:if="change.author">$change.author</dc:creator> 17 17 <pubDate>${http_date(change.date)}</pubDate> 18 18 <title>$change.title</title> 19 19 <link>${abs_href.ticket(ticket.id)}<py:if test="change.cnum">#comment:$change.cnum</py:if></link> -
templates/query.rss
16 16 <guid isPermaLink="false">$href</guid> 17 17 <title>#$result.id: $result.summary</title> 18 18 <pubDate py:if="result.time">${http_date(result.time)}</pubDate> 19 < author py:if="result.reporter">$result.reporter</author>19 <dc:creator py:if="result.reporter">$result.reporter</dc:creator> 20 20 <description>${unicode(context('ticket', result.id, abs_urls=True).wiki_to_html(result.description))}</description> 21 21 <category>Results</category> 22 22 <comments>$href#changelog</comments>
