##// END OF EJS Templates
Merge with upstream
Liad Shani -
r1614:59ae8285 merge beta
parent child Browse files
Show More
@@ -0,0 +1,360 b''
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.lib.compat
4 ~~~~~~~~~~~~~~~~~~~~
5
6 Python backward compatibility functions and common libs
7
8
9 :created_on: Oct 7, 2011
10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
13 """
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
27 #==============================================================================
28 # json
29 #==============================================================================
30 try:
31 import json
32 except ImportError:
33 import simplejson as json
34
35
36 #==============================================================================
37 # izip_longest
38 #==============================================================================
39 try:
40 from itertools import izip_longest
41 except ImportError:
42 import itertools
43
44 def izip_longest(*args, **kwds): # noqa
45 fillvalue = kwds.get("fillvalue")
46
47 def sentinel(counter=([fillvalue] * (len(args) - 1)).pop):
48 yield counter() # yields the fillvalue, or raises IndexError
49
50 fillers = itertools.repeat(fillvalue)
51 iters = [itertools.chain(it, sentinel(), fillers)
52 for it in args]
53 try:
54 for tup in itertools.izip(*iters):
55 yield tup
56 except IndexError:
57 pass
58
59
60 #==============================================================================
61 # OrderedDict
62 #==============================================================================
63
64 # Python Software Foundation License
65
66 # XXX: it feels like using the class with "is" and "is not" instead of "==" and
67 # "!=" should be faster.
68 class _Nil(object):
69
70 def __repr__(self):
71 return "nil"
72
73 def __eq__(self, other):
74 if (isinstance(other, _Nil)):
75 return True
76 else:
77 return NotImplemented
78
79 def __ne__(self, other):
80 if (isinstance(other, _Nil)):
81 return False
82 else:
83 return NotImplemented
84
85 _nil = _Nil()
86
87 class _odict(object):
88 """Ordered dict data structure, with O(1) complexity for dict operations
89 that modify one element.
90
91 Overwriting values doesn't change their original sequential order.
92 """
93
94 def _dict_impl(self):
95 return None
96
97 def __init__(self, data=(), **kwds):
98 """This doesn't accept keyword initialization as normal dicts to avoid
99 a trap - inside a function or method the keyword args are accessible
100 only as a dict, without a defined order, so their original order is
101 lost.
102 """
103 if kwds:
104 raise TypeError("__init__() of ordered dict takes no keyword "
105 "arguments to avoid an ordering trap.")
106 self._dict_impl().__init__(self)
107 # If you give a normal dict, then the order of elements is undefined
108 if hasattr(data, "iteritems"):
109 for key, val in data.iteritems():
110 self[key] = val
111 else:
112 for key, val in data:
113 self[key] = val
114
115 # Double-linked list header
116 def _get_lh(self):
117 dict_impl = self._dict_impl()
118 if not hasattr(self, '_lh'):
119 dict_impl.__setattr__(self, '_lh', _nil)
120 return dict_impl.__getattribute__(self, '_lh')
121
122 def _set_lh(self, val):
123 self._dict_impl().__setattr__(self, '_lh', val)
124
125 lh = property(_get_lh, _set_lh)
126
127 # Double-linked list tail
128 def _get_lt(self):
129 dict_impl = self._dict_impl()
130 if not hasattr(self, '_lt'):
131 dict_impl.__setattr__(self, '_lt', _nil)
132 return dict_impl.__getattribute__(self, '_lt')
133
134 def _set_lt(self, val):
135 self._dict_impl().__setattr__(self, '_lt', val)
136
137 lt = property(_get_lt, _set_lt)
138
139 def __getitem__(self, key):
140 return self._dict_impl().__getitem__(self, key)[1]
141
142 def __setitem__(self, key, val):
143 dict_impl = self._dict_impl()
144 try:
145 dict_impl.__getitem__(self, key)[1] = val
146 except KeyError, e:
147 new = [dict_impl.__getattribute__(self, 'lt'), val, _nil]
148 dict_impl.__setitem__(self, key, new)
149 if dict_impl.__getattribute__(self, 'lt') == _nil:
150 dict_impl.__setattr__(self, 'lh', key)
151 else:
152 dict_impl.__getitem__(
153 self, dict_impl.__getattribute__(self, 'lt'))[2] = key
154 dict_impl.__setattr__(self, 'lt', key)
155
156 def __delitem__(self, key):
157 dict_impl = self._dict_impl()
158 pred, _ , succ = self._dict_impl().__getitem__(self, key)
159 if pred == _nil:
160 dict_impl.__setattr__(self, 'lh', succ)
161 else:
162 dict_impl.__getitem__(self, pred)[2] = succ
163 if succ == _nil:
164 dict_impl.__setattr__(self, 'lt', pred)
165 else:
166 dict_impl.__getitem__(self, succ)[0] = pred
167 dict_impl.__delitem__(self, key)
168
169 def __contains__(self, key):
170 return key in self.keys()
171
172 def __len__(self):
173 return len(self.keys())
174
175 def __str__(self):
176 pairs = ("%r: %r" % (k, v) for k, v in self.iteritems())
177 return "{%s}" % ", ".join(pairs)
178
179 def __repr__(self):
180 if self:
181 pairs = ("(%r, %r)" % (k, v) for k, v in self.iteritems())
182 return "odict([%s])" % ", ".join(pairs)
183 else:
184 return "odict()"
185
186 def get(self, k, x=None):
187 if k in self:
188 return self._dict_impl().__getitem__(self, k)[1]
189 else:
190 return x
191
192 def __iter__(self):
193 dict_impl = self._dict_impl()
194 curr_key = dict_impl.__getattribute__(self, 'lh')
195 while curr_key != _nil:
196 yield curr_key
197 curr_key = dict_impl.__getitem__(self, curr_key)[2]
198
199 iterkeys = __iter__
200
201 def keys(self):
202 return list(self.iterkeys())
203
204 def itervalues(self):
205 dict_impl = self._dict_impl()
206 curr_key = dict_impl.__getattribute__(self, 'lh')
207 while curr_key != _nil:
208 _, val, curr_key = dict_impl.__getitem__(self, curr_key)
209 yield val
210
211 def values(self):
212 return list(self.itervalues())
213
214 def iteritems(self):
215 dict_impl = self._dict_impl()
216 curr_key = dict_impl.__getattribute__(self, 'lh')
217 while curr_key != _nil:
218 _, val, next_key = dict_impl.__getitem__(self, curr_key)
219 yield curr_key, val
220 curr_key = next_key
221
222 def items(self):
223 return list(self.iteritems())
224
225 def sort(self, cmp=None, key=None, reverse=False):
226 items = [(k, v) for k, v in self.items()]
227 if cmp is not None:
228 items = sorted(items, cmp=cmp)
229 elif key is not None:
230 items = sorted(items, key=key)
231 else:
232 items = sorted(items, key=lambda x: x[1])
233 if reverse:
234 items.reverse()
235 self.clear()
236 self.__init__(items)
237
238 def clear(self):
239 dict_impl = self._dict_impl()
240 dict_impl.clear(self)
241 dict_impl.__setattr__(self, 'lh', _nil)
242 dict_impl.__setattr__(self, 'lt', _nil)
243
244 def copy(self):
245 return self.__class__(self)
246
247 def update(self, data=(), **kwds):
248 if kwds:
249 raise TypeError("update() of ordered dict takes no keyword "
250 "arguments to avoid an ordering trap.")
251 if hasattr(data, "iteritems"):
252 data = data.iteritems()
253 for key, val in data:
254 self[key] = val
255
256 def setdefault(self, k, x=None):
257 try:
258 return self[k]
259 except KeyError:
260 self[k] = x
261 return x
262
263 def pop(self, k, x=_nil):
264 try:
265 val = self[k]
266 del self[k]
267 return val
268 except KeyError:
269 if x == _nil:
270 raise
271 return x
272
273 def popitem(self):
274 try:
275 dict_impl = self._dict_impl()
276 key = dict_impl.__getattribute__(self, 'lt')
277 return key, self.pop(key)
278 except KeyError:
279 raise KeyError("'popitem(): ordered dictionary is empty'")
280
281 def riterkeys(self):
282 """To iterate on keys in reversed order.
283 """
284 dict_impl = self._dict_impl()
285 curr_key = dict_impl.__getattribute__(self, 'lt')
286 while curr_key != _nil:
287 yield curr_key
288 curr_key = dict_impl.__getitem__(self, curr_key)[0]
289
290 __reversed__ = riterkeys
291
292 def rkeys(self):
293 """List of the keys in reversed order.
294 """
295 return list(self.riterkeys())
296
297 def ritervalues(self):
298 """To iterate on values in reversed order.
299 """
300 dict_impl = self._dict_impl()
301 curr_key = dict_impl.__getattribute__(self, 'lt')
302 while curr_key != _nil:
303 curr_key, val, _ = dict_impl.__getitem__(self, curr_key)
304 yield val
305
306 def rvalues(self):
307 """List of the values in reversed order.
308 """
309 return list(self.ritervalues())
310
311 def riteritems(self):
312 """To iterate on (key, value) in reversed order.
313 """
314 dict_impl = self._dict_impl()
315 curr_key = dict_impl.__getattribute__(self, 'lt')
316 while curr_key != _nil:
317 pred_key, val, _ = dict_impl.__getitem__(self, curr_key)
318 yield curr_key, val
319 curr_key = pred_key
320
321 def ritems(self):
322 """List of the (key, value) in reversed order.
323 """
324 return list(self.riteritems())
325
326 def firstkey(self):
327 if self:
328 return self._dict_impl().__getattribute__(self, 'lh')
329 else:
330 raise KeyError("'firstkey(): ordered dictionary is empty'")
331
332 def lastkey(self):
333 if self:
334 return self._dict_impl().__getattribute__(self, 'lt')
335 else:
336 raise KeyError("'lastkey(): ordered dictionary is empty'")
337
338 def as_dict(self):
339 return self._dict_impl()(self.items())
340
341 def _repr(self):
342 """_repr(): low level repr of the whole data contained in the odict.
343 Useful for debugging.
344 """
345 dict_impl = self._dict_impl()
346 form = "odict low level repr lh,lt,data: %r, %r, %s"
347 return form % (dict_impl.__getattribute__(self, 'lh'),
348 dict_impl.__getattribute__(self, 'lt'),
349 dict_impl.__repr__(self))
350
351 class OrderedDict(_odict, dict):
352
353 def _dict_impl(self):
354 return dict
355
356
357 #==============================================================================
358 # OrderedSet
359 #==============================================================================
360 from sqlalchemy.util import OrderedSet
@@ -0,0 +1,189 b''
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.tests.test_hg_operations
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Test suite for making push/pull operations
7
8 :created_on: Dec 30, 2010
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
11 """
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25 import os
26 import sys
27 import shutil
28 import logging
29 from os.path import join as jn
30 from os.path import dirname as dn
31
32 from tempfile import _RandomNameSequence
33 from subprocess import Popen, PIPE
34
35 from paste.deploy import appconfig
36 from pylons import config
37 from sqlalchemy import engine_from_config
38
39 from rhodecode.lib.utils import add_cache
40 from rhodecode.model import init_model
41 from rhodecode.model import meta
42 from rhodecode.model.db import User, Repository
43 from rhodecode.lib.auth import get_crypt_password
44
45 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
46 from rhodecode.config.environment import load_environment
47
48 rel_path = dn(dn(dn(os.path.abspath(__file__))))
49 conf = appconfig('config:development.ini', relative_to=rel_path)
50 load_environment(conf.global_conf, conf.local_conf)
51
52 add_cache(conf)
53
54 USER = 'test_admin'
55 PASS = 'test12'
56 HOST = '127.0.0.1:5000'
57 DEBUG = True
58 log = logging.getLogger(__name__)
59
60
61 class Command(object):
62
63 def __init__(self, cwd):
64 self.cwd = cwd
65
66 def execute(self, cmd, *args):
67 """Runs command on the system with given ``args``.
68 """
69
70 command = cmd + ' ' + ' '.join(args)
71 log.debug('Executing %s' % command)
72 if DEBUG:
73 print command
74 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
75 stdout, stderr = p.communicate()
76 if DEBUG:
77 print stdout, stderr
78 return stdout, stderr
79
80 def get_session():
81 engine = engine_from_config(conf, 'sqlalchemy.db1.')
82 init_model(engine)
83 sa = meta.Session()
84 return sa
85
86
87 def create_test_user(force=True):
88 print 'creating test user'
89 sa = get_session()
90
91 user = sa.query(User).filter(User.username == USER).scalar()
92
93 if force and user is not None:
94 print 'removing current user'
95 for repo in sa.query(Repository).filter(Repository.user == user).all():
96 sa.delete(repo)
97 sa.delete(user)
98 sa.commit()
99
100 if user is None or force:
101 print 'creating new one'
102 new_usr = User()
103 new_usr.username = USER
104 new_usr.password = get_crypt_password(PASS)
105 new_usr.email = 'mail@mail.com'
106 new_usr.name = 'test'
107 new_usr.lastname = 'lasttestname'
108 new_usr.active = True
109 new_usr.admin = True
110 sa.add(new_usr)
111 sa.commit()
112
113 print 'done'
114
115
116 def create_test_repo(force=True):
117 print 'creating test repo'
118 from rhodecode.model.repo import RepoModel
119 sa = get_session()
120
121 user = sa.query(User).filter(User.username == USER).scalar()
122 if user is None:
123 raise Exception('user not found')
124
125
126 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
127
128 if repo is None:
129 print 'repo not found creating'
130
131 form_data = {'repo_name':HG_REPO,
132 'repo_type':'hg',
133 'private':False,
134 'clone_uri':'' }
135 rm = RepoModel(sa)
136 rm.base_path = '/home/hg'
137 rm.create(form_data, user)
138
139 print 'done'
140
141 def set_anonymous_access(enable=True):
142 sa = get_session()
143 user = sa.query(User).filter(User.username == 'default').one()
144 user.active = enable
145 sa.add(user)
146 sa.commit()
147
148 def get_anonymous_access():
149 sa = get_session()
150 return sa.query(User).filter(User.username == 'default').one().active
151
152
153 #==============================================================================
154 # TESTS
155 #==============================================================================
156 def test_clone_with_credentials(no_errors=False, repo=HG_REPO):
157 cwd = path = jn(TESTS_TMP_PATH, repo)
158
159
160 try:
161 shutil.rmtree(path, ignore_errors=True)
162 os.makedirs(path)
163 #print 'made dirs %s' % jn(path)
164 except OSError:
165 raise
166
167
168 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
169 {'user':USER,
170 'pass':PASS,
171 'host':HOST,
172 'cloned_repo':repo,
173 'dest':path + _RandomNameSequence().next()}
174
175 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
176
177 if no_errors is False:
178 assert """adding file changes""" in stdout, 'no messages about cloning'
179 assert """abort""" not in stderr , 'got error from clone'
180
181 if __name__ == '__main__':
182 try:
183 create_test_user(force=False)
184
185 for i in range(int(sys.argv[2])):
186 test_clone_with_credentials(repo=sys.argv[1])
187
188 except Exception, e:
189 sys.exit('stop on %s' % e)
@@ -2,10 +2,11 b''
2 Welcome to RhodeCode (RhodiumCode) documentation!
2 Welcome to RhodeCode (RhodiumCode) documentation!
3 =================================================
3 =================================================
4
4
5 ``RhodeCode`` (formerly hg-app) is a Pylons framework based Mercurial repository
5 ``RhodeCode`` is a Pylons framework based Mercurial repository
6 browser/management tool with a built in push/pull server and full text search.
6 browser/management tool with a built in push/pull server and full text search.
7 It works on http/https and has a built in permission/authentication system with
7 It works on http/https and has a built in permission/authentication system with
8 the ability to authenticate via LDAP.
8 the ability to authenticate via LDAP or ActiveDirectory. RhodeCode also supports
9 simple API so it's easy integrable with existing systems.
9
10
10 RhodeCode is similar in some respects to github or bitbucket_,
11 RhodeCode is similar in some respects to github or bitbucket_,
11 however RhodeCode can be run as standalone hosted application on your own server.
12 however RhodeCode can be run as standalone hosted application on your own server.
@@ -6,11 +6,12 b' API'
6
6
7
7
8 Starting from RhodeCode version 1.2 a simple API was implemented.
8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 There's one schema for calling all api methods. API is implemented
9 There's a single schema for calling all api methods. API is implemented
10 with JSON protocol both ways.
10 with JSON protocol both ways. An url to send API request in RhodeCode is
11 <your_server>/_admin/api
11
12
12
13
13 Clients need to send JSON data in such format::
14 All clients need to send JSON data in such format::
14
15
15 {
16 {
16 "api_key":"<api_key>",
17 "api_key":"<api_key>",
@@ -18,16 +19,20 b' Clients need to send JSON data in such f'
18 "args":{"<arg_key>":"<arg_val>"}
19 "args":{"<arg_key>":"<arg_val>"}
19 }
20 }
20
21
21 Simply provide api_key for access and permission validation
22 Example call for autopulling remotes repos using curl::
22 method is name of method to call
23 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
23 and args is an key:value list of arguments to pass to method
24
25 Simply provide
26 - *api_key* for access and permission validation.
27 - *method* is name of method to call
28 - *args* is an key:value list of arguments to pass to method
24
29
25 .. note::
30 .. note::
26
31
27 api_key can be found in your user account page
32 api_key can be found in your user account page
28
33
29
34
30 And will receive JSON formatted answer::
35 RhodeCode API will return always a JSON formatted answer::
31
36
32 {
37 {
33 "result": "<result>",
38 "result": "<result>",
@@ -35,7 +40,7 b' And will receive JSON formatted answer::'
35 }
40 }
36
41
37 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
42 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
38 calling api **error** key from response will contain failure description
43 calling api *error* key from response will contain failure description
39 and result will be null.
44 and result will be null.
40
45
41 API METHODS
46 API METHODS
@@ -46,11 +51,61 b' pull'
46 ----
51 ----
47
52
48 Pulls given repo from remote location. Can be used to automatically keep
53 Pulls given repo from remote location. Can be used to automatically keep
49 remote repos upto date. This command can be executed only using admin users
54 remote repos up to date. This command can be executed only using api_key
50 api_key
55 belonging to user with admin rights
51
56
52 ::
57 INPUT::
53
58
59 api_key:"<api_key>"
54 method: "pull"
60 method: "pull"
55 args: {"repo":<repo_name>}
61 args: {"repo":<repo_name>}
56
62
63 OUTPUT::
64
65 result:"Pulled from <repo_name>"
66 error:null
67
68
69 create_user
70 -----------
71
72 Creates new user in RhodeCode. This command can be executed only using api_key
73 belonging to user with admin rights
74
75 INPUT::
76
77 api_key:"<api_key>"
78 method: "create_user"
79 args: {"username": "<username>",
80 "password": "<password>",
81 "active": "<bool>",
82 "admin": "<bool>",
83 "name": "<firstname>",
84 "lastname": "<lastname>",
85 "email": "<useremail>"}
86
87 OUTPUT::
88
89 result:{"id": <newuserid>,
90 "msg":"created new user <username>"}
91 error:null
92
93
94 create_users_group
95 ------------------
96
97 creates new users group. This command can be executed only using api_key
98 belonging to user with admin rights
99
100 INPUT::
101
102 api_key:"<api_key>"
103 method: "create_user"
104 args: {"name": "<groupname>",
105 "active":"<bool>"}
106
107 OUTPUT::
108
109 result:{"id": <newusersgroupid>,
110 "msg":"created new users group <groupname>"}
111 error:null
@@ -3,7 +3,8 b''
3 Changelog
3 Changelog
4 =========
4 =========
5
5
6 1.2.0 (**2011-XX-XX**)
6
7 1.3.0 (**XXXX-XX-XX**)
7 ======================
8 ======================
8
9
9 :status: in-progress
10 :status: in-progress
@@ -12,6 +13,29 b' 1.2.0 (**2011-XX-XX**)'
12 news
13 news
13 ----
14 ----
14
15
16 fixes
17 -----
18
19 1.2.1 (**2011-10-08**)
20 ======================
21
22 news
23 ----
24
25
26 fixes
27 -----
28
29 - fixed problems with basic auth and push problems
30 - gui fixes
31 - fixed logger
32
33 1.2.0 (**2011-10-07**)
34 ======================
35
36 news
37 ----
38
15 - implemented #47 repository groups
39 - implemented #47 repository groups
16 - implemented #89 Can setup google analytics code from settings menu
40 - implemented #89 Can setup google analytics code from settings menu
17 - implemented #91 added nicer looking archive urls with more download options
41 - implemented #91 added nicer looking archive urls with more download options
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -25,9 +25,9 b''
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import platform
26 import platform
27
27
28 VERSION = (1, 2, 0, 'beta')
28 VERSION = (1, 3, 0, 'beta')
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 __dbversion__ = 3 #defines current db version for migrations
30 __dbversion__ = 4 #defines current db version for migrations
31 __platform__ = platform.system()
31 __platform__ = platform.system()
32 __license__ = 'GPLv3'
32 __license__ = 'GPLv3'
33
33
@@ -51,6 +51,9 b' def make_app(global_conf, full_stack=Tru'
51 from rhodecode.lib.profiler import ProfilingMiddleware
51 from rhodecode.lib.profiler import ProfilingMiddleware
52 app = ProfilingMiddleware(app)
52 app = ProfilingMiddleware(app)
53
53
54
55 # we want our low level middleware to get to the request ASAP. We don't
56 # need any pylons stack middleware in them
54 app = SimpleHg(app, config)
57 app = SimpleHg(app, config)
55 app = SimpleGit(app, config)
58 app = SimpleGit(app, config)
56
59
@@ -77,6 +80,7 b' def make_app(global_conf, full_stack=Tru'
77 app = Cascade([static_app, app])
80 app = Cascade([static_app, app])
78 app = make_gzip_middleware(app, global_conf, compress_level=1)
81 app = make_gzip_middleware(app, global_conf, compress_level=1)
79
82
83
80 app.config = config
84 app.config = config
81
85
82 return app
86 return app
@@ -7,7 +7,7 b' refer to the routes manual at http://rou'
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10 from rhodecode.lib.utils import check_repo_fast as cr
10
11
11
12 # prefix for non repository related links needs to be prefixed with `/`
12 # prefix for non repository related links needs to be prefixed with `/`
13 ADMIN_PREFIX = '/_admin'
13 ADMIN_PREFIX = '/_admin'
@@ -19,23 +19,36 b' def make_map(config):'
19 always_scan=config['debug'])
19 always_scan=config['debug'])
20 rmap.minimization = False
20 rmap.minimization = False
21 rmap.explicit = False
21 rmap.explicit = False
22
22
23 from rhodecode.lib.utils import is_valid_repo
24 from rhodecode.lib.utils import is_valid_repos_group
25
23 def check_repo(environ, match_dict):
26 def check_repo(environ, match_dict):
24 """
27 """
25 check for valid repository for proper 404 handling
28 check for valid repository for proper 404 handling
29
26 :param environ:
30 :param environ:
27 :param match_dict:
31 :param match_dict:
28 """
32 """
33
29 repo_name = match_dict.get('repo_name')
34 repo_name = match_dict.get('repo_name')
30 return not cr(repo_name, config['base_path'])
35 return is_valid_repo(repo_name, config['base_path'])
36
37 def check_group(environ, match_dict):
38 """
39 check for valid repositories group for proper 404 handling
40
41 :param environ:
42 :param match_dict:
43 """
44 repos_group_name = match_dict.get('group_name')
45
46 return is_valid_repos_group(repos_group_name, config['base_path'])
31
47
32
48
33 def check_int(environ, match_dict):
49 def check_int(environ, match_dict):
34 return match_dict.get('id').isdigit()
50 return match_dict.get('id').isdigit()
35
51
36
37
38
39 # The ErrorController route (handles 404/500 error pages); it should
52 # The ErrorController route (handles 404/500 error pages); it should
40 # likely stay at the top, ensuring it can always be resolved
53 # likely stay at the top, ensuring it can always be resolved
41 rmap.connect('/error/{action}', controller='error')
54 rmap.connect('/error/{action}', controller='error')
@@ -319,6 +332,14 b' def make_map(config):'
319 #==========================================================================
332 #==========================================================================
320 # REPOSITORY ROUTES
333 # REPOSITORY ROUTES
321 #==========================================================================
334 #==========================================================================
335 rmap.connect('summary_home', '/{repo_name:.*}',
336 controller='summary',
337 conditions=dict(function=check_repo))
338
339 # rmap.connect('repo_group_home', '/{group_name:.*}',
340 # controller='admin/repos_groups',action="show_by_name",
341 # conditions=dict(function=check_group))
342
322 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
323 controller='changeset', revision='tip',
344 controller='changeset', revision='tip',
324 conditions=dict(function=check_repo))
345 conditions=dict(function=check_repo))
@@ -328,9 +349,6 b' def make_map(config):'
328 controller='changeset', action='raw_changeset',
349 controller='changeset', action='raw_changeset',
329 revision='tip', conditions=dict(function=check_repo))
350 revision='tip', conditions=dict(function=check_repo))
330
351
331 rmap.connect('summary_home', '/{repo_name:.*}',
332 controller='summary', conditions=dict(function=check_repo))
333
334 rmap.connect('summary_home', '/{repo_name:.*}/summary',
352 rmap.connect('summary_home', '/{repo_name:.*}/summary',
335 controller='summary', conditions=dict(function=check_repo))
353 controller='summary', conditions=dict(function=check_repo))
336
354
@@ -23,19 +23,21 b''
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
27 import traceback
28 import formencode
26 from formencode import htmlfill
29 from formencode import htmlfill
30
27 from pylons import request, session, tmpl_context as c, url
31 from pylons import request, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
30 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
33 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
38 from rhodecode.model.forms import DefaultPermissionsForm
34 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.permission import PermissionModel
35 from rhodecode.model.user import UserModel
40 from rhodecode.model.db import User
36 import formencode
37 import logging
38 import traceback
39
41
40 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
41
43
@@ -142,7 +144,7 b' class PermissionsController(BaseControll'
142 c.create_choices = self.create_choices
144 c.create_choices = self.create_choices
143
145
144 if id == 'default':
146 if id == 'default':
145 default_user = UserModel().get_by_username('default')
147 default_user = User.get_by_username('default')
146 defaults = {'_method': 'put',
148 defaults = {'_method': 'put',
147 'anonymous': default_user.active}
149 'anonymous': default_user.active}
148
150
@@ -89,7 +89,7 b' class ReposController(BaseController):'
89 """
89 """
90 self.__load_defaults()
90 self.__load_defaults()
91
91
92 c.repo_info = db_repo = Repository.by_repo_name(repo_name)
92 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
93 repo = scm_repo = db_repo.scm_instance
93 repo = scm_repo = db_repo.scm_instance
94
94
95 if c.repo_info is None:
95 if c.repo_info is None:
@@ -101,7 +101,7 b' class ReposController(BaseController):'
101
101
102 return redirect(url('repos'))
102 return redirect(url('repos'))
103
103
104 c.default_user_id = User.by_username('default').user_id
104 c.default_user_id = User.get_by_username('default').user_id
105 c.in_public_journal = self.sa.query(UserFollowing)\
105 c.in_public_journal = self.sa.query(UserFollowing)\
106 .filter(UserFollowing.user_id == c.default_user_id)\
106 .filter(UserFollowing.user_id == c.default_user_id)\
107 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
107 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
@@ -381,8 +381,8 b' class ReposController(BaseController):'
381 token = get_token()
381 token = get_token()
382 if cur_token == token:
382 if cur_token == token:
383 try:
383 try:
384 repo_id = Repository.by_repo_name(repo_name).repo_id
384 repo_id = Repository.get_by_repo_name(repo_name).repo_id
385 user_id = User.by_username('default').user_id
385 user_id = User.get_by_username('default').user_id
386 self.scm_model.toggle_following_repo(repo_id, user_id)
386 self.scm_model.toggle_following_repo(repo_id, user_id)
387 h.flash(_('Updated repository visibility in public journal'),
387 h.flash(_('Updated repository visibility in public journal'),
388 category='success')
388 category='success')
@@ -46,6 +46,7 b' from rhodecode.model.forms import UserFo'
46 ApplicationUiSettingsForm
46 ApplicationUiSettingsForm
47 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49 from rhodecode.model.db import User
49
50
50 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
51
52
@@ -299,7 +300,7 b' class SettingsController(BaseController)'
299 """
300 """
300 # url('admin_settings_my_account')
301 # url('admin_settings_my_account')
301
302
302 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
303 c.user = User.get(self.rhodecode_user.user_id)
303 all_repos = self.sa.query(Repository)\
304 all_repos = self.sa.query(Repository)\
304 .filter(Repository.user_id == c.user.user_id)\
305 .filter(Repository.user_id == c.user.user_id)\
305 .order_by(func.lower(Repository.repo_name)).all()
306 .order_by(func.lower(Repository.repo_name)).all()
@@ -340,8 +341,7 b' class SettingsController(BaseController)'
340 category='success')
341 category='success')
341
342
342 except formencode.Invalid, errors:
343 except formencode.Invalid, errors:
343 c.user = user_model.get(self.rhodecode_user.user_id, cache=False)
344 c.user = User.get(self.rhodecode_user.user_id)
344 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
345 all_repos = self.sa.query(Repository)\
345 all_repos = self.sa.query(Repository)\
346 .filter(Repository.user_id == c.user.user_id)\
346 .filter(Repository.user_id == c.user.user_id)\
347 .order_by(func.lower(Repository.repo_name))\
347 .order_by(func.lower(Repository.repo_name))\
@@ -26,10 +26,12 b''
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import inspect
28 import inspect
29 import json
30 import logging
29 import logging
31 import types
30 import types
32 import urllib
31 import urllib
32 import traceback
33
34 from rhodecode.lib.compat import izip_longest, json
33
35
34 from paste.response import replace_header
36 from paste.response import replace_header
35
37
@@ -39,7 +41,7 b' from pylons.controllers.util import Resp'
39 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
41 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
40 HTTPBadRequest, HTTPError
42 HTTPBadRequest, HTTPError
41
43
42 from rhodecode.model.user import User
44 from rhodecode.model.db import User
43 from rhodecode.lib.auth import AuthUser
45 from rhodecode.lib.auth import AuthUser
44
46
45 log = logging.getLogger('JSONRPC')
47 log = logging.getLogger('JSONRPC')
@@ -85,10 +87,9 b' class JSONRPCController(WSGIController):'
85 Parse the request body as JSON, look up the method on the
87 Parse the request body as JSON, look up the method on the
86 controller and if it exists, dispatch to it.
88 controller and if it exists, dispatch to it.
87 """
89 """
88
89 if 'CONTENT_LENGTH' not in environ:
90 if 'CONTENT_LENGTH' not in environ:
90 log.debug("No Content-Length")
91 log.debug("No Content-Length")
91 return jsonrpc_error(0, "No Content-Length")
92 return jsonrpc_error(message="No Content-Length in request")
92 else:
93 else:
93 length = environ['CONTENT_LENGTH'] or 0
94 length = environ['CONTENT_LENGTH'] or 0
94 length = int(environ['CONTENT_LENGTH'])
95 length = int(environ['CONTENT_LENGTH'])
@@ -96,20 +97,18 b' class JSONRPCController(WSGIController):'
96
97
97 if length == 0:
98 if length == 0:
98 log.debug("Content-Length is 0")
99 log.debug("Content-Length is 0")
99 return jsonrpc_error(0, "Content-Length is 0")
100 return jsonrpc_error(message="Content-Length is 0")
100
101
101 raw_body = environ['wsgi.input'].read(length)
102 raw_body = environ['wsgi.input'].read(length)
102
103
103 try:
104 try:
104 json_body = json.loads(urllib.unquote_plus(raw_body))
105 json_body = json.loads(urllib.unquote_plus(raw_body))
105 except ValueError as e:
106 except ValueError, e:
106 #catch JSON errors Here
107 #catch JSON errors Here
107 return jsonrpc_error("JSON parse error ERR:%s RAW:%r" \
108 return jsonrpc_error(message="JSON parse error ERR:%s RAW:%r" \
108 % (e, urllib.unquote_plus(raw_body)))
109 % (e, urllib.unquote_plus(raw_body)))
109
110
110
111 #check AUTH based on API KEY
111 #check AUTH based on API KEY
112
113 try:
112 try:
114 self._req_api_key = json_body['api_key']
113 self._req_api_key = json_body['api_key']
115 self._req_method = json_body['method']
114 self._req_method = json_body['method']
@@ -117,47 +116,61 b' class JSONRPCController(WSGIController):'
117 log.debug('method: %s, params: %s',
116 log.debug('method: %s, params: %s',
118 self._req_method,
117 self._req_method,
119 self._req_params)
118 self._req_params)
120 except KeyError as e:
119 except KeyError, e:
121 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
120 return jsonrpc_error(message='Incorrect JSON query missing %s' % e)
122
121
123 #check if we can find this session using api_key
122 #check if we can find this session using api_key
124 try:
123 try:
125 u = User.get_by_api_key(self._req_api_key)
124 u = User.get_by_api_key(self._req_api_key)
126 auth_u = AuthUser(u.user_id, self._req_api_key)
125 auth_u = AuthUser(u.user_id, self._req_api_key)
127 except Exception as e:
126 except Exception, e:
128 return jsonrpc_error(message='Invalid API KEY')
127 return jsonrpc_error(message='Invalid API KEY')
129
128
130 self._error = None
129 self._error = None
131 try:
130 try:
132 self._func = self._find_method()
131 self._func = self._find_method()
133 except AttributeError, e:
132 except AttributeError, e:
134 return jsonrpc_error(str(e))
133 return jsonrpc_error(message=str(e))
135
134
136 # now that we have a method, add self._req_params to
135 # now that we have a method, add self._req_params to
137 # self.kargs and dispatch control to WGIController
136 # self.kargs and dispatch control to WGIController
138 arglist = inspect.getargspec(self._func)[0][1:]
137 argspec = inspect.getargspec(self._func)
138 arglist = argspec[0][1:]
139 defaults = argspec[3] or []
140 default_empty = types.NotImplementedType
141
142 kwarglist = list(izip_longest(reversed(arglist), reversed(defaults),
143 fillvalue=default_empty))
139
144
140 # this is little trick to inject logged in user for
145 # this is little trick to inject logged in user for
141 # perms decorators to work they expect the controller class to have
146 # perms decorators to work they expect the controller class to have
142 # rhodecode_user set
147 # rhodecode_user attribute set
143 self.rhodecode_user = auth_u
148 self.rhodecode_user = auth_u
144
149
145 if 'user' not in arglist:
150 # This attribute will need to be first param of a method that uses
146 return jsonrpc_error('This method [%s] does not support '
151 # api_key, which is translated to instance of user at that name
147 'authentication (missing user param)' %
152 USER_SESSION_ATTR = 'apiuser'
148 self._func.__name__)
153
154 if USER_SESSION_ATTR not in arglist:
155 return jsonrpc_error(message='This method [%s] does not support '
156 'authentication (missing %s param)' %
157 (self._func.__name__, USER_SESSION_ATTR))
149
158
150 # get our arglist and check if we provided them as args
159 # get our arglist and check if we provided them as args
151 for arg in arglist:
160 for arg, default in kwarglist:
152 if arg == 'user':
161 if arg == USER_SESSION_ATTR:
153 # user is something translated from api key and this is
162 # USER_SESSION_ATTR is something translated from api key and
154 # checked before
163 # this is checked before so we don't need validate it
155 continue
164 continue
156
165
157 if not self._req_params or arg not in self._req_params:
166 # skip the required param check if it's default value is
158 return jsonrpc_error('Missing %s arg in JSON DATA' % arg)
167 # NotImplementedType (default_empty)
168 if not self._req_params or (type(default) == default_empty
169 and arg not in self._req_params):
170 return jsonrpc_error(message=('Missing non optional %s arg '
171 'in JSON DATA') % arg)
159
172
160 self._rpc_args = dict(user=u)
173 self._rpc_args = {USER_SESSION_ATTR:u}
161 self._rpc_args.update(self._req_params)
174 self._rpc_args.update(self._req_params)
162
175
163 self._rpc_args['action'] = self._req_method
176 self._rpc_args['action'] = self._req_method
@@ -186,13 +199,13 b' class JSONRPCController(WSGIController):'
186 """
199 """
187 try:
200 try:
188 raw_response = self._inspect_call(self._func)
201 raw_response = self._inspect_call(self._func)
189 print raw_response
190 if isinstance(raw_response, HTTPError):
202 if isinstance(raw_response, HTTPError):
191 self._error = str(raw_response)
203 self._error = str(raw_response)
192 except JSONRPCError as e:
204 except JSONRPCError, e:
193 self._error = str(e)
205 self._error = str(e)
194 except Exception as e:
206 except Exception, e:
195 log.debug('Encountered unhandled exception: %s', repr(e))
207 log.error('Encountered unhandled exception: %s' \
208 % traceback.format_exc())
196 json_exc = JSONRPCError('Internal server error')
209 json_exc = JSONRPCError('Internal server error')
197 self._error = str(json_exc)
210 self._error = str(json_exc)
198
211
@@ -226,3 +239,4 b' class JSONRPCController(WSGIController):'
226 return func
239 return func
227 else:
240 else:
228 raise AttributeError("No such method: %s" % self._req_method)
241 raise AttributeError("No such method: %s" % self._req_method)
242
@@ -1,7 +1,14 b''
1 import traceback
2 import logging
3
1 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
4 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
2 from rhodecode.lib.auth import HasPermissionAllDecorator
5 from rhodecode.lib.auth import HasPermissionAllDecorator
3 from rhodecode.model.scm import ScmModel
6 from rhodecode.model.scm import ScmModel
4
7
8 from rhodecode.model.db import User, UsersGroup, Repository
9
10 log = logging.getLogger(__name__)
11
5
12
6 class ApiController(JSONRPCController):
13 class ApiController(JSONRPCController):
7 """
14 """
@@ -20,15 +27,18 b' class ApiController(JSONRPCController):'
20 """
27 """
21
28
22 @HasPermissionAllDecorator('hg.admin')
29 @HasPermissionAllDecorator('hg.admin')
23 def pull(self, user, repo):
30 def pull(self, apiuser, repo):
24 """
31 """
25 Dispatch pull action on given repo
32 Dispatch pull action on given repo
26
33
27
34
28 param user:
35 :param user:
29 param repo:
36 :param repo:
30 """
37 """
31
38
39 if Repository.is_valid(repo) is False:
40 raise JSONRPCError('Unknown repo "%s"' % repo)
41
32 try:
42 try:
33 ScmModel().pull_changes(repo, self.rhodecode_user.username)
43 ScmModel().pull_changes(repo, self.rhodecode_user.username)
34 return 'Pulled from %s' % repo
44 return 'Pulled from %s' % repo
@@ -36,5 +46,53 b' class ApiController(JSONRPCController):'
36 raise JSONRPCError('Unable to pull changes from "%s"' % repo)
46 raise JSONRPCError('Unable to pull changes from "%s"' % repo)
37
47
38
48
49 @HasPermissionAllDecorator('hg.admin')
50 def create_user(self, apiuser, username, password, active, admin, name,
51 lastname, email):
52 """
53 Creates new user
54
55 :param apiuser:
56 :param username:
57 :param password:
58 :param active:
59 :param admin:
60 :param name:
61 :param lastname:
62 :param email:
63 """
64
65 form_data = dict(username=username,
66 password=password,
67 active=active,
68 admin=admin,
69 name=name,
70 lastname=lastname,
71 email=email)
72 try:
73 u = User.create(form_data)
74 return {'id':u.user_id,
75 'msg':'created new user %s' % name}
76 except Exception:
77 log.error(traceback.format_exc())
78 raise JSONRPCError('failed to create user %s' % name)
39
79
40
80
81 @HasPermissionAllDecorator('hg.admin')
82 def create_users_group(self, apiuser, name, active):
83 """
84 Creates an new usergroup
85
86 :param name:
87 :param active:
88 """
89 form_data = {'users_group_name':name,
90 'users_group_active':active}
91 try:
92 ug = UsersGroup.create(form_data)
93 return {'id':ug.users_group_id,
94 'msg':'created new users group %s' % name}
95 except Exception:
96 log.error(traceback.format_exc())
97 raise JSONRPCError('failed to create group %s' % name)
98 No newline at end of file
@@ -30,7 +30,7 b' import binascii'
30
30
31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.base import BaseRepoController, render
32 from rhodecode.lib.base import BaseRepoController, render
33 from rhodecode.lib.odict import OrderedDict
33 from rhodecode.lib.compat import OrderedDict
34 from rhodecode.lib import safe_unicode
34 from rhodecode.lib import safe_unicode
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
@@ -25,18 +25,13 b''
25
25
26 import logging
26 import logging
27
27
28 try:
29 import json
30 except ImportError:
31 #python 2.5 compatibility
32 import simplejson as json
33
34 from mercurial import graphmod
28 from mercurial import graphmod
35 from pylons import request, session, tmpl_context as c
29 from pylons import request, session, tmpl_context as c
36
30
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 from rhodecode.lib.base import BaseRepoController, render
32 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.helpers import RepoPage
33 from rhodecode.lib.helpers import RepoPage
34 from rhodecode.lib.compat import json
40
35
41 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
42
37
@@ -34,7 +34,7 b' import rhodecode.lib.helpers as h'
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.utils import EmptyChangeset
36 from rhodecode.lib.utils import EmptyChangeset
37 from rhodecode.lib.odict import OrderedDict
37 from rhodecode.lib.compat import OrderedDict
38
38
39 from vcs.exceptions import RepositoryError, ChangesetError, \
39 from vcs.exceptions import RepositoryError, ChangesetError, \
40 ChangesetDoesNotExistError
40 ChangesetDoesNotExistError
@@ -316,6 +316,13 b' class FilesController(BaseRepoController'
316 filename = file_obj.filename
316 filename = file_obj.filename
317 content = file_obj.file
317 content = file_obj.file
318
318
319 #TODO: REMOVE THIS !!
320 ################################
321 import ipdb;ipdb.set_trace()
322 print 'setting ipdb debuggin for rhodecode.controllers.files.FilesController.add'
323 ################################
324
325
319 node_path = os.path.join(location, filename)
326 node_path = os.path.join(location, filename)
320 author = self.rhodecode_user.full_contact
327 author = self.rhodecode_user.full_contact
321
328
@@ -64,8 +64,7 b' class LoginController(BaseController):'
64 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
65 #form checks for username/password, now we're authenticated
65 #form checks for username/password, now we're authenticated
66 username = c.form_result['username']
66 username = c.form_result['username']
67 user = User.by_username(username,
67 user = User.get_by_username(username, case_insensitive=True)
68 case_insensitive=True)
69 auth_user = AuthUser(user.user_id)
68 auth_user = AuthUser(user.user_id)
70 auth_user.set_authenticated()
69 auth_user.set_authenticated()
71 session['rhodecode_user'] = auth_user
70 session['rhodecode_user'] = auth_user
@@ -95,8 +94,7 b' class LoginController(BaseController):'
95 def register(self):
94 def register(self):
96 user_model = UserModel()
95 user_model = UserModel()
97 c.auto_active = False
96 c.auto_active = False
98 for perm in user_model.get_by_username('default',
97 for perm in User.get_by_username('default').user_perms:
99 cache=False).user_perms:
100 if perm.permission.permission_name == 'hg.register.auto_activate':
98 if perm.permission.permission_name == 'hg.register.auto_activate':
101 c.auto_active = True
99 c.auto_active = True
102 break
100 break
@@ -39,18 +39,13 b' from rhodecode.model.repo import RepoMod'
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
40 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.utils import EmptyChangeset
41 from rhodecode.lib.utils import EmptyChangeset
42 from rhodecode.lib.odict import OrderedDict
43
42
44 from rhodecode.lib.celerylib import run_task
43 from rhodecode.lib.celerylib import run_task
45 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
44 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
46 LANGUAGES_EXTENSIONS_MAP
45 LANGUAGES_EXTENSIONS_MAP
47 from rhodecode.lib.helpers import RepoPage
46 from rhodecode.lib.helpers import RepoPage
47 from rhodecode.lib.compat import json, OrderedDict
48
48
49 try:
50 import json
51 except ImportError:
52 #python 2.5 compatibility
53 import simplejson as json
54 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
55
50
56
51
@@ -139,9 +134,9 b' class SummaryController(BaseRepoControll'
139 c.commit_data = stats.commit_activity
134 c.commit_data = stats.commit_activity
140 c.overview_data = stats.commit_activity_combined
135 c.overview_data = stats.commit_activity_combined
141
136
142 lang_stats = [(x, {"count": y,
137 lang_stats = ((x, {"count": y,
143 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
138 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
144 for x, y in lang_stats_d.items()]
139 for x, y in lang_stats_d.items())
145
140
146 c.trending_languages = json.dumps(OrderedDict(
141 c.trending_languages = json.dumps(OrderedDict(
147 sorted(lang_stats, reverse=True,
142 sorted(lang_stats, reverse=True,
@@ -28,7 +28,7 b' from pylons import tmpl_context as c'
28
28
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
30 from rhodecode.lib.base import BaseRepoController, render
30 from rhodecode.lib.base import BaseRepoController, render
31 from rhodecode.lib.odict import OrderedDict
31 from rhodecode.lib.compat import OrderedDict
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
@@ -23,14 +23,6 b''
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26
27 try:
28 import json
29 except ImportError:
30 #python 2.5 compatibility
31 import simplejson as json
32
33
34 def __get_lem():
26 def __get_lem():
35 from pygments import lexers
27 from pygments import lexers
36 from string import lower
28 from string import lower
@@ -157,44 +149,69 b' def generate_api_key(username, salt=None'
157 return hashlib.sha1(username + salt).hexdigest()
149 return hashlib.sha1(username + salt).hexdigest()
158
150
159
151
160 def safe_unicode(_str, from_encoding='utf8'):
152 def safe_unicode(str_, from_encoding='utf8'):
161 """
153 """
162 safe unicode function. In case of UnicodeDecode error we try to return
154 safe unicode function. Does few trick to turn str_ into unicode
163 unicode with errors replaceed
155
156 In case of UnicodeDecode error we try to return it with encoding detected
157 by chardet library if it fails fallback to unicode with errors replaced
164
158
165 :param _str: string to decode
159 :param str_: string to decode
166 :rtype: unicode
160 :rtype: unicode
167 :returns: unicode object
161 :returns: unicode object
168 """
162 """
163 if isinstance(str_, unicode):
164 return str_
169
165
170 if isinstance(_str, unicode):
166 try:
171 return _str
167 return unicode(str_)
168 except UnicodeDecodeError:
169 pass
170
171 try:
172 return unicode(str_, from_encoding)
173 except UnicodeDecodeError:
174 pass
172
175
173 try:
176 try:
174 u_str = unicode(_str, from_encoding)
177 import chardet
175 except UnicodeDecodeError:
178 encoding = chardet.detect(str_)['encoding']
176 u_str = unicode(_str, from_encoding, 'replace')
179 if encoding is None:
177
180 raise Exception()
178 return u_str
181 return str_.decode(encoding)
179
182 except (ImportError, UnicodeDecodeError, Exception):
183 return unicode(str_, from_encoding, 'replace')
180
184
181 def safe_str(_unicode, to_encoding='utf8'):
185 def safe_str(unicode_, to_encoding='utf8'):
182 """
186 """
183 safe str function. In case of UnicodeEncode error we try to return
187 safe str function. Does few trick to turn unicode_ into string
184 str with errors replaceed
188
189 In case of UnicodeEncodeError we try to return it with encoding detected
190 by chardet library if it fails fallback to string with errors replaced
185
191
186 :param _unicode: unicode to encode
192 :param unicode_: unicode to encode
187 :rtype: str
193 :rtype: str
188 :returns: str object
194 :returns: str object
189 """
195 """
190
196
191 if isinstance(_unicode, str):
197 if isinstance(unicode_, str):
192 return _unicode
198 return unicode_
199
200 try:
201 return unicode_.encode(to_encoding)
202 except UnicodeEncodeError:
203 pass
193
204
194 try:
205 try:
195 safe_str = str(_unicode)
206 import chardet
196 except UnicodeEncodeError:
207 encoding = chardet.detect(unicode_)['encoding']
197 safe_str = _unicode.encode(to_encoding, 'replace')
208 print encoding
209 if encoding is None:
210 raise UnicodeEncodeError()
211
212 return unicode_.encode(encoding)
213 except (ImportError, UnicodeEncodeError):
214 return unicode_.encode(to_encoding, 'replace')
198
215
199 return safe_str
216 return safe_str
200
217
@@ -361,4 +378,4 b' def get_changeset_safe(repo, rev):'
361 except RepositoryError:
378 except RepositoryError:
362 from rhodecode.lib.utils import EmptyChangeset
379 from rhodecode.lib.utils import EmptyChangeset
363 cs = EmptyChangeset(requested_revision=rev)
380 cs = EmptyChangeset(requested_revision=rev)
364 return cs No newline at end of file
381 return cs
@@ -6,8 +6,8 b''
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
13 # it under the terms of the GNU General Public License as published by
@@ -48,7 +48,7 b' from rhodecode.lib.auth_ldap import Auth'
48
48
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission, RhodeCodeSettings
51 from rhodecode.model.db import Permission, RhodeCodeSettings, User
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
@@ -151,7 +151,7 b' def authenticate(username, password):'
151 """
151 """
152
152
153 user_model = UserModel()
153 user_model = UserModel()
154 user = user_model.get_by_username(username, cache=False)
154 user = User.get_by_username(username)
155
155
156 log.debug('Authenticating user using RhodeCode account')
156 log.debug('Authenticating user using RhodeCode account')
157 if user is not None and not user.ldap_dn:
157 if user is not None and not user.ldap_dn:
@@ -170,8 +170,7 b' def authenticate(username, password):'
170
170
171 else:
171 else:
172 log.debug('Regular authentication failed')
172 log.debug('Regular authentication failed')
173 user_obj = user_model.get_by_username(username, cache=False,
173 user_obj = User.get_by_username(username, case_insensitive=True)
174 case_insensitive=True)
175
174
176 if user_obj is not None and not user_obj.ldap_dn:
175 if user_obj is not None and not user_obj.ldap_dn:
177 log.debug('this user already exists as non ldap')
176 log.debug('this user already exists as non ldap')
@@ -252,7 +251,7 b' class AuthUser(object):'
252
251
253 def propagate_data(self):
252 def propagate_data(self):
254 user_model = UserModel()
253 user_model = UserModel()
255 self.anonymous_user = user_model.get_by_username('default', cache=True)
254 self.anonymous_user = User.get_by_username('default')
256 is_user_loaded = False
255 is_user_loaded = False
257 if self._api_key and self._api_key != self.anonymous_user.api_key:
256 if self._api_key and self._api_key != self.anonymous_user.api_key:
258 #try go get user by api key
257 #try go get user by api key
@@ -269,7 +268,7 b' class AuthUser(object):'
269 self.username = self.username.partition('@')[0]
268 self.username = self.username.partition('@')[0]
270
269
271 log.debug('Auth User lookup by USER NAME %s', self.username)
270 log.debug('Auth User lookup by USER NAME %s', self.username)
272 dbuser = user_model.get_by_username(self.username)
271 dbuser = User.get_by_username(self.username)
273 if dbuser is not None and dbuser.active:
272 if dbuser is not None and dbuser.active:
274 for k, v in dbuser.get_dict().items():
273 for k, v in dbuser.get_dict().items():
275 setattr(self, k, v)
274 setattr(self, k, v)
@@ -7,8 +7,8 b''
7 repositories and send it to backup server using RSA key via ssh.
7 repositories and send it to backup server using RSA key via ssh.
8
8
9 :created_on: Feb 28, 2010
9 :created_on: Feb 28, 2010
10 :copyright: (c) 2010 by marcink.
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
@@ -76,7 +76,7 b' class BaseRepoController(BaseController)'
76 super(BaseRepoController, self).__before__()
76 super(BaseRepoController, self).__before__()
77 if c.repo_name:
77 if c.repo_name:
78
78
79 c.rhodecode_db_repo = Repository.by_repo_name(c.repo_name)
79 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
80 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
80 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
81
81
82 if c.rhodecode_repo is None:
82 if c.rhodecode_repo is None:
@@ -43,7 +43,8 b' from rhodecode.lib.celerylib import run_'
43 from rhodecode.lib.helpers import person
43 from rhodecode.lib.helpers import person
44 from rhodecode.lib.smtp_mailer import SmtpMailer
44 from rhodecode.lib.smtp_mailer import SmtpMailer
45 from rhodecode.lib.utils import add_cache
45 from rhodecode.lib.utils import add_cache
46 from rhodecode.lib.odict import OrderedDict
46 from rhodecode.lib.compat import json, OrderedDict
47
47 from rhodecode.model import init_model
48 from rhodecode.model import init_model
48 from rhodecode.model import meta
49 from rhodecode.model import meta
49 from rhodecode.model.db import RhodeCodeUi, Statistics, Repository
50 from rhodecode.model.db import RhodeCodeUi, Statistics, Repository
@@ -54,11 +55,7 b' from sqlalchemy import engine_from_confi'
54
55
55 add_cache(config)
56 add_cache(config)
56
57
57 try:
58
58 import json
59 except ImportError:
60 #python 2.5 compatibility
61 import simplejson as json
62
59
63 __all__ = ['whoosh_index', 'get_commits_stats',
60 __all__ = ['whoosh_index', 'get_commits_stats',
64 'reset_user_password', 'send_email']
61 'reset_user_password', 'send_email']
@@ -160,7 +160,9 b' class DbManage(object):'
160 def step_3(self):
160 def step_3(self):
161 print ('Adding additional settings into RhodeCode db')
161 print ('Adding additional settings into RhodeCode db')
162 self.klass.fix_settings()
162 self.klass.fix_settings()
163
163 print ('Adding ldap defaults')
164 self.klass.create_ldap_options(skip_existing=True)
165
164 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
166 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
165
167
166 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
168 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
@@ -307,7 +309,7 b' class DbManage(object):'
307 self.sa.rollback()
309 self.sa.rollback()
308 raise
310 raise
309
311
310 def create_ldap_options(self):
312 def create_ldap_options(self,skip_existing=False):
311 """Creates ldap settings"""
313 """Creates ldap settings"""
312
314
313 try:
315 try:
@@ -319,6 +321,9 b' class DbManage(object):'
319 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
321 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
320 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
322 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
321
323
324 if skip_existing and RhodeCodeSettings.get_by_name(k) != None:
325 log.debug('Skipping option %s' % k)
326 continue
322 setting = RhodeCodeSettings(k, v)
327 setting = RhodeCodeSettings(k, v)
323 self.sa.add(setting)
328 self.sa.add(setting)
324 self.sa.commit()
329 self.sa.commit()
@@ -411,42 +416,30 b' class DbManage(object):'
411
416
412 def create_user(self, username, password, email='', admin=False):
417 def create_user(self, username, password, email='', admin=False):
413 log.info('creating administrator user %s', username)
418 log.info('creating administrator user %s', username)
414 new_user = User()
419
415 new_user.username = username
420 form_data = dict(username=username,
416 new_user.password = get_crypt_password(password)
421 password=password,
417 new_user.api_key = generate_api_key(username)
422 active=True,
418 new_user.name = 'RhodeCode'
423 admin=admin,
419 new_user.lastname = 'Admin'
424 name='RhodeCode',
420 new_user.email = email
425 lastname='Admin',
421 new_user.admin = admin
426 email=email)
422 new_user.active = True
427 User.create(form_data)
423
428
424 try:
425 self.sa.add(new_user)
426 self.sa.commit()
427 except:
428 self.sa.rollback()
429 raise
430
429
431 def create_default_user(self):
430 def create_default_user(self):
432 log.info('creating default user')
431 log.info('creating default user')
433 #create default user for handling default permissions.
432 #create default user for handling default permissions.
434 def_user = User()
435 def_user.username = 'default'
436 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
437 def_user.api_key = generate_api_key('default')
438 def_user.name = 'Anonymous'
439 def_user.lastname = 'User'
440 def_user.email = 'anonymous@rhodecode.org'
441 def_user.admin = False
442 def_user.active = False
443 try:
444 self.sa.add(def_user)
445 self.sa.commit()
446 except:
447 self.sa.rollback()
448 raise
449
433
434 form_data = dict(username='default',
435 password=str(uuid.uuid1())[:8],
436 active=False,
437 admin=False,
438 name='Anonymous',
439 lastname='User',
440 email='anonymous@rhodecode.org')
441 User.create(form_data)
442
450 def create_permissions(self):
443 def create_permissions(self):
451 #module.(access|create|change|delete)_[name]
444 #module.(access|create|change|delete)_[name]
452 #module.(read|write|owner)
445 #module.(read|write|owner)
@@ -76,6 +76,14 b' def upgrade(migrate_engine):'
76 #==========================================================================
76 #==========================================================================
77 from rhodecode.model.db import Repository
77 from rhodecode.model.db import Repository
78
78
79 #ADD clone_uri column#
80
81 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False,
82 assert_unicode=None),
83 nullable=True, unique=False, default=None)
84
85 clone_uri.create(Repository().__table__)
86
79 #ADD downloads column#
87 #ADD downloads column#
80 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
88 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
81 enable_downloads.create(Repository().__table__)
89 enable_downloads.create(Repository().__table__)
@@ -92,21 +100,16 b' def upgrade(migrate_engine):'
92 group_id.create(Repository().__table__)
100 group_id.create(Repository().__table__)
93
101
94
102
95 #ADD clone_uri column#
96
97 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False,
98 assert_unicode=None),
99 nullable=True, unique=False, default=None)
100
101 clone_uri.create(Repository().__table__)
102
103
104 #==========================================================================
103 #==========================================================================
105 # Upgrade of `user_followings` table
104 # Upgrade of `user_followings` table
106 #==========================================================================
105 #==========================================================================
107
106
108 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
107 from rhodecode.model.db import UserFollowing
109 follows_from.create(Repository().__table__)
108
109 follows_from = Column('follows_from', DateTime(timezone=False),
110 nullable=True, unique=None,
111 default=datetime.datetime.now)
112 follows_from.create(UserFollowing().__table__)
110
113
111 return
114 return
112
115
@@ -6,8 +6,8 b''
6 Set of custom exceptions used in RhodeCode
6 Set of custom exceptions used in RhodeCode
7
7
8 :created_on: Nov 17, 2010
8 :created_on: Nov 17, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
13 # it under the terms of the GNU General Public License as published by
@@ -44,11 +44,11 b' class SimpleGitUploadPackHandler(dulserv'
44 get_tagged=self.get_tagged)
44 get_tagged=self.get_tagged)
45
45
46 # Do they want any objects?
46 # Do they want any objects?
47 if len(objects_iter) == 0:
47 if objects_iter is None or len(objects_iter) == 0:
48 return
48 return
49
49
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 dulserver.write_pack_data(dulserver.ProtocolFile(None, write),
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
52 objects_iter, len(objects_iter))
52 objects_iter, len(objects_iter))
53 messages = []
53 messages = []
54 messages.append('thank you for using rhodecode')
54 messages.append('thank you for using rhodecode')
@@ -71,8 +71,8 b' from paste.httpheaders import REMOTE_USE'
71
71
72 from rhodecode.lib import safe_str
72 from rhodecode.lib import safe_str
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
74 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
74 from rhodecode.lib.utils import invalidate_cache, is_valid_repo
75 from rhodecode.model.user import UserModel
75 from rhodecode.model.db import User
76
76
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
78
78
@@ -96,12 +96,10 b' class SimpleGit(object):'
96 def __init__(self, application, config):
96 def __init__(self, application, config):
97 self.application = application
97 self.application = application
98 self.config = config
98 self.config = config
99 #authenticate this git request using
99 # base path of repo locations
100 self.basepath = self.config['base_path']
101 #authenticate this mercurial request using authfunc
100 self.authenticate = AuthBasicAuthenticator('', authfunc)
102 self.authenticate = AuthBasicAuthenticator('', authfunc)
101 self.ipaddr = '0.0.0.0'
102 self.repo_name = None
103 self.username = None
104 self.action = None
105
103
106 def __call__(self, environ, start_response):
104 def __call__(self, environ, start_response):
107 if not is_git(environ):
105 if not is_git(environ):
@@ -109,31 +107,34 b' class SimpleGit(object):'
109
107
110 proxy_key = 'HTTP_X_REAL_IP'
108 proxy_key = 'HTTP_X_REAL_IP'
111 def_key = 'REMOTE_ADDR'
109 def_key = 'REMOTE_ADDR'
112 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
111 username = None
113 # skip passing error to error controller
112 # skip passing error to error controller
114 environ['pylons.status_code_redirect'] = True
113 environ['pylons.status_code_redirect'] = True
115
114
116 #======================================================================
115 #======================================================================
116 # EXTRACT REPOSITORY NAME FROM ENV
117 #======================================================================
118 try:
119 repo_name = self.__get_repository(environ)
120 log.debug('Extracted repo name is %s' % repo_name)
121 except:
122 return HTTPInternalServerError()(environ, start_response)
123
124 #======================================================================
117 # GET ACTION PULL or PUSH
125 # GET ACTION PULL or PUSH
118 #======================================================================
126 #======================================================================
119 self.action = self.__get_action(environ)
127 action = self.__get_action(environ)
120 try:
121 #==================================================================
122 # GET REPOSITORY NAME
123 #==================================================================
124 self.repo_name = self.__get_repository(environ)
125 except:
126 return HTTPInternalServerError()(environ, start_response)
127
128
128 #======================================================================
129 #======================================================================
129 # CHECK ANONYMOUS PERMISSION
130 # CHECK ANONYMOUS PERMISSION
130 #======================================================================
131 #======================================================================
131 if self.action in ['pull', 'push']:
132 if action in ['pull', 'push']:
132 anonymous_user = self.__get_user('default')
133 anonymous_user = self.__get_user('default')
133 self.username = anonymous_user.username
134 username = anonymous_user.username
134 anonymous_perm = self.__check_permission(self.action,
135 anonymous_perm = self.__check_permission(action,
135 anonymous_user,
136 anonymous_user,
136 self.repo_name)
137 repo_name)
137
138
138 if anonymous_perm is not True or anonymous_user.active is False:
139 if anonymous_perm is not True or anonymous_user.active is False:
139 if anonymous_perm is not True:
140 if anonymous_perm is not True:
@@ -162,56 +163,66 b' class SimpleGit(object):'
162 # BASIC AUTH
163 # BASIC AUTH
163 #==============================================================
164 #==============================================================
164
165
165 if self.action in ['pull', 'push']:
166 if action in ['pull', 'push']:
166 username = REMOTE_USER(environ)
167 username = REMOTE_USER(environ)
167 try:
168 try:
168 user = self.__get_user(username)
169 user = self.__get_user(username)
169 self.username = user.username
170 username = user.username
170 except:
171 except:
171 log.error(traceback.format_exc())
172 log.error(traceback.format_exc())
172 return HTTPInternalServerError()(environ,
173 return HTTPInternalServerError()(environ,
173 start_response)
174 start_response)
174
175
175 #check permissions for this repository
176 #check permissions for this repository
176 perm = self.__check_permission(self.action, user,
177 perm = self.__check_permission(action, user,
177 self.repo_name)
178 repo_name)
178 if perm is not True:
179 if perm is not True:
179 return HTTPForbidden()(environ, start_response)
180 return HTTPForbidden()(environ, start_response)
180
181
181 self.extras = {'ip': self.ipaddr,
182 extras = {'ip': ipaddr,
182 'username': self.username,
183 'username': username,
183 'action': self.action,
184 'action': action,
184 'repository': self.repo_name}
185 'repository': repo_name}
185
186
186 #===================================================================
187 #===================================================================
187 # GIT REQUEST HANDLING
188 # GIT REQUEST HANDLING
188 #===================================================================
189 #===================================================================
189 self.basepath = self.config['base_path']
190
190 self.repo_path = os.path.join(self.basepath, self.repo_name)
191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
191 #quick check if that dir exists...
192 log.debug('Repository path is %s' % repo_path)
192 if check_repo_fast(self.repo_name, self.basepath):
193
194 # quick check if that dir exists...
195 if is_valid_repo(repo_name, self.basepath) is False:
193 return HTTPNotFound()(environ, start_response)
196 return HTTPNotFound()(environ, start_response)
197
194 try:
198 try:
195 app = self.__make_app()
199 #invalidate cache on push
196 except:
200 if action == 'push':
201 self.__invalidate_cache(repo_name)
202
203 app = self.__make_app(repo_name, repo_path)
204 return app(environ, start_response)
205 except Exception:
197 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
198 return HTTPInternalServerError()(environ, start_response)
207 return HTTPInternalServerError()(environ, start_response)
199
208
200 #invalidate cache on push
209 def __make_app(self, repo_name, repo_path):
201 if self.action == 'push':
210 """
202 self.__invalidate_cache(self.repo_name)
211 Make an wsgi application using dulserver
212
213 :param repo_name: name of the repository
214 :param repo_path: full path to the repository
215 """
203
216
204 return app(environ, start_response)
217 _d = {'/' + repo_name: Repo(repo_path)}
205
206 def __make_app(self):
207 _d = {'/' + self.repo_name: Repo(self.repo_path)}
208 backend = dulserver.DictBackend(_d)
218 backend = dulserver.DictBackend(_d)
209 gitserve = HTTPGitApplication(backend)
219 gitserve = HTTPGitApplication(backend)
210
220
211 return gitserve
221 return gitserve
212
222
213 def __check_permission(self, action, user, repo_name):
223 def __check_permission(self, action, user, repo_name):
214 """Checks permissions using action (push/pull) user and repository
224 """
225 Checks permissions using action (push/pull) user and repository
215 name
226 name
216
227
217 :param action: push or pull action
228 :param action: push or pull action
@@ -235,7 +246,8 b' class SimpleGit(object):'
235 return True
246 return True
236
247
237 def __get_repository(self, environ):
248 def __get_repository(self, environ):
238 """Get's repository name out of PATH_INFO header
249 """
250 Get's repository name out of PATH_INFO header
239
251
240 :param environ: environ where PATH_INFO is stored
252 :param environ: environ where PATH_INFO is stored
241 """
253 """
@@ -250,7 +262,7 b' class SimpleGit(object):'
250 return repo_name
262 return repo_name
251
263
252 def __get_user(self, username):
264 def __get_user(self, username):
253 return UserModel().get_by_username(username, cache=True)
265 return User.get_by_username(username)
254
266
255 def __get_action(self, environ):
267 def __get_action(self, environ):
256 """Maps git request commands into a pull or push command.
268 """Maps git request commands into a pull or push command.
@@ -274,3 +286,4 b' class SimpleGit(object):'
274 invalidate the cache to see the changes right away but only for
286 invalidate the cache to see the changes right away but only for
275 push requests"""
287 push requests"""
276 invalidate_cache('get_repo_cached_%s' % repo_name)
288 invalidate_cache('get_repo_cached_%s' % repo_name)
289
@@ -29,8 +29,7 b' import logging'
29 import traceback
29 import traceback
30
30
31 from mercurial.error import RepoError
31 from mercurial.error import RepoError
32 from mercurial.hgweb import hgweb
32 from mercurial.hgweb import hgweb_mod
33 from mercurial.hgweb.request import wsgiapplication
34
33
35 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.auth.basic import AuthBasicAuthenticator
36 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
@@ -38,8 +37,8 b' from paste.httpheaders import REMOTE_USE'
38 from rhodecode.lib import safe_str
37 from rhodecode.lib import safe_str
39 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
40 from rhodecode.lib.utils import make_ui, invalidate_cache, \
39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
41 check_repo_fast, ui_sections
40 is_valid_repo, ui_sections
42 from rhodecode.model.user import UserModel
41 from rhodecode.model.db import User
43
42
44 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
45
44
@@ -61,12 +60,11 b' class SimpleHg(object):'
61 def __init__(self, application, config):
60 def __init__(self, application, config):
62 self.application = application
61 self.application = application
63 self.config = config
62 self.config = config
63 # base path of repo locations
64 self.basepath = self.config['base_path']
64 #authenticate this mercurial request using authfunc
65 #authenticate this mercurial request using authfunc
65 self.authenticate = AuthBasicAuthenticator('', authfunc)
66 self.authenticate = AuthBasicAuthenticator('', authfunc)
66 self.ipaddr = '0.0.0.0'
67 self.ipaddr = '0.0.0.0'
67 self.repo_name = None
68 self.username = None
69 self.action = None
70
68
71 def __call__(self, environ, start_response):
69 def __call__(self, environ, start_response):
72 if not is_mercurial(environ):
70 if not is_mercurial(environ):
@@ -74,31 +72,35 b' class SimpleHg(object):'
74
72
75 proxy_key = 'HTTP_X_REAL_IP'
73 proxy_key = 'HTTP_X_REAL_IP'
76 def_key = 'REMOTE_ADDR'
74 def_key = 'REMOTE_ADDR'
77 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
75 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
76
78 # skip passing error to error controller
77 # skip passing error to error controller
79 environ['pylons.status_code_redirect'] = True
78 environ['pylons.status_code_redirect'] = True
80
79
81 #======================================================================
80 #======================================================================
81 # EXTRACT REPOSITORY NAME FROM ENV
82 #======================================================================
83 try:
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
85 log.debug('Extracted repo name is %s' % repo_name)
86 except:
87 return HTTPInternalServerError()(environ, start_response)
88
89 #======================================================================
82 # GET ACTION PULL or PUSH
90 # GET ACTION PULL or PUSH
83 #======================================================================
91 #======================================================================
84 self.action = self.__get_action(environ)
92 action = self.__get_action(environ)
85 try:
86 #==================================================================
87 # GET REPOSITORY NAME
88 #==================================================================
89 self.repo_name = self.__get_repository(environ)
90 except:
91 return HTTPInternalServerError()(environ, start_response)
92
93
93 #======================================================================
94 #======================================================================
94 # CHECK ANONYMOUS PERMISSION
95 # CHECK ANONYMOUS PERMISSION
95 #======================================================================
96 #======================================================================
96 if self.action in ['pull', 'push']:
97 if action in ['pull', 'push']:
97 anonymous_user = self.__get_user('default')
98 anonymous_user = self.__get_user('default')
98 self.username = anonymous_user.username
99
99 anonymous_perm = self.__check_permission(self.action,
100 username = anonymous_user.username
101 anonymous_perm = self.__check_permission(action,
100 anonymous_user,
102 anonymous_user,
101 self.repo_name)
103 repo_name)
102
104
103 if anonymous_perm is not True or anonymous_user.active is False:
105 if anonymous_perm is not True or anonymous_user.active is False:
104 if anonymous_perm is not True:
106 if anonymous_perm is not True:
@@ -127,43 +129,52 b' class SimpleHg(object):'
127 # BASIC AUTH
129 # BASIC AUTH
128 #==============================================================
130 #==============================================================
129
131
130 if self.action in ['pull', 'push']:
132 if action in ['pull', 'push']:
131 #Removing realm from username
133 #Removing realm from username
132 username = REMOTE_USER(environ).partition('@')[0]
134 username = REMOTE_USER(environ).partition('@')[0]
133 try:
135 try:
134 user = self.__get_user(username)
136 user = self.__get_user(username)
135 if user is None:
137 if user is None:
136 return HTTPForbidden()(environ, start_response)
138 return HTTPForbidden()(environ, start_response)
137 self.username = user.username
139 username = user.username
138 except:
140 except:
139 log.error(traceback.format_exc())
141 log.error(traceback.format_exc())
140 return HTTPInternalServerError()(environ,
142 return HTTPInternalServerError()(environ,
141 start_response)
143 start_response)
142
144
143 #check permissions for this repository
145 #check permissions for this repository
144 perm = self.__check_permission(self.action, user,
146 perm = self.__check_permission(action, user,
145 self.repo_name)
147 repo_name)
146 if perm is not True:
148 if perm is not True:
147 return HTTPForbidden()(environ, start_response)
149 return HTTPForbidden()(environ, start_response)
148
150
149 self.extras = {'ip': self.ipaddr,
151 extras = {'ip': ipaddr,
150 'username': self.username,
152 'username': username,
151 'action': self.action,
153 'action': action,
152 'repository': self.repo_name}
154 'repository': repo_name}
153
155
154 #======================================================================
156 #======================================================================
155 # MERCURIAL REQUEST HANDLING
157 # MERCURIAL REQUEST HANDLING
156 #======================================================================
158 #======================================================================
157 environ['PATH_INFO'] = '/' # since we wrap into hgweb, reset the path
159
158 self.baseui = make_ui('db')
160 repo_path = safe_str(os.path.join(self.basepath, repo_name))
159 self.basepath = self.config['base_path']
161 log.debug('Repository path is %s' % repo_path)
160 self.repo_path = os.path.join(self.basepath, self.repo_name)
162
163 baseui = make_ui('db')
164 self.__inject_extras(repo_path, baseui, extras)
165
161
166
162 #quick check if that dir exists...
167 # quick check if that dir exists...
163 if check_repo_fast(self.repo_name, self.basepath):
168 if is_valid_repo(repo_name, self.basepath) is False:
164 return HTTPNotFound()(environ, start_response)
169 return HTTPNotFound()(environ, start_response)
170
165 try:
171 try:
166 app = wsgiapplication(self.__make_app)
172 #invalidate cache on push
173 if action == 'push':
174 self.__invalidate_cache(repo_name)
175
176 app = self.__make_app(repo_path, baseui, extras)
177 return app(environ, start_response)
167 except RepoError, e:
178 except RepoError, e:
168 if str(e).find('not found') != -1:
179 if str(e).find('not found') != -1:
169 return HTTPNotFound()(environ, start_response)
180 return HTTPNotFound()(environ, start_response)
@@ -171,19 +182,12 b' class SimpleHg(object):'
171 log.error(traceback.format_exc())
182 log.error(traceback.format_exc())
172 return HTTPInternalServerError()(environ, start_response)
183 return HTTPInternalServerError()(environ, start_response)
173
184
174 #invalidate cache on push
185 def __make_app(self, repo_name, baseui, extras):
175 if self.action == 'push':
176 self.__invalidate_cache(self.repo_name)
177
178 return app(environ, start_response)
179
180 def __make_app(self):
181 """
186 """
182 Make an wsgi application using hgweb, and inject generated baseui
187 Make an wsgi application using hgweb, and inject generated baseui
183 instance, additionally inject some extras into ui object
188 instance, additionally inject some extras into ui object
184 """
189 """
185 self.__inject_extras(self.baseui, self.extras)
190 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
186 return hgweb(str(self.repo_path), baseui=self.baseui)
187
191
188
192
189 def __check_permission(self, action, user, repo_name):
193 def __check_permission(self, action, user, repo_name):
@@ -228,7 +232,7 b' class SimpleHg(object):'
228 return repo_name
232 return repo_name
229
233
230 def __get_user(self, username):
234 def __get_user(self, username):
231 return UserModel().get_by_username(username, cache=True)
235 return User.get_by_username(username)
232
236
233 def __get_action(self, environ):
237 def __get_action(self, environ):
234 """
238 """
@@ -257,7 +261,7 b' class SimpleHg(object):'
257 push requests"""
261 push requests"""
258 invalidate_cache('get_repo_cached_%s' % repo_name)
262 invalidate_cache('get_repo_cached_%s' % repo_name)
259
263
260 def __inject_extras(self, baseui, extras={}):
264 def __inject_extras(self, repo_path, baseui, extras={}):
261 """
265 """
262 Injects some extra params into baseui instance
266 Injects some extra params into baseui instance
263
267
@@ -267,7 +271,10 b' class SimpleHg(object):'
267 :param extras: dict with extra params to put into baseui
271 :param extras: dict with extra params to put into baseui
268 """
272 """
269
273
270 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
274 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
275
276 # make our hgweb quiet so it doesn't print output
277 baseui.setconfig('ui', 'quiet', 'true')
271
278
272 #inject some additional parameters that will be available in ui
279 #inject some additional parameters that will be available in ui
273 #for hooks
280 #for hooks
@@ -281,3 +288,4 b' class SimpleHg(object):'
281 for section in ui_sections:
288 for section in ui_sections:
282 for k, v in repoui.configitems(section):
289 for k, v in repoui.configitems(section):
283 baseui.setconfig(section, k, v)
290 baseui.setconfig(section, k, v)
291
@@ -6,9 +6,21 b''
6 Simple smtp mailer used in RhodeCode
6 Simple smtp mailer used in RhodeCode
7
7
8 :created_on: Sep 13, 2010
8 :created_on: Sep 13, 2010
9 :copyright: (c) 2011 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
12
24
13 import logging
25 import logging
14 import smtplib
26 import smtplib
@@ -33,23 +33,21 b' from os.path import dirname as dn, join '
33
33
34 from paste.script.command import Command, BadCommand
34 from paste.script.command import Command, BadCommand
35
35
36 from UserDict import DictMixin
36 from mercurial import ui, config
37
38 from mercurial import ui, config, hg
39 from mercurial.error import RepoError
40
37
41 from webhelpers.text import collapse, remove_formatting, strip_tags
38 from webhelpers.text import collapse, remove_formatting, strip_tags
42
39
40 from vcs import get_backend
43 from vcs.backends.base import BaseChangeset
41 from vcs.backends.base import BaseChangeset
44 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
45 from vcs import get_backend
43 from vcs.utils.helpers import get_scm
44 from vcs.exceptions import VCSError
46
45
47 from rhodecode.model import meta
46 from rhodecode.model import meta
48 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
49 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
50 RhodeCodeSettings
49 RhodeCodeSettings
51 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.user import UserModel
53
51
54 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
55
53
@@ -111,11 +109,10 b' def action_logger(user, action, repo, ip'
111 sa = meta.Session()
109 sa = meta.Session()
112
110
113 try:
111 try:
114 um = UserModel()
115 if hasattr(user, 'user_id'):
112 if hasattr(user, 'user_id'):
116 user_obj = user
113 user_obj = user
117 elif isinstance(user, basestring):
114 elif isinstance(user, basestring):
118 user_obj = um.get_by_username(user, cache=False)
115 user_obj = User.get_by_username(user)
119 else:
116 else:
120 raise Exception('You have to provide user object or username')
117 raise Exception('You have to provide user object or username')
121
118
@@ -183,37 +180,40 b' def get_repos(path, recursive=False):'
183 return _get_repos(path)
180 return _get_repos(path)
184
181
185
182
186 def check_repo_fast(repo_name, base_path):
183 def is_valid_repo(repo_name, base_path):
187 """
184 """
188 Check given path for existence of directory
185 Returns True if given path is a valid repository False otherwise
189 :param repo_name:
186 :param repo_name:
190 :param base_path:
187 :param base_path:
191
188
192 :return False: if this directory is present
189 :return True: if given path is a valid repository
193 """
190 """
194 if os.path.isdir(os.path.join(base_path, repo_name)):
191 full_path = os.path.join(base_path, repo_name)
195 return False
196 return True
197
198
199 def check_repo(repo_name, base_path, verify=True):
200
201 repo_path = os.path.join(base_path, repo_name)
202
192
203 try:
193 try:
204 if not check_repo_fast(repo_name, base_path):
194 get_scm(full_path)
205 return False
195 return True
206 r = hg.repository(ui.ui(), repo_path)
196 except VCSError:
207 if verify:
208 hg.verify(r)
209 #here we hnow that repo exists it was verified
210 log.info('%s repo is already created', repo_name)
211 return False
197 return False
212 except RepoError:
198
213 #it means that there is no valid repo there...
199 def is_valid_repos_group(repos_group_name, base_path):
214 log.info('%s repo is free for creation', repo_name)
200 """
201 Returns True if given path is a repos group False otherwise
202
203 :param repo_name:
204 :param base_path:
205 """
206 full_path = os.path.join(base_path, repos_group_name)
207
208 # check if it's not a repo
209 if is_valid_repo(repos_group_name, base_path):
210 return False
211
212 # check if it's a valid path
213 if os.path.isdir(full_path):
215 return True
214 return True
216
215
216 return False
217
217
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
219 while True:
219 while True:
@@ -38,12 +38,14 b' from beaker.cache import cache_region, r'
38
38
39 from vcs import get_backend
39 from vcs import get_backend
40 from vcs.utils.helpers import get_scm
40 from vcs.utils.helpers import get_scm
41 from vcs.exceptions import RepositoryError, VCSError
41 from vcs.exceptions import VCSError
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.nodes import FileNode
44
43
44 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
45 generate_api_key
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib import str2bool, json, safe_str, get_changeset_safe
47 from rhodecode.lib.compat import json
48
47 from rhodecode.model.meta import Base, Session
49 from rhodecode.model.meta import Base, Session
48 from rhodecode.model.caching_query import FromCache
50 from rhodecode.model.caching_query import FromCache
49
51
@@ -279,17 +281,16 b' class User(Base, BaseModel):'
279 return self.__class__.__name__
281 return self.__class__.__name__
280
282
281 @classmethod
283 @classmethod
282 def by_username(cls, username, case_insensitive=False):
284 def get_by_username(cls, username, case_insensitive=False):
283 if case_insensitive:
285 if case_insensitive:
284 return Session.query(cls).filter(cls.username.like(username)).one()
286 return Session.query(cls).filter(cls.username.like(username)).scalar()
285 else:
287 else:
286 return Session.query(cls).filter(cls.username == username).one()
288 return Session.query(cls).filter(cls.username == username).scalar()
287
289
288 @classmethod
290 @classmethod
289 def get_by_api_key(cls, api_key):
291 def get_by_api_key(cls, api_key):
290 return Session.query(cls).filter(cls.api_key == api_key).one()
292 return Session.query(cls).filter(cls.api_key == api_key).one()
291
293
292
293 def update_lastlogin(self):
294 def update_lastlogin(self):
294 """Update user lastlogin"""
295 """Update user lastlogin"""
295
296
@@ -298,6 +299,25 b' class User(Base, BaseModel):'
298 Session.commit()
299 Session.commit()
299 log.debug('updated user %s lastlogin', self.username)
300 log.debug('updated user %s lastlogin', self.username)
300
301
302 @classmethod
303 def create(cls, form_data):
304 from rhodecode.lib.auth import get_crypt_password
305
306 try:
307 new_user = cls()
308 for k, v in form_data.items():
309 if k == 'password':
310 v = get_crypt_password(v)
311 setattr(new_user, k, v)
312
313 new_user.api_key = generate_api_key(form_data['username'])
314 Session.add(new_user)
315 Session.commit()
316 return new_user
317 except:
318 log.error(traceback.format_exc())
319 Session.rollback()
320 raise
301
321
302 class UserLog(Base, BaseModel):
322 class UserLog(Base, BaseModel):
303 __tablename__ = 'user_logs'
323 __tablename__ = 'user_logs'
@@ -362,6 +382,7 b' class UsersGroup(Base, BaseModel):'
362
382
363 Session.add(new_users_group)
383 Session.add(new_users_group)
364 Session.commit()
384 Session.commit()
385 return new_users_group
365 except:
386 except:
366 log.error(traceback.format_exc())
387 log.error(traceback.format_exc())
367 Session.rollback()
388 Session.rollback()
@@ -465,7 +486,7 b' class Repository(Base, BaseModel):'
465 self.repo_id, self.repo_name)
486 self.repo_id, self.repo_name)
466
487
467 @classmethod
488 @classmethod
468 def by_repo_name(cls, repo_name):
489 def get_by_repo_name(cls, repo_name):
469 q = Session.query(cls).filter(cls.repo_name == repo_name)
490 q = Session.query(cls).filter(cls.repo_name == repo_name)
470
491
471 q = q.options(joinedload(Repository.fork))\
492 q = q.options(joinedload(Repository.fork))\
@@ -478,6 +499,17 b' class Repository(Base, BaseModel):'
478 def get_repo_forks(cls, repo_id):
499 def get_repo_forks(cls, repo_id):
479 return Session.query(cls).filter(Repository.fork_id == repo_id)
500 return Session.query(cls).filter(Repository.fork_id == repo_id)
480
501
502 @classmethod
503 def base_path(cls):
504 """
505 Returns base path when all repos are stored
506
507 :param cls:
508 """
509 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
510 q.options(FromCache("sql_cache_short", "repository_repo_path"))
511 return q.one().ui_value
512
481 @property
513 @property
482 def just_name(self):
514 def just_name(self):
483 return self.repo_name.split(os.sep)[-1]
515 return self.repo_name.split(os.sep)[-1]
@@ -549,6 +581,19 b' class Repository(Base, BaseModel):'
549
581
550 return baseui
582 return baseui
551
583
584 @classmethod
585 def is_valid(cls, repo_name):
586 """
587 returns True if given repo name is a valid filesystem repository
588
589 @param cls:
590 @param repo_name:
591 """
592 from rhodecode.lib.utils import is_valid_repo
593
594 return is_valid_repo(repo_name, cls.base_path())
595
596
552 #==========================================================================
597 #==========================================================================
553 # SCM PROPERTIES
598 # SCM PROPERTIES
554 #==========================================================================
599 #==========================================================================
@@ -32,6 +32,7 b' from formencode.validators import Unicod'
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34
34
35 from rhodecode.config.routing import ADMIN_PREFIX
35 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.exceptions import LdapImportError
38 from rhodecode.lib.exceptions import LdapImportError
@@ -70,8 +71,7 b' def ValidUsername(edit, old_data):'
70 old_un = UserModel().get(old_data.get('user_id')).username
71 old_un = UserModel().get(old_data.get('user_id')).username
71
72
72 if old_un != value or not edit:
73 if old_un != value or not edit:
73 if UserModel().get_by_username(value, cache=False,
74 if User.get_by_username(value, case_insensitive=True):
74 case_insensitive=True):
75 raise formencode.Invalid(_('This username already '
75 raise formencode.Invalid(_('This username already '
76 'exists') , value, state)
76 'exists') , value, state)
77
77
@@ -206,7 +206,7 b' class ValidAuth(formencode.validators.Fa'
206 def validate_python(self, value, state):
206 def validate_python(self, value, state):
207 password = value['password']
207 password = value['password']
208 username = value['username']
208 username = value['username']
209 user = UserModel().get_by_username(username)
209 user = User.get_by_username(username)
210
210
211 if authenticate(username, password):
211 if authenticate(username, password):
212 return value
212 return value
@@ -241,7 +241,7 b' def ValidRepoName(edit, old_data):'
241 repo_name = value.get('repo_name')
241 repo_name = value.get('repo_name')
242
242
243 slug = repo_name_slug(repo_name)
243 slug = repo_name_slug(repo_name)
244 if slug in ['_admin', '']:
244 if slug in [ADMIN_PREFIX, '']:
245 e_dict = {'repo_name': _('This repository name is disallowed')}
245 e_dict = {'repo_name': _('This repository name is disallowed')}
246 raise formencode.Invalid('', value, state, error_dict=e_dict)
246 raise formencode.Invalid('', value, state, error_dict=e_dict)
247
247
@@ -283,6 +283,19 b' def ValidRepoName(edit, old_data):'
283 def ValidForkName():
283 def ValidForkName():
284 class _ValidForkName(formencode.validators.FancyValidator):
284 class _ValidForkName(formencode.validators.FancyValidator):
285 def to_python(self, value, state):
285 def to_python(self, value, state):
286
287 repo_name = value.get('fork_name')
288
289 slug = repo_name_slug(repo_name)
290 if slug in [ADMIN_PREFIX, '']:
291 e_dict = {'repo_name': _('This repository name is disallowed')}
292 raise formencode.Invalid('', value, state, error_dict=e_dict)
293
294 if RepoModel().get_by_repo_name(repo_name):
295 e_dict = {'fork_name':_('This repository '
296 'already exists')}
297 raise formencode.Invalid('', value, state,
298 error_dict=e_dict)
286 return value
299 return value
287 return _ValidForkName
300 return _ValidForkName
288
301
@@ -102,7 +102,7 b' class RepoModel(BaseModel):'
102 for member, perm, member_type in form_data['perms_updates']:
102 for member, perm, member_type in form_data['perms_updates']:
103 if member_type == 'user':
103 if member_type == 'user':
104 r2p = self.sa.query(RepoToPerm)\
104 r2p = self.sa.query(RepoToPerm)\
105 .filter(RepoToPerm.user == User.by_username(member))\
105 .filter(RepoToPerm.user == User.get_by_username(member))\
106 .filter(RepoToPerm.repository == cur_repo)\
106 .filter(RepoToPerm.repository == cur_repo)\
107 .one()
107 .one()
108
108
@@ -127,7 +127,7 b' class RepoModel(BaseModel):'
127 if member_type == 'user':
127 if member_type == 'user':
128 r2p = RepoToPerm()
128 r2p = RepoToPerm()
129 r2p.repository = cur_repo
129 r2p.repository = cur_repo
130 r2p.user = User.by_username(member)
130 r2p.user = User.get_by_username(member)
131
131
132 r2p.permission = self.sa.query(Permission)\
132 r2p.permission = self.sa.query(Permission)\
133 .filter(Permission.
133 .filter(Permission.
@@ -147,7 +147,7 b' class RepoModel(BaseModel):'
147 #update current repo
147 #update current repo
148 for k, v in form_data.items():
148 for k, v in form_data.items():
149 if k == 'user':
149 if k == 'user':
150 cur_repo.user = User.by_username(v)
150 cur_repo.user = User.get_by_username(v)
151 elif k == 'repo_name':
151 elif k == 'repo_name':
152 cur_repo.repo_name = form_data['repo_name_full']
152 cur_repo.repo_name = form_data['repo_name_full']
153 elif k == 'repo_group':
153 elif k == 'repo_group':
@@ -192,6 +192,9 b' class RepoModel(BaseModel):'
192 if k == 'repo_group':
192 if k == 'repo_group':
193 k = 'group_id'
193 k = 'group_id'
194
194
195 if k == 'description':
196 v = v or repo_name
197
195 setattr(new_repo, k, v)
198 setattr(new_repo, k, v)
196
199
197 if fork:
200 if fork:
@@ -205,8 +208,7 b' class RepoModel(BaseModel):'
205 #create default permission
208 #create default permission
206 repo_to_perm = RepoToPerm()
209 repo_to_perm = RepoToPerm()
207 default = 'repository.read'
210 default = 'repository.read'
208 for p in UserModel(self.sa).get_by_username('default',
211 for p in User.get_by_username('default').user_perms:
209 cache=False).user_perms:
210 if p.permission.permission_name.startswith('repository.'):
212 if p.permission.permission_name.startswith('repository.'):
211 default = p.permission.permission_name
213 default = p.permission.permission_name
212 break
214 break
@@ -218,8 +220,7 b' class RepoModel(BaseModel):'
218 .one().permission_id
220 .one().permission_id
219
221
220 repo_to_perm.repository = new_repo
222 repo_to_perm.repository = new_repo
221 repo_to_perm.user_id = UserModel(self.sa)\
223 repo_to_perm.user_id = User.get_by_username('default').user_id
222 .get_by_username('default', cache=False).user_id
223
224
224 self.sa.add(repo_to_perm)
225 self.sa.add(repo_to_perm)
225
226
@@ -301,7 +302,7 b' class RepoModel(BaseModel):'
301 :param parent_id:
302 :param parent_id:
302 :param clone_uri:
303 :param clone_uri:
303 """
304 """
304 from rhodecode.lib.utils import check_repo
305 from rhodecode.lib.utils import is_valid_repo
305
306
306 if new_parent_id:
307 if new_parent_id:
307 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
308 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
@@ -312,7 +313,7 b' class RepoModel(BaseModel):'
312 repo_path = os.path.join(*map(lambda x:safe_str(x),
313 repo_path = os.path.join(*map(lambda x:safe_str(x),
313 [self.repos_path, new_parent_path, repo_name]))
314 [self.repos_path, new_parent_path, repo_name]))
314
315
315 if check_repo(repo_path, self.repos_path):
316 if is_valid_repo(repo_path, self.repos_path) is False:
316 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
317 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
317 clone_uri)
318 clone_uri)
318 backend = get_backend(alias)
319 backend = get_backend(alias)
@@ -332,8 +333,8 b' class RepoModel(BaseModel):'
332 old_path = os.path.join(self.repos_path, old)
333 old_path = os.path.join(self.repos_path, old)
333 new_path = os.path.join(self.repos_path, new)
334 new_path = os.path.join(self.repos_path, new)
334 if os.path.isdir(new_path):
335 if os.path.isdir(new_path):
335 raise Exception('Was trying to rename to already existing dir %s',
336 raise Exception('Was trying to rename to already existing dir %s' \
336 new_path)
337 % new_path)
337 shutil.move(old_path, new_path)
338 shutil.move(old_path, new_path)
338
339
339 def __delete_repo(self, repo):
340 def __delete_repo(self, repo):
@@ -22,7 +22,6 b''
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
26 import time
25 import time
27 import traceback
26 import traceback
28 import logging
27 import logging
@@ -41,9 +40,8 b' from rhodecode.lib.auth import HasRepoPe'
41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
40 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 action_logger, EmptyChangeset
41 action_logger, EmptyChangeset
43 from rhodecode.model import BaseModel
42 from rhodecode.model import BaseModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
43 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 UserFollowing, UserLog
44 UserFollowing, UserLog, User
47
45
48 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
49
47
@@ -283,7 +281,7 b' class ScmModel(BaseModel):'
283 return f is not None
281 return f is not None
284
282
285 def is_following_user(self, username, user_id, cache=False):
283 def is_following_user(self, username, user_id, cache=False):
286 u = UserModel(self.sa).get_by_username(username)
284 u = User.get_by_username(username)
287
285
288 f = self.sa.query(UserFollowing)\
286 f = self.sa.query(UserFollowing)\
289 .filter(UserFollowing.follows_user == u)\
287 .filter(UserFollowing.follows_user == u)\
@@ -293,20 +291,24 b' class ScmModel(BaseModel):'
293
291
294 def get_followers(self, repo_id):
292 def get_followers(self, repo_id):
295 if not isinstance(repo_id, int):
293 if not isinstance(repo_id, int):
296 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
294 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
297
295
298 return self.sa.query(UserFollowing)\
296 return self.sa.query(UserFollowing)\
299 .filter(UserFollowing.follows_repo_id == repo_id).count()
297 .filter(UserFollowing.follows_repo_id == repo_id).count()
300
298
301 def get_forks(self, repo_id):
299 def get_forks(self, repo_id):
302 if not isinstance(repo_id, int):
300 if not isinstance(repo_id, int):
303 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
301 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
304
302
305 return self.sa.query(Repository)\
303 return self.sa.query(Repository)\
306 .filter(Repository.fork_id == repo_id).count()
304 .filter(Repository.fork_id == repo_id).count()
307
305
308 def pull_changes(self, repo_name, username):
306 def pull_changes(self, repo_name, username):
309 dbrepo = Repository.by_repo_name(repo_name)
307 dbrepo = Repository.get_by_repo_name(repo_name)
308 clone_uri = dbrepo.clone_uri
309 if not clone_uri:
310 raise Exception("This repository doesn't have a clone uri")
311
310 repo = dbrepo.scm_instance
312 repo = dbrepo.scm_instance
311 try:
313 try:
312 extras = {'ip': '',
314 extras = {'ip': '',
@@ -318,13 +320,12 b' class ScmModel(BaseModel):'
318 for k, v in extras.items():
320 for k, v in extras.items():
319 repo._repo.ui.setconfig('rhodecode_extras', k, v)
321 repo._repo.ui.setconfig('rhodecode_extras', k, v)
320
322
321 repo.pull(dbrepo.clone_uri)
323 repo.pull(clone_uri)
322 self.mark_for_invalidation(repo_name)
324 self.mark_for_invalidation(repo_name)
323 except:
325 except:
324 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
325 raise
327 raise
326
328
327
328 def commit_change(self, repo, repo_name, cs, user, author, message, content,
329 def commit_change(self, repo, repo_name, cs, user, author, message, content,
329 f_path):
330 f_path):
330
331
@@ -360,12 +361,12 b' class ScmModel(BaseModel):'
360 from vcs.backends.git import GitInMemoryChangeset as IMC
361 from vcs.backends.git import GitInMemoryChangeset as IMC
361 # decoding here will force that we have proper encoded values
362 # decoding here will force that we have proper encoded values
362 # in any other case this will throw exceptions and deny commit
363 # in any other case this will throw exceptions and deny commit
363
364
364 if isinstance(content,(basestring,)):
365 if isinstance(content, (basestring,)):
365 content = safe_str(content)
366 content = safe_str(content)
366 elif isinstance(content,file):
367 elif isinstance(content, file):
367 content = content.read()
368 content = content.read()
368
369
369 message = safe_str(message)
370 message = safe_str(message)
370 path = safe_str(f_path)
371 path = safe_str(f_path)
371 author = safe_str(author)
372 author = safe_str(author)
@@ -28,6 +28,7 b' import traceback'
28
28
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 from rhodecode.lib import safe_unicode
31 from rhodecode.model import BaseModel
32 from rhodecode.model import BaseModel
32 from rhodecode.model.caching_query import FromCache
33 from rhodecode.model.caching_query import FromCache
33 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
34 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
@@ -111,7 +112,7 b' class UserModel(BaseModel):'
111 new_user.api_key = generate_api_key(username)
112 new_user.api_key = generate_api_key(username)
112 new_user.email = attrs['email']
113 new_user.email = attrs['email']
113 new_user.active = True
114 new_user.active = True
114 new_user.ldap_dn = user_dn
115 new_user.ldap_dn = safe_unicode(user_dn)
115 new_user.name = attrs['name']
116 new_user.name = attrs['name']
116 new_user.lastname = attrs['lastname']
117 new_user.lastname = attrs['lastname']
117
118
@@ -1466,7 +1466,7 b' clear:both;'
1466 overflow:hidden;
1466 overflow:hidden;
1467 text-align:right;
1467 text-align:right;
1468 margin:0;
1468 margin:0;
1469 padding:10px 14px 3px 5px;
1469 padding:10px 14px 0px 5px;
1470 }
1470 }
1471
1471
1472 #quick_login div.form div.links {
1472 #quick_login div.form div.links {
@@ -2555,7 +2555,7 b' border-top:1px solid #DDD;'
2555 border-left:1px solid #c6c6c6;
2555 border-left:1px solid #c6c6c6;
2556 border-right:1px solid #DDD;
2556 border-right:1px solid #DDD;
2557 border-bottom:1px solid #c6c6c6;
2557 border-bottom:1px solid #c6c6c6;
2558 color:#515151;
2558 color:#515151 !important;
2559 outline:none;
2559 outline:none;
2560 margin:0;
2560 margin:0;
2561 padding:6px 12px;
2561 padding:6px 12px;
@@ -246,7 +246,7 b' class TestLoginController(TestController'
246
246
247 # GOOD KEY
247 # GOOD KEY
248
248
249 key = User.by_username(username).api_key
249 key = User.get_by_username(username).api_key
250
250
251 response = self.app.get(url(controller='login',
251 response = self.app.get(url(controller='login',
252 action='password_reset_confirmation',
252 action='password_reset_confirmation',
@@ -41,7 +41,7 b' class TestSummaryController(TestControll'
41
41
42
42
43 def _enable_stats(self):
43 def _enable_stats(self):
44 r = Repository.by_repo_name(HG_REPO)
44 r = Repository.get_by_repo_name(HG_REPO)
45 r.enable_statistics = True
45 r.enable_statistics = True
46 self.sa.add(r)
46 self.sa.add(r)
47 self.sa.commit()
47 self.sa.commit()
@@ -102,7 +102,7 b' def test_files_walk(limit=100):'
102
102
103 repo = vcs.get_repo(jn(PROJECT_PATH, PROJECT))
103 repo = vcs.get_repo(jn(PROJECT_PATH, PROJECT))
104
104
105 from rhodecode.lib.oset import OrderedSet
105 from rhodecode.lib.compat import OrderedSet
106
106
107 paths_ = OrderedSet([''])
107 paths_ = OrderedSet([''])
108 try:
108 try:
@@ -6,13 +6,28 b''
6 Test suite for making push/pull operations
6 Test suite for making push/pull operations
7
7
8 :created_on: Dec 30, 2010
8 :created_on: Dec 30, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
12
24
13 import os
25 import os
26 import time
27 import sys
14 import shutil
28 import shutil
15 import logging
29 import logging
30
16 from os.path import join as jn
31 from os.path import join as jn
17 from os.path import dirname as dn
32 from os.path import dirname as dn
18
33
@@ -26,7 +41,7 b' from sqlalchemy import engine_from_confi'
26 from rhodecode.lib.utils import add_cache
41 from rhodecode.lib.utils import add_cache
27 from rhodecode.model import init_model
42 from rhodecode.model import init_model
28 from rhodecode.model import meta
43 from rhodecode.model import meta
29 from rhodecode.model.db import User, Repository
44 from rhodecode.model.db import User, Repository, UserLog
30 from rhodecode.lib.auth import get_crypt_password
45 from rhodecode.lib.auth import get_crypt_password
31
46
32 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
47 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
@@ -41,7 +56,8 b' add_cache(conf)'
41 USER = 'test_admin'
56 USER = 'test_admin'
42 PASS = 'test12'
57 PASS = 'test12'
43 HOST = '127.0.0.1:5000'
58 HOST = '127.0.0.1:5000'
44 DEBUG = True
59 DEBUG = True if sys.argv[1:] else False
60 print 'DEBUG:', DEBUG
45 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
46
62
47
63
@@ -64,28 +80,44 b' class Command(object):'
64 print stdout, stderr
80 print stdout, stderr
65 return stdout, stderr
81 return stdout, stderr
66
82
83
84 def test_wrapp(func):
85
86 def __wrapp(*args, **kwargs):
87 print '>>>%s' % func.__name__
88 try:
89 res = func(*args, **kwargs)
90 except Exception, e:
91 print ('###############\n-'
92 '--%s failed %s--\n'
93 '###############\n' % (func.__name__, e))
94 sys.exit()
95 print '++OK++'
96 return res
97 return __wrapp
98
67 def get_session():
99 def get_session():
68 engine = engine_from_config(conf, 'sqlalchemy.db1.')
100 engine = engine_from_config(conf, 'sqlalchemy.db1.')
69 init_model(engine)
101 init_model(engine)
70 sa = meta.Session()
102 sa = meta.Session
71 return sa
103 return sa
72
104
73
105
74 def create_test_user(force=True):
106 def create_test_user(force=True):
75 print 'creating test user'
107 print '\tcreating test user'
76 sa = get_session()
108 sa = get_session()
77
109
78 user = sa.query(User).filter(User.username == USER).scalar()
110 user = sa.query(User).filter(User.username == USER).scalar()
79
111
80 if force and user is not None:
112 if force and user is not None:
81 print 'removing current user'
113 print '\tremoving current user'
82 for repo in sa.query(Repository).filter(Repository.user == user).all():
114 for repo in sa.query(Repository).filter(Repository.user == user).all():
83 sa.delete(repo)
115 sa.delete(repo)
84 sa.delete(user)
116 sa.delete(user)
85 sa.commit()
117 sa.commit()
86
118
87 if user is None or force:
119 if user is None or force:
88 print 'creating new one'
120 print '\tcreating new one'
89 new_usr = User()
121 new_usr = User()
90 new_usr.username = USER
122 new_usr.username = USER
91 new_usr.password = get_crypt_password(PASS)
123 new_usr.password = get_crypt_password(PASS)
@@ -97,7 +129,7 b' def create_test_user(force=True):'
97 sa.add(new_usr)
129 sa.add(new_usr)
98 sa.commit()
130 sa.commit()
99
131
100 print 'done'
132 print '\tdone'
101
133
102
134
103 def create_test_repo(force=True):
135 def create_test_repo(force=True):
@@ -112,7 +144,7 b' def create_test_repo(force=True):'
112 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
144 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
113
145
114 if repo is None:
146 if repo is None:
115 print 'repo not found creating'
147 print '\trepo not found creating'
116
148
117 form_data = {'repo_name':HG_REPO,
149 form_data = {'repo_name':HG_REPO,
118 'repo_type':'hg',
150 'repo_type':'hg',
@@ -126,19 +158,27 b' def create_test_repo(force=True):'
126 def set_anonymous_access(enable=True):
158 def set_anonymous_access(enable=True):
127 sa = get_session()
159 sa = get_session()
128 user = sa.query(User).filter(User.username == 'default').one()
160 user = sa.query(User).filter(User.username == 'default').one()
161 sa.expire(user)
129 user.active = enable
162 user.active = enable
130 sa.add(user)
163 sa.add(user)
131 sa.commit()
164 sa.commit()
165 sa.remove()
166 import time;time.sleep(3)
167 print '\tanonymous access is now:', enable
168
132
169
133 def get_anonymous_access():
170 def get_anonymous_access():
134 sa = get_session()
171 sa = get_session()
135 return sa.query(User).filter(User.username == 'default').one().active
172 obj1 = sa.query(User).filter(User.username == 'default').one()
173 sa.expire(obj1)
174 return obj1.active
136
175
137
176
138 #==============================================================================
177 #==============================================================================
139 # TESTS
178 # TESTS
140 #==============================================================================
179 #==============================================================================
141 def test_clone(no_errors=False):
180 @test_wrapp
181 def test_clone_with_credentials(no_errors=False):
142 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
182 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
143
183
144 try:
184 try:
@@ -148,6 +188,12 b' def test_clone(no_errors=False):'
148 except OSError:
188 except OSError:
149 raise
189 raise
150
190
191 print '\tchecking if anonymous access is enabled'
192 anonymous_access = get_anonymous_access()
193 if anonymous_access:
194 print '\tenabled, disabling it '
195 set_anonymous_access(enable=False)
196 time.sleep(1)
151
197
152 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
198 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
153 {'user':USER,
199 {'user':USER,
@@ -163,8 +209,8 b' def test_clone(no_errors=False):'
163 assert """abort""" not in stderr , 'got error from clone'
209 assert """abort""" not in stderr , 'got error from clone'
164
210
165
211
166
212 @test_wrapp
167 def test_clone_anonymous_ok():
213 def test_clone_anonymous():
168 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
214 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
169
215
170 try:
216 try:
@@ -175,11 +221,12 b' def test_clone_anonymous_ok():'
175 raise
221 raise
176
222
177
223
178 print 'checking if anonymous access is enabled'
224 print '\tchecking if anonymous access is enabled'
179 anonymous_access = get_anonymous_access()
225 anonymous_access = get_anonymous_access()
180 if not anonymous_access:
226 if not anonymous_access:
181 print 'not enabled, enabling it '
227 print '\tnot enabled, enabling it '
182 set_anonymous_access(enable=True)
228 set_anonymous_access(enable=True)
229 time.sleep(1)
183
230
184 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
231 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
185 {'user':USER,
232 {'user':USER,
@@ -189,18 +236,16 b' def test_clone_anonymous_ok():'
189 'dest':path}
236 'dest':path}
190
237
191 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
238 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
192 print stdout, stderr
193
194
239
195 assert """adding file changes""" in stdout, 'no messages about cloning'
240 assert """adding file changes""" in stdout, 'no messages about cloning'
196 assert """abort""" not in stderr , 'got error from clone'
241 assert """abort""" not in stderr , 'got error from clone'
197
242
198 #disable if it was enabled
243 #disable if it was enabled
199 if not anonymous_access:
244 if not anonymous_access:
200 print 'disabling anonymous access'
245 print '\tdisabling anonymous access'
201 set_anonymous_access(enable=False)
246 set_anonymous_access(enable=False)
202
247
203
248 @test_wrapp
204 def test_clone_wrong_credentials():
249 def test_clone_wrong_credentials():
205 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
250 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
206
251
@@ -211,6 +256,11 b' def test_clone_wrong_credentials():'
211 except OSError:
256 except OSError:
212 raise
257 raise
213
258
259 print '\tchecking if anonymous access is enabled'
260 anonymous_access = get_anonymous_access()
261 if anonymous_access:
262 print '\tenabled, disabling it '
263 set_anonymous_access(enable=False)
214
264
215 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
265 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
216 {'user':USER + 'error',
266 {'user':USER + 'error',
@@ -221,12 +271,14 b' def test_clone_wrong_credentials():'
221
271
222 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
272 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
223
273
224 assert """abort: authorization failed""" in stderr , 'no error from wrong credentials'
274 if not """abort: authorization failed""" in stderr:
275 raise Exception('Failure')
225
276
226
277 @test_wrapp
227 def test_pull():
278 def test_pull():
228 pass
279 pass
229
280
281 @test_wrapp
230 def test_push_modify_file(f_name='setup.py'):
282 def test_push_modify_file(f_name='setup.py'):
231 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
283 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
232 modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
284 modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
@@ -239,10 +291,11 b" def test_push_modify_file(f_name='setup."
239
291
240 Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
292 Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
241
293
294 @test_wrapp
242 def test_push_new_file(commits=15, with_clone=True):
295 def test_push_new_file(commits=15, with_clone=True):
243
296
244 if with_clone:
297 if with_clone:
245 test_clone(no_errors=True)
298 test_clone_with_credentials(no_errors=True)
246
299
247 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
300 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
248 added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next())
301 added_file = jn(path, '%ssetupążźć.py' % _RandomNameSequence().next())
@@ -269,6 +322,7 b' def test_push_new_file(commits=15, with_'
269
322
270 Command(cwd).execute('hg push --verbose --debug %s' % push_url)
323 Command(cwd).execute('hg push --verbose --debug %s' % push_url)
271
324
325 @test_wrapp
272 def test_push_wrong_credentials():
326 def test_push_wrong_credentials():
273 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
327 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
274 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
328 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
@@ -288,6 +342,7 b' def test_push_wrong_credentials():'
288
342
289 Command(cwd).execute('hg push %s' % clone_url)
343 Command(cwd).execute('hg push %s' % clone_url)
290
344
345 @test_wrapp
291 def test_push_wrong_path():
346 def test_push_wrong_path():
292 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
347 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
293 added_file = jn(path, 'somefile.py')
348 added_file = jn(path, 'somefile.py')
@@ -295,7 +350,7 b' def test_push_wrong_path():'
295 try:
350 try:
296 shutil.rmtree(path, ignore_errors=True)
351 shutil.rmtree(path, ignore_errors=True)
297 os.makedirs(path)
352 os.makedirs(path)
298 print 'made dirs %s' % jn(path)
353 print '\tmade dirs %s' % jn(path)
299 except OSError:
354 except OSError:
300 raise
355 raise
301
356
@@ -318,20 +373,40 b' def test_push_wrong_path():'
318 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
373 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
319
374
320 stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
375 stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
321 assert """abort: HTTP Error 403: Forbidden""" in stderr
376 if not """abort: HTTP Error 403: Forbidden""" in stderr:
377 raise Exception('Failure')
378
379 @test_wrapp
380 def get_logs():
381 sa = get_session()
382 return len(sa.query(UserLog).all())
383
384 @test_wrapp
385 def test_logs(initial):
386 sa = get_session()
387 logs = sa.query(UserLog).all()
388 operations = 7
389 if initial + operations != len(logs):
390 raise Exception("missing number of logs %s vs %s" % (initial, len(logs)))
322
391
323
392
324 if __name__ == '__main__':
393 if __name__ == '__main__':
325 create_test_user(force=False)
394 create_test_user(force=False)
326 create_test_repo()
395 create_test_repo()
327 #test_push_modify_file()
396
328 #test_clone()
397 initial_logs = get_logs()
329 #test_clone_anonymous_ok()
330
398
331 #test_clone_wrong_credentials()
399 # test_push_modify_file()
400 test_clone_with_credentials()
401 test_clone_wrong_credentials()
332
402
333 test_pull()
403
334 test_push_new_file(commits=2, with_clone=True)
404 test_push_new_file(commits=2, with_clone=True)
335
405
336 #test_push_wrong_path()
406 test_clone_anonymous()
337 #test_push_wrong_credentials()
407 test_push_wrong_path()
408
409
410 test_push_wrong_credentials()
411
412 test_logs(initial_logs)
@@ -7,9 +7,21 b''
7 Package for testing various lib/helper functions in rhodecode
7 Package for testing various lib/helper functions in rhodecode
8
8
9 :created_on: Jun 9, 2011
9 :created_on: Jun 9, 2011
10 :copyright: (c) 2011 by marcink.
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13
25
14
26
15
27
@@ -12,6 +12,7 b' if py_version < (2, 5):'
12 requirements = [
12 requirements = [
13 "Pylons==1.0.0",
13 "Pylons==1.0.0",
14 "WebHelpers>=1.2",
14 "WebHelpers>=1.2",
15 "formencode==1.2.4",
15 "SQLAlchemy>=0.7.2,<0.8",
16 "SQLAlchemy>=0.7.2,<0.8",
16 "Mako>=0.4.2",
17 "Mako>=0.4.2",
17 "pygments>=1.4",
18 "pygments>=1.4",
@@ -22,12 +23,12 b' requirements = ['
22 "python-dateutil>=1.5.0,<2.0.0",
23 "python-dateutil>=1.5.0,<2.0.0",
23 "dulwich>=0.8.0",
24 "dulwich>=0.8.0",
24 "vcs>=0.2.1.dev",
25 "vcs>=0.2.1.dev",
25 "webob==1.0.8"
26 "webob==1.0.8"
26 ]
27 ]
27
28
28 dependency_links = [
29 dependency_links = [
29 "https://secure.rhodecode.org/vcs/archive/default.zip#egg=vcs-0.2.1.dev",
30 "https://secure.rhodecode.org/vcs/archive/default.zip#egg=vcs-0.2.2.dev",
30 "https://bitbucket.org/marcinkuzminski/vcs/get/default.zip#egg=vcs-0.2.1.dev",
31 "https://bitbucket.org/marcinkuzminski/vcs/get/default.zip#egg=vcs-0.2.2.dev",
31 ]
32 ]
32
33
33 classifiers = ['Development Status :: 4 - Beta',
34 classifiers = ['Development Status :: 4 - Beta',
@@ -23,6 +23,7 b' pdebug = false'
23 #smtp_password =
23 #smtp_password =
24 #smtp_port =
24 #smtp_port =
25 #smtp_use_tls = false
25 #smtp_use_tls = false
26 #smtp_use_ssl = true
26
27
27 [server:main]
28 [server:main]
28 ##nr of threads to spawn
29 ##nr of threads to spawn
@@ -49,6 +50,7 b' app_instance_uuid = develop-test'
49 cut_off_limit = 256000
50 cut_off_limit = 256000
50 force_https = false
51 force_https = false
51 commit_parse_limit = 25
52 commit_parse_limit = 25
53 use_gravatar = true
52
54
53 ####################################
55 ####################################
54 ### CELERY CONFIG ####
56 ### CELERY CONFIG ####
@@ -93,7 +95,6 b' beaker.cache.short_term.expire=60'
93 beaker.cache.long_term.type=memory
95 beaker.cache.long_term.type=memory
94 beaker.cache.long_term.expire=36000
96 beaker.cache.long_term.expire=36000
95
97
96
97 beaker.cache.sql_cache_short.type=memory
98 beaker.cache.sql_cache_short.type=memory
98 beaker.cache.sql_cache_short.expire=10
99 beaker.cache.sql_cache_short.expire=10
99
100
@@ -150,13 +151,13 b' sqlalchemy.convert_unicode = true'
150 ### LOGGING CONFIGURATION ####
151 ### LOGGING CONFIGURATION ####
151 ################################
152 ################################
152 [loggers]
153 [loggers]
153 keys = root, routes, rhodecode, sqlalchemy,beaker,templates
154 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
154
155
155 [handlers]
156 [handlers]
156 keys = console
157 keys = console
157
158
158 [formatters]
159 [formatters]
159 keys = generic,color_formatter
160 keys = generic, color_formatter
160
161
161 #############
162 #############
162 ## LOGGERS ##
163 ## LOGGERS ##
@@ -167,9 +168,10 b' handlers = console'
167
168
168 [logger_routes]
169 [logger_routes]
169 level = ERROR
170 level = ERROR
170 handlers = console
171 handlers =
171 qualname = routes.middleware
172 qualname = routes.middleware
172 # "level = DEBUG" logs the route matched and routing variables.
173 # "level = DEBUG" logs the route matched and routing variables.
174 propagate = 1
173
175
174 [logger_beaker]
176 [logger_beaker]
175 level = DEBUG
177 level = DEBUG
@@ -185,9 +187,9 b' propagate = 1'
185
187
186 [logger_rhodecode]
188 [logger_rhodecode]
187 level = ERROR
189 level = ERROR
188 handlers = console
190 handlers =
189 qualname = rhodecode
191 qualname = rhodecode
190 propagate = 0
192 propagate = 1
191
193
192 [logger_sqlalchemy]
194 [logger_sqlalchemy]
193 level = ERROR
195 level = ERROR
@@ -203,7 +205,7 b' propagate = 0'
203 class = StreamHandler
205 class = StreamHandler
204 args = (sys.stderr,)
206 args = (sys.stderr,)
205 level = NOTSET
207 level = NOTSET
206 formatter = color_formatter
208 formatter = generic
207
209
208 ################
210 ################
209 ## FORMATTERS ##
211 ## FORMATTERS ##
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now