Index: trac/ticket/api.py
===================================================================
--- trac/ticket/api.py	(revision 7609)
+++ trac/ticket/api.py	(working copy)
@@ -16,14 +16,18 @@
 
 import re
 from datetime import datetime
+try:
+    import threading
+except ImportError:
+    import dummy_threading as threading
 
 from genshi.builder import tag
 
 from trac.config import *
 from trac.core import *
-from trac.perm import IPermissionRequestor, PermissionSystem, PermissionError
+from trac.perm import IPermissionRequestor, PermissionCache, PermissionSystem
 from trac.resource import IResourceManager
-from trac.util import Ranges
+from trac.util import Ranges, arity
 from trac.util.compat import set, sorted
 from trac.util.datefmt import utc
 from trac.util.text import shorten_line, obfuscate_email_address
@@ -157,6 +161,7 @@
     def __init__(self):
         self.log.debug('action controllers for ticket workflow: %r' % 
                 [c.__class__.__name__ for c in self.action_controllers])
+        self._fields_lock = threading.RLock()
 
     # Public API
 
@@ -185,6 +190,26 @@
 
     def get_ticket_fields(self):
         """Returns the list of fields available for tickets."""
+        # This is now cached - as it makes quite a number of things faster,
+        # e.g. #6436
+        if self._fields is None:
+            self._fields_lock.acquire()
+            try:
+                self._fields = self._get_ticket_fields()
+            finally:
+                self._fields_lock.release()
+        return [f.copy() for f in self._fields]
+
+    def reset_ticket_fields(self):
+        self._fields_lock.acquire()
+        try:
+            self._fields = None
+            self.config.touch() # brute force approach for now
+        finally:
+            self._fields_lock.release()
+
+    _fields = None
+    def _get_ticket_fields(self):
         from trac.ticket import model
 
         db = self.env.get_db_cnx()
@@ -195,16 +220,10 @@
             field = {'name': name, 'type': 'text', 'label': name.title()}
             fields.append(field)
 
-        # Owner field, can be text or drop-down depending on configuration
+        # Owner field, by default text but can be changed dynamically 
+        # into a drop-down depending on configuration (restrict_owner=true)
         field = {'name': 'owner', 'label': 'Owner'}
-        if self.restrict_owner:
-            field['type'] = 'select'
-            perm = PermissionSystem(self.env)
-            field['options'] = perm.get_users_with_permission('TICKET_MODIFY')
-            field['options'].sort()
-            field['optional'] = True
-        else:
-            field['type'] = 'text'
+        field['type'] = 'text'
         fields.append(field)
 
         # Description
@@ -256,6 +275,16 @@
         return fields
 
     def get_custom_fields(self):
+        if self._custom_fields is None:
+            self._fields_lock.acquire()
+            try:
+                self._custom_fields = self._get_custom_fields()
+            finally:
+                self._fields_lock.release()
+        return [f.copy() for f in self._custom_fields]
+
+    _custom_fields = None
+    def _get_custom_fields(self):
         fields = []
         config = self.config['ticket-custom']
         for name in [option for option, value in config.options()
@@ -280,6 +309,23 @@
         fields.sort(lambda x, y: cmp(x['order'], y['order']))
         return fields
 
+    def eventually_restrict_owner(self, field, ticket=None):
+        """Restrict given owner field to be a list of users having
+        the TICKET_MODIFY permission (for the given ticket)
+        """
+        if self.restrict_owner:
+            field['type'] = 'select'
+            possible_owners = []
+            for user in PermissionSystem(self.env) \
+                    .get_users_with_permission('TICKET_MODIFY'):
+                if not ticket or \
+                        'TICKET_MODIFY' in PermissionCache(self.env, user,
+                                                           ticket.resource):
+                    possible_owners.append(user)
+            possible_owners.sort()
+            field['options'] = possible_owners
+            field['optional'] = True
+
     # IPermissionRequestor methods
 
     def get_permission_actions(self):
Index: trac/ticket/web_ui.py
===================================================================
--- trac/ticket/web_ui.py	(revision 7610)
+++ trac/ticket/web_ui.py	(working copy)
@@ -1067,7 +1067,7 @@
     def _prepare_fields(self, req, ticket):
         context = Context.from_request(req, ticket.resource)
         fields = []
-        ownerField = None
+        owner_field = None
         for field in ticket.fields:
             name = field['name']
             type_ = field['type']
@@ -1077,12 +1077,14 @@
                         'resolution'):
                 field['skip'] = True
             elif name == 'owner':
