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