##// END OF EJS Templates
added re-creation of permissions into step4 upgrade
marcink -
r1986:367d76f5 beta
parent child Browse files
Show More
@@ -1,517 +1,519 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 40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
41 41 UserRepoGroupToPerm
42 42
43 43 from sqlalchemy.engine import create_engine
44 44 from rhodecode.model.repos_group import ReposGroupModel
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class DbManage(object):
50 50 def __init__(self, log_sql, dbconf, root, tests=False):
51 51 self.dbname = dbconf.split('/')[-1]
52 52 self.tests = tests
53 53 self.root = root
54 54 self.dburi = dbconf
55 55 self.log_sql = log_sql
56 56 self.db_exists = False
57 57 self.init_db()
58 58
59 59 def init_db(self):
60 60 engine = create_engine(self.dburi, echo=self.log_sql)
61 61 init_model(engine)
62 62 self.sa = meta.Session
63 63
64 64 def create_tables(self, override=False):
65 65 """
66 66 Create a auth database
67 67 """
68 68
69 69 log.info("Any existing database is going to be destroyed")
70 70 if self.tests:
71 71 destroy = True
72 72 else:
73 73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
74 74 if not destroy:
75 75 sys.exit()
76 76 if destroy:
77 77 meta.Base.metadata.drop_all()
78 78
79 79 checkfirst = not override
80 80 meta.Base.metadata.create_all(checkfirst=checkfirst)
81 81 log.info('Created tables for %s' % self.dbname)
82 82
83 83 def set_db_version(self):
84 84 ver = DbMigrateVersion()
85 85 ver.version = __dbversion__
86 86 ver.repository_id = 'rhodecode_db_migrations'
87 87 ver.repository_path = 'versions'
88 88 self.sa.add(ver)
89 89 log.info('db version set to: %s' % __dbversion__)
90 90
91 91 def upgrade(self):
92 92 """
93 93 Upgrades given database schema to given revision following
94 94 all needed steps, to perform the upgrade
95 95
96 96 """
97 97
98 98 from rhodecode.lib.dbmigrate.migrate.versioning import api
99 99 from rhodecode.lib.dbmigrate.migrate.exceptions import \
100 100 DatabaseNotControlledError
101 101
102 102 if 'sqlite' in self.dburi:
103 103 print (
104 104 '********************** WARNING **********************\n'
105 105 'Make sure your version of sqlite is at least 3.7.X. \n'
106 106 'Earlier versions are known to fail on some migrations\n'
107 107 '*****************************************************\n'
108 108 )
109 109 upgrade = ask_ok('You are about to perform database upgrade, make '
110 110 'sure You backed up your database before. '
111 111 'Continue ? [y/n]')
112 112 if not upgrade:
113 113 sys.exit('Nothing done')
114 114
115 115 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
116 116 'rhodecode/lib/dbmigrate')
117 117 db_uri = self.dburi
118 118
119 119 try:
120 120 curr_version = api.db_version(db_uri, repository_path)
121 121 msg = ('Found current database under version'
122 122 ' control with version %s' % curr_version)
123 123
124 124 except (RuntimeError, DatabaseNotControlledError):
125 125 curr_version = 1
126 126 msg = ('Current database is not under version control. Setting'
127 127 ' as version %s' % curr_version)
128 128 api.version_control(db_uri, repository_path, curr_version)
129 129
130 130 print (msg)
131 131
132 132 if curr_version == __dbversion__:
133 133 sys.exit('This database is already at the newest version')
134 134
135 135 #======================================================================
136 136 # UPGRADE STEPS
137 137 #======================================================================
138 138 class UpgradeSteps(object):
139 139 """
140 140 Those steps follow schema versions so for example schema
141 141 for example schema with seq 002 == step_2 and so on.
142 142 """
143 143
144 144 def __init__(self, klass):
145 145 self.klass = klass
146 146
147 147 def step_0(self):
148 148 # step 0 is the schema upgrade, and than follow proper upgrades
149 149 print ('attempting to do database upgrade to version %s' \
150 150 % __dbversion__)
151 151 api.upgrade(db_uri, repository_path, __dbversion__)
152 152 print ('Schema upgrade completed')
153 153
154 154 def step_1(self):
155 155 pass
156 156
157 157 def step_2(self):
158 158 print ('Patching repo paths for newer version of RhodeCode')
159 159 self.klass.fix_repo_paths()
160 160
161 161 print ('Patching default user of RhodeCode')
162 162 self.klass.fix_default_user()
163 163
164 164 log.info('Changing ui settings')
165 165 self.klass.create_ui_settings()
166 166
167 167 def step_3(self):
168 168 print ('Adding additional settings into RhodeCode db')
169 169 self.klass.fix_settings()
170 170 print ('Adding ldap defaults')
171 171 self.klass.create_ldap_options(skip_existing=True)
172 172
173 173 def step_4(self):
174 174 print ('TODO:')
175 self.create_permissions()
175 176 self.klass.fixup_groups()
176 177
177 178 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
178 179
179 180 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
180 181 for step in upgrade_steps:
181 182 print ('performing upgrade step %s' % step)
182 183 getattr(UpgradeSteps(self), 'step_%s' % step)()
183 184
184 185 def fix_repo_paths(self):
185 186 """
186 187 Fixes a old rhodecode version path into new one without a '*'
187 188 """
188 189
189 190 paths = self.sa.query(RhodeCodeUi)\
190 191 .filter(RhodeCodeUi.ui_key == '/')\
191 192 .scalar()
192 193
193 194 paths.ui_value = paths.ui_value.replace('*', '')
194 195
195 196 try:
196 197 self.sa.add(paths)
197 198 self.sa.commit()
198 199 except:
199 200 self.sa.rollback()
200 201 raise
201 202
202 203 def fix_default_user(self):
203 204 """
204 205 Fixes a old default user with some 'nicer' default values,
205 206 used mostly for anonymous access
206 207 """
207 208 def_user = self.sa.query(User)\
208 209 .filter(User.username == 'default')\
209 210 .one()
210 211
211 212 def_user.name = 'Anonymous'
212 213 def_user.lastname = 'User'
213 214 def_user.email = 'anonymous@rhodecode.org'
214 215
215 216 try:
216 217 self.sa.add(def_user)
217 218 self.sa.commit()
218 219 except:
219 220 self.sa.rollback()
220 221 raise
221 222
222 223 def fix_settings(self):
223 224 """
224 225 Fixes rhodecode settings adds ga_code key for google analytics
225 226 """
226 227
227 228 hgsettings3 = RhodeCodeSetting('ga_code', '')
228 229
229 230 try:
230 231 self.sa.add(hgsettings3)
231 232 self.sa.commit()
232 233 except:
233 234 self.sa.rollback()
234 235 raise
235 236
236 237 def admin_prompt(self, second=False):
237 238 if not self.tests:
238 239 import getpass
239 240
240 241 def get_password():
241 242 password = getpass.getpass('Specify admin password '
242 243 '(min 6 chars):')
243 244 confirm = getpass.getpass('Confirm password:')
244 245
245 246 if password != confirm:
246 247 log.error('passwords mismatch')
247 248 return False
248 249 if len(password) < 6:
249 250 log.error('password is to short use at least 6 characters')
250 251 return False
251 252
252 253 return password
253 254
254 255 username = raw_input('Specify admin username:')
255 256
256 257 password = get_password()
257 258 if not password:
258 259 #second try
259 260 password = get_password()
260 261 if not password:
261 262 sys.exit()
262 263
263 264 email = raw_input('Specify admin email:')
264 265 self.create_user(username, password, email, True)
265 266 else:
266 267 log.info('creating admin and regular test users')
267 268 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
268 269 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
269 270 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
270 271 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
271 272 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
272 273
273 274 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
274 275 TEST_USER_ADMIN_EMAIL, True)
275 276
276 277 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
277 278 TEST_USER_REGULAR_EMAIL, False)
278 279
279 280 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
280 281 TEST_USER_REGULAR2_EMAIL, False)
281 282
282 283 def create_ui_settings(self):
283 284 """
284 285 Creates ui settings, fills out hooks
285 286 and disables dotencode
286 287 """
287 288
288 289 #HOOKS
289 290 hooks1_key = RhodeCodeUi.HOOK_UPDATE
290 291 hooks1_ = self.sa.query(RhodeCodeUi)\
291 292 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
292 293
293 294 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
294 295 hooks1.ui_section = 'hooks'
295 296 hooks1.ui_key = hooks1_key
296 297 hooks1.ui_value = 'hg update >&2'
297 298 hooks1.ui_active = False
298 299
299 300 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
300 301 hooks2_ = self.sa.query(RhodeCodeUi)\
301 302 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
302 303
303 304 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
304 305 hooks2.ui_section = 'hooks'
305 306 hooks2.ui_key = hooks2_key
306 307 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
307 308
308 309 hooks3 = RhodeCodeUi()
309 310 hooks3.ui_section = 'hooks'
310 311 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
311 312 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
312 313
313 314 hooks4 = RhodeCodeUi()
314 315 hooks4.ui_section = 'hooks'
315 316 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
316 317 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
317 318
318 319 # For mercurial 1.7 set backward comapatibility with format
319 320 dotencode_disable = RhodeCodeUi()
320 321 dotencode_disable.ui_section = 'format'
321 322 dotencode_disable.ui_key = 'dotencode'
322 323 dotencode_disable.ui_value = 'false'
323 324
324 325 # enable largefiles
325 326 largefiles = RhodeCodeUi()
326 327 largefiles.ui_section = 'extensions'
327 328 largefiles.ui_key = 'largefiles'
328 329 largefiles.ui_value = ''
329 330
330 331 self.sa.add(hooks1)
331 332 self.sa.add(hooks2)
332 333 self.sa.add(hooks3)
333 334 self.sa.add(hooks4)
334 335 self.sa.add(largefiles)
335 336
336 337 def create_ldap_options(self, skip_existing=False):
337 338 """Creates ldap settings"""
338 339
339 340 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
340 341 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
341 342 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
342 343 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
343 344 ('ldap_filter', ''), ('ldap_search_scope', ''),
344 345 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
345 346 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
346 347
347 348 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
348 349 log.debug('Skipping option %s' % k)
349 350 continue
350 351 setting = RhodeCodeSetting(k, v)
351 352 self.sa.add(setting)
352 353
353 354 def fixup_groups(self):
354 355 def_usr = User.get_by_username('default')
355 356 for g in RepoGroup.query().all():
356 357 g.group_name = g.get_new_name(g.name)
357 358 self.sa.add(g)
358 359 # get default perm
359 360 default = UserRepoGroupToPerm.query()\
360 361 .filter(UserRepoGroupToPerm.group == g)\
361 362 .filter(UserRepoGroupToPerm.user == def_usr)\
362 363 .scalar()
363 364
364 365 if default is None:
365 366 log.debug('missing default permission for group %s adding' % g)
366 367 ReposGroupModel()._create_default_perms(g)
367 368
368 369 def config_prompt(self, test_repo_path='', retries=3):
369 370 if retries == 3:
370 371 log.info('Setting up repositories config')
371 372
372 373 if not self.tests and not test_repo_path:
373 374 path = raw_input(
374 375 'Enter a valid path to store repositories. '
375 376 'All repositories in that path will be added automatically:'
376 377 )
377 378 else:
378 379 path = test_repo_path
379 380 path_ok = True
380 381
381 382 # check proper dir
382 383 if not os.path.isdir(path):
383 384 path_ok = False
384 385 log.error('Given path %s is not a valid directory' % path)
385 386
386 387 # check write access
387 388 if not os.access(path, os.W_OK) and path_ok:
388 389 path_ok = False
389 390 log.error('No write permission to given path %s' % path)
390 391
391 392 if retries == 0:
392 393 sys.exit('max retries reached')
393 394 if path_ok is False:
394 395 retries -= 1
395 396 return self.config_prompt(test_repo_path, retries)
396 397
397 398 return path
398 399
399 400 def create_settings(self, path):
400 401
401 402 self.create_ui_settings()
402 403
403 404 #HG UI OPTIONS
404 405 web1 = RhodeCodeUi()
405 406 web1.ui_section = 'web'
406 407 web1.ui_key = 'push_ssl'
407 408 web1.ui_value = 'false'
408 409
409 410 web2 = RhodeCodeUi()
410 411 web2.ui_section = 'web'
411 412 web2.ui_key = 'allow_archive'
412 413 web2.ui_value = 'gz zip bz2'
413 414
414 415 web3 = RhodeCodeUi()
415 416 web3.ui_section = 'web'
416 417 web3.ui_key = 'allow_push'
417 418 web3.ui_value = '*'
418 419
419 420 web4 = RhodeCodeUi()
420 421 web4.ui_section = 'web'
421 422 web4.ui_key = 'baseurl'
422 423 web4.ui_value = '/'
423 424
424 425 paths = RhodeCodeUi()
425 426 paths.ui_section = 'paths'
426 427 paths.ui_key = '/'
427 428 paths.ui_value = path
428 429
429 430 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
430 431 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
431 432 hgsettings3 = RhodeCodeSetting('ga_code', '')
432 433
433 434 self.sa.add(web1)
434 435 self.sa.add(web2)
435 436 self.sa.add(web3)
436 437 self.sa.add(web4)
437 438 self.sa.add(paths)
438 439 self.sa.add(hgsettings1)
439 440 self.sa.add(hgsettings2)
440 441 self.sa.add(hgsettings3)
441 442
442 443 self.create_ldap_options()
443 444
444 445 log.info('created ui config')
445 446
446 447 def create_user(self, username, password, email='', admin=False):
447 448 log.info('creating user %s' % username)
448 449 UserModel().create_or_update(username, password, email,
449 450 name='RhodeCode', lastname='Admin',
450 451 active=True, admin=admin)
451 452
452 453 def create_default_user(self):
453 454 log.info('creating default user')
454 455 # create default user for handling default permissions.
455 456 UserModel().create_or_update(username='default',
456 457 password=str(uuid.uuid1())[:8],
457 458 email='anonymous@rhodecode.org',
458 459 name='Anonymous', lastname='User')
459 460
460 461 def create_permissions(self):
461 462 # module.(access|create|change|delete)_[name]
462 463 # module.(none|read|write|admin)
463 464 perms = [
464 465 ('repository.none', 'Repository no access'),
465 466 ('repository.read', 'Repository read access'),
466 467 ('repository.write', 'Repository write access'),
467 468 ('repository.admin', 'Repository admin access'),
468 469
469 470 ('group.none', 'Repositories Group no access'),
470 471 ('group.read', 'Repositories Group read access'),
471 472 ('group.write', 'Repositories Group write access'),
472 473 ('group.admin', 'Repositories Group admin access'),
473 474
474 475 ('hg.admin', 'Hg Administrator'),
475 476 ('hg.create.repository', 'Repository create'),
476 477 ('hg.create.none', 'Repository creation disabled'),
477 478 ('hg.register.none', 'Register disabled'),
478 479 ('hg.register.manual_activate', 'Register new user with RhodeCode '
479 480 'without manual activation'),
480 481
481 482 ('hg.register.auto_activate', 'Register new user with RhodeCode '
482 483 'without auto activation'),
483 484 ]
484 485
485 486 for p in perms:
486 new_perm = Permission()
487 new_perm.permission_name = p[0]
488 new_perm.permission_longname = p[1]
489 self.sa.add(new_perm)
487 if not Permission.get_by_key(p):
488 new_perm = Permission()
489 new_perm.permission_name = p[0]
490 new_perm.permission_longname = p[1]
491 self.sa.add(new_perm)
490 492
491 493 def populate_default_permissions(self):
492 494 log.info('creating default user permissions')
493 495
494 496 default_user = self.sa.query(User)\
495 497 .filter(User.username == 'default').scalar()
496 498
497 499 reg_perm = UserToPerm()
498 500 reg_perm.user = default_user
499 501 reg_perm.permission = self.sa.query(Permission)\
500 502 .filter(Permission.permission_name == 'hg.register.manual_activate')\
501 503 .scalar()
502 504
503 505 create_repo_perm = UserToPerm()
504 506 create_repo_perm.user = default_user
505 507 create_repo_perm.permission = self.sa.query(Permission)\
506 508 .filter(Permission.permission_name == 'hg.create.repository')\
507 509 .scalar()
508 510
509 511 default_repo_perm = UserToPerm()
510 512 default_repo_perm.user = default_user
511 513 default_repo_perm.permission = self.sa.query(Permission)\
512 514 .filter(Permission.permission_name == 'repository.read')\
513 515 .scalar()
514 516
515 517 self.sa.add(reg_perm)
516 518 self.sa.add(create_repo_perm)
517 519 self.sa.add(default_repo_perm)
General Comments 0
You need to be logged in to leave comments. Login now