diff --git a/trunk/tracrpc/api.py b/trunk/tracrpc/api.py
--- a/trunk/tracrpc/api.py
+++ b/trunk/tracrpc/api.py
@@ -81,7 +81,8 @@
         self.namespace_description = inspect.getdoc(provider)
 
     def __call__(self, req, args):
-        req.perm.assert_permission(self.permission)
+        if self.permission:
+            req.perm.assert_permission(self.permission)
         result = self.callable(req, *args)
         # If result is null, return a zero
         if result is None:
@@ -180,7 +181,7 @@
             for candidate in provider.xmlrpc_methods():
                 # Expand all fields of method description
                 c = Method(provider, *candidate)
-                if req.perm.has_permission(c.permission):
+                if not c.permission or req.perm.has_permission(c.permission):
                     yield c
 
     def multicall(self, req, signatures):
@@ -209,7 +210,8 @@
         use of that method. If no such string is available, an empty string is
         returned. The documentation string may contain HTML markup. """
         p = self.get_method(method)
-        req.perm.assert_permission(p.permission)
+        if p.permission:
+            req.perm.assert_permission(p.permission)
         return '\n'.join((p.signature, '', p.description))
 
     def methodSignature(self, req, method):
@@ -220,7 +222,8 @@
         is an array of types. The first of these types is the return type of
         the method, the rest are parameters. """
         p = self.get_method(method)
-        req.perm.assert_permission(p.permission)
+        if p.permission:
+            req.perm.assert_permission(p.permission)
         return [','.join([RPC_TYPES[x] for x in sig]) for sig in p.xmlrpc_signatures()]
 
     def getAPIVersion(self, req):
diff --git a/trunk/tracrpc/templates/xmlrpclist.html b/trunk/tracrpc/templates/xmlrpclist.html
--- a/trunk/tracrpc/templates/xmlrpclist.html
+++ b/trunk/tracrpc/templates/xmlrpclist.html
@@ -35,7 +35,7 @@
                 <tr class="${'color3-' + (idx % 2 == 0 and 'even' or 'odd')}">
                   <td><nobr>${function[0]}</nobr></td>
                   <td>${function[1]}</td>
-                  <td>${function[2]}</td>
+                  <td>${function[2] or "By resource"}</td>
                 </tr>
               </tbody>
             </table>
diff --git a/trunk/tracrpc/ticket.py b/trunk/tracrpc/ticket.py
--- a/trunk/tracrpc/ticket.py
+++ b/trunk/tracrpc/ticket.py
@@ -1,6 +1,7 @@
 from trac.attachment import Attachment
 from trac.core import *
-from trac.perm import PermissionCache
+from trac.perm import PermissionError
+from trac.resource import Resource
 import trac.ticket.model as model
 import trac.ticket.query as query
 from trac.ticket.api import TicketSystem
@@ -26,31 +27,34 @@
         return 'ticket'
 
     def xmlrpc_methods(self):
-        yield ('TICKET_VIEW', ((list,), (list, str)), self.query)
-        yield ('TICKET_VIEW', ((list, xmlrpclib.DateTime),), self.getRecentChanges)
-        yield ('TICKET_VIEW', ((list, int),), self.getAvailableActions)
-        yield ('TICKET_VIEW', ((list, int),), self.getActions)
-        yield ('TICKET_VIEW', ((list, int),), self.get)
+        yield (None, ((list,), (list, str)), self.query)
+        yield (None, ((list, xmlrpclib.DateTime),), self.getRecentChanges)
+        yield (None, ((list, int),), self.getAvailableActions)
+        yield (None, ((list, int),), self.getActions)
+        yield (None, ((list, int),), self.get)
         yield ('TICKET_CREATE', ((int, str, str), (int, str, str, dict), (int, str, str, dict, bool)), self.create)
