changeset 2065:9ab21c5ddb84 rhodecode-0.0.1.3.2

merge with beta
author Marcin Kuzminski <marcin@python-works.com>
date Tue, 28 Feb 2012 20:21:35 +0200
parents 8fbb1d250804 (current diff) c9adf2a4929a (diff)
children 36feadd70c10
files CONTRIBUTORS docs/changelog.rst docs/theme/nature/layout.html rhodecode/lib/__init__.py rhodecode/lib/middleware/https_fixup.py rhodecode/lib/middleware/simplegit.py rhodecode/lib/utils.py rhodecode/model/db.py rhodecode/model/forms.py
diffstat 15 files changed, 204 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/CONTRIBUTORS	Mon Feb 27 05:14:08 2012 +0200
+++ b/CONTRIBUTORS	Tue Feb 28 20:21:35 2012 +0200
@@ -15,4 +15,5 @@
     Les Peabody <lpeabody@gmail.com>
     Jonas Oberschweiber <jonas.oberschweiber@d-velop.de>
     Matt Zuba <matt.zuba@goodwillaz.org>
-    Aras Pranckevicius <aras@unity3d.com>
\ No newline at end of file
+    Aras Pranckevicius <aras@unity3d.com>
+    Tony Bussieres <t.bussieres@gmail.com>
--- a/docs/changelog.rst	Mon Feb 27 05:14:08 2012 +0200
+++ b/docs/changelog.rst	Tue Feb 28 20:21:35 2012 +0200
@@ -4,6 +4,25 @@
 =========
 
 
+1.3.2 (**2012-02-28**)
+----------------------
+
+news
+++++
+
+
+fixes
++++++
+
+- fixed git protocol issues with repos-groups
+- fixed git remote repos validator that prevented from cloning remote git repos
+- fixes #370 ending slashes fixes for repo and groups
+- fixes #368 improved git-protocol detection to handle other clients
+- fixes #366 When Setting Repository Group To Blank Repo Group Wont Be 
+  Moved To Root
+- fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys 
+- fixed #373 missing cascade drop on user_group_to_perm table
+
 1.3.1 (**2012-02-27**)
 ----------------------
 
--- a/docs/theme/nature/layout.html	Mon Feb 27 05:14:08 2012 +0200
+++ b/docs/theme/nature/layout.html	Tue Feb 28 20:21:35 2012 +0200
@@ -13,6 +13,6 @@
     <div style="padding:5px">
      <a href="http://flattr.com/thing/167489/RhodeCode" target="_blank">
      <img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
-   </div>  
+   </div>
 </div>
 {% endblock %}}
--- a/rhodecode/controllers/admin/repos_groups.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/controllers/admin/repos_groups.py	Tue Feb 28 20:21:35 2012 +0200
@@ -263,6 +263,11 @@
             raise HTTPInternalServerError()
 
     def show_by_name(self, group_name):
+        """
+        This is a proxy that does a lookup group_name -> id, and shows
+        the group by id view instead
+        """
+        group_name = group_name.rstrip('/')
         id_ = RepoGroup.get_by_group_name(group_name).group_id
         return self.show(id_)
 
--- a/rhodecode/controllers/admin/users_groups.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/controllers/admin/users_groups.py	Tue Feb 28 20:21:35 2012 +0200
@@ -160,11 +160,12 @@
 
         try:
             UsersGroupModel().delete(id)
+            Session.commit()
             h.flash(_('successfully deleted users group'), category='success')
-            Session.commit()
         except UsersGroupsAssignedException, e:
             h.flash(e, category='error')
         except Exception:
+            log.error(traceback.format_exc())
             h.flash(_('An error occurred during deletion of users group'),
                     category='error')
         return redirect(url('users_groups'))
