##// END OF EJS Templates
Disable git support due to large problems with dulwich....
marcink -
r710:e2f3c8e6 beta
parent child Browse files
Show More
@@ -1,35 +1,40 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # RhodeCode, a web based repository management based on pylons
3 # RhodeCode, a web based repository management based on pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 RhodeCode, a web based repository management based on pylons
22 RhodeCode, a web based repository management based on pylons
23 versioning implementation: http://semver.org/
23 versioning implementation: http://semver.org/
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 VERSION = (1, 1, 0, 'beta')
27 VERSION = (1, 1, 0, 'beta')
28
28
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30
30
31 def get_version():
31 def get_version():
32 """
32 """
33 Returns shorter version (digit parts only) as string.
33 Returns shorter version (digit parts only) as string.
34 """
34 """
35 return '.'.join((str(each) for each in VERSION[:3]))
35 return '.'.join((str(each) for each in VERSION[:3]))
36
37 BACKENDS = {
38 'hg': 'Mercurial repository',
39 #'git': 'Git repository',
40 }
@@ -1,46 +1,46 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 from pylons import config, tmpl_context as c, request, session
5 from pylons import config, tmpl_context as c, request, session
6 from pylons.controllers import WSGIController
6 from pylons.controllers import WSGIController
7 from pylons.templating import render_mako as render
7 from pylons.templating import render_mako as render
8 from rhodecode import __version__
8 from rhodecode import __version__
9 from rhodecode.lib import auth
9 from rhodecode.lib import auth
10 from rhodecode.lib.utils import get_repo_slug
10 from rhodecode.lib.utils import get_repo_slug
11 from rhodecode.model import meta
11 from rhodecode.model import meta
12 from rhodecode.model.scm import ScmModel
12 from rhodecode.model.scm import ScmModel
13 from vcs import BACKENDS
13 from rhodecode import BACKENDS
14
14
15 class BaseController(WSGIController):
15 class BaseController(WSGIController):
16
16
17 def __before__(self):
17 def __before__(self):
18 c.rhodecode_version = __version__
18 c.rhodecode_version = __version__
19 c.rhodecode_name = config['rhodecode_title']
19 c.rhodecode_name = config['rhodecode_title']
20 c.repo_name = get_repo_slug(request)
20 c.repo_name = get_repo_slug(request)
21 c.cached_repo_list = ScmModel().get_repos()
21 c.cached_repo_list = ScmModel().get_repos()
22 c.backends = BACKENDS.keys()
22 c.backends = BACKENDS.keys()
23
23
24 if c.repo_name:
24 if c.repo_name:
25 cached_repo = ScmModel().get(c.repo_name)
25 cached_repo = ScmModel().get(c.repo_name)
26
26
27 if cached_repo:
27 if cached_repo:
28 c.repository_tags = cached_repo.tags
28 c.repository_tags = cached_repo.tags
29 c.repository_branches = cached_repo.branches
29 c.repository_branches = cached_repo.branches
30 else:
30 else:
31 c.repository_tags = {}
31 c.repository_tags = {}
32 c.repository_branches = {}
32 c.repository_branches = {}
33
33
34 self.sa = meta.Session()
34 self.sa = meta.Session()
35
35
36 def __call__(self, environ, start_response):
36 def __call__(self, environ, start_response):
37 """Invoke the Controller"""
37 """Invoke the Controller"""
38 # WSGIController.__call__ dispatches to the Controller method
38 # WSGIController.__call__ dispatches to the Controller method
39 # the request is routed to. This routing information is
39 # the request is routed to. This routing information is
40 # available in environ['pylons.routes_dict']
40 # available in environ['pylons.routes_dict']
41 try:
41 try:
42 #putting this here makes sure that we update permissions every time
42 #putting this here makes sure that we update permissions every time
43 self.rhodecode_user = c.rhodecode_user = auth.get_user(session)
43 self.rhodecode_user = c.rhodecode_user = auth.get_user(session)
44 return WSGIController.__call__(self, environ, start_response)
44 return WSGIController.__call__(self, environ, start_response)
45 finally:
45 finally:
46 meta.Session.remove()
46 meta.Session.remove()
@@ -1,388 +1,388 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 from formencode import All
22 from formencode import All
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 Email, Bool, StringBoolean
24 Email, Bool, StringBoolean
25 from pylons import session
25 from pylons import session
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from rhodecode.lib.auth import authfunc, get_crypt_password
27 from rhodecode.lib.auth import authfunc, get_crypt_password
28 from rhodecode.lib.exceptions import LdapImportError
28 from rhodecode.lib.exceptions import LdapImportError
29 from rhodecode.model import meta
29 from rhodecode.model import meta
30 from rhodecode.model.user import UserModel
30 from rhodecode.model.user import UserModel
31 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.db import User
32 from rhodecode.model.db import User
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34 from vcs import BACKENDS
34 from rhodecode import BACKENDS
35 import formencode
35 import formencode
36 import logging
36 import logging
37 import os
37 import os
38 import rhodecode.lib.helpers as h
38 import rhodecode.lib.helpers as h
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42 #this is needed to translate the messages using _() in validators
42 #this is needed to translate the messages using _() in validators
43 class State_obj(object):
43 class State_obj(object):
44 _ = staticmethod(_)
44 _ = staticmethod(_)
45
45
46 #===============================================================================
46 #===============================================================================
47 # VALIDATORS
47 # VALIDATORS
48 #===============================================================================
48 #===============================================================================
49 class ValidAuthToken(formencode.validators.FancyValidator):
49 class ValidAuthToken(formencode.validators.FancyValidator):
50 messages = {'invalid_token':_('Token mismatch')}
50 messages = {'invalid_token':_('Token mismatch')}
51
51
52 def validate_python(self, value, state):
52 def validate_python(self, value, state):
53
53
54 if value != authentication_token():
54 if value != authentication_token():
55 raise formencode.Invalid(self.message('invalid_token', state,
55 raise formencode.Invalid(self.message('invalid_token', state,
56 search_number=value), value, state)
56 search_number=value), value, state)
57
57
58 def ValidUsername(edit, old_data):
58 def ValidUsername(edit, old_data):
59 class _ValidUsername(formencode.validators.FancyValidator):
59 class _ValidUsername(formencode.validators.FancyValidator):
60
60
61 def validate_python(self, value, state):
61 def validate_python(self, value, state):
62 if value in ['default', 'new_user']:
62 if value in ['default', 'new_user']:
63 raise formencode.Invalid(_('Invalid username'), value, state)
63 raise formencode.Invalid(_('Invalid username'), value, state)
64 #check if user is unique
64 #check if user is unique
65 old_un = None
65 old_un = None
66 if edit:
66 if edit:
67 old_un = UserModel().get(old_data.get('user_id')).username
67 old_un = UserModel().get(old_data.get('user_id')).username
68
68
69 if old_un != value or not edit:
69 if old_un != value or not edit:
70 if UserModel().get_by_username(value, cache=False):
70 if UserModel().get_by_username(value, cache=False):
71 raise formencode.Invalid(_('This username already exists') ,
71 raise formencode.Invalid(_('This username already exists') ,
72 value, state)
72 value, state)
73
73
74 return _ValidUsername
74 return _ValidUsername
75
75
76 class ValidPassword(formencode.validators.FancyValidator):
76 class ValidPassword(formencode.validators.FancyValidator):
77
77
78 def to_python(self, value, state):
78 def to_python(self, value, state):
79 if value:
79 if value:
80 return get_crypt_password(value)
80 return get_crypt_password(value)
81
81
82 class ValidAuth(formencode.validators.FancyValidator):
82 class ValidAuth(formencode.validators.FancyValidator):
83 messages = {
83 messages = {
84 'invalid_password':_('invalid password'),
84 'invalid_password':_('invalid password'),
85 'invalid_login':_('invalid user name'),
85 'invalid_login':_('invalid user name'),
86 'disabled_account':_('Your account is disabled')
86 'disabled_account':_('Your account is disabled')
87
87
88 }
88 }
89 #error mapping
89 #error mapping
90 e_dict = {'username':messages['invalid_login'],
90 e_dict = {'username':messages['invalid_login'],
91 'password':messages['invalid_password']}
91 'password':messages['invalid_password']}
92 e_dict_disable = {'username':messages['disabled_account']}
92 e_dict_disable = {'username':messages['disabled_account']}
93
93
94 def validate_python(self, value, state):
94 def validate_python(self, value, state):
95 password = value['password']
95 password = value['password']
96 username = value['username']
96 username = value['username']
97 user = UserModel().get_by_username(username)
97 user = UserModel().get_by_username(username)
98
98
99 if authfunc(None, username, password):
99 if authfunc(None, username, password):
100 return value
100 return value
101 else:
101 else:
102 if user and user.active is False:
102 if user and user.active is False:
103 log.warning('user %s is disabled', username)
103 log.warning('user %s is disabled', username)
104 raise formencode.Invalid(self.message('disabled_account',
104 raise formencode.Invalid(self.message('disabled_account',
105 state=State_obj),
105 state=State_obj),
106 value, state,
106 value, state,
107 error_dict=self.e_dict_disable)
107 error_dict=self.e_dict_disable)
108 else:
108 else:
109 log.warning('user %s not authenticated', username)
109 log.warning('user %s not authenticated', username)
110 raise formencode.Invalid(self.message('invalid_password',
110 raise formencode.Invalid(self.message('invalid_password',
111 state=State_obj), value, state,
111 state=State_obj), value, state,
112 error_dict=self.e_dict)
112 error_dict=self.e_dict)
113
113
114 class ValidRepoUser(formencode.validators.FancyValidator):
114 class ValidRepoUser(formencode.validators.FancyValidator):
115
115
116 def to_python(self, value, state):
116 def to_python(self, value, state):
117 sa = meta.Session()
117 sa = meta.Session()
118 try:
118 try:
119 self.user_db = sa.query(User)\
119 self.user_db = sa.query(User)\
120 .filter(User.active == True)\
120 .filter(User.active == True)\
121 .filter(User.username == value).one()
121 .filter(User.username == value).one()
122 except Exception:
122 except Exception:
123 raise formencode.Invalid(_('This username is not valid'),
123 raise formencode.Invalid(_('This username is not valid'),
124 value, state)
124 value, state)
125 finally:
125 finally:
126 meta.Session.remove()
126 meta.Session.remove()
127
127
128 return self.user_db.user_id
128 return self.user_db.user_id
129
129
130 def ValidRepoName(edit, old_data):
130 def ValidRepoName(edit, old_data):
131 class _ValidRepoName(formencode.validators.FancyValidator):
131 class _ValidRepoName(formencode.validators.FancyValidator):
132
132
133 def to_python(self, value, state):
133 def to_python(self, value, state):
134 slug = h.repo_name_slug(value)
134 slug = h.repo_name_slug(value)
135 if slug in ['_admin']:
135 if slug in ['_admin']:
136 raise formencode.Invalid(_('This repository name is disallowed'),
136 raise formencode.Invalid(_('This repository name is disallowed'),
137 value, state)
137 value, state)
138 if old_data.get('repo_name') != value or not edit:
138 if old_data.get('repo_name') != value or not edit:
139 if RepoModel().get(slug, cache=False):
139 if RepoModel().get(slug, cache=False):
140 raise formencode.Invalid(_('This repository already exists') ,
140 raise formencode.Invalid(_('This repository already exists') ,
141 value, state)
141 value, state)
142 return slug
142 return slug
143
143
144
144
145 return _ValidRepoName
145 return _ValidRepoName
146
146
147 def ValidForkType(old_data):
147 def ValidForkType(old_data):
148 class _ValidForkType(formencode.validators.FancyValidator):
148 class _ValidForkType(formencode.validators.FancyValidator):
149
149
150 def to_python(self, value, state):
150 def to_python(self, value, state):
151 if old_data['repo_type'] != value:
151 if old_data['repo_type'] != value:
152 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
152 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
153 return value
153 return value
154 return _ValidForkType
154 return _ValidForkType
155
155
156 class ValidPerms(formencode.validators.FancyValidator):
156 class ValidPerms(formencode.validators.FancyValidator):
157 messages = {'perm_new_user_name':_('This username is not valid')}
157 messages = {'perm_new_user_name':_('This username is not valid')}
158
158
159 def to_python(self, value, state):
159 def to_python(self, value, state):
160 perms_update = []
160 perms_update = []
161 perms_new = []
161 perms_new = []
162 #build a list of permission to update and new permission to create
162 #build a list of permission to update and new permission to create
163 for k, v in value.items():
163 for k, v in value.items():
164 if k.startswith('perm_'):
164 if k.startswith('perm_'):
165 if k.startswith('perm_new_user'):
165 if k.startswith('perm_new_user'):
166 new_perm = value.get('perm_new_user', False)
166 new_perm = value.get('perm_new_user', False)
167 new_user = value.get('perm_new_user_name', False)
167 new_user = value.get('perm_new_user_name', False)
168 if new_user and new_perm:
168 if new_user and new_perm:
169 if (new_user, new_perm) not in perms_new:
169 if (new_user, new_perm) not in perms_new:
170 perms_new.append((new_user, new_perm))
170 perms_new.append((new_user, new_perm))
171 else:
171 else:
172 usr = k[5:]
172 usr = k[5:]
173 if usr == 'default':
173 if usr == 'default':
174 if value['private']:
174 if value['private']:
175 #set none for default when updating to private repo
175 #set none for default when updating to private repo
176 v = 'repository.none'
176 v = 'repository.none'
177 perms_update.append((usr, v))
177 perms_update.append((usr, v))
178 value['perms_updates'] = perms_update
178 value['perms_updates'] = perms_update
179 value['perms_new'] = perms_new
179 value['perms_new'] = perms_new
180 sa = meta.Session
180 sa = meta.Session
181 for k, v in perms_new:
181 for k, v in perms_new:
182 try:
182 try:
183 self.user_db = sa.query(User)\
183 self.user_db = sa.query(User)\
184 .filter(User.active == True)\
184 .filter(User.active == True)\
185 .filter(User.username == k).one()
185 .filter(User.username == k).one()
186 except Exception:
186 except Exception:
187 msg = self.message('perm_new_user_name',
187 msg = self.message('perm_new_user_name',
188 state=State_obj)
188 state=State_obj)
189 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
189 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
190 return value
190 return value
191
191
192 class ValidSettings(formencode.validators.FancyValidator):
192 class ValidSettings(formencode.validators.FancyValidator):
193
193
194 def to_python(self, value, state):
194 def to_python(self, value, state):
195 #settings form can't edit user
195 #settings form can't edit user
196 if value.has_key('user'):
196 if value.has_key('user'):
197 del['value']['user']
197 del['value']['user']
198
198
199 return value
199 return value
200
200
201 class ValidPath(formencode.validators.FancyValidator):
201 class ValidPath(formencode.validators.FancyValidator):
202 def to_python(self, value, state):
202 def to_python(self, value, state):
203
203
204 if not os.path.isdir(value):
204 if not os.path.isdir(value):
205 msg = _('This is not a valid path')
205 msg = _('This is not a valid path')
206 raise formencode.Invalid(msg, value, state,
206 raise formencode.Invalid(msg, value, state,
207 error_dict={'paths_root_path':msg})
207 error_dict={'paths_root_path':msg})
208 return value
208 return value
209
209
210 def UniqSystemEmail(old_data):
210 def UniqSystemEmail(old_data):
211 class _UniqSystemEmail(formencode.validators.FancyValidator):
211 class _UniqSystemEmail(formencode.validators.FancyValidator):
212 def to_python(self, value, state):
212 def to_python(self, value, state):
213 if old_data.get('email') != value:
213 if old_data.get('email') != value:
214 sa = meta.Session()
214 sa = meta.Session()
215 try:
215 try:
216 user = sa.query(User).filter(User.email == value).scalar()
216 user = sa.query(User).filter(User.email == value).scalar()
217 if user:
217 if user:
218 raise formencode.Invalid(_("That e-mail address is already taken") ,
218 raise formencode.Invalid(_("That e-mail address is already taken") ,
219 value, state)
219 value, state)
220 finally:
220 finally:
221 meta.Session.remove()
221 meta.Session.remove()
222
222
223 return value
223 return value
224
224
225 return _UniqSystemEmail
225 return _UniqSystemEmail
226
226
227 class ValidSystemEmail(formencode.validators.FancyValidator):
227 class ValidSystemEmail(formencode.validators.FancyValidator):
228 def to_python(self, value, state):
228 def to_python(self, value, state):
229 sa = meta.Session
229 sa = meta.Session
230 try:
230 try:
231 user = sa.query(User).filter(User.email == value).scalar()
231 user = sa.query(User).filter(User.email == value).scalar()
232 if user is None:
232 if user is None:
233 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
233 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
234 value, state)
234 value, state)
235 finally:
235 finally:
236 meta.Session.remove()
236 meta.Session.remove()
237
237
238 return value
238 return value
239
239
240 class LdapLibValidator(formencode.validators.FancyValidator):
240 class LdapLibValidator(formencode.validators.FancyValidator):
241
241
242 def to_python(self, value, state):
242 def to_python(self, value, state):
243
243
244 try:
244 try:
245 import ldap
245 import ldap
246 except ImportError:
246 except ImportError:
247 raise LdapImportError
247 raise LdapImportError
248 return value
248 return value
249
249
250 #===============================================================================
250 #===============================================================================
251 # FORMS
251 # FORMS
252 #===============================================================================
252 #===============================================================================
253 class LoginForm(formencode.Schema):
253 class LoginForm(formencode.Schema):
254 allow_extra_fields = True
254 allow_extra_fields = True
255 filter_extra_fields = True
255 filter_extra_fields = True
256 username = UnicodeString(
256 username = UnicodeString(
257 strip=True,
257 strip=True,
258 min=1,
258 min=1,
259 not_empty=True,
259 not_empty=True,
260 messages={
260 messages={
261 'empty':_('Please enter a login'),
261 'empty':_('Please enter a login'),
262 'tooShort':_('Enter a value %(min)i characters long or more')}
262 'tooShort':_('Enter a value %(min)i characters long or more')}
263 )
263 )
264
264
265 password = UnicodeString(
265 password = UnicodeString(
266 strip=True,
266 strip=True,
267 min=6,
267 min=6,
268 not_empty=True,
268 not_empty=True,
269 messages={
269 messages={
270 'empty':_('Please enter a password'),
270 'empty':_('Please enter a password'),
271 'tooShort':_('Enter %(min)i characters or more')}
271 'tooShort':_('Enter %(min)i characters or more')}
272 )
272 )
273
273
274
274
275 #chained validators have access to all data
275 #chained validators have access to all data
276 chained_validators = [ValidAuth]
276 chained_validators = [ValidAuth]
277
277
278 def UserForm(edit=False, old_data={}):
278 def UserForm(edit=False, old_data={}):
279 class _UserForm(formencode.Schema):
279 class _UserForm(formencode.Schema):
280 allow_extra_fields = True
280 allow_extra_fields = True
281 filter_extra_fields = True
281 filter_extra_fields = True
282 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
282 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
283 if edit:
283 if edit:
284 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
284 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
285 admin = StringBoolean(if_missing=False)
285 admin = StringBoolean(if_missing=False)
286 else:
286 else:
287 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
287 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
288 active = StringBoolean(if_missing=False)
288 active = StringBoolean(if_missing=False)
289 name = UnicodeString(strip=True, min=1, not_empty=True)
289 name = UnicodeString(strip=True, min=1, not_empty=True)
290 lastname = UnicodeString(strip=True, min=1, not_empty=True)
290 lastname = UnicodeString(strip=True, min=1, not_empty=True)
291 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
291 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
292
292
293 return _UserForm
293 return _UserForm
294
294
295 RegisterForm = UserForm
295 RegisterForm = UserForm
296
296
297 def PasswordResetForm():
297 def PasswordResetForm():
298 class _PasswordResetForm(formencode.Schema):
298 class _PasswordResetForm(formencode.Schema):
299 allow_extra_fields = True
299 allow_extra_fields = True
300 filter_extra_fields = True
300 filter_extra_fields = True
301 email = All(ValidSystemEmail(), Email(not_empty=True))
301 email = All(ValidSystemEmail(), Email(not_empty=True))
302 return _PasswordResetForm
302 return _PasswordResetForm
303
303
304 def RepoForm(edit=False, old_data={}):
304 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
305 class _RepoForm(formencode.Schema):
305 class _RepoForm(formencode.Schema):
306 allow_extra_fields = True
306 allow_extra_fields = True
307 filter_extra_fields = False
307 filter_extra_fields = False
308 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
308 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
309 description = UnicodeString(strip=True, min=1, not_empty=True)
309 description = UnicodeString(strip=True, min=1, not_empty=True)
310 private = StringBoolean(if_missing=False)
310 private = StringBoolean(if_missing=False)
311 repo_type = OneOf(BACKENDS.keys())
311 repo_type = OneOf(supported_backends)
312 if edit:
312 if edit:
313 user = All(Int(not_empty=True), ValidRepoUser)
313 user = All(Int(not_empty=True), ValidRepoUser)
314
314
315 chained_validators = [ValidPerms]
315 chained_validators = [ValidPerms]
316 return _RepoForm
316 return _RepoForm
317
317
318 def RepoForkForm(edit=False, old_data={}):
318 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
319 class _RepoForkForm(formencode.Schema):
319 class _RepoForkForm(formencode.Schema):
320 allow_extra_fields = True
320 allow_extra_fields = True
321 filter_extra_fields = False
321 filter_extra_fields = False
322 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
322 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
323 description = UnicodeString(strip=True, min=1, not_empty=True)
323 description = UnicodeString(strip=True, min=1, not_empty=True)
324 private = StringBoolean(if_missing=False)
324 private = StringBoolean(if_missing=False)
325 repo_type = All(ValidForkType(old_data), OneOf(BACKENDS.keys()))
325 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
326 return _RepoForkForm
326 return _RepoForkForm
327
327
328 def RepoSettingsForm(edit=False, old_data={}):
328 def RepoSettingsForm(edit=False, old_data={}):
329 class _RepoForm(formencode.Schema):
329 class _RepoForm(formencode.Schema):
330 allow_extra_fields = True
330 allow_extra_fields = True
331 filter_extra_fields = False
331 filter_extra_fields = False
332 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
332 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
333 description = UnicodeString(strip=True, min=1, not_empty=True)
333 description = UnicodeString(strip=True, min=1, not_empty=True)
334 private = StringBoolean(if_missing=False)
334 private = StringBoolean(if_missing=False)
335
335
336 chained_validators = [ValidPerms, ValidSettings]
336 chained_validators = [ValidPerms, ValidSettings]
337 return _RepoForm
337 return _RepoForm
338
338
339
339
340 def ApplicationSettingsForm():
340 def ApplicationSettingsForm():
341 class _ApplicationSettingsForm(formencode.Schema):
341 class _ApplicationSettingsForm(formencode.Schema):
342 allow_extra_fields = True
342 allow_extra_fields = True
343 filter_extra_fields = False
343 filter_extra_fields = False
344 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
344 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
345 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
345 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
346
346
347 return _ApplicationSettingsForm
347 return _ApplicationSettingsForm
348
348
349 def ApplicationUiSettingsForm():
349 def ApplicationUiSettingsForm():
350 class _ApplicationUiSettingsForm(formencode.Schema):
350 class _ApplicationUiSettingsForm(formencode.Schema):
351 allow_extra_fields = True
351 allow_extra_fields = True
352 filter_extra_fields = False
352 filter_extra_fields = False
353 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
353 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
354 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
354 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
355 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
355 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
356 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
356 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
357 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
357 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
358 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
358 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
359
359
360 return _ApplicationUiSettingsForm
360 return _ApplicationUiSettingsForm
361
361
362 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
362 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
363 class _DefaultPermissionsForm(formencode.Schema):
363 class _DefaultPermissionsForm(formencode.Schema):
364 allow_extra_fields = True
364 allow_extra_fields = True
365 filter_extra_fields = True
365 filter_extra_fields = True
366 overwrite_default = StringBoolean(if_missing=False)
366 overwrite_default = StringBoolean(if_missing=False)
367 anonymous = OneOf(['True', 'False'], if_missing=False)
367 anonymous = OneOf(['True', 'False'], if_missing=False)
368 default_perm = OneOf(perms_choices)
368 default_perm = OneOf(perms_choices)
369 default_register = OneOf(register_choices)
369 default_register = OneOf(register_choices)
370 default_create = OneOf(create_choices)
370 default_create = OneOf(create_choices)
371
371
372 return _DefaultPermissionsForm
372 return _DefaultPermissionsForm
373
373
374
374
375 def LdapSettingsForm():
375 def LdapSettingsForm():
376 class _LdapSettingsForm(formencode.Schema):
376 class _LdapSettingsForm(formencode.Schema):
377 allow_extra_fields = True
377 allow_extra_fields = True
378 filter_extra_fields = True
378 filter_extra_fields = True
379 pre_validators = [LdapLibValidator]
379 pre_validators = [LdapLibValidator]
380 ldap_active = StringBoolean(if_missing=False)
380 ldap_active = StringBoolean(if_missing=False)
381 ldap_host = UnicodeString(strip=True,)
381 ldap_host = UnicodeString(strip=True,)
382 ldap_port = Number(strip=True,)
382 ldap_port = Number(strip=True,)
383 ldap_ldaps = StringBoolean(if_missing=False)
383 ldap_ldaps = StringBoolean(if_missing=False)
384 ldap_dn_user = UnicodeString(strip=True,)
384 ldap_dn_user = UnicodeString(strip=True,)
385 ldap_dn_pass = UnicodeString(strip=True,)
385 ldap_dn_pass = UnicodeString(strip=True,)
386 ldap_base_dn = UnicodeString(strip=True,)
386 ldap_base_dn = UnicodeString(strip=True,)
387
387
388 return _LdapSettingsForm
388 return _LdapSettingsForm
@@ -1,240 +1,242 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for RhodeCode
3 # Model for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for RhodeCode
22 Model for RhodeCode
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region, region_invalidate
25 from beaker.cache import cache_region, region_invalidate
26 from mercurial import ui
26 from mercurial import ui
27 from rhodecode import BACKENDS
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import HasRepoPermissionAny
29 from rhodecode.lib.auth import HasRepoPermissionAny
29 from rhodecode.lib.utils import get_repos
30 from rhodecode.lib.utils import get_repos
30 from rhodecode.model import meta
31 from rhodecode.model import meta
31 from rhodecode.model.db import Repository, User, RhodeCodeUi, CacheInvalidation
32 from rhodecode.model.db import Repository, User, RhodeCodeUi, CacheInvalidation
32 from rhodecode.model.caching_query import FromCache
33 from rhodecode.model.caching_query import FromCache
33 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm.session import make_transient
35 from sqlalchemy.orm.session import make_transient
35 from vcs import get_backend
36 from vcs import get_backend
36 from vcs.utils.helpers import get_scm
37 from vcs.utils.helpers import get_scm
37 from vcs.exceptions import RepositoryError, VCSError
38 from vcs.exceptions import RepositoryError, VCSError
38 from vcs.utils.lazy import LazyProperty
39 from vcs.utils.lazy import LazyProperty
39 import traceback
40 import traceback
40 import logging
41 import logging
41 import os
42 import os
42 import time
43 import time
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
46 class ScmModel(object):
47 class ScmModel(object):
47 """
48 """
48 Mercurial Model
49 Mercurial Model
49 """
50 """
50
51
51 def __init__(self):
52 def __init__(self):
52 self.sa = meta.Session()
53 self.sa = meta.Session()
53
54
54 @LazyProperty
55 @LazyProperty
55 def repos_path(self):
56 def repos_path(self):
56 """
57 """
57 Get's the repositories root path from database
58 Get's the repositories root path from database
58 """
59 """
59 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
60 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
60
61
61 return q.ui_value
62 return q.ui_value
62
63
63 def repo_scan(self, repos_path, baseui, initial=False):
64 def repo_scan(self, repos_path, baseui, initial=False):
64 """
65 """
65 Listing of repositories in given path. This path should not be a
66 Listing of repositories in given path. This path should not be a
66 repository itself. Return a dictionary of repository objects
67 repository itself. Return a dictionary of repository objects
67
68
68 :param repos_path: path to directory containing repositories
69 :param repos_path: path to directory containing repositories
69 :param baseui
70 :param baseui
70 :param initial: initial scan
71 :param initial: initial scan
71 """
72 """
72 log.info('scanning for repositories in %s', repos_path)
73 log.info('scanning for repositories in %s', repos_path)
73
74
74 if not isinstance(baseui, ui.ui):
75 if not isinstance(baseui, ui.ui):
75 baseui = ui.ui()
76 baseui = ui.ui()
76 repos_list = {}
77 repos_list = {}
77
78
78 for name, path in get_repos(repos_path):
79 for name, path in get_repos(repos_path):
79 try:
80 try:
80 if repos_list.has_key(name):
81 if repos_list.has_key(name):
81 raise RepositoryError('Duplicate repository name %s '
82 raise RepositoryError('Duplicate repository name %s '
82 'found in %s' % (name, path))
83 'found in %s' % (name, path))
83 else:
84 else:
84
85
85 klass = get_backend(path[0])
86 klass = get_backend(path[0])
86
87
87 if path[0] == 'hg':
88 if path[0] == 'hg' and path[0] in BACKENDS.keys():
88 repos_list[name] = klass(path[1], baseui=baseui)
89 repos_list[name] = klass(path[1], baseui=baseui)
89
90
90 if path[0] == 'git':
91 if path[0] == 'git' and path[0] in BACKENDS.keys():
91 repos_list[name] = klass(path[1])
92 repos_list[name] = klass(path[1])
92 except OSError:
93 except OSError:
93 continue
94 continue
94
95
95 return repos_list
96 return repos_list
96
97
97 def get_repos(self, all_repos=None):
98 def get_repos(self, all_repos=None):
98 """
99 """
99 Get all repos from db and for each repo create it's backend instance.
100 Get all repos from db and for each repo create it's backend instance.
100 and fill that backed with information from database
101 and fill that backed with information from database
101
102
102 :param all_repos: give specific repositories list, good for filtering
103 :param all_repos: give specific repositories list, good for filtering
103 """
104 """
104 if not all_repos:
105 if not all_repos:
105 all_repos = self.sa.query(Repository)\
106 all_repos = self.sa.query(Repository)\
106 .order_by(Repository.repo_name).all()
107 .order_by(Repository.repo_name).all()
107
108
108 for r in all_repos:
109 for r in all_repos:
109
110
110 repo = self.get(r.repo_name)
111 repo = self.get(r.repo_name)
111
112
112 if repo is not None:
113 if repo is not None:
113 last_change = repo.last_change
114 last_change = repo.last_change
114 tip = h.get_changeset_safe(repo, 'tip')
115 tip = h.get_changeset_safe(repo, 'tip')
115
116
116 tmp_d = {}
117 tmp_d = {}
117 tmp_d['name'] = repo.name
118 tmp_d['name'] = repo.name
118 tmp_d['name_sort'] = tmp_d['name'].lower()
119 tmp_d['name_sort'] = tmp_d['name'].lower()
119 tmp_d['description'] = repo.dbrepo.description
120 tmp_d['description'] = repo.dbrepo.description
120 tmp_d['description_sort'] = tmp_d['description']
121 tmp_d['description_sort'] = tmp_d['description']
121 tmp_d['last_change'] = last_change
122 tmp_d['last_change'] = last_change
122 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
123 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
123 tmp_d['tip'] = tip.raw_id
124 tmp_d['tip'] = tip.raw_id
124 tmp_d['tip_sort'] = tip.revision
125 tmp_d['tip_sort'] = tip.revision
125 tmp_d['rev'] = tip.revision
126 tmp_d['rev'] = tip.revision
126 tmp_d['contact'] = repo.dbrepo.user.full_contact
127 tmp_d['contact'] = repo.dbrepo.user.full_contact
127 tmp_d['contact_sort'] = tmp_d['contact']
128 tmp_d['contact_sort'] = tmp_d['contact']
128 tmp_d['repo_archives'] = list(repo._get_archives())
129 tmp_d['repo_archives'] = list(repo._get_archives())
129 tmp_d['last_msg'] = tip.message
130 tmp_d['last_msg'] = tip.message
130 tmp_d['repo'] = repo
131 tmp_d['repo'] = repo
131 yield tmp_d
132 yield tmp_d
132
133
133 def get_repo(self, repo_name):
134 def get_repo(self, repo_name):
134 return self.get(repo_name)
135 return self.get(repo_name)
135
136
136 def get(self, repo_name):
137 def get(self, repo_name):
137 """
138 """
138 Get's repository from given name, creates BackendInstance and
139 Get's repository from given name, creates BackendInstance and
139 propagates it's data from database with all additional information
140 propagates it's data from database with all additional information
140 :param repo_name:
141 :param repo_name:
141 """
142 """
142 if not HasRepoPermissionAny('repository.read', 'repository.write',
143 if not HasRepoPermissionAny('repository.read', 'repository.write',
143 'repository.admin')(repo_name, 'get repo check'):
144 'repository.admin')(repo_name, 'get repo check'):
144 return
145 return
145
146
146 @cache_region('long_term')
147 @cache_region('long_term')
147 def _get_repo(repo_name):
148 def _get_repo(repo_name):
148
149
149 repo_path = os.path.join(self.repos_path, repo_name)
150 repo_path = os.path.join(self.repos_path, repo_name)
150 alias = get_scm(repo_path)[0]
151 alias = get_scm(repo_path)[0]
151
152
152 log.debug('Creating instance of %s repository', alias)
153 log.debug('Creating instance of %s repository', alias)
153 backend = get_backend(alias)
154 backend = get_backend(alias)
154
155
156 #TODO: get the baseui from somewhere for this
155 if alias == 'hg':
157 if alias == 'hg':
156 repo = backend(repo_path, create=False, baseui=None)
158 repo = backend(repo_path, create=False, baseui=None)
157 #skip hidden web repository
159 #skip hidden web repository
158 if repo._get_hidden():
160 if repo._get_hidden():
159 return
161 return
160 else:
162 else:
161 repo = backend(repo_path, create=False)
163 repo = backend(repo_path, create=False)
162
164
163 dbrepo = self.sa.query(Repository)\
165 dbrepo = self.sa.query(Repository)\
164 .options(joinedload(Repository.fork))\
166 .options(joinedload(Repository.fork))\
165 .options(joinedload(Repository.user))\
167 .options(joinedload(Repository.user))\
166 .filter(Repository.repo_name == repo_name)\
168 .filter(Repository.repo_name == repo_name)\
167 .scalar()
169 .scalar()
168 make_transient(dbrepo)
170 make_transient(dbrepo)
169 repo.dbrepo = dbrepo
171 repo.dbrepo = dbrepo
170 return repo
172 return repo
171
173
172 invalidate = self._should_invalidate(repo_name)
174 invalidate = self._should_invalidate(repo_name)
173 if invalidate:
175 if invalidate:
174 log.info('invalidating cache for repository %s', repo_name)
176 log.info('invalidating cache for repository %s', repo_name)
175 region_invalidate(_get_repo, None, repo_name)
177 region_invalidate(_get_repo, None, repo_name)
176 self._mark_invalidated(invalidate)
178 self._mark_invalidated(invalidate)
177
179
178 return _get_repo(repo_name)
180 return _get_repo(repo_name)
179
181
180
182
181
183
182 def mark_for_invalidation(self, repo_name):
184 def mark_for_invalidation(self, repo_name):
183 """
185 """
184 Puts cache invalidation task into db for
186 Puts cache invalidation task into db for
185 further global cache invalidation
187 further global cache invalidation
186
188
187 :param repo_name: this repo that should invalidation take place
189 :param repo_name: this repo that should invalidation take place
188 """
190 """
189 log.debug('marking %s for invalidation', repo_name)
191 log.debug('marking %s for invalidation', repo_name)
190 cache = self.sa.query(CacheInvalidation)\
192 cache = self.sa.query(CacheInvalidation)\
191 .filter(CacheInvalidation.cache_key == repo_name).scalar()
193 .filter(CacheInvalidation.cache_key == repo_name).scalar()
192
194
193 if cache:
195 if cache:
194 #mark this cache as inactive
196 #mark this cache as inactive
195 cache.cache_active = False
197 cache.cache_active = False
196 else:
198 else:
197 log.debug('cache key not found in invalidation db -> creating one')
199 log.debug('cache key not found in invalidation db -> creating one')
198 cache = CacheInvalidation(repo_name)
200 cache = CacheInvalidation(repo_name)
199
201
200 try:
202 try:
201 self.sa.add(cache)
203 self.sa.add(cache)
202 self.sa.commit()
204 self.sa.commit()
203 except:
205 except:
204 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
205 self.sa.rollback()
207 self.sa.rollback()
206
208
207
209
208
210
209
211
210
212
211 def _should_invalidate(self, repo_name):
213 def _should_invalidate(self, repo_name):
212 """
214 """
213 Looks up database for invalidation signals for this repo_name
215 Looks up database for invalidation signals for this repo_name
214 :param repo_name:
216 :param repo_name:
215 """
217 """
216
218
217 ret = self.sa.query(CacheInvalidation)\
219 ret = self.sa.query(CacheInvalidation)\
218 .options(FromCache('sql_cache_short',
220 .options(FromCache('sql_cache_short',
219 'get_invalidation_%s' % repo_name))\
221 'get_invalidation_%s' % repo_name))\
220 .filter(CacheInvalidation.cache_key == repo_name)\
222 .filter(CacheInvalidation.cache_key == repo_name)\
221 .filter(CacheInvalidation.cache_active == False)\
223 .filter(CacheInvalidation.cache_active == False)\
222 .scalar()
224 .scalar()
223
225
224 return ret
226 return ret
225
227
226 def _mark_invalidated(self, cache_key):
228 def _mark_invalidated(self, cache_key):
227 """
229 """
228 Marks all occurences of cache to invaldation as already invalidated
230 Marks all occurences of cache to invaldation as already invalidated
229 @param repo_name:
231 @param repo_name:
230 """
232 """
231 if cache_key:
233 if cache_key:
232 log.debug('marking %s as already invalidated', cache_key)
234 log.debug('marking %s as already invalidated', cache_key)
233 try:
235 try:
234 cache_key.cache_active = True
236 cache_key.cache_active = True
235 self.sa.add(cache_key)
237 self.sa.add(cache_key)
236 self.sa.commit()
238 self.sa.commit()
237 except:
239 except:
238 log.error(traceback.format_exc())
240 log.error(traceback.format_exc())
239 self.sa.rollback()
241 self.sa.rollback()
240
242
General Comments 0
You need to be logged in to leave comments. Login now