You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by jo...@apache.org on 2012/12/07 17:11:48 UTC

[14/21] git commit: [#5240] ticket:213 security functions optimization

[#5240] ticket:213 security functions optimization

 * query ProjectRoles using pymongo instead of Ming ODM inside Credentials and RoleCache
 * fix stuff that are using Credentials and RoleCache
 * new logic for cache invalidation after project creation


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/be14410c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/be14410c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/be14410c

Branch: refs/heads/cj/4691
Commit: be14410ceaeb1b84bd3d015f7d4ebb71193b088f
Parents: 1cb7c1d
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Nov 22 15:02:45 2012 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Wed Dec 5 22:36:20 2012 +0000

----------------------------------------------------------------------
 Allura/allura/lib/app_globals.py                   |    2 +-
 Allura/allura/lib/plugin.py                        |    2 +-
 Allura/allura/lib/security.py                      |   79 ++++++++-------
 Allura/allura/model/project.py                     |   12 ++-
 Allura/allura/tests/model/test_auth.py             |    7 +-
 .../forgetracker/tests/unit/test_ticket_model.py   |    1 +
 6 files changed, 59 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be14410c/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index a263177..ac46d90 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -228,7 +228,7 @@ class Globals(object):
                 cred = Credentials.get()
                 if cred is not None:
                     for pr in cred.user_roles(user._id, project._id).reaching_roles:
-                        if pr.name and pr.name[0] != '*':
+                        if pr.get('name') and pr.get('name')[0] != '*':
                             context['is_project_member'] = True
         if app:
             context.update(

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be14410c/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index f18ee99..6067e27 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -529,7 +529,7 @@ class ProjectRegistrationProvider(object):
 
         # clear the RoleCache for the user so this project will
         # be picked up by user.my_projects()
-        g.credentials.clear_user(user._id)
+        g.credentials.clear_user(user._id, '*')
         with h.push_config(c, project=p, user=user):
             ThreadLocalORMSession.flush_all()
             # have to add user to context, since this may occur inside auth code

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be14410c/Allura/allura/lib/security.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 0d48fbf..a7cc8bf 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -21,6 +21,12 @@ class Credentials(object):
     def __init__(self):
         self.clear()
 
+    @property
+    def project_role(self):
+        from allura import model as M
+        db = M.session.main_doc_session.db
+        return db[M.ProjectRole.__mongometa__.name]
+
     @classmethod
     def get(cls):
         'get the global Credentials instance'
@@ -33,43 +39,47 @@ class Credentials(object):
         self.projects = {}
 
     def clear_user(self, user_id, project_id=None):
-        self.users.pop((user_id, project_id), None)
+        if project_id == '*':
+            to_remove = [(uid, pid) for uid, pid in self.users if uid == user_id]
+        else:
+            to_remove = [(user_id, project_id)]
+        for uid, pid in to_remove:
+            self.projects.pop(pid, None)
+            self.users.pop((uid, pid), None)
 
     def load_user_roles(self, user_id, *project_ids):
         '''Load the credentials with all user roles for a set of projects'''
-        from allura import model as M
         # Don't reload roles
         project_ids = [ pid for pid in project_ids if self.users.get((user_id, pid)) is None ]
-        if not project_ids: return 
+        if not project_ids: return
         if user_id is None:
-            q = M.ProjectRole.query.find(
-                dict(
-                    project_id={'$in': project_ids},
-                    name='*anonymous'))
+            q = self.project_role.find({
+                'project_id': {'$in': project_ids},
+                'name': '*anonymous'})
         else:
-            q0 = M.ProjectRole.query.find(
-                dict(project_id={'$in': list(project_ids)},
-                     name={'$in':['*anonymous', '*authenticated']}))
-            q1 = M.ProjectRole.query.find(
-                        dict(project_id={'$in': list(project_ids)},user_id=user_id))
+            q0 = self.project_role.find({
+                'project_id': {'$in': project_ids},
+                'name': {'$in': ['*anonymous', '*authenticated']}})
+            q1 = self.project_role.find({
+                'project_id': {'$in': project_ids},
+                'user_id': user_id})
             q = chain(q0, q1)
         roles_by_project = dict((pid, []) for pid in project_ids)
         for role in q:
-            roles_by_project[role.project_id].append(role)
+            roles_by_project[role['project_id']].append(role)
         for pid, roles in roles_by_project.iteritems():
             self.users[user_id, pid] = RoleCache(self, roles)
 
     def load_project_roles(self, *project_ids):
         '''Load the credentials with all user roles for a set of projects'''
-        from allura import model as M
         # Don't reload roles
         project_ids = [ pid for pid in project_ids if self.projects.get(pid) is None ]
-        if not project_ids: return 
-        q = M.ProjectRole.query.find(dict(
-                project_id={'$in': project_ids}))
+        if not project_ids: return
+        q = self.project_role.find({
+            'project_id': {'$in': project_ids}})
         roles_by_project = dict((pid, []) for pid in project_ids)
         for role in q:
-            roles_by_project[role.project_id].append(role)
+            roles_by_project[role['project_id']].append(role)
         for pid, roles in roles_by_project.iteritems():
             self.projects[pid] = RoleCache(self, roles)
 
@@ -87,14 +97,13 @@ class Credentials(object):
         '''
         :returns: a RoleCache of ProjectRoles for given user_id and project_id, *anonymous and *authenticated checked as appropriate
         '''
-        from allura import model as M
         roles = self.users.get((user_id, project_id))
         if roles is None:
             if project_id is None:
                 if user_id is None:
                     q = []
                 else:
-                    q = M.ProjectRole.query.find(dict(user_id=user_id))
+                    q = self.project_role.find({'user_id': user_id})
                 roles = RoleCache(self, q)
             else:
                 self.load_user_roles(user_id, project_id)
@@ -126,7 +135,7 @@ class RoleCache(object):
         def _iter():
             for r in self:
                 for k,v in tests:
-                    val = getattr(r, k)
+                    val = r.get(k)
                     if callable(v):
                         if not v(val): break
                     elif v != val: break
@@ -146,19 +155,19 @@ class RoleCache(object):
 
     @LazyProperty
     def index(self):
-        return dict((r._id, r) for r in self.q)
+        return dict((r['_id'], r) for r in self.q)
 
     @LazyProperty
     def named(self):
         return RoleCache(self.cred, (
             r for r in self
-            if r.name and not r.name.startswith('*')))
+            if r.get('name') and not r.get('name').startswith('*')))
 
     @LazyProperty
     def reverse_index(self):
         rev_index = defaultdict(list)
         for r in self:
-            for rr_id in r.roles:
+            for rr_id in r['roles']:
                 rev_index[rr_id].append(r)
         return rev_index
 
@@ -169,22 +178,22 @@ class RoleCache(object):
             to_visit = list(self)
             while to_visit:
                 r = to_visit.pop(0)
-                if r in visited: continue
-                visited.add(r)
+                if r['_id'] in visited: continue
+                visited.add(r['_id'])
                 yield r
-                pr_rindex = self.cred.project_roles(r.project_id).reverse_index
-                to_visit += pr_rindex[r._id]
+                pr_rindex = self.cred.project_roles(r['project_id']).reverse_index
+                to_visit += pr_rindex[r['_id']]
         return RoleCache(self.cred, _iter())
 
     @LazyProperty
     def users_that_reach(self):
-        return [
-            r.user for r in self.roles_that_reach if r.user ]
+        from allura import model as M
+        uids = [uid for uid in self.userids_that_reach if uid]
+        return M.User.query.find({'_id': {'$in': uids}})
 
     @LazyProperty
     def userids_that_reach(self):
-        return [
-            r.user_id for r in self.roles_that_reach ]
+        return [ r['user_id'] for r in self.roles_that_reach ]
 
     @LazyProperty
     def reaching_roles(self):
@@ -195,16 +204,16 @@ class RoleCache(object):
                 (rid, role) = to_visit.pop()
                 if rid in visited: continue
                 yield role
-                pr_index = self.cred.project_roles(role.project_id).index
+                pr_index = self.cred.project_roles(role['project_id']).index
                 if rid in pr_index:
-                    for i in pr_index[rid].roles:
+                    for i in pr_index[rid]['roles']:
                         if i in pr_index:
                             to_visit.append((i, pr_index[i]))
         return RoleCache(self.cred, _iter())
 
     @LazyProperty
     def reaching_ids(self):
-        return [ r._id for r in self.reaching_roles ]
+        return [ r['_id'] for r in self.reaching_roles ]
 
     @LazyProperty
     def reaching_ids_set(self):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be14410c/Allura/allura/model/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index 7861243..da0f816 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -20,7 +20,7 @@ from allura.lib.security import has_access
 from .session import main_orm_session
 from .session import project_orm_session, project_doc_session
 from .neighborhood import Neighborhood
-from .auth import ProjectRole
+from .auth import ProjectRole, User
 from .timeline import ActivityNode, ActivityObject
 from .types import ACL, ACE
 
@@ -458,9 +458,10 @@ class Project(MappedClass, ActivityNode, ActivityObject):
 
     @property
     def named_roles(self):
+        roles_ids = [r['_id'] for r in g.credentials.project_roles(self.root_project._id).named]
         roles = sorted(
-            g.credentials.project_roles(self.root_project._id).named,
-            key=lambda r:r.name.lower())
+            ProjectRole.query.find({'_id': {'$in': roles_ids}}),
+            key=lambda r: r.name.lower())
         return roles
 
     def install_app(self, ep_name, mount_point=None, mount_label=None, ordinal=None, **override_options):