--- a/rhodecode/lib/__init__.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/lib/__init__.py	Tue Feb 28 20:21:35 2012 +0200
@@ -231,7 +231,7 @@
     :rtype: str
     :returns: str object
     """
-    
+
     # if it's not basestr cast to str
     if not isinstance(unicode_, basestring):
         return str(unicode_)
--- a/rhodecode/lib/caching_query.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/lib/caching_query.py	Tue Feb 28 20:21:35 2012 +0200
@@ -24,6 +24,7 @@
 from sqlalchemy.orm.interfaces import MapperOption
 from sqlalchemy.orm.query import Query
 from sqlalchemy.sql import visitors
+from rhodecode.lib import safe_str
 
 
 class CachingQuery(Query):
@@ -137,9 +138,10 @@
 
     if cache_key is None:
         # cache key - the value arguments from this query's parameters.
-        args = [str(x) for x in _params_from_query(query)]
-        args.extend(filter(lambda k:k not in ['None', None, u'None'],
+        args = [safe_str(x) for x in _params_from_query(query)]
+        args.extend(filter(lambda k: k not in ['None', None, u'None'],
                            [str(query._limit), str(query._offset)]))
+
         cache_key = " ".join(args)
 
     if cache_key is None:
--- a/rhodecode/lib/middleware/https_fixup.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/lib/middleware/https_fixup.py	Tue Feb 28 20:21:35 2012 +0200
@@ -42,13 +42,21 @@
         middleware you should set this header inside your
         proxy ie. nginx, apache etc.
         """
-        proto = environ.get('HTTP_X_URL_SCHEME')
 
         if str2bool(self.config.get('force_https')):
             proto = 'https'
-
+        else:
+            if 'HTTP_X_URL_SCHEME' in environ:
+                proto = environ.get('HTTP_X_URL_SCHEME')
+            elif 'HTTP_X_FORWARDED_SCHEME' in environ:
+                proto = environ.get('HTTP_X_FORWARDED_SCHEME')
+            elif 'HTTP_X_FORWARDED_PROTO' in environ:
+                proto = environ.get('HTTP_X_FORWARDED_PROTO')
+            else:
+                proto = 'http'
         if proto == 'https':
             environ['wsgi.url_scheme'] = proto
         else:
             environ['wsgi.url_scheme'] = 'http'
+
         return None
--- a/rhodecode/lib/middleware/simplegit.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/lib/middleware/simplegit.py	Tue Feb 28 20:21:35 2012 +0200
@@ -25,6 +25,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
+import re
 import logging
 import traceback
 
@@ -79,21 +80,20 @@
 log = logging.getLogger(__name__)
 
 
-def is_git(environ):
-    """Returns True if request's target is git server.
-    ``HTTP_USER_AGENT`` would then have git client version given.
+GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
+
 
-    :param environ:
-    """
-    http_user_agent = environ.get('HTTP_USER_AGENT')
-    if http_user_agent and http_user_agent.startswith('git'):
-        return True
-    return False
+def is_git(environ):
+    path_info = environ['PATH_INFO']
+    isgit_path = GIT_PROTO_PAT.match(path_info)
+    log.debug('is a git path %s pathinfo : %s' % (isgit_path, path_info))
+    return isgit_path
 
 
 class SimpleGit(BaseVCSController):
 
     def _handle_request(self, environ, start_response):
+
         if not is_git(environ):
             return self.application(environ, start_response)
 
@@ -218,13 +218,11 @@
         """
         try:
             environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
-            repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
-            if repo_name.endswith('/'):
-                repo_name = repo_name.rstrip('/')
+            repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
         except:
             log.error(traceback.format_exc())
             raise
-        repo_name = repo_name.split('/')[0]
+
         return repo_name
 
     def __get_user(self, username):
@@ -238,9 +236,10 @@
         service = environ['QUERY_STRING'].split('=')
         if len(service) > 1:
             service_cmd = service[1]
-            mapping = {'git-receive-pack': 'push',
-                       'git-upload-pack': 'pull',
-                       }
+            mapping = {
+                'git-receive-pack': 'push',
+                'git-upload-pack': 'pull',
+            }
 
             return mapping.get(service_cmd,
                                service_cmd if service_cmd else 'other')
--- a/rhodecode/lib/utils.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/lib/utils.py	Tue Feb 28 20:21:35 2012 +0200
@@ -92,11 +92,17 @@
 
 
 def get_repo_slug(request):
-    return request.environ['pylons.routes_dict'].get('repo_name')
+    _repo = request.environ['pylons.routes_dict'].get('repo_name')
+    if _repo:
+        _repo = _repo.rstrip('/')
+    return _repo
 
 
 def get_repos_group_slug(request):
-    return request.environ['pylons.routes_dict'].get('group_name')
+    _group = request.environ['pylons.routes_dict'].get('group_name')
+    if _group:
+        _group = _group.rstrip('/')
+    return _group
 
 
 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
--- a/rhodecode/model/db.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/model/db.py	Tue Feb 28 20:21:35 2012 +0200
@@ -44,6 +44,7 @@
 from rhodecode.lib.caching_query import FromCache
 
 from rhodecode.model.meta import Base, Session
+import hashlib
 
 
 log = logging.getLogger(__name__)
@@ -52,6 +53,8 @@
 # BASE CLASSES
 #==============================================================================
 
+_hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
+
 
 class ModelSerializer(json.JSONEncoder):
     """
