##// END OF EJS Templates
fixed saving settings on repositories inside groups, also fixes #187...
marcink -
r1323:a7a772ea beta
parent child Browse files
Show More
@@ -1,415 +1,415 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Admin controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 from operator import itemgetter
30 30 from formencode import htmlfill
31 31
32 32 from paste.httpexceptions import HTTPInternalServerError
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode.lib import helpers as h
38 38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 39 HasPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 42 from rhodecode.lib.helpers import get_token
43 43 from rhodecode.model.db import User, Repository, UserFollowing, Group
44 44 from rhodecode.model.forms import RepoForm
45 45 from rhodecode.model.scm import ScmModel
46 46 from rhodecode.model.repo import RepoModel
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 class ReposController(BaseController):
52 52 """
53 53 REST Controller styled on the Atom Publishing Protocol"""
54 54 # To properly map this controller, ensure your config/routing.py
55 55 # file has a resource setup:
56 56 # map.resource('repo', 'repos')
57 57
58 58 @LoginRequired()
59 59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 60 def __before__(self):
61 61 c.admin_user = session.get('admin_user')
62 62 c.admin_username = session.get('admin_username')
63 63 super(ReposController, self).__before__()
64 64
65 65 def __load_defaults(self):
66 66 repo_model = RepoModel()
67 67
68 68 c.repo_groups = [('', '')]
69 69 parents_link = lambda k: h.literal('&raquo;'.join(
70 70 map(lambda k: k.group_name,
71 71 k.parents + [k])
72 72 )
73 73 )
74 74
75 75 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
76 76 x in self.sa.query(Group).all()])
77 77 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
78 78 c.users_array = repo_model.get_users_js()
79 79 c.users_groups_array = repo_model.get_users_groups_js()
80 80
81 81 def __load_data(self, repo_name=None):
82 82 """
83 83 Load defaults settings for edit, and update
84 84
85 85 :param repo_name:
86 86 """
87 87 self.__load_defaults()
88 88
89 89 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
90 90
91 91 repo_model = RepoModel()
92 92 c.repo_info = repo_model.get_by_repo_name(repo_name)
93 93
94 94 if c.repo_info is None:
95 95 h.flash(_('%s repository is not mapped to db perhaps'
96 96 ' it was created or renamed from the filesystem'
97 97 ' please run the application again'
98 98 ' in order to rescan repositories') % repo_name,
99 99 category='error')
100 100
101 101 return redirect(url('repos'))
102 102
103 103 c.default_user_id = User.by_username('default').user_id
104 104 c.in_public_journal = self.sa.query(UserFollowing)\
105 105 .filter(UserFollowing.user_id == c.default_user_id)\
106 106 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
107 107
108 108 if c.repo_info.stats:
109 109 last_rev = c.repo_info.stats.stat_on_revision
110 110 else:
111 111 last_rev = 0
112 112 c.stats_revision = last_rev
113 113
114 114 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
115 115
116 116 if last_rev == 0 or c.repo_last_rev == 0:
117 117 c.stats_percentage = 0
118 118 else:
119 119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
120 120 c.repo_last_rev) * 100)
121 121
122 122 defaults = c.repo_info.get_dict()
123 123 group, repo_name = c.repo_info.groups_and_repo
124 124 defaults['repo_name'] = repo_name
125 125 defaults['repo_group'] = getattr(group[-1] if group else None,
126 126 'group_id', None)
127 127
128 128 #fill owner
129 129 if c.repo_info.user:
130 130 defaults.update({'user': c.repo_info.user.username})
131 131 else:
132 132 replacement_user = self.sa.query(User)\
133 133 .filter(User.admin == True).first().username
134 134 defaults.update({'user': replacement_user})
135 135
136 136 #fill repository users
137 137 for p in c.repo_info.repo_to_perm:
138 138 defaults.update({'u_perm_%s' % p.user.username:
139 139 p.permission.permission_name})
140 140
141 141 #fill repository groups
142 142 for p in c.repo_info.users_group_to_perm:
143 143 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
144 144 p.permission.permission_name})
145 145
146 146 return defaults
147 147
148 148 @HasPermissionAllDecorator('hg.admin')
149 149 def index(self, format='html'):
150 150 """GET /repos: All items in the collection"""
151 151 # url('repos')
152 152 cached_repo_list = ScmModel().get_repos()
153 153 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
154 154 return render('admin/repos/repos.html')
155 155
156 156 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
157 157 def create(self):
158 158 """
159 159 POST /repos: Create a new item"""
160 160 # url('repos')
161 161 repo_model = RepoModel()
162 162 self.__load_defaults()
163 163 form_result = {}
164 164 try:
165 165 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
166 166 .to_python(dict(request.POST))
167 167 repo_model.create(form_result, self.rhodecode_user)
168 168 if form_result['clone_uri']:
169 169 h.flash(_('created repository %s from %s') \
170 170 % (form_result['repo_name'], form_result['clone_uri']),
171 171 category='success')
172 172 else:
173 173 h.flash(_('created repository %s') % form_result['repo_name'],
174 174 category='success')
175 175
176 176 if request.POST.get('user_created'):
177 177 action_logger(self.rhodecode_user, 'user_created_repo',
178 178 form_result['repo_name'], '', self.sa)
179 179 else:
180 180 action_logger(self.rhodecode_user, 'admin_created_repo',
181 181 form_result['repo_name'], '', self.sa)
182 182
183 183 except formencode.Invalid, errors:
184 184
185 185 c.new_repo = errors.value['repo_name']
186 186
187 187 if request.POST.get('user_created'):
188 188 r = render('admin/repos/repo_add_create_repository.html')
189 189 else:
190 190 r = render('admin/repos/repo_add.html')
191 191
192 192 return htmlfill.render(
193 193 r,
194 194 defaults=errors.value,
195 195 errors=errors.error_dict or {},
196 196 prefix_error=False,
197 197 encoding="UTF-8")
198 198
199 199 except Exception:
200 200 log.error(traceback.format_exc())
201 201 msg = _('error occurred during creation of repository %s') \
202 202 % form_result.get('repo_name')
203 203 h.flash(msg, category='error')
204 204 if request.POST.get('user_created'):
205 205 return redirect(url('home'))
206 206 return redirect(url('repos'))
207 207
208 208 @HasPermissionAllDecorator('hg.admin')
209 209 def new(self, format='html'):
210 210 """GET /repos/new: Form to create a new item"""
211 211 new_repo = request.GET.get('repo', '')
212 212 c.new_repo = repo_name_slug(new_repo)
213 213 self.__load_defaults()
214 214 return render('admin/repos/repo_add.html')
215 215
216 216 @HasPermissionAllDecorator('hg.admin')
217 217 def update(self, repo_name):
218 218 """
219 219 PUT /repos/repo_name: Update an existing item"""
220 220 # Forms posted to this method should contain a hidden field:
221 221 # <input type="hidden" name="_method" value="PUT" />
222 222 # Or using helpers:
223 223 # h.form(url('repo', repo_name=ID),
224 224 # method='put')
225 225 # url('repo', repo_name=ID)
226 226 self.__load_defaults()
227 227 repo_model = RepoModel()
228 228 changed_name = repo_name
229 229 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
230 230 repo_groups=c.repo_groups_choices)()
231 231 try:
232 232 form_result = _form.to_python(dict(request.POST))
233 233 repo_model.update(repo_name, form_result)
234 234 invalidate_cache('get_repo_cached_%s' % repo_name)
235 235 h.flash(_('Repository %s updated successfully' % repo_name),
236 236 category='success')
237 changed_name = form_result['repo_name']
237 changed_name = form_result['repo_name_full']
238 238 action_logger(self.rhodecode_user, 'admin_updated_repo',
239 239 changed_name, '', self.sa)
240 240
241 241 except formencode.Invalid, errors:
242 242 defaults = self.__load_data(repo_name)
243 243 defaults.update(errors.value)
244 244 return htmlfill.render(
245 245 render('admin/repos/repo_edit.html'),
246 246 defaults=defaults,
247 247 errors=errors.error_dict or {},
248 248 prefix_error=False,
249 249 encoding="UTF-8")
250 250
251 251 except Exception:
252 252 log.error(traceback.format_exc())
253 253 h.flash(_('error occurred during update of repository %s') \
254 254 % repo_name, category='error')
255 255 return redirect(url('edit_repo', repo_name=changed_name))
256 256
257 257 @HasPermissionAllDecorator('hg.admin')
258 258 def delete(self, repo_name):
259 259 """
260 260 DELETE /repos/repo_name: Delete an existing item"""
261 261 # Forms posted to this method should contain a hidden field:
262 262 # <input type="hidden" name="_method" value="DELETE" />
263 263 # Or using helpers:
264 264 # h.form(url('repo', repo_name=ID),
265 265 # method='delete')
266 266 # url('repo', repo_name=ID)
267 267
268 268 repo_model = RepoModel()
269 269 repo = repo_model.get_by_repo_name(repo_name)
270 270 if not repo:
271 271 h.flash(_('%s repository is not mapped to db perhaps'
272 272 ' it was moved or renamed from the filesystem'
273 273 ' please run the application again'
274 274 ' in order to rescan repositories') % repo_name,
275 275 category='error')
276 276
277 277 return redirect(url('repos'))
278 278 try:
279 279 action_logger(self.rhodecode_user, 'admin_deleted_repo',
280 280 repo_name, '', self.sa)
281 281 repo_model.delete(repo)
282 282 invalidate_cache('get_repo_cached_%s' % repo_name)
283 283 h.flash(_('deleted repository %s') % repo_name, category='success')
284 284
285 285 except Exception, e:
286 286 log.error(traceback.format_exc())
287 287 h.flash(_('An error occurred during deletion of %s') % repo_name,
288 288 category='error')
289 289
290 290 return redirect(url('repos'))
291 291
292 292 @HasPermissionAllDecorator('hg.admin')
293 293 def delete_perm_user(self, repo_name):
294 294 """
295 295 DELETE an existing repository permission user
296 296
297 297 :param repo_name:
298 298 """
299 299
300 300 try:
301 301 repo_model = RepoModel()
302 302 repo_model.delete_perm_user(request.POST, repo_name)
303 303 except Exception, e:
304 304 h.flash(_('An error occurred during deletion of repository user'),
305 305 category='error')
306 306 raise HTTPInternalServerError()
307 307
308 308 @HasPermissionAllDecorator('hg.admin')
309 309 def delete_perm_users_group(self, repo_name):
310 310 """
311 311 DELETE an existing repository permission users group
312 312
313 313 :param repo_name:
314 314 """
315 315 try:
316 316 repo_model = RepoModel()
317 317 repo_model.delete_perm_users_group(request.POST, repo_name)
318 318 except Exception, e:
319 319 h.flash(_('An error occurred during deletion of repository'
320 320 ' users groups'),
321 321 category='error')
322 322 raise HTTPInternalServerError()
323 323
324 324 @HasPermissionAllDecorator('hg.admin')
325 325 def repo_stats(self, repo_name):
326 326 """
327 327 DELETE an existing repository statistics
328 328
329 329 :param repo_name:
330 330 """
331 331
332 332 try:
333 333 repo_model = RepoModel()
334 334 repo_model.delete_stats(repo_name)
335 335 except Exception, e:
336 336 h.flash(_('An error occurred during deletion of repository stats'),
337 337 category='error')
338 338 return redirect(url('edit_repo', repo_name=repo_name))
339 339
340 340 @HasPermissionAllDecorator('hg.admin')
341 341 def repo_cache(self, repo_name):
342 342 """
343 343 INVALIDATE existing repository cache
344 344
345 345 :param repo_name:
346 346 """
347 347
348 348 try:
349 349 ScmModel().mark_for_invalidation(repo_name)
350 350 except Exception, e:
351 351 h.flash(_('An error occurred during cache invalidation'),
352 352 category='error')
353 353 return redirect(url('edit_repo', repo_name=repo_name))
354 354
355 355 @HasPermissionAllDecorator('hg.admin')
356 356 def repo_public_journal(self, repo_name):
357 357 """
358 358 Set's this repository to be visible in public journal,
359 359 in other words assing default user to follow this repo
360 360
361 361 :param repo_name:
362 362 """
363 363
364 364 cur_token = request.POST.get('auth_token')
365 365 token = get_token()
366 366 if cur_token == token:
367 367 try:
368 368 repo_id = Repository.by_repo_name(repo_name).repo_id
369 369 user_id = User.by_username('default').user_id
370 370 self.scm_model.toggle_following_repo(repo_id, user_id)
371 371 h.flash(_('Updated repository visibility in public journal'),
372 372 category='success')
373 373 except:
374 374 h.flash(_('An error occurred during setting this'
375 375 ' repository in public journal'),
376 376 category='error')
377 377
378 378 else:
379 379 h.flash(_('Token mismatch'), category='error')
380 380 return redirect(url('edit_repo', repo_name=repo_name))
381 381
382 382 @HasPermissionAllDecorator('hg.admin')
383 383 def repo_pull(self, repo_name):
384 384 """
385 385 Runs task to update given repository with remote changes,
386 386 ie. make pull on remote location
387 387
388 388 :param repo_name:
389 389 """
390 390 try:
391 391 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
392 392 h.flash(_('Pulled from remote location'), category='success')
393 393 except Exception, e:
394 394 h.flash(_('An error occurred during pull from remote location'),
395 395 category='error')
396 396
397 397 return redirect(url('edit_repo', repo_name=repo_name))
398 398
399 399 @HasPermissionAllDecorator('hg.admin')
400 400 def show(self, repo_name, format='html'):
401 401 """GET /repos/repo_name: Show a specific item"""
402 402 # url('repo', repo_name=ID)
403 403
404 404 @HasPermissionAllDecorator('hg.admin')
405 405 def edit(self, repo_name, format='html'):
406 406 """GET /repos/repo_name/edit: Form to edit an existing item"""
407 407 # url('edit_repo', repo_name=ID)
408 408 defaults = self.__load_data(repo_name)
409 409
410 410 return htmlfill.render(
411 411 render('admin/repos/repo_edit.html'),
412 412 defaults=defaults,
413 413 encoding="UTF-8",
414 414 force_defaults=False
415 415 )
@@ -1,538 +1,544 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 from datetime import date
30 30
31 31 from sqlalchemy import *
32 32 from sqlalchemy.exc import DatabaseError
33 33 from sqlalchemy.orm import relationship, backref
34 34 from sqlalchemy.orm.interfaces import MapperExtension
35 35
36 36 from rhodecode.lib import str2bool
37 37 from rhodecode.model.meta import Base, Session
38 38 from rhodecode.model.caching_query import FromCache
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42 #==============================================================================
43 43 # MAPPER EXTENSIONS
44 44 #==============================================================================
45 45
46 46 class RepositoryMapper(MapperExtension):
47 47 def after_update(self, mapper, connection, instance):
48 48 pass
49 49
50 50
51 51 class RhodeCodeSettings(Base):
52 52 __tablename__ = 'rhodecode_settings'
53 53 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
54 54 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
55 55 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
56 56 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
57 57
58 58 def __init__(self, k='', v=''):
59 59 self.app_settings_name = k
60 60 self.app_settings_value = v
61 61
62 62 def __repr__(self):
63 63 return "<%s('%s:%s')>" % (self.__class__.__name__,
64 64 self.app_settings_name, self.app_settings_value)
65 65
66 66
67 67 @classmethod
68 68 def get_by_name(cls, ldap_key):
69 69 return Session.query(cls)\
70 70 .filter(cls.app_settings_name == ldap_key).scalar()
71 71
72 72 @classmethod
73 73 def get_app_settings(cls, cache=False):
74 74
75 75 ret = Session.query(cls)
76 76
77 77 if cache:
78 78 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
79 79
80 80 if not ret:
81 81 raise Exception('Could not get application settings !')
82 82 settings = {}
83 83 for each in ret:
84 84 settings['rhodecode_' + each.app_settings_name] = \
85 85 each.app_settings_value
86 86
87 87 return settings
88 88
89 89 @classmethod
90 90 def get_ldap_settings(cls, cache=False):
91 91 ret = Session.query(cls)\
92 92 .filter(cls.app_settings_name.startswith('ldap_'))\
93 93 .all()
94 94 fd = {}
95 95 for row in ret:
96 96 fd.update({row.app_settings_name:row.app_settings_value})
97 97 return fd
98 98
99 99
100 100 class RhodeCodeUi(Base):
101 101 __tablename__ = 'rhodecode_ui'
102 102 __table_args__ = {'useexisting':True}
103 103 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
104 104 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 105 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 106 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 107 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
108 108
109 109
110 110 class User(Base):
111 111 __tablename__ = 'users'
112 112 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
113 113 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
114 114 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
115 115 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
116 116 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
117 117 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
118 118 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
119 119 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
120 120 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
121 121 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
122 122 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
123 123 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
124 124
125 125 user_log = relationship('UserLog', cascade='all')
126 126 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
127 127
128 128 repositories = relationship('Repository')
129 129 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
130 130
131 131 group_member = relationship('UsersGroupMember', cascade='all')
132 132
133 133 @property
134 134 def full_contact(self):
135 135 return '%s %s <%s>' % (self.name, self.lastname, self.email)
136 136
137 137 @property
138 138 def short_contact(self):
139 139 return '%s %s' % (self.name, self.lastname)
140 140
141 141
142 142 @property
143 143 def is_admin(self):
144 144 return self.admin
145 145
146 146 def __repr__(self):
147 147 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
148 148 self.user_id, self.username)
149 149
150 150 @classmethod
151 151 def by_username(cls, username):
152 152 return Session.query(cls).filter(cls.username == username).one()
153 153
154 154
155 155 def update_lastlogin(self):
156 156 """Update user lastlogin"""
157 157
158 158 try:
159 159 session = Session.object_session(self)
160 160 self.last_login = datetime.datetime.now()
161 161 session.add(self)
162 162 session.commit()
163 163 log.debug('updated user %s lastlogin', self.username)
164 164 except (DatabaseError,):
165 165 session.rollback()
166 166
167 167
168 168 class UserLog(Base):
169 169 __tablename__ = 'user_logs'
170 170 __table_args__ = {'useexisting':True}
171 171 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
172 172 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
173 173 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
174 174 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
175 175 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
176 176 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
177 177 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
178 178
179 179 @property
180 180 def action_as_day(self):
181 181 return date(*self.action_date.timetuple()[:3])
182 182
183 183 user = relationship('User')
184 184 repository = relationship('Repository')
185 185
186 186
187 187 class UsersGroup(Base):
188 188 __tablename__ = 'users_groups'
189 189 __table_args__ = {'useexisting':True}
190 190
191 191 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
192 192 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
193 193 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
194 194
195 195 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
196 196
197 197
198 198 @classmethod
199 199 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
200 200 if case_insensitive:
201 201 gr = Session.query(cls)\
202 202 .filter(cls.users_group_name.ilike(group_name))
203 203 else:
204 204 gr = Session.query(UsersGroup)\
205 205 .filter(UsersGroup.users_group_name == group_name)
206 206 if cache:
207 207 gr = gr.options(FromCache("sql_cache_short",
208 208 "get_user_%s" % group_name))
209 209 return gr.scalar()
210 210
211 211 class UsersGroupMember(Base):
212 212 __tablename__ = 'users_groups_members'
213 213 __table_args__ = {'useexisting':True}
214 214
215 215 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
216 216 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
217 217 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
218 218
219 219 user = relationship('User', lazy='joined')
220 220 users_group = relationship('UsersGroup')
221 221
222 222 def __init__(self, gr_id='', u_id=''):
223 223 self.users_group_id = gr_id
224 224 self.user_id = u_id
225 225
226 226 class Repository(Base):
227 227 __tablename__ = 'repositories'
228 228 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
229 229 __mapper_args__ = {'extension':RepositoryMapper()}
230 230
231 231 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 232 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
233 233 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
234 234 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
235 235 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
236 236 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
237 237 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
238 238 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
239 239 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 240 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
241 241
242 242 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
243 243 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
244 244
245 245
246 246 user = relationship('User')
247 247 fork = relationship('Repository', remote_side=repo_id)
248 248 group = relationship('Group')
249 249 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
250 250 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
251 251 stats = relationship('Statistics', cascade='all', uselist=False)
252 252
253 253 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
254 254
255 255 logs = relationship('UserLog', cascade='all')
256 256
257 257 def __repr__(self):
258 258 return "<%s('%s:%s')>" % (self.__class__.__name__,
259 259 self.repo_id, self.repo_name)
260 260
261 261 @classmethod
262 262 def by_repo_name(cls, repo_name):
263 263 return Session.query(cls).filter(cls.repo_name == repo_name).one()
264 264
265 265
266 266 @classmethod
267 267 def get_repo_forks(cls, repo_id):
268 268 return Session.query(cls).filter(Repository.fork_id == repo_id)
269 269
270 270 @property
271 271 def just_name(self):
272 272 return self.repo_name.split(os.sep)[-1]
273 273
274 274 @property
275 275 def groups_with_parents(self):
276 276 groups = []
277 277 if self.group is None:
278 278 return groups
279 279
280 280 cur_gr = self.group
281 281 groups.insert(0, cur_gr)
282 282 while 1:
283 283 gr = getattr(cur_gr, 'parent_group', None)
284 284 cur_gr = cur_gr.parent_group
285 285 if gr is None:
286 286 break
287 287 groups.insert(0, gr)
288 288
289 289 return groups
290 290
291 291 @property
292 292 def groups_and_repo(self):
293 293 return self.groups_with_parents, self.just_name
294 294
295 295
296 296 class Group(Base):
297 297 __tablename__ = 'groups'
298 298 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
299 299
300 300 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
301 301 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
302 302 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
303 303
304 304 parent_group = relationship('Group', remote_side=group_id)
305 305
306 306
307 307 def __init__(self, group_name='', parent_group=None):
308 308 self.group_name = group_name
309 309 self.parent_group = parent_group
310 310
311 311 def __repr__(self):
312 312 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
313 313 self.group_name)
314 314
315 315 @property
316 316 def parents(self):
317 317 groups = []
318 318 if self.parent_group is None:
319 319 return groups
320 320 cur_gr = self.parent_group
321 321 groups.insert(0, cur_gr)
322 322 while 1:
323 323 gr = getattr(cur_gr, 'parent_group', None)
324 324 cur_gr = cur_gr.parent_group
325 325 if gr is None:
326 326 break
327 327 groups.insert(0, gr)
328 328 return groups
329 329
330
331 @property
332 def full_path(self):
333 return '/'.join([g.group_name for g in self.parents] +
334 [self.group_name])
335
330 336 @property
331 337 def repositories(self):
332 338 return Session.query(Repository).filter(Repository.group == self).all()
333 339
334 340 class Permission(Base):
335 341 __tablename__ = 'permissions'
336 342 __table_args__ = {'useexisting':True}
337 343 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
338 344 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
339 345 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
340 346
341 347 def __repr__(self):
342 348 return "<%s('%s:%s')>" % (self.__class__.__name__,
343 349 self.permission_id, self.permission_name)
344 350
345 351 @classmethod
346 352 def get_by_key(cls, key):
347 353 return Session.query(cls).filter(cls.permission_name == key).scalar()
348 354
349 355 class RepoToPerm(Base):
350 356 __tablename__ = 'repo_to_perm'
351 357 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
352 358 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
353 359 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
354 360 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
355 361 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
356 362
357 363 user = relationship('User')
358 364 permission = relationship('Permission')
359 365 repository = relationship('Repository')
360 366
361 367 class UserToPerm(Base):
362 368 __tablename__ = 'user_to_perm'
363 369 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
364 370 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
365 371 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
366 372 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
367 373
368 374 user = relationship('User')
369 375 permission = relationship('Permission')
370 376
371 377 @classmethod
372 378 def has_perm(cls, user_id, perm):
373 379 if not isinstance(perm, Permission):
374 380 raise Exception('perm needs to be an instance of Permission class')
375 381
376 382 return Session.query(cls).filter(cls.user_id == user_id)\
377 383 .filter(cls.permission == perm).scalar() is not None
378 384
379 385 @classmethod
380 386 def grant_perm(cls, user_id, perm):
381 387 if not isinstance(perm, Permission):
382 388 raise Exception('perm needs to be an instance of Permission class')
383 389
384 390 new = cls()
385 391 new.user_id = user_id
386 392 new.permission = perm
387 393 try:
388 394 Session.add(new)
389 395 Session.commit()
390 396 except:
391 397 Session.rollback()
392 398
393 399
394 400 @classmethod
395 401 def revoke_perm(cls, user_id, perm):
396 402 if not isinstance(perm, Permission):
397 403 raise Exception('perm needs to be an instance of Permission class')
398 404
399 405 try:
400 406 Session.query(cls).filter(cls.user_id == user_id)\
401 407 .filter(cls.permission == perm).delete()
402 408 Session.commit()
403 409 except:
404 410 Session.rollback()
405 411
406 412 class UsersGroupRepoToPerm(Base):
407 413 __tablename__ = 'users_group_repo_to_perm'
408 414 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
409 415 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
410 416 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
411 417 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
412 418 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
413 419
414 420 users_group = relationship('UsersGroup')
415 421 permission = relationship('Permission')
416 422 repository = relationship('Repository')
417 423
418 424
419 425 class UsersGroupToPerm(Base):
420 426 __tablename__ = 'users_group_to_perm'
421 427 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
422 428 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
423 429 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
424 430
425 431 users_group = relationship('UsersGroup')
426 432 permission = relationship('Permission')
427 433
428 434
429 435 @classmethod
430 436 def has_perm(cls, users_group_id, perm):
431 437 if not isinstance(perm, Permission):
432 438 raise Exception('perm needs to be an instance of Permission class')
433 439
434 440 return Session.query(cls).filter(cls.users_group_id ==
435 441 users_group_id)\
436 442 .filter(cls.permission == perm)\
437 443 .scalar() is not None
438 444
439 445 @classmethod
440 446 def grant_perm(cls, users_group_id, perm):
441 447 if not isinstance(perm, Permission):
442 448 raise Exception('perm needs to be an instance of Permission class')
443 449
444 450 new = cls()
445 451 new.users_group_id = users_group_id
446 452 new.permission = perm
447 453 try:
448 454 Session.add(new)
449 455 Session.commit()
450 456 except:
451 457 Session.rollback()
452 458
453 459
454 460 @classmethod
455 461 def revoke_perm(cls, users_group_id, perm):
456 462 if not isinstance(perm, Permission):
457 463 raise Exception('perm needs to be an instance of Permission class')
458 464
459 465 try:
460 466 Session.query(cls).filter(cls.users_group_id == users_group_id)\
461 467 .filter(cls.permission == perm).delete()
462 468 Session.commit()
463 469 except:
464 470 Session.rollback()
465 471
466 472
467 473 class GroupToPerm(Base):
468 474 __tablename__ = 'group_to_perm'
469 475 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
470 476
471 477 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
472 478 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
473 479 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
474 480 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
475 481
476 482 user = relationship('User')
477 483 permission = relationship('Permission')
478 484 group = relationship('Group')
479 485
480 486 class Statistics(Base):
481 487 __tablename__ = 'statistics'
482 488 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
483 489 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
484 490 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
485 491 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
486 492 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
487 493 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
488 494 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
489 495
490 496 repository = relationship('Repository', single_parent=True)
491 497
492 498 class UserFollowing(Base):
493 499 __tablename__ = 'user_followings'
494 500 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
495 501 UniqueConstraint('user_id', 'follows_user_id')
496 502 , {'useexisting':True})
497 503
498 504 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
499 505 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
500 506 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
501 507 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
502 508 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
503 509
504 510 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
505 511
506 512 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
507 513 follows_repository = relationship('Repository', order_by='Repository.repo_name')
508 514
509 515
510 516
511 517 @classmethod
512 518 def get_repo_followers(cls, repo_id):
513 519 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
514 520
515 521 class CacheInvalidation(Base):
516 522 __tablename__ = 'cache_invalidation'
517 523 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
518 524 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
519 525 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
520 526 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
521 527 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
522 528
523 529
524 530 def __init__(self, cache_key, cache_args=''):
525 531 self.cache_key = cache_key
526 532 self.cache_args = cache_args
527 533 self.cache_active = False
528 534
529 535 def __repr__(self):
530 536 return "<%s('%s:%s')>" % (self.__class__.__name__,
531 537 self.cache_id, self.cache_key)
532 538
533 539 class DbMigrateVersion(Base):
534 540 __tablename__ = 'db_migrate_version'
535 541 __table_args__ = {'useexisting':True}
536 542 repository_id = Column('repository_id', String(250), primary_key=True)
537 543 repository_path = Column('repository_path', Text)
538 544 version = Column('version', Integer)
@@ -1,584 +1,611 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 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 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 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 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 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25 import traceback
26 26
27 27 import formencode
28 28 from formencode import All
29 29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 30 Email, Bool, StringBoolean, Set
31 31
32 32 from pylons.i18n.translation import _
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34
35 35 from rhodecode.lib.utils import repo_name_slug
36 36 from rhodecode.lib.auth import authenticate, get_crypt_password
37 37 from rhodecode.lib.exceptions import LdapImportError
38 38 from rhodecode.model import meta
39 39 from rhodecode.model.user import UserModel
40 40 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.db import User, UsersGroup
41 from rhodecode.model.db import User, UsersGroup, Group
42 42 from rhodecode import BACKENDS
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 #this is needed to translate the messages using _() in validators
47 47 class State_obj(object):
48 48 _ = staticmethod(_)
49 49
50 50 #==============================================================================
51 51 # VALIDATORS
52 52 #==============================================================================
53 53 class ValidAuthToken(formencode.validators.FancyValidator):
54 54 messages = {'invalid_token':_('Token mismatch')}
55 55
56 56 def validate_python(self, value, state):
57 57
58 58 if value != authentication_token():
59 59 raise formencode.Invalid(self.message('invalid_token', state,
60 60 search_number=value), value, state)
61 61
62 62 def ValidUsername(edit, old_data):
63 63 class _ValidUsername(formencode.validators.FancyValidator):
64 64
65 65 def validate_python(self, value, state):
66 66 if value in ['default', 'new_user']:
67 67 raise formencode.Invalid(_('Invalid username'), value, state)
68 68 #check if user is unique
69 69 old_un = None
70 70 if edit:
71 71 old_un = UserModel().get(old_data.get('user_id')).username
72 72
73 73 if old_un != value or not edit:
74 74 if UserModel().get_by_username(value, cache=False,
75 75 case_insensitive=True):
76 76 raise formencode.Invalid(_('This username already '
77 77 'exists') , value, state)
78 78
79 79 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
80 80 raise formencode.Invalid(_('Username may only contain '
81 81 'alphanumeric characters '
82 82 'underscores, periods or dashes '
83 83 'and must begin with alphanumeric '
84 84 'character'), value, state)
85 85
86 86 return _ValidUsername
87 87
88 88
89 89 def ValidUsersGroup(edit, old_data):
90 90
91 91 class _ValidUsersGroup(formencode.validators.FancyValidator):
92 92
93 93 def validate_python(self, value, state):
94 94 if value in ['default']:
95 95 raise formencode.Invalid(_('Invalid group name'), value, state)
96 96 #check if group is unique
97 97 old_ugname = None
98 98 if edit:
99 99 old_ugname = UsersGroup.get(
100 100 old_data.get('users_group_id')).users_group_name
101 101
102 102 if old_ugname != value or not edit:
103 103 if UsersGroup.get_by_group_name(value, cache=False,
104 104 case_insensitive=True):
105 105 raise formencode.Invalid(_('This users group '
106 106 'already exists') , value,
107 107 state)
108 108
109 109
110 110 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
111 111 raise formencode.Invalid(_('Group name may only contain '
112 112 'alphanumeric characters '
113 113 'underscores, periods or dashes '
114 114 'and must begin with alphanumeric '
115 115 'character'), value, state)
116 116
117 117 return _ValidUsersGroup
118 118
119 119
120 120
121 121 class ValidPassword(formencode.validators.FancyValidator):
122 122
123 123 def to_python(self, value, state):
124 124
125 125 if value:
126 126
127 127 if value.get('password'):
128 128 try:
129 129 value['password'] = get_crypt_password(value['password'])
130 130 except UnicodeEncodeError:
131 131 e_dict = {'password':_('Invalid characters in password')}
132 132 raise formencode.Invalid('', value, state, error_dict=e_dict)
133 133
134 134 if value.get('password_confirmation'):
135 135 try:
136 136 value['password_confirmation'] = \
137 137 get_crypt_password(value['password_confirmation'])
138 138 except UnicodeEncodeError:
139 139 e_dict = {'password_confirmation':_('Invalid characters in password')}
140 140 raise formencode.Invalid('', value, state, error_dict=e_dict)
141 141
142 142 if value.get('new_password'):
143 143 try:
144 144 value['new_password'] = \
145 145 get_crypt_password(value['new_password'])
146 146 except UnicodeEncodeError:
147 147 e_dict = {'new_password':_('Invalid characters in password')}
148 148 raise formencode.Invalid('', value, state, error_dict=e_dict)
149 149
150 150 return value
151 151
152 152 class ValidPasswordsMatch(formencode.validators.FancyValidator):
153 153
154 154 def validate_python(self, value, state):
155 155
156 156 if value['password'] != value['password_confirmation']:
157 157 e_dict = {'password_confirmation':
158 158 _('Password do not match')}
159 159 raise formencode.Invalid('', value, state, error_dict=e_dict)
160 160
161 161 class ValidAuth(formencode.validators.FancyValidator):
162 162 messages = {
163 163 'invalid_password':_('invalid password'),
164 164 'invalid_login':_('invalid user name'),
165 165 'disabled_account':_('Your account is disabled')
166 166
167 167 }
168 168 #error mapping
169 169 e_dict = {'username':messages['invalid_login'],
170 170 'password':messages['invalid_password']}
171 171 e_dict_disable = {'username':messages['disabled_account']}
172 172
173 173 def validate_python(self, value, state):
174 174 password = value['password']
175 175 username = value['username']
176 176 user = UserModel().get_by_username(username)
177 177
178 178 if authenticate(username, password):
179 179 return value
180 180 else:
181 181 if user and user.active is False:
182 182 log.warning('user %s is disabled', username)
183 183 raise formencode.Invalid(self.message('disabled_account',
184 184 state=State_obj),
185 185 value, state,
186 186 error_dict=self.e_dict_disable)
187 187 else:
188 188 log.warning('user %s not authenticated', username)
189 189 raise formencode.Invalid(self.message('invalid_password',
190 190 state=State_obj), value, state,
191 191 error_dict=self.e_dict)
192 192
193 193 class ValidRepoUser(formencode.validators.FancyValidator):
194 194
195 195 def to_python(self, value, state):
196 196 sa = meta.Session()
197 197 try:
198 198 self.user_db = sa.query(User)\
199 199 .filter(User.active == True)\
200 200 .filter(User.username == value).one()
201 201 except Exception:
202 202 raise formencode.Invalid(_('This username is not valid'),
203 203 value, state)
204 204 finally:
205 205 meta.Session.remove()
206 206
207 return self.user_db.user_id
207 return value
208 208
209 209 def ValidRepoName(edit, old_data):
210 210 class _ValidRepoName(formencode.validators.FancyValidator):
211 def to_python(self, value, state):
211 212
212 def to_python(self, value, state):
213 slug = repo_name_slug(value)
214 if slug in ['_admin']:
215 raise formencode.Invalid(_('This repository name is disallowed'),
216 value, state)
217 if old_data.get('repo_name') != value or not edit:
218 if RepoModel().get_by_repo_name(slug, cache=False):
219 raise formencode.Invalid(_('This repository already exists') ,
220 value, state)
221 return slug
213 repo_name = value.get('repo_name')
214
215 slug = repo_name_slug(repo_name)
216 if slug in ['_admin', '']:
217 e_dict = {'repo_name': _('This repository name is disallowed')}
218 raise formencode.Invalid('', value, state, error_dict=e_dict)
219
220 gr = Group.get(value.get('repo_group'))
221
222 # value needs to be aware of group name
223 repo_name_full = gr.full_path + '/' + repo_name
224 value['repo_name_full'] = repo_name_full
225 if old_data.get('repo_name') != repo_name_full or not edit:
226
227 if gr.full_path != '':
228 if RepoModel().get_by_repo_name(repo_name_full,):
229 e_dict = {'repo_name':_('This repository already '
230 'exists in group "%s"') %
231 gr.group_name}
232 raise formencode.Invalid('', value, state,
233 error_dict=e_dict)
234
235 else:
236 if RepoModel().get_by_repo_name(repo_name_full):
237 e_dict = {'repo_name':_('This repository already exists')}
238 raise formencode.Invalid('', value, state,
239 error_dict=e_dict)
240 return value
222 241
223 242
224 243 return _ValidRepoName
225 244
245 def SlugifyRepo():
246 class _SlugifyRepo(formencode.validators.FancyValidator):
247
248 def to_python(self, value, state):
249 return repo_name_slug(value)
250
251 return _SlugifyRepo
252
226 253 def ValidCloneUri():
227 254 from mercurial.httprepo import httprepository, httpsrepository
228 255 from rhodecode.lib.utils import make_ui
229 256
230 257 class _ValidCloneUri(formencode.validators.FancyValidator):
231 258
232 259 def to_python(self, value, state):
233 260 if not value:
234 261 pass
235 262 elif value.startswith('https'):
236 263 try:
237 264 httpsrepository(make_ui('db'), value).capabilities
238 265 except Exception, e:
239 266 log.error(traceback.format_exc())
240 267 raise formencode.Invalid(_('invalid clone url'), value,
241 268 state)
242 269 elif value.startswith('http'):
243 270 try:
244 271 httprepository(make_ui('db'), value).capabilities
245 272 except Exception, e:
246 273 log.error(traceback.format_exc())
247 274 raise formencode.Invalid(_('invalid clone url'), value,
248 275 state)
249 276 else:
250 277 raise formencode.Invalid(_('Invalid clone url, provide a '
251 278 'valid clone http\s url'), value,
252 279 state)
253 280 return value
254 281
255 282 return _ValidCloneUri
256 283
257 284 def ValidForkType(old_data):
258 285 class _ValidForkType(formencode.validators.FancyValidator):
259 286
260 287 def to_python(self, value, state):
261 288 if old_data['repo_type'] != value:
262 289 raise formencode.Invalid(_('Fork have to be the same '
263 290 'type as original'), value, state)
264 291 return value
265 292 return _ValidForkType
266 293
267 294 class ValidPerms(formencode.validators.FancyValidator):
268 295 messages = {'perm_new_member_name':_('This username or users group name'
269 296 ' is not valid')}
270 297
271 298 def to_python(self, value, state):
272 299 perms_update = []
273 300 perms_new = []
274 301 #build a list of permission to update and new permission to create
275 302 for k, v in value.items():
276 303 #means new added member to permissions
277 304 if k.startswith('perm_new_member'):
278 305 new_perm = value.get('perm_new_member', False)
279 306 new_member = value.get('perm_new_member_name', False)
280 307 new_type = value.get('perm_new_member_type')
281 308
282 309 if new_member and new_perm:
283 310 if (new_member, new_perm, new_type) not in perms_new:
284 311 perms_new.append((new_member, new_perm, new_type))
285 312 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
286 313 member = k[7:]
287 314 t = {'u':'user',
288 315 'g':'users_group'}[k[0]]
289 316 if member == 'default':
290 317 if value['private']:
291 318 #set none for default when updating to private repo
292 319 v = 'repository.none'
293 320 perms_update.append((member, v, t))
294 321
295 322 value['perms_updates'] = perms_update
296 323 value['perms_new'] = perms_new
297 324
298 325 #update permissions
299 326 sa = meta.Session
300 327 for k, v, t in perms_new:
301 328 try:
302 329 if t is 'user':
303 330 self.user_db = sa.query(User)\
304 331 .filter(User.active == True)\
305 332 .filter(User.username == k).one()
306 333 if t is 'users_group':
307 334 self.user_db = sa.query(UsersGroup)\
308 335 .filter(UsersGroup.users_group_active == True)\
309 336 .filter(UsersGroup.users_group_name == k).one()
310 337
311 338 except Exception:
312 339 msg = self.message('perm_new_member_name',
313 340 state=State_obj)
314 341 raise formencode.Invalid(msg, value, state,
315 342 error_dict={'perm_new_member_name':msg})
316 343 return value
317 344
318 345 class ValidSettings(formencode.validators.FancyValidator):
319 346
320 347 def to_python(self, value, state):
321 348 #settings form can't edit user
322 349 if value.has_key('user'):
323 350 del['value']['user']
324 351
325 352 return value
326 353
327 354 class ValidPath(formencode.validators.FancyValidator):
328 355 def to_python(self, value, state):
329 356
330 357 if not os.path.isdir(value):
331 358 msg = _('This is not a valid path')
332 359 raise formencode.Invalid(msg, value, state,
333 360 error_dict={'paths_root_path':msg})
334 361 return value
335 362
336 363 def UniqSystemEmail(old_data):
337 364 class _UniqSystemEmail(formencode.validators.FancyValidator):
338 365 def to_python(self, value, state):
339 366 value = value.lower()
340 367 if old_data.get('email') != value:
341 368 sa = meta.Session()
342 369 try:
343 370 user = sa.query(User).filter(User.email == value).scalar()
344 371 if user:
345 372 raise formencode.Invalid(_("This e-mail address is already taken") ,
346 373 value, state)
347 374 finally:
348 375 meta.Session.remove()
349 376
350 377 return value
351 378
352 379 return _UniqSystemEmail
353 380
354 381 class ValidSystemEmail(formencode.validators.FancyValidator):
355 382 def to_python(self, value, state):
356 383 value = value.lower()
357 384 sa = meta.Session
358 385 try:
359 386 user = sa.query(User).filter(User.email == value).scalar()
360 387 if user is None:
361 388 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
362 389 value, state)
363 390 finally:
364 391 meta.Session.remove()
365 392
366 393 return value
367 394
368 395 class LdapLibValidator(formencode.validators.FancyValidator):
369 396
370 397 def to_python(self, value, state):
371 398
372 399 try:
373 400 import ldap
374 401 except ImportError:
375 402 raise LdapImportError
376 403 return value
377 404
378 405 class AttrLoginValidator(formencode.validators.FancyValidator):
379 406
380 407 def to_python(self, value, state):
381 408
382 409 if not value or not isinstance(value, (str, unicode)):
383 410 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
384 411 "must be specified - this is the name "
385 412 "of the attribute that is equivalent "
386 413 "to 'username'"),
387 414 value, state)
388 415
389 416 return value
390 417
391 418 #===============================================================================
392 419 # FORMS
393 420 #===============================================================================
394 421 class LoginForm(formencode.Schema):
395 422 allow_extra_fields = True
396 423 filter_extra_fields = True
397 424 username = UnicodeString(
398 425 strip=True,
399 426 min=1,
400 427 not_empty=True,
401 428 messages={
402 429 'empty':_('Please enter a login'),
403 430 'tooShort':_('Enter a value %(min)i characters long or more')}
404 431 )
405 432
406 433 password = UnicodeString(
407 434 strip=True,
408 435 min=6,
409 436 not_empty=True,
410 437 messages={
411 438 'empty':_('Please enter a password'),
412 439 'tooShort':_('Enter %(min)i characters or more')}
413 440 )
414 441
415 442
416 443 #chained validators have access to all data
417 444 chained_validators = [ValidAuth]
418 445
419 446 def UserForm(edit=False, old_data={}):
420 447 class _UserForm(formencode.Schema):
421 448 allow_extra_fields = True
422 449 filter_extra_fields = True
423 450 username = All(UnicodeString(strip=True, min=1, not_empty=True),
424 451 ValidUsername(edit, old_data))
425 452 if edit:
426 453 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
427 454 admin = StringBoolean(if_missing=False)
428 455 else:
429 456 password = All(UnicodeString(strip=True, min=6, not_empty=True))
430 457 active = StringBoolean(if_missing=False)
431 458 name = UnicodeString(strip=True, min=1, not_empty=True)
432 459 lastname = UnicodeString(strip=True, min=1, not_empty=True)
433 460 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
434 461
435 462 chained_validators = [ValidPassword]
436 463
437 464 return _UserForm
438 465
439 466
440 467 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
441 468 class _UsersGroupForm(formencode.Schema):
442 469 allow_extra_fields = True
443 470 filter_extra_fields = True
444 471
445 472 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
446 473 ValidUsersGroup(edit, old_data))
447 474
448 475 users_group_active = StringBoolean(if_missing=False)
449 476
450 477 if edit:
451 478 users_group_members = OneOf(available_members, hideList=False,
452 479 testValueList=True,
453 480 if_missing=None, not_empty=False)
454 481
455 482 return _UsersGroupForm
456 483
457 484 def RegisterForm(edit=False, old_data={}):
458 485 class _RegisterForm(formencode.Schema):
459 486 allow_extra_fields = True
460 487 filter_extra_fields = True
461 488 username = All(ValidUsername(edit, old_data),
462 489 UnicodeString(strip=True, min=1, not_empty=True))
463 490 password = All(UnicodeString(strip=True, min=6, not_empty=True))
464 491 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
465 492 active = StringBoolean(if_missing=False)
466 493 name = UnicodeString(strip=True, min=1, not_empty=True)
467 494 lastname = UnicodeString(strip=True, min=1, not_empty=True)
468 495 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
469 496
470 497 chained_validators = [ValidPasswordsMatch, ValidPassword]
471 498
472 499 return _RegisterForm
473 500
474 501 def PasswordResetForm():
475 502 class _PasswordResetForm(formencode.Schema):
476 503 allow_extra_fields = True
477 504 filter_extra_fields = True
478 505 email = All(ValidSystemEmail(), Email(not_empty=True))
479 506 return _PasswordResetForm
480 507
481 508 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
482 509 repo_groups=[]):
483 510 class _RepoForm(formencode.Schema):
484 511 allow_extra_fields = True
485 512 filter_extra_fields = False
486 513 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
487 ValidRepoName(edit, old_data))
514 SlugifyRepo())
488 515 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
489 516 ValidCloneUri()())
490 517 repo_group = OneOf(repo_groups, hideList=True)
491 518 repo_type = OneOf(supported_backends)
492 519 description = UnicodeString(strip=True, min=1, not_empty=True)
493 520 private = StringBoolean(if_missing=False)
494 521 enable_statistics = StringBoolean(if_missing=False)
495 522 enable_downloads = StringBoolean(if_missing=False)
496 523
497 524 if edit:
498 525 #this is repo owner
499 user = All(Int(not_empty=True), ValidRepoUser)
526 user = All(UnicodeString(not_empty=True), ValidRepoUser)
500 527
501 chained_validators = [ValidPerms]
528 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
502 529 return _RepoForm
503 530
504 531 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
505 532 class _RepoForkForm(formencode.Schema):
506 533 allow_extra_fields = True
507 534 filter_extra_fields = False
508 535 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
509 ValidRepoName(edit, old_data))
536 SlugifyRepo())
510 537 description = UnicodeString(strip=True, min=1, not_empty=True)
511 538 private = StringBoolean(if_missing=False)
512 539 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
513 540 return _RepoForkForm
514 541
515 542 def RepoSettingsForm(edit=False, old_data={}):
516 543 class _RepoForm(formencode.Schema):
517 544 allow_extra_fields = True
518 545 filter_extra_fields = False
519 546 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
520 ValidRepoName(edit, old_data))
547 SlugifyRepo())
521 548 description = UnicodeString(strip=True, min=1, not_empty=True)
522 549 private = StringBoolean(if_missing=False)
523 550
524 chained_validators = [ValidPerms, ValidSettings]
551 chained_validators = [ValidRepoName(edit, old_data), ValidPerms, ValidSettings]
525 552 return _RepoForm
526 553
527 554
528 555 def ApplicationSettingsForm():
529 556 class _ApplicationSettingsForm(formencode.Schema):
530 557 allow_extra_fields = True
531 558 filter_extra_fields = False
532 559 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
533 560 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
534 561 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
535 562
536 563 return _ApplicationSettingsForm
537 564
538 565 def ApplicationUiSettingsForm():
539 566 class _ApplicationUiSettingsForm(formencode.Schema):
540 567 allow_extra_fields = True
541 568 filter_extra_fields = False
542 569 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
543 570 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
544 571 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
545 572 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
546 573 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
547 574 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
548 575
549 576 return _ApplicationUiSettingsForm
550 577
551 578 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
552 579 class _DefaultPermissionsForm(formencode.Schema):
553 580 allow_extra_fields = True
554 581 filter_extra_fields = True
555 582 overwrite_default = StringBoolean(if_missing=False)
556 583 anonymous = OneOf(['True', 'False'], if_missing=False)
557 584 default_perm = OneOf(perms_choices)
558 585 default_register = OneOf(register_choices)
559 586 default_create = OneOf(create_choices)
560 587
561 588 return _DefaultPermissionsForm
562 589
563 590
564 591 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
565 592 class _LdapSettingsForm(formencode.Schema):
566 593 allow_extra_fields = True
567 594 filter_extra_fields = True
568 595 pre_validators = [LdapLibValidator]
569 596 ldap_active = StringBoolean(if_missing=False)
570 597 ldap_host = UnicodeString(strip=True,)
571 598 ldap_port = Number(strip=True,)
572 599 ldap_tls_kind = OneOf(tls_kind_choices)
573 600 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
574 601 ldap_dn_user = UnicodeString(strip=True,)
575 602 ldap_dn_pass = UnicodeString(strip=True,)
576 603 ldap_base_dn = UnicodeString(strip=True,)
577 604 ldap_filter = UnicodeString(strip=True,)
578 605 ldap_search_scope = OneOf(search_scope_choices)
579 606 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
580 607 ldap_attr_firstname = UnicodeString(strip=True,)
581 608 ldap_attr_lastname = UnicodeString(strip=True,)
582 609 ldap_attr_email = UnicodeString(strip=True,)
583 610
584 611 return _LdapSettingsForm
@@ -1,349 +1,355 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.repo
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository model for rhodecode
7 7
8 8 :created_on: Jun 5, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import shutil
27 27 import logging
28 28 import traceback
29 29 from datetime import datetime
30 30
31 31 from sqlalchemy.orm import joinedload, make_transient
32 32
33 33 from vcs.utils.lazy import LazyProperty
34 34 from vcs.backends import get_backend
35 35
36 36 from rhodecode.model import BaseModel
37 37 from rhodecode.model.caching_query import FromCache
38 38 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
39 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi
39 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group
40 40 from rhodecode.model.user import UserModel
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 class RepoModel(BaseModel):
46 46
47 47 @LazyProperty
48 48 def repos_path(self):
49 49 """Get's the repositories root path from database
50 50 """
51 51
52 52 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
53 53 return q.ui_value
54 54
55 55 def get(self, repo_id, cache=False):
56 56 repo = self.sa.query(Repository)\
57 57 .filter(Repository.repo_id == repo_id)
58 58
59 59 if cache:
60 60 repo = repo.options(FromCache("sql_cache_short",
61 61 "get_repo_%s" % repo_id))
62 62 return repo.scalar()
63 63
64 64 def get_by_repo_name(self, repo_name, cache=False):
65 65 repo = self.sa.query(Repository)\
66 66 .filter(Repository.repo_name == repo_name)
67 67
68 68 if cache:
69 69 repo = repo.options(FromCache("sql_cache_short",
70 70 "get_repo_%s" % repo_name))
71 71 return repo.scalar()
72 72
73 73 def get_full(self, repo_name, cache=False, invalidate=False):
74 74 repo = self.sa.query(Repository)\
75 75 .options(joinedload(Repository.fork))\
76 76 .options(joinedload(Repository.user))\
77 77 .filter(Repository.repo_name == repo_name)\
78 78
79 79 if cache:
80 80 repo = repo.options(FromCache("sql_cache_long",
81 81 "get_repo_full_%s" % repo_name))
82 82 if invalidate and cache:
83 83 repo.invalidate()
84 84
85 85 ret = repo.scalar()
86 86
87 87 #make transient for sake of errors
88 88 make_transient(ret)
89 89 for k in ['fork', 'user']:
90 90 attr = getattr(ret, k, False)
91 91 if attr:
92 92 make_transient(attr)
93 93 return ret
94 94
95 95 def get_users_js(self):
96 96
97 97 users = self.sa.query(User).filter(User.active == True).all()
98 98 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
99 99 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
100 100 u.lastname, u.username)
101 101 for u in users])
102 102 return users_array
103 103
104 104 def get_users_groups_js(self):
105 105 users_groups = self.sa.query(UsersGroup)\
106 106 .filter(UsersGroup.users_group_active == True).all()
107 107
108 108 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
109 109
110 110 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
111 111 (gr.users_group_id, gr.users_group_name,
112 112 len(gr.members))
113 113 for gr in users_groups])
114 114 return users_groups_array
115 115
116 116 def update(self, repo_name, form_data):
117 117 try:
118 118 cur_repo = self.get_by_repo_name(repo_name, cache=False)
119 119 user_model = UserModel(self.sa)
120 120
121 121 #update permissions
122 122 for member, perm, member_type in form_data['perms_updates']:
123 123 if member_type == 'user':
124 124 r2p = self.sa.query(RepoToPerm)\
125 125 .filter(RepoToPerm.user == user_model.
126 126 get_by_username(member))\
127 127 .filter(RepoToPerm.repository == cur_repo)\
128 128 .one()
129 129
130 130 r2p.permission = self.sa.query(Permission)\
131 131 .filter(Permission.permission_name ==
132 132 perm).scalar()
133 133 self.sa.add(r2p)
134 134 else:
135 135 g2p = self.sa.query(UsersGroupRepoToPerm)\
136 136 .filter(UsersGroupRepoToPerm.users_group ==
137 137 UsersGroup.get_by_group_name(member))\
138 138 .filter(UsersGroupRepoToPerm.repository ==
139 139 cur_repo).one()
140 140
141 141 g2p.permission = self.sa.query(Permission)\
142 142 .filter(Permission.permission_name ==
143 143 perm).scalar()
144 144 self.sa.add(g2p)
145 145
146 146 #set new permissions
147 147 for member, perm, member_type in form_data['perms_new']:
148 148 if member_type == 'user':
149 149 r2p = RepoToPerm()
150 150 r2p.repository = cur_repo
151 151 r2p.user = user_model.get_by_username(member)
152 152
153 153 r2p.permission = self.sa.query(Permission)\
154 154 .filter(Permission.
155 155 permission_name == perm)\
156 156 .scalar()
157 157 self.sa.add(r2p)
158 158 else:
159 159 g2p = UsersGroupRepoToPerm()
160 160 g2p.repository = cur_repo
161 161 g2p.users_group = UsersGroup.get_by_group_name(member)
162 162
163 163 g2p.permission = self.sa.query(Permission)\
164 164 .filter(Permission.
165 165 permission_name == perm)\
166 166 .scalar()
167 167 self.sa.add(g2p)
168 168
169 169 #update current repo
170 170 for k, v in form_data.items():
171 171 if k == 'user':
172 cur_repo.user = user_model.get(v)
172 cur_repo.user = user_model.get_by_username(v)
173 elif k == 'repo_name':
174 cur_repo.repo_name = form_data['repo_name_full']
175 elif k == 'repo_group' and v:
176 cur_repo.group_id = v
177
173 178 else:
174 179 setattr(cur_repo, k, v)
175 180
176 181 self.sa.add(cur_repo)
177 182
178 if repo_name != form_data['repo_name']:
179 #rename our data
180 self.__rename_repo(repo_name, form_data['repo_name'])
183 if repo_name != form_data['repo_name_full']:
184 # rename repository
185 self.__rename_repo(old=repo_name,
186 new=form_data['repo_name_full'])
181 187
182 188 self.sa.commit()
183 189 except:
184 190 log.error(traceback.format_exc())
185 191 self.sa.rollback()
186 192 raise
187 193
188 194 def create(self, form_data, cur_user, just_db=False, fork=False):
189 195 try:
190 196 if fork:
191 197 #force str since hg doesn't go with unicode
192 198 repo_name = str(form_data['fork_name'])
193 199 org_name = str(form_data['repo_name'])
194 200
195 201 else:
196 202 org_name = repo_name = str(form_data['repo_name'])
197 203 new_repo = Repository()
198 204 new_repo.enable_statistics = False
199 205 for k, v in form_data.items():
200 206 if k == 'repo_name':
201 207 v = repo_name
202 208 setattr(new_repo, k, v)
203 209
204 210 if fork:
205 211 parent_repo = self.sa.query(Repository)\
206 212 .filter(Repository.repo_name == org_name).scalar()
207 213 new_repo.fork = parent_repo
208 214
209 215 new_repo.user_id = cur_user.user_id
210 216 self.sa.add(new_repo)
211 217
212 218 #create default permission
213 219 repo_to_perm = RepoToPerm()
214 220 default = 'repository.read'
215 221 for p in UserModel(self.sa).get_by_username('default',
216 222 cache=False).user_perms:
217 223 if p.permission.permission_name.startswith('repository.'):
218 224 default = p.permission.permission_name
219 225 break
220 226
221 227 default_perm = 'repository.none' if form_data['private'] else default
222 228
223 229 repo_to_perm.permission_id = self.sa.query(Permission)\
224 230 .filter(Permission.permission_name == default_perm)\
225 231 .one().permission_id
226 232
227 233 repo_to_perm.repository = new_repo
228 234 repo_to_perm.user_id = UserModel(self.sa)\
229 235 .get_by_username('default', cache=False).user_id
230 236
231 237 self.sa.add(repo_to_perm)
232 238
233 239 if not just_db:
234 240 self.__create_repo(repo_name, form_data['repo_type'],
235 241 form_data['clone_uri'])
236 242
237 243 self.sa.commit()
238 244
239 245 #now automatically start following this repository as owner
240 246 from rhodecode.model.scm import ScmModel
241 247 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
242 248 cur_user.user_id)
243 249
244 250 except:
245 251 log.error(traceback.format_exc())
246 252 self.sa.rollback()
247 253 raise
248 254
249 255 def create_fork(self, form_data, cur_user):
250 256 from rhodecode.lib.celerylib import tasks, run_task
251 257 run_task(tasks.create_repo_fork, form_data, cur_user)
252 258
253 259 def delete(self, repo):
254 260 try:
255 261 self.sa.delete(repo)
256 262 self.__delete_repo(repo)
257 263 self.sa.commit()
258 264 except:
259 265 log.error(traceback.format_exc())
260 266 self.sa.rollback()
261 267 raise
262 268
263 269 def delete_perm_user(self, form_data, repo_name):
264 270 try:
265 271 self.sa.query(RepoToPerm)\
266 272 .filter(RepoToPerm.repository \
267 273 == self.get_by_repo_name(repo_name))\
268 274 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
269 275 self.sa.commit()
270 276 except:
271 277 log.error(traceback.format_exc())
272 278 self.sa.rollback()
273 279 raise
274 280
275 281 def delete_perm_users_group(self, form_data, repo_name):
276 282 try:
277 283 self.sa.query(UsersGroupRepoToPerm)\
278 284 .filter(UsersGroupRepoToPerm.repository \
279 285 == self.get_by_repo_name(repo_name))\
280 286 .filter(UsersGroupRepoToPerm.users_group_id \
281 287 == form_data['users_group_id']).delete()
282 288 self.sa.commit()
283 289 except:
284 290 log.error(traceback.format_exc())
285 291 self.sa.rollback()
286 292 raise
287 293
288 294 def delete_stats(self, repo_name):
289 295 try:
290 296 self.sa.query(Statistics)\
291 297 .filter(Statistics.repository == \
292 298 self.get_by_repo_name(repo_name)).delete()
293 299 self.sa.commit()
294 300 except:
295 301 log.error(traceback.format_exc())
296 302 self.sa.rollback()
297 303 raise
298 304
299 305 def __create_repo(self, repo_name, alias, clone_uri=False):
300 306 """
301 307 makes repository on filesystem
302 308
303 309 :param repo_name:
304 310 :param alias:
305 311 """
306 312 from rhodecode.lib.utils import check_repo
307 313 repo_path = os.path.join(self.repos_path, repo_name)
308 314 if check_repo(repo_name, self.repos_path):
309 315 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
310 316 clone_uri)
311 317 backend = get_backend(alias)
312 318 backend(repo_path, create=True, src_url=clone_uri)
313 319
314 320 def __rename_repo(self, old, new):
315 321 """
316 322 renames repository on filesystem
317 323
318 324 :param old: old name
319 325 :param new: new name
320 326 """
321 327 log.info('renaming repo from %s to %s', old, new)
322 328
323 329 old_path = os.path.join(self.repos_path, old)
324 330 new_path = os.path.join(self.repos_path, new)
325 331 if os.path.isdir(new_path):
326 332 raise Exception('Was trying to rename to already existing dir %s',
327 333 new_path)
328 334 shutil.move(old_path, new_path)
329 335
330 336 def __delete_repo(self, repo):
331 337 """
332 338 removes repo from filesystem, the removal is acctually made by
333 339 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
334 340 repository is no longer valid for rhodecode, can be undeleted later on
335 341 by reverting the renames on this repository
336 342
337 343 :param repo: repo object
338 344 """
339 345 rm_path = os.path.join(self.repos_path, repo.repo_name)
340 346 log.info("Removing %s", rm_path)
341 347 #disable hg/git
342 348 alias = repo.repo_type
343 349 shutil.move(os.path.join(rm_path, '.%s' % alias),
344 350 os.path.join(rm_path, 'rm__.%s' % alias))
345 351 #disable repo
346 352 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
347 353 % (datetime.today()\
348 354 .strftime('%Y%m%d_%H%M%S_%f'),
349 355 repo.repo_name)))
General Comments 0
You need to be logged in to leave comments. Login now