##// END OF EJS Templates
Added group permission autofix for older version of rhodecode which didn't have default permissions for repos groups
marcink -
r1985:40f04021 beta
parent child Browse files
Show More
@@ -1,500 +1,517 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.db_manage
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Database creation, and setup module for RhodeCode. Used for creation
7 7 of database as well as for migration operations
8 8
9 9 :created_on: Apr 10, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import sys
29 29 import uuid
30 30 import logging
31 31 from os.path import dirname as dn, join as jn
32 32
33 33 from rhodecode import __dbversion__
34 34 from rhodecode.model import meta
35 35
36 36 from rhodecode.model.user import UserModel
37 37 from rhodecode.lib.utils import ask_ok
38 38 from rhodecode.model import init_model
39 39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
41 UserRepoGroupToPerm
41 42
42 43 from sqlalchemy.engine import create_engine
44 from rhodecode.model.repos_group import ReposGroupModel
43 45
44 46 log = logging.getLogger(__name__)
45 47
46 48
47 49 class DbManage(object):
48 50 def __init__(self, log_sql, dbconf, root, tests=False):
49 51 self.dbname = dbconf.split('/')[-1]
50 52 self.tests = tests
51 53 self.root = root
52 54 self.dburi = dbconf
53 55 self.log_sql = log_sql
54 56 self.db_exists = False
55 57 self.init_db()
56 58
57 59 def init_db(self):
58 60 engine = create_engine(self.dburi, echo=self.log_sql)
59 61 init_model(engine)
60 62 self.sa = meta.Session
61 63
62 64 def create_tables(self, override=False):
63 65 """
64 66 Create a auth database
65 67 """
66 68
67 69 log.info("Any existing database is going to be destroyed")
68 70 if self.tests:
69 71 destroy = True
70 72 else:
71 73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
72 74 if not destroy:
73 75 sys.exit()
74 76 if destroy:
75 77 meta.Base.metadata.drop_all()
76 78
77 79 checkfirst = not override
78 80 meta.Base.metadata.create_all(checkfirst=checkfirst)
79 81 log.info('Created tables for %s' % self.dbname)
80 82
81 83 def set_db_version(self):
82 84 ver = DbMigrateVersion()
83 85 ver.version = __dbversion__
84 86 ver.repository_id = 'rhodecode_db_migrations'
85 87 ver.repository_path = 'versions'
86 88 self.sa.add(ver)
87 89 log.info('db version set to: %s' % __dbversion__)
88 90
89 91 def upgrade(self):
90 92 """
91 93 Upgrades given database schema to given revision following
92 94 all needed steps, to perform the upgrade
93 95
94 96 """
95 97
96 98 from rhodecode.lib.dbmigrate.migrate.versioning import api
97 99 from rhodecode.lib.dbmigrate.migrate.exceptions import \
98 100 DatabaseNotControlledError
99 101
100 102 if 'sqlite' in self.dburi:
101 103 print (
102 104 '********************** WARNING **********************\n'
103 105 'Make sure your version of sqlite is at least 3.7.X. \n'
104 106 'Earlier versions are known to fail on some migrations\n'
105 107 '*****************************************************\n'
106 108 )
107 109 upgrade = ask_ok('You are about to perform database upgrade, make '
108 110 'sure You backed up your database before. '
109 111 'Continue ? [y/n]')
110 112 if not upgrade:
111 113 sys.exit('Nothing done')
112 114
113 115 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
114 116 'rhodecode/lib/dbmigrate')
115 117 db_uri = self.dburi
116 118
117 119 try:
118 120 curr_version = api.db_version(db_uri, repository_path)
119 121 msg = ('Found current database under version'
120 122 ' control with version %s' % curr_version)
121 123
122 124 except (RuntimeError, DatabaseNotControlledError):
123 125 curr_version = 1
124 126 msg = ('Current database is not under version control. Setting'
125 127 ' as version %s' % curr_version)
126 128 api.version_control(db_uri, repository_path, curr_version)
127 129
128 130 print (msg)
129 131
130 132 if curr_version == __dbversion__:
131 133 sys.exit('This database is already at the newest version')
132 134
133 135 #======================================================================
134 136 # UPGRADE STEPS
135 137 #======================================================================
136 138 class UpgradeSteps(object):
137 139 """
138 140 Those steps follow schema versions so for example schema
139 141 for example schema with seq 002 == step_2 and so on.
140 142 """
141 143
142 144 def __init__(self, klass):
143 145 self.klass = klass
144 146
145 147 def step_0(self):
146 148 # step 0 is the schema upgrade, and than follow proper upgrades
147 149 print ('attempting to do database upgrade to version %s' \
148 150 % __dbversion__)
149 151 api.upgrade(db_uri, repository_path, __dbversion__)
150 152 print ('Schema upgrade completed')
151 153
152 154 def step_1(self):
153 155 pass
154 156
155 157 def step_2(self):
156 158 print ('Patching repo paths for newer version of RhodeCode')
157 159 self.klass.fix_repo_paths()
158 160
159 161 print ('Patching default user of RhodeCode')
160 162 self.klass.fix_default_user()
161 163
162 164 log.info('Changing ui settings')
163 165 self.klass.create_ui_settings()
164 166
165 167 def step_3(self):
166 168 print ('Adding additional settings into RhodeCode db')
167 169 self.klass.fix_settings()
168 170 print ('Adding ldap defaults')
169 171 self.klass.create_ldap_options(skip_existing=True)
170 172
171 173 def step_4(self):
172 174 print ('TODO:')
173 raise NotImplementedError()
175 self.klass.fixup_groups()
174 176
175 177 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
176 178
177 179 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
178 180 for step in upgrade_steps:
179 181 print ('performing upgrade step %s' % step)
180 182 getattr(UpgradeSteps(self), 'step_%s' % step)()
181 183
182 184 def fix_repo_paths(self):
183 185 """
184 186 Fixes a old rhodecode version path into new one without a '*'
185 187 """
186 188
187 189 paths = self.sa.query(RhodeCodeUi)\
188 190 .filter(RhodeCodeUi.ui_key == '/')\
189 191 .scalar()
190 192
191 193 paths.ui_value = paths.ui_value.replace('*', '')
192 194
193 195 try:
194 196 self.sa.add(paths)
195 197 self.sa.commit()
196 198 except:
197 199 self.sa.rollback()
198 200 raise
199 201
200 202 def fix_default_user(self):
201 203 """
202 204 Fixes a old default user with some 'nicer' default values,
203 205 used mostly for anonymous access
204 206 """
205 207 def_user = self.sa.query(User)\
206 208 .filter(User.username == 'default')\
207 209 .one()
208 210
209 211 def_user.name = 'Anonymous'
210 212 def_user.lastname = 'User'
211 213 def_user.email = 'anonymous@rhodecode.org'
212 214
213 215 try:
214 216 self.sa.add(def_user)
215 217 self.sa.commit()
216 218 except:
217 219 self.sa.rollback()
218 220 raise
219 221
220 222 def fix_settings(self):
221 223 """
222 224 Fixes rhodecode settings adds ga_code key for google analytics
223 225 """
224 226
225 227 hgsettings3 = RhodeCodeSetting('ga_code', '')
226 228
227 229 try:
228 230 self.sa.add(hgsettings3)
229 231 self.sa.commit()
230 232 except:
231 233 self.sa.rollback()
232 234 raise
233 235
234 236 def admin_prompt(self, second=False):
235 237 if not self.tests:
236 238 import getpass
237 239
238 240 def get_password():
239 241 password = getpass.getpass('Specify admin password '
240 242 '(min 6 chars):')
241 243 confirm = getpass.getpass('Confirm password:')
242 244
243 245 if password != confirm:
244 246 log.error('passwords mismatch')
245 247 return False
246 248 if len(password) < 6:
247 249 log.error('password is to short use at least 6 characters')
248 250 return False
249 251
250 252 return password
251 253
252 254 username = raw_input('Specify admin username:')
253 255
254 256 password = get_password()
255 257 if not password:
256 258 #second try
257 259 password = get_password()
258 260 if not password:
259 261 sys.exit()
260 262
261 263 email = raw_input('Specify admin email:')
262 264 self.create_user(username, password, email, True)
263 265 else:
264 266 log.info('creating admin and regular test users')
265 267 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
266 268 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
267 269 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
268 270 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
269 271 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
270 272
271 273 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
272 274 TEST_USER_ADMIN_EMAIL, True)
273 275
274 276 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
275 277 TEST_USER_REGULAR_EMAIL, False)
276 278
277 279 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
278 280 TEST_USER_REGULAR2_EMAIL, False)
279 281
280 282 def create_ui_settings(self):
281 283 """
282 284 Creates ui settings, fills out hooks
283 285 and disables dotencode
284 286 """
285 287
286 288 #HOOKS
287 289 hooks1_key = RhodeCodeUi.HOOK_UPDATE
288 290 hooks1_ = self.sa.query(RhodeCodeUi)\
289 291 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
290 292
291 293 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
292 294 hooks1.ui_section = 'hooks'
293 295 hooks1.ui_key = hooks1_key
294 296 hooks1.ui_value = 'hg update >&2'
295 297 hooks1.ui_active = False
296 298
297 299 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
298 300 hooks2_ = self.sa.query(RhodeCodeUi)\
299 301 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
300 302
301 303 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
302 304 hooks2.ui_section = 'hooks'
303 305 hooks2.ui_key = hooks2_key
304 306 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
305 307
306 308 hooks3 = RhodeCodeUi()
307 309 hooks3.ui_section = 'hooks'
308 310 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
309 311 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
310 312
311 313 hooks4 = RhodeCodeUi()
312 314 hooks4.ui_section = 'hooks'
313 315 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
314 316 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
315 317
316 318 # For mercurial 1.7 set backward comapatibility with format
317 319 dotencode_disable = RhodeCodeUi()
318 320 dotencode_disable.ui_section = 'format'
319 321 dotencode_disable.ui_key = 'dotencode'
320 322 dotencode_disable.ui_value = 'false'
321 323
322 324 # enable largefiles
323 325 largefiles = RhodeCodeUi()
324 326 largefiles.ui_section = 'extensions'
325 327 largefiles.ui_key = 'largefiles'
326 328 largefiles.ui_value = ''
327 329
328 330 self.sa.add(hooks1)
329 331 self.sa.add(hooks2)
330 332 self.sa.add(hooks3)
331 333 self.sa.add(hooks4)
332 334 self.sa.add(largefiles)
333 335
334 336 def create_ldap_options(self, skip_existing=False):
335 337 """Creates ldap settings"""
336 338
337 339 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
338 340 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
339 341 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
340 342 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
341 343 ('ldap_filter', ''), ('ldap_search_scope', ''),
342 344 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
343 345 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
344 346
345 347 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
346 348 log.debug('Skipping option %s' % k)
347 349 continue
348 350 setting = RhodeCodeSetting(k, v)
349 351 self.sa.add(setting)
350 352
353 def fixup_groups(self):
354 def_usr = User.get_by_username('default')
355 for g in RepoGroup.query().all():
356 g.group_name = g.get_new_name(g.name)
357 self.sa.add(g)
358 # get default perm
359 default = UserRepoGroupToPerm.query()\
360 .filter(UserRepoGroupToPerm.group == g)\
361 .filter(UserRepoGroupToPerm.user == def_usr)\
362 .scalar()
363
364 if default is None:
365 log.debug('missing default permission for group %s adding' % g)
366 ReposGroupModel()._create_default_perms(g)
367
351 368 def config_prompt(self, test_repo_path='', retries=3):
352 369 if retries == 3:
353 370 log.info('Setting up repositories config')
354 371
355 372 if not self.tests and not test_repo_path:
356 373 path = raw_input(
357 374 'Enter a valid path to store repositories. '
358 375 'All repositories in that path will be added automatically:'
359 376 )
360 377 else:
361 378 path = test_repo_path
362 379 path_ok = True
363 380
364 381 # check proper dir
365 382 if not os.path.isdir(path):
366 383 path_ok = False
367 384 log.error('Given path %s is not a valid directory' % path)
368 385
369 386 # check write access
370 387 if not os.access(path, os.W_OK) and path_ok:
371 388 path_ok = False
372 389 log.error('No write permission to given path %s' % path)
373 390
374 391 if retries == 0:
375 392 sys.exit('max retries reached')
376 393 if path_ok is False:
377 394 retries -= 1
378 395 return self.config_prompt(test_repo_path, retries)
379 396
380 397 return path
381 398
382 399 def create_settings(self, path):
383 400
384 401 self.create_ui_settings()
385 402
386 403 #HG UI OPTIONS
387 404 web1 = RhodeCodeUi()
388 405 web1.ui_section = 'web'
389 406 web1.ui_key = 'push_ssl'
390 407 web1.ui_value = 'false'
391 408
392 409 web2 = RhodeCodeUi()
393 410 web2.ui_section = 'web'
394 411 web2.ui_key = 'allow_archive'
395 412 web2.ui_value = 'gz zip bz2'
396 413
397 414 web3 = RhodeCodeUi()
398 415 web3.ui_section = 'web'
399 416 web3.ui_key = 'allow_push'
400 417 web3.ui_value = '*'
401 418
402 419 web4 = RhodeCodeUi()
403 420 web4.ui_section = 'web'
404 421 web4.ui_key = 'baseurl'
405 422 web4.ui_value = '/'
406 423
407 424 paths = RhodeCodeUi()
408 425 paths.ui_section = 'paths'
409 426 paths.ui_key = '/'
410 427 paths.ui_value = path
411 428
412 429 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
413 430 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
414 431 hgsettings3 = RhodeCodeSetting('ga_code', '')
415 432
416 433 self.sa.add(web1)
417 434 self.sa.add(web2)
418 435 self.sa.add(web3)
419 436 self.sa.add(web4)
420 437 self.sa.add(paths)
421 438 self.sa.add(hgsettings1)
422 439 self.sa.add(hgsettings2)
423 440 self.sa.add(hgsettings3)
424 441
425 442 self.create_ldap_options()
426 443
427 444 log.info('created ui config')
428 445
429 446 def create_user(self, username, password, email='', admin=False):
430 447 log.info('creating user %s' % username)
431 448 UserModel().create_or_update(username, password, email,
432 449 name='RhodeCode', lastname='Admin',
433 450 active=True, admin=admin)
434 451
435 452 def create_default_user(self):
436 453 log.info('creating default user')
437 454 # create default user for handling default permissions.
438 455 UserModel().create_or_update(username='default',
439 456 password=str(uuid.uuid1())[:8],
440 457 email='anonymous@rhodecode.org',
441 458 name='Anonymous', lastname='User')
442 459
443 460 def create_permissions(self):
444 461 # module.(access|create|change|delete)_[name]
445 462 # module.(none|read|write|admin)
446 463 perms = [
447 464 ('repository.none', 'Repository no access'),
448 465 ('repository.read', 'Repository read access'),
449 466 ('repository.write', 'Repository write access'),
450 467 ('repository.admin', 'Repository admin access'),
451 468
452 469 ('group.none', 'Repositories Group no access'),
453 470 ('group.read', 'Repositories Group read access'),
454 471 ('group.write', 'Repositories Group write access'),
455 472 ('group.admin', 'Repositories Group admin access'),
456 473
457 474 ('hg.admin', 'Hg Administrator'),
458 475 ('hg.create.repository', 'Repository create'),
459 476 ('hg.create.none', 'Repository creation disabled'),
460 477 ('hg.register.none', 'Register disabled'),
461 478 ('hg.register.manual_activate', 'Register new user with RhodeCode '
462 479 'without manual activation'),
463 480
464 481 ('hg.register.auto_activate', 'Register new user with RhodeCode '
465 482 'without auto activation'),
466 483 ]
467 484
468 485 for p in perms:
469 486 new_perm = Permission()
470 487 new_perm.permission_name = p[0]
471 488 new_perm.permission_longname = p[1]
472 489 self.sa.add(new_perm)
473 490
474 491 def populate_default_permissions(self):
475 492 log.info('creating default user permissions')
476 493
477 494 default_user = self.sa.query(User)\
478 495 .filter(User.username == 'default').scalar()
479 496
480 497 reg_perm = UserToPerm()
481 498 reg_perm.user = default_user
482 499 reg_perm.permission = self.sa.query(Permission)\
483 500 .filter(Permission.permission_name == 'hg.register.manual_activate')\
484 501 .scalar()
485 502
486 503 create_repo_perm = UserToPerm()
487 504 create_repo_perm.user = default_user
488 505 create_repo_perm.permission = self.sa.query(Permission)\
489 506 .filter(Permission.permission_name == 'hg.create.repository')\
490 507 .scalar()
491 508
492 509 default_repo_perm = UserToPerm()
493 510 default_repo_perm.user = default_user
494 511 default_repo_perm.permission = self.sa.query(Permission)\
495 512 .filter(Permission.permission_name == 'repository.read')\
496 513 .scalar()
497 514
498 515 self.sa.add(reg_perm)
499 516 self.sa.add(create_repo_perm)
500 517 self.sa.add(default_repo_perm)
@@ -1,616 +1,633 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 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 import traceback
30 30 import paste
31 31 import beaker
32 32 import tarfile
33 33 import shutil
34 34 from os.path import abspath
35 35 from os.path import dirname as dn, join as jn
36 36
37 37 from paste.script.command import Command, BadCommand
38 38
39 39 from mercurial import ui, config
40 40
41 41 from webhelpers.text import collapse, remove_formatting, strip_tags
42 42
43 43 from vcs import get_backend
44 44 from vcs.backends.base import BaseChangeset
45 45 from vcs.utils.lazy import LazyProperty
46 46 from vcs.utils.helpers import get_scm
47 47 from vcs.exceptions import VCSError
48 48
49 49 from rhodecode.lib.caching_query import FromCache
50 50
51 51 from rhodecode.model import meta
52 52 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 UserLog, RepoGroup, RhodeCodeSetting
53 UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm
54 54 from rhodecode.model.meta import Session
55 55 from rhodecode.model.repos_group import ReposGroupModel
56 56
57 57 log = logging.getLogger(__name__)
58 58
59 59
60 60 def recursive_replace(str_, replace=' '):
61 61 """Recursive replace of given sign to just one instance
62 62
63 63 :param str_: given string
64 64 :param replace: char to find and replace multiple instances
65 65
66 66 Examples::
67 67 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
68 68 'Mighty-Mighty-Bo-sstones'
69 69 """
70 70
71 71 if str_.find(replace * 2) == -1:
72 72 return str_
73 73 else:
74 74 str_ = str_.replace(replace * 2, replace)
75 75 return recursive_replace(str_, replace)
76 76
77 77
78 78 def repo_name_slug(value):
79 79 """Return slug of name of repository
80 80 This function is called on each creation/modification
81 81 of repository to prevent bad names in repo
82 82 """
83 83
84 84 slug = remove_formatting(value)
85 85 slug = strip_tags(slug)
86 86
87 87 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
88 88 slug = slug.replace(c, '-')
89 89 slug = recursive_replace(slug, '-')
90 90 slug = collapse(slug, '-')
91 91 return slug
92 92
93 93
94 94 def get_repo_slug(request):
95 95 return request.environ['pylons.routes_dict'].get('repo_name')
96 96
97 97
98 98 def get_repos_group_slug(request):
99 99 return request.environ['pylons.routes_dict'].get('group_name')
100 100
101 101
102 102 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
103 103 """
104 104 Action logger for various actions made by users
105 105
106 106 :param user: user that made this action, can be a unique username string or
107 107 object containing user_id attribute
108 108 :param action: action to log, should be on of predefined unique actions for
109 109 easy translations
110 110 :param repo: string name of repository or object containing repo_id,
111 111 that action was made on
112 112 :param ipaddr: optional ip address from what the action was made
113 113 :param sa: optional sqlalchemy session
114 114
115 115 """
116 116
117 117 if not sa:
118 118 sa = meta.Session
119 119
120 120 try:
121 121 if hasattr(user, 'user_id'):
122 122 user_obj = user
123 123 elif isinstance(user, basestring):
124 124 user_obj = User.get_by_username(user)
125 125 else:
126 126 raise Exception('You have to provide user object or username')
127 127
128 128 if hasattr(repo, 'repo_id'):
129 129 repo_obj = Repository.get(repo.repo_id)
130 130 repo_name = repo_obj.repo_name
131 131 elif isinstance(repo, basestring):
132 132 repo_name = repo.lstrip('/')
133 133 repo_obj = Repository.get_by_repo_name(repo_name)
134 134 else:
135 135 raise Exception('You have to provide repository to action logger')
136 136
137 137 user_log = UserLog()
138 138 user_log.user_id = user_obj.user_id
139 139 user_log.action = action
140 140
141 141 user_log.repository_id = repo_obj.repo_id
142 142 user_log.repository_name = repo_name
143 143
144 144 user_log.action_date = datetime.datetime.now()
145 145 user_log.user_ip = ipaddr
146 146 sa.add(user_log)
147 147
148 148 log.info('Adding user %s, action %s on %s' % (user_obj, action, repo))
149 149 if commit:
150 150 sa.commit()
151 151 except:
152 152 log.error(traceback.format_exc())
153 153 raise
154 154
155 155
156 156 def get_repos(path, recursive=False):
157 157 """
158 158 Scans given path for repos and return (name,(type,path)) tuple
159 159
160 160 :param path: path to scan for repositories
161 161 :param recursive: recursive search and return names with subdirs in front
162 162 """
163 163
164 164 # remove ending slash for better results
165 165 path = path.rstrip(os.sep)
166 166
167 167 def _get_repos(p):
168 168 if not os.access(p, os.W_OK):
169 169 return
170 170 for dirpath in os.listdir(p):
171 171 if os.path.isfile(os.path.join(p, dirpath)):
172 172 continue
173 173 cur_path = os.path.join(p, dirpath)
174 174 try:
175 175 scm_info = get_scm(cur_path)
176 176 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
177 177 except VCSError:
178 178 if not recursive:
179 179 continue
180 180 #check if this dir containts other repos for recursive scan
181 181 rec_path = os.path.join(p, dirpath)
182 182 if os.path.isdir(rec_path):
183 183 for inner_scm in _get_repos(rec_path):
184 184 yield inner_scm
185 185
186 186 return _get_repos(path)
187 187
188 188
189 189 def is_valid_repo(repo_name, base_path):
190 190 """
191 191 Returns True if given path is a valid repository False otherwise
192 192 :param repo_name:
193 193 :param base_path:
194 194
195 195 :return True: if given path is a valid repository
196 196 """
197 197 full_path = os.path.join(base_path, repo_name)
198 198
199 199 try:
200 200 get_scm(full_path)
201 201 return True
202 202 except VCSError:
203 203 return False
204 204
205 205
206 206 def is_valid_repos_group(repos_group_name, base_path):
207 207 """
208 208 Returns True if given path is a repos group False otherwise
209 209
210 210 :param repo_name:
211 211 :param base_path:
212 212 """
213 213 full_path = os.path.join(base_path, repos_group_name)
214 214
215 215 # check if it's not a repo
216 216 if is_valid_repo(repos_group_name, base_path):
217 217 return False
218 218
219 219 # check if it's a valid path
220 220 if os.path.isdir(full_path):
221 221 return True
222 222
223 223 return False
224 224
225 225
226 226 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
227 227 while True:
228 228 ok = raw_input(prompt)
229 229 if ok in ('y', 'ye', 'yes'):
230 230 return True
231 231 if ok in ('n', 'no', 'nop', 'nope'):
232 232 return False
233 233 retries = retries - 1
234 234 if retries < 0:
235 235 raise IOError
236 236 print complaint
237 237
238 238 #propagated from mercurial documentation
239 239 ui_sections = ['alias', 'auth',
240 240 'decode/encode', 'defaults',
241 241 'diff', 'email',
242 242 'extensions', 'format',
243 243 'merge-patterns', 'merge-tools',
244 244 'hooks', 'http_proxy',
245 245 'smtp', 'patch',
246 246 'paths', 'profiling',
247 247 'server', 'trusted',
248 248 'ui', 'web', ]
249 249
250 250
251 251 def make_ui(read_from='file', path=None, checkpaths=True):
252 252 """A function that will read python rc files or database
253 253 and make an mercurial ui object from read options
254 254
255 255 :param path: path to mercurial config file
256 256 :param checkpaths: check the path
257 257 :param read_from: read from 'file' or 'db'
258 258 """
259 259
260 260 baseui = ui.ui()
261 261
262 262 #clean the baseui object
263 263 baseui._ocfg = config.config()
264 264 baseui._ucfg = config.config()
265 265 baseui._tcfg = config.config()
266 266
267 267 if read_from == 'file':
268 268 if not os.path.isfile(path):
269 269 log.warning('Unable to read config file %s' % path)
270 270 return False
271 271 log.debug('reading hgrc from %s' % path)
272 272 cfg = config.config()
273 273 cfg.read(path)
274 274 for section in ui_sections:
275 275 for k, v in cfg.items(section):
276 276 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
277 277 baseui.setconfig(section, k, v)
278 278
279 279 elif read_from == 'db':
280 280 sa = meta.Session
281 281 ret = sa.query(RhodeCodeUi)\
282 282 .options(FromCache("sql_cache_short",
283 283 "get_hg_ui_settings")).all()
284 284
285 285 hg_ui = ret
286 286 for ui_ in hg_ui:
287 287 if ui_.ui_active:
288 288 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
289 289 ui_.ui_key, ui_.ui_value)
290 290 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
291 291
292 292 meta.Session.remove()
293 293 return baseui
294 294
295 295
296 296 def set_rhodecode_config(config):
297 297 """
298 298 Updates pylons config with new settings from database
299 299
300 300 :param config:
301 301 """
302 302 hgsettings = RhodeCodeSetting.get_app_settings()
303 303
304 304 for k, v in hgsettings.items():
305 305 config[k] = v
306 306
307 307
308 308 def invalidate_cache(cache_key, *args):
309 309 """
310 310 Puts cache invalidation task into db for
311 311 further global cache invalidation
312 312 """
313 313
314 314 from rhodecode.model.scm import ScmModel
315 315
316 316 if cache_key.startswith('get_repo_cached_'):
317 317 name = cache_key.split('get_repo_cached_')[-1]
318 318 ScmModel().mark_for_invalidation(name)
319 319
320 320
321 321 class EmptyChangeset(BaseChangeset):
322 322 """
323 323 An dummy empty changeset. It's possible to pass hash when creating
324 324 an EmptyChangeset
325 325 """
326 326
327 327 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
328 328 alias=None):
329 329 self._empty_cs = cs
330 330 self.revision = -1
331 331 self.message = ''
332 332 self.author = ''
333 333 self.date = ''
334 334 self.repository = repo
335 335 self.requested_revision = requested_revision
336 336 self.alias = alias
337 337
338 338 @LazyProperty
339 339 def raw_id(self):
340 340 """
341 341 Returns raw string identifying this changeset, useful for web
342 342 representation.
343 343 """
344 344
345 345 return self._empty_cs
346 346
347 347 @LazyProperty
348 348 def branch(self):
349 349 return get_backend(self.alias).DEFAULT_BRANCH_NAME
350 350
351 351 @LazyProperty
352 352 def short_id(self):
353 353 return self.raw_id[:12]
354 354
355 355 def get_file_changeset(self, path):
356 356 return self
357 357
358 358 def get_file_content(self, path):
359 359 return u''
360 360
361 361 def get_file_size(self, path):
362 362 return 0
363 363
364 364
365 365 def map_groups(groups):
366 366 """
367 367 Checks for groups existence, and creates groups structures.
368 368 It returns last group in structure
369 369
370 370 :param groups: list of groups structure
371 371 """
372 372 sa = meta.Session
373 373
374 374 parent = None
375 375 group = None
376 376
377 377 # last element is repo in nested groups structure
378 378 groups = groups[:-1]
379 379 rgm = ReposGroupModel(sa)
380 380 for lvl, group_name in enumerate(groups):
381 381 log.debug('creating group level: %s group_name: %s' % (lvl, group_name))
382 382 group_name = '/'.join(groups[:lvl] + [group_name])
383 383 group = RepoGroup.get_by_group_name(group_name)
384 384 desc = '%s group' % group_name
385 385
386 386 # # WTF that doesn't work !?
387 387 # if group is None:
388 388 # group = rgm.create(group_name, desc, parent, just_db=True)
389 389 # sa.commit()
390 390
391 391 if group is None:
392 392 group = RepoGroup(group_name, parent)
393 393 group.group_description = desc
394 394 sa.add(group)
395 395 rgm._create_default_perms(group)
396 396 sa.commit()
397 397 parent = group
398 398 return group
399 399
400 400
401 401 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
402 402 """
403 403 maps all repos given in initial_repo_list, non existing repositories
404 404 are created, if remove_obsolete is True it also check for db entries
405 405 that are not in initial_repo_list and removes them.
406 406
407 407 :param initial_repo_list: list of repositories found by scanning methods
408 408 :param remove_obsolete: check for obsolete entries in database
409 409 """
410 410 from rhodecode.model.repo import RepoModel
411 411 sa = meta.Session
412 412 rm = RepoModel()
413 413 user = sa.query(User).filter(User.admin == True).first()
414 414 if user is None:
415 415 raise Exception('Missing administrative account !')
416 416 added = []
417 417
418 # fixup groups paths to new format on the fly. Helps with migration from
419 # old rhodecode versions also set permissions if they are not present !
420 # TODO: remove this in future, before release
421 def_usr = User.get_by_username('default')
422 for g in RepoGroup.query().all():
423 g.group_name = g.get_new_name(g.name)
424 sa.add(g)
425 # get default perm
426 default = UserRepoGroupToPerm.query()\
427 .filter(UserRepoGroupToPerm.group == g)\
428 .filter(UserRepoGroupToPerm.user == def_usr)\
429 .scalar()
430
431 if default is None:
432 log.debug('missing default permission for group %s adding' % g)
433 ReposGroupModel()._create_default_perms(g)
434
418 435 for name, repo in initial_repo_list.items():
419 436 group = map_groups(name.split(Repository.url_sep()))
420 437 if not rm.get_by_repo_name(name, cache=False):
421 438 log.info('repository %s not found creating default' % name)
422 439 added.append(name)
423 440 form_data = {
424 441 'repo_name': name,
425 442 'repo_name_full': name,
426 443 'repo_type': repo.alias,
427 444 'description': repo.description \
428 445 if repo.description != 'unknown' else '%s repository' % name,
429 446 'private': False,
430 447 'group_id': getattr(group, 'group_id', None)
431 448 }
432 449 rm.create(form_data, user, just_db=True)
433 450 sa.commit()
434 451 removed = []
435 452 if remove_obsolete:
436 453 #remove from database those repositories that are not in the filesystem
437 454 for repo in sa.query(Repository).all():
438 455 if repo.repo_name not in initial_repo_list.keys():
439 456 removed.append(repo.repo_name)
440 457 sa.delete(repo)
441 458 sa.commit()
442 459
443 460 return added, removed
444 461
445 462
446 463 # set cache regions for beaker so celery can utilise it
447 464 def add_cache(settings):
448 465 cache_settings = {'regions': None}
449 466 for key in settings.keys():
450 467 for prefix in ['beaker.cache.', 'cache.']:
451 468 if key.startswith(prefix):
452 469 name = key.split(prefix)[1].strip()
453 470 cache_settings[name] = settings[key].strip()
454 471 if cache_settings['regions']:
455 472 for region in cache_settings['regions'].split(','):
456 473 region = region.strip()
457 474 region_settings = {}
458 475 for key, value in cache_settings.items():
459 476 if key.startswith(region):
460 477 region_settings[key.split('.')[1]] = value
461 478 region_settings['expire'] = int(region_settings.get('expire',
462 479 60))
463 480 region_settings.setdefault('lock_dir',
464 481 cache_settings.get('lock_dir'))
465 482 region_settings.setdefault('data_dir',
466 483 cache_settings.get('data_dir'))
467 484
468 485 if 'type' not in region_settings:
469 486 region_settings['type'] = cache_settings.get('type',
470 487 'memory')
471 488 beaker.cache.cache_regions[region] = region_settings
472 489
473 490
474 491 #==============================================================================
475 492 # TEST FUNCTIONS AND CREATORS
476 493 #==============================================================================
477 494 def create_test_index(repo_location, config, full_index):
478 495 """
479 496 Makes default test index
480 497
481 498 :param config: test config
482 499 :param full_index:
483 500 """
484 501
485 502 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
486 503 from rhodecode.lib.pidlock import DaemonLock, LockHeld
487 504
488 505 repo_location = repo_location
489 506
490 507 index_location = os.path.join(config['app_conf']['index_dir'])
491 508 if not os.path.exists(index_location):
492 509 os.makedirs(index_location)
493 510
494 511 try:
495 512 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
496 513 WhooshIndexingDaemon(index_location=index_location,
497 514 repo_location=repo_location)\
498 515 .run(full_index=full_index)
499 516 l.release()
500 517 except LockHeld:
501 518 pass
502 519
503 520
504 521 def create_test_env(repos_test_path, config):
505 522 """
506 523 Makes a fresh database and
507 524 install test repository into tmp dir
508 525 """
509 526 from rhodecode.lib.db_manage import DbManage
510 527 from rhodecode.tests import HG_REPO, TESTS_TMP_PATH
511 528
512 529 # PART ONE create db
513 530 dbconf = config['sqlalchemy.db1.url']
514 531 log.debug('making test db %s' % dbconf)
515 532
516 533 # create test dir if it doesn't exist
517 534 if not os.path.isdir(repos_test_path):
518 535 log.debug('Creating testdir %s' % repos_test_path)
519 536 os.makedirs(repos_test_path)
520 537
521 538 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
522 539 tests=True)
523 540 dbmanage.create_tables(override=True)
524 541 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
525 542 dbmanage.create_default_user()
526 543 dbmanage.admin_prompt()
527 544 dbmanage.create_permissions()
528 545 dbmanage.populate_default_permissions()
529 546 Session.commit()
530 547 # PART TWO make test repo
531 548 log.debug('making test vcs repositories')
532 549
533 550 idx_path = config['app_conf']['index_dir']
534 551 data_path = config['app_conf']['cache_dir']
535 552
536 553 #clean index and data
537 554 if idx_path and os.path.exists(idx_path):
538 555 log.debug('remove %s' % idx_path)
539 556 shutil.rmtree(idx_path)
540 557
541 558 if data_path and os.path.exists(data_path):
542 559 log.debug('remove %s' % data_path)
543 560 shutil.rmtree(data_path)
544 561
545 562 #CREATE DEFAULT HG REPOSITORY
546 563 cur_dir = dn(dn(abspath(__file__)))
547 564 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
548 565 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
549 566 tar.close()
550 567
551 568
552 569 #==============================================================================
553 570 # PASTER COMMANDS
554 571 #==============================================================================
555 572 class BasePasterCommand(Command):
556 573 """
557 574 Abstract Base Class for paster commands.
558 575
559 576 The celery commands are somewhat aggressive about loading
560 577 celery.conf, and since our module sets the `CELERY_LOADER`
561 578 environment variable to our loader, we have to bootstrap a bit and
562 579 make sure we've had a chance to load the pylons config off of the
563 580 command line, otherwise everything fails.
564 581 """
565 582 min_args = 1
566 583 min_args_error = "Please provide a paster config file as an argument."
567 584 takes_config_file = 1
568 585 requires_config_file = True
569 586
570 587 def notify_msg(self, msg, log=False):
571 588 """Make a notification to user, additionally if logger is passed
572 589 it logs this action using given logger
573 590
574 591 :param msg: message that will be printed to user
575 592 :param log: logging instance, to use to additionally log this message
576 593
577 594 """
578 595 if log and isinstance(log, logging):
579 596 log(msg)
580 597
581 598 def run(self, args):
582 599 """
583 600 Overrides Command.run
584 601
585 602 Checks for a config file argument and loads it.
586 603 """
587 604 if len(args) < self.min_args:
588 605 raise BadCommand(
589 606 self.min_args_error % {'min_args': self.min_args,
590 607 'actual_args': len(args)})
591 608
592 609 # Decrement because we're going to lob off the first argument.
593 610 # @@ This is hacky
594 611 self.min_args -= 1
595 612 self.bootstrap_config(args[0])
596 613 self.update_parser()
597 614 return super(BasePasterCommand, self).run(args[1:])
598 615
599 616 def update_parser(self):
600 617 """
601 618 Abstract method. Allows for the class's parser to be updated
602 619 before the superclass's `run` method is called. Necessary to
603 620 allow options/arguments to be passed through to the underlying
604 621 celery command.
605 622 """
606 623 raise NotImplementedError("Abstract Method.")
607 624
608 625 def bootstrap_config(self, conf):
609 626 """
610 627 Loads the pylons configuration.
611 628 """
612 629 from pylons import config as pylonsconfig
613 630
614 631 path_to_ini_file = os.path.realpath(conf)
615 632 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
616 633 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now