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`` |
|
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 |
|
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 |
|
|
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 |
|
|
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 |
|
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 a |
|
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 |
|
|
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, |
|
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__ = |
|
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 |
|
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 |
|
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. |
|
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 = User |
|
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 = User |
|
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 = |
|
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. |
|
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( |
|
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( |
|
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 |
|
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 |
|
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 |
|
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 |
arg |
|
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 == |
|
161 | if arg == USER_SESSION_ATTR: | |
153 |
# |
|
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 = |
|
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 |
|
204 | except JSONRPCError, e: | |
193 | self._error = str(e) |
|
205 | self._error = str(e) | |
194 |
except Exception |
|
206 | except Exception, e: | |
195 |
log. |
|
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. |
|
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. |
|
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 |
|
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 = |
|
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. |
|
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( |
|
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 |
|
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 |
|
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( |
|
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 |
|
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( |
|
197 | if isinstance(unicode_, str): | |
192 |
return |
|
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: |
|
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 = |
|
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 = |
|
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 = |
|
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 = |
|
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: |
|
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. |
|
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 |
|
|
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: |
|
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_ |
|
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, |
|
74 | from rhodecode.lib.utils import invalidate_cache, is_valid_repo | |
75 |
from rhodecode.model. |
|
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 |
|
|
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 |
|
|
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 |
|
132 | if action in ['pull', 'push']: | |
132 | anonymous_user = self.__get_user('default') |
|
133 | anonymous_user = self.__get_user('default') | |
133 |
|
|
134 | username = anonymous_user.username | |
134 |
anonymous_perm = self.__check_permission( |
|
135 | anonymous_perm = self.__check_permission(action, | |
135 | anonymous_user, |
|
136 | anonymous_user, | |
136 |
|
|
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 |
|
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 |
|
|
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( |
|
177 | perm = self.__check_permission(action, user, | |
177 |
|
|
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 |
|
|
182 | extras = {'ip': ipaddr, | |
182 |
|
|
183 | 'username': username, | |
183 |
|
|
184 | 'action': action, | |
184 |
|
|
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 |
|
|
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 User |
|
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 |
|
|
40 | is_valid_repo, ui_sections | |
42 |
from rhodecode.model. |
|
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 |
|
|
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 |
|
|
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 |
|
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 |
|
|
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 |
|
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 |
|
|
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( |
|
146 | perm = self.__check_permission(action, user, | |
145 |
|
|
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 |
|
|
151 | extras = {'ip': ipaddr, | |
150 |
|
|
152 | 'username': username, | |
151 |
|
|
153 | 'action': action, | |
152 |
|
|
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 |
|
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 User |
|
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( |
|
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: ( |
|
9 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
10 |
:license: |
|
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 |
|
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_ |
|
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 = |
|
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 |
|
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 |
|
189 | :return True: if given path is a valid repository | |
193 | """ |
|
190 | """ | |
194 |
|
|
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 |
|
|
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 |
|
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)). |
|
286 | return Session.query(cls).filter(cls.username.like(username)).scalar() | |
285 | else: |
|
287 | else: | |
286 |
return Session.query(cls).filter(cls.username == username). |
|
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 User |
|
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 = User |
|
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 [ |
|
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 User |
|
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 = User |
|
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 |
|
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 |
|
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 |
|
|
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 = User |
|
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( |
|
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 |
|
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. |
|
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: |
|
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 |
|
|
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 |
|
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 |
|
|
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 |
|
|
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 |
|
|
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: ( |
|
10 | :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com> | |
11 |
:license: |
|
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. |
|
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. |
|
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 = |
|
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 = |
|
190 | handlers = | |
189 | qualname = rhodecode |
|
191 | qualname = rhodecode | |
190 |
propagate = |
|
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 = |
|
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