##// END OF EJS Templates
fixes issue #16 reimplementation of database repository, for using generic pk instead of repo naming as pk. Which caused to many problems....
marcink -
r367:a26f48ad default
parent child Browse files
Show More
@@ -1,98 +1,100
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for 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 """
21 Created on June 30, 2010
22 settings controller for pylons
23 @author: marcink
24 """
20 from formencode import htmlfill
25 from formencode import htmlfill
21 from pylons import tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request, url
22 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
23 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
24 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
29 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
25 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.base import BaseController, render
26 from pylons_app.lib.utils import invalidate_cache
31 from pylons_app.lib.utils import invalidate_cache
27 from pylons_app.model.forms import RepoSettingsForm
32 from pylons_app.model.forms import RepoSettingsForm
28 from pylons_app.model.repo_model import RepoModel
33 from pylons_app.model.repo_model import RepoModel
29 import formencode
34 import formencode
30 import logging
35 import logging
31 import pylons_app.lib.helpers as h
36 import pylons_app.lib.helpers as h
32 import traceback
37 import traceback
33 """
38
34 Created on June 30, 2010
35 settings controller for pylons
36 @author: marcink
37 """
38 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
39
40
40 class SettingsController(BaseController):
41 class SettingsController(BaseController):
41
42
42 @LoginRequired()
43 @LoginRequired()
43 @HasRepoPermissionAllDecorator('repository.admin')
44 @HasRepoPermissionAllDecorator('repository.admin')
44 def __before__(self):
45 def __before__(self):
45 super(SettingsController, self).__before__()
46 super(SettingsController, self).__before__()
46
47
47 def index(self, repo_name):
48 def index(self, repo_name):
48 repo_model = RepoModel()
49 repo_model = RepoModel()
49 c.repo_info = repo = repo_model.get(repo_name)
50 c.repo_info = repo = repo_model.get(repo_name)
50 if not repo:
51 if not repo:
51 h.flash(_('%s repository is not mapped to db perhaps'
52 h.flash(_('%s repository is not mapped to db perhaps'
52 ' it was created or renamed from the filesystem'
53 ' it was created or renamed from the filesystem'
53 ' please run the application again'
54 ' please run the application again'
54 ' in order to rescan repositories') % repo_name,
55 ' in order to rescan repositories') % repo_name,
55 category='error')
56 category='error')
56
57
57 return redirect(url('repos'))
58 return redirect(url('repos'))
58 defaults = c.repo_info.__dict__
59 defaults = c.repo_info.__dict__
59 defaults.update({'user':c.repo_info.user.username})
60 defaults.update({'user':c.repo_info.user.username})
60 c.users_array = repo_model.get_users_js()
61 c.users_array = repo_model.get_users_js()
61
62
62 for p in c.repo_info.repo2perm:
63 for p in c.repo_info.repo2perm:
63 defaults.update({'perm_%s' % p.user.username:
64 defaults.update({'perm_%s' % p.user.username:
64 p.permission.permission_name})
65 p.permission.permission_name})
65
66
66 return htmlfill.render(
67 return htmlfill.render(
67 render('settings/repo_settings.html'),
68 render('settings/repo_settings.html'),
68 defaults=defaults,
69 defaults=defaults,
69 encoding="UTF-8",
70 encoding="UTF-8",
70 force_defaults=False
71 force_defaults=False
71 )
72 )
72
73
73 def update(self, repo_name):
74 def update(self, repo_name):
74 repo_model = RepoModel()
75 repo_model = RepoModel()
76 changed_name = repo_name
75 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
76 try:
78 try:
77 form_result = _form.to_python(dict(request.POST))
79 form_result = _form.to_python(dict(request.POST))
78 repo_model.update(repo_name, form_result)
80 repo_model.update(repo_name, form_result)
79 invalidate_cache('cached_repo_list')
81 invalidate_cache('cached_repo_list')
80 h.flash(_('Repository %s updated succesfully' % repo_name),
82 h.flash(_('Repository %s updated succesfully' % repo_name),
81 category='success')
83 category='success')
82
84 changed_name = form_result['repo_name']
83 except formencode.Invalid as errors:
85 except formencode.Invalid as errors:
84 c.repo_info = repo_model.get(repo_name)
86 c.repo_info = repo_model.get(repo_name)
85 c.users_array = repo_model.get_users_js()
87 c.users_array = repo_model.get_users_js()
86 errors.value.update({'user':c.repo_info.user.username})
88 errors.value.update({'user':c.repo_info.user.username})
87 return htmlfill.render(
89 return htmlfill.render(
88 render('settings/repo_settings.html'),
90 render('settings/repo_settings.html'),
89 defaults=errors.value,
91 defaults=errors.value,
90 errors=errors.error_dict or {},
92 errors=errors.error_dict or {},
91 prefix_error=False,
93 prefix_error=False,
92 encoding="UTF-8")
94 encoding="UTF-8")
93 except Exception:
95 except Exception:
94 log.error(traceback.format_exc())
96 log.error(traceback.format_exc())
95 h.flash(_('error occured during update of repository %s') \
97 h.flash(_('error occured during update of repository %s') \
96 % form_result['repo_name'], category='error')
98 % repo_name, category='error')
97
99
98 return redirect(url('repo_settings_home', repo_name=form_result['repo_name']))
100 return redirect(url('repo_settings_home', repo_name=changed_name))
@@ -1,415 +1,419
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # authentication and permission libraries
3 # authentication and permission libraries
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 4, 2010
21 Created on April 4, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from functools import wraps
26 from functools import wraps
27 from pylons import config, session, url, request
27 from pylons import config, session, url, request
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons_app.lib.utils import get_repo_slug
29 from pylons_app.lib.utils import get_repo_slug
30 from pylons_app.model import meta
30 from pylons_app.model import meta
31 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
31 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
32 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.exc import OperationalError
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 import crypt
34 import crypt
35 import logging
35 import logging
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 def get_crypt_password(password):
39 def get_crypt_password(password):
40 """
40 """
41 Cryptographic function used for password hashing
41 Cryptographic function used for password hashing
42 @param password: password to hash
42 @param password: password to hash
43 """
43 """
44 return crypt.crypt(password, '6a')
44 return crypt.crypt(password, '6a')
45
45
46
46
47 @cache_region('super_short_term', 'cached_user')
47 @cache_region('super_short_term', 'cached_user')
48 def get_user_cached(username):
48 def get_user_cached(username):
49 sa = meta.Session
49 sa = meta.Session
50 try:
50 try:
51 user = sa.query(User).filter(User.username == username).one()
51 user = sa.query(User).filter(User.username == username).one()
52 finally:
52 finally:
53 meta.Session.remove()
53 meta.Session.remove()
54 return user
54 return user
55
55
56 def authfunc(environ, username, password):
56 def authfunc(environ, username, password):
57 password_crypt = get_crypt_password(password)
57 password_crypt = get_crypt_password(password)
58 try:
58 try:
59 user = get_user_cached(username)
59 user = get_user_cached(username)
60 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
60 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
61 log.error(e)
61 log.error(e)
62 user = None
62 user = None
63
63
64 if user:
64 if user:
65 if user.active:
65 if user.active:
66 if user.username == username and user.password == password_crypt:
66 if user.username == username and user.password == password_crypt:
67 log.info('user %s authenticated correctly', username)
67 log.info('user %s authenticated correctly', username)
68 return True
68 return True
69 else:
69 else:
70 log.error('user %s is disabled', username)
70 log.error('user %s is disabled', username)
71
71
72 return False
72 return False
73
73
74 class AuthUser(object):
74 class AuthUser(object):
75 """
75 """
76 A simple object that handles a mercurial username for authentication
76 A simple object that handles a mercurial username for authentication
77 """
77 """
78 def __init__(self):
78 def __init__(self):
79 self.username = 'None'
79 self.username = 'None'
80 self.name = ''
80 self.name = ''
81 self.lastname = ''
81 self.lastname = ''
82 self.user_id = None
82 self.user_id = None
83 self.is_authenticated = False
83 self.is_authenticated = False
84 self.is_admin = False
84 self.is_admin = False
85 self.permissions = {}
85 self.permissions = {}
86
86
87
87
88 def set_available_permissions(config):
88 def set_available_permissions(config):
89 """
89 """
90 This function will propagate pylons globals with all available defined
90 This function will propagate pylons globals with all available defined
91 permission given in db. We don't wannt to check each time from db for new
91 permission given in db. We don't wannt to check each time from db for new
92 permissions since adding a new permission also requires application restart
92 permissions since adding a new permission also requires application restart
93 ie. to decorate new views with the newly created permission
93 ie. to decorate new views with the newly created permission
94 @param config:
94 @param config:
95 """
95 """
96 log.info('getting information about all available permissions')
96 log.info('getting information about all available permissions')
97 try:
97 try:
98 sa = meta.Session
98 sa = meta.Session
99 all_perms = sa.query(Permission).all()
99 all_perms = sa.query(Permission).all()
100 finally:
100 finally:
101 meta.Session.remove()
101 meta.Session.remove()
102
102
103 config['available_permissions'] = [x.permission_name for x in all_perms]
103 config['available_permissions'] = [x.permission_name for x in all_perms]
104
104
105 def set_base_path(config):
105 def set_base_path(config):
106 config['base_path'] = config['pylons.app_globals'].base_path
106 config['base_path'] = config['pylons.app_globals'].base_path
107
107
108 def fill_perms(user):
108 def fill_perms(user):
109 """
110 Fills user permission attribute with permissions taken from database
111 @param user:
112 """
113
109 sa = meta.Session
114 sa = meta.Session
110 user.permissions['repositories'] = {}
115 user.permissions['repositories'] = {}
111
116
112 #first fetch default permissions
117 #first fetch default permissions
113 default_perms = sa.query(Repo2Perm, Repository, Permission)\
118 default_perms = sa.query(Repo2Perm, Repository, Permission)\
114 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
119 .join((Repository, Repo2Perm.repository_id == Repository.repo_id))\
115 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
120 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
116 .filter(Repo2Perm.user_id == sa.query(User).filter(User.username ==
121 .filter(Repo2Perm.user_id == sa.query(User).filter(User.username ==
117 'default').one().user_id).all()
122 'default').one().user_id).all()
118
123
119 if user.is_admin:
124 if user.is_admin:
120 user.permissions['global'] = set(['hg.admin'])
125 user.permissions['global'] = set(['hg.admin'])
121 #admin have all rights full
126 #admin have all rights full
122 for perm in default_perms:
127 for perm in default_perms:
123 p = 'repository.admin'
128 p = 'repository.admin'
124 user.permissions['repositories'][perm.Repo2Perm.repository] = p
129 user.permissions['repositories'][perm.Repo2Perm.repository.repo_name] = p
125
130
126 else:
131 else:
127 user.permissions['global'] = set()
132 user.permissions['global'] = set()
128 for perm in default_perms:
133 for perm in default_perms:
129 if perm.Repository.private:
134 if perm.Repository.private:
130 #disable defaults for private repos,
135 #disable defaults for private repos,
131 p = 'repository.none'
136 p = 'repository.none'
132 elif perm.Repository.user_id == user.user_id:
137 elif perm.Repository.user_id == user.user_id:
133 #set admin if owner
138 #set admin if owner
134 p = 'repository.admin'
139 p = 'repository.admin'
135 else:
140 else:
136 p = perm.Permission.permission_name
141 p = perm.Permission.permission_name
137
142
138 user.permissions['repositories'][perm.Repo2Perm.repository] = p
143 user.permissions['repositories'][perm.Repo2Perm.repository.repo_name] = p
139
144
140
145
141 user_perms = sa.query(Repo2Perm, Permission, Repository)\
146 user_perms = sa.query(Repo2Perm, Permission, Repository)\
142 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
147 .join((Repository, Repo2Perm.repository_id == Repository.repo_id))\
143 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
148 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
144 .filter(Repo2Perm.user_id == user.user_id).all()
149 .filter(Repo2Perm.user_id == user.user_id).all()
145 #overwrite userpermissions with defaults
150 #overwrite userpermissions with defaults
146 for perm in user_perms:
151 for perm in user_perms:
147 #set write if owner
152 #set write if owner
148 if perm.Repository.user_id == user.user_id:
153 if perm.Repository.user_id == user.user_id:
149 p = 'repository.write'
154 p = 'repository.write'
150 else:
155 else:
151 p = perm.Permission.permission_name
156 p = perm.Permission.permission_name
152 user.permissions['repositories'][perm.Repo2Perm.repository] = p
157 user.permissions['repositories'][perm.Repo2Perm.repository.repo_name] = p
153 meta.Session.remove()
158 meta.Session.remove()
154 return user
159 return user
155
160
156 def get_user(session):
161 def get_user(session):
157 """
162 """
158 Gets user from session, and wraps permissions into user
163 Gets user from session, and wraps permissions into user
159 @param session:
164 @param session:
160 """
165 """
161 user = session.get('hg_app_user', AuthUser())
166 user = session.get('hg_app_user', AuthUser())
162
167
163 if user.is_authenticated:
168 if user.is_authenticated:
164 user = fill_perms(user)
169 user = fill_perms(user)
165
166 session['hg_app_user'] = user
170 session['hg_app_user'] = user
167 session.save()
171 session.save()
168 return user
172 return user
169
173
170 #===============================================================================
174 #===============================================================================
171 # CHECK DECORATORS
175 # CHECK DECORATORS
172 #===============================================================================
176 #===============================================================================
173 class LoginRequired(object):
177 class LoginRequired(object):
174 """
178 """
175 Must be logged in to execute this function else redirect to login page
179 Must be logged in to execute this function else redirect to login page
176 """
180 """
177
181
178 def __call__(self, func):
182 def __call__(self, func):
179 @wraps(func)
183 @wraps(func)
180 def _wrapper(*fargs, **fkwargs):
184 def _wrapper(*fargs, **fkwargs):
181 user = session.get('hg_app_user', AuthUser())
185 user = session.get('hg_app_user', AuthUser())
182 log.debug('Checking login required for user:%s', user.username)
186 log.debug('Checking login required for user:%s', user.username)
183 if user.is_authenticated:
187 if user.is_authenticated:
184 log.debug('user %s is authenticated', user.username)
188 log.debug('user %s is authenticated', user.username)
185 func(*fargs)
189 func(*fargs)
186 else:
190 else:
187 log.warn('user %s not authenticated', user.username)
191 log.warn('user %s not authenticated', user.username)
188 log.debug('redirecting to login page')
192 log.debug('redirecting to login page')
189 return redirect(url('login_home'))
193 return redirect(url('login_home'))
190
194
191 return _wrapper
195 return _wrapper
192
196
193 class PermsDecorator(object):
197 class PermsDecorator(object):
194 """
198 """
195 Base class for decorators
199 Base class for decorators
196 """
200 """
197
201
198 def __init__(self, *required_perms):
202 def __init__(self, *required_perms):
199 available_perms = config['available_permissions']
203 available_perms = config['available_permissions']
200 for perm in required_perms:
204 for perm in required_perms:
201 if perm not in available_perms:
205 if perm not in available_perms:
202 raise Exception("'%s' permission is not defined" % perm)
206 raise Exception("'%s' permission is not defined" % perm)
203 self.required_perms = set(required_perms)
207 self.required_perms = set(required_perms)
204 self.user_perms = None
208 self.user_perms = None
205
209
206 def __call__(self, func):
210 def __call__(self, func):
207 @wraps(func)
211 @wraps(func)
208 def _wrapper(*fargs, **fkwargs):
212 def _wrapper(*fargs, **fkwargs):
209 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
213 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
210 log.debug('checking %s permissions %s for %s',
214 log.debug('checking %s permissions %s for %s',
211 self.__class__.__name__, self.required_perms, func.__name__)
215 self.__class__.__name__, self.required_perms, func.__name__)
212
216
213 if self.check_permissions():
217 if self.check_permissions():
214 log.debug('Permission granted for %s', func.__name__)
218 log.debug('Permission granted for %s', func.__name__)
215 return func(*fargs)
219 return func(*fargs)
216
220
217 else:
221 else:
218 log.warning('Permission denied for %s', func.__name__)
222 log.warning('Permission denied for %s', func.__name__)
219 #redirect with forbidden ret code
223 #redirect with forbidden ret code
220 return abort(403)
224 return abort(403)
221 return _wrapper
225 return _wrapper
222
226
223
227
224 def check_permissions(self):
228 def check_permissions(self):
225 """
229 """
226 Dummy function for overriding
230 Dummy function for overriding
227 """
231 """
228 raise Exception('You have to write this function in child class')
232 raise Exception('You have to write this function in child class')
229
233
230 class HasPermissionAllDecorator(PermsDecorator):
234 class HasPermissionAllDecorator(PermsDecorator):
231 """
235 """
232 Checks for access permission for all given predicates. All of them have to
236 Checks for access permission for all given predicates. All of them have to
233 be meet in order to fulfill the request
237 be meet in order to fulfill the request
234 """
238 """
235
239
236 def check_permissions(self):
240 def check_permissions(self):
237 if self.required_perms.issubset(self.user_perms.get('global')):
241 if self.required_perms.issubset(self.user_perms.get('global')):
238 return True
242 return True
239 return False
243 return False
240
244
241
245
242 class HasPermissionAnyDecorator(PermsDecorator):
246 class HasPermissionAnyDecorator(PermsDecorator):
243 """
247 """
244 Checks for access permission for any of given predicates. In order to
248 Checks for access permission for any of given predicates. In order to
245 fulfill the request any of predicates must be meet
249 fulfill the request any of predicates must be meet
246 """
250 """
247
251
248 def check_permissions(self):
252 def check_permissions(self):
249 if self.required_perms.intersection(self.user_perms.get('global')):
253 if self.required_perms.intersection(self.user_perms.get('global')):
250 return True
254 return True
251 return False
255 return False
252
256
253 class HasRepoPermissionAllDecorator(PermsDecorator):
257 class HasRepoPermissionAllDecorator(PermsDecorator):
254 """
258 """
255 Checks for access permission for all given predicates for specific
259 Checks for access permission for all given predicates for specific
256 repository. All of them have to be meet in order to fulfill the request
260 repository. All of them have to be meet in order to fulfill the request
257 """
261 """
258
262
259 def check_permissions(self):
263 def check_permissions(self):
260 repo_name = get_repo_slug(request)
264 repo_name = get_repo_slug(request)
261 try:
265 try:
262 user_perms = set([self.user_perms['repositories'][repo_name]])
266 user_perms = set([self.user_perms['repositories'][repo_name]])
263 except KeyError:
267 except KeyError:
264 return False
268 return False
265 if self.required_perms.issubset(user_perms):
269 if self.required_perms.issubset(user_perms):
266 return True
270 return True
267 return False
271 return False
268
272
269
273
270 class HasRepoPermissionAnyDecorator(PermsDecorator):
274 class HasRepoPermissionAnyDecorator(PermsDecorator):
271 """
275 """
272 Checks for access permission for any of given predicates for specific
276 Checks for access permission for any of given predicates for specific
273 repository. In order to fulfill the request any of predicates must be meet
277 repository. In order to fulfill the request any of predicates must be meet
274 """
278 """
275
279
276 def check_permissions(self):
280 def check_permissions(self):
277 repo_name = get_repo_slug(request)
281 repo_name = get_repo_slug(request)
278
282
279 try:
283 try:
280 user_perms = set([self.user_perms['repositories'][repo_name]])
284 user_perms = set([self.user_perms['repositories'][repo_name]])
281 except KeyError:
285 except KeyError:
282 return False
286 return False
283 if self.required_perms.intersection(user_perms):
287 if self.required_perms.intersection(user_perms):
284 return True
288 return True
285 return False
289 return False
286 #===============================================================================
290 #===============================================================================
287 # CHECK FUNCTIONS
291 # CHECK FUNCTIONS
288 #===============================================================================
292 #===============================================================================
289
293
290 class PermsFunction(object):
294 class PermsFunction(object):
291 """
295 """
292 Base function for other check functions
296 Base function for other check functions
293 """
297 """
294
298
295 def __init__(self, *perms):
299 def __init__(self, *perms):
296 available_perms = config['available_permissions']
300 available_perms = config['available_permissions']
297
301
298 for perm in perms:
302 for perm in perms:
299 if perm not in available_perms:
303 if perm not in available_perms:
300 raise Exception("'%s' permission in not defined" % perm)
304 raise Exception("'%s' permission in not defined" % perm)
301 self.required_perms = set(perms)
305 self.required_perms = set(perms)
302 self.user_perms = None
306 self.user_perms = None
303 self.granted_for = ''
307 self.granted_for = ''
304 self.repo_name = None
308 self.repo_name = None
305
309
306 def __call__(self, check_Location=''):
310 def __call__(self, check_Location=''):
307 user = session.get('hg_app_user', False)
311 user = session.get('hg_app_user', False)
308 if not user:
312 if not user:
309 return False
313 return False
310 self.user_perms = user.permissions
314 self.user_perms = user.permissions
311 self.granted_for = user.username
315 self.granted_for = user.username
312 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
316 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
313
317
314 if self.check_permissions():
318 if self.check_permissions():
315 log.debug('Permission granted for %s @%s', self.granted_for,
319 log.debug('Permission granted for %s @%s', self.granted_for,
316 check_Location)
320 check_Location)
317 return True
321 return True
318
322
319 else:
323 else:
320 log.warning('Permission denied for %s @%s', self.granted_for,
324 log.warning('Permission denied for %s @%s', self.granted_for,
321 check_Location)
325 check_Location)
322 return False
326 return False
323
327
324 def check_permissions(self):
328 def check_permissions(self):
325 """
329 """
326 Dummy function for overriding
330 Dummy function for overriding
327 """
331 """
328 raise Exception('You have to write this function in child class')
332 raise Exception('You have to write this function in child class')
329
333
330 class HasPermissionAll(PermsFunction):
334 class HasPermissionAll(PermsFunction):
331 def check_permissions(self):
335 def check_permissions(self):
332 if self.required_perms.issubset(self.user_perms.get('global')):
336 if self.required_perms.issubset(self.user_perms.get('global')):
333 return True
337 return True
334 return False
338 return False
335
339
336 class HasPermissionAny(PermsFunction):
340 class HasPermissionAny(PermsFunction):
337 def check_permissions(self):
341 def check_permissions(self):
338 if self.required_perms.intersection(self.user_perms.get('global')):
342 if self.required_perms.intersection(self.user_perms.get('global')):
339 return True
343 return True
340 return False
344 return False
341
345
342 class HasRepoPermissionAll(PermsFunction):
346 class HasRepoPermissionAll(PermsFunction):
343
347
344 def __call__(self, repo_name=None, check_Location=''):
348 def __call__(self, repo_name=None, check_Location=''):
345 self.repo_name = repo_name
349 self.repo_name = repo_name
346 return super(HasRepoPermissionAll, self).__call__(check_Location)
350 return super(HasRepoPermissionAll, self).__call__(check_Location)
347
351
348 def check_permissions(self):
352 def check_permissions(self):
349 if not self.repo_name:
353 if not self.repo_name:
350 self.repo_name = get_repo_slug(request)
354 self.repo_name = get_repo_slug(request)
351
355
352 try:
356 try:
353 self.user_perms = set([self.user_perms['repositories']\
357 self.user_perms = set([self.user_perms['repositories']\
354 [self.repo_name]])
358 [self.repo_name]])
355 except KeyError:
359 except KeyError:
356 return False
360 return False
357 self.granted_for = self.repo_name
361 self.granted_for = self.repo_name
358 if self.required_perms.issubset(self.user_perms):
362 if self.required_perms.issubset(self.user_perms):
359 return True
363 return True
360 return False
364 return False
361
365
362 class HasRepoPermissionAny(PermsFunction):
366 class HasRepoPermissionAny(PermsFunction):
363
367
364
368
365 def __call__(self, repo_name=None, check_Location=''):
369 def __call__(self, repo_name=None, check_Location=''):
366 self.repo_name = repo_name
370 self.repo_name = repo_name
367 return super(HasRepoPermissionAny, self).__call__(check_Location)
371 return super(HasRepoPermissionAny, self).__call__(check_Location)
368
372
369 def check_permissions(self):
373 def check_permissions(self):
370 if not self.repo_name:
374 if not self.repo_name:
371 self.repo_name = get_repo_slug(request)
375 self.repo_name = get_repo_slug(request)
372
376
373 try:
377 try:
374 self.user_perms = set([self.user_perms['repositories']\
378 self.user_perms = set([self.user_perms['repositories']\
375 [self.repo_name]])
379 [self.repo_name]])
376 except KeyError:
380 except KeyError:
377 return False
381 return False
378 self.granted_for = self.repo_name
382 self.granted_for = self.repo_name
379 if self.required_perms.intersection(self.user_perms):
383 if self.required_perms.intersection(self.user_perms):
380 return True
384 return True
381 return False
385 return False
382
386
383 #===============================================================================
387 #===============================================================================
384 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
388 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
385 #===============================================================================
389 #===============================================================================
386
390
387 class HasPermissionAnyMiddleware(object):
391 class HasPermissionAnyMiddleware(object):
388 def __init__(self, *perms):
392 def __init__(self, *perms):
389 self.required_perms = set(perms)
393 self.required_perms = set(perms)
390
394
391 def __call__(self, user, repo_name):
395 def __call__(self, user, repo_name):
392 usr = AuthUser()
396 usr = AuthUser()
393 usr.user_id = user.user_id
397 usr.user_id = user.user_id
394 usr.username = user.username
398 usr.username = user.username
395 usr.is_admin = user.admin
399 usr.is_admin = user.admin
396
400
397 try:
401 try:
398 self.user_perms = set([fill_perms(usr)\
402 self.user_perms = set([fill_perms(usr)\
399 .permissions['repositories'][repo_name]])
403 .permissions['repositories'][repo_name]])
400 except:
404 except:
401 self.user_perms = set()
405 self.user_perms = set()
402 self.granted_for = ''
406 self.granted_for = ''
403 self.username = user.username
407 self.username = user.username
404 self.repo_name = repo_name
408 self.repo_name = repo_name
405 return self.check_permissions()
409 return self.check_permissions()
406
410
407 def check_permissions(self):
411 def check_permissions(self):
408 log.debug('checking mercurial protocol '
412 log.debug('checking mercurial protocol '
409 'permissions for user:%s repository:%s',
413 'permissions for user:%s repository:%s',
410 self.username, self.repo_name)
414 self.username, self.repo_name)
411 if self.required_perms.intersection(self.user_perms):
415 if self.required_perms.intersection(self.user_perms):
412 log.debug('permission granted')
416 log.debug('permission granted')
413 return True
417 return True
414 log.debug('permission denied')
418 log.debug('permission denied')
415 return False
419 return False
@@ -1,192 +1,192
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # database managment for hg app
3 # database managment for hg app
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 """
21 """
22 Created on April 10, 2010
22 Created on April 10, 2010
23 database managment and creation for hg app
23 database managment and creation for hg app
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 from os.path import dirname as dn, join as jn
27 from os.path import dirname as dn, join as jn
28 import os
28 import os
29 import sys
29 import sys
30 import uuid
30 import uuid
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
32 sys.path.append(ROOT)
32 sys.path.append(ROOT)
33
33
34 from pylons_app.lib.auth import get_crypt_password
34 from pylons_app.lib.auth import get_crypt_password
35 from pylons_app.lib.utils import ask_ok
35 from pylons_app.lib.utils import ask_ok
36 from pylons_app.model import init_model
36 from pylons_app.model import init_model
37 from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings
37 from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings
38 from pylons_app.model import meta
38 from pylons_app.model import meta
39 from sqlalchemy.engine import create_engine
39 from sqlalchemy.engine import create_engine
40 import logging
40 import logging
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 class DbManage(object):
44 class DbManage(object):
45 def __init__(self, log_sql):
45 def __init__(self, log_sql):
46 self.dbname = 'hg_app.db'
46 self.dbname = 'hg_app.db'
47 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
47 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
48 engine = create_engine(dburi, echo=log_sql)
48 engine = create_engine(dburi, echo=log_sql)
49 init_model(engine)
49 init_model(engine)
50 self.sa = meta.Session
50 self.sa = meta.Session
51 self.db_exists = False
51 self.db_exists = False
52
52
53 def check_for_db(self, override):
53 def check_for_db(self, override):
54 log.info('checking for exisiting db')
54 log.info('checking for exisiting db')
55 if os.path.isfile(jn(ROOT, self.dbname)):
55 if os.path.isfile(jn(ROOT, self.dbname)):
56 self.db_exists = True
56 self.db_exists = True
57 log.info('database exisist')
57 log.info('database exisist')
58 if not override:
58 if not override:
59 raise Exception('database already exists')
59 raise Exception('database already exists')
60
60
61 def create_tables(self, override=False):
61 def create_tables(self, override=False):
62 """
62 """
63 Create a auth database
63 Create a auth database
64 """
64 """
65 self.check_for_db(override)
65 self.check_for_db(override)
66 if override:
66 if override:
67 log.info("database exisist and it's going to be destroyed")
67 log.info("database exisist and it's going to be destroyed")
68 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
68 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
69 if not destroy:
69 if not destroy:
70 sys.exit()
70 sys.exit()
71 if self.db_exists and destroy:
71 if self.db_exists and destroy:
72 os.remove(jn(ROOT, self.dbname))
72 os.remove(jn(ROOT, self.dbname))
73 checkfirst = not override
73 checkfirst = not override
74 meta.Base.metadata.create_all(checkfirst=checkfirst)
74 meta.Base.metadata.create_all(checkfirst=checkfirst)
75 log.info('Created tables for %s', self.dbname)
75 log.info('Created tables for %s', self.dbname)
76
76
77 def admin_prompt(self):
77 def admin_prompt(self):
78 import getpass
78 import getpass
79 username = raw_input('Specify admin username:')
79 username = raw_input('Specify admin username:')
80 password = getpass.getpass('Specify admin password:')
80 password = getpass.getpass('Specify admin password:')
81 self.create_user(username, password, True)
81 self.create_user(username, password, True)
82
82
83 def config_prompt(self):
83 def config_prompt(self):
84 log.info('Setting up repositories config')
84 log.info('Setting up repositories config')
85
85
86 path = raw_input('Specify valid full path to your repositories'
86 path = raw_input('Specify valid full path to your repositories'
87 ' you can change this later in application settings:')
87 ' you can change this later in application settings:')
88
88
89 if not os.path.isdir(path):
89 if not os.path.isdir(path):
90 log.error('You entered wrong path')
90 log.error('You entered wrong path')
91 sys.exit()
91 sys.exit()
92
92
93 hooks = HgAppUi()
93 hooks = HgAppUi()
94 hooks.ui_section = 'hooks'
94 hooks.ui_section = 'hooks'
95 hooks.ui_key = 'changegroup'
95 hooks.ui_key = 'changegroup'
96 hooks.ui_value = 'hg update >&2'
96 hooks.ui_value = 'hg update >&2'
97
97
98 web1 = HgAppUi()
98 web1 = HgAppUi()
99 web1.ui_section = 'web'
99 web1.ui_section = 'web'
100 web1.ui_key = 'push_ssl'
100 web1.ui_key = 'push_ssl'
101 web1.ui_value = 'false'
101 web1.ui_value = 'false'
102
102
103 web2 = HgAppUi()
103 web2 = HgAppUi()
104 web2.ui_section = 'web'
104 web2.ui_section = 'web'
105 web2.ui_key = 'allow_archive'
105 web2.ui_key = 'allow_archive'
106 web2.ui_value = 'gz zip bz2'
106 web2.ui_value = 'gz zip bz2'
107
107
108 web3 = HgAppUi()
108 web3 = HgAppUi()
109 web3.ui_section = 'web'
109 web3.ui_section = 'web'
110 web3.ui_key = 'allow_push'
110 web3.ui_key = 'allow_push'
111 web3.ui_value = '*'
111 web3.ui_value = '*'
112
112
113 web4 = HgAppUi()
113 web4 = HgAppUi()
114 web4.ui_section = 'web'
114 web4.ui_section = 'web'
115 web4.ui_key = 'baseurl'
115 web4.ui_key = 'baseurl'
116 web4.ui_value = '/'
116 web4.ui_value = '/'
117
117
118 paths = HgAppUi()
118 paths = HgAppUi()
119 paths.ui_section = 'paths'
119 paths.ui_section = 'paths'
120 paths.ui_key = '/'
120 paths.ui_key = '/'
121 paths.ui_value = os.path.join(path, '*')
121 paths.ui_value = os.path.join(path, '*')
122
122
123
123
124 hgsettings = HgAppSettings()
124 hgsettings = HgAppSettings()
125 hgsettings.app_auth_realm = 'hg-app authentication'
125 hgsettings.app_auth_realm = 'hg-app authentication'
126 hgsettings.app_title = 'hg-app'
126 hgsettings.app_title = 'hg-app'
127
127
128 try:
128 try:
129 self.sa.add(hooks)
129 #self.sa.add(hooks)
130 self.sa.add(web1)
130 self.sa.add(web1)
131 self.sa.add(web2)
131 self.sa.add(web2)
132 self.sa.add(web3)
132 self.sa.add(web3)
133 self.sa.add(web4)
133 self.sa.add(web4)
134 self.sa.add(paths)
134 self.sa.add(paths)
135 self.sa.add(hgsettings)
135 self.sa.add(hgsettings)
136 self.sa.commit()
136 self.sa.commit()
137 except:
137 except:
138 self.sa.rollback()
138 self.sa.rollback()
139 raise
139 raise
140 log.info('created ui config')
140 log.info('created ui config')
141
141
142 def create_user(self, username, password, admin=False):
142 def create_user(self, username, password, admin=False):
143
143
144 log.info('creating default user')
144 log.info('creating default user')
145 #create default user for handling default permissions.
145 #create default user for handling default permissions.
146 def_user = User()
146 def_user = User()
147 def_user.username = 'default'
147 def_user.username = 'default'
148 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
148 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
149 def_user.name = 'default'
149 def_user.name = 'default'
150 def_user.lastname = 'default'
150 def_user.lastname = 'default'
151 def_user.email = 'default@default.com'
151 def_user.email = 'default@default.com'
152 def_user.admin = False
152 def_user.admin = False
153 def_user.active = False
153 def_user.active = False
154
154
155 log.info('creating administrator user %s', username)
155 log.info('creating administrator user %s', username)
156 new_user = User()
156 new_user = User()
157 new_user.username = username
157 new_user.username = username
158 new_user.password = get_crypt_password(password)
158 new_user.password = get_crypt_password(password)
159 new_user.name = 'Hg'
159 new_user.name = 'Hg'
160 new_user.lastname = 'Admin'
160 new_user.lastname = 'Admin'
161 new_user.email = 'admin@localhost'
161 new_user.email = 'admin@localhost'
162 new_user.admin = admin
162 new_user.admin = admin
163 new_user.active = True
163 new_user.active = True
164
164
165 try:
165 try:
166 self.sa.add(def_user)
166 self.sa.add(def_user)
167 self.sa.add(new_user)
167 self.sa.add(new_user)
168 self.sa.commit()
168 self.sa.commit()
169 except:
169 except:
170 self.sa.rollback()
170 self.sa.rollback()
171 raise
171 raise
172
172
173 def create_permissions(self):
173 def create_permissions(self):
174 #module.(access|create|change|delete)_[name]
174 #module.(access|create|change|delete)_[name]
175 #module.(read|write|owner)
175 #module.(read|write|owner)
176 perms = [('repository.none', 'Repository no access'),
176 perms = [('repository.none', 'Repository no access'),
177 ('repository.read', 'Repository read access'),
177 ('repository.read', 'Repository read access'),
178 ('repository.write', 'Repository write access'),
178 ('repository.write', 'Repository write access'),
179 ('repository.admin', 'Repository admin access'),
179 ('repository.admin', 'Repository admin access'),
180 ('hg.admin', 'Hg Administrator'),
180 ('hg.admin', 'Hg Administrator'),
181 ]
181 ]
182
182
183 for p in perms:
183 for p in perms:
184 new_perm = Permission()
184 new_perm = Permission()
185 new_perm.permission_name = p[0]
185 new_perm.permission_name = p[0]
186 new_perm.permission_longname = p[1]
186 new_perm.permission_longname = p[1]
187 try:
187 try:
188 self.sa.add(new_perm)
188 self.sa.add(new_perm)
189 self.sa.commit()
189 self.sa.commit()
190 except:
190 except:
191 self.sa.rollback()
191 self.sa.rollback()
192 raise
192 raise
@@ -1,329 +1,329
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for hg app
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for hg app
22 Utilities for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
30 from vcs.backends.base import BaseChangeset
30 from vcs.backends.base import BaseChangeset
31 from vcs.utils.lazy import LazyProperty
31 from vcs.utils.lazy import LazyProperty
32 import logging
32 import logging
33 import os
33 import os
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 def get_repo_slug(request):
37 def get_repo_slug(request):
38 return request.environ['pylons.routes_dict'].get('repo_name')
38 return request.environ['pylons.routes_dict'].get('repo_name')
39
39
40 def is_mercurial(environ):
40 def is_mercurial(environ):
41 """
41 """
42 Returns True if request's target is mercurial server - header
42 Returns True if request's target is mercurial server - header
43 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
43 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
44 """
44 """
45 http_accept = environ.get('HTTP_ACCEPT')
45 http_accept = environ.get('HTTP_ACCEPT')
46 if http_accept and http_accept.startswith('application/mercurial'):
46 if http_accept and http_accept.startswith('application/mercurial'):
47 return True
47 return True
48 return False
48 return False
49
49
50 def check_repo_dir(paths):
50 def check_repo_dir(paths):
51 repos_path = paths[0][1].split('/')
51 repos_path = paths[0][1].split('/')
52 if repos_path[-1] in ['*', '**']:
52 if repos_path[-1] in ['*', '**']:
53 repos_path = repos_path[:-1]
53 repos_path = repos_path[:-1]
54 if repos_path[0] != '/':
54 if repos_path[0] != '/':
55 repos_path[0] = '/'
55 repos_path[0] = '/'
56 if not os.path.isdir(os.path.join(*repos_path)):
56 if not os.path.isdir(os.path.join(*repos_path)):
57 raise Exception('Not a valid repository in %s' % paths[0][1])
57 raise Exception('Not a valid repository in %s' % paths[0][1])
58
58
59 def check_repo_fast(repo_name, base_path):
59 def check_repo_fast(repo_name, base_path):
60 if os.path.isdir(os.path.join(base_path, repo_name)):return False
60 if os.path.isdir(os.path.join(base_path, repo_name)):return False
61 return True
61 return True
62
62
63 def check_repo(repo_name, base_path, verify=True):
63 def check_repo(repo_name, base_path, verify=True):
64
64
65 repo_path = os.path.join(base_path, repo_name)
65 repo_path = os.path.join(base_path, repo_name)
66
66
67 try:
67 try:
68 if not check_repo_fast(repo_name, base_path):
68 if not check_repo_fast(repo_name, base_path):
69 return False
69 return False
70 r = hg.repository(ui.ui(), repo_path)
70 r = hg.repository(ui.ui(), repo_path)
71 if verify:
71 if verify:
72 hg.verify(r)
72 hg.verify(r)
73 #here we hnow that repo exists it was verified
73 #here we hnow that repo exists it was verified
74 log.info('%s repo is already created', repo_name)
74 log.info('%s repo is already created', repo_name)
75 return False
75 return False
76 except RepoError:
76 except RepoError:
77 #it means that there is no valid repo there...
77 #it means that there is no valid repo there...
78 log.info('%s repo is free for creation', repo_name)
78 log.info('%s repo is free for creation', repo_name)
79 return True
79 return True
80
80
81 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
81 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
82 while True:
82 while True:
83 ok = raw_input(prompt)
83 ok = raw_input(prompt)
84 if ok in ('y', 'ye', 'yes'): return True
84 if ok in ('y', 'ye', 'yes'): return True
85 if ok in ('n', 'no', 'nop', 'nope'): return False
85 if ok in ('n', 'no', 'nop', 'nope'): return False
86 retries = retries - 1
86 retries = retries - 1
87 if retries < 0: raise IOError
87 if retries < 0: raise IOError
88 print complaint
88 print complaint
89
89
90 @cache_region('super_short_term', 'cached_hg_ui')
90 @cache_region('super_short_term', 'cached_hg_ui')
91 def get_hg_ui_cached():
91 def get_hg_ui_cached():
92 try:
92 try:
93 sa = meta.Session
93 sa = meta.Session
94 ret = sa.query(HgAppUi).all()
94 ret = sa.query(HgAppUi).all()
95 finally:
95 finally:
96 meta.Session.remove()
96 meta.Session.remove()
97 return ret
97 return ret
98
98
99
99
100 def get_hg_settings():
100 def get_hg_settings():
101 try:
101 try:
102 sa = meta.Session
102 sa = meta.Session
103 ret = sa.query(HgAppSettings).scalar()
103 ret = sa.query(HgAppSettings).scalar()
104 finally:
104 finally:
105 meta.Session.remove()
105 meta.Session.remove()
106
106
107 if not ret:
107 if not ret:
108 raise Exception('Could not get application settings !')
108 raise Exception('Could not get application settings !')
109 return ret
109 return ret
110
110
111 def make_ui(read_from='file', path=None, checkpaths=True):
111 def make_ui(read_from='file', path=None, checkpaths=True):
112 """
112 """
113 A function that will read python rc files or database
113 A function that will read python rc files or database
114 and make an mercurial ui object from read options
114 and make an mercurial ui object from read options
115
115
116 @param path: path to mercurial config file
116 @param path: path to mercurial config file
117 @param checkpaths: check the path
117 @param checkpaths: check the path
118 @param read_from: read from 'file' or 'db'
118 @param read_from: read from 'file' or 'db'
119 """
119 """
120 #propagated from mercurial documentation
120 #propagated from mercurial documentation
121 sections = ['alias', 'auth',
121 sections = ['alias', 'auth',
122 'decode/encode', 'defaults',
122 'decode/encode', 'defaults',
123 'diff', 'email',
123 'diff', 'email',
124 'extensions', 'format',
124 'extensions', 'format',
125 'merge-patterns', 'merge-tools',
125 'merge-patterns', 'merge-tools',
126 'hooks', 'http_proxy',
126 'hooks', 'http_proxy',
127 'smtp', 'patch',
127 'smtp', 'patch',
128 'paths', 'profiling',
128 'paths', 'profiling',
129 'server', 'trusted',
129 'server', 'trusted',
130 'ui', 'web', ]
130 'ui', 'web', ]
131 baseui = ui.ui()
131 baseui = ui.ui()
132
132
133
133
134 if read_from == 'file':
134 if read_from == 'file':
135 if not os.path.isfile(path):
135 if not os.path.isfile(path):
136 log.warning('Unable to read config file %s' % path)
136 log.warning('Unable to read config file %s' % path)
137 return False
137 return False
138
138
139 cfg = config.config()
139 cfg = config.config()
140 cfg.read(path)
140 cfg.read(path)
141 for section in sections:
141 for section in sections:
142 for k, v in cfg.items(section):
142 for k, v in cfg.items(section):
143 baseui.setconfig(section, k, v)
143 baseui.setconfig(section, k, v)
144 if checkpaths:check_repo_dir(cfg.items('paths'))
144 if checkpaths:check_repo_dir(cfg.items('paths'))
145
145
146
146
147 elif read_from == 'db':
147 elif read_from == 'db':
148 hg_ui = get_hg_ui_cached()
148 hg_ui = get_hg_ui_cached()
149 for ui_ in hg_ui:
149 for ui_ in hg_ui:
150 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
150 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
151
151
152
152
153 return baseui
153 return baseui
154
154
155
155
156 def set_hg_app_config(config):
156 def set_hg_app_config(config):
157 hgsettings = get_hg_settings()
157 hgsettings = get_hg_settings()
158 config['hg_app_auth_realm'] = hgsettings.app_auth_realm
158 config['hg_app_auth_realm'] = hgsettings.app_auth_realm
159 config['hg_app_name'] = hgsettings.app_title
159 config['hg_app_name'] = hgsettings.app_title
160
160
161 def invalidate_cache(name, *args):
161 def invalidate_cache(name, *args):
162 """Invalidates given name cache"""
162 """Invalidates given name cache"""
163
163
164 from beaker.cache import region_invalidate
164 from beaker.cache import region_invalidate
165 log.info('INVALIDATING CACHE FOR %s', name)
165 log.info('INVALIDATING CACHE FOR %s', name)
166
166
167 """propagate our arguments to make sure invalidation works. First
167 """propagate our arguments to make sure invalidation works. First
168 argument has to be the name of cached func name give to cache decorator
168 argument has to be the name of cached func name give to cache decorator
169 without that the invalidation would not work"""
169 without that the invalidation would not work"""
170 tmp = [name]
170 tmp = [name]
171 tmp.extend(args)
171 tmp.extend(args)
172 args = tuple(tmp)
172 args = tuple(tmp)
173
173
174 if name == 'cached_repo_list':
174 if name == 'cached_repo_list':
175 from pylons_app.model.hg_model import _get_repos_cached
175 from pylons_app.model.hg_model import _get_repos_cached
176 region_invalidate(_get_repos_cached, None, *args)
176 region_invalidate(_get_repos_cached, None, *args)
177
177
178 if name == 'full_changelog':
178 if name == 'full_changelog':
179 from pylons_app.model.hg_model import _full_changelog_cached
179 from pylons_app.model.hg_model import _full_changelog_cached
180 region_invalidate(_full_changelog_cached, None, *args)
180 region_invalidate(_full_changelog_cached, None, *args)
181
181
182 class EmptyChangeset(BaseChangeset):
182 class EmptyChangeset(BaseChangeset):
183
183
184 revision = -1
184 revision = -1
185 message = ''
185 message = ''
186
186
187 @LazyProperty
187 @LazyProperty
188 def raw_id(self):
188 def raw_id(self):
189 """
189 """
190 Returns raw string identifing this changeset, useful for web
190 Returns raw string identifing this changeset, useful for web
191 representation.
191 representation.
192 """
192 """
193 return '0' * 12
193 return '0' * 12
194
194
195
195
196 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
196 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
197 """
197 """
198 maps all found repositories into db
198 maps all found repositories into db
199 """
199 """
200 from pylons_app.model.repo_model import RepoModel
200 from pylons_app.model.repo_model import RepoModel
201
201
202 sa = meta.Session
202 sa = meta.Session
203 user = sa.query(User).filter(User.admin == True).first()
203 user = sa.query(User).filter(User.admin == True).first()
204
204
205 rm = RepoModel()
205 rm = RepoModel()
206
206
207 for name, repo in initial_repo_list.items():
207 for name, repo in initial_repo_list.items():
208 if not sa.query(Repository).get(name):
208 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
209 log.info('repository %s not found creating default', name)
209 log.info('repository %s not found creating default', name)
210
210
211 form_data = {
211 form_data = {
212 'repo_name':name,
212 'repo_name':name,
213 'description':repo.description if repo.description != 'unknown' else \
213 'description':repo.description if repo.description != 'unknown' else \
214 'auto description for %s' % name,
214 'auto description for %s' % name,
215 'private':False
215 'private':False
216 }
216 }
217 rm.create(form_data, user, just_db=True)
217 rm.create(form_data, user, just_db=True)
218
218
219
219
220 if remove_obsolete:
220 if remove_obsolete:
221 #remove from database those repositories that are not in the filesystem
221 #remove from database those repositories that are not in the filesystem
222 for repo in sa.query(Repository).all():
222 for repo in sa.query(Repository).all():
223 if repo.repo_name not in initial_repo_list.keys():
223 if repo.repo_name not in initial_repo_list.keys():
224 sa.delete(repo)
224 sa.delete(repo)
225 sa.commit()
225 sa.commit()
226
226
227
227
228 meta.Session.remove()
228 meta.Session.remove()
229
229
230 from UserDict import DictMixin
230 from UserDict import DictMixin
231
231
232 class OrderedDict(dict, DictMixin):
232 class OrderedDict(dict, DictMixin):
233
233
234 def __init__(self, *args, **kwds):
234 def __init__(self, *args, **kwds):
235 if len(args) > 1:
235 if len(args) > 1:
236 raise TypeError('expected at most 1 arguments, got %d' % len(args))
236 raise TypeError('expected at most 1 arguments, got %d' % len(args))
237 try:
237 try:
238 self.__end
238 self.__end
239 except AttributeError:
239 except AttributeError:
240 self.clear()
240 self.clear()
241 self.update(*args, **kwds)
241 self.update(*args, **kwds)
242
242
243 def clear(self):
243 def clear(self):
244 self.__end = end = []
244 self.__end = end = []
245 end += [None, end, end] # sentinel node for doubly linked list
245 end += [None, end, end] # sentinel node for doubly linked list
246 self.__map = {} # key --> [key, prev, next]
246 self.__map = {} # key --> [key, prev, next]
247 dict.clear(self)
247 dict.clear(self)
248
248
249 def __setitem__(self, key, value):
249 def __setitem__(self, key, value):
250 if key not in self:
250 if key not in self:
251 end = self.__end
251 end = self.__end
252 curr = end[1]
252 curr = end[1]
253 curr[2] = end[1] = self.__map[key] = [key, curr, end]
253 curr[2] = end[1] = self.__map[key] = [key, curr, end]
254 dict.__setitem__(self, key, value)
254 dict.__setitem__(self, key, value)
255
255
256 def __delitem__(self, key):
256 def __delitem__(self, key):
257 dict.__delitem__(self, key)
257 dict.__delitem__(self, key)
258 key, prev, next = self.__map.pop(key)
258 key, prev, next = self.__map.pop(key)
259 prev[2] = next
259 prev[2] = next
260 next[1] = prev
260 next[1] = prev
261
261
262 def __iter__(self):
262 def __iter__(self):
263 end = self.__end
263 end = self.__end
264 curr = end[2]
264 curr = end[2]
265 while curr is not end:
265 while curr is not end:
266 yield curr[0]
266 yield curr[0]
267 curr = curr[2]
267 curr = curr[2]
268
268
269 def __reversed__(self):
269 def __reversed__(self):
270 end = self.__end
270 end = self.__end
271 curr = end[1]
271 curr = end[1]
272 while curr is not end:
272 while curr is not end:
273 yield curr[0]
273 yield curr[0]
274 curr = curr[1]
274 curr = curr[1]
275
275
276 def popitem(self, last=True):
276 def popitem(self, last=True):
277 if not self:
277 if not self:
278 raise KeyError('dictionary is empty')
278 raise KeyError('dictionary is empty')
279 if last:
279 if last:
280 key = reversed(self).next()
280 key = reversed(self).next()
281 else:
281 else:
282 key = iter(self).next()
282 key = iter(self).next()
283 value = self.pop(key)
283 value = self.pop(key)
284 return key, value
284 return key, value
285
285
286 def __reduce__(self):
286 def __reduce__(self):
287 items = [[k, self[k]] for k in self]
287 items = [[k, self[k]] for k in self]
288 tmp = self.__map, self.__end
288 tmp = self.__map, self.__end
289 del self.__map, self.__end
289 del self.__map, self.__end
290 inst_dict = vars(self).copy()
290 inst_dict = vars(self).copy()
291 self.__map, self.__end = tmp
291 self.__map, self.__end = tmp
292 if inst_dict:
292 if inst_dict:
293 return (self.__class__, (items,), inst_dict)
293 return (self.__class__, (items,), inst_dict)
294 return self.__class__, (items,)
294 return self.__class__, (items,)
295
295
296 def keys(self):
296 def keys(self):
297 return list(self)
297 return list(self)
298
298
299 setdefault = DictMixin.setdefault
299 setdefault = DictMixin.setdefault
300 update = DictMixin.update
300 update = DictMixin.update
301 pop = DictMixin.pop
301 pop = DictMixin.pop
302 values = DictMixin.values
302 values = DictMixin.values
303 items = DictMixin.items
303 items = DictMixin.items
304 iterkeys = DictMixin.iterkeys
304 iterkeys = DictMixin.iterkeys
305 itervalues = DictMixin.itervalues
305 itervalues = DictMixin.itervalues
306 iteritems = DictMixin.iteritems
306 iteritems = DictMixin.iteritems
307
307
308 def __repr__(self):
308 def __repr__(self):
309 if not self:
309 if not self:
310 return '%s()' % (self.__class__.__name__,)
310 return '%s()' % (self.__class__.__name__,)
311 return '%s(%r)' % (self.__class__.__name__, self.items())
311 return '%s(%r)' % (self.__class__.__name__, self.items())
312
312
313 def copy(self):
313 def copy(self):
314 return self.__class__(self)
314 return self.__class__(self)
315
315
316 @classmethod
316 @classmethod
317 def fromkeys(cls, iterable, value=None):
317 def fromkeys(cls, iterable, value=None):
318 d = cls()
318 d = cls()
319 for key in iterable:
319 for key in iterable:
320 d[key] = value
320 d[key] = value
321 return d
321 return d
322
322
323 def __eq__(self, other):
323 def __eq__(self, other):
324 if isinstance(other, OrderedDict):
324 if isinstance(other, OrderedDict):
325 return len(self) == len(other) and self.items() == other.items()
325 return len(self) == len(other) and self.items() == other.items()
326 return dict.__eq__(self, other)
326 return dict.__eq__(self, other)
327
327
328 def __ne__(self, other):
328 def __ne__(self, other):
329 return not self == other
329 return not self == other
@@ -1,86 +1,87
1 from pylons_app.model.meta import Base
1 from pylons_app.model.meta import Base
2 from sqlalchemy.orm import relation, backref
2 from sqlalchemy.orm import relation, backref
3 from sqlalchemy import *
3 from sqlalchemy import *
4 from vcs.utils.lazy import LazyProperty
4 from vcs.utils.lazy import LazyProperty
5
5
6 class HgAppSettings(Base):
6 class HgAppSettings(Base):
7 __tablename__ = 'hg_app_settings'
7 __tablename__ = 'hg_app_settings'
8 __table_args__ = {'useexisting':True}
8 __table_args__ = {'useexisting':True}
9 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
9 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
10 app_title = Column("app_title", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
10 app_title = Column("app_title", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
11 app_auth_realm = Column("auth_realm", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
11 app_auth_realm = Column("auth_realm", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
12
12
13 class HgAppUi(Base):
13 class HgAppUi(Base):
14 __tablename__ = 'hg_app_ui'
14 __tablename__ = 'hg_app_ui'
15 __table_args__ = {'useexisting':True}
15 __table_args__ = {'useexisting':True}
16 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
16 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
17 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
17 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
18 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
18 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
19 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
19 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
20
20
21 class User(Base):
21 class User(Base):
22 __tablename__ = 'users'
22 __tablename__ = 'users'
23 __table_args__ = {'useexisting':True}
23 __table_args__ = {'useexisting':True}
24 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
24 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
25 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
25 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
26 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
26 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
27 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
27 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
28 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
28 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
29 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
29 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
30 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
30 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
31 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
31 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
32 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
32 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
33
33
34 user_log = relation('UserLog')
34 user_log = relation('UserLog')
35
35
36 @LazyProperty
36 @LazyProperty
37 def full_contact(self):
37 def full_contact(self):
38 return '%s %s <%s>' % (self.name, self.lastname, self.email)
38 return '%s %s <%s>' % (self.name, self.lastname, self.email)
39
39
40 def __repr__(self):
40 def __repr__(self):
41 return "<User('%s:%s')>" % (self.user_id, self.username)
41 return "<User('%s:%s')>" % (self.user_id, self.username)
42
42
43 class UserLog(Base):
43 class UserLog(Base):
44 __tablename__ = 'user_logs'
44 __tablename__ = 'user_logs'
45 __table_args__ = {'useexisting':True}
45 __table_args__ = {'useexisting':True}
46 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
46 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
47 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
47 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
48 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
48 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
49 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
49 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
50 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
50 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
51 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
51 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
52
52
53 user = relation('User')
53 user = relation('User')
54
54
55 class Repository(Base):
55 class Repository(Base):
56 __tablename__ = 'repositories'
56 __tablename__ = 'repositories'
57 __table_args__ = {'useexisting':True}
57 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
58 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None, primary_key=True)
58 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
59 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
59 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
60 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
60 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
61 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
61 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
62 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
62
63
63 user = relation('User')
64 user = relation('User')
64 repo2perm = relation('Repo2Perm', cascade='all')
65 repo2perm = relation('Repo2Perm', cascade='all')
65
66
66 class Permission(Base):
67 class Permission(Base):
67 __tablename__ = 'permissions'
68 __tablename__ = 'permissions'
68 __table_args__ = {'useexisting':True}
69 __table_args__ = {'useexisting':True}
69 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
70 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
70 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72
73
73 def __repr__(self):
74 def __repr__(self):
74 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
75 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
75
76
76 class Repo2Perm(Base):
77 class Repo2Perm(Base):
77 __tablename__ = 'repo_to_perm'
78 __tablename__ = 'repo_to_perm'
78 __table_args__ = (UniqueConstraint('user_id', 'repository'), {'useexisting':True})
79 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
79 repo2perm_id = Column("repo2perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
80 repo2perm_id = Column("repo2perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
80 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
81 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
81 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
82 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
82 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
83 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
83
84
84 user = relation('User')
85 user = relation('User')
85 permission = relation('Permission')
86 permission = relation('Permission')
86
87 repository = relation('Repository')
@@ -1,307 +1,306
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 pylons_app.lib.auth import get_crypt_password
27 from pylons_app.lib.auth import get_crypt_password
28 import pylons_app.lib.helpers as h
28 import pylons_app.lib.helpers as h
29 from pylons_app.model import meta
29 from pylons_app.model import meta
30 from pylons_app.model.db import User, Repository
30 from pylons_app.model.db import User, Repository
31 from sqlalchemy.exc import OperationalError
31 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34 import datetime
34 import datetime
35 import formencode
35 import formencode
36 import logging
36 import logging
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 #this is needed to translate the messages using _() in validators
40 #this is needed to translate the messages using _() in validators
41 class State_obj(object):
41 class State_obj(object):
42 _ = staticmethod(_)
42 _ = staticmethod(_)
43
43
44 #===============================================================================
44 #===============================================================================
45 # VALIDATORS
45 # VALIDATORS
46 #===============================================================================
46 #===============================================================================
47 class ValidAuthToken(formencode.validators.FancyValidator):
47 class ValidAuthToken(formencode.validators.FancyValidator):
48 messages = {'invalid_token':_('Token mismatch')}
48 messages = {'invalid_token':_('Token mismatch')}
49
49
50 def validate_python(self, value, state):
50 def validate_python(self, value, state):
51
51
52 if value != authentication_token():
52 if value != authentication_token():
53 raise formencode.Invalid(self.message('invalid_token', state,
53 raise formencode.Invalid(self.message('invalid_token', state,
54 search_number=value), value, state)
54 search_number=value), value, state)
55
55
56 def ValidUsername(edit, old_data):
56 def ValidUsername(edit, old_data):
57 class _ValidUsername(formencode.validators.FancyValidator):
57 class _ValidUsername(formencode.validators.FancyValidator):
58
58
59 def validate_python(self, value, state):
59 def validate_python(self, value, state):
60 if value in ['default', 'new_user']:
60 if value in ['default', 'new_user']:
61 raise formencode.Invalid(_('Invalid username'), value, state)
61 raise formencode.Invalid(_('Invalid username'), value, state)
62 #check if user is uniq
62 #check if user is uniq
63 sa = meta.Session
63 sa = meta.Session
64 old_un = None
64 old_un = None
65 if edit:
65 if edit:
66 old_un = sa.query(User).get(old_data.get('user_id')).username
66 old_un = sa.query(User).get(old_data.get('user_id')).username
67
67
68 if old_un != value or not edit:
68 if old_un != value or not edit:
69 if sa.query(User).filter(User.username == value).scalar():
69 if sa.query(User).filter(User.username == value).scalar():
70 raise formencode.Invalid(_('This username already exists') ,
70 raise formencode.Invalid(_('This username already exists') ,
71 value, state)
71 value, state)
72 meta.Session.remove()
72 meta.Session.remove()
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 acccount is disabled')
86 'disabled_account':_('Your acccount 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 sa = meta.Session
95 sa = meta.Session
96 crypted_passwd = get_crypt_password(value['password'])
96 crypted_passwd = get_crypt_password(value['password'])
97 username = value['username']
97 username = value['username']
98 try:
98 try:
99 user = sa.query(User).filter(User.username == username).one()
99 user = sa.query(User).filter(User.username == username).one()
100 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
100 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
101 log.error(e)
101 log.error(e)
102 user = None
102 user = None
103 raise formencode.Invalid(self.message('invalid_password',
103 raise formencode.Invalid(self.message('invalid_password',
104 state=State_obj), value, state,
104 state=State_obj), value, state,
105 error_dict=self.e_dict)
105 error_dict=self.e_dict)
106 if user:
106 if user:
107 if user.active:
107 if user.active:
108 if user.username == username and user.password == crypted_passwd:
108 if user.username == username and user.password == crypted_passwd:
109 from pylons_app.lib.auth import AuthUser
109 from pylons_app.lib.auth import AuthUser
110 auth_user = AuthUser()
110 auth_user = AuthUser()
111 auth_user.username = username
111 auth_user.username = username
112 auth_user.is_authenticated = True
112 auth_user.is_authenticated = True
113 auth_user.is_admin = user.admin
113 auth_user.is_admin = user.admin
114 auth_user.user_id = user.user_id
114 auth_user.user_id = user.user_id
115 auth_user.name = user.name
115 auth_user.name = user.name
116 auth_user.lastname = user.lastname
116 auth_user.lastname = user.lastname
117 session['hg_app_user'] = auth_user
117 session['hg_app_user'] = auth_user
118 session.save()
118 session.save()
119 log.info('user %s is now authenticated', username)
119 log.info('user %s is now authenticated', username)
120
120
121 try:
121 try:
122 user.last_login = datetime.datetime.now()
122 user.last_login = datetime.datetime.now()
123 sa.add(user)
123 sa.add(user)
124 sa.commit()
124 sa.commit()
125 except (OperationalError) as e:
125 except (OperationalError) as e:
126 log.error(e)
126 log.error(e)
127 sa.rollback()
127 sa.rollback()
128
128
129 return value
129 return value
130 else:
130 else:
131 log.warning('user %s not authenticated', username)
131 log.warning('user %s not authenticated', username)
132 raise formencode.Invalid(self.message('invalid_password',
132 raise formencode.Invalid(self.message('invalid_password',
133 state=State_obj), value, state,
133 state=State_obj), value, state,
134 error_dict=self.e_dict)
134 error_dict=self.e_dict)
135 else:
135 else:
136 log.warning('user %s is disabled', username)
136 log.warning('user %s is disabled', username)
137 raise formencode.Invalid(self.message('disabled_account',
137 raise formencode.Invalid(self.message('disabled_account',
138 state=State_obj),
138 state=State_obj),
139 value, state,
139 value, state,
140 error_dict=self.e_dict_disable)
140 error_dict=self.e_dict_disable)
141
141
142 meta.Session.remove()
142 meta.Session.remove()
143
143
144
144
145 class ValidRepoUser(formencode.validators.FancyValidator):
145 class ValidRepoUser(formencode.validators.FancyValidator):
146
146
147 def to_python(self, value, state):
147 def to_python(self, value, state):
148 sa = meta.Session
148 sa = meta.Session
149 try:
149 try:
150 self.user_db = sa.query(User)\
150 self.user_db = sa.query(User)\
151 .filter(User.active == True)\
151 .filter(User.active == True)\
152 .filter(User.username == value).one()
152 .filter(User.username == value).one()
153 except Exception:
153 except Exception:
154 raise formencode.Invalid(_('This username is not valid'),
154 raise formencode.Invalid(_('This username is not valid'),
155 value, state)
155 value, state)
156 meta.Session.remove()
156 meta.Session.remove()
157 return self.user_db.user_id
157 return self.user_db.user_id
158
158
159 def ValidRepoName(edit, old_data):
159 def ValidRepoName(edit, old_data):
160 class _ValidRepoName(formencode.validators.FancyValidator):
160 class _ValidRepoName(formencode.validators.FancyValidator):
161
161
162 def to_python(self, value, state):
162 def to_python(self, value, state):
163 slug = h.repo_name_slug(value)
163 slug = h.repo_name_slug(value)
164 if slug in ['_admin']:
164 if slug in ['_admin']:
165 raise formencode.Invalid(_('This repository name is disallowed'),
165 raise formencode.Invalid(_('This repository name is disallowed'),
166 value, state)
166 value, state)
167
168 if old_data.get('repo_name') != value or not edit:
167 if old_data.get('repo_name') != value or not edit:
169 sa = meta.Session
168 sa = meta.Session
170 if sa.query(Repository).get(slug):
169 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
171 raise formencode.Invalid(_('This repository already exists') ,
170 raise formencode.Invalid(_('This repository already exists') ,
172 value, state)
171 value, state)
173 meta.Session.remove()
172 meta.Session.remove()
174 return slug
173 return slug
175
174
176
175
177 return _ValidRepoName
176 return _ValidRepoName
178
177
179 class ValidPerms(formencode.validators.FancyValidator):
178 class ValidPerms(formencode.validators.FancyValidator):
180 messages = {'perm_new_user_name':_('This username is not valid')}
179 messages = {'perm_new_user_name':_('This username is not valid')}
181
180
182 def to_python(self, value, state):
181 def to_python(self, value, state):
183 perms_update = []
182 perms_update = []
184 perms_new = []
183 perms_new = []
185 #build a list of permission to update and new permission to create
184 #build a list of permission to update and new permission to create
186 for k, v in value.items():
185 for k, v in value.items():
187 if k.startswith('perm_'):
186 if k.startswith('perm_'):
188 if k.startswith('perm_new_user'):
187 if k.startswith('perm_new_user'):
189 new_perm = value.get('perm_new_user', False)
188 new_perm = value.get('perm_new_user', False)
190 new_user = value.get('perm_new_user_name', False)
189 new_user = value.get('perm_new_user_name', False)
191 if new_user and new_perm:
190 if new_user and new_perm:
192 if (new_user, new_perm) not in perms_new:
191 if (new_user, new_perm) not in perms_new:
193 perms_new.append((new_user, new_perm))
192 perms_new.append((new_user, new_perm))
194 else:
193 else:
195 usr = k[5:]
194 usr = k[5:]
196 if usr == 'default':
195 if usr == 'default':
197 if value['private']:
196 if value['private']:
198 #set none for default when updating to private repo
197 #set none for default when updating to private repo
199 v = 'repository.none'
198 v = 'repository.none'
200 perms_update.append((usr, v))
199 perms_update.append((usr, v))
201 value['perms_updates'] = perms_update
200 value['perms_updates'] = perms_update
202 value['perms_new'] = perms_new
201 value['perms_new'] = perms_new
203 sa = meta.Session
202 sa = meta.Session
204 for k, v in perms_new:
203 for k, v in perms_new:
205 try:
204 try:
206 self.user_db = sa.query(User)\
205 self.user_db = sa.query(User)\
207 .filter(User.active == True)\
206 .filter(User.active == True)\
208 .filter(User.username == k).one()
207 .filter(User.username == k).one()
209 except Exception:
208 except Exception:
210 msg = self.message('perm_new_user_name',
209 msg = self.message('perm_new_user_name',
211 state=State_obj)
210 state=State_obj)
212 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
211 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
213 return value
212 return value
214
213
215 class ValidSettings(formencode.validators.FancyValidator):
214 class ValidSettings(formencode.validators.FancyValidator):
216
215
217 def to_python(self, value, state):
216 def to_python(self, value, state):
218 #settings form can't edit user
217 #settings form can't edit user
219 if value.has_key('user'):
218 if value.has_key('user'):
220 del['value']['user']
219 del['value']['user']
221
220
222 return value
221 return value
223 #===============================================================================
222 #===============================================================================
224 # FORMS
223 # FORMS
225 #===============================================================================
224 #===============================================================================
226 class LoginForm(formencode.Schema):
225 class LoginForm(formencode.Schema):
227 allow_extra_fields = True
226 allow_extra_fields = True
228 filter_extra_fields = True
227 filter_extra_fields = True
229 username = UnicodeString(
228 username = UnicodeString(
230 strip=True,
229 strip=True,
231 min=3,
230 min=3,
232 not_empty=True,
231 not_empty=True,
233 messages={
232 messages={
234 'empty':_('Please enter a login'),
233 'empty':_('Please enter a login'),
235 'tooShort':_('Enter a value %(min)i characters long or more')}
234 'tooShort':_('Enter a value %(min)i characters long or more')}
236 )
235 )
237
236
238 password = UnicodeString(
237 password = UnicodeString(
239 strip=True,
238 strip=True,
240 min=3,
239 min=3,
241 not_empty=True,
240 not_empty=True,
242 messages={
241 messages={
243 'empty':_('Please enter a password'),
242 'empty':_('Please enter a password'),
244 'tooShort':_('Enter a value %(min)i characters long or more')}
243 'tooShort':_('Enter a value %(min)i characters long or more')}
245 )
244 )
246
245
247
246
248 #chained validators have access to all data
247 #chained validators have access to all data
249 chained_validators = [ValidAuth]
248 chained_validators = [ValidAuth]
250
249
251 def UserForm(edit=False, old_data={}):
250 def UserForm(edit=False, old_data={}):
252 class _UserForm(formencode.Schema):
251 class _UserForm(formencode.Schema):
253 allow_extra_fields = True
252 allow_extra_fields = True
254 filter_extra_fields = True
253 filter_extra_fields = True
255 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
254 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
256 if edit:
255 if edit:
257 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
256 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
258 admin = StringBoolean(if_missing=False)
257 admin = StringBoolean(if_missing=False)
259 else:
258 else:
260 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
259 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
261 active = StringBoolean(if_missing=False)
260 active = StringBoolean(if_missing=False)
262 name = UnicodeString(strip=True, min=3, not_empty=True)
261 name = UnicodeString(strip=True, min=3, not_empty=True)
263 lastname = UnicodeString(strip=True, min=3, not_empty=True)
262 lastname = UnicodeString(strip=True, min=3, not_empty=True)
264 email = Email(not_empty=True)
263 email = Email(not_empty=True)
265
264
266 return _UserForm
265 return _UserForm
267
266
268 RegisterForm = UserForm
267 RegisterForm = UserForm
269
268
270
269
271 def RepoForm(edit=False, old_data={}):
270 def RepoForm(edit=False, old_data={}):
272 class _RepoForm(formencode.Schema):
271 class _RepoForm(formencode.Schema):
273 allow_extra_fields = True
272 allow_extra_fields = True
274 filter_extra_fields = False
273 filter_extra_fields = False
275 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
274 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
276 description = UnicodeString(strip=True, min=3, not_empty=True)
275 description = UnicodeString(strip=True, min=3, not_empty=True)
277 private = StringBoolean(if_missing=False)
276 private = StringBoolean(if_missing=False)
278
277
279 if edit:
278 if edit:
280 user = All(Int(not_empty=True), ValidRepoUser)
279 user = All(Int(not_empty=True), ValidRepoUser)
281
280
282 chained_validators = [ValidPerms]
281 chained_validators = [ValidPerms]
283 return _RepoForm
282 return _RepoForm
284
283
285 def RepoSettingsForm(edit=False, old_data={}):
284 def RepoSettingsForm(edit=False, old_data={}):
286 class _RepoForm(formencode.Schema):
285 class _RepoForm(formencode.Schema):
287 allow_extra_fields = True
286 allow_extra_fields = True
288 filter_extra_fields = False
287 filter_extra_fields = False
289 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
288 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
290 description = UnicodeString(strip=True, min=3, not_empty=True)
289 description = UnicodeString(strip=True, min=3, not_empty=True)
291 private = StringBoolean(if_missing=False)
290 private = StringBoolean(if_missing=False)
292
291
293 chained_validators = [ValidPerms, ValidSettings]
292 chained_validators = [ValidPerms, ValidSettings]
294 return _RepoForm
293 return _RepoForm
295
294
296
295
297 def ApplicationSettingsForm():
296 def ApplicationSettingsForm():
298 class _ApplicationSettingsForm(formencode.Schema):
297 class _ApplicationSettingsForm(formencode.Schema):
299 allow_extra_fields = True
298 allow_extra_fields = True
300 filter_extra_fields = False
299 filter_extra_fields = False
301 app_title = UnicodeString(strip=True, min=3, not_empty=True)
300 app_title = UnicodeString(strip=True, min=3, not_empty=True)
302 app_auth_realm = UnicodeString(strip=True, min=3, not_empty=True)
301 app_auth_realm = UnicodeString(strip=True, min=3, not_empty=True)
303
302
304 return _ApplicationSettingsForm
303 return _ApplicationSettingsForm
305
304
306
305
307
306
@@ -1,170 +1,178
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # model for handling repositories actions
3 # model for handling repositories actions
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
20 """
19 """
21 Created on Jun 5, 2010
20 Created on Jun 5, 2010
22 model for handling repositories actions
21 model for handling repositories actions
23 @author: marcink
22 @author: marcink
24 """
23 """
25 from pylons_app.model.meta import Session
24 from datetime import datetime
25 from pylons import app_globals as g
26 from pylons_app.lib.utils import check_repo
26 from pylons_app.model.db import Repository, Repo2Perm, User, Permission
27 from pylons_app.model.db import Repository, Repo2Perm, User, Permission
27 import shutil
28 from pylons_app.model.meta import Session
29 import logging
28 import os
30 import os
29 from datetime import datetime
31 import shutil
30 from pylons_app.lib.utils import check_repo
31 from pylons import app_globals as g
32 import traceback
32 import traceback
33 import logging
34 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
35
34
36 class RepoModel(object):
35 class RepoModel(object):
37
36
38 def __init__(self):
37 def __init__(self):
39 self.sa = Session()
38 self.sa = Session()
40
39
41 def get(self, id):
40 def get(self, id):
42 return self.sa.query(Repository).get(id)
41 return self.sa.query(Repository).filter(Repository.repo_name == id).scalar()
43
42
44 def get_users_js(self):
43 def get_users_js(self):
45
44
46 users = self.sa.query(User).filter(User.active == True).all()
45 users = self.sa.query(User).filter(User.active == True).all()
47 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
46 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
48 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
47 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
49 u.lastname, u.username)
48 u.lastname, u.username)
50 for u in users])
49 for u in users])
51 return users_array
50 return users_array
52
51
53
52
54 def update(self, repo_id, form_data):
53 def update(self, repo_name, form_data):
55 try:
54 try:
56 if repo_id != form_data['repo_name']:
57 #rename our data
58 self.__rename_repo(repo_id, form_data['repo_name'])
59 cur_repo = self.sa.query(Repository).get(repo_id)
60 for k, v in form_data.items():
61 if k == 'user':
62 cur_repo.user_id = v
63 else:
64 setattr(cur_repo, k, v)
65
55
66 #update permissions
56 #update permissions
67 for username, perm in form_data['perms_updates']:
57 for username, perm in form_data['perms_updates']:
68 r2p = self.sa.query(Repo2Perm)\
58 r2p = self.sa.query(Repo2Perm)\
69 .filter(Repo2Perm.user == self.sa.query(User)\
59 .filter(Repo2Perm.user == self.sa.query(User)\
70 .filter(User.username == username).one())\
60 .filter(User.username == username).one())\
71 .filter(Repo2Perm.repository == repo_id).one()
61 .filter(Repo2Perm.repository == self.get(repo_name))\
62 .one()
72
63
73 r2p.permission_id = self.sa.query(Permission).filter(
64 r2p.permission_id = self.sa.query(Permission).filter(
74 Permission.permission_name ==
65 Permission.permission_name ==
75 perm).one().permission_id
66 perm).one().permission_id
76 self.sa.add(r2p)
67 self.sa.add(r2p)
77
68
69 #set new permissions
78 for username, perm in form_data['perms_new']:
70 for username, perm in form_data['perms_new']:
79 r2p = Repo2Perm()
71 r2p = Repo2Perm()
80 r2p.repository = repo_id
72 r2p.repository = self.get(repo_name)
81 r2p.user = self.sa.query(User)\
73 r2p.user = self.sa.query(User)\
82 .filter(User.username == username).one()
74 .filter(User.username == username).one()
83
75
84 r2p.permission_id = self.sa.query(Permission).filter(
76 r2p.permission_id = self.sa.query(Permission).filter(
85 Permission.permission_name ==
77 Permission.permission_name == perm)\
86 perm).one().permission_id
78 .one().permission_id
87 self.sa.add(r2p)
79 self.sa.add(r2p)
88
80
81 #update current repo
82 cur_repo = self.get(repo_name)
83
84 for k, v in form_data.items():
85 if k == 'user':
86 cur_repo.user_id = v
87 else:
88 setattr(cur_repo, k, v)
89
89 self.sa.add(cur_repo)
90 self.sa.add(cur_repo)
91
92 if repo_name != form_data['repo_name']:
93 #rename our data
94 self.__rename_repo(repo_name, form_data['repo_name'])
95
90 self.sa.commit()
96 self.sa.commit()
91 except:
97 except:
92 log.error(traceback.format_exc())
98 log.error(traceback.format_exc())
93 self.sa.rollback()
99 self.sa.rollback()
94 raise
100 raise
95
101
96 def create(self, form_data, cur_user, just_db=False):
102 def create(self, form_data, cur_user, just_db=False):
97 try:
103 try:
98 repo_name = form_data['repo_name']
104 repo_name = form_data['repo_name']
99 new_repo = Repository()
105 new_repo = Repository()
100 for k, v in form_data.items():
106 for k, v in form_data.items():
101 setattr(new_repo, k, v)
107 setattr(new_repo, k, v)
102
108
103 new_repo.user_id = cur_user.user_id
109 new_repo.user_id = cur_user.user_id
104 self.sa.add(new_repo)
110 self.sa.add(new_repo)
105
111
106 #create default permission
112 #create default permission
107 repo2perm = Repo2Perm()
113 repo2perm = Repo2Perm()
108 default_perm = 'repository.none' if form_data['private'] \
114 default_perm = 'repository.none' if form_data['private'] \
109 else 'repository.read'
115 else 'repository.read'
110 repo2perm.permission_id = self.sa.query(Permission)\
116 repo2perm.permission_id = self.sa.query(Permission)\
111 .filter(Permission.permission_name == default_perm)\
117 .filter(Permission.permission_name == default_perm)\
112 .one().permission_id
118 .one().permission_id
113
119
114 repo2perm.repository = repo_name
120 repo2perm.repository_id = new_repo.repo_id
115 repo2perm.user_id = self.sa.query(User)\
121 repo2perm.user_id = self.sa.query(User)\
116 .filter(User.username == 'default').one().user_id
122 .filter(User.username == 'default').one().user_id
117
123
118 self.sa.add(repo2perm)
124 self.sa.add(repo2perm)
119 self.sa.commit()
125 self.sa.commit()
120 if not just_db:
126 if not just_db:
121 self.__create_repo(repo_name)
127 self.__create_repo(repo_name)
122 except:
128 except:
123 log.error(traceback.format_exc())
129 log.error(traceback.format_exc())
124 self.sa.rollback()
130 self.sa.rollback()
125 raise
131 raise
126
132
127 def delete(self, repo):
133 def delete(self, repo):
128 try:
134 try:
129 self.sa.delete(repo)
135 self.sa.delete(repo)
130 self.sa.commit()
136 self.sa.commit()
131 self.__delete_repo(repo.repo_name)
137 self.__delete_repo(repo.repo_name)
132 except:
138 except:
133 log.error(traceback.format_exc())
139 log.error(traceback.format_exc())
134 self.sa.rollback()
140 self.sa.rollback()
135 raise
141 raise
142
136 def delete_perm_user(self, form_data, repo_name):
143 def delete_perm_user(self, form_data, repo_name):
137 try:
144 try:
138 r2p = self.sa.query(Repo2Perm).filter(Repo2Perm.repository == repo_name)\
145 self.sa.query(Repo2Perm)\
146 .filter(Repo2Perm.repository == self.get(repo_name))\
139 .filter(Repo2Perm.user_id == form_data['user_id']).delete()
147 .filter(Repo2Perm.user_id == form_data['user_id']).delete()
140 self.sa.commit()
148 self.sa.commit()
141 except:
149 except:
142 log.error(traceback.format_exc())
150 log.error(traceback.format_exc())
143 self.sa.rollback()
151 self.sa.rollback()
144 raise
152 raise
145
153
146 def __create_repo(self, repo_name):
154 def __create_repo(self, repo_name):
147 repo_path = os.path.join(g.base_path, repo_name)
155 repo_path = os.path.join(g.base_path, repo_name)
148 if check_repo(repo_name, g.base_path):
156 if check_repo(repo_name, g.base_path):
149 log.info('creating repo %s in %s', repo_name, repo_path)
157 log.info('creating repo %s in %s', repo_name, repo_path)
150 from vcs.backends.hg import MercurialRepository
158 from vcs.backends.hg import MercurialRepository
151 MercurialRepository(repo_path, create=True)
159 MercurialRepository(repo_path, create=True)
152
160
153 def __rename_repo(self, old, new):
161 def __rename_repo(self, old, new):
154 log.info('renaming repo from %s to %s', old, new)
162 log.info('renaming repo from %s to %s', old, new)
155
163
156 old_path = os.path.join(g.base_path, old)
164 old_path = os.path.join(g.base_path, old)
157 new_path = os.path.join(g.base_path, new)
165 new_path = os.path.join(g.base_path, new)
158 if os.path.isdir(new_path):
166 if os.path.isdir(new_path):
159 raise Exception('Was trying to rename to already existing dir %s',
167 raise Exception('Was trying to rename to already existing dir %s',
160 new_path)
168 new_path)
161 shutil.move(old_path, new_path)
169 shutil.move(old_path, new_path)
162
170
163 def __delete_repo(self, name):
171 def __delete_repo(self, name):
164 rm_path = os.path.join(g.base_path, name)
172 rm_path = os.path.join(g.base_path, name)
165 log.info("Removing %s", rm_path)
173 log.info("Removing %s", rm_path)
166 #disable hg
174 #disable hg
167 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
175 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
168 #disable repo
176 #disable repo
169 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
177 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
170 % (datetime.today(), name)))
178 % (datetime.today(), name)))
General Comments 0
You need to be logged in to leave comments. Login now