PKÀºv6“×2EGG-INFO/dependency_links.txt PKÀºv6wVBßêêEGG-INFO/entry_points.txt[trac.plugins] privatetickets.api = privatetickets.api privatetickets.view = privatetickets.view privatetickets.search = privatetickets.search privatetickets.report = privatetickets.report privatetickets.query = privatetickets.query PKÀºv6º5‡‡EGG-INFO/PKG-INFOMetadata-Version: 1.0 Name: TracPrivateTickets Version: 1.1 Summary: Modified ticket security for Trac. Home-page: http://trac-hacks.org/wiki/PrivateTickets Author: Noah Kantrowitz Author-email: coderanger@yahoo.com License: BSD Description: Allow users to only see tickets are involved with. Keywords: trac plugin ticket permissions security Platform: UNKNOWN Classifier: Framework :: Trac PKÀºv6‚@cppEGG-INFO/SOURCES.txtsetup.py TracPrivateTickets.egg-info/PKG-INFO TracPrivateTickets.egg-info/SOURCES.txt TracPrivateTickets.egg-info/dependency_links.txt TracPrivateTickets.egg-info/entry_points.txt TracPrivateTickets.egg-info/top_level.txt privatetickets/__init__.py privatetickets/api.py privatetickets/query.py privatetickets/report.py privatetickets/search.py privatetickets/view.py PKÀºv6’[â EGG-INFO/top_level.txtprivatetickets PKÁºv6“×2EGG-INFO/zip-safe PK(»_5privatetickets/__init__.pyPKÁºv6ÁÓ4”ŒŒprivatetickets/__init__.pyc;ò ­!HEc@sdS(N((((s<build/bdist.darwin-8.9.1-i386/egg/privatetickets/__init__.pys?sPKºv6úÐîy00privatetickets/api.pyfrom trac.core import * from trac.perm import IPermissionRequestor, IPermissionGroupProvider, PermissionSystem from trac.ticket.model import Ticket from trac.config import IntOption, ListOption try: set = set except NameError: from sets import Set as set __all__ = ['PrivateTicketsSystem'] class PrivateTicketsSystem(Component): """Central tasks for the PrivateTickets plugin.""" implements(IPermissionRequestor) group_providers = ExtensionPoint(IPermissionGroupProvider) blacklist = ListOption('privatetickets', 'group_blacklist', default='anonymous, authenticated', doc='Groups that do not affect the common membership check.') # IPermissionRequestor methods def get_permission_actions(self): actions = ['TICKET_VIEW_REPORTER', 'TICKET_VIEW_OWNER', 'TICKET_VIEW_CC'] group_actions = ['TICKET_VIEW_REPORTER_GROUP', 'TICKET_VIEW_OWNER_GROUP', 'TICKET_VIEW_CC_GROUP'] all_actions = actions + [(a+'_GROUP', [a]) for a in actions] return all_actions + [('TICKET_VIEW_SELF', actions), ('TICKET_VIEW_GROUP', group_actions)] # Public methods def check_ticket_access(self, req, id): """Return if this req is permitted access to the given ticket ID.""" try: tkt = Ticket(self.env, id) except TracError: return False # Ticket doesn't exist if req.perm.has_permission('TICKET_VIEW_REPORTER') and \ tkt['reporter'] == req.authname: return True if req.perm.has_permission('TICKET_VIEW_CC') and \ req.authname in [x.strip() for x in tkt['cc'].split(',')]: return True if req.perm.has_permission('TICKET_VIEW_OWNER') and \ req.authname == tkt['owner']: return True if req.perm.has_permission('TICKET_VIEW_REPORTER_GROUP') and \ self._check_group(req.authname, tkt['reporter']): return True if req.perm.has_permission('TICKET_VIEW_OWNER_GROUP') and \ self._check_group(req.authname, tkt['owner']): return True if req.perm.has_permission('TICKET_VIEW_CC_GROUP'): for user in tkt['cc'].split(','): #self.log.debug('Private: CC check: %s, %s', req.authname, user.strip()) if self._check_group(req.authname, user.strip()): return True return False # Internal methods def _check_group(self, user1, user2): """Check if user1 and user2 share a common group.""" user1_groups = self._get_groups(user1) user2_groups = self._get_groups(user2) both = user1_groups.intersection(user2_groups) both -= set(self.blacklist) #self.log.debug('PrivateTicket: %s&%s = (%s)&(%s) = (%s)', user1, user2, ','.join(user1_groups), ','.join(user2_groups), ','.join(both)) return bool(both) def _get_groups(self, user): # Get initial subjects groups = set([user]) for provider in self.group_providers: for group in provider.get_permission_groups(user): groups.add(group) perms = PermissionSystem(self.env).get_all_permissions() repeat = True while repeat: repeat = False for subject, action in perms: if subject in groups and action.islower() and action not in groups: groups.add(action) repeat = True return groups PKÁºv6 ¸¹ÔÔprivatetickets/api.pyc;ò €GFc@sdkTdklZlZlZdklZdklZl Z y e Z Wn e j odk l Z nXdgZdefd„ƒYZdS((s*(sIPermissionRequestorsIPermissionGroupProvidersPermissionSystem(sTicket(s IntOptions ListOption(sSetsPrivateTicketsSystemcBsctZdZeeƒeeƒZeddddddƒZ d„Z d„Z d „Z d „Z RS( s,Central tasks for the PrivateTickets plugin.sprivateticketssgroup_blacklistsdefaultsanonymous, authenticatedsdocs6Groups that do not affect the common membership check.cCstdddg}dddg}|gi}|D]}||d|gfƒq/~}|d|fd |fgSdS( NsTICKET_VIEW_REPORTERsTICKET_VIEW_OWNERsTICKET_VIEW_CCsTICKET_VIEW_REPORTER_GROUPsTICKET_VIEW_OWNER_GROUPsTICKET_VIEW_CC_GROUPs_GROUPsTICKET_VIEW_SELFsTICKET_VIEW_GROUP(sactionss group_actionssappends_[1]sas all_actions(sselfsas group_actionss all_actionss_[1]sactions((s7build/bdist.darwin-8.9.1-i386/egg/privatetickets/api.pysget_permission_actionss8cCs¸yt|i|ƒ}Wntj o tSnX|ii dƒo|d|i jot Sn|ii dƒoA|i gi }|didƒD]}||iƒƒq“~jot Sn|ii dƒo|i |djot Sn|ii dƒo|i|i |dƒot Sn|ii d ƒo|i|i |dƒot Sn|ii d ƒoFxC|didƒD]*}|i|i |iƒƒot Sq~q~WntSd S( s>Return if this req is permitted access to the given ticket ID.sTICKET_VIEW_REPORTERsreportersTICKET_VIEW_CCsccs,sTICKET_VIEW_OWNERsownersTICKET_VIEW_REPORTER_GROUPsTICKET_VIEW_OWNER_GROUPsTICKET_VIEW_CC_GROUPN(sTicketsselfsenvsidstkts TracErrorsFalsesreqspermshas_permissionsauthnamesTruesappends_[1]ssplitsxsstrips _check_groupsuser(sselfsreqsids_[1]stktsusersx((s7build/bdist.darwin-8.9.1-i386/egg/privatetickets/api.pyscheck_ticket_accesss* 'W'--cCsN|i|ƒ}|i|ƒ}|i|ƒ}|t|i ƒ8}t |ƒSdS(s.Check if user1 and user2 share a common group.N( sselfs _get_groupssuser1s user1_groupssuser2s user2_groupss intersectionsbothssets blacklistsbool(sselfsuser1suser2sboths user1_groupss user2_groups((s7build/bdist.darwin-8.9.1-i386/egg/privatetickets/api.pys _check_groupCs c Cs×t|gƒ}x8|iD]-}x$|i|ƒD]}|i|ƒq/WqWt |i ƒi ƒ}t }xg|o_t}xR|D]J\}}||jo|iƒo ||jo|i|ƒt }q|q|WqhW|SdS(N(ssetsusersgroupssselfsgroup_providerssprovidersget_permission_groupssgroupsaddsPermissionSystemsenvsget_all_permissionsspermssTruesrepeatsFalsessubjectsactionsislower( sselfsusersgroupspermssrepeatsgroupssprovidersactionssubject((s7build/bdist.darwin-8.9.1-i386/egg/privatetickets/api.pys _get_groupsMs"  ' (s__name__s __module__s__doc__s implementssIPermissionRequestorsExtensionPointsIPermissionGroupProvidersgroup_providerss ListOptions blacklistsget_permission_actionsscheck_ticket_accesss _check_groups _get_groups(((s7build/bdist.darwin-8.9.1-i386/egg/privatetickets/api.pysPrivateTicketsSystem s      $ N(s trac.cores trac.permsIPermissionRequestorsIPermissionGroupProvidersPermissionSystemstrac.ticket.modelsTickets trac.configs IntOptions ListOptionssets NameErrorssetssSets__all__s ComponentsPrivateTicketsSystem( s ListOptionsIPermissionGroupProviderssets__all__sIPermissionRequestors IntOptionsPermissionSystemsTicketsPrivateTicketsSystem((s7build/bdist.darwin-8.9.1-i386/egg/privatetickets/api.pys?s   PK™Ž}5ˆ£âÙÙprivatetickets/query.pyfrom trac.core import * from trac.web.api import IRequestFilter from trac.ticket.query import QueryModule, Query from trac.mimeview.api import Mimeview, IContentConverter from trac.wiki import wiki_to_html from trac.util.datefmt import http_date from trac.util.text import CRLF from StringIO import StringIO from api import PrivateTicketsSystem __all__ = ['PrivateTicketsQueryFilter'] class PrivateTicketsQueryFilter(Component): """Remove entires from queries if this user shouldn't see them.""" implements(IRequestFilter) # IRequestFilter methods def pre_process_request(self, req, handler): if isinstance(handler, QueryModule) and req.args.get('format'): self.log.debug('PrivateTickets: Intercepting formatted query') return self # XXX: Hack due to IContentConverter being b0rked return handler def post_process_request(self, req, template, content_type): if req.args.get('DO_PRIVATETICKETS_FILTER') == 'query': # Extract the data results = [] node = req.hdf.getObj('query.results') if not node: return template, content_type node = node.child() while node: data = {} sub_node = node.child() while sub_node: data[sub_node.name()] = sub_node.value() sub_node = sub_node.next() results.append(data) node = node.next() self.log.debug('PrivateTickets: results = %r', results) # Nuke the old data req.hdf.removeTree('query.results') # Filter down the data fn = PrivateTicketsSystem(self.env).check_ticket_access new_results = [d for d in results if fn(req, d['id'])] self.log.debug('PrivateTickets: new_results = %r', new_results) # Reinsert the data req.hdf['query.results'] = new_results return template, content_type # Content conversion insanity def process_request(self, req): constraints = QueryModule(self.env)._get_constraints(req) if not constraints and not req.args.has_key('order'): # avoid displaying all tickets when the query module is invoked # with no parameters. Instead show only open tickets, possibly # associated with the user constraints = {'status': ('new', 'assigned', 'reopened')} if req.authname and req.authname != 'anonymous': constraints['owner'] = (req.authname,) else: email = req.session.get('email') name = req.session.get('name') if email or name: constraints['cc'] = ('~%s' % email or name,) query = Query(self.env, constraints, req.args.get('order'), req.args.has_key('desc'), req.args.get('group'), req.args.has_key('groupdesc'), req.args.has_key('verbose')) format = req.args.get('format') self.send_converted(req, 'trac.ticket.Query', query, format, 'query') def get_supported_conversions(self): yield ('csv', 'Comma-delimited Text', 'csv', 'trac.ticket.Query', 'text/csv', 9) def convert_content(self, req, mimetype, query, key): if key == 'rss': return self.export_rss(req, query) + ('rss',) elif key == 'csv': return self.export_csv(req, query, mimetype='text/csv') + ('csv',) elif key == 'tab': return self.export_csv(req, query, '\t', 'text/tab-separated-values') + ('tsv',) def send_converted(self, req, in_type, content, selector, filename='file'): # Stolen from Mimetype """Helper method for converting `content` and sending it directly. `selector` can be either a key or a MIME Type.""" from trac.web import RequestDone content, output_type, ext = self.convert_content(req, in_type, content, selector) req.send_response(200) req.send_header('Content-Type', output_type) req.send_header('Content-Disposition', 'filename=%s.%s' % (filename, ext)) req.end_headers() req.write(content) raise RequestDone # Hacked content converters def export_csv(self, req, query, sep=',', mimetype='text/plain'): self.log.debug('PrivateTicket: Running hacked CSV converter') content = StringIO() cols = query.get_columns() content.write(sep.join([col for col in cols]) + CRLF) fn = PrivateTicketsSystem(self.env).check_ticket_access results = query.execute(req, self.env.get_db_cnx()) for result in results: # Filter data if not fn(req, result['id']): continue content.write(sep.join([unicode(result[col]).replace(sep, '_') .replace('\n', ' ') .replace('\r', ' ') for col in cols]) + CRLF) return (content.getvalue(), '%s;charset=utf-8' % mimetype) def export_rss(self, req, query): query.verbose = True db = self.env.get_db_cnx() fn = PrivateTicketsSystem(self.env).check_ticket_access results = [r for r in query.execute(req, db) if fn(req, r['id'])] for result in results: result['href'] = req.abs_href.ticket(result['id']) if result['reporter'].find('@') == -1: result['reporter'] = '' if result['description']: # unicode() cancels out the Markup() returned by wiki_to_html descr = wiki_to_html(result['description'], self.env, req, db, absurls=True) result['description'] = unicode(descr) if result['time']: result['time'] = http_date(result['time']) req.hdf['query.results'] = results req.hdf['query.href'] = req.abs_href.query(group=query.group, groupdesc=query.groupdesc and 1 or None, verbose=query.verbose and 1 or None, **query.constraints) return (req.hdf.render('query_rss.cs'), 'application/rss+xml') PKÁºv6~íÿ•RRprivatetickets/query.pyc;ò BnEc@sždkTdklZdklZlZdklZlZdk l Z dk l Z dk lZdklZdklZd gZd efd „ƒYZd S( (s*(sIRequestFilter(s QueryModulesQuery(sMimeviewsIContentConverter(s wiki_to_html(s http_date(sCRLF(sStringIO(sPrivateTicketsSystemsPrivateTicketsQueryFiltercBsitZdZeeƒd„Zd„Zd„Zd„Zd„Z dd„Z dd d „Z d „Z RS( s<Remove entires from queries if this user shouldn't see them.cCsCt|tƒo|iidƒo|iidƒ|Sn|SdS(Nsformats,PrivateTickets: Intercepting formatted query( s isinstanceshandlers QueryModulesreqsargssgetsselfslogsdebug(sselfsreqshandler((s9build/bdist.darwin-8.9.1-i386/egg/privatetickets/query.pyspre_process_requests#c Csd|iidƒdjo=g}|iidƒ}| o||fSn|i ƒ}xh|o`h} |i ƒ} x.| o&| i ƒ| | i ƒ<| iƒ} qrW|i| ƒ|iƒ}qVW|iid|ƒ|iidƒt|iƒi} gi}|D](}| ||dƒo||ƒqq~}|iid|ƒ||id} |t|| ƒi|dƒiddƒiddƒƒqÐ~ƒtƒq”W| iƒd|fSdS(Ns+PrivateTicket: Running hacked CSV convertersids_s s s s%s;charset=utf-8(sselfslogsdebugsStringIOscontentsquerys get_columnsscolsswritessepsjoinsappends_[1]scolsCRLFsPrivateTicketsSystemsenvscheck_ticket_accesssfnsexecutesreqs get_db_cnxsresultssresultsunicodesreplacesgetvaluesmimetype( sselfsreqsqueryssepsmimetypes_[1]sresultscolssresultsscontentscolsfn((s9build/bdist.darwin-8.9.1-i386/egg/privatetickets/query.pys export_csvps  ;mc Cs¦t|_|iiƒ}t|iƒi} gi }|i ||ƒD](}| ||dƒo||ƒqDqD~}x´|D]¬}|ii|dƒ|d<|didƒdjod|d