+                TicketSystem(self.env).eventually_restrict_owner(field, ticket)
+                type_ = field['type']
                 field['skip'] = True
                 if not ticket.exists:
                     field['label'] = 'Assign to'
                     if 'TICKET_MODIFY' in req.perm(ticket.resource):
                         field['skip'] = False
-                        ownerField = field
+                        owner_field = field
             elif name == 'milestone':
                 milestones = [(opt, Milestone(self.env, opt))
                               for opt in field['options']]
@@ -1147,9 +1149,9 @@
             fields.append(field)
         
         # Move owner field to end when shown
-        if ownerField is not None:
-            fields.remove(ownerField)
-            fields.append(ownerField)
+        if owner_field is not None:
+            fields.remove(owner_field)
+            fields.append(owner_field)
         return fields
         
     def _insert_ticket_data(self, req, ticket, data, author_id, field_changes):
Index: trac/ticket/model.py
===================================================================
--- trac/ticket/model.py	(revision 7609)
+++ trac/ticket/model.py	(working copy)
@@ -408,6 +408,7 @@
             db.commit()
         self.value = self._old_value = None
         self.name = self._old_name = None
+        TicketSystem(self.env).reset_ticket_fields()
 
     def insert(self, db=None):
         assert not self.exists, 'Cannot insert existing %s' % self.type
@@ -433,6 +434,7 @@
             db.commit()
         self._old_name = self.name
         self._old_value = self.value
+        TicketSystem(self.env).reset_ticket_fields()
 
     def update(self, db=None):
         assert self.exists, 'Cannot update non-existent %s' % self.type
@@ -459,6 +461,7 @@
             db.commit()
         self._old_name = self.name
         self._old_value = self.value
+        TicketSystem(self.env).reset_ticket_fields()
 
     def select(cls, env, db=None):
         if not db:
@@ -545,6 +548,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def insert(self, db=None):
         assert not self.exists, 'Cannot insert existing component'
@@ -564,6 +568,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def update(self, db=None):
         assert self.exists, 'Cannot update non-existent component'
@@ -589,6 +594,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def select(cls, env, db=None):
         if not db:
@@ -669,6 +675,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def insert(self, db=None):
         assert self.name, 'Cannot create milestone with no name'
@@ -688,6 +695,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def update(self, db=None):
         assert self.name, 'Cannot update milestone with no name'
@@ -713,6 +721,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def select(cls, env, include_completed=True, db=None):
         if not db:
@@ -775,6 +784,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def insert(self, db=None):
         assert not self.exists, 'Cannot insert existing version'
@@ -794,6 +804,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def update(self, db=None):
         assert self.exists, 'Cannot update non-existent version'
@@ -819,6 +830,7 @@
 
         if handle_ta:
             db.commit()
+        TicketSystem(self.env).reset_ticket_fields()
 
     def select(cls, env, db=None):
         if not db:
Index: trac/ticket/query.py
===================================================================
--- trac/ticket/query.py	(revision 7609)
+++ trac/ticket/query.py	(working copy)
@@ -885,6 +885,9 @@
             orig_time = query_time
 
         context = Context.from_request(req, 'query')
+        owner_field = [f for f in query.fields if f['name'] == 'owner']
+        if owner_field:
+            TicketSystem(self.env).eventually_restrict_owner(owner_field[0])
         data = query.template_data(context, tickets, orig_list, orig_time, req)
 
         # For clients without JavaScript, we add a new constraint here if