@@ -598,7 +599,8 @@ class Project(MappedClass, ActivityNode, ActivityObject):
         named_roles = security.RoleCache(
             g.credentials,
             g.credentials.project_roles(project_id=self.root_project._id).named)
-        return [ r.user for r in named_roles.roles_that_reach if r.user_id is not None ]
+        uids = [uid for uid in named_roles.userids_that_reach if uid is not None]
+        return list(User.query.find({'_id': {'$in': uids}}))
 
     def users_with_role(self, *role_names):
         """Return all users in this project that have at least one of the roles
@@ -624,7 +626,7 @@ class Project(MappedClass, ActivityNode, ActivityObject):
             return None
         named_roles = g.credentials.project_roles(project_id=self.root_project._id).named
         for r in named_roles.roles_that_reach:
-            if r.user_id == u._id: return u
+            if r.get('user_id') == u._id: return u
         return None
 
     def configure_project(

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be14410c/Allura/allura/tests/model/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index e32349c..46bdf97 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -97,8 +97,11 @@ def test_project_role():
     role = M.ProjectRole(project_id=c.project._id, name='test_role')
     c.user.project_role().roles.append(role._id)
     ThreadLocalORMSession.flush_all()
-    for pr in g.credentials.user_roles(
-        c.user._id, project_id=c.project.root_project._id):
+    roles = g.credentials.user_roles(
+        c.user._id, project_id=c.project.root_project._id)
+    roles_ids = [role['_id'] for role in roles]
+    roles = M.ProjectRole.query.find({'_id': {'$in': roles_ids}})
+    for pr in roles:
         assert pr.display()
         pr.special
         assert pr.user in (c.user, None, M.User.anonymous())

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be14410c/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
index a66ef9a..0bc9e5d 100644
--- a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
+++ b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
@@ -75,6 +75,7 @@ class TestTicketModel(TrackerTestWithModel):
         role_developer = ProjectRole.by_name('Developer')._id
         role_creator = t.reported_by.project_role()._id
         developer.project_role().roles.append(role_developer)
+        ThreadLocalORMSession.flush_all()
         cred = Credentials.get().clear()
 
         t.private = True