##// END OF EJS Templates
merge with beta
marcink -
r2065:9ab21c5d merge rhodecode-0.0.1.3.2 default
parent child Browse files
Show More
@@ -15,4 +15,5 b' List of contributors to RhodeCode projec'
15 15 Les Peabody <lpeabody@gmail.com>
16 16 Jonas Oberschweiber <jonas.oberschweiber@d-velop.de>
17 17 Matt Zuba <matt.zuba@goodwillaz.org>
18 Aras Pranckevicius <aras@unity3d.com> No newline at end of file
18 Aras Pranckevicius <aras@unity3d.com>
19 Tony Bussieres <t.bussieres@gmail.com>
@@ -4,6 +4,25 b' Changelog'
4 4 =========
5 5
6 6
7 1.3.2 (**2012-02-28**)
8 ----------------------
9
10 news
11 ++++
12
13
14 fixes
15 +++++
16
17 - fixed git protocol issues with repos-groups
18 - fixed git remote repos validator that prevented from cloning remote git repos
19 - fixes #370 ending slashes fixes for repo and groups
20 - fixes #368 improved git-protocol detection to handle other clients
21 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
22 Moved To Root
23 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
24 - fixed #373 missing cascade drop on user_group_to_perm table
25
7 26 1.3.1 (**2012-02-27**)
8 27 ----------------------
9 28
@@ -13,6 +13,6 b''
13 13 <div style="padding:5px">
14 14 <a href="http://flattr.com/thing/167489/RhodeCode" target="_blank">
15 15 <img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
16 </div>
16 </div>
17 17 </div>
18 18 {% endblock %}}
@@ -263,6 +263,11 b' class ReposGroupsController(BaseControll'
263 263 raise HTTPInternalServerError()
264 264
265 265 def show_by_name(self, group_name):
266 """
267 This is a proxy that does a lookup group_name -> id, and shows
268 the group by id view instead
269 """
270 group_name = group_name.rstrip('/')
266 271 id_ = RepoGroup.get_by_group_name(group_name).group_id
267 272 return self.show(id_)
268 273
@@ -160,11 +160,12 b' class UsersGroupsController(BaseControll'
160 160
161 161 try:
162 162 UsersGroupModel().delete(id)
163 Session.commit()
163 164 h.flash(_('successfully deleted users group'), category='success')
164 Session.commit()
165 165 except UsersGroupsAssignedException, e:
166 166 h.flash(e, category='error')
167 167 except Exception:
168 log.error(traceback.format_exc())
168 169 h.flash(_('An error occurred during deletion of users group'),
169 170 category='error')
170 171 return redirect(url('users_groups'))
@@ -231,7 +231,7 b' def safe_str(unicode_, to_encoding=None)'
231 231 :rtype: str
232 232 :returns: str object
233 233 """
234
234
235 235 # if it's not basestr cast to str
236 236 if not isinstance(unicode_, basestring):
237 237 return str(unicode_)
@@ -24,6 +24,7 b' from beaker.exceptions import BeakerExce'
24 24 from sqlalchemy.orm.interfaces import MapperOption
25 25 from sqlalchemy.orm.query import Query
26 26 from sqlalchemy.sql import visitors
27 from rhodecode.lib import safe_str
27 28
28 29
29 30 class CachingQuery(Query):
@@ -137,9 +138,10 b' def _get_cache_parameters(query):'
137 138
138 139 if cache_key is None:
139 140 # cache key - the value arguments from this query's parameters.
140 args = [str(x) for x in _params_from_query(query)]
141 args.extend(filter(lambda k:k not in ['None', None, u'None'],
141 args = [safe_str(x) for x in _params_from_query(query)]
142 args.extend(filter(lambda k: k not in ['None', None, u'None'],
142 143 [str(query._limit), str(query._offset)]))
144
143 145 cache_key = " ".join(args)
144 146
145 147 if cache_key is None:
@@ -42,13 +42,21 b' class HttpsFixup(object):'
42 42 middleware you should set this header inside your
43 43 proxy ie. nginx, apache etc.
44 44 """
45 proto = environ.get('HTTP_X_URL_SCHEME')
46 45
47 46 if str2bool(self.config.get('force_https')):
48 47 proto = 'https'
49
48 else:
49 if 'HTTP_X_URL_SCHEME' in environ:
50 proto = environ.get('HTTP_X_URL_SCHEME')
51 elif 'HTTP_X_FORWARDED_SCHEME' in environ:
52 proto = environ.get('HTTP_X_FORWARDED_SCHEME')
53 elif 'HTTP_X_FORWARDED_PROTO' in environ:
54 proto = environ.get('HTTP_X_FORWARDED_PROTO')
55 else:
56 proto = 'http'
50 57 if proto == 'https':
51 58 environ['wsgi.url_scheme'] = proto
52 59 else:
53 60 environ['wsgi.url_scheme'] = 'http'
61
54 62 return None
@@ -25,6 +25,7 b''
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 import re
28 29 import logging
29 30 import traceback
30 31
@@ -79,21 +80,20 b' from webob.exc import HTTPNotFound, HTTP'
79 80 log = logging.getLogger(__name__)
80 81
81 82
82 def is_git(environ):
83 """Returns True if request's target is git server.
84 ``HTTP_USER_AGENT`` would then have git client version given.
83 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
84
85 85
86 :param environ:
87 """
88 http_user_agent = environ.get('HTTP_USER_AGENT')
89 if http_user_agent and http_user_agent.startswith('git'):
90 return True
91 return False
86 def is_git(environ):
87 path_info = environ['PATH_INFO']
88 isgit_path = GIT_PROTO_PAT.match(path_info)
89 log.debug('is a git path %s pathinfo : %s' % (isgit_path, path_info))
90 return isgit_path
92 91
93 92
94 93 class SimpleGit(BaseVCSController):
95 94
96 95 def _handle_request(self, environ, start_response):
96
97 97 if not is_git(environ):
98 98 return self.application(environ, start_response)
99 99
@@ -218,13 +218,11 b' class SimpleGit(BaseVCSController):'
218 218 """
219 219 try:
220 220 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
221 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
222 if repo_name.endswith('/'):
223 repo_name = repo_name.rstrip('/')
221 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
224 222 except:
225 223 log.error(traceback.format_exc())
226 224 raise
227 repo_name = repo_name.split('/')[0]
225
228 226 return repo_name
229 227
230 228 def __get_user(self, username):
@@ -238,9 +236,10 b' class SimpleGit(BaseVCSController):'
238 236 service = environ['QUERY_STRING'].split('=')
239 237 if len(service) > 1:
240 238 service_cmd = service[1]
241 mapping = {'git-receive-pack': 'push',
242 'git-upload-pack': 'pull',
243 }
239 mapping = {
240 'git-receive-pack': 'push',
241 'git-upload-pack': 'pull',
242 }
244 243
245 244 return mapping.get(service_cmd,
246 245 service_cmd if service_cmd else 'other')
@@ -92,11 +92,17 b' def repo_name_slug(value):'
92 92
93 93
94 94 def get_repo_slug(request):
95 return request.environ['pylons.routes_dict'].get('repo_name')
95 _repo = request.environ['pylons.routes_dict'].get('repo_name')
96 if _repo:
97 _repo = _repo.rstrip('/')
98 return _repo
96 99
97 100
98 101 def get_repos_group_slug(request):
99 return request.environ['pylons.routes_dict'].get('group_name')
102 _group = request.environ['pylons.routes_dict'].get('group_name')
103 if _group:
104 _group = _group.rstrip('/')
105 return _group
100 106
101 107
102 108 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
@@ -44,6 +44,7 b' from rhodecode.lib.compat import json'
44 44 from rhodecode.lib.caching_query import FromCache
45 45
46 46 from rhodecode.model.meta import Base, Session
47 import hashlib
47 48
48 49
49 50 log = logging.getLogger(__name__)
@@ -52,6 +53,8 b' log = logging.getLogger(__name__)'
52 53 # BASE CLASSES
53 54 #==============================================================================
54 55
56 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
57
55 58
56 59 class ModelSerializer(json.JSONEncoder):
57 60 """
@@ -337,8 +340,11 b' class User(Base, BaseModel):'
337 340 q = cls.query().filter(cls.username == username)
338 341
339 342 if cache:
340 q = q.options(FromCache("sql_cache_short",
341 "get_user_%s" % username))
343 q = q.options(FromCache(
344 "sql_cache_short",
345 "get_user_%s" % _hash_key(username)
346 )
347 )
342 348 return q.scalar()
343 349
344 350 @classmethod
@@ -394,7 +400,7 b' class UserLog(Base, BaseModel):'
394 400 return datetime.date(*self.action_date.timetuple()[:3])
395 401
396 402 user = relationship('User')
397 repository = relationship('Repository',cascade='')
403 repository = relationship('Repository', cascade='')
398 404
399 405
400 406 class UsersGroup(Base, BaseModel):
@@ -406,6 +412,7 b' class UsersGroup(Base, BaseModel):'
406 412 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
407 413
408 414 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
415 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
409 416
410 417 def __repr__(self):
411 418 return '<userGroup(%s)>' % (self.users_group_name)
@@ -418,8 +425,11 b' class UsersGroup(Base, BaseModel):'
418 425 else:
419 426 q = cls.query().filter(cls.users_group_name == group_name)
420 427 if cache:
421 q = q.options(FromCache("sql_cache_short",
422 "get_user_%s" % group_name))
428 q = q.options(FromCache(
429 "sql_cache_short",
430 "get_user_%s" % _hash_key(group_name)
431 )
432 )
423 433 return q.scalar()
424 434
425 435 @classmethod
@@ -748,8 +758,11 b' class RepoGroup(Base, BaseModel):'
748 758 gr = cls.query()\
749 759 .filter(cls.group_name == group_name)
750 760 if cache:
751 gr = gr.options(FromCache("sql_cache_short",
752 "get_group_%s" % group_name))
761 gr = gr.options(FromCache(
762 "sql_cache_short",
763 "get_group_%s" % _hash_key(group_name)
764 )
765 )
753 766 return gr.scalar()
754 767
755 768 @property
@@ -1038,7 +1051,7 b' class CacheInvalidation(Base, BaseModel)'
1038 1051 prefix = ''
1039 1052 iid = rhodecode.CONFIG.get('instance_id')
1040 1053 if iid:
1041 prefix = iid
1054 prefix = iid
1042 1055 return "%s%s" % (prefix, key)
1043 1056
1044 1057 @classmethod
@@ -345,32 +345,46 b' def SlugifyName():'
345 345
346 346
347 347 def ValidCloneUri():
348 from mercurial.httprepo import httprepository, httpsrepository
349 348 from rhodecode.lib.utils import make_ui
350 349
350 def url_handler(repo_type, url, proto, ui=None):
351 if repo_type == 'hg':
352 from mercurial.httprepo import httprepository, httpsrepository
353 if proto == 'https':
354 httpsrepository(make_ui('db'), url).capabilities
355 elif proto == 'http':
356 httprepository(make_ui('db'), url).capabilities
357 elif repo_type == 'git':
358 #TODO: write a git url validator
359 pass
360
351 361 class _ValidCloneUri(formencode.validators.FancyValidator):
352 362
353 363 def to_python(self, value, state):
354 if not value:
364
365 repo_type = value.get('repo_type')
366 url = value.get('clone_uri')
367 e_dict = {'clone_uri': _('invalid clone url')}
368
369 if not url:
355 370 pass
356 elif value.startswith('https'):
371 elif url.startswith('https'):
357 372 try:
358 httpsrepository(make_ui('db'), value).capabilities
373 url_handler(repo_type, url, 'https', make_ui('db'))
359 374 except Exception:
360 375 log.error(traceback.format_exc())
361 raise formencode.Invalid(_('invalid clone url'), value,
362 state)
363 elif value.startswith('http'):
376 raise formencode.Invalid('', value, state, error_dict=e_dict)
377 elif url.startswith('http'):
364 378 try:
365 httprepository(make_ui('db'), value).capabilities
379 url_handler(repo_type, url, 'http', make_ui('db'))
366 380 except Exception:
367 381 log.error(traceback.format_exc())
368 raise formencode.Invalid(_('invalid clone url'), value,
369 state)
382 raise formencode.Invalid('', value, state, error_dict=e_dict)
370 383 else:
371 raise formencode.Invalid(_('Invalid clone url, provide a '
372 'valid clone http\s url'), value,
373 state)
384 e_dict = {'clone_uri': _('Invalid clone url, provide a '
385 'valid clone http\s url')}
386 raise formencode.Invalid('', value, state, error_dict=e_dict)
387
374 388 return value
375 389
376 390 return _ValidCloneUri
@@ -645,8 +659,7 b' def RepoForm(edit=False, old_data={}, su'
645 659 filter_extra_fields = False
646 660 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
647 661 SlugifyName())
648 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
649 ValidCloneUri()())
662 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
650 663 repo_group = OneOf(repo_groups, hideList=True)
651 664 repo_type = OneOf(supported_backends)
652 665 description = UnicodeString(strip=True, min=1, not_empty=True)
@@ -658,7 +671,9 b' def RepoForm(edit=False, old_data={}, su'
658 671 #this is repo owner
659 672 user = All(UnicodeString(not_empty=True), ValidRepoUser)
660 673
661 chained_validators = [ValidRepoName(edit, old_data), ValidPerms()]
674 chained_validators = [ValidCloneUri()(),
675 ValidRepoName(edit, old_data),
676 ValidPerms()]
662 677 return _RepoForm
663 678
664 679
@@ -187,20 +187,20 b' class ReposGroupModel(BaseModel):'
187 187 # change properties
188 188 repos_group.group_description = form_data['group_description']
189 189 repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
190 repos_group.group_parent_id = form_data['group_parent_id']
190 191 repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
191
192 192 new_path = repos_group.full_path
193 193
194 194 self.sa.add(repos_group)
195 195
196 self.__rename_group(old_path, new_path)
197
198 196 # we need to get all repositories from this new group and
199 197 # rename them accordingly to new group path
200 198 for r in repos_group.repositories:
201 199 r.repo_name = r.get_new_name(r.just_name)
202 200 self.sa.add(r)
203 201
202 self.__rename_group(old_path, new_path)
203
204 204 return repos_group
205 205 except:
206 206 log.error(traceback.format_exc())
@@ -1,8 +1,9 b''
1 1 from rhodecode.tests import *
2 from rhodecode.model.db import UsersGroup
2 from rhodecode.model.db import UsersGroup, UsersGroupToPerm, Permission
3 3
4 4 TEST_USERS_GROUP = 'admins_test'
5 5
6
6 7 class TestAdminUsersGroupsController(TestController):
7 8
8 9 def test_index(self):
@@ -16,7 +17,7 b' class TestAdminUsersGroupsController(Tes'
16 17 self.log_user()
17 18 users_group_name = TEST_USERS_GROUP
18 19 response = self.app.post(url('users_groups'),
19 {'users_group_name':users_group_name,
20 {'users_group_name': users_group_name,
20 21 'active':True})
21 22 response.follow()
22 23
@@ -47,7 +48,6 b' class TestAdminUsersGroupsController(Tes'
47 48 self.checkSessionFlash(response,
48 49 'created users group %s' % users_group_name)
49 50
50
51 51 gr = self.Session.query(UsersGroup)\
52 52 .filter(UsersGroup.users_group_name ==
53 53 users_group_name).one()
@@ -60,6 +60,53 b' class TestAdminUsersGroupsController(Tes'
60 60
61 61 self.assertEqual(gr, None)
62 62
63 def test_enable_repository_read_on_group(self):
64 self.log_user()
65 users_group_name = TEST_USERS_GROUP + 'another2'
66 response = self.app.post(url('users_groups'),
67 {'users_group_name': users_group_name,
68 'active':True})
69 response.follow()
70
71 ug = UsersGroup.get_by_group_name(users_group_name)
72 self.checkSessionFlash(response,
73 'created users group %s' % users_group_name)
74
75 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
76 {'create_repo_perm': True})
77
78 response.follow()
79 ug = UsersGroup.get_by_group_name(users_group_name)
80 p = Permission.get_by_key('hg.create.repository')
81 # check if user has this perm
82 perms = UsersGroupToPerm.query()\
83 .filter(UsersGroupToPerm.users_group == ug).all()
84 perms = [[x.__dict__['users_group_id'],
85 x.__dict__['permission_id'],] for x in perms]
86 self.assertEqual(
87 perms,
88 [[ug.users_group_id, p.permission_id]]
89 )
90
91 # DELETE !
92 ug = UsersGroup.get_by_group_name(users_group_name)
93 ugid = ug.users_group_id
94 response = self.app.delete(url('users_group', id=ug.users_group_id))
95 response = response.follow()
96 gr = self.Session.query(UsersGroup)\
97 .filter(UsersGroup.users_group_name ==
98 users_group_name).scalar()
99
100 self.assertEqual(gr, None)
101 p = Permission.get_by_key('hg.create.repository')
102 perms = UsersGroupToPerm.query()\
103 .filter(UsersGroupToPerm.users_group_id == ugid).all()
104 perms = [[x.__dict__['users_group_id'],
105 x.__dict__['permission_id'],] for x in perms]
106 self.assertEqual(
107 perms,
108 []
109 )
63 110
64 111 def test_delete_browser_fakeout(self):
65 112 response = self.app.post(url('users_group', id=1),
@@ -23,7 +23,6 b" def _make_group(path, desc='desc', paren"
23 23 return gr
24 24
25 25 gr = ReposGroupModel().create(path, desc, parent_id)
26 Session.commit()
27 26 return gr
28 27
29 28
@@ -31,13 +30,19 b' class TestReposGroups(unittest.TestCase)'
31 30
32 31 def setUp(self):
33 32 self.g1 = _make_group('test1', skip_if_exists=True)
33 Session.commit()
34 34 self.g2 = _make_group('test2', skip_if_exists=True)
35 Session.commit()
35 36 self.g3 = _make_group('test3', skip_if_exists=True)
37 Session.commit()
36 38
37 39 def tearDown(self):
38 40 print 'out'
39 41
40 42 def __check_path(self, *path):
43 """
44 Checks the path for existance !
45 """
41 46 path = [TESTS_TMP_PATH] + list(path)
42 47 path = os.path.join(*path)
43 48 return os.path.isdir(path)
@@ -49,12 +54,13 b' class TestReposGroups(unittest.TestCase)'
49 54 ReposGroupModel().delete(id_)
50 55
51 56 def __update_group(self, id_, path, desc='desc', parent_id=None):
52 form_data = dict(group_name=path,
53 group_description=desc,
54 group_parent_id=parent_id,
55 perms_updates=[],
56 perms_new=[])
57
57 form_data = dict(
58 group_name=path,
59 group_description=desc,
60 group_parent_id=parent_id,
61 perms_updates=[],
62 perms_new=[]
63 )
58 64 gr = ReposGroupModel().update(id_, form_data)
59 65 return gr
60 66
@@ -150,6 +156,25 b' class TestReposGroups(unittest.TestCase)'
150 156 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
151 157
152 158
159 def test_move_to_root(self):
160 g1 = _make_group('t11')
161 Session.commit()
162 g2 = _make_group('t22',parent_id=g1.group_id)
163 Session.commit()
164
165 self.assertEqual(g2.full_path,'t11/t22')
166 self.assertTrue(self.__check_path('t11', 't22'))
167
168 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
169 Session.commit()
170
171 self.assertEqual(g2.group_name,'g22')
172 # we moved out group from t1 to '' so it's full path should be 'g2'
173 self.assertEqual(g2.full_path,'g22')
174 self.assertFalse(self.__check_path('t11', 't22'))
175 self.assertTrue(self.__check_path('g22'))
176
177
153 178 class TestUser(unittest.TestCase):
154 179 def __init__(self, methodName='runTest'):
155 180 Session.remove()
General Comments 0
You need to be logged in to leave comments. Login now