-        yield ('TICKET_VIEW', ((list, int, str), (list, int, str, dict), (list, int, str, dict, bool)), self.update)
-        yield ('TICKET_ADMIN', ((None, int),), self.delete)
-        yield ('TICKET_VIEW', ((dict, int), (dict, int, int)), self.changeLog)
-        yield ('TICKET_VIEW', ((list, int),), self.listAttachments)
-        yield ('TICKET_VIEW', ((xmlrpclib.Binary, int, str),), self.getAttachment)
-        yield ('TICKET_APPEND',
+        yield (None, ((list, int, str), (list, int, str, dict), (list, int, str, dict, bool)), self.update)
+        yield (None, ((None, int),), self.delete)
+        yield (None, ((dict, int), (dict, int, int)), self.changeLog)
+        yield (None, ((list, int),), self.listAttachments)
+        yield (None, ((xmlrpclib.Binary, int, str),), self.getAttachment)
+        yield (None,
                ((str, int, str, str, xmlrpclib.Binary, bool),
                 (str, int, str, str, xmlrpclib.Binary)),
                self.putAttachment)
-        yield ('TICKET_ADMIN', ((bool, int, str),), self.deleteAttachment)
+        yield (None, ((bool, int, str),), self.deleteAttachment)
         yield ('TICKET_VIEW', ((list,),), self.getTicketFields)
 
     # Exported methods
     def query(self, req, qstr='status!=closed'):
         """ Perform a ticket query, returning a list of ticket ID's. """
         q = query.Query.from_string(self.env, qstr)
+        ticket_realm = Resource('ticket')
         out = []
         for t in q.execute(req):
-            out.append(t['id'])
+            tid = t['id']
+            if 'TICKET_VIEW' in req.perm(ticket_realm(id=tid)):
+                out.append(tid)
         return out
 
     def getRecentChanges(self, req, since):
@@ -61,8 +65,11 @@
         cursor.execute('SELECT id FROM ticket'
                        ' WHERE changetime >= %s', (since,))
         result = []
+        ticket_realm = Resource('ticket')
         for row in cursor:
-            result.append(int(row[0]))
+            tid = int(row[0])
+            if 'TICKET_VIEW' in req.perm(ticket_realm(id=tid)):
+                result.append(tid)
         return result
 
     def getAvailableActions(self, req, id):
@@ -76,6 +83,9 @@
         a list of `[name, value, [options]]` for any required action inputs."""
         ts = TicketSystem(self.env)
         t = model.Ticket(self.env, id)
+        if not ('TICKET_MODIFY' in req.perm(t.resource) \
+                    or 'TICKET_CHGPROP' in req.perm(t.resource)):
+            raise PermissionError("TICKET_APPEND or TICKET_CHGPROP")
         actions = []
         for action in ts.get_available_actions(req, t):
             fragment = hints = genshi.builder.Fragment()
@@ -108,13 +118,12 @@
                     controls.append((elem.attrib.get('name'),
                                     value, options))
             actions.append((action, first_label, ". ".join(hints) + '.', controls))
-        self.log.debug('Rpc ticket.getActions for ticket %d, user %s: %s' % (
-                    id, req.authname, repr(actions)))
         return actions
 
     def get(self, req, id):
         """ Fetch a ticket. Returns [id, time_created, time_changed, attributes]. """
         t = model.Ticket(self.env, id)
+        req.perm(t.resource).require('TICKET_VIEW')
         return (t.id, t.time_created, t.time_changed, t.values)
 
     def create(self, req, summary, description, attributes = {}, notify=False):
@@ -150,6 +159,7 @@
             # FIXME: Old, non-restricted update - remove soon!
             self.log.warning("Rpc ticket.update for ticket %d by user %s " \
                     "has no workflow 'action'." % (id, req.authname))
+            req.perm(t.resource).require('TICKET_MODIFY')
             for k, v in attributes.iteritems():
                 t[k] = v
             t.save_changes(req.authname, comment, when=now)
@@ -200,6 +210,7 @@
     def delete(self, req, id):
         """ Delete ticket with the given id. """
         t = model.Ticket(self.env, id)
+        req.perm(t.resource).require('TICKET_ADMIN')
         t.delete()
         ts = TicketSystem(self.env)
         # Call ticket change listeners
@@ -208,6 +219,7 @@
 
     def changeLog(self, req, id, when=0):
         t = model.Ticket(self.env, id)
+        req.perm(t.resource).require('TICKET_VIEW')
         for date, author, field, old, new, permanent in t.get_changelog(when):
             yield (date, author, field, old, new, permanent)
     # Use existing documentation from Ticket model
@@ -216,27 +228,31 @@
     def listAttachments(self, req, ticket):
         """ Lists attachments for a given ticket. Returns (filename,
         description, size, time, author) for each attachment."""
-        for t in Attachment.select(self.env, 'ticket', ticket):
-            yield (t.filename, t.description, t.size, 
-                   t.date, t.author)
+        attachments = []
+        for a in Attachment.select(self.env, 'ticket', ticket):
+            if 'ATTACHMENT_VIEW' in req.perm(a.resource):
+                yield (a.filename, a.description, a.size, a.date, a.author)
 
     def getAttachment(self, req, ticket, filename):
         """ returns the content of an attachment. """
         attachment = Attachment(self.env, 'ticket', ticket, filename)
+        req.perm(attachment.resource).require('ATTACHMENT_VIEW')
         return xmlrpclib.Binary(attachment.open().read())
 
     def putAttachment(self, req, ticket, filename, description, data, replace=True):
         """ Add an attachment, optionally (and defaulting to) overwriting an
         existing one. Returns filename."""
         if not model.Ticket(self.env, ticket).exists:
-            raise TracError, 'Ticket "%s" does not exist' % ticket
+            raise TracError('Ticket "%s" does not exist' % ticket)
         if replace:
             try:
                 attachment = Attachment(self.env, 'ticket', ticket, filename)
+                req.perm(attachment.resource).require('ATTACHMENT_DELETE')
                 attachment.delete()
             except TracError:
                 pass
         attachment = Attachment(self.env, 'ticket', ticket)
+        req.perm(attachment.resource).require('ATTACHMENT_CREATE')
         attachment.author = req.authname
         attachment.description = description
         attachment.insert(filename, StringIO(data.data), len(data.data))
@@ -247,6 +263,7 @@
         if not model.Ticket(self.env, ticket).exists:
             raise TracError('Ticket "%s" does not exists' % ticket)
         attachment = Attachment(self.env, 'ticket', ticket, filename)
+        req.perm(attachment.resource).require('ATTACHMENT_DELETE')
         attachment.delete()
         return True
 
diff --git a/trunk/tracrpc/wiki.py b/trunk/tracrpc/wiki.py
--- a/trunk/tracrpc/wiki.py
+++ b/trunk/tracrpc/wiki.py
@@ -6,7 +6,7 @@
 import posixpath
 
 from trac.core import *
-from trac.perm import IPermissionRequestor
+from trac.resource import Resource
 from trac.util.datefmt import to_timestamp, to_datetime, utc
 from trac.wiki.api import WikiSystem
 from trac.wiki.model import WikiPage
@@ -28,24 +28,24 @@
         return 'wiki'
 
     def xmlrpc_methods(self):
-        yield ('WIKI_VIEW', ((dict, xmlrpclib.DateTime),), self.getRecentChanges)
+        yield (None, ((dict, xmlrpclib.DateTime),), self.getRecentChanges)
         yield ('WIKI_VIEW', ((int,),), self.getRPCVersionSupported)
-        yield ('WIKI_VIEW', ((str, str), (str, str, int),), self.getPage)
-        yield ('WIKI_VIEW', ((str, str, int),), self.getPage, 'getPageVersion')
-        yield ('WIKI_VIEW', ((str, str), (str, str, int)), self.getPageHTML)
-        yield ('WIKI_VIEW', ((str, str), (str, str, int)), self.getPageHTML, 'getPageHTMLVersion')
-        yield ('WIKI_VIEW', ((list,),), self.getAllPages)
-        yield ('WIKI_VIEW', ((dict, str), (dict, str, int)), self.getPageInfo)
-        yield ('WIKI_VIEW', ((dict, str, int),), self.getPageInfo, 'getPageInfoVersion')
-        yield ('WIKI_CREATE', ((bool, str, str, dict),), self.putPage)
-        yield ('WIKI_VIEW', ((list, str),), self.listAttachments)
-        yield ('WIKI_VIEW', ((xmlrpclib.Binary, str),), self.getAttachment)
-        yield ('WIKI_MODIFY', ((bool, str, xmlrpclib.Binary),), self.putAttachment)
-        yield ('WIKI_MODIFY', ((bool, str, str, str, xmlrpclib.Binary),
+        yield (None, ((str, str), (str, str, int),), self.getPage)
+        yield (None, ((str, str, int),), self.getPage, 'getPageVersion')
+        yield (None, ((str, str), (str, str, int)), self.getPageHTML)
+        yield (None, ((str, str), (str, str, int)), self.getPageHTML, 'getPageHTMLVersion')
+        yield (None, ((list,),), self.getAllPages)
+        yield (None, ((dict, str), (dict, str, int)), self.getPageInfo)
+        yield (None, ((dict, str, int),), self.getPageInfo, 'getPageInfoVersion')
+        yield (None, ((bool, str, str, dict),), self.putPage)
+        yield (None, ((list, str),), self.listAttachments)
+        yield (None, ((xmlrpclib.Binary, str),), self.getAttachment)
+        yield (None, ((bool, str, xmlrpclib.Binary),), self.putAttachment)
+        yield (None, ((bool, str, str, str, xmlrpclib.Binary),
                                (bool, str, str, str, xmlrpclib.Binary, bool)),
                                self.putAttachmentEx)
-        yield ('WIKI_DELETE', ((bool, str),(bool, str, int)), self.deletePage)
-        yield ('WIKI_DELETE', ((bool, str),), self.deleteAttachment)
+        yield (None, ((bool, str),(bool, str, int)), self.deletePage)
+        yield (None, ((bool, str),), self.deleteAttachment)
         yield ('WIKI_VIEW', ((list, str),), self.listLinks)
         yield ('WIKI_VIEW', ((str, str),), self.wikiToHtml)
 
@@ -56,13 +56,16 @@
     def getRecentChanges(self, req, since):
         """ Get list of changed pages since timestamp """
         since = to_timestamp(since)
+        wiki_realm = Resource('wiki')
         db = self.env.get_db_cnx()
         cursor = db.cursor()
         cursor.execute('SELECT name, max(time), author, version, comment FROM wiki'
                        ' WHERE time >= %s GROUP BY name ORDER BY max(time) DESC', (since,))
         result = []
         for name, when, author, version, comment in cursor:
-            result.append(self._page_info(name, when, author, version, comment))
+            if 'WIKI_VIEW' in req.perm(wiki_realm(id=name, version=version)):
+                result.append(
+                    self._page_info(name, when, author, version, comment))
         return result
 
     def getRPCVersionSupported(self, req):
@@ -72,6 +75,7 @@
     def getPage(self, req, pagename, version=None):
         """ Get the raw Wiki text of page, latest version. """
         page = WikiPage(self.env, pagename, version)
+        req.perm(page.resource).require('WIKI_VIEW')
         if page.exists:
             return page.text
         else:
@@ -88,11 +92,16 @@
 
     def getAllPages(self, req):
         """ Returns a list of all pages. The result is an array of utf8 pagenames. """
-        return list(self.wiki.get_pages())
+        pages = []
+        for page in self.wiki.get_pages():
+            if 'WIKI_VIEW' in req.perm(Resource('wiki', page)):
+                pages.append(page)
+        return pages
 
     def getPageInfo(self, req, pagename, version=None):
         """ Returns information about the given page. """
         page = WikiPage(self.env, pagename, version)
+        req.perm(page.resource).require('WIKI_VIEW')
         if page.exists:
             last_update = page.get_history().next()
             return self._page_info(page.name, last_update[1],
@@ -102,27 +111,27 @@
         """ writes the content of the page. """
         page = WikiPage(self.env, pagename)
         if page.readonly:
-            req.perm.assert_permission('WIKI_ADMIN')
+            req.perm(page.resource).require('WIKI_ADMIN')
         elif not page.exists:
-            req.perm.assert_permission('WIKI_CREATE')
+            req.perm(page.resource).require('WIKI_CREATE')
         else:
-            req.perm.assert_permission('WIKI_MODIFY')
+            req.perm(page.resource).require('WIKI_MODIFY')
 
         page.text = content
-        if req.perm.has_permission('WIKI_ADMIN'):
+        if req.perm(page.resource).has_permission('WIKI_ADMIN'):
             page.readonly = attributes.get('readonly') and 1 or 0
 
         page.save(attributes.get('author', req.authname),
-                  attributes.get('comment'),
-                  req.remote_addr)
+                  attributes.get('comment'), req.remote_addr)
         return True
 
     def deletePage(self, req, name, version=None):
         """Delete a Wiki page (all versions) or a specific version by
         including an optional version number. Attachments will also be
         deleted if page no longer exists. Returns True for success."""
+        wp = WikiPage(self.env, name, version)
+        req.perm(wp.resource).require('WIKI_DELETE')
         try:
-            wp = WikiPage(self.env, name, version)
             wp.delete(version)
             return True
         except:
@@ -130,12 +139,15 @@
 
     def listAttachments(self, req, pagename):
         """ Lists attachments on a given page. """
-        return [pagename + '/' + a.filename for a in Attachment.select(self.env, 'wiki', pagename)]
+        for a in Attachment.select(self.env, 'wiki', pagename):
+            if 'ATTACHMENT_VIEW' in req.perm(a.resource):
+                yield pagename + '/' + a.filename
 
     def getAttachment(self, req, path):
         """ returns the content of an attachment. """
         pagename, filename = posixpath.split(path)
         attachment = Attachment(self.env, 'wiki', pagename, filename)
+        req.perm(attachment.resource).require('ATTACHMENT_VIEW')
         return xmlrpclib.Binary(attachment.open().read())
 
     def putAttachment(self, req, path, data):
@@ -157,10 +169,12 @@
         if replace:
             try:
                 attachment = Attachment(self.env, 'wiki', pagename, filename)
+                req.perm(attachment.resource).require('ATTACHMENT_DELETE')
                 attachment.delete()
             except TracError:
                 pass
         attachment = Attachment(self.env, 'wiki', pagename)
+        req.perm(attachment.resource).require('ATTACHMENT_CREATE')
         attachment.author = req.authname
         attachment.description = description
         attachment.insert(filename, StringIO(data.data), len(data.data))
@@ -172,6 +186,7 @@
         if not WikiPage(self.env, pagename).exists:
             raise TracError, 'Wiki page "%s" does not exist' % pagename
         attachment = Attachment(self.env, 'wiki', pagename, filename)
+        req.perm(attachment.resource).require('ATTACHMENT_DELETE')
         attachment.delete()
         return True
 
