##// END OF EJS Templates
db: use unicode for some of the defaults to reduce sqlalchemy warnings.
marcink -
r1967:df3f2899 default
parent child Browse files
Show More
@@ -1,52 +1,52 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.model.meta import Session
23 from rhodecode.model.meta import Session
24 from rhodecode.model.user import UserModel
24 from rhodecode.model.user import UserModel
25 from rhodecode.model.auth_token import AuthTokenModel
25 from rhodecode.model.auth_token import AuthTokenModel
26 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
27
27
28
28
29 @pytest.fixture(scope="class")
29 @pytest.fixture(scope="class")
30 def testuser_api(request, pylonsapp):
30 def testuser_api(request, pylonsapp):
31 cls = request.cls
31 cls = request.cls
32
32
33 # ADMIN USER
33 # ADMIN USER
34 cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
34 cls.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
35 cls.apikey = cls.usr.api_key
35 cls.apikey = cls.usr.api_key
36
36
37 # REGULAR USER
37 # REGULAR USER
38 cls.test_user = UserModel().create_or_update(
38 cls.test_user = UserModel().create_or_update(
39 username='test-api',
39 username='test-api',
40 password='test',
40 password='test',
41 email='test@api.rhodecode.org',
41 email='test@api.rhodecode.org',
42 firstname='first',
42 firstname='first',
43 lastname='last'
43 lastname='last'
44 )
44 )
45 # create TOKEN for user, if he doesn't have one
45 # create TOKEN for user, if he doesn't have one
46 if not cls.test_user.api_key:
46 if not cls.test_user.api_key:
47 AuthTokenModel().create(
47 AuthTokenModel().create(
48 user=cls.test_user, description='TEST_USER_TOKEN')
48 user=cls.test_user, description=u'TEST_USER_TOKEN')
49
49
50 Session().commit()
50 Session().commit()
51 cls.TEST_USER_LOGIN = cls.test_user.username
51 cls.TEST_USER_LOGIN = cls.test_user.username
52 cls.apikey_regular = cls.test_user.api_key
52 cls.apikey_regular = cls.test_user.api_key
@@ -1,620 +1,620 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Database creation, and setup module for RhodeCode Enterprise. Used for creation
22 Database creation, and setup module for RhodeCode Enterprise. Used for creation
23 of database as well as for migration operations
23 of database as well as for migration operations
24 """
24 """
25
25
26 import os
26 import os
27 import sys
27 import sys
28 import time
28 import time
29 import uuid
29 import uuid
30 import logging
30 import logging
31 import getpass
31 import getpass
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from sqlalchemy.engine import create_engine
34 from sqlalchemy.engine import create_engine
35
35
36 from rhodecode import __dbversion__
36 from rhodecode import __dbversion__
37 from rhodecode.model import init_model
37 from rhodecode.model import init_model
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.db import (
39 from rhodecode.model.db import (
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
42 from rhodecode.model.meta import Session, Base
42 from rhodecode.model.meta import Session, Base
43 from rhodecode.model.permission import PermissionModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.repo_group import RepoGroupModel
46 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 def notify(msg):
52 def notify(msg):
53 """
53 """
54 Notification for migrations messages
54 Notification for migrations messages
55 """
55 """
56 ml = len(msg) + (4 * 2)
56 ml = len(msg) + (4 * 2)
57 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
57 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
58
58
59
59
60 class DbManage(object):
60 class DbManage(object):
61
61
62 def __init__(self, log_sql, dbconf, root, tests=False,
62 def __init__(self, log_sql, dbconf, root, tests=False,
63 SESSION=None, cli_args={}):
63 SESSION=None, cli_args={}):
64 self.dbname = dbconf.split('/')[-1]
64 self.dbname = dbconf.split('/')[-1]
65 self.tests = tests
65 self.tests = tests
66 self.root = root
66 self.root = root
67 self.dburi = dbconf
67 self.dburi = dbconf
68 self.log_sql = log_sql
68 self.log_sql = log_sql
69 self.db_exists = False
69 self.db_exists = False
70 self.cli_args = cli_args
70 self.cli_args = cli_args
71 self.init_db(SESSION=SESSION)
71 self.init_db(SESSION=SESSION)
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
73
73
74 def get_ask_ok_func(self, param):
74 def get_ask_ok_func(self, param):
75 if param not in [None]:
75 if param not in [None]:
76 # return a function lambda that has a default set to param
76 # return a function lambda that has a default set to param
77 return lambda *args, **kwargs: param
77 return lambda *args, **kwargs: param
78 else:
78 else:
79 from rhodecode.lib.utils import ask_ok
79 from rhodecode.lib.utils import ask_ok
80 return ask_ok
80 return ask_ok
81
81
82 def init_db(self, SESSION=None):
82 def init_db(self, SESSION=None):
83 if SESSION:
83 if SESSION:
84 self.sa = SESSION
84 self.sa = SESSION
85 else:
85 else:
86 # init new sessions
86 # init new sessions
87 engine = create_engine(self.dburi, echo=self.log_sql)
87 engine = create_engine(self.dburi, echo=self.log_sql)
88 init_model(engine)
88 init_model(engine)
89 self.sa = Session()
89 self.sa = Session()
90
90
91 def create_tables(self, override=False):
91 def create_tables(self, override=False):
92 """
92 """
93 Create a auth database
93 Create a auth database
94 """
94 """
95
95
96 log.info("Existing database with the same name is going to be destroyed.")
96 log.info("Existing database with the same name is going to be destroyed.")
97 log.info("Setup command will run DROP ALL command on that database.")
97 log.info("Setup command will run DROP ALL command on that database.")
98 if self.tests:
98 if self.tests:
99 destroy = True
99 destroy = True
100 else:
100 else:
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
102 if not destroy:
102 if not destroy:
103 log.info('Nothing done.')
103 log.info('Nothing done.')
104 sys.exit(0)
104 sys.exit(0)
105 if destroy:
105 if destroy:
106 Base.metadata.drop_all()
106 Base.metadata.drop_all()
107
107
108 checkfirst = not override
108 checkfirst = not override
109 Base.metadata.create_all(checkfirst=checkfirst)
109 Base.metadata.create_all(checkfirst=checkfirst)
110 log.info('Created tables for %s' % self.dbname)
110 log.info('Created tables for %s' % self.dbname)
111
111
112 def set_db_version(self):
112 def set_db_version(self):
113 ver = DbMigrateVersion()
113 ver = DbMigrateVersion()
114 ver.version = __dbversion__
114 ver.version = __dbversion__
115 ver.repository_id = 'rhodecode_db_migrations'
115 ver.repository_id = 'rhodecode_db_migrations'
116 ver.repository_path = 'versions'
116 ver.repository_path = 'versions'
117 self.sa.add(ver)
117 self.sa.add(ver)
118 log.info('db version set to: %s' % __dbversion__)
118 log.info('db version set to: %s' % __dbversion__)
119
119
120 def run_pre_migration_tasks(self):
120 def run_pre_migration_tasks(self):
121 """
121 """
122 Run various tasks before actually doing migrations
122 Run various tasks before actually doing migrations
123 """
123 """
124 # delete cache keys on each upgrade
124 # delete cache keys on each upgrade
125 total = CacheKey.query().count()
125 total = CacheKey.query().count()
126 log.info("Deleting (%s) cache keys now...", total)
126 log.info("Deleting (%s) cache keys now...", total)
127 CacheKey.delete_all_cache()
127 CacheKey.delete_all_cache()
128
128
129 def upgrade(self):
129 def upgrade(self):
130 """
130 """
131 Upgrades given database schema to given revision following
131 Upgrades given database schema to given revision following
132 all needed steps, to perform the upgrade
132 all needed steps, to perform the upgrade
133
133
134 """
134 """
135
135
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
138 DatabaseNotControlledError
138 DatabaseNotControlledError
139
139
140 if 'sqlite' in self.dburi:
140 if 'sqlite' in self.dburi:
141 print (
141 print (
142 '********************** WARNING **********************\n'
142 '********************** WARNING **********************\n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
144 'Earlier versions are known to fail on some migrations\n'
144 'Earlier versions are known to fail on some migrations\n'
145 '*****************************************************\n')
145 '*****************************************************\n')
146
146
147 upgrade = self.ask_ok(
147 upgrade = self.ask_ok(
148 'You are about to perform a database upgrade. Make '
148 'You are about to perform a database upgrade. Make '
149 'sure you have backed up your database. '
149 'sure you have backed up your database. '
150 'Continue ? [y/n]')
150 'Continue ? [y/n]')
151 if not upgrade:
151 if not upgrade:
152 log.info('No upgrade performed')
152 log.info('No upgrade performed')
153 sys.exit(0)
153 sys.exit(0)
154
154
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
156 'rhodecode/lib/dbmigrate')
156 'rhodecode/lib/dbmigrate')
157 db_uri = self.dburi
157 db_uri = self.dburi
158
158
159 try:
159 try:
160 curr_version = api.db_version(db_uri, repository_path)
160 curr_version = api.db_version(db_uri, repository_path)
161 msg = ('Found current database under version '
161 msg = ('Found current database under version '
162 'control with version %s' % curr_version)
162 'control with version %s' % curr_version)
163
163
164 except (RuntimeError, DatabaseNotControlledError):
164 except (RuntimeError, DatabaseNotControlledError):
165 curr_version = 1
165 curr_version = 1
166 msg = ('Current database is not under version control. Setting '
166 msg = ('Current database is not under version control. Setting '
167 'as version %s' % curr_version)
167 'as version %s' % curr_version)
168 api.version_control(db_uri, repository_path, curr_version)
168 api.version_control(db_uri, repository_path, curr_version)
169
169
170 notify(msg)
170 notify(msg)
171
171
172 self.run_pre_migration_tasks()
172 self.run_pre_migration_tasks()
173
173
174 if curr_version == __dbversion__:
174 if curr_version == __dbversion__:
175 log.info('This database is already at the newest version')
175 log.info('This database is already at the newest version')
176 sys.exit(0)
176 sys.exit(0)
177
177
178 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
178 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
179 notify('attempting to upgrade database from '
179 notify('attempting to upgrade database from '
180 'version %s to version %s' % (curr_version, __dbversion__))
180 'version %s to version %s' % (curr_version, __dbversion__))
181
181
182 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
182 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
183 _step = None
183 _step = None
184 for step in upgrade_steps:
184 for step in upgrade_steps:
185 notify('performing upgrade step %s' % step)
185 notify('performing upgrade step %s' % step)
186 time.sleep(0.5)
186 time.sleep(0.5)
187
187
188 api.upgrade(db_uri, repository_path, step)
188 api.upgrade(db_uri, repository_path, step)
189 self.sa.rollback()
189 self.sa.rollback()
190 notify('schema upgrade for step %s completed' % (step,))
190 notify('schema upgrade for step %s completed' % (step,))
191
191
192 _step = step
192 _step = step
193
193
194 notify('upgrade to version %s successful' % _step)
194 notify('upgrade to version %s successful' % _step)
195
195
196 def fix_repo_paths(self):
196 def fix_repo_paths(self):
197 """
197 """
198 Fixes an old RhodeCode version path into new one without a '*'
198 Fixes an old RhodeCode version path into new one without a '*'
199 """
199 """
200
200
201 paths = self.sa.query(RhodeCodeUi)\
201 paths = self.sa.query(RhodeCodeUi)\
202 .filter(RhodeCodeUi.ui_key == '/')\
202 .filter(RhodeCodeUi.ui_key == '/')\
203 .scalar()
203 .scalar()
204
204
205 paths.ui_value = paths.ui_value.replace('*', '')
205 paths.ui_value = paths.ui_value.replace('*', '')
206
206
207 try:
207 try:
208 self.sa.add(paths)
208 self.sa.add(paths)
209 self.sa.commit()
209 self.sa.commit()
210 except Exception:
210 except Exception:
211 self.sa.rollback()
211 self.sa.rollback()
212 raise
212 raise
213
213
214 def fix_default_user(self):
214 def fix_default_user(self):
215 """
215 """
216 Fixes an old default user with some 'nicer' default values,
216 Fixes an old default user with some 'nicer' default values,
217 used mostly for anonymous access
217 used mostly for anonymous access
218 """
218 """
219 def_user = self.sa.query(User)\
219 def_user = self.sa.query(User)\
220 .filter(User.username == User.DEFAULT_USER)\
220 .filter(User.username == User.DEFAULT_USER)\
221 .one()
221 .one()
222
222
223 def_user.name = 'Anonymous'
223 def_user.name = 'Anonymous'
224 def_user.lastname = 'User'
224 def_user.lastname = 'User'
225 def_user.email = User.DEFAULT_USER_EMAIL
225 def_user.email = User.DEFAULT_USER_EMAIL
226
226
227 try:
227 try:
228 self.sa.add(def_user)
228 self.sa.add(def_user)
229 self.sa.commit()
229 self.sa.commit()
230 except Exception:
230 except Exception:
231 self.sa.rollback()
231 self.sa.rollback()
232 raise
232 raise
233
233
234 def fix_settings(self):
234 def fix_settings(self):
235 """
235 """
236 Fixes rhodecode settings and adds ga_code key for google analytics
236 Fixes rhodecode settings and adds ga_code key for google analytics
237 """
237 """
238
238
239 hgsettings3 = RhodeCodeSetting('ga_code', '')
239 hgsettings3 = RhodeCodeSetting('ga_code', '')
240
240
241 try:
241 try:
242 self.sa.add(hgsettings3)
242 self.sa.add(hgsettings3)
243 self.sa.commit()
243 self.sa.commit()
244 except Exception:
244 except Exception:
245 self.sa.rollback()
245 self.sa.rollback()
246 raise
246 raise
247
247
248 def create_admin_and_prompt(self):
248 def create_admin_and_prompt(self):
249
249
250 # defaults
250 # defaults
251 defaults = self.cli_args
251 defaults = self.cli_args
252 username = defaults.get('username')
252 username = defaults.get('username')
253 password = defaults.get('password')
253 password = defaults.get('password')
254 email = defaults.get('email')
254 email = defaults.get('email')
255
255
256 if username is None:
256 if username is None:
257 username = raw_input('Specify admin username:')
257 username = raw_input('Specify admin username:')
258 if password is None:
258 if password is None:
259 password = self._get_admin_password()
259 password = self._get_admin_password()
260 if not password:
260 if not password:
261 # second try
261 # second try
262 password = self._get_admin_password()
262 password = self._get_admin_password()
263 if not password:
263 if not password:
264 sys.exit()
264 sys.exit()
265 if email is None:
265 if email is None:
266 email = raw_input('Specify admin email:')
266 email = raw_input('Specify admin email:')
267 api_key = self.cli_args.get('api_key')
267 api_key = self.cli_args.get('api_key')
268 self.create_user(username, password, email, True,
268 self.create_user(username, password, email, True,
269 strict_creation_check=False,
269 strict_creation_check=False,
270 api_key=api_key)
270 api_key=api_key)
271
271
272 def _get_admin_password(self):
272 def _get_admin_password(self):
273 password = getpass.getpass('Specify admin password '
273 password = getpass.getpass('Specify admin password '
274 '(min 6 chars):')
274 '(min 6 chars):')
275 confirm = getpass.getpass('Confirm password:')
275 confirm = getpass.getpass('Confirm password:')
276
276
277 if password != confirm:
277 if password != confirm:
278 log.error('passwords mismatch')
278 log.error('passwords mismatch')
279 return False
279 return False
280 if len(password) < 6:
280 if len(password) < 6:
281 log.error('password is too short - use at least 6 characters')
281 log.error('password is too short - use at least 6 characters')
282 return False
282 return False
283
283
284 return password
284 return password
285
285
286 def create_test_admin_and_users(self):
286 def create_test_admin_and_users(self):
287 log.info('creating admin and regular test users')
287 log.info('creating admin and regular test users')
288 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
288 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
289 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
289 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
290 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
290 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
291 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
291 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
292 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
292 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
293
293
294 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
294 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
295 TEST_USER_ADMIN_EMAIL, True, api_key=True)
295 TEST_USER_ADMIN_EMAIL, True, api_key=True)
296
296
297 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
297 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
298 TEST_USER_REGULAR_EMAIL, False, api_key=True)
298 TEST_USER_REGULAR_EMAIL, False, api_key=True)
299
299
300 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
300 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
301 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
301 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
302
302
303 def create_ui_settings(self, repo_store_path):
303 def create_ui_settings(self, repo_store_path):
304 """
304 """
305 Creates ui settings, fills out hooks
305 Creates ui settings, fills out hooks
306 and disables dotencode
306 and disables dotencode
307 """
307 """
308 settings_model = SettingsModel(sa=self.sa)
308 settings_model = SettingsModel(sa=self.sa)
309 from rhodecode.lib.vcs.backends.hg import largefiles_store
309 from rhodecode.lib.vcs.backends.hg import largefiles_store
310 from rhodecode.lib.vcs.backends.git import lfs_store
310 from rhodecode.lib.vcs.backends.git import lfs_store
311
311
312 # Build HOOKS
312 # Build HOOKS
313 hooks = [
313 hooks = [
314 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
314 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
315
315
316 # HG
316 # HG
317 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
317 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
318 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
318 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
319 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
319 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
320 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
320 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
321 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
321 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
322 (RhodeCodeUi.HOOK_PUSH_KEY, 'python:vcsserver.hooks.key_push'),
322 (RhodeCodeUi.HOOK_PUSH_KEY, 'python:vcsserver.hooks.key_push'),
323
323
324 ]
324 ]
325
325
326 for key, value in hooks:
326 for key, value in hooks:
327 hook_obj = settings_model.get_ui_by_key(key)
327 hook_obj = settings_model.get_ui_by_key(key)
328 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
328 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
329 hooks2.ui_section = 'hooks'
329 hooks2.ui_section = 'hooks'
330 hooks2.ui_key = key
330 hooks2.ui_key = key
331 hooks2.ui_value = value
331 hooks2.ui_value = value
332 self.sa.add(hooks2)
332 self.sa.add(hooks2)
333
333
334 # enable largefiles
334 # enable largefiles
335 largefiles = RhodeCodeUi()
335 largefiles = RhodeCodeUi()
336 largefiles.ui_section = 'extensions'
336 largefiles.ui_section = 'extensions'
337 largefiles.ui_key = 'largefiles'
337 largefiles.ui_key = 'largefiles'
338 largefiles.ui_value = ''
338 largefiles.ui_value = ''
339 self.sa.add(largefiles)
339 self.sa.add(largefiles)
340
340
341 # set default largefiles cache dir, defaults to
341 # set default largefiles cache dir, defaults to
342 # /repo_store_location/.cache/largefiles
342 # /repo_store_location/.cache/largefiles
343 largefiles = RhodeCodeUi()
343 largefiles = RhodeCodeUi()
344 largefiles.ui_section = 'largefiles'
344 largefiles.ui_section = 'largefiles'
345 largefiles.ui_key = 'usercache'
345 largefiles.ui_key = 'usercache'
346 largefiles.ui_value = largefiles_store(repo_store_path)
346 largefiles.ui_value = largefiles_store(repo_store_path)
347
347
348 self.sa.add(largefiles)
348 self.sa.add(largefiles)
349
349
350 # set default lfs cache dir, defaults to
350 # set default lfs cache dir, defaults to
351 # /repo_store_location/.cache/lfs_store
351 # /repo_store_location/.cache/lfs_store
352 lfsstore = RhodeCodeUi()
352 lfsstore = RhodeCodeUi()
353 lfsstore.ui_section = 'vcs_git_lfs'
353 lfsstore.ui_section = 'vcs_git_lfs'
354 lfsstore.ui_key = 'store_location'
354 lfsstore.ui_key = 'store_location'
355 lfsstore.ui_value = lfs_store(repo_store_path)
355 lfsstore.ui_value = lfs_store(repo_store_path)
356
356
357 self.sa.add(lfsstore)
357 self.sa.add(lfsstore)
358
358
359 # enable hgsubversion disabled by default
359 # enable hgsubversion disabled by default
360 hgsubversion = RhodeCodeUi()
360 hgsubversion = RhodeCodeUi()
361 hgsubversion.ui_section = 'extensions'
361 hgsubversion.ui_section = 'extensions'
362 hgsubversion.ui_key = 'hgsubversion'
362 hgsubversion.ui_key = 'hgsubversion'
363 hgsubversion.ui_value = ''
363 hgsubversion.ui_value = ''
364 hgsubversion.ui_active = False
364 hgsubversion.ui_active = False
365 self.sa.add(hgsubversion)
365 self.sa.add(hgsubversion)
366
366
367 # enable hgevolve disabled by default
367 # enable hgevolve disabled by default
368 hgevolve = RhodeCodeUi()
368 hgevolve = RhodeCodeUi()
369 hgevolve.ui_section = 'extensions'
369 hgevolve.ui_section = 'extensions'
370 hgevolve.ui_key = 'evolve'
370 hgevolve.ui_key = 'evolve'
371 hgevolve.ui_value = ''
371 hgevolve.ui_value = ''
372 hgevolve.ui_active = False
372 hgevolve.ui_active = False
373 self.sa.add(hgevolve)
373 self.sa.add(hgevolve)
374
374
375 # enable hggit disabled by default
375 # enable hggit disabled by default
376 hggit = RhodeCodeUi()
376 hggit = RhodeCodeUi()
377 hggit.ui_section = 'extensions'
377 hggit.ui_section = 'extensions'
378 hggit.ui_key = 'hggit'
378 hggit.ui_key = 'hggit'
379 hggit.ui_value = ''
379 hggit.ui_value = ''
380 hggit.ui_active = False
380 hggit.ui_active = False
381 self.sa.add(hggit)
381 self.sa.add(hggit)
382
382
383 # set svn branch defaults
383 # set svn branch defaults
384 branches = ["/branches/*", "/trunk"]
384 branches = ["/branches/*", "/trunk"]
385 tags = ["/tags/*"]
385 tags = ["/tags/*"]
386
386
387 for branch in branches:
387 for branch in branches:
388 settings_model.create_ui_section_value(
388 settings_model.create_ui_section_value(
389 RhodeCodeUi.SVN_BRANCH_ID, branch)
389 RhodeCodeUi.SVN_BRANCH_ID, branch)
390
390
391 for tag in tags:
391 for tag in tags:
392 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
392 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
393
393
394 def create_auth_plugin_options(self, skip_existing=False):
394 def create_auth_plugin_options(self, skip_existing=False):
395 """
395 """
396 Create default auth plugin settings, and make it active
396 Create default auth plugin settings, and make it active
397
397
398 :param skip_existing:
398 :param skip_existing:
399 """
399 """
400
400
401 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
401 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
402 ('auth_rhodecode_enabled', 'True', 'bool')]:
402 ('auth_rhodecode_enabled', 'True', 'bool')]:
403 if (skip_existing and
403 if (skip_existing and
404 SettingsModel().get_setting_by_name(k) is not None):
404 SettingsModel().get_setting_by_name(k) is not None):
405 log.debug('Skipping option %s' % k)
405 log.debug('Skipping option %s' % k)
406 continue
406 continue
407 setting = RhodeCodeSetting(k, v, t)
407 setting = RhodeCodeSetting(k, v, t)
408 self.sa.add(setting)
408 self.sa.add(setting)
409
409
410 def create_default_options(self, skip_existing=False):
410 def create_default_options(self, skip_existing=False):
411 """Creates default settings"""
411 """Creates default settings"""
412
412
413 for k, v, t in [
413 for k, v, t in [
414 ('default_repo_enable_locking', False, 'bool'),
414 ('default_repo_enable_locking', False, 'bool'),
415 ('default_repo_enable_downloads', False, 'bool'),
415 ('default_repo_enable_downloads', False, 'bool'),
416 ('default_repo_enable_statistics', False, 'bool'),
416 ('default_repo_enable_statistics', False, 'bool'),
417 ('default_repo_private', False, 'bool'),
417 ('default_repo_private', False, 'bool'),
418 ('default_repo_type', 'hg', 'unicode')]:
418 ('default_repo_type', 'hg', 'unicode')]:
419
419
420 if (skip_existing and
420 if (skip_existing and
421 SettingsModel().get_setting_by_name(k) is not None):
421 SettingsModel().get_setting_by_name(k) is not None):
422 log.debug('Skipping option %s' % k)
422 log.debug('Skipping option %s' % k)
423 continue
423 continue
424 setting = RhodeCodeSetting(k, v, t)
424 setting = RhodeCodeSetting(k, v, t)
425 self.sa.add(setting)
425 self.sa.add(setting)
426
426
427 def fixup_groups(self):
427 def fixup_groups(self):
428 def_usr = User.get_default_user()
428 def_usr = User.get_default_user()
429 for g in RepoGroup.query().all():
429 for g in RepoGroup.query().all():
430 g.group_name = g.get_new_name(g.name)
430 g.group_name = g.get_new_name(g.name)
431 self.sa.add(g)
431 self.sa.add(g)
432 # get default perm
432 # get default perm
433 default = UserRepoGroupToPerm.query()\
433 default = UserRepoGroupToPerm.query()\
434 .filter(UserRepoGroupToPerm.group == g)\
434 .filter(UserRepoGroupToPerm.group == g)\
435 .filter(UserRepoGroupToPerm.user == def_usr)\
435 .filter(UserRepoGroupToPerm.user == def_usr)\
436 .scalar()
436 .scalar()
437
437
438 if default is None:
438 if default is None:
439 log.debug('missing default permission for group %s adding' % g)
439 log.debug('missing default permission for group %s adding' % g)
440 perm_obj = RepoGroupModel()._create_default_perms(g)
440 perm_obj = RepoGroupModel()._create_default_perms(g)
441 self.sa.add(perm_obj)
441 self.sa.add(perm_obj)
442
442
443 def reset_permissions(self, username):
443 def reset_permissions(self, username):
444 """
444 """
445 Resets permissions to default state, useful when old systems had
445 Resets permissions to default state, useful when old systems had
446 bad permissions, we must clean them up
446 bad permissions, we must clean them up
447
447
448 :param username:
448 :param username:
449 """
449 """
450 default_user = User.get_by_username(username)
450 default_user = User.get_by_username(username)
451 if not default_user:
451 if not default_user:
452 return
452 return
453
453
454 u2p = UserToPerm.query()\
454 u2p = UserToPerm.query()\
455 .filter(UserToPerm.user == default_user).all()
455 .filter(UserToPerm.user == default_user).all()
456 fixed = False
456 fixed = False
457 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
457 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
458 for p in u2p:
458 for p in u2p:
459 Session().delete(p)
459 Session().delete(p)
460 fixed = True
460 fixed = True
461 self.populate_default_permissions()
461 self.populate_default_permissions()
462 return fixed
462 return fixed
463
463
464 def update_repo_info(self):
464 def update_repo_info(self):
465 RepoModel.update_repoinfo()
465 RepoModel.update_repoinfo()
466
466
467 def config_prompt(self, test_repo_path='', retries=3):
467 def config_prompt(self, test_repo_path='', retries=3):
468 defaults = self.cli_args
468 defaults = self.cli_args
469 _path = defaults.get('repos_location')
469 _path = defaults.get('repos_location')
470 if retries == 3:
470 if retries == 3:
471 log.info('Setting up repositories config')
471 log.info('Setting up repositories config')
472
472
473 if _path is not None:
473 if _path is not None:
474 path = _path
474 path = _path
475 elif not self.tests and not test_repo_path:
475 elif not self.tests and not test_repo_path:
476 path = raw_input(
476 path = raw_input(
477 'Enter a valid absolute path to store repositories. '
477 'Enter a valid absolute path to store repositories. '
478 'All repositories in that path will be added automatically:'
478 'All repositories in that path will be added automatically:'
479 )
479 )
480 else:
480 else:
481 path = test_repo_path
481 path = test_repo_path
482 path_ok = True
482 path_ok = True
483
483
484 # check proper dir
484 # check proper dir
485 if not os.path.isdir(path):
485 if not os.path.isdir(path):
486 path_ok = False
486 path_ok = False
487 log.error('Given path %s is not a valid directory' % (path,))
487 log.error('Given path %s is not a valid directory' % (path,))
488
488
489 elif not os.path.isabs(path):
489 elif not os.path.isabs(path):
490 path_ok = False
490 path_ok = False
491 log.error('Given path %s is not an absolute path' % (path,))
491 log.error('Given path %s is not an absolute path' % (path,))
492
492
493 # check if path is at least readable.
493 # check if path is at least readable.
494 if not os.access(path, os.R_OK):
494 if not os.access(path, os.R_OK):
495 path_ok = False
495 path_ok = False
496 log.error('Given path %s is not readable' % (path,))
496 log.error('Given path %s is not readable' % (path,))
497
497
498 # check write access, warn user about non writeable paths
498 # check write access, warn user about non writeable paths
499 elif not os.access(path, os.W_OK) and path_ok:
499 elif not os.access(path, os.W_OK) and path_ok:
500 log.warning('No write permission to given path %s' % (path,))
500 log.warning('No write permission to given path %s' % (path,))
501
501
502 q = ('Given path %s is not writeable, do you want to '
502 q = ('Given path %s is not writeable, do you want to '
503 'continue with read only mode ? [y/n]' % (path,))
503 'continue with read only mode ? [y/n]' % (path,))
504 if not self.ask_ok(q):
504 if not self.ask_ok(q):
505 log.error('Canceled by user')
505 log.error('Canceled by user')
506 sys.exit(-1)
506 sys.exit(-1)
507
507
508 if retries == 0:
508 if retries == 0:
509 sys.exit('max retries reached')
509 sys.exit('max retries reached')
510 if not path_ok:
510 if not path_ok:
511 retries -= 1
511 retries -= 1
512 return self.config_prompt(test_repo_path, retries)
512 return self.config_prompt(test_repo_path, retries)
513
513
514 real_path = os.path.normpath(os.path.realpath(path))
514 real_path = os.path.normpath(os.path.realpath(path))
515
515
516 if real_path != os.path.normpath(path):
516 if real_path != os.path.normpath(path):
517 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
517 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
518 'given path as %s ? [y/n]') % (real_path,)
518 'given path as %s ? [y/n]') % (real_path,)
519 if not self.ask_ok(q):
519 if not self.ask_ok(q):
520 log.error('Canceled by user')
520 log.error('Canceled by user')
521 sys.exit(-1)
521 sys.exit(-1)
522
522
523 return real_path
523 return real_path
524
524
525 def create_settings(self, path):
525 def create_settings(self, path):
526
526
527 self.create_ui_settings(path)
527 self.create_ui_settings(path)
528
528
529 ui_config = [
529 ui_config = [
530 ('web', 'push_ssl', 'False'),
530 ('web', 'push_ssl', 'False'),
531 ('web', 'allow_archive', 'gz zip bz2'),
531 ('web', 'allow_archive', 'gz zip bz2'),
532 ('web', 'allow_push', '*'),
532 ('web', 'allow_push', '*'),
533 ('web', 'baseurl', '/'),
533 ('web', 'baseurl', '/'),
534 ('paths', '/', path),
534 ('paths', '/', path),
535 ('phases', 'publish', 'True')
535 ('phases', 'publish', 'True')
536 ]
536 ]
537 for section, key, value in ui_config:
537 for section, key, value in ui_config:
538 ui_conf = RhodeCodeUi()
538 ui_conf = RhodeCodeUi()
539 setattr(ui_conf, 'ui_section', section)
539 setattr(ui_conf, 'ui_section', section)
540 setattr(ui_conf, 'ui_key', key)
540 setattr(ui_conf, 'ui_key', key)
541 setattr(ui_conf, 'ui_value', value)
541 setattr(ui_conf, 'ui_value', value)
542 self.sa.add(ui_conf)
542 self.sa.add(ui_conf)
543
543
544 # rhodecode app settings
544 # rhodecode app settings
545 settings = [
545 settings = [
546 ('realm', 'RhodeCode', 'unicode'),
546 ('realm', 'RhodeCode', 'unicode'),
547 ('title', '', 'unicode'),
547 ('title', '', 'unicode'),
548 ('pre_code', '', 'unicode'),
548 ('pre_code', '', 'unicode'),
549 ('post_code', '', 'unicode'),
549 ('post_code', '', 'unicode'),
550 ('show_public_icon', True, 'bool'),
550 ('show_public_icon', True, 'bool'),
551 ('show_private_icon', True, 'bool'),
551 ('show_private_icon', True, 'bool'),
552 ('stylify_metatags', False, 'bool'),
552 ('stylify_metatags', False, 'bool'),
553 ('dashboard_items', 100, 'int'),
553 ('dashboard_items', 100, 'int'),
554 ('admin_grid_items', 25, 'int'),
554 ('admin_grid_items', 25, 'int'),
555 ('show_version', True, 'bool'),
555 ('show_version', True, 'bool'),
556 ('use_gravatar', False, 'bool'),
556 ('use_gravatar', False, 'bool'),
557 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
557 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
558 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
558 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
559 ('support_url', '', 'unicode'),
559 ('support_url', '', 'unicode'),
560 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
560 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
561 ('show_revision_number', True, 'bool'),
561 ('show_revision_number', True, 'bool'),
562 ('show_sha_length', 12, 'int'),
562 ('show_sha_length', 12, 'int'),
563 ]
563 ]
564
564
565 for key, val, type_ in settings:
565 for key, val, type_ in settings:
566 sett = RhodeCodeSetting(key, val, type_)
566 sett = RhodeCodeSetting(key, val, type_)
567 self.sa.add(sett)
567 self.sa.add(sett)
568
568
569 self.create_auth_plugin_options()
569 self.create_auth_plugin_options()
570 self.create_default_options()
570 self.create_default_options()
571
571
572 log.info('created ui config')
572 log.info('created ui config')
573
573
574 def create_user(self, username, password, email='', admin=False,
574 def create_user(self, username, password, email='', admin=False,
575 strict_creation_check=True, api_key=None):
575 strict_creation_check=True, api_key=None):
576 log.info('creating user %s' % username)
576 log.info('creating user %s' % username)
577 user = UserModel().create_or_update(
577 user = UserModel().create_or_update(
578 username, password, email, firstname='RhodeCode', lastname='Admin',
578 username, password, email, firstname=u'RhodeCode', lastname=u'Admin',
579 active=True, admin=admin, extern_type="rhodecode",
579 active=True, admin=admin, extern_type="rhodecode",
580 strict_creation_check=strict_creation_check)
580 strict_creation_check=strict_creation_check)
581
581
582 if api_key:
582 if api_key:
583 log.info('setting a provided api key for the user %s', username)
583 log.info('setting a provided api key for the user %s', username)
584 from rhodecode.model.auth_token import AuthTokenModel
584 from rhodecode.model.auth_token import AuthTokenModel
585 AuthTokenModel().create(
585 AuthTokenModel().create(
586 user=user, description='BUILTIN TOKEN')
586 user=user, description=u'BUILTIN TOKEN')
587
587
588 def create_default_user(self):
588 def create_default_user(self):
589 log.info('creating default user')
589 log.info('creating default user')
590 # create default user for handling default permissions.
590 # create default user for handling default permissions.
591 user = UserModel().create_or_update(username=User.DEFAULT_USER,
591 user = UserModel().create_or_update(username=User.DEFAULT_USER,
592 password=str(uuid.uuid1())[:20],
592 password=str(uuid.uuid1())[:20],
593 email=User.DEFAULT_USER_EMAIL,
593 email=User.DEFAULT_USER_EMAIL,
594 firstname='Anonymous',
594 firstname=u'Anonymous',
595 lastname='User',
595 lastname=u'User',
596 strict_creation_check=False)
596 strict_creation_check=False)
597 # based on configuration options activate/deactive this user which
597 # based on configuration options activate/deactive this user which
598 # controlls anonymous access
598 # controlls anonymous access
599 if self.cli_args.get('public_access') is False:
599 if self.cli_args.get('public_access') is False:
600 log.info('Public access disabled')
600 log.info('Public access disabled')
601 user.active = False
601 user.active = False
602 Session().add(user)
602 Session().add(user)
603 Session().commit()
603 Session().commit()
604
604
605 def create_permissions(self):
605 def create_permissions(self):
606 """
606 """
607 Creates all permissions defined in the system
607 Creates all permissions defined in the system
608 """
608 """
609 # module.(access|create|change|delete)_[name]
609 # module.(access|create|change|delete)_[name]
610 # module.(none|read|write|admin)
610 # module.(none|read|write|admin)
611 log.info('creating permissions')
611 log.info('creating permissions')
612 PermissionModel(self.sa).create_permissions()
612 PermissionModel(self.sa).create_permissions()
613
613
614 def populate_default_permissions(self):
614 def populate_default_permissions(self):
615 """
615 """
616 Populate default permissions. It will create only the default
616 Populate default permissions. It will create only the default
617 permissions that are missing, and not alter already defined ones
617 permissions that are missing, and not alter already defined ones
618 """
618 """
619 log.info('creating default user permissions')
619 log.info('creating default user permissions')
620 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
620 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
@@ -1,910 +1,910 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 users model for RhodeCode
22 users model for RhodeCode
23 """
23 """
24
24
25 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 import datetime
28 import datetime
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 import ipaddress
31 import ipaddress
32 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.exc import DatabaseError
33
33
34 from rhodecode import events
34 from rhodecode import events
35 from rhodecode.lib.user_log_filter import user_log_filter
35 from rhodecode.lib.user_log_filter import user_log_filter
36 from rhodecode.lib.utils2 import (
36 from rhodecode.lib.utils2 import (
37 safe_unicode, get_current_rhodecode_user, action_logger_generic,
37 safe_unicode, get_current_rhodecode_user, action_logger_generic,
38 AttributeDict, str2bool)
38 AttributeDict, str2bool)
39 from rhodecode.lib.exceptions import (
39 from rhodecode.lib.exceptions import (
40 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
40 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
41 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
42 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.lib.caching_query import FromCache
43 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
44 from rhodecode.model.auth_token import AuthTokenModel
44 from rhodecode.model.auth_token import AuthTokenModel
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 _hash_key, true, false, or_, joinedload, User, UserToPerm,
46 _hash_key, true, false, or_, joinedload, User, UserToPerm,
47 UserEmailMap, UserIpMap, UserLog)
47 UserEmailMap, UserIpMap, UserLog)
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
49 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
50
50
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class UserModel(BaseModel):
55 class UserModel(BaseModel):
56 cls = User
56 cls = User
57
57
58 def get(self, user_id, cache=False):
58 def get(self, user_id, cache=False):
59 user = self.sa.query(User)
59 user = self.sa.query(User)
60 if cache:
60 if cache:
61 user = user.options(
61 user = user.options(
62 FromCache("sql_cache_short", "get_user_%s" % user_id))
62 FromCache("sql_cache_short", "get_user_%s" % user_id))
63 return user.get(user_id)
63 return user.get(user_id)
64
64
65 def get_user(self, user):
65 def get_user(self, user):
66 return self._get_user(user)
66 return self._get_user(user)
67
67
68 def _serialize_user(self, user):
68 def _serialize_user(self, user):
69 import rhodecode.lib.helpers as h
69 import rhodecode.lib.helpers as h
70
70
71 return {
71 return {
72 'id': user.user_id,
72 'id': user.user_id,
73 'first_name': user.first_name,
73 'first_name': user.first_name,
74 'last_name': user.last_name,
74 'last_name': user.last_name,
75 'username': user.username,
75 'username': user.username,
76 'email': user.email,
76 'email': user.email,
77 'icon_link': h.gravatar_url(user.email, 30),
77 'icon_link': h.gravatar_url(user.email, 30),
78 'value_display': h.escape(h.person(user)),
78 'value_display': h.escape(h.person(user)),
79 'value': user.username,
79 'value': user.username,
80 'value_type': 'user',
80 'value_type': 'user',
81 'active': user.active,
81 'active': user.active,
82 }
82 }
83
83
84 def get_users(self, name_contains=None, limit=20, only_active=True):
84 def get_users(self, name_contains=None, limit=20, only_active=True):
85
85
86 query = self.sa.query(User)
86 query = self.sa.query(User)
87 if only_active:
87 if only_active:
88 query = query.filter(User.active == true())
88 query = query.filter(User.active == true())
89
89
90 if name_contains:
90 if name_contains:
91 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
91 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
92 query = query.filter(
92 query = query.filter(
93 or_(
93 or_(
94 User.name.ilike(ilike_expression),
94 User.name.ilike(ilike_expression),
95 User.lastname.ilike(ilike_expression),
95 User.lastname.ilike(ilike_expression),
96 User.username.ilike(ilike_expression)
96 User.username.ilike(ilike_expression)
97 )
97 )
98 )
98 )
99 query = query.limit(limit)
99 query = query.limit(limit)
100 users = query.all()
100 users = query.all()
101
101
102 _users = [
102 _users = [
103 self._serialize_user(user) for user in users
103 self._serialize_user(user) for user in users
104 ]
104 ]
105 return _users
105 return _users
106
106
107 def get_by_username(self, username, cache=False, case_insensitive=False):
107 def get_by_username(self, username, cache=False, case_insensitive=False):
108
108
109 if case_insensitive:
109 if case_insensitive:
110 user = self.sa.query(User).filter(User.username.ilike(username))
110 user = self.sa.query(User).filter(User.username.ilike(username))
111 else:
111 else:
112 user = self.sa.query(User)\
112 user = self.sa.query(User)\
113 .filter(User.username == username)
113 .filter(User.username == username)
114 if cache:
114 if cache:
115 name_key = _hash_key(username)
115 name_key = _hash_key(username)
116 user = user.options(
116 user = user.options(
117 FromCache("sql_cache_short", "get_user_%s" % name_key))
117 FromCache("sql_cache_short", "get_user_%s" % name_key))
118 return user.scalar()
118 return user.scalar()
119
119
120 def get_by_email(self, email, cache=False, case_insensitive=False):
120 def get_by_email(self, email, cache=False, case_insensitive=False):
121 return User.get_by_email(email, case_insensitive, cache)
121 return User.get_by_email(email, case_insensitive, cache)
122
122
123 def get_by_auth_token(self, auth_token, cache=False):
123 def get_by_auth_token(self, auth_token, cache=False):
124 return User.get_by_auth_token(auth_token, cache)
124 return User.get_by_auth_token(auth_token, cache)
125
125
126 def get_active_user_count(self, cache=False):
126 def get_active_user_count(self, cache=False):
127 return User.query().filter(
127 return User.query().filter(
128 User.active == True).filter(
128 User.active == True).filter(
129 User.username != User.DEFAULT_USER).count()
129 User.username != User.DEFAULT_USER).count()
130
130
131 def create(self, form_data, cur_user=None):
131 def create(self, form_data, cur_user=None):
132 if not cur_user:
132 if not cur_user:
133 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
133 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
134
134
135 user_data = {
135 user_data = {
136 'username': form_data['username'],
136 'username': form_data['username'],
137 'password': form_data['password'],
137 'password': form_data['password'],
138 'email': form_data['email'],
138 'email': form_data['email'],
139 'firstname': form_data['firstname'],
139 'firstname': form_data['firstname'],
140 'lastname': form_data['lastname'],
140 'lastname': form_data['lastname'],
141 'active': form_data['active'],
141 'active': form_data['active'],
142 'extern_type': form_data['extern_type'],
142 'extern_type': form_data['extern_type'],
143 'extern_name': form_data['extern_name'],
143 'extern_name': form_data['extern_name'],
144 'admin': False,
144 'admin': False,
145 'cur_user': cur_user
145 'cur_user': cur_user
146 }
146 }
147
147
148 if 'create_repo_group' in form_data:
148 if 'create_repo_group' in form_data:
149 user_data['create_repo_group'] = str2bool(
149 user_data['create_repo_group'] = str2bool(
150 form_data.get('create_repo_group'))
150 form_data.get('create_repo_group'))
151
151
152 try:
152 try:
153 if form_data.get('password_change'):
153 if form_data.get('password_change'):
154 user_data['force_password_change'] = True
154 user_data['force_password_change'] = True
155 return UserModel().create_or_update(**user_data)
155 return UserModel().create_or_update(**user_data)
156 except Exception:
156 except Exception:
157 log.error(traceback.format_exc())
157 log.error(traceback.format_exc())
158 raise
158 raise
159
159
160 def update_user(self, user, skip_attrs=None, **kwargs):
160 def update_user(self, user, skip_attrs=None, **kwargs):
161 from rhodecode.lib.auth import get_crypt_password
161 from rhodecode.lib.auth import get_crypt_password
162
162
163 user = self._get_user(user)
163 user = self._get_user(user)
164 if user.username == User.DEFAULT_USER:
164 if user.username == User.DEFAULT_USER:
165 raise DefaultUserException(
165 raise DefaultUserException(
166 _("You can't Edit this user since it's"
166 _("You can't Edit this user since it's"
167 " crucial for entire application"))
167 " crucial for entire application"))
168
168
169 # first store only defaults
169 # first store only defaults
170 user_attrs = {
170 user_attrs = {
171 'updating_user_id': user.user_id,
171 'updating_user_id': user.user_id,
172 'username': user.username,
172 'username': user.username,
173 'password': user.password,
173 'password': user.password,
174 'email': user.email,
174 'email': user.email,
175 'firstname': user.name,
175 'firstname': user.name,
176 'lastname': user.lastname,
176 'lastname': user.lastname,
177 'active': user.active,
177 'active': user.active,
178 'admin': user.admin,
178 'admin': user.admin,
179 'extern_name': user.extern_name,
179 'extern_name': user.extern_name,
180 'extern_type': user.extern_type,
180 'extern_type': user.extern_type,
181 'language': user.user_data.get('language')
181 'language': user.user_data.get('language')
182 }
182 }
183
183
184 # in case there's new_password, that comes from form, use it to
184 # in case there's new_password, that comes from form, use it to
185 # store password
185 # store password
186 if kwargs.get('new_password'):
186 if kwargs.get('new_password'):
187 kwargs['password'] = kwargs['new_password']
187 kwargs['password'] = kwargs['new_password']
188
188
189 # cleanups, my_account password change form
189 # cleanups, my_account password change form
190 kwargs.pop('current_password', None)
190 kwargs.pop('current_password', None)
191 kwargs.pop('new_password', None)
191 kwargs.pop('new_password', None)
192
192
193 # cleanups, user edit password change form
193 # cleanups, user edit password change form
194 kwargs.pop('password_confirmation', None)
194 kwargs.pop('password_confirmation', None)
195 kwargs.pop('password_change', None)
195 kwargs.pop('password_change', None)
196
196
197 # create repo group on user creation
197 # create repo group on user creation
198 kwargs.pop('create_repo_group', None)
198 kwargs.pop('create_repo_group', None)
199
199
200 # legacy forms send name, which is the firstname
200 # legacy forms send name, which is the firstname
201 firstname = kwargs.pop('name', None)
201 firstname = kwargs.pop('name', None)
202 if firstname:
202 if firstname:
203 kwargs['firstname'] = firstname
203 kwargs['firstname'] = firstname
204
204
205 for k, v in kwargs.items():
205 for k, v in kwargs.items():
206 # skip if we don't want to update this
206 # skip if we don't want to update this
207 if skip_attrs and k in skip_attrs:
207 if skip_attrs and k in skip_attrs:
208 continue
208 continue
209
209
210 user_attrs[k] = v
210 user_attrs[k] = v
211
211
212 try:
212 try:
213 return self.create_or_update(**user_attrs)
213 return self.create_or_update(**user_attrs)
214 except Exception:
214 except Exception:
215 log.error(traceback.format_exc())
215 log.error(traceback.format_exc())
216 raise
216 raise
217
217
218 def create_or_update(
218 def create_or_update(
219 self, username, password, email, firstname='', lastname='',
219 self, username, password, email, firstname='', lastname='',
220 active=True, admin=False, extern_type=None, extern_name=None,
220 active=True, admin=False, extern_type=None, extern_name=None,
221 cur_user=None, plugin=None, force_password_change=False,
221 cur_user=None, plugin=None, force_password_change=False,
222 allow_to_create_user=True, create_repo_group=None,
222 allow_to_create_user=True, create_repo_group=None,
223 updating_user_id=None, language=None, strict_creation_check=True):
223 updating_user_id=None, language=None, strict_creation_check=True):
224 """
224 """
225 Creates a new instance if not found, or updates current one
225 Creates a new instance if not found, or updates current one
226
226
227 :param username:
227 :param username:
228 :param password:
228 :param password:
229 :param email:
229 :param email:
230 :param firstname:
230 :param firstname:
231 :param lastname:
231 :param lastname:
232 :param active:
232 :param active:
233 :param admin:
233 :param admin:
234 :param extern_type:
234 :param extern_type:
235 :param extern_name:
235 :param extern_name:
236 :param cur_user:
236 :param cur_user:
237 :param plugin: optional plugin this method was called from
237 :param plugin: optional plugin this method was called from
238 :param force_password_change: toggles new or existing user flag
238 :param force_password_change: toggles new or existing user flag
239 for password change
239 for password change
240 :param allow_to_create_user: Defines if the method can actually create
240 :param allow_to_create_user: Defines if the method can actually create
241 new users
241 new users
242 :param create_repo_group: Defines if the method should also
242 :param create_repo_group: Defines if the method should also
243 create an repo group with user name, and owner
243 create an repo group with user name, and owner
244 :param updating_user_id: if we set it up this is the user we want to
244 :param updating_user_id: if we set it up this is the user we want to
245 update this allows to editing username.
245 update this allows to editing username.
246 :param language: language of user from interface.
246 :param language: language of user from interface.
247
247
248 :returns: new User object with injected `is_new_user` attribute.
248 :returns: new User object with injected `is_new_user` attribute.
249 """
249 """
250 if not cur_user:
250 if not cur_user:
251 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
251 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
252
252
253 from rhodecode.lib.auth import (
253 from rhodecode.lib.auth import (
254 get_crypt_password, check_password, generate_auth_token)
254 get_crypt_password, check_password, generate_auth_token)
255 from rhodecode.lib.hooks_base import (
255 from rhodecode.lib.hooks_base import (
256 log_create_user, check_allowed_create_user)
256 log_create_user, check_allowed_create_user)
257
257
258 def _password_change(new_user, password):
258 def _password_change(new_user, password):
259 # empty password
259 # empty password
260 if not new_user.password:
260 if not new_user.password:
261 return False
261 return False
262
262
263 # password check is only needed for RhodeCode internal auth calls
263 # password check is only needed for RhodeCode internal auth calls
264 # in case it's a plugin we don't care
264 # in case it's a plugin we don't care
265 if not plugin:
265 if not plugin:
266
266
267 # first check if we gave crypted password back, and if it
267 # first check if we gave crypted password back, and if it
268 # matches it's not password change
268 # matches it's not password change
269 if new_user.password == password:
269 if new_user.password == password:
270 return False
270 return False
271
271
272 password_match = check_password(password, new_user.password)
272 password_match = check_password(password, new_user.password)
273 if not password_match:
273 if not password_match:
274 return True
274 return True
275
275
276 return False
276 return False
277
277
278 # read settings on default personal repo group creation
278 # read settings on default personal repo group creation
279 if create_repo_group is None:
279 if create_repo_group is None:
280 default_create_repo_group = RepoGroupModel()\
280 default_create_repo_group = RepoGroupModel()\
281 .get_default_create_personal_repo_group()
281 .get_default_create_personal_repo_group()
282 create_repo_group = default_create_repo_group
282 create_repo_group = default_create_repo_group
283
283
284 user_data = {
284 user_data = {
285 'username': username,
285 'username': username,
286 'password': password,
286 'password': password,
287 'email': email,
287 'email': email,
288 'firstname': firstname,
288 'firstname': firstname,
289 'lastname': lastname,
289 'lastname': lastname,
290 'active': active,
290 'active': active,
291 'admin': admin
291 'admin': admin
292 }
292 }
293
293
294 if updating_user_id:
294 if updating_user_id:
295 log.debug('Checking for existing account in RhodeCode '
295 log.debug('Checking for existing account in RhodeCode '
296 'database with user_id `%s` ' % (updating_user_id,))
296 'database with user_id `%s` ' % (updating_user_id,))
297 user = User.get(updating_user_id)
297 user = User.get(updating_user_id)
298 else:
298 else:
299 log.debug('Checking for existing account in RhodeCode '
299 log.debug('Checking for existing account in RhodeCode '
300 'database with username `%s` ' % (username,))
300 'database with username `%s` ' % (username,))
301 user = User.get_by_username(username, case_insensitive=True)
301 user = User.get_by_username(username, case_insensitive=True)
302
302
303 if user is None:
303 if user is None:
304 # we check internal flag if this method is actually allowed to
304 # we check internal flag if this method is actually allowed to
305 # create new user
305 # create new user
306 if not allow_to_create_user:
306 if not allow_to_create_user:
307 msg = ('Method wants to create new user, but it is not '
307 msg = ('Method wants to create new user, but it is not '
308 'allowed to do so')
308 'allowed to do so')
309 log.warning(msg)
309 log.warning(msg)
310 raise NotAllowedToCreateUserError(msg)
310 raise NotAllowedToCreateUserError(msg)
311
311
312 log.debug('Creating new user %s', username)
312 log.debug('Creating new user %s', username)
313
313
314 # only if we create user that is active
314 # only if we create user that is active
315 new_active_user = active
315 new_active_user = active
316 if new_active_user and strict_creation_check:
316 if new_active_user and strict_creation_check:
317 # raises UserCreationError if it's not allowed for any reason to
317 # raises UserCreationError if it's not allowed for any reason to
318 # create new active user, this also executes pre-create hooks
318 # create new active user, this also executes pre-create hooks
319 check_allowed_create_user(user_data, cur_user, strict_check=True)
319 check_allowed_create_user(user_data, cur_user, strict_check=True)
320 events.trigger(events.UserPreCreate(user_data))
320 events.trigger(events.UserPreCreate(user_data))
321 new_user = User()
321 new_user = User()
322 edit = False
322 edit = False
323 else:
323 else:
324 log.debug('updating user %s', username)
324 log.debug('updating user %s', username)
325 events.trigger(events.UserPreUpdate(user, user_data))
325 events.trigger(events.UserPreUpdate(user, user_data))
326 new_user = user
326 new_user = user
327 edit = True
327 edit = True
328
328
329 # we're not allowed to edit default user
329 # we're not allowed to edit default user
330 if user.username == User.DEFAULT_USER:
330 if user.username == User.DEFAULT_USER:
331 raise DefaultUserException(
331 raise DefaultUserException(
332 _("You can't edit this user (`%(username)s`) since it's "
332 _("You can't edit this user (`%(username)s`) since it's "
333 "crucial for entire application") % {'username': user.username})
333 "crucial for entire application") % {'username': user.username})
334
334
335 # inject special attribute that will tell us if User is new or old
335 # inject special attribute that will tell us if User is new or old
336 new_user.is_new_user = not edit
336 new_user.is_new_user = not edit
337 # for users that didn's specify auth type, we use RhodeCode built in
337 # for users that didn's specify auth type, we use RhodeCode built in
338 from rhodecode.authentication.plugins import auth_rhodecode
338 from rhodecode.authentication.plugins import auth_rhodecode
339 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
339 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
340 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
340 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
341
341
342 try:
342 try:
343 new_user.username = username
343 new_user.username = username
344 new_user.admin = admin
344 new_user.admin = admin
345 new_user.email = email
345 new_user.email = email
346 new_user.active = active
346 new_user.active = active
347 new_user.extern_name = safe_unicode(extern_name)
347 new_user.extern_name = safe_unicode(extern_name)
348 new_user.extern_type = safe_unicode(extern_type)
348 new_user.extern_type = safe_unicode(extern_type)
349 new_user.name = firstname
349 new_user.name = firstname
350 new_user.lastname = lastname
350 new_user.lastname = lastname
351
351
352 # set password only if creating an user or password is changed
352 # set password only if creating an user or password is changed
353 if not edit or _password_change(new_user, password):
353 if not edit or _password_change(new_user, password):
354 reason = 'new password' if edit else 'new user'
354 reason = 'new password' if edit else 'new user'
355 log.debug('Updating password reason=>%s', reason)
355 log.debug('Updating password reason=>%s', reason)
356 new_user.password = get_crypt_password(password) if password else None
356 new_user.password = get_crypt_password(password) if password else None
357
357
358 if force_password_change:
358 if force_password_change:
359 new_user.update_userdata(force_password_change=True)
359 new_user.update_userdata(force_password_change=True)
360 if language:
360 if language:
361 new_user.update_userdata(language=language)
361 new_user.update_userdata(language=language)
362 new_user.update_userdata(notification_status=True)
362 new_user.update_userdata(notification_status=True)
363
363
364 self.sa.add(new_user)
364 self.sa.add(new_user)
365
365
366 if not edit and create_repo_group:
366 if not edit and create_repo_group:
367 RepoGroupModel().create_personal_repo_group(
367 RepoGroupModel().create_personal_repo_group(
368 new_user, commit_early=False)
368 new_user, commit_early=False)
369
369
370 if not edit:
370 if not edit:
371 # add the RSS token
371 # add the RSS token
372 AuthTokenModel().create(username,
372 AuthTokenModel().create(username,
373 description='Generated feed token',
373 description=u'Generated feed token',
374 role=AuthTokenModel.cls.ROLE_FEED)
374 role=AuthTokenModel.cls.ROLE_FEED)
375 kwargs = new_user.get_dict()
375 kwargs = new_user.get_dict()
376 # backward compat, require api_keys present
376 # backward compat, require api_keys present
377 kwargs['api_keys'] = kwargs['auth_tokens']
377 kwargs['api_keys'] = kwargs['auth_tokens']
378 log_create_user(created_by=cur_user, **kwargs)
378 log_create_user(created_by=cur_user, **kwargs)
379 events.trigger(events.UserPostCreate(user_data))
379 events.trigger(events.UserPostCreate(user_data))
380 return new_user
380 return new_user
381 except (DatabaseError,):
381 except (DatabaseError,):
382 log.error(traceback.format_exc())
382 log.error(traceback.format_exc())
383 raise
383 raise
384
384
385 def create_registration(self, form_data):
385 def create_registration(self, form_data):
386 from rhodecode.model.notification import NotificationModel
386 from rhodecode.model.notification import NotificationModel
387 from rhodecode.model.notification import EmailNotificationModel
387 from rhodecode.model.notification import EmailNotificationModel
388
388
389 try:
389 try:
390 form_data['admin'] = False
390 form_data['admin'] = False
391 form_data['extern_name'] = 'rhodecode'
391 form_data['extern_name'] = 'rhodecode'
392 form_data['extern_type'] = 'rhodecode'
392 form_data['extern_type'] = 'rhodecode'
393 new_user = self.create(form_data)
393 new_user = self.create(form_data)
394
394
395 self.sa.add(new_user)
395 self.sa.add(new_user)
396 self.sa.flush()
396 self.sa.flush()
397
397
398 user_data = new_user.get_dict()
398 user_data = new_user.get_dict()
399 kwargs = {
399 kwargs = {
400 # use SQLALCHEMY safe dump of user data
400 # use SQLALCHEMY safe dump of user data
401 'user': AttributeDict(user_data),
401 'user': AttributeDict(user_data),
402 'date': datetime.datetime.now()
402 'date': datetime.datetime.now()
403 }
403 }
404 notification_type = EmailNotificationModel.TYPE_REGISTRATION
404 notification_type = EmailNotificationModel.TYPE_REGISTRATION
405 # pre-generate the subject for notification itself
405 # pre-generate the subject for notification itself
406 (subject,
406 (subject,
407 _h, _e, # we don't care about those
407 _h, _e, # we don't care about those
408 body_plaintext) = EmailNotificationModel().render_email(
408 body_plaintext) = EmailNotificationModel().render_email(
409 notification_type, **kwargs)
409 notification_type, **kwargs)
410
410
411 # create notification objects, and emails
411 # create notification objects, and emails
412 NotificationModel().create(
412 NotificationModel().create(
413 created_by=new_user,
413 created_by=new_user,
414 notification_subject=subject,
414 notification_subject=subject,
415 notification_body=body_plaintext,
415 notification_body=body_plaintext,
416 notification_type=notification_type,
416 notification_type=notification_type,
417 recipients=None, # all admins
417 recipients=None, # all admins
418 email_kwargs=kwargs,
418 email_kwargs=kwargs,
419 )
419 )
420
420
421 return new_user
421 return new_user
422 except Exception:
422 except Exception:
423 log.error(traceback.format_exc())
423 log.error(traceback.format_exc())
424 raise
424 raise
425
425
426 def _handle_user_repos(self, username, repositories, handle_mode=None):
426 def _handle_user_repos(self, username, repositories, handle_mode=None):
427 _superadmin = self.cls.get_first_super_admin()
427 _superadmin = self.cls.get_first_super_admin()
428 left_overs = True
428 left_overs = True
429
429
430 from rhodecode.model.repo import RepoModel
430 from rhodecode.model.repo import RepoModel
431
431
432 if handle_mode == 'detach':
432 if handle_mode == 'detach':
433 for obj in repositories:
433 for obj in repositories:
434 obj.user = _superadmin
434 obj.user = _superadmin
435 # set description we know why we super admin now owns
435 # set description we know why we super admin now owns
436 # additional repositories that were orphaned !
436 # additional repositories that were orphaned !
437 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
437 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
438 self.sa.add(obj)
438 self.sa.add(obj)
439 left_overs = False
439 left_overs = False
440 elif handle_mode == 'delete':
440 elif handle_mode == 'delete':
441 for obj in repositories:
441 for obj in repositories:
442 RepoModel().delete(obj, forks='detach')
442 RepoModel().delete(obj, forks='detach')
443 left_overs = False
443 left_overs = False
444
444
445 # if nothing is done we have left overs left
445 # if nothing is done we have left overs left
446 return left_overs
446 return left_overs
447
447
448 def _handle_user_repo_groups(self, username, repository_groups,
448 def _handle_user_repo_groups(self, username, repository_groups,
449 handle_mode=None):
449 handle_mode=None):
450 _superadmin = self.cls.get_first_super_admin()
450 _superadmin = self.cls.get_first_super_admin()
451 left_overs = True
451 left_overs = True
452
452
453 from rhodecode.model.repo_group import RepoGroupModel
453 from rhodecode.model.repo_group import RepoGroupModel
454
454
455 if handle_mode == 'detach':
455 if handle_mode == 'detach':
456 for r in repository_groups:
456 for r in repository_groups:
457 r.user = _superadmin
457 r.user = _superadmin
458 # set description we know why we super admin now owns
458 # set description we know why we super admin now owns
459 # additional repositories that were orphaned !
459 # additional repositories that were orphaned !
460 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
460 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
461 self.sa.add(r)
461 self.sa.add(r)
462 left_overs = False
462 left_overs = False
463 elif handle_mode == 'delete':
463 elif handle_mode == 'delete':
464 for r in repository_groups:
464 for r in repository_groups:
465 RepoGroupModel().delete(r)
465 RepoGroupModel().delete(r)
466 left_overs = False
466 left_overs = False
467
467
468 # if nothing is done we have left overs left
468 # if nothing is done we have left overs left
469 return left_overs
469 return left_overs
470
470
471 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
471 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
472 _superadmin = self.cls.get_first_super_admin()
472 _superadmin = self.cls.get_first_super_admin()
473 left_overs = True
473 left_overs = True
474
474
475 from rhodecode.model.user_group import UserGroupModel
475 from rhodecode.model.user_group import UserGroupModel
476
476
477 if handle_mode == 'detach':
477 if handle_mode == 'detach':
478 for r in user_groups:
478 for r in user_groups:
479 for user_user_group_to_perm in r.user_user_group_to_perm:
479 for user_user_group_to_perm in r.user_user_group_to_perm:
480 if user_user_group_to_perm.user.username == username:
480 if user_user_group_to_perm.user.username == username:
481 user_user_group_to_perm.user = _superadmin
481 user_user_group_to_perm.user = _superadmin
482 r.user = _superadmin
482 r.user = _superadmin
483 # set description we know why we super admin now owns
483 # set description we know why we super admin now owns
484 # additional repositories that were orphaned !
484 # additional repositories that were orphaned !
485 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
485 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
486 self.sa.add(r)
486 self.sa.add(r)
487 left_overs = False
487 left_overs = False
488 elif handle_mode == 'delete':
488 elif handle_mode == 'delete':
489 for r in user_groups:
489 for r in user_groups:
490 UserGroupModel().delete(r)
490 UserGroupModel().delete(r)
491 left_overs = False
491 left_overs = False
492
492
493 # if nothing is done we have left overs left
493 # if nothing is done we have left overs left
494 return left_overs
494 return left_overs
495
495
496 def delete(self, user, cur_user=None, handle_repos=None,
496 def delete(self, user, cur_user=None, handle_repos=None,
497 handle_repo_groups=None, handle_user_groups=None):
497 handle_repo_groups=None, handle_user_groups=None):
498 if not cur_user:
498 if not cur_user:
499 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
499 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
500 user = self._get_user(user)
500 user = self._get_user(user)
501
501
502 try:
502 try:
503 if user.username == User.DEFAULT_USER:
503 if user.username == User.DEFAULT_USER:
504 raise DefaultUserException(
504 raise DefaultUserException(
505 _(u"You can't remove this user since it's"
505 _(u"You can't remove this user since it's"
506 u" crucial for entire application"))
506 u" crucial for entire application"))
507
507
508 left_overs = self._handle_user_repos(
508 left_overs = self._handle_user_repos(
509 user.username, user.repositories, handle_repos)
509 user.username, user.repositories, handle_repos)
510 if left_overs and user.repositories:
510 if left_overs and user.repositories:
511 repos = [x.repo_name for x in user.repositories]
511 repos = [x.repo_name for x in user.repositories]
512 raise UserOwnsReposException(
512 raise UserOwnsReposException(
513 _(u'user "%s" still owns %s repositories and cannot be '
513 _(u'user "%s" still owns %s repositories and cannot be '
514 u'removed. Switch owners or remove those repositories:%s')
514 u'removed. Switch owners or remove those repositories:%s')
515 % (user.username, len(repos), ', '.join(repos)))
515 % (user.username, len(repos), ', '.join(repos)))
516
516
517 left_overs = self._handle_user_repo_groups(
517 left_overs = self._handle_user_repo_groups(
518 user.username, user.repository_groups, handle_repo_groups)
518 user.username, user.repository_groups, handle_repo_groups)
519 if left_overs and user.repository_groups:
519 if left_overs and user.repository_groups:
520 repo_groups = [x.group_name for x in user.repository_groups]
520 repo_groups = [x.group_name for x in user.repository_groups]
521 raise UserOwnsRepoGroupsException(
521 raise UserOwnsRepoGroupsException(
522 _(u'user "%s" still owns %s repository groups and cannot be '
522 _(u'user "%s" still owns %s repository groups and cannot be '
523 u'removed. Switch owners or remove those repository groups:%s')
523 u'removed. Switch owners or remove those repository groups:%s')
524 % (user.username, len(repo_groups), ', '.join(repo_groups)))
524 % (user.username, len(repo_groups), ', '.join(repo_groups)))
525
525
526 left_overs = self._handle_user_user_groups(
526 left_overs = self._handle_user_user_groups(
527 user.username, user.user_groups, handle_user_groups)
527 user.username, user.user_groups, handle_user_groups)
528 if left_overs and user.user_groups:
528 if left_overs and user.user_groups:
529 user_groups = [x.users_group_name for x in user.user_groups]
529 user_groups = [x.users_group_name for x in user.user_groups]
530 raise UserOwnsUserGroupsException(
530 raise UserOwnsUserGroupsException(
531 _(u'user "%s" still owns %s user groups and cannot be '
531 _(u'user "%s" still owns %s user groups and cannot be '
532 u'removed. Switch owners or remove those user groups:%s')
532 u'removed. Switch owners or remove those user groups:%s')
533 % (user.username, len(user_groups), ', '.join(user_groups)))
533 % (user.username, len(user_groups), ', '.join(user_groups)))
534
534
535 # we might change the user data with detach/delete, make sure
535 # we might change the user data with detach/delete, make sure
536 # the object is marked as expired before actually deleting !
536 # the object is marked as expired before actually deleting !
537 self.sa.expire(user)
537 self.sa.expire(user)
538 self.sa.delete(user)
538 self.sa.delete(user)
539 from rhodecode.lib.hooks_base import log_delete_user
539 from rhodecode.lib.hooks_base import log_delete_user
540 log_delete_user(deleted_by=cur_user, **user.get_dict())
540 log_delete_user(deleted_by=cur_user, **user.get_dict())
541 except Exception:
541 except Exception:
542 log.error(traceback.format_exc())
542 log.error(traceback.format_exc())
543 raise
543 raise
544
544
545 def reset_password_link(self, data, pwd_reset_url):
545 def reset_password_link(self, data, pwd_reset_url):
546 from rhodecode.lib.celerylib import tasks, run_task
546 from rhodecode.lib.celerylib import tasks, run_task
547 from rhodecode.model.notification import EmailNotificationModel
547 from rhodecode.model.notification import EmailNotificationModel
548 user_email = data['email']
548 user_email = data['email']
549 try:
549 try:
550 user = User.get_by_email(user_email)
550 user = User.get_by_email(user_email)
551 if user:
551 if user:
552 log.debug('password reset user found %s', user)
552 log.debug('password reset user found %s', user)
553
553
554 email_kwargs = {
554 email_kwargs = {
555 'password_reset_url': pwd_reset_url,
555 'password_reset_url': pwd_reset_url,
556 'user': user,
556 'user': user,
557 'email': user_email,
557 'email': user_email,
558 'date': datetime.datetime.now()
558 'date': datetime.datetime.now()
559 }
559 }
560
560
561 (subject, headers, email_body,
561 (subject, headers, email_body,
562 email_body_plaintext) = EmailNotificationModel().render_email(
562 email_body_plaintext) = EmailNotificationModel().render_email(
563 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
563 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
564
564
565 recipients = [user_email]
565 recipients = [user_email]
566
566
567 action_logger_generic(
567 action_logger_generic(
568 'sending password reset email to user: {}'.format(
568 'sending password reset email to user: {}'.format(
569 user), namespace='security.password_reset')
569 user), namespace='security.password_reset')
570
570
571 run_task(tasks.send_email, recipients, subject,
571 run_task(tasks.send_email, recipients, subject,
572 email_body_plaintext, email_body)
572 email_body_plaintext, email_body)
573
573
574 else:
574 else:
575 log.debug("password reset email %s not found", user_email)
575 log.debug("password reset email %s not found", user_email)
576 except Exception:
576 except Exception:
577 log.error(traceback.format_exc())
577 log.error(traceback.format_exc())
578 return False
578 return False
579
579
580 return True
580 return True
581
581
582 def reset_password(self, data):
582 def reset_password(self, data):
583 from rhodecode.lib.celerylib import tasks, run_task
583 from rhodecode.lib.celerylib import tasks, run_task
584 from rhodecode.model.notification import EmailNotificationModel
584 from rhodecode.model.notification import EmailNotificationModel
585 from rhodecode.lib import auth
585 from rhodecode.lib import auth
586 user_email = data['email']
586 user_email = data['email']
587 pre_db = True
587 pre_db = True
588 try:
588 try:
589 user = User.get_by_email(user_email)
589 user = User.get_by_email(user_email)
590 new_passwd = auth.PasswordGenerator().gen_password(
590 new_passwd = auth.PasswordGenerator().gen_password(
591 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
591 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
592 if user:
592 if user:
593 user.password = auth.get_crypt_password(new_passwd)
593 user.password = auth.get_crypt_password(new_passwd)
594 # also force this user to reset his password !
594 # also force this user to reset his password !
595 user.update_userdata(force_password_change=True)
595 user.update_userdata(force_password_change=True)
596
596
597 Session().add(user)
597 Session().add(user)
598
598
599 # now delete the token in question
599 # now delete the token in question
600 UserApiKeys = AuthTokenModel.cls
600 UserApiKeys = AuthTokenModel.cls
601 UserApiKeys().query().filter(
601 UserApiKeys().query().filter(
602 UserApiKeys.api_key == data['token']).delete()
602 UserApiKeys.api_key == data['token']).delete()
603
603
604 Session().commit()
604 Session().commit()
605 log.info('successfully reset password for `%s`', user_email)
605 log.info('successfully reset password for `%s`', user_email)
606
606
607 if new_passwd is None:
607 if new_passwd is None:
608 raise Exception('unable to generate new password')
608 raise Exception('unable to generate new password')
609
609
610 pre_db = False
610 pre_db = False
611
611
612 email_kwargs = {
612 email_kwargs = {
613 'new_password': new_passwd,
613 'new_password': new_passwd,
614 'user': user,
614 'user': user,
615 'email': user_email,
615 'email': user_email,
616 'date': datetime.datetime.now()
616 'date': datetime.datetime.now()
617 }
617 }
618
618
619 (subject, headers, email_body,
619 (subject, headers, email_body,
620 email_body_plaintext) = EmailNotificationModel().render_email(
620 email_body_plaintext) = EmailNotificationModel().render_email(
621 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
621 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
622 **email_kwargs)
622 **email_kwargs)
623
623
624 recipients = [user_email]
624 recipients = [user_email]
625
625
626 action_logger_generic(
626 action_logger_generic(
627 'sent new password to user: {} with email: {}'.format(
627 'sent new password to user: {} with email: {}'.format(
628 user, user_email), namespace='security.password_reset')
628 user, user_email), namespace='security.password_reset')
629
629
630 run_task(tasks.send_email, recipients, subject,
630 run_task(tasks.send_email, recipients, subject,
631 email_body_plaintext, email_body)
631 email_body_plaintext, email_body)
632
632
633 except Exception:
633 except Exception:
634 log.error('Failed to update user password')
634 log.error('Failed to update user password')
635 log.error(traceback.format_exc())
635 log.error(traceback.format_exc())
636 if pre_db:
636 if pre_db:
637 # we rollback only if local db stuff fails. If it goes into
637 # we rollback only if local db stuff fails. If it goes into
638 # run_task, we're pass rollback state this wouldn't work then
638 # run_task, we're pass rollback state this wouldn't work then
639 Session().rollback()
639 Session().rollback()
640
640
641 return True
641 return True
642
642
643 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
643 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
644 """
644 """
645 Fetches auth_user by user_id,or api_key if present.
645 Fetches auth_user by user_id,or api_key if present.
646 Fills auth_user attributes with those taken from database.
646 Fills auth_user attributes with those taken from database.
647 Additionally set's is_authenitated if lookup fails
647 Additionally set's is_authenitated if lookup fails
648 present in database
648 present in database
649
649
650 :param auth_user: instance of user to set attributes
650 :param auth_user: instance of user to set attributes
651 :param user_id: user id to fetch by
651 :param user_id: user id to fetch by
652 :param api_key: api key to fetch by
652 :param api_key: api key to fetch by
653 :param username: username to fetch by
653 :param username: username to fetch by
654 """
654 """
655 if user_id is None and api_key is None and username is None:
655 if user_id is None and api_key is None and username is None:
656 raise Exception('You need to pass user_id, api_key or username')
656 raise Exception('You need to pass user_id, api_key or username')
657
657
658 log.debug(
658 log.debug(
659 'AuthUser: fill data execution based on: '
659 'AuthUser: fill data execution based on: '
660 'user_id:%s api_key:%s username:%s', user_id, api_key, username)
660 'user_id:%s api_key:%s username:%s', user_id, api_key, username)
661 try:
661 try:
662 dbuser = None
662 dbuser = None
663 if user_id:
663 if user_id:
664 dbuser = self.get(user_id)
664 dbuser = self.get(user_id)
665 elif api_key:
665 elif api_key:
666 dbuser = self.get_by_auth_token(api_key)
666 dbuser = self.get_by_auth_token(api_key)
667 elif username:
667 elif username:
668 dbuser = self.get_by_username(username)
668 dbuser = self.get_by_username(username)
669
669
670 if not dbuser:
670 if not dbuser:
671 log.warning(
671 log.warning(
672 'Unable to lookup user by id:%s api_key:%s username:%s',
672 'Unable to lookup user by id:%s api_key:%s username:%s',
673 user_id, api_key, username)
673 user_id, api_key, username)
674 return False
674 return False
675 if not dbuser.active:
675 if not dbuser.active:
676 log.debug('User `%s:%s` is inactive, skipping fill data',
676 log.debug('User `%s:%s` is inactive, skipping fill data',
677 username, user_id)
677 username, user_id)
678 return False
678 return False
679
679
680 log.debug('AuthUser: filling found user:%s data', dbuser)
680 log.debug('AuthUser: filling found user:%s data', dbuser)
681 user_data = dbuser.get_dict()
681 user_data = dbuser.get_dict()
682
682
683 user_data.update({
683 user_data.update({
684 # set explicit the safe escaped values
684 # set explicit the safe escaped values
685 'first_name': dbuser.first_name,
685 'first_name': dbuser.first_name,
686 'last_name': dbuser.last_name,
686 'last_name': dbuser.last_name,
687 })
687 })
688
688
689 for k, v in user_data.items():
689 for k, v in user_data.items():
690 # properties of auth user we dont update
690 # properties of auth user we dont update
691 if k not in ['auth_tokens', 'permissions']:
691 if k not in ['auth_tokens', 'permissions']:
692 setattr(auth_user, k, v)
692 setattr(auth_user, k, v)
693
693
694 # few extras
694 # few extras
695 setattr(auth_user, 'feed_token', dbuser.feed_token)
695 setattr(auth_user, 'feed_token', dbuser.feed_token)
696 except Exception:
696 except Exception:
697 log.error(traceback.format_exc())
697 log.error(traceback.format_exc())
698 auth_user.is_authenticated = False
698 auth_user.is_authenticated = False
699 return False
699 return False
700
700
701 return True
701 return True
702
702
703 def has_perm(self, user, perm):
703 def has_perm(self, user, perm):
704 perm = self._get_perm(perm)
704 perm = self._get_perm(perm)
705 user = self._get_user(user)
705 user = self._get_user(user)
706
706
707 return UserToPerm.query().filter(UserToPerm.user == user)\
707 return UserToPerm.query().filter(UserToPerm.user == user)\
708 .filter(UserToPerm.permission == perm).scalar() is not None
708 .filter(UserToPerm.permission == perm).scalar() is not None
709
709
710 def grant_perm(self, user, perm):
710 def grant_perm(self, user, perm):
711 """
711 """
712 Grant user global permissions
712 Grant user global permissions
713
713
714 :param user:
714 :param user:
715 :param perm:
715 :param perm:
716 """
716 """
717 user = self._get_user(user)
717 user = self._get_user(user)
718 perm = self._get_perm(perm)
718 perm = self._get_perm(perm)
719 # if this permission is already granted skip it
719 # if this permission is already granted skip it
720 _perm = UserToPerm.query()\
720 _perm = UserToPerm.query()\
721 .filter(UserToPerm.user == user)\
721 .filter(UserToPerm.user == user)\
722 .filter(UserToPerm.permission == perm)\
722 .filter(UserToPerm.permission == perm)\
723 .scalar()
723 .scalar()
724 if _perm:
724 if _perm:
725 return
725 return
726 new = UserToPerm()
726 new = UserToPerm()
727 new.user = user
727 new.user = user
728 new.permission = perm
728 new.permission = perm
729 self.sa.add(new)
729 self.sa.add(new)
730 return new
730 return new
731
731
732 def revoke_perm(self, user, perm):
732 def revoke_perm(self, user, perm):
733 """
733 """
734 Revoke users global permissions
734 Revoke users global permissions
735
735
736 :param user:
736 :param user:
737 :param perm:
737 :param perm:
738 """
738 """
739 user = self._get_user(user)
739 user = self._get_user(user)
740 perm = self._get_perm(perm)
740 perm = self._get_perm(perm)
741
741
742 obj = UserToPerm.query()\
742 obj = UserToPerm.query()\
743 .filter(UserToPerm.user == user)\
743 .filter(UserToPerm.user == user)\
744 .filter(UserToPerm.permission == perm)\
744 .filter(UserToPerm.permission == perm)\
745 .scalar()
745 .scalar()
746 if obj:
746 if obj:
747 self.sa.delete(obj)
747 self.sa.delete(obj)
748
748
749 def add_extra_email(self, user, email):
749 def add_extra_email(self, user, email):
750 """
750 """
751 Adds email address to UserEmailMap
751 Adds email address to UserEmailMap
752
752
753 :param user:
753 :param user:
754 :param email:
754 :param email:
755 """
755 """
756 from rhodecode.model import forms
756 from rhodecode.model import forms
757 form = forms.UserExtraEmailForm()()
757 form = forms.UserExtraEmailForm()()
758 data = form.to_python({'email': email})
758 data = form.to_python({'email': email})
759 user = self._get_user(user)
759 user = self._get_user(user)
760
760
761 obj = UserEmailMap()
761 obj = UserEmailMap()
762 obj.user = user
762 obj.user = user
763 obj.email = data['email']
763 obj.email = data['email']
764 self.sa.add(obj)
764 self.sa.add(obj)
765 return obj
765 return obj
766
766
767 def delete_extra_email(self, user, email_id):
767 def delete_extra_email(self, user, email_id):
768 """
768 """
769 Removes email address from UserEmailMap
769 Removes email address from UserEmailMap
770
770
771 :param user:
771 :param user:
772 :param email_id:
772 :param email_id:
773 """
773 """
774 user = self._get_user(user)
774 user = self._get_user(user)
775 obj = UserEmailMap.query().get(email_id)
775 obj = UserEmailMap.query().get(email_id)
776 if obj and obj.user_id == user.user_id:
776 if obj and obj.user_id == user.user_id:
777 self.sa.delete(obj)
777 self.sa.delete(obj)
778
778
779 def parse_ip_range(self, ip_range):
779 def parse_ip_range(self, ip_range):
780 ip_list = []
780 ip_list = []
781
781
782 def make_unique(value):
782 def make_unique(value):
783 seen = []
783 seen = []
784 return [c for c in value if not (c in seen or seen.append(c))]
784 return [c for c in value if not (c in seen or seen.append(c))]
785
785
786 # firsts split by commas
786 # firsts split by commas
787 for ip_range in ip_range.split(','):
787 for ip_range in ip_range.split(','):
788 if not ip_range:
788 if not ip_range:
789 continue
789 continue
790 ip_range = ip_range.strip()
790 ip_range = ip_range.strip()
791 if '-' in ip_range:
791 if '-' in ip_range:
792 start_ip, end_ip = ip_range.split('-', 1)
792 start_ip, end_ip = ip_range.split('-', 1)
793 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
793 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
794 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
794 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
795 parsed_ip_range = []
795 parsed_ip_range = []
796
796
797 for index in xrange(int(start_ip), int(end_ip) + 1):
797 for index in xrange(int(start_ip), int(end_ip) + 1):
798 new_ip = ipaddress.ip_address(index)
798 new_ip = ipaddress.ip_address(index)
799 parsed_ip_range.append(str(new_ip))
799 parsed_ip_range.append(str(new_ip))
800 ip_list.extend(parsed_ip_range)
800 ip_list.extend(parsed_ip_range)
801 else:
801 else:
802 ip_list.append(ip_range)
802 ip_list.append(ip_range)
803
803
804 return make_unique(ip_list)
804 return make_unique(ip_list)
805
805
806 def add_extra_ip(self, user, ip, description=None):
806 def add_extra_ip(self, user, ip, description=None):
807 """
807 """
808 Adds ip address to UserIpMap
808 Adds ip address to UserIpMap
809
809
810 :param user:
810 :param user:
811 :param ip:
811 :param ip:
812 """
812 """
813 from rhodecode.model import forms
813 from rhodecode.model import forms
814 form = forms.UserExtraIpForm()()
814 form = forms.UserExtraIpForm()()
815 data = form.to_python({'ip': ip})
815 data = form.to_python({'ip': ip})
816 user = self._get_user(user)
816 user = self._get_user(user)
817
817
818 obj = UserIpMap()
818 obj = UserIpMap()
819 obj.user = user
819 obj.user = user
820 obj.ip_addr = data['ip']
820 obj.ip_addr = data['ip']
821 obj.description = description
821 obj.description = description
822 self.sa.add(obj)
822 self.sa.add(obj)
823 return obj
823 return obj
824
824
825 def delete_extra_ip(self, user, ip_id):
825 def delete_extra_ip(self, user, ip_id):
826 """
826 """
827 Removes ip address from UserIpMap
827 Removes ip address from UserIpMap
828
828
829 :param user:
829 :param user:
830 :param ip_id:
830 :param ip_id:
831 """
831 """
832 user = self._get_user(user)
832 user = self._get_user(user)
833 obj = UserIpMap.query().get(ip_id)
833 obj = UserIpMap.query().get(ip_id)
834 if obj and obj.user_id == user.user_id:
834 if obj and obj.user_id == user.user_id:
835 self.sa.delete(obj)
835 self.sa.delete(obj)
836
836
837 def get_accounts_in_creation_order(self, current_user=None):
837 def get_accounts_in_creation_order(self, current_user=None):
838 """
838 """
839 Get accounts in order of creation for deactivation for license limits
839 Get accounts in order of creation for deactivation for license limits
840
840
841 pick currently logged in user, and append to the list in position 0
841 pick currently logged in user, and append to the list in position 0
842 pick all super-admins in order of creation date and add it to the list
842 pick all super-admins in order of creation date and add it to the list
843 pick all other accounts in order of creation and add it to the list.
843 pick all other accounts in order of creation and add it to the list.
844
844
845 Based on that list, the last accounts can be disabled as they are
845 Based on that list, the last accounts can be disabled as they are
846 created at the end and don't include any of the super admins as well
846 created at the end and don't include any of the super admins as well
847 as the current user.
847 as the current user.
848
848
849 :param current_user: optionally current user running this operation
849 :param current_user: optionally current user running this operation
850 """
850 """
851
851
852 if not current_user:
852 if not current_user:
853 current_user = get_current_rhodecode_user()
853 current_user = get_current_rhodecode_user()
854 active_super_admins = [
854 active_super_admins = [
855 x.user_id for x in User.query()
855 x.user_id for x in User.query()
856 .filter(User.user_id != current_user.user_id)
856 .filter(User.user_id != current_user.user_id)
857 .filter(User.active == true())
857 .filter(User.active == true())
858 .filter(User.admin == true())
858 .filter(User.admin == true())
859 .order_by(User.created_on.asc())]
859 .order_by(User.created_on.asc())]
860
860
861 active_regular_users = [
861 active_regular_users = [
862 x.user_id for x in User.query()
862 x.user_id for x in User.query()
863 .filter(User.user_id != current_user.user_id)
863 .filter(User.user_id != current_user.user_id)
864 .filter(User.active == true())
864 .filter(User.active == true())
865 .filter(User.admin == false())
865 .filter(User.admin == false())
866 .order_by(User.created_on.asc())]
866 .order_by(User.created_on.asc())]
867
867
868 list_of_accounts = [current_user.user_id]
868 list_of_accounts = [current_user.user_id]
869 list_of_accounts += active_super_admins
869 list_of_accounts += active_super_admins
870 list_of_accounts += active_regular_users
870 list_of_accounts += active_regular_users
871
871
872 return list_of_accounts
872 return list_of_accounts
873
873
874 def deactivate_last_users(self, expected_users, current_user=None):
874 def deactivate_last_users(self, expected_users, current_user=None):
875 """
875 """
876 Deactivate accounts that are over the license limits.
876 Deactivate accounts that are over the license limits.
877 Algorithm of which accounts to disabled is based on the formula:
877 Algorithm of which accounts to disabled is based on the formula:
878
878
879 Get current user, then super admins in creation order, then regular
879 Get current user, then super admins in creation order, then regular
880 active users in creation order.
880 active users in creation order.
881
881
882 Using that list we mark all accounts from the end of it as inactive.
882 Using that list we mark all accounts from the end of it as inactive.
883 This way we block only latest created accounts.
883 This way we block only latest created accounts.
884
884
885 :param expected_users: list of users in special order, we deactivate
885 :param expected_users: list of users in special order, we deactivate
886 the end N ammoun of users from that list
886 the end N ammoun of users from that list
887 """
887 """
888
888
889 list_of_accounts = self.get_accounts_in_creation_order(
889 list_of_accounts = self.get_accounts_in_creation_order(
890 current_user=current_user)
890 current_user=current_user)
891
891
892 for acc_id in list_of_accounts[expected_users + 1:]:
892 for acc_id in list_of_accounts[expected_users + 1:]:
893 user = User.get(acc_id)
893 user = User.get(acc_id)
894 log.info('Deactivating account %s for license unlock', user)
894 log.info('Deactivating account %s for license unlock', user)
895 user.active = False
895 user.active = False
896 Session().add(user)
896 Session().add(user)
897 Session().commit()
897 Session().commit()
898
898
899 return
899 return
900
900
901 def get_user_log(self, user, filter_term):
901 def get_user_log(self, user, filter_term):
902 user_log = UserLog.query()\
902 user_log = UserLog.query()\
903 .filter(or_(UserLog.user_id == user.user_id,
903 .filter(or_(UserLog.user_id == user.user_id,
904 UserLog.username == user.username))\
904 UserLog.username == user.username))\
905 .options(joinedload(UserLog.user))\
905 .options(joinedload(UserLog.user))\
906 .options(joinedload(UserLog.repository))\
906 .options(joinedload(UserLog.repository))\
907 .order_by(UserLog.action_date.desc())
907 .order_by(UserLog.action_date.desc())
908
908
909 user_log = user_log_filter(user_log, filter_term)
909 user_log = user_log_filter(user_log, filter_term)
910 return user_log
910 return user_log
@@ -1,341 +1,341 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Helpers for fixture generation
22 Helpers for fixture generation
23 """
23 """
24
24
25 import os
25 import os
26 import time
26 import time
27 import tempfile
27 import tempfile
28 import shutil
28 import shutil
29
29
30 import configobj
30 import configobj
31
31
32 from rhodecode.tests import *
32 from rhodecode.tests import *
33 from rhodecode.model.db import Repository, User, RepoGroup, UserGroup, Gist
33 from rhodecode.model.db import Repository, User, RepoGroup, UserGroup, Gist
34 from rhodecode.model.meta import Session
34 from rhodecode.model.meta import Session
35 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from rhodecode.model.repo_group import RepoGroupModel
37 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.user_group import UserGroupModel
38 from rhodecode.model.user_group import UserGroupModel
39 from rhodecode.model.gist import GistModel
39 from rhodecode.model.gist import GistModel
40 from rhodecode.model.auth_token import AuthTokenModel
40 from rhodecode.model.auth_token import AuthTokenModel
41
41
42 dn = os.path.dirname
42 dn = os.path.dirname
43 FIXTURES = os.path.join(dn(dn(os.path.abspath(__file__))), 'tests', 'fixtures')
43 FIXTURES = os.path.join(dn(dn(os.path.abspath(__file__))), 'tests', 'fixtures')
44
44
45
45
46 def error_function(*args, **kwargs):
46 def error_function(*args, **kwargs):
47 raise Exception('Total Crash !')
47 raise Exception('Total Crash !')
48
48
49
49
50 class TestINI(object):
50 class TestINI(object):
51 """
51 """
52 Allows to create a new test.ini file as a copy of existing one with edited
52 Allows to create a new test.ini file as a copy of existing one with edited
53 data. Example usage::
53 data. Example usage::
54
54
55 with TestINI('test.ini', [{'section':{'key':val'}]) as new_test_ini_path:
55 with TestINI('test.ini', [{'section':{'key':val'}]) as new_test_ini_path:
56 print 'paster server %s' % new_test_ini
56 print 'paster server %s' % new_test_ini
57 """
57 """
58
58
59 def __init__(self, ini_file_path, ini_params, new_file_prefix='DEFAULT',
59 def __init__(self, ini_file_path, ini_params, new_file_prefix='DEFAULT',
60 destroy=True, dir=None):
60 destroy=True, dir=None):
61 self.ini_file_path = ini_file_path
61 self.ini_file_path = ini_file_path
62 self.ini_params = ini_params
62 self.ini_params = ini_params
63 self.new_path = None
63 self.new_path = None
64 self.new_path_prefix = new_file_prefix
64 self.new_path_prefix = new_file_prefix
65 self._destroy = destroy
65 self._destroy = destroy
66 self._dir = dir
66 self._dir = dir
67
67
68 def __enter__(self):
68 def __enter__(self):
69 return self.create()
69 return self.create()
70
70
71 def __exit__(self, exc_type, exc_val, exc_tb):
71 def __exit__(self, exc_type, exc_val, exc_tb):
72 self.destroy()
72 self.destroy()
73
73
74 def create(self):
74 def create(self):
75 config = configobj.ConfigObj(
75 config = configobj.ConfigObj(
76 self.ini_file_path, file_error=True, write_empty_values=True)
76 self.ini_file_path, file_error=True, write_empty_values=True)
77
77
78 for data in self.ini_params:
78 for data in self.ini_params:
79 section, ini_params = data.items()[0]
79 section, ini_params = data.items()[0]
80 for key, val in ini_params.items():
80 for key, val in ini_params.items():
81 config[section][key] = val
81 config[section][key] = val
82 with tempfile.NamedTemporaryFile(
82 with tempfile.NamedTemporaryFile(
83 prefix=self.new_path_prefix, suffix='.ini', dir=self._dir,
83 prefix=self.new_path_prefix, suffix='.ini', dir=self._dir,
84 delete=False) as new_ini_file:
84 delete=False) as new_ini_file:
85 config.write(new_ini_file)
85 config.write(new_ini_file)
86 self.new_path = new_ini_file.name
86 self.new_path = new_ini_file.name
87
87
88 return self.new_path
88 return self.new_path
89
89
90 def destroy(self):
90 def destroy(self):
91 if self._destroy:
91 if self._destroy:
92 os.remove(self.new_path)
92 os.remove(self.new_path)
93
93
94
94
95 class Fixture(object):
95 class Fixture(object):
96
96
97 def anon_access(self, status):
97 def anon_access(self, status):
98 """
98 """
99 Context process for disabling anonymous access. use like:
99 Context process for disabling anonymous access. use like:
100 fixture = Fixture()
100 fixture = Fixture()
101 with fixture.anon_access(False):
101 with fixture.anon_access(False):
102 #tests
102 #tests
103
103
104 after this block anon access will be set to `not status`
104 after this block anon access will be set to `not status`
105 """
105 """
106
106
107 class context(object):
107 class context(object):
108 def __enter__(self):
108 def __enter__(self):
109 anon = User.get_default_user()
109 anon = User.get_default_user()
110 anon.active = status
110 anon.active = status
111 Session().add(anon)
111 Session().add(anon)
112 Session().commit()
112 Session().commit()
113 time.sleep(1.5) # must sleep for cache (1s to expire)
113 time.sleep(1.5) # must sleep for cache (1s to expire)
114
114
115 def __exit__(self, exc_type, exc_val, exc_tb):
115 def __exit__(self, exc_type, exc_val, exc_tb):
116 anon = User.get_default_user()
116 anon = User.get_default_user()
117 anon.active = not status
117 anon.active = not status
118 Session().add(anon)
118 Session().add(anon)
119 Session().commit()
119 Session().commit()
120
120
121 return context()
121 return context()
122
122
123 def _get_repo_create_params(self, **custom):
123 def _get_repo_create_params(self, **custom):
124 defs = {
124 defs = {
125 'repo_name': None,
125 'repo_name': None,
126 'repo_type': 'hg',
126 'repo_type': 'hg',
127 'clone_uri': '',
127 'clone_uri': '',
128 'repo_group': '-1',
128 'repo_group': '-1',
129 'repo_description': 'DESC',
129 'repo_description': 'DESC',
130 'repo_private': False,
130 'repo_private': False,
131 'repo_landing_rev': 'rev:tip',
131 'repo_landing_rev': 'rev:tip',
132 'repo_copy_permissions': False,
132 'repo_copy_permissions': False,
133 'repo_state': Repository.STATE_CREATED,
133 'repo_state': Repository.STATE_CREATED,
134 }
134 }
135 defs.update(custom)
135 defs.update(custom)
136 if 'repo_name_full' not in custom:
136 if 'repo_name_full' not in custom:
137 defs.update({'repo_name_full': defs['repo_name']})
137 defs.update({'repo_name_full': defs['repo_name']})
138
138
139 # fix the repo name if passed as repo_name_full
139 # fix the repo name if passed as repo_name_full
140 if defs['repo_name']:
140 if defs['repo_name']:
141 defs['repo_name'] = defs['repo_name'].split('/')[-1]
141 defs['repo_name'] = defs['repo_name'].split('/')[-1]
142
142
143 return defs
143 return defs
144
144
145 def _get_group_create_params(self, **custom):
145 def _get_group_create_params(self, **custom):
146 defs = {
146 defs = {
147 'group_name': None,
147 'group_name': None,
148 'group_description': 'DESC',
148 'group_description': 'DESC',
149 'perm_updates': [],
149 'perm_updates': [],
150 'perm_additions': [],
150 'perm_additions': [],
151 'perm_deletions': [],
151 'perm_deletions': [],
152 'group_parent_id': -1,
152 'group_parent_id': -1,
153 'enable_locking': False,
153 'enable_locking': False,
154 'recursive': False,
154 'recursive': False,
155 }
155 }
156 defs.update(custom)
156 defs.update(custom)
157
157
158 return defs
158 return defs
159
159
160 def _get_user_create_params(self, name, **custom):
160 def _get_user_create_params(self, name, **custom):
161 defs = {
161 defs = {
162 'username': name,
162 'username': name,
163 'password': 'qweqwe',
163 'password': 'qweqwe',
164 'email': '%s+test@rhodecode.org' % name,
164 'email': '%s+test@rhodecode.org' % name,
165 'firstname': 'TestUser',
165 'firstname': 'TestUser',
166 'lastname': 'Test',
166 'lastname': 'Test',
167 'active': True,
167 'active': True,
168 'admin': False,
168 'admin': False,
169 'extern_type': 'rhodecode',
169 'extern_type': 'rhodecode',
170 'extern_name': None,
170 'extern_name': None,
171 }
171 }
172 defs.update(custom)
172 defs.update(custom)
173
173
174 return defs
174 return defs
175
175
176 def _get_user_group_create_params(self, name, **custom):
176 def _get_user_group_create_params(self, name, **custom):
177 defs = {
177 defs = {
178 'users_group_name': name,
178 'users_group_name': name,
179 'user_group_description': 'DESC',
179 'user_group_description': 'DESC',
180 'users_group_active': True,
180 'users_group_active': True,
181 'user_group_data': {},
181 'user_group_data': {},
182 }
182 }
183 defs.update(custom)
183 defs.update(custom)
184
184
185 return defs
185 return defs
186
186
187 def create_repo(self, name, **kwargs):
187 def create_repo(self, name, **kwargs):
188 repo_group = kwargs.get('repo_group')
188 repo_group = kwargs.get('repo_group')
189 if isinstance(repo_group, RepoGroup):
189 if isinstance(repo_group, RepoGroup):
190 kwargs['repo_group'] = repo_group.group_id
190 kwargs['repo_group'] = repo_group.group_id
191 name = name.split(Repository.NAME_SEP)[-1]
191 name = name.split(Repository.NAME_SEP)[-1]
192 name = Repository.NAME_SEP.join((repo_group.group_name, name))
192 name = Repository.NAME_SEP.join((repo_group.group_name, name))
193
193
194 if 'skip_if_exists' in kwargs:
194 if 'skip_if_exists' in kwargs:
195 del kwargs['skip_if_exists']
195 del kwargs['skip_if_exists']
196 r = Repository.get_by_repo_name(name)
196 r = Repository.get_by_repo_name(name)
197 if r:
197 if r:
198 return r
198 return r
199
199
200 form_data = self._get_repo_create_params(repo_name=name, **kwargs)
200 form_data = self._get_repo_create_params(repo_name=name, **kwargs)
201 cur_user = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
201 cur_user = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
202 RepoModel().create(form_data, cur_user)
202 RepoModel().create(form_data, cur_user)
203 Session().commit()
203 Session().commit()
204 repo = Repository.get_by_repo_name(name)
204 repo = Repository.get_by_repo_name(name)
205 assert repo
205 assert repo
206 return repo
206 return repo
207
207
208 def create_fork(self, repo_to_fork, fork_name, **kwargs):
208 def create_fork(self, repo_to_fork, fork_name, **kwargs):
209 repo_to_fork = Repository.get_by_repo_name(repo_to_fork)
209 repo_to_fork = Repository.get_by_repo_name(repo_to_fork)
210
210
211 form_data = self._get_repo_create_params(repo_name=fork_name,
211 form_data = self._get_repo_create_params(repo_name=fork_name,
212 fork_parent_id=repo_to_fork.repo_id,
212 fork_parent_id=repo_to_fork.repo_id,
213 repo_type=repo_to_fork.repo_type,
213 repo_type=repo_to_fork.repo_type,
214 **kwargs)
214 **kwargs)
215 #TODO: fix it !!
215 #TODO: fix it !!
216 form_data['description'] = form_data['repo_description']
216 form_data['description'] = form_data['repo_description']
217 form_data['private'] = form_data['repo_private']
217 form_data['private'] = form_data['repo_private']
218 form_data['landing_rev'] = form_data['repo_landing_rev']
218 form_data['landing_rev'] = form_data['repo_landing_rev']
219
219
220 owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
220 owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
221 RepoModel().create_fork(form_data, cur_user=owner)
221 RepoModel().create_fork(form_data, cur_user=owner)
222 Session().commit()
222 Session().commit()
223 r = Repository.get_by_repo_name(fork_name)
223 r = Repository.get_by_repo_name(fork_name)
224 assert r
224 assert r
225 return r
225 return r
226
226
227 def destroy_repo(self, repo_name, **kwargs):
227 def destroy_repo(self, repo_name, **kwargs):
228 RepoModel().delete(repo_name, **kwargs)
228 RepoModel().delete(repo_name, **kwargs)
229 Session().commit()
229 Session().commit()
230
230
231 def destroy_repo_on_filesystem(self, repo_name):
231 def destroy_repo_on_filesystem(self, repo_name):
232 rm_path = os.path.join(RepoModel().repos_path, repo_name)
232 rm_path = os.path.join(RepoModel().repos_path, repo_name)
233 if os.path.isdir(rm_path):
233 if os.path.isdir(rm_path):
234 shutil.rmtree(rm_path)
234 shutil.rmtree(rm_path)
235
235
236 def create_repo_group(self, name, **kwargs):
236 def create_repo_group(self, name, **kwargs):
237 if 'skip_if_exists' in kwargs:
237 if 'skip_if_exists' in kwargs:
238 del kwargs['skip_if_exists']
238 del kwargs['skip_if_exists']
239 gr = RepoGroup.get_by_group_name(group_name=name)
239 gr = RepoGroup.get_by_group_name(group_name=name)
240 if gr:
240 if gr:
241 return gr
241 return gr
242 form_data = self._get_group_create_params(group_name=name, **kwargs)
242 form_data = self._get_group_create_params(group_name=name, **kwargs)
243 owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
243 owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
244 gr = RepoGroupModel().create(
244 gr = RepoGroupModel().create(
245 group_name=form_data['group_name'],
245 group_name=form_data['group_name'],
246 group_description=form_data['group_name'],
246 group_description=form_data['group_name'],
247 owner=owner)
247 owner=owner)
248 Session().commit()
248 Session().commit()
249 gr = RepoGroup.get_by_group_name(gr.group_name)
249 gr = RepoGroup.get_by_group_name(gr.group_name)
250 return gr
250 return gr
251
251
252 def destroy_repo_group(self, repogroupid):
252 def destroy_repo_group(self, repogroupid):
253 RepoGroupModel().delete(repogroupid)
253 RepoGroupModel().delete(repogroupid)
254 Session().commit()
254 Session().commit()
255
255
256 def create_user(self, name, **kwargs):
256 def create_user(self, name, **kwargs):
257 if 'skip_if_exists' in kwargs:
257 if 'skip_if_exists' in kwargs:
258 del kwargs['skip_if_exists']
258 del kwargs['skip_if_exists']
259 user = User.get_by_username(name)
259 user = User.get_by_username(name)
260 if user:
260 if user:
261 return user
261 return user
262 form_data = self._get_user_create_params(name, **kwargs)
262 form_data = self._get_user_create_params(name, **kwargs)
263 user = UserModel().create(form_data)
263 user = UserModel().create(form_data)
264
264
265 # create token for user
265 # create token for user
266 AuthTokenModel().create(
266 AuthTokenModel().create(
267 user=user, description='TEST_USER_TOKEN')
267 user=user, description=u'TEST_USER_TOKEN')
268
268
269 Session().commit()
269 Session().commit()
270 user = User.get_by_username(user.username)
270 user = User.get_by_username(user.username)
271 return user
271 return user
272
272
273 def destroy_user(self, userid):
273 def destroy_user(self, userid):
274 UserModel().delete(userid)
274 UserModel().delete(userid)
275 Session().commit()
275 Session().commit()
276
276
277 def destroy_users(self, userid_iter):
277 def destroy_users(self, userid_iter):
278 for user_id in userid_iter:
278 for user_id in userid_iter:
279 if User.get_by_username(user_id):
279 if User.get_by_username(user_id):
280 UserModel().delete(user_id)
280 UserModel().delete(user_id)
281 Session().commit()
281 Session().commit()
282
282
283 def create_user_group(self, name, **kwargs):
283 def create_user_group(self, name, **kwargs):
284 if 'skip_if_exists' in kwargs:
284 if 'skip_if_exists' in kwargs:
285 del kwargs['skip_if_exists']
285 del kwargs['skip_if_exists']
286 gr = UserGroup.get_by_group_name(group_name=name)
286 gr = UserGroup.get_by_group_name(group_name=name)
287 if gr:
287 if gr:
288 return gr
288 return gr
289 # map active flag to the real attribute. For API consistency of fixtures
289 # map active flag to the real attribute. For API consistency of fixtures
290 if 'active' in kwargs:
290 if 'active' in kwargs:
291 kwargs['users_group_active'] = kwargs['active']
291 kwargs['users_group_active'] = kwargs['active']
292 del kwargs['active']
292 del kwargs['active']
293 form_data = self._get_user_group_create_params(name, **kwargs)
293 form_data = self._get_user_group_create_params(name, **kwargs)
294 owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
294 owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN)
295 user_group = UserGroupModel().create(
295 user_group = UserGroupModel().create(
296 name=form_data['users_group_name'],
296 name=form_data['users_group_name'],
297 description=form_data['user_group_description'],
297 description=form_data['user_group_description'],
298 owner=owner, active=form_data['users_group_active'],
298 owner=owner, active=form_data['users_group_active'],
299 group_data=form_data['user_group_data'])
299 group_data=form_data['user_group_data'])
300 Session().commit()
300 Session().commit()
301 user_group = UserGroup.get_by_group_name(user_group.users_group_name)
301 user_group = UserGroup.get_by_group_name(user_group.users_group_name)
302 return user_group
302 return user_group
303
303
304 def destroy_user_group(self, usergroupid):
304 def destroy_user_group(self, usergroupid):
305 UserGroupModel().delete(user_group=usergroupid, force=True)
305 UserGroupModel().delete(user_group=usergroupid, force=True)
306 Session().commit()
306 Session().commit()
307
307
308 def create_gist(self, **kwargs):
308 def create_gist(self, **kwargs):
309 form_data = {
309 form_data = {
310 'description': 'new-gist',
310 'description': 'new-gist',
311 'owner': TEST_USER_ADMIN_LOGIN,
311 'owner': TEST_USER_ADMIN_LOGIN,
312 'gist_type': GistModel.cls.GIST_PUBLIC,
312 'gist_type': GistModel.cls.GIST_PUBLIC,
313 'lifetime': -1,
313 'lifetime': -1,
314 'acl_level': Gist.ACL_LEVEL_PUBLIC,
314 'acl_level': Gist.ACL_LEVEL_PUBLIC,
315 'gist_mapping': {'filename1.txt': {'content': 'hello world'},}
315 'gist_mapping': {'filename1.txt': {'content': 'hello world'},}
316 }
316 }
317 form_data.update(kwargs)
317 form_data.update(kwargs)
318 gist = GistModel().create(
318 gist = GistModel().create(
319 description=form_data['description'], owner=form_data['owner'],
319 description=form_data['description'], owner=form_data['owner'],
320 gist_mapping=form_data['gist_mapping'], gist_type=form_data['gist_type'],
320 gist_mapping=form_data['gist_mapping'], gist_type=form_data['gist_type'],
321 lifetime=form_data['lifetime'], gist_acl_level=form_data['acl_level']
321 lifetime=form_data['lifetime'], gist_acl_level=form_data['acl_level']
322 )
322 )
323 Session().commit()
323 Session().commit()
324 return gist
324 return gist
325
325
326 def destroy_gists(self, gistid=None):
326 def destroy_gists(self, gistid=None):
327 for g in GistModel.cls.get_all():
327 for g in GistModel.cls.get_all():
328 if gistid:
328 if gistid:
329 if gistid == g.gist_access_id:
329 if gistid == g.gist_access_id:
330 GistModel().delete(g)
330 GistModel().delete(g)
331 else:
331 else:
332 GistModel().delete(g)
332 GistModel().delete(g)
333 Session().commit()
333 Session().commit()
334
334
335 def load_resource(self, resource_name, strip=False):
335 def load_resource(self, resource_name, strip=False):
336 with open(os.path.join(FIXTURES, resource_name)) as f:
336 with open(os.path.join(FIXTURES, resource_name)) as f:
337 source = f.read()
337 source = f.read()
338 if strip:
338 if strip:
339 source = source.strip()
339 source = source.strip()
340
340
341 return source
341 return source
General Comments 0
You need to be logged in to leave comments. Login now