@@ -337,8 +340,11 @@
             q = cls.query().filter(cls.username == username)
 
         if cache:
-            q = q.options(FromCache("sql_cache_short",
-                                    "get_user_%s" % username))
+            q = q.options(FromCache(
+                            "sql_cache_short",
+                            "get_user_%s" % _hash_key(username)
+                          )
+            )
         return q.scalar()
 
     @classmethod
@@ -394,7 +400,7 @@
         return datetime.date(*self.action_date.timetuple()[:3])
 
     user = relationship('User')
-    repository = relationship('Repository',cascade='')
+    repository = relationship('Repository', cascade='')
 
 
 class UsersGroup(Base, BaseModel):
@@ -406,6 +412,7 @@
     users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
 
     members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
+    users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
 
     def __repr__(self):
         return '<userGroup(%s)>' % (self.users_group_name)
@@ -418,8 +425,11 @@
         else:
             q = cls.query().filter(cls.users_group_name == group_name)
         if cache:
-            q = q.options(FromCache("sql_cache_short",
-                                    "get_user_%s" % group_name))
+            q = q.options(FromCache(
+                            "sql_cache_short",
+                            "get_user_%s" % _hash_key(group_name)
+                          )
+            )
         return q.scalar()
 
     @classmethod
@@ -748,8 +758,11 @@
             gr = cls.query()\
                 .filter(cls.group_name == group_name)
         if cache:
-            gr = gr.options(FromCache("sql_cache_short",
-                                          "get_group_%s" % group_name))
+            gr = gr.options(FromCache(
+                            "sql_cache_short",
+                            "get_group_%s" % _hash_key(group_name)
+                            )
+            )
         return gr.scalar()
 
     @property
@@ -1038,7 +1051,7 @@
         prefix = ''
         iid = rhodecode.CONFIG.get('instance_id')
         if iid:
-            prefix = iid 
+            prefix = iid
         return "%s%s" % (prefix, key)
 
     @classmethod
--- a/rhodecode/model/forms.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/model/forms.py	Tue Feb 28 20:21:35 2012 +0200
@@ -345,32 +345,46 @@
 
 
 def ValidCloneUri():
-    from mercurial.httprepo import httprepository, httpsrepository
     from rhodecode.lib.utils import make_ui
 
+    def url_handler(repo_type, url, proto, ui=None):
+        if repo_type == 'hg':
+            from mercurial.httprepo import httprepository, httpsrepository
+            if proto == 'https':
+                httpsrepository(make_ui('db'), url).capabilities
+            elif proto == 'http':
+                httprepository(make_ui('db'), url).capabilities
+        elif repo_type == 'git':
+            #TODO: write a git url validator
+            pass
+
     class _ValidCloneUri(formencode.validators.FancyValidator):
 
         def to_python(self, value, state):
-            if not value:
+
+            repo_type = value.get('repo_type')
+            url = value.get('clone_uri')
+            e_dict = {'clone_uri': _('invalid clone url')}
+
+            if not url:
                 pass
-            elif value.startswith('https'):
+            elif url.startswith('https'):
                 try:
-                    httpsrepository(make_ui('db'), value).capabilities
+                    url_handler(repo_type, url, 'https', make_ui('db'))
                 except Exception:
                     log.error(traceback.format_exc())
-                    raise formencode.Invalid(_('invalid clone url'), value,
-                                             state)
-            elif value.startswith('http'):
+                    raise formencode.Invalid('', value, state, error_dict=e_dict)
+            elif url.startswith('http'):
                 try:
-                    httprepository(make_ui('db'), value).capabilities
+                    url_handler(repo_type, url, 'http', make_ui('db'))
                 except Exception:
                     log.error(traceback.format_exc())
-                    raise formencode.Invalid(_('invalid clone url'), value,
-                                             state)
+                    raise formencode.Invalid('', value, state, error_dict=e_dict)
             else:
-                raise formencode.Invalid(_('Invalid clone url, provide a '
-                                           'valid clone http\s url'), value,
-                                         state)
+                e_dict = {'clone_uri': _('Invalid clone url, provide a '
+                                         'valid clone http\s url')}
+                raise formencode.Invalid('', value, state, error_dict=e_dict)
+
             return value
 
     return _ValidCloneUri
@@ -645,8 +659,7 @@
         filter_extra_fields = False
         repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
                         SlugifyName())
-        clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
-                        ValidCloneUri()())
+        clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
         repo_group = OneOf(repo_groups, hideList=True)
         repo_type = OneOf(supported_backends)
         description = UnicodeString(strip=True, min=1, not_empty=True)
@@ -658,7 +671,9 @@
             #this is repo owner
             user = All(UnicodeString(not_empty=True), ValidRepoUser)
 
-        chained_validators = [ValidRepoName(edit, old_data), ValidPerms()]
+        chained_validators = [ValidCloneUri()(),
+                              ValidRepoName(edit, old_data),
+                              ValidPerms()]
     return _RepoForm
 
 
--- a/rhodecode/model/repos_group.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/model/repos_group.py	Tue Feb 28 20:21:35 2012 +0200
@@ -187,20 +187,20 @@
             # change properties
             repos_group.group_description = form_data['group_description']
             repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
+            repos_group.group_parent_id = form_data['group_parent_id']
             repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
-
             new_path = repos_group.full_path
 
             self.sa.add(repos_group)
 
-            self.__rename_group(old_path, new_path)
-
             # we need to get all repositories from this new group and
             # rename them accordingly to new group path
             for r in repos_group.repositories:
                 r.repo_name = r.get_new_name(r.just_name)
                 self.sa.add(r)
 
+            self.__rename_group(old_path, new_path)
+
             return repos_group
         except:
             log.error(traceback.format_exc())
--- a/rhodecode/tests/functional/test_admin_users_groups.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/tests/functional/test_admin_users_groups.py	Tue Feb 28 20:21:35 2012 +0200
@@ -1,8 +1,9 @@
 from rhodecode.tests import *
-from rhodecode.model.db import UsersGroup
+from rhodecode.model.db import UsersGroup, UsersGroupToPerm, Permission
 
 TEST_USERS_GROUP = 'admins_test'
 
+
 class TestAdminUsersGroupsController(TestController):
 
     def test_index(self):
