--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -132,11 +132,11 @@
                 SitemapEntry('Screenshots', admin_url+'screenshots'),
                 SitemapEntry('Categorization', admin_url+'trove')
                 ]
-        if has_access(c.project, 'admin')():
-            links.append(SitemapEntry('Permissions', admin_url+'permissions/'))
         links.append(SitemapEntry('Tools', admin_url+'tools'))
         if c.project.is_root and has_access(c.project, 'admin')():
-            links.append(SitemapEntry('Usergroups', admin_url+'groups/'))
+            links.append(SitemapEntry('User Permissions', admin_url+'groups/'))
+        if not c.project.is_root and has_access(c.project, 'admin')():
+            links.append(SitemapEntry('Permissions', admin_url+'permissions/'))
         if len(c.project.neighborhood_invitations):
             links.append(SitemapEntry('Invitation(s)', admin_url+'invitations'))
         links.append(SitemapEntry('Audit Trail', admin_url+ 'audit/'))
@@ -599,14 +599,123 @@
     def _check_security(self):
         require_access(c.project, 'admin')
 
+    def _index_permissions(self):
+        permissions = dict(
+            (p,[]) for p in c.project.permissions)
+        for ace in c.project.acl:
+            if ace.access == M.ACE.ALLOW:
+                permissions[ace.permission].append(ace.role_id)
+        return permissions
+
+    def _map_group_permissions(self):
+        roles = c.project.named_roles
+        permissions=self._index_permissions()
+        permissions_by_role = dict()
+        auth_role = M.ProjectRole.authenticated()
+        anon_role = M.ProjectRole.anonymous()
+        for role in roles+[auth_role, anon_role]:
+            permissions_by_role[str(role._id)] = []
+            for perm in permissions:
+                perm_info = dict(has="no", text="Does not have permission %s" % perm, name=perm)
+                role_ids = permissions[perm]
+                for r in role.child_roles():
+                    if r._id in role_ids:
+                        perm_info['text'] = "Inherited permission %s from %s" % (perm, r.name)
+                        perm_info['has'] = "inherit"
+                        break
+                if perm_info['has'] == "no":
+                    if role._id in role_ids:
+                        perm_info['text'] = "Has permission %s" % perm
+                        perm_info['has'] = "yes"
+                    elif anon_role._id in role_ids:
+                        perm_info['text'] = "Inherited permission %s from Anonymous" % perm
+                        perm_info['has'] = "inherit"
+                    elif auth_role._id in role_ids and role != anon_role:
+                        perm_info['text'] = "Inherited permission %s from Authenticated" % perm
+                        perm_info['has'] = "inherit"
+                permissions_by_role[str(role._id)].append(perm_info)
+        return permissions_by_role
+
+    @without_trailing_slash
+    @expose()
+    @h.vardec
+    def delete_group(self, group_name, **kw):
+        role = M.ProjectRole.by_name(group_name)
+        if not role:
+            flash('Group "%s" does not exist.' % group_name, 'error')
+        else:
+            role.delete()
+            M.AuditLog.log('delete group %s', group_name)
+            flash('Group "%s" deleted successfully.' % group_name)
+            g.post_event('project_updated')
+        redirect('.')
+
     @with_trailing_slash
     @expose('jinja:allura.ext.admin:templates/project_groups.html')
     def index(self, **kw):
         c.admin_modal = W.admin_modal
         c.card = W.group_card
+        permissions_by_role = self._map_group_permissions()
+        auth_role = M.ProjectRole.authenticated()
+        anon_role = M.ProjectRole.anonymous()
         roles = c.project.named_roles
         roles.append(None)
-        return dict(roles=roles)
+        return dict(roles=roles, permissions_by_role=permissions_by_role,
+                    auth_role=auth_role, anon_role=anon_role)
+
+    @without_trailing_slash
+    @expose('json:')
+    @require_post()
+    @h.vardec
+    def change_perm(self, role_id, permission, allow="true", **kw):
+        if allow=="true":
+            M.AuditLog.log('granted permission %s to group with id %s', permission, role_id)
+            c.project.acl.append(M.ACE.allow(ObjectId(role_id), permission))
+        else:
+            admin_group_id = str(M.ProjectRole.by_name('Admin')._id)
+            if admin_group_id == role_id and permission == 'admin':
+                return dict(error='You cannot remove the admin permission from the admin group.')
+            M.AuditLog.log('revoked permission %s from group with id %s', permission, role_id)
+            c.project.acl.remove(M.ACE.allow(ObjectId(role_id), permission))
+        return self._map_group_permissions()
+
+    @without_trailing_slash
+    @expose('json:')
+    @require_post()
+    @h.vardec
+    def add_user(self, role_id, username, **kw):
+        if not username or username=='*anonymous':
+            return dict(error='You must choose a user to add.')
+        group = M.ProjectRole.query.get(_id=ObjectId(role_id))
+        user = M.User.by_username(username.strip())
+        if not group:
+            return dict(error='Could not find group with id %s' % role_id)
+        if not user:
+            return dict(error='User %s not found' % username)
+        if group._id in user.project_role().roles:
+            return dict(error='%s (%s) is already in the group %s.' % (user.display_name, username, group.name))
+        M.AuditLog.log('add user %s to %s', username, group.name)
+        user.project_role().roles.append(group._id)
+        return dict(username=username, displayname=user.display_name)
+
+    @without_trailing_slash
+    @expose('json:')
+    @require_post()
+    @h.vardec
+    def remove_user(self, role_id, username, **kw):
+        group = M.ProjectRole.query.get(_id=ObjectId(role_id))
+        user = M.User.by_username(username.strip())
+        if group.name == 'Admin' and len(group.users_with_role()) == 1:
+            return dict(error='You must have at least one user with the Admin role.')
+        if not group:
+            return dict(error='Could not find group with id %s' % role_id)
+        if not user:
+            return dict(error='User %s not found' % username)
+        if group._id not in user.project_role().roles:
+            return dict(error='%s (%s) is not in the group %s.' % (user.display_name, username, group.name))
+        M.AuditLog.log('remove user %s from %s', username, group.name)
+        user.project_role().roles.remove(group._id)
+        return dict()
 
     @without_trailing_slash
     @expose()