##// END OF EJS Templates
prompts: fixed input() calls for python3
super-admin -
r5148:5ce7d4ae default
parent child Browse files
Show More
@@ -1,687 +1,687 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 """
19 """
20 Database creation, and setup module for RhodeCode Enterprise. Used for creation
20 Database creation, and setup module for RhodeCode Enterprise. Used for creation
21 of database as well as for migration operations
21 of database as well as for migration operations
22 """
22 """
23
23
24 import os
24 import os
25 import sys
25 import sys
26 import time
26 import time
27 import uuid
27 import uuid
28 import logging
28 import logging
29 import getpass
29 import getpass
30 from os.path import dirname as dn, join as jn
30 from os.path import dirname as dn, join as jn
31
31
32 from sqlalchemy.engine import create_engine
32 from sqlalchemy.engine import create_engine
33
33
34 from rhodecode import __dbversion__
34 from rhodecode import __dbversion__
35 from rhodecode.model import init_model
35 from rhodecode.model import init_model
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from rhodecode.model.db import (
37 from rhodecode.model.db import (
38 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
38 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
39 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
39 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
40 from rhodecode.model.meta import Session, Base
40 from rhodecode.model.meta import Session, Base
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo_group import RepoGroupModel
43 from rhodecode.model.repo_group import RepoGroupModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45
45
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 def notify(msg):
50 def notify(msg):
51 """
51 """
52 Notification for migrations messages
52 Notification for migrations messages
53 """
53 """
54 ml = len(msg) + (4 * 2)
54 ml = len(msg) + (4 * 2)
55 print((('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()))
55 print((('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()))
56
56
57
57
58 class DbManage(object):
58 class DbManage(object):
59
59
60 def __init__(self, log_sql, dbconf, root, tests=False,
60 def __init__(self, log_sql, dbconf, root, tests=False,
61 SESSION=None, cli_args=None, enc_key=b''):
61 SESSION=None, cli_args=None, enc_key=b''):
62
62
63 self.dbname = dbconf.split('/')[-1]
63 self.dbname = dbconf.split('/')[-1]
64 self.tests = tests
64 self.tests = tests
65 self.root = root
65 self.root = root
66 self.dburi = dbconf
66 self.dburi = dbconf
67 self.log_sql = log_sql
67 self.log_sql = log_sql
68 self.cli_args = cli_args or {}
68 self.cli_args = cli_args or {}
69 self.sa = None
69 self.sa = None
70 self.engine = None
70 self.engine = None
71 self.enc_key = enc_key
71 self.enc_key = enc_key
72 # sets .sa .engine
72 # sets .sa .engine
73 self.init_db(SESSION=SESSION)
73 self.init_db(SESSION=SESSION)
74
74
75 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
75 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
76
76
77 def db_exists(self):
77 def db_exists(self):
78 if not self.sa:
78 if not self.sa:
79 self.init_db()
79 self.init_db()
80 try:
80 try:
81 self.sa.query(RhodeCodeUi)\
81 self.sa.query(RhodeCodeUi)\
82 .filter(RhodeCodeUi.ui_key == '/')\
82 .filter(RhodeCodeUi.ui_key == '/')\
83 .scalar()
83 .scalar()
84 return True
84 return True
85 except Exception:
85 except Exception:
86 return False
86 return False
87 finally:
87 finally:
88 self.sa.rollback()
88 self.sa.rollback()
89
89
90 def get_ask_ok_func(self, param):
90 def get_ask_ok_func(self, param):
91 if param not in [None]:
91 if param not in [None]:
92 # return a function lambda that has a default set to param
92 # return a function lambda that has a default set to param
93 return lambda *args, **kwargs: param
93 return lambda *args, **kwargs: param
94 else:
94 else:
95 from rhodecode.lib.utils import ask_ok
95 from rhodecode.lib.utils import ask_ok
96 return ask_ok
96 return ask_ok
97
97
98 def init_db(self, SESSION=None):
98 def init_db(self, SESSION=None):
99
99
100 if SESSION:
100 if SESSION:
101 self.sa = SESSION
101 self.sa = SESSION
102 self.engine = SESSION.bind
102 self.engine = SESSION.bind
103 else:
103 else:
104 # init new sessions
104 # init new sessions
105 engine = create_engine(self.dburi, echo=self.log_sql)
105 engine = create_engine(self.dburi, echo=self.log_sql)
106 init_model(engine, encryption_key=self.enc_key)
106 init_model(engine, encryption_key=self.enc_key)
107 self.sa = Session()
107 self.sa = Session()
108 self.engine = engine
108 self.engine = engine
109
109
110 def create_tables(self, override=False):
110 def create_tables(self, override=False):
111 """
111 """
112 Create a auth database
112 Create a auth database
113 """
113 """
114
114
115 log.info("Existing database with the same name is going to be destroyed.")
115 log.info("Existing database with the same name is going to be destroyed.")
116 log.info("Setup command will run DROP ALL command on that database.")
116 log.info("Setup command will run DROP ALL command on that database.")
117 engine = self.engine
117 engine = self.engine
118
118
119 if self.tests:
119 if self.tests:
120 destroy = True
120 destroy = True
121 else:
121 else:
122 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
122 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
123 if not destroy:
123 if not destroy:
124 log.info('db tables bootstrap: Nothing done.')
124 log.info('db tables bootstrap: Nothing done.')
125 sys.exit(0)
125 sys.exit(0)
126 if destroy:
126 if destroy:
127 Base.metadata.drop_all(bind=engine)
127 Base.metadata.drop_all(bind=engine)
128
128
129 checkfirst = not override
129 checkfirst = not override
130 Base.metadata.create_all(bind=engine, checkfirst=checkfirst)
130 Base.metadata.create_all(bind=engine, checkfirst=checkfirst)
131 log.info('Created tables for %s', self.dbname)
131 log.info('Created tables for %s', self.dbname)
132
132
133 def set_db_version(self):
133 def set_db_version(self):
134 ver = DbMigrateVersion()
134 ver = DbMigrateVersion()
135 ver.version = __dbversion__
135 ver.version = __dbversion__
136 ver.repository_id = 'rhodecode_db_migrations'
136 ver.repository_id = 'rhodecode_db_migrations'
137 ver.repository_path = 'versions'
137 ver.repository_path = 'versions'
138 self.sa.add(ver)
138 self.sa.add(ver)
139 log.info('db version set to: %s', __dbversion__)
139 log.info('db version set to: %s', __dbversion__)
140
140
141 def run_post_migration_tasks(self):
141 def run_post_migration_tasks(self):
142 """
142 """
143 Run various tasks before actually doing migrations
143 Run various tasks before actually doing migrations
144 """
144 """
145 # delete cache keys on each upgrade
145 # delete cache keys on each upgrade
146 total = CacheKey.query().count()
146 total = CacheKey.query().count()
147 log.info("Deleting (%s) cache keys now...", total)
147 log.info("Deleting (%s) cache keys now...", total)
148 CacheKey.delete_all_cache()
148 CacheKey.delete_all_cache()
149
149
150 def upgrade(self, version=None):
150 def upgrade(self, version=None):
151 """
151 """
152 Upgrades given database schema to given revision following
152 Upgrades given database schema to given revision following
153 all needed steps, to perform the upgrade
153 all needed steps, to perform the upgrade
154
154
155 """
155 """
156
156
157 from rhodecode.lib.dbmigrate.migrate.versioning import api
157 from rhodecode.lib.dbmigrate.migrate.versioning import api
158 from rhodecode.lib.dbmigrate.migrate.exceptions import DatabaseNotControlledError
158 from rhodecode.lib.dbmigrate.migrate.exceptions import DatabaseNotControlledError
159
159
160 if 'sqlite' in self.dburi:
160 if 'sqlite' in self.dburi:
161 print(
161 print(
162 '********************** WARNING **********************\n'
162 '********************** WARNING **********************\n'
163 'Make sure your version of sqlite is at least 3.7.X. \n'
163 'Make sure your version of sqlite is at least 3.7.X. \n'
164 'Earlier versions are known to fail on some migrations\n'
164 'Earlier versions are known to fail on some migrations\n'
165 '*****************************************************\n')
165 '*****************************************************\n')
166
166
167 upgrade = self.ask_ok(
167 upgrade = self.ask_ok(
168 'You are about to perform a database upgrade. Make '
168 'You are about to perform a database upgrade. Make '
169 'sure you have backed up your database. '
169 'sure you have backed up your database. '
170 'Continue ? [y/n]')
170 'Continue ? [y/n]')
171 if not upgrade:
171 if not upgrade:
172 log.info('No upgrade performed')
172 log.info('No upgrade performed')
173 sys.exit(0)
173 sys.exit(0)
174
174
175 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
175 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
176 'rhodecode/lib/dbmigrate')
176 'rhodecode/lib/dbmigrate')
177 db_uri = self.dburi
177 db_uri = self.dburi
178
178
179 if version:
179 if version:
180 DbMigrateVersion.set_version(version)
180 DbMigrateVersion.set_version(version)
181
181
182 try:
182 try:
183 curr_version = api.db_version(db_uri, repository_path)
183 curr_version = api.db_version(db_uri, repository_path)
184 msg = (f'Found current database db_uri under version '
184 msg = (f'Found current database db_uri under version '
185 f'control with version {curr_version}')
185 f'control with version {curr_version}')
186
186
187 except (RuntimeError, DatabaseNotControlledError):
187 except (RuntimeError, DatabaseNotControlledError):
188 curr_version = 1
188 curr_version = 1
189 msg = f'Current database is not under version control. ' \
189 msg = f'Current database is not under version control. ' \
190 f'Setting as version {curr_version}'
190 f'Setting as version {curr_version}'
191 api.version_control(db_uri, repository_path, curr_version)
191 api.version_control(db_uri, repository_path, curr_version)
192
192
193 notify(msg)
193 notify(msg)
194
194
195 if curr_version == __dbversion__:
195 if curr_version == __dbversion__:
196 log.info('This database is already at the newest version')
196 log.info('This database is already at the newest version')
197 sys.exit(0)
197 sys.exit(0)
198
198
199 upgrade_steps = list(range(curr_version + 1, __dbversion__ + 1))
199 upgrade_steps = list(range(curr_version + 1, __dbversion__ + 1))
200 notify(f'attempting to upgrade database from '
200 notify(f'attempting to upgrade database from '
201 f'version {curr_version} to version {__dbversion__}')
201 f'version {curr_version} to version {__dbversion__}')
202
202
203 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
203 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
204 _step = None
204 _step = None
205 for step in upgrade_steps:
205 for step in upgrade_steps:
206 notify(f'performing upgrade step {step}')
206 notify(f'performing upgrade step {step}')
207 time.sleep(0.5)
207 time.sleep(0.5)
208
208
209 api.upgrade(db_uri, repository_path, step)
209 api.upgrade(db_uri, repository_path, step)
210 self.sa.rollback()
210 self.sa.rollback()
211 notify(f'schema upgrade for step {step} completed')
211 notify(f'schema upgrade for step {step} completed')
212
212
213 _step = step
213 _step = step
214
214
215 self.run_post_migration_tasks()
215 self.run_post_migration_tasks()
216 notify(f'upgrade to version {step} successful')
216 notify(f'upgrade to version {step} successful')
217
217
218 def fix_repo_paths(self):
218 def fix_repo_paths(self):
219 """
219 """
220 Fixes an old RhodeCode version path into new one without a '*'
220 Fixes an old RhodeCode version path into new one without a '*'
221 """
221 """
222
222
223 paths = self.sa.query(RhodeCodeUi)\
223 paths = self.sa.query(RhodeCodeUi)\
224 .filter(RhodeCodeUi.ui_key == '/')\
224 .filter(RhodeCodeUi.ui_key == '/')\
225 .scalar()
225 .scalar()
226
226
227 paths.ui_value = paths.ui_value.replace('*', '')
227 paths.ui_value = paths.ui_value.replace('*', '')
228
228
229 try:
229 try:
230 self.sa.add(paths)
230 self.sa.add(paths)
231 self.sa.commit()
231 self.sa.commit()
232 except Exception:
232 except Exception:
233 self.sa.rollback()
233 self.sa.rollback()
234 raise
234 raise
235
235
236 def fix_default_user(self):
236 def fix_default_user(self):
237 """
237 """
238 Fixes an old default user with some 'nicer' default values,
238 Fixes an old default user with some 'nicer' default values,
239 used mostly for anonymous access
239 used mostly for anonymous access
240 """
240 """
241 def_user = self.sa.query(User)\
241 def_user = self.sa.query(User)\
242 .filter(User.username == User.DEFAULT_USER)\
242 .filter(User.username == User.DEFAULT_USER)\
243 .one()
243 .one()
244
244
245 def_user.name = 'Anonymous'
245 def_user.name = 'Anonymous'
246 def_user.lastname = 'User'
246 def_user.lastname = 'User'
247 def_user.email = User.DEFAULT_USER_EMAIL
247 def_user.email = User.DEFAULT_USER_EMAIL
248
248
249 try:
249 try:
250 self.sa.add(def_user)
250 self.sa.add(def_user)
251 self.sa.commit()
251 self.sa.commit()
252 except Exception:
252 except Exception:
253 self.sa.rollback()
253 self.sa.rollback()
254 raise
254 raise
255
255
256 def fix_settings(self):
256 def fix_settings(self):
257 """
257 """
258 Fixes rhodecode settings and adds ga_code key for google analytics
258 Fixes rhodecode settings and adds ga_code key for google analytics
259 """
259 """
260
260
261 hgsettings3 = RhodeCodeSetting('ga_code', '')
261 hgsettings3 = RhodeCodeSetting('ga_code', '')
262
262
263 try:
263 try:
264 self.sa.add(hgsettings3)
264 self.sa.add(hgsettings3)
265 self.sa.commit()
265 self.sa.commit()
266 except Exception:
266 except Exception:
267 self.sa.rollback()
267 self.sa.rollback()
268 raise
268 raise
269
269
270 def create_admin_and_prompt(self):
270 def create_admin_and_prompt(self):
271
271
272 # defaults
272 # defaults
273 defaults = self.cli_args
273 defaults = self.cli_args
274 username = defaults.get('username')
274 username = defaults.get('username')
275 password = defaults.get('password')
275 password = defaults.get('password')
276 email = defaults.get('email')
276 email = defaults.get('email')
277
277
278 if username is None:
278 if username is None:
279 username = eval(input('Specify admin username:'))
279 username = input('Specify admin username:')
280 if password is None:
280 if password is None:
281 password = self._get_admin_password()
281 password = self._get_admin_password()
282 if not password:
282 if not password:
283 # second try
283 # second try
284 password = self._get_admin_password()
284 password = self._get_admin_password()
285 if not password:
285 if not password:
286 sys.exit()
286 sys.exit()
287 if email is None:
287 if email is None:
288 email = eval(input('Specify admin email:'))
288 email = input('Specify admin email:')
289 api_key = self.cli_args.get('api_key')
289 api_key = self.cli_args.get('api_key')
290 self.create_user(username, password, email, True,
290 self.create_user(username, password, email, True,
291 strict_creation_check=False,
291 strict_creation_check=False,
292 api_key=api_key)
292 api_key=api_key)
293
293
294 def _get_admin_password(self):
294 def _get_admin_password(self):
295 password = getpass.getpass('Specify admin password '
295 password = getpass.getpass('Specify admin password '
296 '(min 6 chars):')
296 '(min 6 chars):')
297 confirm = getpass.getpass('Confirm password:')
297 confirm = getpass.getpass('Confirm password:')
298
298
299 if password != confirm:
299 if password != confirm:
300 log.error('passwords mismatch')
300 log.error('passwords mismatch')
301 return False
301 return False
302 if len(password) < 6:
302 if len(password) < 6:
303 log.error('password is too short - use at least 6 characters')
303 log.error('password is too short - use at least 6 characters')
304 return False
304 return False
305
305
306 return password
306 return password
307
307
308 def create_test_admin_and_users(self):
308 def create_test_admin_and_users(self):
309 log.info('creating admin and regular test users')
309 log.info('creating admin and regular test users')
310 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
310 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
311 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
311 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
312 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
312 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
313 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
313 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
314 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
314 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
315
315
316 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
316 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
317 TEST_USER_ADMIN_EMAIL, True, api_key=True)
317 TEST_USER_ADMIN_EMAIL, True, api_key=True)
318
318
319 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
319 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
320 TEST_USER_REGULAR_EMAIL, False, api_key=True)
320 TEST_USER_REGULAR_EMAIL, False, api_key=True)
321
321
322 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
322 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
323 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
323 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
324
324
325 def create_ui_settings(self, repo_store_path):
325 def create_ui_settings(self, repo_store_path):
326 """
326 """
327 Creates ui settings, fills out hooks
327 Creates ui settings, fills out hooks
328 and disables dotencode
328 and disables dotencode
329 """
329 """
330 settings_model = SettingsModel(sa=self.sa)
330 settings_model = SettingsModel(sa=self.sa)
331 from rhodecode.lib.vcs.backends.hg import largefiles_store
331 from rhodecode.lib.vcs.backends.hg import largefiles_store
332 from rhodecode.lib.vcs.backends.git import lfs_store
332 from rhodecode.lib.vcs.backends.git import lfs_store
333
333
334 # Build HOOKS
334 # Build HOOKS
335 hooks = [
335 hooks = [
336 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
336 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
337
337
338 # HG
338 # HG
339 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
339 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
340 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
340 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
341 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
341 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
342 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
342 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
343 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
343 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
344 (RhodeCodeUi.HOOK_PUSH_KEY, 'python:vcsserver.hooks.key_push'),
344 (RhodeCodeUi.HOOK_PUSH_KEY, 'python:vcsserver.hooks.key_push'),
345
345
346 ]
346 ]
347
347
348 for key, value in hooks:
348 for key, value in hooks:
349 hook_obj = settings_model.get_ui_by_key(key)
349 hook_obj = settings_model.get_ui_by_key(key)
350 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
350 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
351 hooks2.ui_section = 'hooks'
351 hooks2.ui_section = 'hooks'
352 hooks2.ui_key = key
352 hooks2.ui_key = key
353 hooks2.ui_value = value
353 hooks2.ui_value = value
354 self.sa.add(hooks2)
354 self.sa.add(hooks2)
355
355
356 # enable largefiles
356 # enable largefiles
357 largefiles = RhodeCodeUi()
357 largefiles = RhodeCodeUi()
358 largefiles.ui_section = 'extensions'
358 largefiles.ui_section = 'extensions'
359 largefiles.ui_key = 'largefiles'
359 largefiles.ui_key = 'largefiles'
360 largefiles.ui_value = ''
360 largefiles.ui_value = ''
361 self.sa.add(largefiles)
361 self.sa.add(largefiles)
362
362
363 # set default largefiles cache dir, defaults to
363 # set default largefiles cache dir, defaults to
364 # /repo_store_location/.cache/largefiles
364 # /repo_store_location/.cache/largefiles
365 largefiles = RhodeCodeUi()
365 largefiles = RhodeCodeUi()
366 largefiles.ui_section = 'largefiles'
366 largefiles.ui_section = 'largefiles'
367 largefiles.ui_key = 'usercache'
367 largefiles.ui_key = 'usercache'
368 largefiles.ui_value = largefiles_store(repo_store_path)
368 largefiles.ui_value = largefiles_store(repo_store_path)
369
369
370 self.sa.add(largefiles)
370 self.sa.add(largefiles)
371
371
372 # set default lfs cache dir, defaults to
372 # set default lfs cache dir, defaults to
373 # /repo_store_location/.cache/lfs_store
373 # /repo_store_location/.cache/lfs_store
374 lfsstore = RhodeCodeUi()
374 lfsstore = RhodeCodeUi()
375 lfsstore.ui_section = 'vcs_git_lfs'
375 lfsstore.ui_section = 'vcs_git_lfs'
376 lfsstore.ui_key = 'store_location'
376 lfsstore.ui_key = 'store_location'
377 lfsstore.ui_value = lfs_store(repo_store_path)
377 lfsstore.ui_value = lfs_store(repo_store_path)
378
378
379 self.sa.add(lfsstore)
379 self.sa.add(lfsstore)
380
380
381 # enable hgsubversion disabled by default
381 # enable hgsubversion disabled by default
382 hgsubversion = RhodeCodeUi()
382 hgsubversion = RhodeCodeUi()
383 hgsubversion.ui_section = 'extensions'
383 hgsubversion.ui_section = 'extensions'
384 hgsubversion.ui_key = 'hgsubversion'
384 hgsubversion.ui_key = 'hgsubversion'
385 hgsubversion.ui_value = ''
385 hgsubversion.ui_value = ''
386 hgsubversion.ui_active = False
386 hgsubversion.ui_active = False
387 self.sa.add(hgsubversion)
387 self.sa.add(hgsubversion)
388
388
389 # enable hgevolve disabled by default
389 # enable hgevolve disabled by default
390 hgevolve = RhodeCodeUi()
390 hgevolve = RhodeCodeUi()
391 hgevolve.ui_section = 'extensions'
391 hgevolve.ui_section = 'extensions'
392 hgevolve.ui_key = 'evolve'
392 hgevolve.ui_key = 'evolve'
393 hgevolve.ui_value = ''
393 hgevolve.ui_value = ''
394 hgevolve.ui_active = False
394 hgevolve.ui_active = False
395 self.sa.add(hgevolve)
395 self.sa.add(hgevolve)
396
396
397 hgevolve = RhodeCodeUi()
397 hgevolve = RhodeCodeUi()
398 hgevolve.ui_section = 'experimental'
398 hgevolve.ui_section = 'experimental'
399 hgevolve.ui_key = 'evolution'
399 hgevolve.ui_key = 'evolution'
400 hgevolve.ui_value = ''
400 hgevolve.ui_value = ''
401 hgevolve.ui_active = False
401 hgevolve.ui_active = False
402 self.sa.add(hgevolve)
402 self.sa.add(hgevolve)
403
403
404 hgevolve = RhodeCodeUi()
404 hgevolve = RhodeCodeUi()
405 hgevolve.ui_section = 'experimental'
405 hgevolve.ui_section = 'experimental'
406 hgevolve.ui_key = 'evolution.exchange'
406 hgevolve.ui_key = 'evolution.exchange'
407 hgevolve.ui_value = ''
407 hgevolve.ui_value = ''
408 hgevolve.ui_active = False
408 hgevolve.ui_active = False
409 self.sa.add(hgevolve)
409 self.sa.add(hgevolve)
410
410
411 hgevolve = RhodeCodeUi()
411 hgevolve = RhodeCodeUi()
412 hgevolve.ui_section = 'extensions'
412 hgevolve.ui_section = 'extensions'
413 hgevolve.ui_key = 'topic'
413 hgevolve.ui_key = 'topic'
414 hgevolve.ui_value = ''
414 hgevolve.ui_value = ''
415 hgevolve.ui_active = False
415 hgevolve.ui_active = False
416 self.sa.add(hgevolve)
416 self.sa.add(hgevolve)
417
417
418 # enable hggit disabled by default
418 # enable hggit disabled by default
419 hggit = RhodeCodeUi()
419 hggit = RhodeCodeUi()
420 hggit.ui_section = 'extensions'
420 hggit.ui_section = 'extensions'
421 hggit.ui_key = 'hggit'
421 hggit.ui_key = 'hggit'
422 hggit.ui_value = ''
422 hggit.ui_value = ''
423 hggit.ui_active = False
423 hggit.ui_active = False
424 self.sa.add(hggit)
424 self.sa.add(hggit)
425
425
426 # set svn branch defaults
426 # set svn branch defaults
427 branches = ["/branches/*", "/trunk"]
427 branches = ["/branches/*", "/trunk"]
428 tags = ["/tags/*"]
428 tags = ["/tags/*"]
429
429
430 for branch in branches:
430 for branch in branches:
431 settings_model.create_ui_section_value(
431 settings_model.create_ui_section_value(
432 RhodeCodeUi.SVN_BRANCH_ID, branch)
432 RhodeCodeUi.SVN_BRANCH_ID, branch)
433
433
434 for tag in tags:
434 for tag in tags:
435 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
435 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
436
436
437 def create_auth_plugin_options(self, skip_existing=False):
437 def create_auth_plugin_options(self, skip_existing=False):
438 """
438 """
439 Create default auth plugin settings, and make it active
439 Create default auth plugin settings, and make it active
440
440
441 :param skip_existing:
441 :param skip_existing:
442 """
442 """
443 defaults = [
443 defaults = [
444 ('auth_plugins',
444 ('auth_plugins',
445 'egg:rhodecode-enterprise-ce#token,egg:rhodecode-enterprise-ce#rhodecode',
445 'egg:rhodecode-enterprise-ce#token,egg:rhodecode-enterprise-ce#rhodecode',
446 'list'),
446 'list'),
447
447
448 ('auth_authtoken_enabled',
448 ('auth_authtoken_enabled',
449 'True',
449 'True',
450 'bool'),
450 'bool'),
451
451
452 ('auth_rhodecode_enabled',
452 ('auth_rhodecode_enabled',
453 'True',
453 'True',
454 'bool'),
454 'bool'),
455 ]
455 ]
456 for k, v, t in defaults:
456 for k, v, t in defaults:
457 if (skip_existing and
457 if (skip_existing and
458 SettingsModel().get_setting_by_name(k) is not None):
458 SettingsModel().get_setting_by_name(k) is not None):
459 log.debug('Skipping option %s', k)
459 log.debug('Skipping option %s', k)
460 continue
460 continue
461 setting = RhodeCodeSetting(k, v, t)
461 setting = RhodeCodeSetting(k, v, t)
462 self.sa.add(setting)
462 self.sa.add(setting)
463
463
464 def create_default_options(self, skip_existing=False):
464 def create_default_options(self, skip_existing=False):
465 """Creates default settings"""
465 """Creates default settings"""
466
466
467 for k, v, t in [
467 for k, v, t in [
468 ('default_repo_enable_locking', False, 'bool'),
468 ('default_repo_enable_locking', False, 'bool'),
469 ('default_repo_enable_downloads', False, 'bool'),
469 ('default_repo_enable_downloads', False, 'bool'),
470 ('default_repo_enable_statistics', False, 'bool'),
470 ('default_repo_enable_statistics', False, 'bool'),
471 ('default_repo_private', False, 'bool'),
471 ('default_repo_private', False, 'bool'),
472 ('default_repo_type', 'hg', 'unicode')]:
472 ('default_repo_type', 'hg', 'unicode')]:
473
473
474 if (skip_existing and
474 if (skip_existing and
475 SettingsModel().get_setting_by_name(k) is not None):
475 SettingsModel().get_setting_by_name(k) is not None):
476 log.debug('Skipping option %s', k)
476 log.debug('Skipping option %s', k)
477 continue
477 continue
478 setting = RhodeCodeSetting(k, v, t)
478 setting = RhodeCodeSetting(k, v, t)
479 self.sa.add(setting)
479 self.sa.add(setting)
480
480
481 def fixup_groups(self):
481 def fixup_groups(self):
482 def_usr = User.get_default_user()
482 def_usr = User.get_default_user()
483 for g in RepoGroup.query().all():
483 for g in RepoGroup.query().all():
484 g.group_name = g.get_new_name(g.name)
484 g.group_name = g.get_new_name(g.name)
485 self.sa.add(g)
485 self.sa.add(g)
486 # get default perm
486 # get default perm
487 default = UserRepoGroupToPerm.query()\
487 default = UserRepoGroupToPerm.query()\
488 .filter(UserRepoGroupToPerm.group == g)\
488 .filter(UserRepoGroupToPerm.group == g)\
489 .filter(UserRepoGroupToPerm.user == def_usr)\
489 .filter(UserRepoGroupToPerm.user == def_usr)\
490 .scalar()
490 .scalar()
491
491
492 if default is None:
492 if default is None:
493 log.debug('missing default permission for group %s adding', g)
493 log.debug('missing default permission for group %s adding', g)
494 perm_obj = RepoGroupModel()._create_default_perms(g)
494 perm_obj = RepoGroupModel()._create_default_perms(g)
495 self.sa.add(perm_obj)
495 self.sa.add(perm_obj)
496
496
497 def reset_permissions(self, username):
497 def reset_permissions(self, username):
498 """
498 """
499 Resets permissions to default state, useful when old systems had
499 Resets permissions to default state, useful when old systems had
500 bad permissions, we must clean them up
500 bad permissions, we must clean them up
501
501
502 :param username:
502 :param username:
503 """
503 """
504 default_user = User.get_by_username(username)
504 default_user = User.get_by_username(username)
505 if not default_user:
505 if not default_user:
506 return
506 return
507
507
508 u2p = UserToPerm.query()\
508 u2p = UserToPerm.query()\
509 .filter(UserToPerm.user == default_user).all()
509 .filter(UserToPerm.user == default_user).all()
510 fixed = False
510 fixed = False
511 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
511 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
512 for p in u2p:
512 for p in u2p:
513 Session().delete(p)
513 Session().delete(p)
514 fixed = True
514 fixed = True
515 self.populate_default_permissions()
515 self.populate_default_permissions()
516 return fixed
516 return fixed
517
517
518 def config_prompt(self, test_repo_path='', retries=3):
518 def config_prompt(self, test_repo_path='', retries=3):
519 defaults = self.cli_args
519 defaults = self.cli_args
520 _path = defaults.get('repos_location')
520 _path = defaults.get('repos_location')
521 if retries == 3:
521 if retries == 3:
522 log.info('Setting up repositories config')
522 log.info('Setting up repositories config')
523
523
524 if _path is not None:
524 if _path is not None:
525 path = _path
525 path = _path
526 elif not self.tests and not test_repo_path:
526 elif not self.tests and not test_repo_path:
527 path = eval(input(
527 path = input(
528 'Enter a valid absolute path to store repositories. '
528 'Enter a valid absolute path to store repositories. '
529 'All repositories in that path will be added automatically:'
529 'All repositories in that path will be added automatically:'
530 ))
530 )
531 else:
531 else:
532 path = test_repo_path
532 path = test_repo_path
533 path_ok = True
533 path_ok = True
534
534
535 # check proper dir
535 # check proper dir
536 if not os.path.isdir(path):
536 if not os.path.isdir(path):
537 path_ok = False
537 path_ok = False
538 log.error('Given path %s is not a valid directory', path)
538 log.error('Given path %s is not a valid directory', path)
539
539
540 elif not os.path.isabs(path):
540 elif not os.path.isabs(path):
541 path_ok = False
541 path_ok = False
542 log.error('Given path %s is not an absolute path', path)
542 log.error('Given path %s is not an absolute path', path)
543
543
544 # check if path is at least readable.
544 # check if path is at least readable.
545 if not os.access(path, os.R_OK):
545 if not os.access(path, os.R_OK):
546 path_ok = False
546 path_ok = False
547 log.error('Given path %s is not readable', path)
547 log.error('Given path %s is not readable', path)
548
548
549 # check write access, warn user about non writeable paths
549 # check write access, warn user about non writeable paths
550 elif not os.access(path, os.W_OK) and path_ok:
550 elif not os.access(path, os.W_OK) and path_ok:
551 log.warning('No write permission to given path %s', path)
551 log.warning('No write permission to given path %s', path)
552
552
553 q = (f'Given path {path} is not writeable, do you want to '
553 q = (f'Given path {path} is not writeable, do you want to '
554 f'continue with read only mode ? [y/n]')
554 f'continue with read only mode ? [y/n]')
555 if not self.ask_ok(q):
555 if not self.ask_ok(q):
556 log.error('Canceled by user')
556 log.error('Canceled by user')
557 sys.exit(-1)
557 sys.exit(-1)
558
558
559 if retries == 0:
559 if retries == 0:
560 sys.exit('max retries reached')
560 sys.exit('max retries reached')
561 if not path_ok:
561 if not path_ok:
562 retries -= 1
562 retries -= 1
563 return self.config_prompt(test_repo_path, retries)
563 return self.config_prompt(test_repo_path, retries)
564
564
565 real_path = os.path.normpath(os.path.realpath(path))
565 real_path = os.path.normpath(os.path.realpath(path))
566
566
567 if real_path != os.path.normpath(path):
567 if real_path != os.path.normpath(path):
568 q = (f'Path looks like a symlink, RhodeCode Enterprise will store '
568 q = (f'Path looks like a symlink, RhodeCode Enterprise will store '
569 f'given path as {real_path} ? [y/n]')
569 f'given path as {real_path} ? [y/n]')
570 if not self.ask_ok(q):
570 if not self.ask_ok(q):
571 log.error('Canceled by user')
571 log.error('Canceled by user')
572 sys.exit(-1)
572 sys.exit(-1)
573
573
574 return real_path
574 return real_path
575
575
576 def create_settings(self, path):
576 def create_settings(self, path):
577
577
578 self.create_ui_settings(path)
578 self.create_ui_settings(path)
579
579
580 ui_config = [
580 ui_config = [
581 ('web', 'push_ssl', 'False'),
581 ('web', 'push_ssl', 'False'),
582 ('web', 'allow_archive', 'gz zip bz2'),
582 ('web', 'allow_archive', 'gz zip bz2'),
583 ('web', 'allow_push', '*'),
583 ('web', 'allow_push', '*'),
584 ('web', 'baseurl', '/'),
584 ('web', 'baseurl', '/'),
585 ('paths', '/', path),
585 ('paths', '/', path),
586 ('phases', 'publish', 'True')
586 ('phases', 'publish', 'True')
587 ]
587 ]
588 for section, key, value in ui_config:
588 for section, key, value in ui_config:
589 ui_conf = RhodeCodeUi()
589 ui_conf = RhodeCodeUi()
590 setattr(ui_conf, 'ui_section', section)
590 setattr(ui_conf, 'ui_section', section)
591 setattr(ui_conf, 'ui_key', key)
591 setattr(ui_conf, 'ui_key', key)
592 setattr(ui_conf, 'ui_value', value)
592 setattr(ui_conf, 'ui_value', value)
593 self.sa.add(ui_conf)
593 self.sa.add(ui_conf)
594
594
595 # rhodecode app settings
595 # rhodecode app settings
596 settings = [
596 settings = [
597 ('realm', 'RhodeCode', 'unicode'),
597 ('realm', 'RhodeCode', 'unicode'),
598 ('title', '', 'unicode'),
598 ('title', '', 'unicode'),
599 ('pre_code', '', 'unicode'),
599 ('pre_code', '', 'unicode'),
600 ('post_code', '', 'unicode'),
600 ('post_code', '', 'unicode'),
601
601
602 # Visual
602 # Visual
603 ('show_public_icon', True, 'bool'),
603 ('show_public_icon', True, 'bool'),
604 ('show_private_icon', True, 'bool'),
604 ('show_private_icon', True, 'bool'),
605 ('stylify_metatags', True, 'bool'),
605 ('stylify_metatags', True, 'bool'),
606 ('dashboard_items', 100, 'int'),
606 ('dashboard_items', 100, 'int'),
607 ('admin_grid_items', 25, 'int'),
607 ('admin_grid_items', 25, 'int'),
608
608
609 ('markup_renderer', 'markdown', 'unicode'),
609 ('markup_renderer', 'markdown', 'unicode'),
610
610
611 ('repository_fields', True, 'bool'),
611 ('repository_fields', True, 'bool'),
612 ('show_version', True, 'bool'),
612 ('show_version', True, 'bool'),
613 ('show_revision_number', True, 'bool'),
613 ('show_revision_number', True, 'bool'),
614 ('show_sha_length', 12, 'int'),
614 ('show_sha_length', 12, 'int'),
615
615
616 ('use_gravatar', False, 'bool'),
616 ('use_gravatar', False, 'bool'),
617 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
617 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
618
618
619 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
619 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
620 ('clone_uri_id_tmpl', Repository.DEFAULT_CLONE_URI_ID, 'unicode'),
620 ('clone_uri_id_tmpl', Repository.DEFAULT_CLONE_URI_ID, 'unicode'),
621 ('clone_uri_ssh_tmpl', Repository.DEFAULT_CLONE_URI_SSH, 'unicode'),
621 ('clone_uri_ssh_tmpl', Repository.DEFAULT_CLONE_URI_SSH, 'unicode'),
622 ('support_url', '', 'unicode'),
622 ('support_url', '', 'unicode'),
623 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
623 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
624
624
625 # VCS Settings
625 # VCS Settings
626 ('pr_merge_enabled', True, 'bool'),
626 ('pr_merge_enabled', True, 'bool'),
627 ('use_outdated_comments', True, 'bool'),
627 ('use_outdated_comments', True, 'bool'),
628 ('diff_cache', True, 'bool'),
628 ('diff_cache', True, 'bool'),
629 ]
629 ]
630
630
631 for key, val, type_ in settings:
631 for key, val, type_ in settings:
632 sett = RhodeCodeSetting(key, val, type_)
632 sett = RhodeCodeSetting(key, val, type_)
633 self.sa.add(sett)
633 self.sa.add(sett)
634
634
635 self.create_auth_plugin_options()
635 self.create_auth_plugin_options()
636 self.create_default_options()
636 self.create_default_options()
637
637
638 log.info('created ui config')
638 log.info('created ui config')
639
639
640 def create_user(self, username, password, email='', admin=False,
640 def create_user(self, username, password, email='', admin=False,
641 strict_creation_check=True, api_key=None):
641 strict_creation_check=True, api_key=None):
642 log.info('creating user `%s`', username)
642 log.info('creating user `%s`', username)
643 user = UserModel().create_or_update(
643 user = UserModel().create_or_update(
644 username, password, email, firstname='RhodeCode', lastname='Admin',
644 username, password, email, firstname='RhodeCode', lastname='Admin',
645 active=True, admin=admin, extern_type="rhodecode",
645 active=True, admin=admin, extern_type="rhodecode",
646 strict_creation_check=strict_creation_check)
646 strict_creation_check=strict_creation_check)
647
647
648 if api_key:
648 if api_key:
649 log.info('setting a new default auth token for user `%s`', username)
649 log.info('setting a new default auth token for user `%s`', username)
650 UserModel().add_auth_token(
650 UserModel().add_auth_token(
651 user=user, lifetime_minutes=-1,
651 user=user, lifetime_minutes=-1,
652 role=UserModel.auth_token_role.ROLE_ALL,
652 role=UserModel.auth_token_role.ROLE_ALL,
653 description='BUILTIN TOKEN')
653 description='BUILTIN TOKEN')
654
654
655 def create_default_user(self):
655 def create_default_user(self):
656 log.info('creating default user')
656 log.info('creating default user')
657 # create default user for handling default permissions.
657 # create default user for handling default permissions.
658 user = UserModel().create_or_update(username=User.DEFAULT_USER,
658 user = UserModel().create_or_update(username=User.DEFAULT_USER,
659 password=str(uuid.uuid1())[:20],
659 password=str(uuid.uuid1())[:20],
660 email=User.DEFAULT_USER_EMAIL,
660 email=User.DEFAULT_USER_EMAIL,
661 firstname='Anonymous',
661 firstname='Anonymous',
662 lastname='User',
662 lastname='User',
663 strict_creation_check=False)
663 strict_creation_check=False)
664 # based on configuration options activate/de-activate this user which
664 # based on configuration options activate/de-activate this user which
665 # controls anonymous access
665 # controls anonymous access
666 if self.cli_args.get('public_access') is False:
666 if self.cli_args.get('public_access') is False:
667 log.info('Public access disabled')
667 log.info('Public access disabled')
668 user.active = False
668 user.active = False
669 Session().add(user)
669 Session().add(user)
670 Session().commit()
670 Session().commit()
671
671
672 def create_permissions(self):
672 def create_permissions(self):
673 """
673 """
674 Creates all permissions defined in the system
674 Creates all permissions defined in the system
675 """
675 """
676 # module.(access|create|change|delete)_[name]
676 # module.(access|create|change|delete)_[name]
677 # module.(none|read|write|admin)
677 # module.(none|read|write|admin)
678 log.info('creating permissions')
678 log.info('creating permissions')
679 PermissionModel(self.sa).create_permissions()
679 PermissionModel(self.sa).create_permissions()
680
680
681 def populate_default_permissions(self):
681 def populate_default_permissions(self):
682 """
682 """
683 Populate default permissions. It will create only the default
683 Populate default permissions. It will create only the default
684 permissions that are missing, and not alter already defined ones
684 permissions that are missing, and not alter already defined ones
685 """
685 """
686 log.info('creating default user permissions')
686 log.info('creating default user permissions')
687 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
687 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
@@ -1,172 +1,171 b''
1 # Copyright (c) 2010 Agendaless Consulting and Contributors.
1 # Copyright (c) 2010 Agendaless Consulting and Contributors.
2 # (http://www.agendaless.com), All Rights Reserved
2 # (http://www.agendaless.com), All Rights Reserved
3 # License: BSD-derived (http://www.repoze.org/LICENSE.txt)
3 # License: BSD-derived (http://www.repoze.org/LICENSE.txt)
4 # With Patches from RhodeCode GmBH
4 # With Patches from RhodeCode GmBH
5
5
6 import os
6 import os
7
7
8 from beaker import cache
8 from beaker import cache
9 from beaker.session import SessionObject, Session
9 from beaker.session import SessionObject, Session
10 from beaker.util import coerce_cache_params
10 from beaker.util import coerce_cache_params
11 from beaker.util import coerce_session_params
11 from beaker.util import coerce_session_params
12
12
13 from pyramid.interfaces import ISession
13 from pyramid.interfaces import ISession
14 from pyramid.settings import asbool
14 from pyramid.settings import asbool
15 from zope.interface import implementer
15 from zope.interface import implementer
16
16
17 from binascii import hexlify
17 from binascii import hexlify
18
18
19
19
20
21 class CustomSession(Session):
20 class CustomSession(Session):
22 pass
21 pass
23
22
24
23
25 def BeakerSessionFactoryConfig(**options):
24 def BeakerSessionFactoryConfig(**options):
26 """ Return a Pyramid session factory using Beaker session settings
25 """ Return a Pyramid session factory using Beaker session settings
27 supplied directly as ``**options``"""
26 supplied directly as ``**options``"""
28
27
29 class PyramidBeakerSessionObject(SessionObject):
28 class PyramidBeakerSessionObject(SessionObject):
30 _options = options
29 _options = options
31 _cookie_on_exception = _options.pop('cookie_on_exception', True)
30 _cookie_on_exception = _options.pop('cookie_on_exception', True)
32 _constant_csrf_token = _options.pop('constant_csrf_token', False)
31 _constant_csrf_token = _options.pop('constant_csrf_token', False)
33 _sa_opts = _options.pop('sa_opts', {})
32 _sa_opts = _options.pop('sa_opts', {})
34
33
35 def __init__(self, request):
34 def __init__(self, request):
36 self._options['session_class'] = CustomSession
35 self._options['session_class'] = CustomSession
37 self._options['sa_opts'] = self._sa_opts
36 self._options['sa_opts'] = self._sa_opts
38 SessionObject.__init__(self, request.environ, **self._options)
37 SessionObject.__init__(self, request.environ, **self._options)
39
38
40 def session_callback(_request, _response):
39 def session_callback(_request, _response):
41 exception = getattr(_request, 'exception', None)
40 exception = getattr(_request, 'exception', None)
42 file_response = getattr(_request, '_file_response', None)
41 file_response = getattr(_request, '_file_response', None)
43 api_call = getattr(_request, 'rpc_method', None)
42 api_call = getattr(_request, 'rpc_method', None)
44
43
45 if file_response is not None:
44 if file_response is not None:
46 return
45 return
47 if api_call is not None:
46 if api_call is not None:
48 return
47 return
49
48
50 if exception is not None and not self._cookie_on_exception:
49 if exception is not None and not self._cookie_on_exception:
51 return
50 return
52
51
53 if self.accessed():
52 if self.accessed():
54 self.persist()
53 self.persist()
55 headers = self.__dict__['_headers']
54 headers = self.__dict__['_headers']
56 if headers.get('set_cookie') and headers.get('cookie_out'):
55 if headers.get('set_cookie') and headers.get('cookie_out'):
57 _response.headerlist.append(('Set-Cookie', headers['cookie_out']))
56 _response.headerlist.append(('Set-Cookie', headers['cookie_out']))
58
57
59 request.add_response_callback(session_callback)
58 request.add_response_callback(session_callback)
60
59
61 # ISession API
60 # ISession API
62
61
63 @property
62 @property
64 def id(self):
63 def id(self):
65 # this is as inspected in SessionObject.__init__
64 # this is as inspected in SessionObject.__init__
66 if self.__dict__['_params'].get('type') != 'cookie':
65 if self.__dict__['_params'].get('type') != 'cookie':
67 return self._session().id
66 return self._session().id
68 return None
67 return None
69
68
70 @property
69 @property
71 def new(self):
70 def new(self):
72 return self.last_accessed is None
71 return self.last_accessed is None
73
72
74 changed = SessionObject.save
73 changed = SessionObject.save
75
74
76 # modifying dictionary methods
75 # modifying dictionary methods
77
76
78 @call_save
77 @call_save
79 def clear(self):
78 def clear(self):
80 return self._session().clear()
79 return self._session().clear()
81
80
82 @call_save
81 @call_save
83 def update(self, d, **kw):
82 def update(self, d, **kw):
84 return self._session().update(d, **kw)
83 return self._session().update(d, **kw)
85
84
86 @call_save
85 @call_save
87 def setdefault(self, k, d=None):
86 def setdefault(self, k, d=None):
88 return self._session().setdefault(k, d)
87 return self._session().setdefault(k, d)
89
88
90 @call_save
89 @call_save
91 def pop(self, k, d=None):
90 def pop(self, k, d=None):
92 return self._session().pop(k, d)
91 return self._session().pop(k, d)
93
92
94 @call_save
93 @call_save
95 def popitem(self):
94 def popitem(self):
96 return self._session().popitem()
95 return self._session().popitem()
97
96
98 __setitem__ = call_save(SessionObject.__setitem__)
97 __setitem__ = call_save(SessionObject.__setitem__)
99 __delitem__ = call_save(SessionObject.__delitem__)
98 __delitem__ = call_save(SessionObject.__delitem__)
100
99
101 # Flash API methods
100 # Flash API methods
102 def flash(self, msg, queue='', allow_duplicate=True):
101 def flash(self, msg, queue='', allow_duplicate=True):
103 storage = self.setdefault(f'_f_{queue}', [])
102 storage = self.setdefault(f'_f_{queue}', [])
104 if allow_duplicate or (msg not in storage):
103 if allow_duplicate or (msg not in storage):
105 storage.append(msg)
104 storage.append(msg)
106
105
107 def pop_flash(self, queue=''):
106 def pop_flash(self, queue=''):
108 storage = self.pop(f'_f_{queue}', [])
107 storage = self.pop(f'_f_{queue}', [])
109 return storage
108 return storage
110
109
111 def peek_flash(self, queue=''):
110 def peek_flash(self, queue=''):
112 storage = self.get('_f_' + queue, [])
111 storage = self.get(f'_f_{queue}', [])
113 return storage
112 return storage
114
113
115 # CSRF API methods
114 # CSRF API methods
116 def new_csrf_token(self):
115 def new_csrf_token(self):
117 token = (self._constant_csrf_token
116 token = (self._constant_csrf_token
118 or hexlify(os.urandom(20)).decode('ascii'))
117 or hexlify(os.urandom(20)).decode('ascii'))
119 self['_csrft_'] = token
118 self['_csrft_'] = token
120 return token
119 return token
121
120
122 def get_csrf_token(self):
121 def get_csrf_token(self):
123 token = self.get('_csrft_', None)
122 token = self.get('_csrft_', None)
124 if token is None:
123 if token is None:
125 token = self.new_csrf_token()
124 token = self.new_csrf_token()
126 return token
125 return token
127
126
128 return implementer(ISession)(PyramidBeakerSessionObject)
127 return implementer(ISession)(PyramidBeakerSessionObject)
129
128
130
129
131 def call_save(wrapped):
130 def call_save(wrapped):
132 """ By default, in non-auto-mode beaker badly wants people to
131 """ By default, in non-auto-mode beaker badly wants people to
133 call save even though it should know something has changed when
132 call save even though it should know something has changed when
134 a mutating method is called. This hack should be removed if
133 a mutating method is called. This hack should be removed if
135 Beaker ever starts to do this by default. """
134 Beaker ever starts to do this by default. """
136 def save(session, *arg, **kw):
135 def save(session, *arg, **kw):
137 value = wrapped(session, *arg, **kw)
136 value = wrapped(session, *arg, **kw)
138 session.save()
137 session.save()
139 return value
138 return value
140 save.__doc__ = wrapped.__doc__
139 save.__doc__ = wrapped.__doc__
141 return save
140 return save
142
141
143
142
144 def session_factory_from_settings(settings):
143 def session_factory_from_settings(settings):
145 """ Return a Pyramid session factory using Beaker session settings
144 """ Return a Pyramid session factory using Beaker session settings
146 supplied from a Paste configuration file"""
145 supplied from a Paste configuration file"""
147 prefixes = ('session.', 'beaker.session.')
146 prefixes = ('session.', 'beaker.session.')
148 options = {}
147 options = {}
149
148
150 # custom gather of our specific sqlalchemy session db configuration we need to translate this into a single entry
149 # custom gather of our specific sqlalchemy session db configuration we need to translate this into a single entry
151 # dict because this is how beaker expects that.
150 # dict because this is how beaker expects that.
152 sa_opts = {}
151 sa_opts = {}
153
152
154 # Pull out any config args meant for beaker session. if there are any
153 # Pull out any config args meant for beaker session. if there are any
155 for k, v in settings.items():
154 for k, v in settings.items():
156 for prefix in prefixes:
155 for prefix in prefixes:
157 if k.startswith(prefix):
156 if k.startswith(prefix):
158 option_name = k[len(prefix):]
157 option_name = k[len(prefix):]
159 if option_name == 'cookie_on_exception':
158 if option_name == 'cookie_on_exception':
160 v = asbool(v)
159 v = asbool(v)
161 if option_name.startswith('sa.'):
160 if option_name.startswith('sa.'):
162 sa_opts[option_name] = v
161 sa_opts[option_name] = v
163 options[option_name] = v
162 options[option_name] = v
164
163
165 options = coerce_session_params(options)
164 options = coerce_session_params(options)
166 options['sa_opts'] = sa_opts
165 options['sa_opts'] = sa_opts
167 return BeakerSessionFactoryConfig(**options)
166 return BeakerSessionFactoryConfig(**options)
168
167
169
168
170 def includeme(config):
169 def includeme(config):
171 session_factory = session_factory_from_settings(config.registry.settings)
170 session_factory = session_factory_from_settings(config.registry.settings)
172 config.set_session_factory(session_factory)
171 config.set_session_factory(session_factory)
@@ -1,807 +1,807 b''
1 # Copyright (C) 2010-2023 RhodeCode GmbH
1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 """
19 """
20 Utilities library for RhodeCode
20 Utilities library for RhodeCode
21 """
21 """
22
22
23 import datetime
23 import datetime
24 import decorator
24 import decorator
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 import sys
28 import sys
29 import shutil
29 import shutil
30 import socket
30 import socket
31 import tempfile
31 import tempfile
32 import traceback
32 import traceback
33 import tarfile
33 import tarfile
34 import warnings
34 import warnings
35 from os.path import join as jn
35 from os.path import join as jn
36
36
37 import paste
37 import paste
38 import pkg_resources
38 import pkg_resources
39 from webhelpers2.text import collapse, strip_tags, convert_accented_entities, convert_misc_entities
39 from webhelpers2.text import collapse, strip_tags, convert_accented_entities, convert_misc_entities
40
40
41 from mako import exceptions
41 from mako import exceptions
42
42
43 from rhodecode.lib.hash_utils import sha256_safe, md5, sha1
43 from rhodecode.lib.hash_utils import sha256_safe, md5, sha1
44 from rhodecode.lib.str_utils import safe_bytes, safe_str
44 from rhodecode.lib.str_utils import safe_bytes, safe_str
45 from rhodecode.lib.vcs.backends.base import Config
45 from rhodecode.lib.vcs.backends.base import Config
46 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.exceptions import VCSError
47 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
47 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
48 from rhodecode.lib.ext_json import sjson as json
48 from rhodecode.lib.ext_json import sjson as json
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.db import (
50 from rhodecode.model.db import (
51 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
51 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
52 from rhodecode.model.meta import Session
52 from rhodecode.model.meta import Session
53
53
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
57 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
58
58
59 # String which contains characters that are not allowed in slug names for
59 # String which contains characters that are not allowed in slug names for
60 # repositories or repository groups. It is properly escaped to use it in
60 # repositories or repository groups. It is properly escaped to use it in
61 # regular expressions.
61 # regular expressions.
62 SLUG_BAD_CHARS = re.escape(r'`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
62 SLUG_BAD_CHARS = re.escape(r'`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
63
63
64 # Regex that matches forbidden characters in repo/group slugs.
64 # Regex that matches forbidden characters in repo/group slugs.
65 SLUG_BAD_CHAR_RE = re.compile(r'[{}\x00-\x08\x0b-\x0c\x0e-\x1f]'.format(SLUG_BAD_CHARS))
65 SLUG_BAD_CHAR_RE = re.compile(r'[{}\x00-\x08\x0b-\x0c\x0e-\x1f]'.format(SLUG_BAD_CHARS))
66
66
67 # Regex that matches allowed characters in repo/group slugs.
67 # Regex that matches allowed characters in repo/group slugs.
68 SLUG_GOOD_CHAR_RE = re.compile(r'[^{}]'.format(SLUG_BAD_CHARS))
68 SLUG_GOOD_CHAR_RE = re.compile(r'[^{}]'.format(SLUG_BAD_CHARS))
69
69
70 # Regex that matches whole repo/group slugs.
70 # Regex that matches whole repo/group slugs.
71 SLUG_RE = re.compile(r'[^{}]+'.format(SLUG_BAD_CHARS))
71 SLUG_RE = re.compile(r'[^{}]+'.format(SLUG_BAD_CHARS))
72
72
73 _license_cache = None
73 _license_cache = None
74
74
75
75
76 def repo_name_slug(value):
76 def repo_name_slug(value):
77 """
77 """
78 Return slug of name of repository
78 Return slug of name of repository
79 This function is called on each creation/modification
79 This function is called on each creation/modification
80 of repository to prevent bad names in repo
80 of repository to prevent bad names in repo
81 """
81 """
82
82
83 replacement_char = '-'
83 replacement_char = '-'
84
84
85 slug = strip_tags(value)
85 slug = strip_tags(value)
86 slug = convert_accented_entities(slug)
86 slug = convert_accented_entities(slug)
87 slug = convert_misc_entities(slug)
87 slug = convert_misc_entities(slug)
88
88
89 slug = SLUG_BAD_CHAR_RE.sub('', slug)
89 slug = SLUG_BAD_CHAR_RE.sub('', slug)
90 slug = re.sub(r'[\s]+', '-', slug)
90 slug = re.sub(r'[\s]+', '-', slug)
91 slug = collapse(slug, replacement_char)
91 slug = collapse(slug, replacement_char)
92
92
93 return slug
93 return slug
94
94
95
95
96 #==============================================================================
96 #==============================================================================
97 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
97 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
98 #==============================================================================
98 #==============================================================================
99 def get_repo_slug(request):
99 def get_repo_slug(request):
100 _repo = ''
100 _repo = ''
101
101
102 if hasattr(request, 'db_repo_name'):
102 if hasattr(request, 'db_repo_name'):
103 # if our requests has set db reference use it for name, this
103 # if our requests has set db reference use it for name, this
104 # translates the example.com/_<id> into proper repo names
104 # translates the example.com/_<id> into proper repo names
105 _repo = request.db_repo_name
105 _repo = request.db_repo_name
106 elif getattr(request, 'matchdict', None):
106 elif getattr(request, 'matchdict', None):
107 # pyramid
107 # pyramid
108 _repo = request.matchdict.get('repo_name')
108 _repo = request.matchdict.get('repo_name')
109
109
110 if _repo:
110 if _repo:
111 _repo = _repo.rstrip('/')
111 _repo = _repo.rstrip('/')
112 return _repo
112 return _repo
113
113
114
114
115 def get_repo_group_slug(request):
115 def get_repo_group_slug(request):
116 _group = ''
116 _group = ''
117 if hasattr(request, 'db_repo_group'):
117 if hasattr(request, 'db_repo_group'):
118 # if our requests has set db reference use it for name, this
118 # if our requests has set db reference use it for name, this
119 # translates the example.com/_<id> into proper repo group names
119 # translates the example.com/_<id> into proper repo group names
120 _group = request.db_repo_group.group_name
120 _group = request.db_repo_group.group_name
121 elif getattr(request, 'matchdict', None):
121 elif getattr(request, 'matchdict', None):
122 # pyramid
122 # pyramid
123 _group = request.matchdict.get('repo_group_name')
123 _group = request.matchdict.get('repo_group_name')
124
124
125 if _group:
125 if _group:
126 _group = _group.rstrip('/')
126 _group = _group.rstrip('/')
127 return _group
127 return _group
128
128
129
129
130 def get_user_group_slug(request):
130 def get_user_group_slug(request):
131 _user_group = ''
131 _user_group = ''
132
132
133 if hasattr(request, 'db_user_group'):
133 if hasattr(request, 'db_user_group'):
134 _user_group = request.db_user_group.users_group_name
134 _user_group = request.db_user_group.users_group_name
135 elif getattr(request, 'matchdict', None):
135 elif getattr(request, 'matchdict', None):
136 # pyramid
136 # pyramid
137 _user_group = request.matchdict.get('user_group_id')
137 _user_group = request.matchdict.get('user_group_id')
138 _user_group_name = request.matchdict.get('user_group_name')
138 _user_group_name = request.matchdict.get('user_group_name')
139 try:
139 try:
140 if _user_group:
140 if _user_group:
141 _user_group = UserGroup.get(_user_group)
141 _user_group = UserGroup.get(_user_group)
142 elif _user_group_name:
142 elif _user_group_name:
143 _user_group = UserGroup.get_by_group_name(_user_group_name)
143 _user_group = UserGroup.get_by_group_name(_user_group_name)
144
144
145 if _user_group:
145 if _user_group:
146 _user_group = _user_group.users_group_name
146 _user_group = _user_group.users_group_name
147 except Exception:
147 except Exception:
148 log.exception('Failed to get user group by id and name')
148 log.exception('Failed to get user group by id and name')
149 # catch all failures here
149 # catch all failures here
150 return None
150 return None
151
151
152 return _user_group
152 return _user_group
153
153
154
154
155 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
155 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
156 """
156 """
157 Scans given path for repos and return (name,(type,path)) tuple
157 Scans given path for repos and return (name,(type,path)) tuple
158
158
159 :param path: path to scan for repositories
159 :param path: path to scan for repositories
160 :param recursive: recursive search and return names with subdirs in front
160 :param recursive: recursive search and return names with subdirs in front
161 """
161 """
162
162
163 # remove ending slash for better results
163 # remove ending slash for better results
164 path = path.rstrip(os.sep)
164 path = path.rstrip(os.sep)
165 log.debug('now scanning in %s location recursive:%s...', path, recursive)
165 log.debug('now scanning in %s location recursive:%s...', path, recursive)
166
166
167 def _get_repos(p):
167 def _get_repos(p):
168 dirpaths = get_dirpaths(p)
168 dirpaths = get_dirpaths(p)
169 if not _is_dir_writable(p):
169 if not _is_dir_writable(p):
170 log.warning('repo path without write access: %s', p)
170 log.warning('repo path without write access: %s', p)
171
171
172 for dirpath in dirpaths:
172 for dirpath in dirpaths:
173 if os.path.isfile(os.path.join(p, dirpath)):
173 if os.path.isfile(os.path.join(p, dirpath)):
174 continue
174 continue
175 cur_path = os.path.join(p, dirpath)
175 cur_path = os.path.join(p, dirpath)
176
176
177 # skip removed repos
177 # skip removed repos
178 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
178 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
179 continue
179 continue
180
180
181 #skip .<somethin> dirs
181 #skip .<somethin> dirs
182 if dirpath.startswith('.'):
182 if dirpath.startswith('.'):
183 continue
183 continue
184
184
185 try:
185 try:
186 scm_info = get_scm(cur_path)
186 scm_info = get_scm(cur_path)
187 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
187 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
188 except VCSError:
188 except VCSError:
189 if not recursive:
189 if not recursive:
190 continue
190 continue
191 #check if this dir containts other repos for recursive scan
191 #check if this dir containts other repos for recursive scan
192 rec_path = os.path.join(p, dirpath)
192 rec_path = os.path.join(p, dirpath)
193 if os.path.isdir(rec_path):
193 if os.path.isdir(rec_path):
194 yield from _get_repos(rec_path)
194 yield from _get_repos(rec_path)
195
195
196 return _get_repos(path)
196 return _get_repos(path)
197
197
198
198
199 def get_dirpaths(p: str) -> list:
199 def get_dirpaths(p: str) -> list:
200 try:
200 try:
201 # OS-independable way of checking if we have at least read-only
201 # OS-independable way of checking if we have at least read-only
202 # access or not.
202 # access or not.
203 dirpaths = os.listdir(p)
203 dirpaths = os.listdir(p)
204 except OSError:
204 except OSError:
205 log.warning('ignoring repo path without read access: %s', p)
205 log.warning('ignoring repo path without read access: %s', p)
206 return []
206 return []
207
207
208 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
208 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
209 # decode paths and suddenly returns unicode objects itself. The items it
209 # decode paths and suddenly returns unicode objects itself. The items it
210 # cannot decode are returned as strings and cause issues.
210 # cannot decode are returned as strings and cause issues.
211 #
211 #
212 # Those paths are ignored here until a solid solution for path handling has
212 # Those paths are ignored here until a solid solution for path handling has
213 # been built.
213 # been built.
214 expected_type = type(p)
214 expected_type = type(p)
215
215
216 def _has_correct_type(item):
216 def _has_correct_type(item):
217 if type(item) is not expected_type:
217 if type(item) is not expected_type:
218 log.error(
218 log.error(
219 "Ignoring path %s since it cannot be decoded into str.",
219 "Ignoring path %s since it cannot be decoded into str.",
220 # Using "repr" to make sure that we see the byte value in case
220 # Using "repr" to make sure that we see the byte value in case
221 # of support.
221 # of support.
222 repr(item))
222 repr(item))
223 return False
223 return False
224 return True
224 return True
225
225
226 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
226 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
227
227
228 return dirpaths
228 return dirpaths
229
229
230
230
231 def _is_dir_writable(path):
231 def _is_dir_writable(path):
232 """
232 """
233 Probe if `path` is writable.
233 Probe if `path` is writable.
234
234
235 Due to trouble on Cygwin / Windows, this is actually probing if it is
235 Due to trouble on Cygwin / Windows, this is actually probing if it is
236 possible to create a file inside of `path`, stat does not produce reliable
236 possible to create a file inside of `path`, stat does not produce reliable
237 results in this case.
237 results in this case.
238 """
238 """
239 try:
239 try:
240 with tempfile.TemporaryFile(dir=path):
240 with tempfile.TemporaryFile(dir=path):
241 pass
241 pass
242 except OSError:
242 except OSError:
243 return False
243 return False
244 return True
244 return True
245
245
246
246
247 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
247 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
248 """
248 """
249 Returns True if given path is a valid repository False otherwise.
249 Returns True if given path is a valid repository False otherwise.
250 If expect_scm param is given also, compare if given scm is the same
250 If expect_scm param is given also, compare if given scm is the same
251 as expected from scm parameter. If explicit_scm is given don't try to
251 as expected from scm parameter. If explicit_scm is given don't try to
252 detect the scm, just use the given one to check if repo is valid
252 detect the scm, just use the given one to check if repo is valid
253
253
254 :param repo_name:
254 :param repo_name:
255 :param base_path:
255 :param base_path:
256 :param expect_scm:
256 :param expect_scm:
257 :param explicit_scm:
257 :param explicit_scm:
258 :param config:
258 :param config:
259
259
260 :return True: if given path is a valid repository
260 :return True: if given path is a valid repository
261 """
261 """
262 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
262 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
263 log.debug('Checking if `%s` is a valid path for repository. '
263 log.debug('Checking if `%s` is a valid path for repository. '
264 'Explicit type: %s', repo_name, explicit_scm)
264 'Explicit type: %s', repo_name, explicit_scm)
265
265
266 try:
266 try:
267 if explicit_scm:
267 if explicit_scm:
268 detected_scms = [get_scm_backend(explicit_scm)(
268 detected_scms = [get_scm_backend(explicit_scm)(
269 full_path, config=config).alias]
269 full_path, config=config).alias]
270 else:
270 else:
271 detected_scms = get_scm(full_path)
271 detected_scms = get_scm(full_path)
272
272
273 if expect_scm:
273 if expect_scm:
274 return detected_scms[0] == expect_scm
274 return detected_scms[0] == expect_scm
275 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
275 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
276 return True
276 return True
277 except VCSError:
277 except VCSError:
278 log.debug('path: %s is not a valid repo !', full_path)
278 log.debug('path: %s is not a valid repo !', full_path)
279 return False
279 return False
280
280
281
281
282 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
282 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
283 """
283 """
284 Returns True if a given path is a repository group, False otherwise
284 Returns True if a given path is a repository group, False otherwise
285
285
286 :param repo_group_name:
286 :param repo_group_name:
287 :param base_path:
287 :param base_path:
288 """
288 """
289 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
289 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
290 log.debug('Checking if `%s` is a valid path for repository group',
290 log.debug('Checking if `%s` is a valid path for repository group',
291 repo_group_name)
291 repo_group_name)
292
292
293 # check if it's not a repo
293 # check if it's not a repo
294 if is_valid_repo(repo_group_name, base_path):
294 if is_valid_repo(repo_group_name, base_path):
295 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
295 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
296 return False
296 return False
297
297
298 try:
298 try:
299 # we need to check bare git repos at higher level
299 # we need to check bare git repos at higher level
300 # since we might match branches/hooks/info/objects or possible
300 # since we might match branches/hooks/info/objects or possible
301 # other things inside bare git repo
301 # other things inside bare git repo
302 maybe_repo = os.path.dirname(full_path)
302 maybe_repo = os.path.dirname(full_path)
303 if maybe_repo == base_path:
303 if maybe_repo == base_path:
304 # skip root level repo check; we know root location CANNOT BE a repo group
304 # skip root level repo check; we know root location CANNOT BE a repo group
305 return False
305 return False
306
306
307 scm_ = get_scm(maybe_repo)
307 scm_ = get_scm(maybe_repo)
308 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
308 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
309 return False
309 return False
310 except VCSError:
310 except VCSError:
311 pass
311 pass
312
312
313 # check if it's a valid path
313 # check if it's a valid path
314 if skip_path_check or os.path.isdir(full_path):
314 if skip_path_check or os.path.isdir(full_path):
315 log.debug('path: %s is a valid repo group !', full_path)
315 log.debug('path: %s is a valid repo group !', full_path)
316 return True
316 return True
317
317
318 log.debug('path: %s is not a valid repo group !', full_path)
318 log.debug('path: %s is not a valid repo group !', full_path)
319 return False
319 return False
320
320
321
321
322 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
322 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
323 while True:
323 while True:
324 ok = eval(input(prompt))
324 ok = input(prompt)
325 if ok.lower() in ('y', 'ye', 'yes'):
325 if ok.lower() in ('y', 'ye', 'yes'):
326 return True
326 return True
327 if ok.lower() in ('n', 'no', 'nop', 'nope'):
327 if ok.lower() in ('n', 'no', 'nop', 'nope'):
328 return False
328 return False
329 retries = retries - 1
329 retries = retries - 1
330 if retries < 0:
330 if retries < 0:
331 raise OSError
331 raise OSError
332 print(complaint)
332 print(complaint)
333
333
334 # propagated from mercurial documentation
334 # propagated from mercurial documentation
335 ui_sections = [
335 ui_sections = [
336 'alias', 'auth',
336 'alias', 'auth',
337 'decode/encode', 'defaults',
337 'decode/encode', 'defaults',
338 'diff', 'email',
338 'diff', 'email',
339 'extensions', 'format',
339 'extensions', 'format',
340 'merge-patterns', 'merge-tools',
340 'merge-patterns', 'merge-tools',
341 'hooks', 'http_proxy',
341 'hooks', 'http_proxy',
342 'smtp', 'patch',
342 'smtp', 'patch',
343 'paths', 'profiling',
343 'paths', 'profiling',
344 'server', 'trusted',
344 'server', 'trusted',
345 'ui', 'web', ]
345 'ui', 'web', ]
346
346
347
347
348 def config_data_from_db(clear_session=True, repo=None):
348 def config_data_from_db(clear_session=True, repo=None):
349 """
349 """
350 Read the configuration data from the database and return configuration
350 Read the configuration data from the database and return configuration
351 tuples.
351 tuples.
352 """
352 """
353 from rhodecode.model.settings import VcsSettingsModel
353 from rhodecode.model.settings import VcsSettingsModel
354
354
355 config = []
355 config = []
356
356
357 sa = meta.Session()
357 sa = meta.Session()
358 settings_model = VcsSettingsModel(repo=repo, sa=sa)
358 settings_model = VcsSettingsModel(repo=repo, sa=sa)
359
359
360 ui_settings = settings_model.get_ui_settings()
360 ui_settings = settings_model.get_ui_settings()
361
361
362 ui_data = []
362 ui_data = []
363 for setting in ui_settings:
363 for setting in ui_settings:
364 if setting.active:
364 if setting.active:
365 ui_data.append((setting.section, setting.key, setting.value))
365 ui_data.append((setting.section, setting.key, setting.value))
366 config.append((
366 config.append((
367 safe_str(setting.section), safe_str(setting.key),
367 safe_str(setting.section), safe_str(setting.key),
368 safe_str(setting.value)))
368 safe_str(setting.value)))
369 if setting.key == 'push_ssl':
369 if setting.key == 'push_ssl':
370 # force set push_ssl requirement to False, rhodecode
370 # force set push_ssl requirement to False, rhodecode
371 # handles that
371 # handles that
372 config.append((
372 config.append((
373 safe_str(setting.section), safe_str(setting.key), False))
373 safe_str(setting.section), safe_str(setting.key), False))
374 log.debug(
374 log.debug(
375 'settings ui from db@repo[%s]: %s',
375 'settings ui from db@repo[%s]: %s',
376 repo,
376 repo,
377 ','.join(['[{}] {}={}'.format(*s) for s in ui_data]))
377 ','.join(['[{}] {}={}'.format(*s) for s in ui_data]))
378 if clear_session:
378 if clear_session:
379 meta.Session.remove()
379 meta.Session.remove()
380
380
381 # TODO: mikhail: probably it makes no sense to re-read hooks information.
381 # TODO: mikhail: probably it makes no sense to re-read hooks information.
382 # It's already there and activated/deactivated
382 # It's already there and activated/deactivated
383 skip_entries = []
383 skip_entries = []
384 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
384 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
385 if 'pull' not in enabled_hook_classes:
385 if 'pull' not in enabled_hook_classes:
386 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
386 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
387 if 'push' not in enabled_hook_classes:
387 if 'push' not in enabled_hook_classes:
388 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
388 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
389 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
389 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
390 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
390 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
391
391
392 config = [entry for entry in config if entry[:2] not in skip_entries]
392 config = [entry for entry in config if entry[:2] not in skip_entries]
393
393
394 return config
394 return config
395
395
396
396
397 def make_db_config(clear_session=True, repo=None):
397 def make_db_config(clear_session=True, repo=None):
398 """
398 """
399 Create a :class:`Config` instance based on the values in the database.
399 Create a :class:`Config` instance based on the values in the database.
400 """
400 """
401 config = Config()
401 config = Config()
402 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
402 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
403 for section, option, value in config_data:
403 for section, option, value in config_data:
404 config.set(section, option, value)
404 config.set(section, option, value)
405 return config
405 return config
406
406
407
407
408 def get_enabled_hook_classes(ui_settings):
408 def get_enabled_hook_classes(ui_settings):
409 """
409 """
410 Return the enabled hook classes.
410 Return the enabled hook classes.
411
411
412 :param ui_settings: List of ui_settings as returned
412 :param ui_settings: List of ui_settings as returned
413 by :meth:`VcsSettingsModel.get_ui_settings`
413 by :meth:`VcsSettingsModel.get_ui_settings`
414
414
415 :return: a list with the enabled hook classes. The order is not guaranteed.
415 :return: a list with the enabled hook classes. The order is not guaranteed.
416 :rtype: list
416 :rtype: list
417 """
417 """
418 enabled_hooks = []
418 enabled_hooks = []
419 active_hook_keys = [
419 active_hook_keys = [
420 key for section, key, value, active in ui_settings
420 key for section, key, value, active in ui_settings
421 if section == 'hooks' and active]
421 if section == 'hooks' and active]
422
422
423 hook_names = {
423 hook_names = {
424 RhodeCodeUi.HOOK_PUSH: 'push',
424 RhodeCodeUi.HOOK_PUSH: 'push',
425 RhodeCodeUi.HOOK_PULL: 'pull',
425 RhodeCodeUi.HOOK_PULL: 'pull',
426 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
426 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
427 }
427 }
428
428
429 for key in active_hook_keys:
429 for key in active_hook_keys:
430 hook = hook_names.get(key)
430 hook = hook_names.get(key)
431 if hook:
431 if hook:
432 enabled_hooks.append(hook)
432 enabled_hooks.append(hook)
433
433
434 return enabled_hooks
434 return enabled_hooks
435
435
436
436
437 def set_rhodecode_config(config):
437 def set_rhodecode_config(config):
438 """
438 """
439 Updates pyramid config with new settings from database
439 Updates pyramid config with new settings from database
440
440
441 :param config:
441 :param config:
442 """
442 """
443 from rhodecode.model.settings import SettingsModel
443 from rhodecode.model.settings import SettingsModel
444 app_settings = SettingsModel().get_all_settings()
444 app_settings = SettingsModel().get_all_settings()
445
445
446 for k, v in list(app_settings.items()):
446 for k, v in list(app_settings.items()):
447 config[k] = v
447 config[k] = v
448
448
449
449
450 def get_rhodecode_realm():
450 def get_rhodecode_realm():
451 """
451 """
452 Return the rhodecode realm from database.
452 Return the rhodecode realm from database.
453 """
453 """
454 from rhodecode.model.settings import SettingsModel
454 from rhodecode.model.settings import SettingsModel
455 realm = SettingsModel().get_setting_by_name('realm')
455 realm = SettingsModel().get_setting_by_name('realm')
456 return safe_str(realm.app_settings_value)
456 return safe_str(realm.app_settings_value)
457
457
458
458
459 def get_rhodecode_base_path():
459 def get_rhodecode_base_path():
460 """
460 """
461 Returns the base path. The base path is the filesystem path which points
461 Returns the base path. The base path is the filesystem path which points
462 to the repository store.
462 to the repository store.
463 """
463 """
464
464
465 import rhodecode
465 import rhodecode
466 return rhodecode.CONFIG['default_base_path']
466 return rhodecode.CONFIG['default_base_path']
467
467
468
468
469 def map_groups(path):
469 def map_groups(path):
470 """
470 """
471 Given a full path to a repository, create all nested groups that this
471 Given a full path to a repository, create all nested groups that this
472 repo is inside. This function creates parent-child relationships between
472 repo is inside. This function creates parent-child relationships between
473 groups and creates default perms for all new groups.
473 groups and creates default perms for all new groups.
474
474
475 :param paths: full path to repository
475 :param paths: full path to repository
476 """
476 """
477 from rhodecode.model.repo_group import RepoGroupModel
477 from rhodecode.model.repo_group import RepoGroupModel
478 sa = meta.Session()
478 sa = meta.Session()
479 groups = path.split(Repository.NAME_SEP)
479 groups = path.split(Repository.NAME_SEP)
480 parent = None
480 parent = None
481 group = None
481 group = None
482
482
483 # last element is repo in nested groups structure
483 # last element is repo in nested groups structure
484 groups = groups[:-1]
484 groups = groups[:-1]
485 rgm = RepoGroupModel(sa)
485 rgm = RepoGroupModel(sa)
486 owner = User.get_first_super_admin()
486 owner = User.get_first_super_admin()
487 for lvl, group_name in enumerate(groups):
487 for lvl, group_name in enumerate(groups):
488 group_name = '/'.join(groups[:lvl] + [group_name])
488 group_name = '/'.join(groups[:lvl] + [group_name])
489 group = RepoGroup.get_by_group_name(group_name)
489 group = RepoGroup.get_by_group_name(group_name)
490 desc = '%s group' % group_name
490 desc = '%s group' % group_name
491
491
492 # skip folders that are now removed repos
492 # skip folders that are now removed repos
493 if REMOVED_REPO_PAT.match(group_name):
493 if REMOVED_REPO_PAT.match(group_name):
494 break
494 break
495
495
496 if group is None:
496 if group is None:
497 log.debug('creating group level: %s group_name: %s',
497 log.debug('creating group level: %s group_name: %s',
498 lvl, group_name)
498 lvl, group_name)
499 group = RepoGroup(group_name, parent)
499 group = RepoGroup(group_name, parent)
500 group.group_description = desc
500 group.group_description = desc
501 group.user = owner
501 group.user = owner
502 sa.add(group)
502 sa.add(group)
503 perm_obj = rgm._create_default_perms(group)
503 perm_obj = rgm._create_default_perms(group)
504 sa.add(perm_obj)
504 sa.add(perm_obj)
505 sa.flush()
505 sa.flush()
506
506
507 parent = group
507 parent = group
508 return group
508 return group
509
509
510
510
511 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
511 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
512 """
512 """
513 maps all repos given in initial_repo_list, non existing repositories
513 maps all repos given in initial_repo_list, non existing repositories
514 are created, if remove_obsolete is True it also checks for db entries
514 are created, if remove_obsolete is True it also checks for db entries
515 that are not in initial_repo_list and removes them.
515 that are not in initial_repo_list and removes them.
516
516
517 :param initial_repo_list: list of repositories found by scanning methods
517 :param initial_repo_list: list of repositories found by scanning methods
518 :param remove_obsolete: check for obsolete entries in database
518 :param remove_obsolete: check for obsolete entries in database
519 """
519 """
520 from rhodecode.model.repo import RepoModel
520 from rhodecode.model.repo import RepoModel
521 from rhodecode.model.repo_group import RepoGroupModel
521 from rhodecode.model.repo_group import RepoGroupModel
522 from rhodecode.model.settings import SettingsModel
522 from rhodecode.model.settings import SettingsModel
523
523
524 sa = meta.Session()
524 sa = meta.Session()
525 repo_model = RepoModel()
525 repo_model = RepoModel()
526 user = User.get_first_super_admin()
526 user = User.get_first_super_admin()
527 added = []
527 added = []
528
528
529 # creation defaults
529 # creation defaults
530 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
530 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
531 enable_statistics = defs.get('repo_enable_statistics')
531 enable_statistics = defs.get('repo_enable_statistics')
532 enable_locking = defs.get('repo_enable_locking')
532 enable_locking = defs.get('repo_enable_locking')
533 enable_downloads = defs.get('repo_enable_downloads')
533 enable_downloads = defs.get('repo_enable_downloads')
534 private = defs.get('repo_private')
534 private = defs.get('repo_private')
535
535
536 for name, repo in list(initial_repo_list.items()):
536 for name, repo in list(initial_repo_list.items()):
537 group = map_groups(name)
537 group = map_groups(name)
538 str_name = safe_str(name)
538 str_name = safe_str(name)
539 db_repo = repo_model.get_by_repo_name(str_name)
539 db_repo = repo_model.get_by_repo_name(str_name)
540 # found repo that is on filesystem not in RhodeCode database
540 # found repo that is on filesystem not in RhodeCode database
541 if not db_repo:
541 if not db_repo:
542 log.info('repository %s not found, creating now', name)
542 log.info('repository %s not found, creating now', name)
543 added.append(name)
543 added.append(name)
544 desc = (repo.description
544 desc = (repo.description
545 if repo.description != 'unknown'
545 if repo.description != 'unknown'
546 else '%s repository' % name)
546 else '%s repository' % name)
547
547
548 db_repo = repo_model._create_repo(
548 db_repo = repo_model._create_repo(
549 repo_name=name,
549 repo_name=name,
550 repo_type=repo.alias,
550 repo_type=repo.alias,
551 description=desc,
551 description=desc,
552 repo_group=getattr(group, 'group_id', None),
552 repo_group=getattr(group, 'group_id', None),
553 owner=user,
553 owner=user,
554 enable_locking=enable_locking,
554 enable_locking=enable_locking,
555 enable_downloads=enable_downloads,
555 enable_downloads=enable_downloads,
556 enable_statistics=enable_statistics,
556 enable_statistics=enable_statistics,
557 private=private,
557 private=private,
558 state=Repository.STATE_CREATED
558 state=Repository.STATE_CREATED
559 )
559 )
560 sa.commit()
560 sa.commit()
561 # we added that repo just now, and make sure we updated server info
561 # we added that repo just now, and make sure we updated server info
562 if db_repo.repo_type == 'git':
562 if db_repo.repo_type == 'git':
563 git_repo = db_repo.scm_instance()
563 git_repo = db_repo.scm_instance()
564 # update repository server-info
564 # update repository server-info
565 log.debug('Running update server info')
565 log.debug('Running update server info')
566 git_repo._update_server_info()
566 git_repo._update_server_info()
567
567
568 db_repo.update_commit_cache()
568 db_repo.update_commit_cache()
569
569
570 config = db_repo._config
570 config = db_repo._config
571 config.set('extensions', 'largefiles', '')
571 config.set('extensions', 'largefiles', '')
572 repo = db_repo.scm_instance(config=config)
572 repo = db_repo.scm_instance(config=config)
573 repo.install_hooks()
573 repo.install_hooks()
574
574
575 removed = []
575 removed = []
576 if remove_obsolete:
576 if remove_obsolete:
577 # remove from database those repositories that are not in the filesystem
577 # remove from database those repositories that are not in the filesystem
578 for repo in sa.query(Repository).all():
578 for repo in sa.query(Repository).all():
579 if repo.repo_name not in list(initial_repo_list.keys()):
579 if repo.repo_name not in list(initial_repo_list.keys()):
580 log.debug("Removing non-existing repository found in db `%s`",
580 log.debug("Removing non-existing repository found in db `%s`",
581 repo.repo_name)
581 repo.repo_name)
582 try:
582 try:
583 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
583 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
584 sa.commit()
584 sa.commit()
585 removed.append(repo.repo_name)
585 removed.append(repo.repo_name)
586 except Exception:
586 except Exception:
587 # don't hold further removals on error
587 # don't hold further removals on error
588 log.error(traceback.format_exc())
588 log.error(traceback.format_exc())
589 sa.rollback()
589 sa.rollback()
590
590
591 def splitter(full_repo_name):
591 def splitter(full_repo_name):
592 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
592 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
593 gr_name = None
593 gr_name = None
594 if len(_parts) == 2:
594 if len(_parts) == 2:
595 gr_name = _parts[0]
595 gr_name = _parts[0]
596 return gr_name
596 return gr_name
597
597
598 initial_repo_group_list = [splitter(x) for x in
598 initial_repo_group_list = [splitter(x) for x in
599 list(initial_repo_list.keys()) if splitter(x)]
599 list(initial_repo_list.keys()) if splitter(x)]
600
600
601 # remove from database those repository groups that are not in the
601 # remove from database those repository groups that are not in the
602 # filesystem due to parent child relationships we need to delete them
602 # filesystem due to parent child relationships we need to delete them
603 # in a specific order of most nested first
603 # in a specific order of most nested first
604 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
604 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
605 def nested_sort(gr):
605 def nested_sort(gr):
606 return len(gr.split('/'))
606 return len(gr.split('/'))
607 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
607 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
608 if group_name not in initial_repo_group_list:
608 if group_name not in initial_repo_group_list:
609 repo_group = RepoGroup.get_by_group_name(group_name)
609 repo_group = RepoGroup.get_by_group_name(group_name)
610 if (repo_group.children.all() or
610 if (repo_group.children.all() or
611 not RepoGroupModel().check_exist_filesystem(
611 not RepoGroupModel().check_exist_filesystem(
612 group_name=group_name, exc_on_failure=False)):
612 group_name=group_name, exc_on_failure=False)):
613 continue
613 continue
614
614
615 log.info(
615 log.info(
616 'Removing non-existing repository group found in db `%s`',
616 'Removing non-existing repository group found in db `%s`',
617 group_name)
617 group_name)
618 try:
618 try:
619 RepoGroupModel(sa).delete(group_name, fs_remove=False)
619 RepoGroupModel(sa).delete(group_name, fs_remove=False)
620 sa.commit()
620 sa.commit()
621 removed.append(group_name)
621 removed.append(group_name)
622 except Exception:
622 except Exception:
623 # don't hold further removals on error
623 # don't hold further removals on error
624 log.exception(
624 log.exception(
625 'Unable to remove repository group `%s`',
625 'Unable to remove repository group `%s`',
626 group_name)
626 group_name)
627 sa.rollback()
627 sa.rollback()
628 raise
628 raise
629
629
630 return added, removed
630 return added, removed
631
631
632
632
633 def load_rcextensions(root_path):
633 def load_rcextensions(root_path):
634 import rhodecode
634 import rhodecode
635 from rhodecode.config import conf
635 from rhodecode.config import conf
636
636
637 path = os.path.join(root_path)
637 path = os.path.join(root_path)
638 sys.path.append(path)
638 sys.path.append(path)
639
639
640 try:
640 try:
641 rcextensions = __import__('rcextensions')
641 rcextensions = __import__('rcextensions')
642 except ImportError:
642 except ImportError:
643 if os.path.isdir(os.path.join(path, 'rcextensions')):
643 if os.path.isdir(os.path.join(path, 'rcextensions')):
644 log.warning('Unable to load rcextensions from %s', path)
644 log.warning('Unable to load rcextensions from %s', path)
645 rcextensions = None
645 rcextensions = None
646
646
647 if rcextensions:
647 if rcextensions:
648 log.info('Loaded rcextensions from %s...', rcextensions)
648 log.info('Loaded rcextensions from %s...', rcextensions)
649 rhodecode.EXTENSIONS = rcextensions
649 rhodecode.EXTENSIONS = rcextensions
650
650
651 # Additional mappings that are not present in the pygments lexers
651 # Additional mappings that are not present in the pygments lexers
652 conf.LANGUAGES_EXTENSIONS_MAP.update(
652 conf.LANGUAGES_EXTENSIONS_MAP.update(
653 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
653 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
654
654
655
655
656 def get_custom_lexer(extension):
656 def get_custom_lexer(extension):
657 """
657 """
658 returns a custom lexer if it is defined in rcextensions module, or None
658 returns a custom lexer if it is defined in rcextensions module, or None
659 if there's no custom lexer defined
659 if there's no custom lexer defined
660 """
660 """
661 import rhodecode
661 import rhodecode
662 from pygments import lexers
662 from pygments import lexers
663
663
664 # custom override made by RhodeCode
664 # custom override made by RhodeCode
665 if extension in ['mako']:
665 if extension in ['mako']:
666 return lexers.get_lexer_by_name('html+mako')
666 return lexers.get_lexer_by_name('html+mako')
667
667
668 # check if we didn't define this extension as other lexer
668 # check if we didn't define this extension as other lexer
669 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
669 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
670 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
670 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
671 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
671 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
672 return lexers.get_lexer_by_name(_lexer_name)
672 return lexers.get_lexer_by_name(_lexer_name)
673
673
674
674
675 #==============================================================================
675 #==============================================================================
676 # TEST FUNCTIONS AND CREATORS
676 # TEST FUNCTIONS AND CREATORS
677 #==============================================================================
677 #==============================================================================
678 def create_test_index(repo_location, config):
678 def create_test_index(repo_location, config):
679 """
679 """
680 Makes default test index.
680 Makes default test index.
681 """
681 """
682 try:
682 try:
683 import rc_testdata
683 import rc_testdata
684 except ImportError:
684 except ImportError:
685 raise ImportError('Failed to import rc_testdata, '
685 raise ImportError('Failed to import rc_testdata, '
686 'please make sure this package is installed from requirements_test.txt')
686 'please make sure this package is installed from requirements_test.txt')
687 rc_testdata.extract_search_index(
687 rc_testdata.extract_search_index(
688 'vcs_search_index', os.path.dirname(config['search.location']))
688 'vcs_search_index', os.path.dirname(config['search.location']))
689
689
690
690
691 def create_test_directory(test_path):
691 def create_test_directory(test_path):
692 """
692 """
693 Create test directory if it doesn't exist.
693 Create test directory if it doesn't exist.
694 """
694 """
695 if not os.path.isdir(test_path):
695 if not os.path.isdir(test_path):
696 log.debug('Creating testdir %s', test_path)
696 log.debug('Creating testdir %s', test_path)
697 os.makedirs(test_path)
697 os.makedirs(test_path)
698
698
699
699
700 def create_test_database(test_path, config):
700 def create_test_database(test_path, config):
701 """
701 """
702 Makes a fresh database.
702 Makes a fresh database.
703 """
703 """
704 from rhodecode.lib.db_manage import DbManage
704 from rhodecode.lib.db_manage import DbManage
705 from rhodecode.lib.utils2 import get_encryption_key
705 from rhodecode.lib.utils2 import get_encryption_key
706
706
707 # PART ONE create db
707 # PART ONE create db
708 dbconf = config['sqlalchemy.db1.url']
708 dbconf = config['sqlalchemy.db1.url']
709 enc_key = get_encryption_key(config)
709 enc_key = get_encryption_key(config)
710
710
711 log.debug('making test db %s', dbconf)
711 log.debug('making test db %s', dbconf)
712
712
713 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
713 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
714 tests=True, cli_args={'force_ask': True}, enc_key=enc_key)
714 tests=True, cli_args={'force_ask': True}, enc_key=enc_key)
715 dbmanage.create_tables(override=True)
715 dbmanage.create_tables(override=True)
716 dbmanage.set_db_version()
716 dbmanage.set_db_version()
717 # for tests dynamically set new root paths based on generated content
717 # for tests dynamically set new root paths based on generated content
718 dbmanage.create_settings(dbmanage.config_prompt(test_path))
718 dbmanage.create_settings(dbmanage.config_prompt(test_path))
719 dbmanage.create_default_user()
719 dbmanage.create_default_user()
720 dbmanage.create_test_admin_and_users()
720 dbmanage.create_test_admin_and_users()
721 dbmanage.create_permissions()
721 dbmanage.create_permissions()
722 dbmanage.populate_default_permissions()
722 dbmanage.populate_default_permissions()
723 Session().commit()
723 Session().commit()
724
724
725
725
726 def create_test_repositories(test_path, config):
726 def create_test_repositories(test_path, config):
727 """
727 """
728 Creates test repositories in the temporary directory. Repositories are
728 Creates test repositories in the temporary directory. Repositories are
729 extracted from archives within the rc_testdata package.
729 extracted from archives within the rc_testdata package.
730 """
730 """
731 import rc_testdata
731 import rc_testdata
732 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
732 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
733
733
734 log.debug('making test vcs repositories')
734 log.debug('making test vcs repositories')
735
735
736 idx_path = config['search.location']
736 idx_path = config['search.location']
737 data_path = config['cache_dir']
737 data_path = config['cache_dir']
738
738
739 # clean index and data
739 # clean index and data
740 if idx_path and os.path.exists(idx_path):
740 if idx_path and os.path.exists(idx_path):
741 log.debug('remove %s', idx_path)
741 log.debug('remove %s', idx_path)
742 shutil.rmtree(idx_path)
742 shutil.rmtree(idx_path)
743
743
744 if data_path and os.path.exists(data_path):
744 if data_path and os.path.exists(data_path):
745 log.debug('remove %s', data_path)
745 log.debug('remove %s', data_path)
746 shutil.rmtree(data_path)
746 shutil.rmtree(data_path)
747
747
748 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
748 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
749 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
749 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
750
750
751 # Note: Subversion is in the process of being integrated with the system,
751 # Note: Subversion is in the process of being integrated with the system,
752 # until we have a properly packed version of the test svn repository, this
752 # until we have a properly packed version of the test svn repository, this
753 # tries to copy over the repo from a package "rc_testdata"
753 # tries to copy over the repo from a package "rc_testdata"
754 svn_repo_path = rc_testdata.get_svn_repo_archive()
754 svn_repo_path = rc_testdata.get_svn_repo_archive()
755 with tarfile.open(svn_repo_path) as tar:
755 with tarfile.open(svn_repo_path) as tar:
756 tar.extractall(jn(test_path, SVN_REPO))
756 tar.extractall(jn(test_path, SVN_REPO))
757
757
758
758
759 def password_changed(auth_user, session):
759 def password_changed(auth_user, session):
760 # Never report password change in case of default user or anonymous user.
760 # Never report password change in case of default user or anonymous user.
761 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
761 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
762 return False
762 return False
763
763
764 password_hash = md5(safe_bytes(auth_user.password)) if auth_user.password else None
764 password_hash = md5(safe_bytes(auth_user.password)) if auth_user.password else None
765 rhodecode_user = session.get('rhodecode_user', {})
765 rhodecode_user = session.get('rhodecode_user', {})
766 session_password_hash = rhodecode_user.get('password', '')
766 session_password_hash = rhodecode_user.get('password', '')
767 return password_hash != session_password_hash
767 return password_hash != session_password_hash
768
768
769
769
770 def read_opensource_licenses():
770 def read_opensource_licenses():
771 global _license_cache
771 global _license_cache
772
772
773 if not _license_cache:
773 if not _license_cache:
774 licenses = pkg_resources.resource_string(
774 licenses = pkg_resources.resource_string(
775 'rhodecode', 'config/licenses.json')
775 'rhodecode', 'config/licenses.json')
776 _license_cache = json.loads(licenses)
776 _license_cache = json.loads(licenses)
777
777
778 return _license_cache
778 return _license_cache
779
779
780
780
781 def generate_platform_uuid():
781 def generate_platform_uuid():
782 """
782 """
783 Generates platform UUID based on it's name
783 Generates platform UUID based on it's name
784 """
784 """
785 import platform
785 import platform
786
786
787 try:
787 try:
788 uuid_list = [platform.platform()]
788 uuid_list = [platform.platform()]
789 return sha256_safe(':'.join(uuid_list))
789 return sha256_safe(':'.join(uuid_list))
790 except Exception as e:
790 except Exception as e:
791 log.error('Failed to generate host uuid: %s', e)
791 log.error('Failed to generate host uuid: %s', e)
792 return 'UNDEFINED'
792 return 'UNDEFINED'
793
793
794
794
795 def send_test_email(recipients, email_body='TEST EMAIL'):
795 def send_test_email(recipients, email_body='TEST EMAIL'):
796 """
796 """
797 Simple code for generating test emails.
797 Simple code for generating test emails.
798 Usage::
798 Usage::
799
799
800 from rhodecode.lib import utils
800 from rhodecode.lib import utils
801 utils.send_test_email()
801 utils.send_test_email()
802 """
802 """
803 from rhodecode.lib.celerylib import tasks, run_task
803 from rhodecode.lib.celerylib import tasks, run_task
804
804
805 email_body = email_body_plaintext = email_body
805 email_body = email_body_plaintext = email_body
806 subject = f'SUBJECT FROM: {socket.gethostname()}'
806 subject = f'SUBJECT FROM: {socket.gethostname()}'
807 tasks.send_email(recipients, subject, email_body_plaintext, email_body)
807 tasks.send_email(recipients, subject, email_body_plaintext, email_body)
General Comments 0
You need to be logged in to leave comments. Login now