@@ -16,7 +17,7 @@
         self.log_user()
         users_group_name = TEST_USERS_GROUP
         response = self.app.post(url('users_groups'),
-                                 {'users_group_name':users_group_name,
+                                 {'users_group_name': users_group_name,
                                   'active':True})
         response.follow()
 
@@ -47,7 +48,6 @@
         self.checkSessionFlash(response,
                                'created users group %s' % users_group_name)
 
-
         gr = self.Session.query(UsersGroup)\
                            .filter(UsersGroup.users_group_name ==
                                    users_group_name).one()
@@ -60,6 +60,53 @@
 
         self.assertEqual(gr, None)
 
+    def test_enable_repository_read_on_group(self):
+        self.log_user()
+        users_group_name = TEST_USERS_GROUP + 'another2'
+        response = self.app.post(url('users_groups'),
+                                 {'users_group_name': users_group_name,
+                                  'active':True})
+        response.follow()
+
+        ug = UsersGroup.get_by_group_name(users_group_name)
+        self.checkSessionFlash(response,
+                               'created users group %s' % users_group_name)
+
+        response = self.app.put(url('users_group_perm', id=ug.users_group_id),
+                                 {'create_repo_perm': True})
+
+        response.follow()
+        ug = UsersGroup.get_by_group_name(users_group_name)
+        p = Permission.get_by_key('hg.create.repository')
+        # check if user has this perm
+        perms = UsersGroupToPerm.query()\
+            .filter(UsersGroupToPerm.users_group == ug).all()
+        perms = [[x.__dict__['users_group_id'],
+                  x.__dict__['permission_id'],] for x in perms]
+        self.assertEqual(
+            perms,
+            [[ug.users_group_id, p.permission_id]]
+        )
+
+        # DELETE !
+        ug = UsersGroup.get_by_group_name(users_group_name)
+        ugid = ug.users_group_id
+        response = self.app.delete(url('users_group', id=ug.users_group_id))
+        response = response.follow()
+        gr = self.Session.query(UsersGroup)\
+                           .filter(UsersGroup.users_group_name ==
+                                   users_group_name).scalar()
+
+        self.assertEqual(gr, None)
+        p = Permission.get_by_key('hg.create.repository')
+        perms = UsersGroupToPerm.query()\
+            .filter(UsersGroupToPerm.users_group_id == ugid).all()
+        perms = [[x.__dict__['users_group_id'],
+                  x.__dict__['permission_id'],] for x in perms]
+        self.assertEqual(
+            perms,
+            []
+        )
 
     def test_delete_browser_fakeout(self):
         response = self.app.post(url('users_group', id=1),
--- a/rhodecode/tests/test_models.py	Mon Feb 27 05:14:08 2012 +0200
+++ b/rhodecode/tests/test_models.py	Tue Feb 28 20:21:35 2012 +0200
@@ -23,7 +23,6 @@
         return gr
 
     gr = ReposGroupModel().create(path, desc, parent_id)
-    Session.commit()
     return gr
 
 
@@ -31,13 +30,19 @@
 
     def setUp(self):
         self.g1 = _make_group('test1', skip_if_exists=True)
+        Session.commit()
         self.g2 = _make_group('test2', skip_if_exists=True)
+        Session.commit()
         self.g3 = _make_group('test3', skip_if_exists=True)
+        Session.commit()
 
     def tearDown(self):
         print 'out'
 
     def __check_path(self, *path):
+        """
+        Checks the path for existance !
+        """
         path = [TESTS_TMP_PATH] + list(path)
         path = os.path.join(*path)
         return os.path.isdir(path)
@@ -49,12 +54,13 @@
         ReposGroupModel().delete(id_)
 
     def __update_group(self, id_, path, desc='desc', parent_id=None):
-        form_data = dict(group_name=path,
-                         group_description=desc,
-                         group_parent_id=parent_id,
-                         perms_updates=[],
-                         perms_new=[])
-
+        form_data = dict(
+            group_name=path,
+            group_description=desc,
+            group_parent_id=parent_id,
+            perms_updates=[],
+            perms_new=[]
+        )
         gr = ReposGroupModel().update(id_, form_data)
         return gr
 
@@ -150,6 +156,25 @@
         self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
 
 
+    def test_move_to_root(self):
+        g1 = _make_group('t11')
+        Session.commit()
+        g2 = _make_group('t22',parent_id=g1.group_id)
+        Session.commit()
+
+        self.assertEqual(g2.full_path,'t11/t22')
+        self.assertTrue(self.__check_path('t11', 't22'))
+
+        g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
+        Session.commit()
+
+        self.assertEqual(g2.group_name,'g22')
+        # we moved out group from t1 to '' so it's full path should be 'g2'
+        self.assertEqual(g2.full_path,'g22')
+        self.assertFalse(self.__check_path('t11', 't22'))
+        self.assertTrue(self.__check_path('g22'))
+
+
 class TestUser(unittest.TestCase):
     def __init__(self, methodName='runTest'):
         Session.remove()