Index: /boot/home/svn/trac/trac/ticket/web_ui.py
===================================================================
--- /boot/home/svn/trac/trac/ticket/web_ui.py (revision 2031)
+++ /boot/home/svn/trac/trac/ticket/web_ui.py (working copy)
@@ -164,7 +164,7 @@
db = self.env.get_db_cnx()
id = int(req.args.get('id'))
- ticket = Ticket(self.env, id, db=db)
+ ticket = Ticket(self.env, id, db=db, roles=req.perm.roles)
reporter_id = util.get_reporter_id(req)
if req.method == 'POST':
@@ -254,8 +254,8 @@
db = self.env.get_db_cnx()
cursor = db.cursor()
- cursor.execute(" UNION ALL ".join(sql), (start, stop, start, stop,
- start, stop))
+ cursor.execute(" UNION ALL ".join(sql), start, stop, start, stop,
+ start, stop)
kinds = {'new': 'newticket', 'reopened': 'newticket',
'closed': 'closedticket'}
verbs = {'new': 'created', 'reopened': 'reopened',
Index: /boot/home/svn/trac/trac/ticket/report.py
===================================================================
--- /boot/home/svn/trac/trac/ticket/report.py (revision 2031)
+++ /boot/home/svn/trac/trac/ticket/report.py (working copy)
@@ -93,6 +93,12 @@
actions = ['REPORT_CREATE', 'REPORT_DELETE', 'REPORT_MODIFY',
'REPORT_SQL_VIEW', 'REPORT_VIEW']
return actions + [('REPORT_ADMIN', actions)]
+
+ # IPermissionGroupProvider methods
+
+ def get_permission_group(self, username):
+ pass
+
# IRequestHandler methods
@@ -156,7 +162,7 @@
cursor = db.cursor()
cursor.execute("INSERT INTO report (title,sql,description) "
"VALUES (%s,%s,%s)", (title, sql, description))
- id = db.get_last_id(cursor, 'report')
+ id = db.get_last_id('report')
db.commit()
req.redirect(self.env.href.report(id))
@@ -229,7 +235,7 @@
req.hdf['report.href'] = self.env.href.report()
req.hdf['report.action'] = 'new'
else:
- req.hdf['title'] = 'Edit Report {%d} %s' % (id, title)
+ req.hdf['title'] = 'Edit Report {%d} %s' % (id, row['title'])
req.hdf['report.href'] = self.env.href.report(id)
req.hdf['report.action'] = 'edit'
@@ -275,7 +281,9 @@
try:
cols, rows = self.execute_report(req, db, id, sql, args)
except Exception, e:
- req.hdf['report.message'] = 'Report execution failed: %s' % e
+ req.hdf['report.message'] = 'Report execution failed: %s \
+
\
+ %s
%s' % (e, sql, req.perm.roles)
return 'report.cs', None
# Convert the header info to HDF-format
@@ -342,11 +350,10 @@
value['hidehtml'] = 1
column = column[1:]
if column in ['id', 'ticket', '#', 'summary']:
- id_cols = [idx for idx, col in util.enum(cols)
- if col[0] in ('ticket', 'id')]
- if id_cols:
- id_val = row[id_cols[0]]
- value['ticket_href'] = self.env.href.ticket(id_val)
+ if row.has_key('ticket'):
+ value['ticket_href'] = self.env.href.ticket(row['ticket'])
+ elif row.has_key('id'):
+ value['ticket_href'] = self.env.href.ticket(row['id'])
elif column == 'description':
value['parsed'] = wiki_to_html(cell, self.env, req, db)
elif column == 'reporter':
@@ -403,15 +410,26 @@
'text/plain')
def execute_report(self, req, db, id, sql, args):
- sql = self.sql_sub_vars(req, sql, args)
+ cursor = db.cursor()
+ self.paras = []
+ args['ROLES'] = tuple(req.perm.roles)
+
+ #sql = self.sql_sub_vars(req, sql, args)
+ for key, value in args.items():
+ match = "$%s" % key.upper()
+ if sql.find(match) > 0:
+ sql = sql.replace("$" + key.upper(), "%s")
+ self.paras.append(value)
+ self.paras = tuple(self.paras)
+
if not sql:
raise util.TracError('Report %s has no SQL query.' % id)
if sql.find('__group__') == -1:
req.hdf['report.sorting.enabled'] = 1
-
- cursor = db.cursor()
- cursor.execute(sql)
-
+ if not self.paras:
+ cursor.execute(sql)
+ else:
+ cursor.execute(sql, (self.paras))
# FIXME: fetchall should probably not be used.
info = cursor.fetchall()
cols = cursor.description
@@ -460,7 +478,8 @@
# Set some default dynamic variables
if not report_args.has_key('USER'):
report_args['USER'] = req.authname
-
+## if not report_args.has_key('GROUPS'):
+## report_args['GROUPS'] = 'anonymous'
return report_args
def sql_sub_vars(self, req, sql, args):
Index: /boot/home/svn/trac/trac/ticket/model.py
===================================================================
--- /boot/home/svn/trac/trac/ticket/model.py (revision 2031)
+++ /boot/home/svn/trac/trac/ticket/model.py (working copy)
@@ -33,10 +33,11 @@
class Ticket(object):
- def __init__(self, env, tkt_id=None, db=None):
+ def __init__(self, env, tkt_id=None, db=None, roles=[]):
self.env = env
self.fields = TicketSystem(self.env).get_ticket_fields()
self.values = {}
+ self.roles = roles
if tkt_id:
self._fetch_ticket(tkt_id, db)
else:
@@ -75,8 +76,12 @@
# Fetch the standard ticket fields
std_fields = [f['name'] for f in self.fields if not f.get('custom')]
cursor = db.cursor()
- cursor.execute("SELECT %s,time,changetime FROM ticket WHERE id=%%s"
- % ','.join(std_fields), (tkt_id,))
+ cursor.execute("SELECT %s,time,changetime FROM ticket \
+ INNER JOIN user_role ON \
+ (user_role.username = ticket.reporter) \
+ WHERE role in %s \
+ AND id=%s"
+ % (','.join(std_fields), tuple(self.roles), tkt_id))
row = cursor.fetchone()
if not row:
raise TracError('Ticket %d does not exist.' % tkt_id,
@@ -154,15 +159,14 @@
','.join(['%s'] * (len(std_fields) + 2))),
[self[name] for name in std_fields] +
[self.time_created, self.time_changed])
- tkt_id = db.get_last_id(cursor, 'ticket')
+ tkt_id = db.get_last_id('ticket')
# Insert custom fields
custom_fields = [f['name'] for f in self.fields if f.get('custom')
and f['name'] in self.values.keys()]
- if custom_fields:
- cursor.executemany("INSERT INTO ticket_custom (ticket,name,value) "
- "VALUES (%s,%s,%s)", [(tkt_id, name, self[name])
- for name in custom_fields])
+ cursor.executemany("INSERT INTO ticket_custom (ticket,name,value) "
+ "VALUES (%s,%s,%s)", [(tkt_id, name, self[name])
+ for name in custom_fields])
if handle_ta:
db.commit()
@@ -325,11 +329,11 @@
self.env.log.debug("Creating new %s '%s'" % (self.type, self.name))
value = self.value
if not value:
- cursor.execute("SELECT COALESCE(MAX(value),0) FROM enum "
+ cursor.execute("SELECT COALESCE(MAX(value)) FROM enum "
"WHERE type=%s", (self.type,))
- value = int(cursor.fetchone()[0]) + 1
- cursor.execute("INSERT INTO enum (type,name,value) VALUES (%s,%s,%s)",
- (self.type, self.name, value))
+ value = int(cursor.fetchone()[0])
+ cursor.execute("INSERT INTO enum (name,value) VALUES (%s,%s)",
+ (self.name, self.value))
if handle_ta:
db.commit()
Index: /boot/home/svn/trac/trac/ticket/query.py
===================================================================
--- /boot/home/svn/trac/trac/ticket/query.py (revision 2031)
+++ /boot/home/svn/trac/trac/ticket/query.py (working copy)
@@ -40,7 +40,7 @@
class Query(object):
def __init__(self, env, constraints=None, order=None, desc=0, group=None,
- groupdesc = 0, verbose=0):
+ groupdesc = 0, verbose=0, roles=[]):
self.env = env
self.constraints = constraints or {}
self.order = order
@@ -48,6 +48,7 @@
self.group = group
self.groupdesc = groupdesc
self.verbose = verbose
+ self.roles = roles
self.fields = TicketSystem(self.env).get_ticket_fields()
self.cols = [] # lazily initialized
@@ -198,7 +199,8 @@
sql.append(",priority.value AS priority_value")
for k in [k for k in cols if k in custom_fields]:
sql.append(",%s.value AS %s" % (k, k))
- sql.append("\nFROM ticket AS t")
+ sql.append("\nFROM ticket AS t \nINNER JOIN user_role ON \
+ (user_role.username = t.reporter)")
for k in [k for k in cols if k in custom_fields]:
sql.append("\n LEFT OUTER JOIN ticket_custom AS %s ON " \
"(id=%s.ticket AND %s.name='%s')" % (k, k, k, k))
@@ -229,6 +231,7 @@
name, neg and '!' or '', value)
clauses = []
+ clauses.append("role IN %s" %(tuple(self.roles),))
for k, v in self.constraints.items():
# Determine the match mode of the constraint (contains, starts-with,
# negation, etc)
@@ -350,7 +353,7 @@
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'))
+ req.args.has_key('verbose'), req.perm.roles)
if req.args.has_key('update'):
# Reset session vars
Index: /boot/home/svn/trac/trac/perm.py
===================================================================
--- /boot/home/svn/trac/trac/perm.py (revision 2031)
+++ /boot/home/svn/trac/trac/perm.py (working copy)
@@ -75,6 +75,9 @@
def revoke_permission(username, action):
"""Revokes the permission of the given user to perform an action."""
+
+ def get_user_roles(username):
+ """Return a list of roles for a particular user"""
class PermissionSystem(Component):
@@ -165,6 +168,11 @@
actions.append(action)
return [('TRAC_ADMIN', actions)]
+ # IPermissionGroupProvider methods
+ def get_permission_groups(self, username=None):
+ roles = self.store.get_permission_groups(username)
+ return roles
+
# Internal methods
def _get_store(self):
@@ -260,7 +268,59 @@
self.log.info('Revoked permission for %s to %s' % (action, username))
db.commit()
+class RelationalPermissionStore(DefaultPermissionStore):
+ """Implementation of permission storage and simple group management with support
+ for explicit groups
+
+ This component uses the `PERMISSION` and USER_ROLE and ROLE_ROLE table in the database to store both
+ permissions and groups.
+ """
+ implements(IPermissionStore)
+ role_providers = ExtensionPoint(IPermissionGroupProvider)
+
+ def get_permission_groups(self, username):
+ db = self.env.get_db_cnx()
+ cursor = db.cursor()
+
+ roles = []
+ cursor.execute("""SELECT role FROM user_role
+ WHERE username = %s""", username)
+ role = cursor.fetchone()
+ while role:
+ roles.append(role[0])
+ cursor.execute("""SELECT parent FROM role_role
+ WHERE role = %s""", role[0])
+ role = cursor.fetchone()
+ if not roles:
+ roles = ['anonymous']
+ return roles
+
+ def get_user_permissions(self, username):
+ """Retrieve the permissions for the given user and return them in a
+ dictionary.
+
+ The permissions are stored in the database as (username, action)
+ records.
+
+ Users are members of explicit groups and have both user and group
+ permissions.
+
+ The groups that users belong to should be available outside this
+ method.
+ """
+ roles = self.get_permission_groups(username)
+ db = self.env.get_db_cnx()
+ cursor = db.cursor()
+ cursor.execute("""SELECT action FROM permission
+ WHERE username = %s
+ OR
+ username IN %s""",
+ (username, tuple(roles)))
+ rows = cursor.fetchall()
+
+ return [row[0] for row in rows]
+
class DefaultPermissionGroupProvider(Component):
"""Provides the basic builtin permission groups 'anonymous' and
'authenticated'."""
@@ -272,13 +332,37 @@
if username and username != 'anonymous':
groups.append('authenticated')
return groups
+
+class RelationalPermissionGroupProvider(Component):
+ """Provides the basic builtin permission groups 'anonymous' and
+ 'authenticated'."""
+ implements(IPermissionGroupProvider)
+ def get_permission_groups(self, username):
+ db = self.env.get_db_cnx()
+ cursor = db.cursor()
+
+ roles = []
+ cursor.execute("""SELECT role FROM user_role
+ WHERE username = %s""", username)
+ role = cursor.fetchone()
+ while role:
+ roles.append(role[0])
+ cursor.execute("""SELECT parent FROM role_role
+ WHERE role = %s""", role[0])
+ role = cursor.fetchone()
+ if not roles:
+ roles = ['anonymous']
+ return roles
+
+
class PermissionCache:
"""Cache that maintains the permissions of a single user."""
def __init__(self, env, username):
self.perms = PermissionSystem(env).get_user_permissions(username)
+ self.roles = PermissionSystem(env).get_permission_groups(username)
def has_permission(self, action):
return self.perms.has_key(action)
@@ -289,3 +373,6 @@
def permissions(self):
return self.perms.keys()
+
+ def roles(self):
+ return self.roles