##// END OF EJS Templates
caches: cleanup code...
super-admin -
r5009:4102d6ca default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,1197 +1,1197 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 os
21 import os
22 import re
22 import re
23 import shutil
23 import shutil
24 import time
24 import time
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import datetime
27 import datetime
28
28
29 from pyramid.threadlocal import get_current_request
29 from pyramid.threadlocal import get_current_request
30 from zope.cachedescriptors.property import Lazy as LazyProperty
30 from zope.cachedescriptors.property import Lazy as LazyProperty
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.lib.auth import HasUserGroupPermissionAny
33 from rhodecode.lib.auth import HasUserGroupPermissionAny
34 from rhodecode.lib.caching_query import FromCache
34 from rhodecode.lib.caching_query import FromCache
35 from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError
35 from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError
36 from rhodecode.lib import hooks_base
36 from rhodecode.lib import hooks_base
37 from rhodecode.lib.user_log_filter import user_log_filter
37 from rhodecode.lib.user_log_filter import user_log_filter
38 from rhodecode.lib.utils import make_db_config
38 from rhodecode.lib.utils import make_db_config
39 from rhodecode.lib.utils2 import (
39 from rhodecode.lib.utils2 import (
40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
41 get_current_rhodecode_user, safe_int, action_logger_generic)
41 get_current_rhodecode_user, safe_int, action_logger_generic)
42 from rhodecode.lib.vcs.backends import get_backend
42 from rhodecode.lib.vcs.backends import get_backend
43 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 _hash_key, func, case, joinedload, or_, in_filter_generator,
45 _hash_key, func, case, joinedload, or_, in_filter_generator,
46 Session, Repository, UserRepoToPerm, UserGroupRepoToPerm,
46 Session, Repository, UserRepoToPerm, UserGroupRepoToPerm,
47 UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission,
47 UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission,
48 Statistics, UserGroup, RepoGroup, RepositoryField, UserLog)
48 Statistics, UserGroup, RepoGroup, RepositoryField, UserLog)
49 from rhodecode.model.permission import PermissionModel
49 from rhodecode.model.permission import PermissionModel
50 from rhodecode.model.settings import VcsSettingsModel
50 from rhodecode.model.settings import VcsSettingsModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class RepoModel(BaseModel):
55 class RepoModel(BaseModel):
56
56
57 cls = Repository
57 cls = Repository
58
58
59 def _get_user_group(self, users_group):
59 def _get_user_group(self, users_group):
60 return self._get_instance(UserGroup, users_group,
60 return self._get_instance(UserGroup, users_group,
61 callback=UserGroup.get_by_group_name)
61 callback=UserGroup.get_by_group_name)
62
62
63 def _get_repo_group(self, repo_group):
63 def _get_repo_group(self, repo_group):
64 return self._get_instance(RepoGroup, repo_group,
64 return self._get_instance(RepoGroup, repo_group,
65 callback=RepoGroup.get_by_group_name)
65 callback=RepoGroup.get_by_group_name)
66
66
67 def _create_default_perms(self, repository, private):
67 def _create_default_perms(self, repository, private):
68 # create default permission
68 # create default permission
69 default = 'repository.read'
69 default = 'repository.read'
70 def_user = User.get_default_user()
70 def_user = User.get_default_user()
71 for p in def_user.user_perms:
71 for p in def_user.user_perms:
72 if p.permission.permission_name.startswith('repository.'):
72 if p.permission.permission_name.startswith('repository.'):
73 default = p.permission.permission_name
73 default = p.permission.permission_name
74 break
74 break
75
75
76 default_perm = 'repository.none' if private else default
76 default_perm = 'repository.none' if private else default
77
77
78 repo_to_perm = UserRepoToPerm()
78 repo_to_perm = UserRepoToPerm()
79 repo_to_perm.permission = Permission.get_by_key(default_perm)
79 repo_to_perm.permission = Permission.get_by_key(default_perm)
80
80
81 repo_to_perm.repository = repository
81 repo_to_perm.repository = repository
82 repo_to_perm.user_id = def_user.user_id
82 repo_to_perm.user_id = def_user.user_id
83
83
84 return repo_to_perm
84 return repo_to_perm
85
85
86 @LazyProperty
86 @LazyProperty
87 def repos_path(self):
87 def repos_path(self):
88 """
88 """
89 Gets the repositories root path from database
89 Gets the repositories root path from database
90 """
90 """
91 settings_model = VcsSettingsModel(sa=self.sa)
91 settings_model = VcsSettingsModel(sa=self.sa)
92 return settings_model.get_repos_location()
92 return settings_model.get_repos_location()
93
93
94 def get(self, repo_id):
94 def get(self, repo_id):
95 repo = self.sa.query(Repository) \
95 repo = self.sa.query(Repository) \
96 .filter(Repository.repo_id == repo_id)
96 .filter(Repository.repo_id == repo_id)
97
97
98 return repo.scalar()
98 return repo.scalar()
99
99
100 def get_repo(self, repository):
100 def get_repo(self, repository):
101 return self._get_repo(repository)
101 return self._get_repo(repository)
102
102
103 def get_by_repo_name(self, repo_name, cache=False):
103 def get_by_repo_name(self, repo_name, cache=False):
104 repo = self.sa.query(Repository) \
104 repo = self.sa.query(Repository) \
105 .filter(Repository.repo_name == repo_name)
105 .filter(Repository.repo_name == repo_name)
106
106
107 if cache:
107 if cache:
108 name_key = _hash_key(repo_name)
108 name_key = _hash_key(repo_name)
109 repo = repo.options(
109 repo = repo.options(
110 FromCache("sql_cache_short", "get_repo_%s" % name_key))
110 FromCache("sql_cache_short", f"get_repo_{name_key}"))
111 return repo.scalar()
111 return repo.scalar()
112
112
113 def _extract_id_from_repo_name(self, repo_name):
113 def _extract_id_from_repo_name(self, repo_name):
114 if repo_name.startswith('/'):
114 if repo_name.startswith('/'):
115 repo_name = repo_name.lstrip('/')
115 repo_name = repo_name.lstrip('/')
116 by_id_match = re.match(r'^_(\d{1,})', repo_name)
116 by_id_match = re.match(r'^_(\d{1,})', repo_name)
117 if by_id_match:
117 if by_id_match:
118 return by_id_match.groups()[0]
118 return by_id_match.groups()[0]
119
119
120 def get_repo_by_id(self, repo_name):
120 def get_repo_by_id(self, repo_name):
121 """
121 """
122 Extracts repo_name by id from special urls.
122 Extracts repo_name by id from special urls.
123 Example url is _11/repo_name
123 Example url is _11/repo_name
124
124
125 :param repo_name:
125 :param repo_name:
126 :return: repo object if matched else None
126 :return: repo object if matched else None
127 """
127 """
128 _repo_id = None
128 _repo_id = None
129 try:
129 try:
130 _repo_id = self._extract_id_from_repo_name(repo_name)
130 _repo_id = self._extract_id_from_repo_name(repo_name)
131 if _repo_id:
131 if _repo_id:
132 return self.get(_repo_id)
132 return self.get(_repo_id)
133 except Exception:
133 except Exception:
134 log.exception('Failed to extract repo_name from URL')
134 log.exception('Failed to extract repo_name from URL')
135 if _repo_id:
135 if _repo_id:
136 Session().rollback()
136 Session().rollback()
137
137
138 return None
138 return None
139
139
140 def get_repos_for_root(self, root, traverse=False):
140 def get_repos_for_root(self, root, traverse=False):
141 if traverse:
141 if traverse:
142 like_expression = u'{}%'.format(safe_unicode(root))
142 like_expression = u'{}%'.format(safe_unicode(root))
143 repos = Repository.query().filter(
143 repos = Repository.query().filter(
144 Repository.repo_name.like(like_expression)).all()
144 Repository.repo_name.like(like_expression)).all()
145 else:
145 else:
146 if root and not isinstance(root, RepoGroup):
146 if root and not isinstance(root, RepoGroup):
147 raise ValueError(
147 raise ValueError(
148 'Root must be an instance '
148 'Root must be an instance '
149 'of RepoGroup, got:{} instead'.format(type(root)))
149 'of RepoGroup, got:{} instead'.format(type(root)))
150 repos = Repository.query().filter(Repository.group == root).all()
150 repos = Repository.query().filter(Repository.group == root).all()
151 return repos
151 return repos
152
152
153 def get_url(self, repo, request=None, permalink=False):
153 def get_url(self, repo, request=None, permalink=False):
154 if not request:
154 if not request:
155 request = get_current_request()
155 request = get_current_request()
156
156
157 if not request:
157 if not request:
158 return
158 return
159
159
160 if permalink:
160 if permalink:
161 return request.route_url(
161 return request.route_url(
162 'repo_summary', repo_name='_{}'.format(safe_str(repo.repo_id)))
162 'repo_summary', repo_name='_{}'.format(safe_str(repo.repo_id)))
163 else:
163 else:
164 return request.route_url(
164 return request.route_url(
165 'repo_summary', repo_name=safe_str(repo.repo_name))
165 'repo_summary', repo_name=safe_str(repo.repo_name))
166
166
167 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
167 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
168 if not request:
168 if not request:
169 request = get_current_request()
169 request = get_current_request()
170
170
171 if not request:
171 if not request:
172 return
172 return
173
173
174 if permalink:
174 if permalink:
175 return request.route_url(
175 return request.route_url(
176 'repo_commit', repo_name=safe_str(repo.repo_id),
176 'repo_commit', repo_name=safe_str(repo.repo_id),
177 commit_id=commit_id)
177 commit_id=commit_id)
178
178
179 else:
179 else:
180 return request.route_url(
180 return request.route_url(
181 'repo_commit', repo_name=safe_str(repo.repo_name),
181 'repo_commit', repo_name=safe_str(repo.repo_name),
182 commit_id=commit_id)
182 commit_id=commit_id)
183
183
184 def get_repo_log(self, repo, filter_term):
184 def get_repo_log(self, repo, filter_term):
185 repo_log = UserLog.query()\
185 repo_log = UserLog.query()\
186 .filter(or_(UserLog.repository_id == repo.repo_id,
186 .filter(or_(UserLog.repository_id == repo.repo_id,
187 UserLog.repository_name == repo.repo_name))\
187 UserLog.repository_name == repo.repo_name))\
188 .options(joinedload(UserLog.user))\
188 .options(joinedload(UserLog.user))\
189 .options(joinedload(UserLog.repository))\
189 .options(joinedload(UserLog.repository))\
190 .order_by(UserLog.action_date.desc())
190 .order_by(UserLog.action_date.desc())
191
191
192 repo_log = user_log_filter(repo_log, filter_term)
192 repo_log = user_log_filter(repo_log, filter_term)
193 return repo_log
193 return repo_log
194
194
195 @classmethod
195 @classmethod
196 def update_commit_cache(cls, repositories=None):
196 def update_commit_cache(cls, repositories=None):
197 if not repositories:
197 if not repositories:
198 repositories = Repository.getAll()
198 repositories = Repository.getAll()
199 for repo in repositories:
199 for repo in repositories:
200 repo.update_commit_cache()
200 repo.update_commit_cache()
201
201
202 def get_repos_as_dict(self, repo_list=None, admin=False,
202 def get_repos_as_dict(self, repo_list=None, admin=False,
203 super_user_actions=False, short_name=None):
203 super_user_actions=False, short_name=None):
204
204
205 _render = get_current_request().get_partial_renderer(
205 _render = get_current_request().get_partial_renderer(
206 'rhodecode:templates/data_table/_dt_elements.mako')
206 'rhodecode:templates/data_table/_dt_elements.mako')
207 c = _render.get_call_context()
207 c = _render.get_call_context()
208 h = _render.get_helpers()
208 h = _render.get_helpers()
209
209
210 def quick_menu(repo_name):
210 def quick_menu(repo_name):
211 return _render('quick_menu', repo_name)
211 return _render('quick_menu', repo_name)
212
212
213 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
213 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
214 if short_name is not None:
214 if short_name is not None:
215 short_name_var = short_name
215 short_name_var = short_name
216 else:
216 else:
217 short_name_var = not admin
217 short_name_var = not admin
218 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
218 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
219 short_name=short_name_var, admin=False)
219 short_name=short_name_var, admin=False)
220
220
221 def last_change(last_change):
221 def last_change(last_change):
222 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
222 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
223 ts = time.time()
223 ts = time.time()
224 utc_offset = (datetime.datetime.fromtimestamp(ts)
224 utc_offset = (datetime.datetime.fromtimestamp(ts)
225 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
225 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
226 last_change = last_change + datetime.timedelta(seconds=utc_offset)
226 last_change = last_change + datetime.timedelta(seconds=utc_offset)
227
227
228 return _render("last_change", last_change)
228 return _render("last_change", last_change)
229
229
230 def rss_lnk(repo_name):
230 def rss_lnk(repo_name):
231 return _render("rss", repo_name)
231 return _render("rss", repo_name)
232
232
233 def atom_lnk(repo_name):
233 def atom_lnk(repo_name):
234 return _render("atom", repo_name)
234 return _render("atom", repo_name)
235
235
236 def last_rev(repo_name, cs_cache):
236 def last_rev(repo_name, cs_cache):
237 return _render('revision', repo_name, cs_cache.get('revision'),
237 return _render('revision', repo_name, cs_cache.get('revision'),
238 cs_cache.get('raw_id'), cs_cache.get('author'),
238 cs_cache.get('raw_id'), cs_cache.get('author'),
239 cs_cache.get('message'), cs_cache.get('date'))
239 cs_cache.get('message'), cs_cache.get('date'))
240
240
241 def desc(desc):
241 def desc(desc):
242 return _render('repo_desc', desc, c.visual.stylify_metatags)
242 return _render('repo_desc', desc, c.visual.stylify_metatags)
243
243
244 def state(repo_state):
244 def state(repo_state):
245 return _render("repo_state", repo_state)
245 return _render("repo_state", repo_state)
246
246
247 def repo_actions(repo_name):
247 def repo_actions(repo_name):
248 return _render('repo_actions', repo_name, super_user_actions)
248 return _render('repo_actions', repo_name, super_user_actions)
249
249
250 def user_profile(username):
250 def user_profile(username):
251 return _render('user_profile', username)
251 return _render('user_profile', username)
252
252
253 repos_data = []
253 repos_data = []
254 for repo in repo_list:
254 for repo in repo_list:
255 # NOTE(marcink): because we use only raw column we need to load it like that
255 # NOTE(marcink): because we use only raw column we need to load it like that
256 changeset_cache = Repository._load_changeset_cache(
256 changeset_cache = Repository._load_changeset_cache(
257 repo.repo_id, repo._changeset_cache)
257 repo.repo_id, repo._changeset_cache)
258
258
259 row = {
259 row = {
260 "menu": quick_menu(repo.repo_name),
260 "menu": quick_menu(repo.repo_name),
261
261
262 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
262 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
263 repo.private, repo.archived, repo.fork),
263 repo.private, repo.archived, repo.fork),
264
264
265 "desc": desc(h.escape(repo.description)),
265 "desc": desc(h.escape(repo.description)),
266
266
267 "last_change": last_change(repo.updated_on),
267 "last_change": last_change(repo.updated_on),
268
268
269 "last_changeset": last_rev(repo.repo_name, changeset_cache),
269 "last_changeset": last_rev(repo.repo_name, changeset_cache),
270 "last_changeset_raw": changeset_cache.get('revision'),
270 "last_changeset_raw": changeset_cache.get('revision'),
271
271
272 "owner": user_profile(repo.User.username),
272 "owner": user_profile(repo.User.username),
273
273
274 "state": state(repo.repo_state),
274 "state": state(repo.repo_state),
275 "rss": rss_lnk(repo.repo_name),
275 "rss": rss_lnk(repo.repo_name),
276 "atom": atom_lnk(repo.repo_name),
276 "atom": atom_lnk(repo.repo_name),
277 }
277 }
278 if admin:
278 if admin:
279 row.update({
279 row.update({
280 "action": repo_actions(repo.repo_name),
280 "action": repo_actions(repo.repo_name),
281 })
281 })
282 repos_data.append(row)
282 repos_data.append(row)
283
283
284 return repos_data
284 return repos_data
285
285
286 def get_repos_data_table(
286 def get_repos_data_table(
287 self, draw, start, limit,
287 self, draw, start, limit,
288 search_q, order_by, order_dir,
288 search_q, order_by, order_dir,
289 auth_user, repo_group_id):
289 auth_user, repo_group_id):
290 from rhodecode.model.scm import RepoList
290 from rhodecode.model.scm import RepoList
291
291
292 _perms = ['repository.read', 'repository.write', 'repository.admin']
292 _perms = ['repository.read', 'repository.write', 'repository.admin']
293
293
294 repos = Repository.query() \
294 repos = Repository.query() \
295 .filter(Repository.group_id == repo_group_id) \
295 .filter(Repository.group_id == repo_group_id) \
296 .all()
296 .all()
297 auth_repo_list = RepoList(
297 auth_repo_list = RepoList(
298 repos, perm_set=_perms,
298 repos, perm_set=_perms,
299 extra_kwargs=dict(user=auth_user))
299 extra_kwargs=dict(user=auth_user))
300
300
301 allowed_ids = [-1]
301 allowed_ids = [-1]
302 for repo in auth_repo_list:
302 for repo in auth_repo_list:
303 allowed_ids.append(repo.repo_id)
303 allowed_ids.append(repo.repo_id)
304
304
305 repos_data_total_count = Repository.query() \
305 repos_data_total_count = Repository.query() \
306 .filter(Repository.group_id == repo_group_id) \
306 .filter(Repository.group_id == repo_group_id) \
307 .filter(or_(
307 .filter(or_(
308 # generate multiple IN to fix limitation problems
308 # generate multiple IN to fix limitation problems
309 *in_filter_generator(Repository.repo_id, allowed_ids))
309 *in_filter_generator(Repository.repo_id, allowed_ids))
310 ) \
310 ) \
311 .count()
311 .count()
312
312
313 base_q = Session.query(
313 base_q = Session.query(
314 Repository.repo_id,
314 Repository.repo_id,
315 Repository.repo_name,
315 Repository.repo_name,
316 Repository.description,
316 Repository.description,
317 Repository.repo_type,
317 Repository.repo_type,
318 Repository.repo_state,
318 Repository.repo_state,
319 Repository.private,
319 Repository.private,
320 Repository.archived,
320 Repository.archived,
321 Repository.fork,
321 Repository.fork,
322 Repository.updated_on,
322 Repository.updated_on,
323 Repository._changeset_cache,
323 Repository._changeset_cache,
324 User,
324 User,
325 ) \
325 ) \
326 .filter(Repository.group_id == repo_group_id) \
326 .filter(Repository.group_id == repo_group_id) \
327 .filter(or_(
327 .filter(or_(
328 # generate multiple IN to fix limitation problems
328 # generate multiple IN to fix limitation problems
329 *in_filter_generator(Repository.repo_id, allowed_ids))
329 *in_filter_generator(Repository.repo_id, allowed_ids))
330 ) \
330 ) \
331 .join(User, User.user_id == Repository.user_id) \
331 .join(User, User.user_id == Repository.user_id) \
332 .group_by(Repository, User)
332 .group_by(Repository, User)
333
333
334 repos_data_total_filtered_count = base_q.count()
334 repos_data_total_filtered_count = base_q.count()
335
335
336 sort_defined = False
336 sort_defined = False
337 if order_by == 'repo_name':
337 if order_by == 'repo_name':
338 sort_col = func.lower(Repository.repo_name)
338 sort_col = func.lower(Repository.repo_name)
339 sort_defined = True
339 sort_defined = True
340 elif order_by == 'user_username':
340 elif order_by == 'user_username':
341 sort_col = User.username
341 sort_col = User.username
342 else:
342 else:
343 sort_col = getattr(Repository, order_by, None)
343 sort_col = getattr(Repository, order_by, None)
344
344
345 if sort_defined or sort_col:
345 if sort_defined or sort_col:
346 if order_dir == 'asc':
346 if order_dir == 'asc':
347 sort_col = sort_col.asc()
347 sort_col = sort_col.asc()
348 else:
348 else:
349 sort_col = sort_col.desc()
349 sort_col = sort_col.desc()
350
350
351 base_q = base_q.order_by(sort_col)
351 base_q = base_q.order_by(sort_col)
352 base_q = base_q.offset(start).limit(limit)
352 base_q = base_q.offset(start).limit(limit)
353
353
354 repos_list = base_q.all()
354 repos_list = base_q.all()
355
355
356 repos_data = RepoModel().get_repos_as_dict(
356 repos_data = RepoModel().get_repos_as_dict(
357 repo_list=repos_list, admin=False)
357 repo_list=repos_list, admin=False)
358
358
359 data = ({
359 data = ({
360 'draw': draw,
360 'draw': draw,
361 'data': repos_data,
361 'data': repos_data,
362 'recordsTotal': repos_data_total_count,
362 'recordsTotal': repos_data_total_count,
363 'recordsFiltered': repos_data_total_filtered_count,
363 'recordsFiltered': repos_data_total_filtered_count,
364 })
364 })
365 return data
365 return data
366
366
367 def _get_defaults(self, repo_name):
367 def _get_defaults(self, repo_name):
368 """
368 """
369 Gets information about repository, and returns a dict for
369 Gets information about repository, and returns a dict for
370 usage in forms
370 usage in forms
371
371
372 :param repo_name:
372 :param repo_name:
373 """
373 """
374
374
375 repo_info = Repository.get_by_repo_name(repo_name)
375 repo_info = Repository.get_by_repo_name(repo_name)
376
376
377 if repo_info is None:
377 if repo_info is None:
378 return None
378 return None
379
379
380 defaults = repo_info.get_dict()
380 defaults = repo_info.get_dict()
381 defaults['repo_name'] = repo_info.just_name
381 defaults['repo_name'] = repo_info.just_name
382
382
383 groups = repo_info.groups_with_parents
383 groups = repo_info.groups_with_parents
384 parent_group = groups[-1] if groups else None
384 parent_group = groups[-1] if groups else None
385
385
386 # we use -1 as this is how in HTML, we mark an empty group
386 # we use -1 as this is how in HTML, we mark an empty group
387 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
387 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
388
388
389 keys_to_process = (
389 keys_to_process = (
390 {'k': 'repo_type', 'strip': False},
390 {'k': 'repo_type', 'strip': False},
391 {'k': 'repo_enable_downloads', 'strip': True},
391 {'k': 'repo_enable_downloads', 'strip': True},
392 {'k': 'repo_description', 'strip': True},
392 {'k': 'repo_description', 'strip': True},
393 {'k': 'repo_enable_locking', 'strip': True},
393 {'k': 'repo_enable_locking', 'strip': True},
394 {'k': 'repo_landing_rev', 'strip': True},
394 {'k': 'repo_landing_rev', 'strip': True},
395 {'k': 'clone_uri', 'strip': False},
395 {'k': 'clone_uri', 'strip': False},
396 {'k': 'push_uri', 'strip': False},
396 {'k': 'push_uri', 'strip': False},
397 {'k': 'repo_private', 'strip': True},
397 {'k': 'repo_private', 'strip': True},
398 {'k': 'repo_enable_statistics', 'strip': True}
398 {'k': 'repo_enable_statistics', 'strip': True}
399 )
399 )
400
400
401 for item in keys_to_process:
401 for item in keys_to_process:
402 attr = item['k']
402 attr = item['k']
403 if item['strip']:
403 if item['strip']:
404 attr = remove_prefix(item['k'], 'repo_')
404 attr = remove_prefix(item['k'], 'repo_')
405
405
406 val = defaults[attr]
406 val = defaults[attr]
407 if item['k'] == 'repo_landing_rev':
407 if item['k'] == 'repo_landing_rev':
408 val = ':'.join(defaults[attr])
408 val = ':'.join(defaults[attr])
409 defaults[item['k']] = val
409 defaults[item['k']] = val
410 if item['k'] == 'clone_uri':
410 if item['k'] == 'clone_uri':
411 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
411 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
412 if item['k'] == 'push_uri':
412 if item['k'] == 'push_uri':
413 defaults['push_uri_hidden'] = repo_info.push_uri_hidden
413 defaults['push_uri_hidden'] = repo_info.push_uri_hidden
414
414
415 # fill owner
415 # fill owner
416 if repo_info.user:
416 if repo_info.user:
417 defaults.update({'user': repo_info.user.username})
417 defaults.update({'user': repo_info.user.username})
418 else:
418 else:
419 replacement_user = User.get_first_super_admin().username
419 replacement_user = User.get_first_super_admin().username
420 defaults.update({'user': replacement_user})
420 defaults.update({'user': replacement_user})
421
421
422 return defaults
422 return defaults
423
423
424 def update(self, repo, **kwargs):
424 def update(self, repo, **kwargs):
425 try:
425 try:
426 cur_repo = self._get_repo(repo)
426 cur_repo = self._get_repo(repo)
427 source_repo_name = cur_repo.repo_name
427 source_repo_name = cur_repo.repo_name
428
428
429 affected_user_ids = []
429 affected_user_ids = []
430 if 'user' in kwargs:
430 if 'user' in kwargs:
431 old_owner_id = cur_repo.user.user_id
431 old_owner_id = cur_repo.user.user_id
432 new_owner = User.get_by_username(kwargs['user'])
432 new_owner = User.get_by_username(kwargs['user'])
433 cur_repo.user = new_owner
433 cur_repo.user = new_owner
434
434
435 if old_owner_id != new_owner.user_id:
435 if old_owner_id != new_owner.user_id:
436 affected_user_ids = [new_owner.user_id, old_owner_id]
436 affected_user_ids = [new_owner.user_id, old_owner_id]
437
437
438 if 'repo_group' in kwargs:
438 if 'repo_group' in kwargs:
439 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
439 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
440 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
440 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
441
441
442 update_keys = [
442 update_keys = [
443 (1, 'repo_description'),
443 (1, 'repo_description'),
444 (1, 'repo_landing_rev'),
444 (1, 'repo_landing_rev'),
445 (1, 'repo_private'),
445 (1, 'repo_private'),
446 (1, 'repo_enable_downloads'),
446 (1, 'repo_enable_downloads'),
447 (1, 'repo_enable_locking'),
447 (1, 'repo_enable_locking'),
448 (1, 'repo_enable_statistics'),
448 (1, 'repo_enable_statistics'),
449 (0, 'clone_uri'),
449 (0, 'clone_uri'),
450 (0, 'push_uri'),
450 (0, 'push_uri'),
451 (0, 'fork_id')
451 (0, 'fork_id')
452 ]
452 ]
453 for strip, k in update_keys:
453 for strip, k in update_keys:
454 if k in kwargs:
454 if k in kwargs:
455 val = kwargs[k]
455 val = kwargs[k]
456 if strip:
456 if strip:
457 k = remove_prefix(k, 'repo_')
457 k = remove_prefix(k, 'repo_')
458
458
459 setattr(cur_repo, k, val)
459 setattr(cur_repo, k, val)
460
460
461 new_name = cur_repo.get_new_name(kwargs['repo_name'])
461 new_name = cur_repo.get_new_name(kwargs['repo_name'])
462 cur_repo.repo_name = new_name
462 cur_repo.repo_name = new_name
463
463
464 # if private flag is set, reset default permission to NONE
464 # if private flag is set, reset default permission to NONE
465 if kwargs.get('repo_private'):
465 if kwargs.get('repo_private'):
466 EMPTY_PERM = 'repository.none'
466 EMPTY_PERM = 'repository.none'
467 RepoModel().grant_user_permission(
467 RepoModel().grant_user_permission(
468 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
468 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
469 )
469 )
470 if kwargs.get('repo_landing_rev'):
470 if kwargs.get('repo_landing_rev'):
471 landing_rev_val = kwargs['repo_landing_rev']
471 landing_rev_val = kwargs['repo_landing_rev']
472 RepoModel().set_landing_rev(cur_repo, landing_rev_val)
472 RepoModel().set_landing_rev(cur_repo, landing_rev_val)
473
473
474 # handle extra fields
474 # handle extra fields
475 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
475 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
476 k = RepositoryField.un_prefix_key(field)
476 k = RepositoryField.un_prefix_key(field)
477 ex_field = RepositoryField.get_by_key_name(
477 ex_field = RepositoryField.get_by_key_name(
478 key=k, repo=cur_repo)
478 key=k, repo=cur_repo)
479 if ex_field:
479 if ex_field:
480 ex_field.field_value = kwargs[field]
480 ex_field.field_value = kwargs[field]
481 self.sa.add(ex_field)
481 self.sa.add(ex_field)
482
482
483 self.sa.add(cur_repo)
483 self.sa.add(cur_repo)
484
484
485 if source_repo_name != new_name:
485 if source_repo_name != new_name:
486 # rename repository
486 # rename repository
487 self._rename_filesystem_repo(
487 self._rename_filesystem_repo(
488 old=source_repo_name, new=new_name)
488 old=source_repo_name, new=new_name)
489
489
490 if affected_user_ids:
490 if affected_user_ids:
491 PermissionModel().trigger_permission_flush(affected_user_ids)
491 PermissionModel().trigger_permission_flush(affected_user_ids)
492
492
493 return cur_repo
493 return cur_repo
494 except Exception:
494 except Exception:
495 log.error(traceback.format_exc())
495 log.error(traceback.format_exc())
496 raise
496 raise
497
497
498 def _create_repo(self, repo_name, repo_type, description, owner,
498 def _create_repo(self, repo_name, repo_type, description, owner,
499 private=False, clone_uri=None, repo_group=None,
499 private=False, clone_uri=None, repo_group=None,
500 landing_rev=None, fork_of=None,
500 landing_rev=None, fork_of=None,
501 copy_fork_permissions=False, enable_statistics=False,
501 copy_fork_permissions=False, enable_statistics=False,
502 enable_locking=False, enable_downloads=False,
502 enable_locking=False, enable_downloads=False,
503 copy_group_permissions=False,
503 copy_group_permissions=False,
504 state=Repository.STATE_PENDING):
504 state=Repository.STATE_PENDING):
505 """
505 """
506 Create repository inside database with PENDING state, this should be
506 Create repository inside database with PENDING state, this should be
507 only executed by create() repo. With exception of importing existing
507 only executed by create() repo. With exception of importing existing
508 repos
508 repos
509 """
509 """
510 from rhodecode.model.scm import ScmModel
510 from rhodecode.model.scm import ScmModel
511
511
512 owner = self._get_user(owner)
512 owner = self._get_user(owner)
513 fork_of = self._get_repo(fork_of)
513 fork_of = self._get_repo(fork_of)
514 repo_group = self._get_repo_group(safe_int(repo_group))
514 repo_group = self._get_repo_group(safe_int(repo_group))
515 default_landing_ref, _lbl = ScmModel.backend_landing_ref(repo_type)
515 default_landing_ref, _lbl = ScmModel.backend_landing_ref(repo_type)
516 landing_rev = landing_rev or default_landing_ref
516 landing_rev = landing_rev or default_landing_ref
517
517
518 try:
518 try:
519 repo_name = safe_unicode(repo_name)
519 repo_name = safe_unicode(repo_name)
520 description = safe_unicode(description)
520 description = safe_unicode(description)
521 # repo name is just a name of repository
521 # repo name is just a name of repository
522 # while repo_name_full is a full qualified name that is combined
522 # while repo_name_full is a full qualified name that is combined
523 # with name and path of group
523 # with name and path of group
524 repo_name_full = repo_name
524 repo_name_full = repo_name
525 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
525 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
526
526
527 new_repo = Repository()
527 new_repo = Repository()
528 new_repo.repo_state = state
528 new_repo.repo_state = state
529 new_repo.enable_statistics = False
529 new_repo.enable_statistics = False
530 new_repo.repo_name = repo_name_full
530 new_repo.repo_name = repo_name_full
531 new_repo.repo_type = repo_type
531 new_repo.repo_type = repo_type
532 new_repo.user = owner
532 new_repo.user = owner
533 new_repo.group = repo_group
533 new_repo.group = repo_group
534 new_repo.description = description or repo_name
534 new_repo.description = description or repo_name
535 new_repo.private = private
535 new_repo.private = private
536 new_repo.archived = False
536 new_repo.archived = False
537 new_repo.clone_uri = clone_uri
537 new_repo.clone_uri = clone_uri
538 new_repo.landing_rev = landing_rev
538 new_repo.landing_rev = landing_rev
539
539
540 new_repo.enable_statistics = enable_statistics
540 new_repo.enable_statistics = enable_statistics
541 new_repo.enable_locking = enable_locking
541 new_repo.enable_locking = enable_locking
542 new_repo.enable_downloads = enable_downloads
542 new_repo.enable_downloads = enable_downloads
543
543
544 if repo_group:
544 if repo_group:
545 new_repo.enable_locking = repo_group.enable_locking
545 new_repo.enable_locking = repo_group.enable_locking
546
546
547 if fork_of:
547 if fork_of:
548 parent_repo = fork_of
548 parent_repo = fork_of
549 new_repo.fork = parent_repo
549 new_repo.fork = parent_repo
550
550
551 events.trigger(events.RepoPreCreateEvent(new_repo))
551 events.trigger(events.RepoPreCreateEvent(new_repo))
552
552
553 self.sa.add(new_repo)
553 self.sa.add(new_repo)
554
554
555 EMPTY_PERM = 'repository.none'
555 EMPTY_PERM = 'repository.none'
556 if fork_of and copy_fork_permissions:
556 if fork_of and copy_fork_permissions:
557 repo = fork_of
557 repo = fork_of
558 user_perms = UserRepoToPerm.query() \
558 user_perms = UserRepoToPerm.query() \
559 .filter(UserRepoToPerm.repository == repo).all()
559 .filter(UserRepoToPerm.repository == repo).all()
560 group_perms = UserGroupRepoToPerm.query() \
560 group_perms = UserGroupRepoToPerm.query() \
561 .filter(UserGroupRepoToPerm.repository == repo).all()
561 .filter(UserGroupRepoToPerm.repository == repo).all()
562
562
563 for perm in user_perms:
563 for perm in user_perms:
564 UserRepoToPerm.create(
564 UserRepoToPerm.create(
565 perm.user, new_repo, perm.permission)
565 perm.user, new_repo, perm.permission)
566
566
567 for perm in group_perms:
567 for perm in group_perms:
568 UserGroupRepoToPerm.create(
568 UserGroupRepoToPerm.create(
569 perm.users_group, new_repo, perm.permission)
569 perm.users_group, new_repo, perm.permission)
570 # in case we copy permissions and also set this repo to private
570 # in case we copy permissions and also set this repo to private
571 # override the default user permission to make it a private repo
571 # override the default user permission to make it a private repo
572 if private:
572 if private:
573 RepoModel(self.sa).grant_user_permission(
573 RepoModel(self.sa).grant_user_permission(
574 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
574 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
575
575
576 elif repo_group and copy_group_permissions:
576 elif repo_group and copy_group_permissions:
577 user_perms = UserRepoGroupToPerm.query() \
577 user_perms = UserRepoGroupToPerm.query() \
578 .filter(UserRepoGroupToPerm.group == repo_group).all()
578 .filter(UserRepoGroupToPerm.group == repo_group).all()
579
579
580 group_perms = UserGroupRepoGroupToPerm.query() \
580 group_perms = UserGroupRepoGroupToPerm.query() \
581 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
581 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
582
582
583 for perm in user_perms:
583 for perm in user_perms:
584 perm_name = perm.permission.permission_name.replace(
584 perm_name = perm.permission.permission_name.replace(
585 'group.', 'repository.')
585 'group.', 'repository.')
586 perm_obj = Permission.get_by_key(perm_name)
586 perm_obj = Permission.get_by_key(perm_name)
587 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
587 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
588
588
589 for perm in group_perms:
589 for perm in group_perms:
590 perm_name = perm.permission.permission_name.replace(
590 perm_name = perm.permission.permission_name.replace(
591 'group.', 'repository.')
591 'group.', 'repository.')
592 perm_obj = Permission.get_by_key(perm_name)
592 perm_obj = Permission.get_by_key(perm_name)
593 UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
593 UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
594
594
595 if private:
595 if private:
596 RepoModel(self.sa).grant_user_permission(
596 RepoModel(self.sa).grant_user_permission(
597 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
597 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
598
598
599 else:
599 else:
600 perm_obj = self._create_default_perms(new_repo, private)
600 perm_obj = self._create_default_perms(new_repo, private)
601 self.sa.add(perm_obj)
601 self.sa.add(perm_obj)
602
602
603 # now automatically start following this repository as owner
603 # now automatically start following this repository as owner
604 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, owner.user_id)
604 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, owner.user_id)
605
605
606 # we need to flush here, in order to check if database won't
606 # we need to flush here, in order to check if database won't
607 # throw any exceptions, create filesystem dirs at the very end
607 # throw any exceptions, create filesystem dirs at the very end
608 self.sa.flush()
608 self.sa.flush()
609 events.trigger(events.RepoCreateEvent(new_repo))
609 events.trigger(events.RepoCreateEvent(new_repo))
610 return new_repo
610 return new_repo
611
611
612 except Exception:
612 except Exception:
613 log.error(traceback.format_exc())
613 log.error(traceback.format_exc())
614 raise
614 raise
615
615
616 def create(self, form_data, cur_user):
616 def create(self, form_data, cur_user):
617 """
617 """
618 Create repository using celery tasks
618 Create repository using celery tasks
619
619
620 :param form_data:
620 :param form_data:
621 :param cur_user:
621 :param cur_user:
622 """
622 """
623 from rhodecode.lib.celerylib import tasks, run_task
623 from rhodecode.lib.celerylib import tasks, run_task
624 return run_task(tasks.create_repo, form_data, cur_user)
624 return run_task(tasks.create_repo, form_data, cur_user)
625
625
626 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
626 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
627 perm_deletions=None, check_perms=True,
627 perm_deletions=None, check_perms=True,
628 cur_user=None):
628 cur_user=None):
629 if not perm_additions:
629 if not perm_additions:
630 perm_additions = []
630 perm_additions = []
631 if not perm_updates:
631 if not perm_updates:
632 perm_updates = []
632 perm_updates = []
633 if not perm_deletions:
633 if not perm_deletions:
634 perm_deletions = []
634 perm_deletions = []
635
635
636 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
636 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
637
637
638 changes = {
638 changes = {
639 'added': [],
639 'added': [],
640 'updated': [],
640 'updated': [],
641 'deleted': [],
641 'deleted': [],
642 'default_user_changed': None
642 'default_user_changed': None
643 }
643 }
644
644
645 repo = self._get_repo(repo)
645 repo = self._get_repo(repo)
646
646
647 # update permissions
647 # update permissions
648 for member_id, perm, member_type in perm_updates:
648 for member_id, perm, member_type in perm_updates:
649 member_id = int(member_id)
649 member_id = int(member_id)
650 if member_type == 'user':
650 if member_type == 'user':
651 member_name = User.get(member_id).username
651 member_name = User.get(member_id).username
652 if member_name == User.DEFAULT_USER:
652 if member_name == User.DEFAULT_USER:
653 # NOTE(dan): detect if we changed permissions for default user
653 # NOTE(dan): detect if we changed permissions for default user
654 perm_obj = self.sa.query(UserRepoToPerm) \
654 perm_obj = self.sa.query(UserRepoToPerm) \
655 .filter(UserRepoToPerm.user_id == member_id) \
655 .filter(UserRepoToPerm.user_id == member_id) \
656 .filter(UserRepoToPerm.repository == repo) \
656 .filter(UserRepoToPerm.repository == repo) \
657 .scalar()
657 .scalar()
658 if perm_obj and perm_obj.permission.permission_name != perm:
658 if perm_obj and perm_obj.permission.permission_name != perm:
659 changes['default_user_changed'] = True
659 changes['default_user_changed'] = True
660
660
661 # this updates also current one if found
661 # this updates also current one if found
662 self.grant_user_permission(
662 self.grant_user_permission(
663 repo=repo, user=member_id, perm=perm)
663 repo=repo, user=member_id, perm=perm)
664 elif member_type == 'user_group':
664 elif member_type == 'user_group':
665 # check if we have permissions to alter this usergroup
665 # check if we have permissions to alter this usergroup
666 member_name = UserGroup.get(member_id).users_group_name
666 member_name = UserGroup.get(member_id).users_group_name
667 if not check_perms or HasUserGroupPermissionAny(
667 if not check_perms or HasUserGroupPermissionAny(
668 *req_perms)(member_name, user=cur_user):
668 *req_perms)(member_name, user=cur_user):
669 self.grant_user_group_permission(
669 self.grant_user_group_permission(
670 repo=repo, group_name=member_id, perm=perm)
670 repo=repo, group_name=member_id, perm=perm)
671 else:
671 else:
672 raise ValueError("member_type must be 'user' or 'user_group' "
672 raise ValueError("member_type must be 'user' or 'user_group' "
673 "got {} instead".format(member_type))
673 "got {} instead".format(member_type))
674 changes['updated'].append({'type': member_type, 'id': member_id,
674 changes['updated'].append({'type': member_type, 'id': member_id,
675 'name': member_name, 'new_perm': perm})
675 'name': member_name, 'new_perm': perm})
676
676
677 # set new permissions
677 # set new permissions
678 for member_id, perm, member_type in perm_additions:
678 for member_id, perm, member_type in perm_additions:
679 member_id = int(member_id)
679 member_id = int(member_id)
680 if member_type == 'user':
680 if member_type == 'user':
681 member_name = User.get(member_id).username
681 member_name = User.get(member_id).username
682 self.grant_user_permission(
682 self.grant_user_permission(
683 repo=repo, user=member_id, perm=perm)
683 repo=repo, user=member_id, perm=perm)
684 elif member_type == 'user_group':
684 elif member_type == 'user_group':
685 # check if we have permissions to alter this usergroup
685 # check if we have permissions to alter this usergroup
686 member_name = UserGroup.get(member_id).users_group_name
686 member_name = UserGroup.get(member_id).users_group_name
687 if not check_perms or HasUserGroupPermissionAny(
687 if not check_perms or HasUserGroupPermissionAny(
688 *req_perms)(member_name, user=cur_user):
688 *req_perms)(member_name, user=cur_user):
689 self.grant_user_group_permission(
689 self.grant_user_group_permission(
690 repo=repo, group_name=member_id, perm=perm)
690 repo=repo, group_name=member_id, perm=perm)
691 else:
691 else:
692 raise ValueError("member_type must be 'user' or 'user_group' "
692 raise ValueError("member_type must be 'user' or 'user_group' "
693 "got {} instead".format(member_type))
693 "got {} instead".format(member_type))
694
694
695 changes['added'].append({'type': member_type, 'id': member_id,
695 changes['added'].append({'type': member_type, 'id': member_id,
696 'name': member_name, 'new_perm': perm})
696 'name': member_name, 'new_perm': perm})
697 # delete permissions
697 # delete permissions
698 for member_id, perm, member_type in perm_deletions:
698 for member_id, perm, member_type in perm_deletions:
699 member_id = int(member_id)
699 member_id = int(member_id)
700 if member_type == 'user':
700 if member_type == 'user':
701 member_name = User.get(member_id).username
701 member_name = User.get(member_id).username
702 self.revoke_user_permission(repo=repo, user=member_id)
702 self.revoke_user_permission(repo=repo, user=member_id)
703 elif member_type == 'user_group':
703 elif member_type == 'user_group':
704 # check if we have permissions to alter this usergroup
704 # check if we have permissions to alter this usergroup
705 member_name = UserGroup.get(member_id).users_group_name
705 member_name = UserGroup.get(member_id).users_group_name
706 if not check_perms or HasUserGroupPermissionAny(
706 if not check_perms or HasUserGroupPermissionAny(
707 *req_perms)(member_name, user=cur_user):
707 *req_perms)(member_name, user=cur_user):
708 self.revoke_user_group_permission(
708 self.revoke_user_group_permission(
709 repo=repo, group_name=member_id)
709 repo=repo, group_name=member_id)
710 else:
710 else:
711 raise ValueError("member_type must be 'user' or 'user_group' "
711 raise ValueError("member_type must be 'user' or 'user_group' "
712 "got {} instead".format(member_type))
712 "got {} instead".format(member_type))
713
713
714 changes['deleted'].append({'type': member_type, 'id': member_id,
714 changes['deleted'].append({'type': member_type, 'id': member_id,
715 'name': member_name, 'new_perm': perm})
715 'name': member_name, 'new_perm': perm})
716 return changes
716 return changes
717
717
718 def create_fork(self, form_data, cur_user):
718 def create_fork(self, form_data, cur_user):
719 """
719 """
720 Simple wrapper into executing celery task for fork creation
720 Simple wrapper into executing celery task for fork creation
721
721
722 :param form_data:
722 :param form_data:
723 :param cur_user:
723 :param cur_user:
724 """
724 """
725 from rhodecode.lib.celerylib import tasks, run_task
725 from rhodecode.lib.celerylib import tasks, run_task
726 return run_task(tasks.create_repo_fork, form_data, cur_user)
726 return run_task(tasks.create_repo_fork, form_data, cur_user)
727
727
728 def archive(self, repo):
728 def archive(self, repo):
729 """
729 """
730 Archive given repository. Set archive flag.
730 Archive given repository. Set archive flag.
731
731
732 :param repo:
732 :param repo:
733 """
733 """
734 repo = self._get_repo(repo)
734 repo = self._get_repo(repo)
735 if repo:
735 if repo:
736
736
737 try:
737 try:
738 repo.archived = True
738 repo.archived = True
739 self.sa.add(repo)
739 self.sa.add(repo)
740 self.sa.commit()
740 self.sa.commit()
741 except Exception:
741 except Exception:
742 log.error(traceback.format_exc())
742 log.error(traceback.format_exc())
743 raise
743 raise
744
744
745 def delete(self, repo, forks=None, pull_requests=None, fs_remove=True, cur_user=None):
745 def delete(self, repo, forks=None, pull_requests=None, fs_remove=True, cur_user=None):
746 """
746 """
747 Delete given repository, forks parameter defines what do do with
747 Delete given repository, forks parameter defines what do do with
748 attached forks. Throws AttachedForksError if deleted repo has attached
748 attached forks. Throws AttachedForksError if deleted repo has attached
749 forks
749 forks
750
750
751 :param repo:
751 :param repo:
752 :param forks: str 'delete' or 'detach'
752 :param forks: str 'delete' or 'detach'
753 :param pull_requests: str 'delete' or None
753 :param pull_requests: str 'delete' or None
754 :param fs_remove: remove(archive) repo from filesystem
754 :param fs_remove: remove(archive) repo from filesystem
755 """
755 """
756 if not cur_user:
756 if not cur_user:
757 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
757 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
758 repo = self._get_repo(repo)
758 repo = self._get_repo(repo)
759 if repo:
759 if repo:
760 if forks == 'detach':
760 if forks == 'detach':
761 for r in repo.forks:
761 for r in repo.forks:
762 r.fork = None
762 r.fork = None
763 self.sa.add(r)
763 self.sa.add(r)
764 elif forks == 'delete':
764 elif forks == 'delete':
765 for r in repo.forks:
765 for r in repo.forks:
766 self.delete(r, forks='delete')
766 self.delete(r, forks='delete')
767 elif [f for f in repo.forks]:
767 elif [f for f in repo.forks]:
768 raise AttachedForksError()
768 raise AttachedForksError()
769
769
770 # check for pull requests
770 # check for pull requests
771 pr_sources = repo.pull_requests_source
771 pr_sources = repo.pull_requests_source
772 pr_targets = repo.pull_requests_target
772 pr_targets = repo.pull_requests_target
773 if pull_requests != 'delete' and (pr_sources or pr_targets):
773 if pull_requests != 'delete' and (pr_sources or pr_targets):
774 raise AttachedPullRequestsError()
774 raise AttachedPullRequestsError()
775
775
776 old_repo_dict = repo.get_dict()
776 old_repo_dict = repo.get_dict()
777 events.trigger(events.RepoPreDeleteEvent(repo))
777 events.trigger(events.RepoPreDeleteEvent(repo))
778 try:
778 try:
779 self.sa.delete(repo)
779 self.sa.delete(repo)
780 if fs_remove:
780 if fs_remove:
781 self._delete_filesystem_repo(repo)
781 self._delete_filesystem_repo(repo)
782 else:
782 else:
783 log.debug('skipping removal from filesystem')
783 log.debug('skipping removal from filesystem')
784 old_repo_dict.update({
784 old_repo_dict.update({
785 'deleted_by': cur_user,
785 'deleted_by': cur_user,
786 'deleted_on': time.time(),
786 'deleted_on': time.time(),
787 })
787 })
788 hooks_base.delete_repository(**old_repo_dict)
788 hooks_base.delete_repository(**old_repo_dict)
789 events.trigger(events.RepoDeleteEvent(repo))
789 events.trigger(events.RepoDeleteEvent(repo))
790 except Exception:
790 except Exception:
791 log.error(traceback.format_exc())
791 log.error(traceback.format_exc())
792 raise
792 raise
793
793
794 def grant_user_permission(self, repo, user, perm):
794 def grant_user_permission(self, repo, user, perm):
795 """
795 """
796 Grant permission for user on given repository, or update existing one
796 Grant permission for user on given repository, or update existing one
797 if found
797 if found
798
798
799 :param repo: Instance of Repository, repository_id, or repository name
799 :param repo: Instance of Repository, repository_id, or repository name
800 :param user: Instance of User, user_id or username
800 :param user: Instance of User, user_id or username
801 :param perm: Instance of Permission, or permission_name
801 :param perm: Instance of Permission, or permission_name
802 """
802 """
803 user = self._get_user(user)
803 user = self._get_user(user)
804 repo = self._get_repo(repo)
804 repo = self._get_repo(repo)
805 permission = self._get_perm(perm)
805 permission = self._get_perm(perm)
806
806
807 # check if we have that permission already
807 # check if we have that permission already
808 obj = self.sa.query(UserRepoToPerm) \
808 obj = self.sa.query(UserRepoToPerm) \
809 .filter(UserRepoToPerm.user == user) \
809 .filter(UserRepoToPerm.user == user) \
810 .filter(UserRepoToPerm.repository == repo) \
810 .filter(UserRepoToPerm.repository == repo) \
811 .scalar()
811 .scalar()
812 if obj is None:
812 if obj is None:
813 # create new !
813 # create new !
814 obj = UserRepoToPerm()
814 obj = UserRepoToPerm()
815 obj.repository = repo
815 obj.repository = repo
816 obj.user = user
816 obj.user = user
817 obj.permission = permission
817 obj.permission = permission
818 self.sa.add(obj)
818 self.sa.add(obj)
819 log.debug('Granted perm %s to %s on %s', perm, user, repo)
819 log.debug('Granted perm %s to %s on %s', perm, user, repo)
820 action_logger_generic(
820 action_logger_generic(
821 'granted permission: {} to user: {} on repo: {}'.format(
821 'granted permission: {} to user: {} on repo: {}'.format(
822 perm, user, repo), namespace='security.repo')
822 perm, user, repo), namespace='security.repo')
823 return obj
823 return obj
824
824
825 def revoke_user_permission(self, repo, user):
825 def revoke_user_permission(self, repo, user):
826 """
826 """
827 Revoke permission for user on given repository
827 Revoke permission for user on given repository
828
828
829 :param repo: Instance of Repository, repository_id, or repository name
829 :param repo: Instance of Repository, repository_id, or repository name
830 :param user: Instance of User, user_id or username
830 :param user: Instance of User, user_id or username
831 """
831 """
832
832
833 user = self._get_user(user)
833 user = self._get_user(user)
834 repo = self._get_repo(repo)
834 repo = self._get_repo(repo)
835
835
836 obj = self.sa.query(UserRepoToPerm) \
836 obj = self.sa.query(UserRepoToPerm) \
837 .filter(UserRepoToPerm.repository == repo) \
837 .filter(UserRepoToPerm.repository == repo) \
838 .filter(UserRepoToPerm.user == user) \
838 .filter(UserRepoToPerm.user == user) \
839 .scalar()
839 .scalar()
840 if obj:
840 if obj:
841 self.sa.delete(obj)
841 self.sa.delete(obj)
842 log.debug('Revoked perm on %s on %s', repo, user)
842 log.debug('Revoked perm on %s on %s', repo, user)
843 action_logger_generic(
843 action_logger_generic(
844 'revoked permission from user: {} on repo: {}'.format(
844 'revoked permission from user: {} on repo: {}'.format(
845 user, repo), namespace='security.repo')
845 user, repo), namespace='security.repo')
846
846
847 def grant_user_group_permission(self, repo, group_name, perm):
847 def grant_user_group_permission(self, repo, group_name, perm):
848 """
848 """
849 Grant permission for user group on given repository, or update
849 Grant permission for user group on given repository, or update
850 existing one if found
850 existing one if found
851
851
852 :param repo: Instance of Repository, repository_id, or repository name
852 :param repo: Instance of Repository, repository_id, or repository name
853 :param group_name: Instance of UserGroup, users_group_id,
853 :param group_name: Instance of UserGroup, users_group_id,
854 or user group name
854 or user group name
855 :param perm: Instance of Permission, or permission_name
855 :param perm: Instance of Permission, or permission_name
856 """
856 """
857 repo = self._get_repo(repo)
857 repo = self._get_repo(repo)
858 group_name = self._get_user_group(group_name)
858 group_name = self._get_user_group(group_name)
859 permission = self._get_perm(perm)
859 permission = self._get_perm(perm)
860
860
861 # check if we have that permission already
861 # check if we have that permission already
862 obj = self.sa.query(UserGroupRepoToPerm) \
862 obj = self.sa.query(UserGroupRepoToPerm) \
863 .filter(UserGroupRepoToPerm.users_group == group_name) \
863 .filter(UserGroupRepoToPerm.users_group == group_name) \
864 .filter(UserGroupRepoToPerm.repository == repo) \
864 .filter(UserGroupRepoToPerm.repository == repo) \
865 .scalar()
865 .scalar()
866
866
867 if obj is None:
867 if obj is None:
868 # create new
868 # create new
869 obj = UserGroupRepoToPerm()
869 obj = UserGroupRepoToPerm()
870
870
871 obj.repository = repo
871 obj.repository = repo
872 obj.users_group = group_name
872 obj.users_group = group_name
873 obj.permission = permission
873 obj.permission = permission
874 self.sa.add(obj)
874 self.sa.add(obj)
875 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
875 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
876 action_logger_generic(
876 action_logger_generic(
877 'granted permission: {} to usergroup: {} on repo: {}'.format(
877 'granted permission: {} to usergroup: {} on repo: {}'.format(
878 perm, group_name, repo), namespace='security.repo')
878 perm, group_name, repo), namespace='security.repo')
879
879
880 return obj
880 return obj
881
881
882 def revoke_user_group_permission(self, repo, group_name):
882 def revoke_user_group_permission(self, repo, group_name):
883 """
883 """
884 Revoke permission for user group on given repository
884 Revoke permission for user group on given repository
885
885
886 :param repo: Instance of Repository, repository_id, or repository name
886 :param repo: Instance of Repository, repository_id, or repository name
887 :param group_name: Instance of UserGroup, users_group_id,
887 :param group_name: Instance of UserGroup, users_group_id,
888 or user group name
888 or user group name
889 """
889 """
890 repo = self._get_repo(repo)
890 repo = self._get_repo(repo)
891 group_name = self._get_user_group(group_name)
891 group_name = self._get_user_group(group_name)
892
892
893 obj = self.sa.query(UserGroupRepoToPerm) \
893 obj = self.sa.query(UserGroupRepoToPerm) \
894 .filter(UserGroupRepoToPerm.repository == repo) \
894 .filter(UserGroupRepoToPerm.repository == repo) \
895 .filter(UserGroupRepoToPerm.users_group == group_name) \
895 .filter(UserGroupRepoToPerm.users_group == group_name) \
896 .scalar()
896 .scalar()
897 if obj:
897 if obj:
898 self.sa.delete(obj)
898 self.sa.delete(obj)
899 log.debug('Revoked perm to %s on %s', repo, group_name)
899 log.debug('Revoked perm to %s on %s', repo, group_name)
900 action_logger_generic(
900 action_logger_generic(
901 'revoked permission from usergroup: {} on repo: {}'.format(
901 'revoked permission from usergroup: {} on repo: {}'.format(
902 group_name, repo), namespace='security.repo')
902 group_name, repo), namespace='security.repo')
903
903
904 def delete_stats(self, repo_name):
904 def delete_stats(self, repo_name):
905 """
905 """
906 removes stats for given repo
906 removes stats for given repo
907
907
908 :param repo_name:
908 :param repo_name:
909 """
909 """
910 repo = self._get_repo(repo_name)
910 repo = self._get_repo(repo_name)
911 try:
911 try:
912 obj = self.sa.query(Statistics) \
912 obj = self.sa.query(Statistics) \
913 .filter(Statistics.repository == repo).scalar()
913 .filter(Statistics.repository == repo).scalar()
914 if obj:
914 if obj:
915 self.sa.delete(obj)
915 self.sa.delete(obj)
916 except Exception:
916 except Exception:
917 log.error(traceback.format_exc())
917 log.error(traceback.format_exc())
918 raise
918 raise
919
919
920 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
920 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
921 field_type='str', field_desc=''):
921 field_type='str', field_desc=''):
922
922
923 repo = self._get_repo(repo_name)
923 repo = self._get_repo(repo_name)
924
924
925 new_field = RepositoryField()
925 new_field = RepositoryField()
926 new_field.repository = repo
926 new_field.repository = repo
927 new_field.field_key = field_key
927 new_field.field_key = field_key
928 new_field.field_type = field_type # python type
928 new_field.field_type = field_type # python type
929 new_field.field_value = field_value
929 new_field.field_value = field_value
930 new_field.field_desc = field_desc
930 new_field.field_desc = field_desc
931 new_field.field_label = field_label
931 new_field.field_label = field_label
932 self.sa.add(new_field)
932 self.sa.add(new_field)
933 return new_field
933 return new_field
934
934
935 def delete_repo_field(self, repo_name, field_key):
935 def delete_repo_field(self, repo_name, field_key):
936 repo = self._get_repo(repo_name)
936 repo = self._get_repo(repo_name)
937 field = RepositoryField.get_by_key_name(field_key, repo)
937 field = RepositoryField.get_by_key_name(field_key, repo)
938 if field:
938 if field:
939 self.sa.delete(field)
939 self.sa.delete(field)
940
940
941 def set_landing_rev(self, repo, landing_rev_name):
941 def set_landing_rev(self, repo, landing_rev_name):
942 if landing_rev_name.startswith('branch:'):
942 if landing_rev_name.startswith('branch:'):
943 landing_rev_name = landing_rev_name.split('branch:')[-1]
943 landing_rev_name = landing_rev_name.split('branch:')[-1]
944 scm_instance = repo.scm_instance()
944 scm_instance = repo.scm_instance()
945 if scm_instance:
945 if scm_instance:
946 return scm_instance._remote.set_head_ref(landing_rev_name)
946 return scm_instance._remote.set_head_ref(landing_rev_name)
947
947
948 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
948 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
949 clone_uri=None, repo_store_location=None,
949 clone_uri=None, repo_store_location=None,
950 use_global_config=False, install_hooks=True):
950 use_global_config=False, install_hooks=True):
951 """
951 """
952 makes repository on filesystem. It's group aware means it'll create
952 makes repository on filesystem. It's group aware means it'll create
953 a repository within a group, and alter the paths accordingly of
953 a repository within a group, and alter the paths accordingly of
954 group location
954 group location
955
955
956 :param repo_name:
956 :param repo_name:
957 :param alias:
957 :param alias:
958 :param parent:
958 :param parent:
959 :param clone_uri:
959 :param clone_uri:
960 :param repo_store_location:
960 :param repo_store_location:
961 """
961 """
962 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
962 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
963 from rhodecode.model.scm import ScmModel
963 from rhodecode.model.scm import ScmModel
964
964
965 if Repository.NAME_SEP in repo_name:
965 if Repository.NAME_SEP in repo_name:
966 raise ValueError(
966 raise ValueError(
967 'repo_name must not contain groups got `%s`' % repo_name)
967 'repo_name must not contain groups got `%s`' % repo_name)
968
968
969 if isinstance(repo_group, RepoGroup):
969 if isinstance(repo_group, RepoGroup):
970 new_parent_path = os.sep.join(repo_group.full_path_splitted)
970 new_parent_path = os.sep.join(repo_group.full_path_splitted)
971 else:
971 else:
972 new_parent_path = repo_group or ''
972 new_parent_path = repo_group or ''
973
973
974 if repo_store_location:
974 if repo_store_location:
975 _paths = [repo_store_location]
975 _paths = [repo_store_location]
976 else:
976 else:
977 _paths = [self.repos_path, new_parent_path, repo_name]
977 _paths = [self.repos_path, new_parent_path, repo_name]
978 # we need to make it str for mercurial
978 # we need to make it str for mercurial
979 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
979 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
980
980
981 # check if this path is not a repository
981 # check if this path is not a repository
982 if is_valid_repo(repo_path, self.repos_path):
982 if is_valid_repo(repo_path, self.repos_path):
983 raise Exception('This path %s is a valid repository' % repo_path)
983 raise Exception('This path %s is a valid repository' % repo_path)
984
984
985 # check if this path is a group
985 # check if this path is a group
986 if is_valid_repo_group(repo_path, self.repos_path):
986 if is_valid_repo_group(repo_path, self.repos_path):
987 raise Exception('This path %s is a valid group' % repo_path)
987 raise Exception('This path %s is a valid group' % repo_path)
988
988
989 log.info('creating repo %s in %s from url: `%s`',
989 log.info('creating repo %s in %s from url: `%s`',
990 repo_name, safe_unicode(repo_path),
990 repo_name, safe_unicode(repo_path),
991 obfuscate_url_pw(clone_uri))
991 obfuscate_url_pw(clone_uri))
992
992
993 backend = get_backend(repo_type)
993 backend = get_backend(repo_type)
994
994
995 config_repo = None if use_global_config else repo_name
995 config_repo = None if use_global_config else repo_name
996 if config_repo and new_parent_path:
996 if config_repo and new_parent_path:
997 config_repo = Repository.NAME_SEP.join(
997 config_repo = Repository.NAME_SEP.join(
998 (new_parent_path, config_repo))
998 (new_parent_path, config_repo))
999 config = make_db_config(clear_session=False, repo=config_repo)
999 config = make_db_config(clear_session=False, repo=config_repo)
1000 config.set('extensions', 'largefiles', '')
1000 config.set('extensions', 'largefiles', '')
1001
1001
1002 # patch and reset hooks section of UI config to not run any
1002 # patch and reset hooks section of UI config to not run any
1003 # hooks on creating remote repo
1003 # hooks on creating remote repo
1004 config.clear_section('hooks')
1004 config.clear_section('hooks')
1005
1005
1006 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
1006 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
1007 if repo_type == 'git':
1007 if repo_type == 'git':
1008 repo = backend(
1008 repo = backend(
1009 repo_path, config=config, create=True, src_url=clone_uri, bare=True,
1009 repo_path, config=config, create=True, src_url=clone_uri, bare=True,
1010 with_wire={"cache": False})
1010 with_wire={"cache": False})
1011 else:
1011 else:
1012 repo = backend(
1012 repo = backend(
1013 repo_path, config=config, create=True, src_url=clone_uri,
1013 repo_path, config=config, create=True, src_url=clone_uri,
1014 with_wire={"cache": False})
1014 with_wire={"cache": False})
1015
1015
1016 if install_hooks:
1016 if install_hooks:
1017 repo.install_hooks()
1017 repo.install_hooks()
1018
1018
1019 log.debug('Created repo %s with %s backend',
1019 log.debug('Created repo %s with %s backend',
1020 safe_unicode(repo_name), safe_unicode(repo_type))
1020 safe_unicode(repo_name), safe_unicode(repo_type))
1021 return repo
1021 return repo
1022
1022
1023 def _rename_filesystem_repo(self, old, new):
1023 def _rename_filesystem_repo(self, old, new):
1024 """
1024 """
1025 renames repository on filesystem
1025 renames repository on filesystem
1026
1026
1027 :param old: old name
1027 :param old: old name
1028 :param new: new name
1028 :param new: new name
1029 """
1029 """
1030 log.info('renaming repo from %s to %s', old, new)
1030 log.info('renaming repo from %s to %s', old, new)
1031
1031
1032 old_path = os.path.join(self.repos_path, old)
1032 old_path = os.path.join(self.repos_path, old)
1033 new_path = os.path.join(self.repos_path, new)
1033 new_path = os.path.join(self.repos_path, new)
1034 if os.path.isdir(new_path):
1034 if os.path.isdir(new_path):
1035 raise Exception(
1035 raise Exception(
1036 'Was trying to rename to already existing dir %s' % new_path
1036 'Was trying to rename to already existing dir %s' % new_path
1037 )
1037 )
1038 shutil.move(old_path, new_path)
1038 shutil.move(old_path, new_path)
1039
1039
1040 def _delete_filesystem_repo(self, repo):
1040 def _delete_filesystem_repo(self, repo):
1041 """
1041 """
1042 removes repo from filesystem, the removal is acctually made by
1042 removes repo from filesystem, the removal is acctually made by
1043 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
1043 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
1044 repository is no longer valid for rhodecode, can be undeleted later on
1044 repository is no longer valid for rhodecode, can be undeleted later on
1045 by reverting the renames on this repository
1045 by reverting the renames on this repository
1046
1046
1047 :param repo: repo object
1047 :param repo: repo object
1048 """
1048 """
1049 rm_path = os.path.join(self.repos_path, repo.repo_name)
1049 rm_path = os.path.join(self.repos_path, repo.repo_name)
1050 repo_group = repo.group
1050 repo_group = repo.group
1051 log.info("Removing repository %s", rm_path)
1051 log.info("Removing repository %s", rm_path)
1052 # disable hg/git internal that it doesn't get detected as repo
1052 # disable hg/git internal that it doesn't get detected as repo
1053 alias = repo.repo_type
1053 alias = repo.repo_type
1054
1054
1055 config = make_db_config(clear_session=False)
1055 config = make_db_config(clear_session=False)
1056 config.set('extensions', 'largefiles', '')
1056 config.set('extensions', 'largefiles', '')
1057 bare = getattr(repo.scm_instance(config=config), 'bare', False)
1057 bare = getattr(repo.scm_instance(config=config), 'bare', False)
1058
1058
1059 # skip this for bare git repos
1059 # skip this for bare git repos
1060 if not bare:
1060 if not bare:
1061 # disable VCS repo
1061 # disable VCS repo
1062 vcs_path = os.path.join(rm_path, '.%s' % alias)
1062 vcs_path = os.path.join(rm_path, '.%s' % alias)
1063 if os.path.exists(vcs_path):
1063 if os.path.exists(vcs_path):
1064 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
1064 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
1065
1065
1066 _now = datetime.datetime.now()
1066 _now = datetime.datetime.now()
1067 _ms = str(_now.microsecond).rjust(6, '0')
1067 _ms = str(_now.microsecond).rjust(6, '0')
1068 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
1068 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
1069 repo.just_name)
1069 repo.just_name)
1070 if repo_group:
1070 if repo_group:
1071 # if repository is in group, prefix the removal path with the group
1071 # if repository is in group, prefix the removal path with the group
1072 args = repo_group.full_path_splitted + [_d]
1072 args = repo_group.full_path_splitted + [_d]
1073 _d = os.path.join(*args)
1073 _d = os.path.join(*args)
1074
1074
1075 if os.path.isdir(rm_path):
1075 if os.path.isdir(rm_path):
1076 shutil.move(rm_path, os.path.join(self.repos_path, _d))
1076 shutil.move(rm_path, os.path.join(self.repos_path, _d))
1077
1077
1078 # finally cleanup diff-cache if it exists
1078 # finally cleanup diff-cache if it exists
1079 cached_diffs_dir = repo.cached_diffs_dir
1079 cached_diffs_dir = repo.cached_diffs_dir
1080 if os.path.isdir(cached_diffs_dir):
1080 if os.path.isdir(cached_diffs_dir):
1081 shutil.rmtree(cached_diffs_dir)
1081 shutil.rmtree(cached_diffs_dir)
1082
1082
1083
1083
1084 class ReadmeFinder:
1084 class ReadmeFinder:
1085 """
1085 """
1086 Utility which knows how to find a readme for a specific commit.
1086 Utility which knows how to find a readme for a specific commit.
1087
1087
1088 The main idea is that this is a configurable algorithm. When creating an
1088 The main idea is that this is a configurable algorithm. When creating an
1089 instance you can define parameters, currently only the `default_renderer`.
1089 instance you can define parameters, currently only the `default_renderer`.
1090 Based on this configuration the method :meth:`search` behaves slightly
1090 Based on this configuration the method :meth:`search` behaves slightly
1091 different.
1091 different.
1092 """
1092 """
1093
1093
1094 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
1094 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
1095 path_re = re.compile(r'^docs?', re.IGNORECASE)
1095 path_re = re.compile(r'^docs?', re.IGNORECASE)
1096
1096
1097 default_priorities = {
1097 default_priorities = {
1098 None: 0,
1098 None: 0,
1099 '.text': 2,
1099 '.text': 2,
1100 '.txt': 3,
1100 '.txt': 3,
1101 '.rst': 1,
1101 '.rst': 1,
1102 '.rest': 2,
1102 '.rest': 2,
1103 '.md': 1,
1103 '.md': 1,
1104 '.mkdn': 2,
1104 '.mkdn': 2,
1105 '.mdown': 3,
1105 '.mdown': 3,
1106 '.markdown': 4,
1106 '.markdown': 4,
1107 }
1107 }
1108
1108
1109 path_priority = {
1109 path_priority = {
1110 'doc': 0,
1110 'doc': 0,
1111 'docs': 1,
1111 'docs': 1,
1112 }
1112 }
1113
1113
1114 FALLBACK_PRIORITY = 99
1114 FALLBACK_PRIORITY = 99
1115
1115
1116 RENDERER_TO_EXTENSION = {
1116 RENDERER_TO_EXTENSION = {
1117 'rst': ['.rst', '.rest'],
1117 'rst': ['.rst', '.rest'],
1118 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
1118 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
1119 }
1119 }
1120
1120
1121 def __init__(self, default_renderer=None):
1121 def __init__(self, default_renderer=None):
1122 self._default_renderer = default_renderer
1122 self._default_renderer = default_renderer
1123 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1123 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1124 default_renderer, [])
1124 default_renderer, [])
1125
1125
1126 def search(self, commit, path=u'/'):
1126 def search(self, commit, path=u'/'):
1127 """
1127 """
1128 Find a readme in the given `commit`.
1128 Find a readme in the given `commit`.
1129 """
1129 """
1130 nodes = commit.get_nodes(path)
1130 nodes = commit.get_nodes(path)
1131 matches = self._match_readmes(nodes)
1131 matches = self._match_readmes(nodes)
1132 matches = self._sort_according_to_priority(matches)
1132 matches = self._sort_according_to_priority(matches)
1133 if matches:
1133 if matches:
1134 return matches[0].node
1134 return matches[0].node
1135
1135
1136 paths = self._match_paths(nodes)
1136 paths = self._match_paths(nodes)
1137 paths = self._sort_paths_according_to_priority(paths)
1137 paths = self._sort_paths_according_to_priority(paths)
1138 for path in paths:
1138 for path in paths:
1139 match = self.search(commit, path=path)
1139 match = self.search(commit, path=path)
1140 if match:
1140 if match:
1141 return match
1141 return match
1142
1142
1143 return None
1143 return None
1144
1144
1145 def _match_readmes(self, nodes):
1145 def _match_readmes(self, nodes):
1146 for node in nodes:
1146 for node in nodes:
1147 if not node.is_file():
1147 if not node.is_file():
1148 continue
1148 continue
1149 path = node.path.rsplit('/', 1)[-1]
1149 path = node.path.rsplit('/', 1)[-1]
1150 match = self.readme_re.match(path)
1150 match = self.readme_re.match(path)
1151 if match:
1151 if match:
1152 extension = match.group(1)
1152 extension = match.group(1)
1153 yield ReadmeMatch(node, match, self._priority(extension))
1153 yield ReadmeMatch(node, match, self._priority(extension))
1154
1154
1155 def _match_paths(self, nodes):
1155 def _match_paths(self, nodes):
1156 for node in nodes:
1156 for node in nodes:
1157 if not node.is_dir():
1157 if not node.is_dir():
1158 continue
1158 continue
1159 match = self.path_re.match(node.path)
1159 match = self.path_re.match(node.path)
1160 if match:
1160 if match:
1161 yield node.path
1161 yield node.path
1162
1162
1163 def _priority(self, extension):
1163 def _priority(self, extension):
1164 renderer_priority = (
1164 renderer_priority = (
1165 0 if extension in self._renderer_extensions else 1)
1165 0 if extension in self._renderer_extensions else 1)
1166 extension_priority = self.default_priorities.get(
1166 extension_priority = self.default_priorities.get(
1167 extension, self.FALLBACK_PRIORITY)
1167 extension, self.FALLBACK_PRIORITY)
1168 return (renderer_priority, extension_priority)
1168 return (renderer_priority, extension_priority)
1169
1169
1170 def _sort_according_to_priority(self, matches):
1170 def _sort_according_to_priority(self, matches):
1171
1171
1172 def priority_and_path(match):
1172 def priority_and_path(match):
1173 return (match.priority, match.path)
1173 return (match.priority, match.path)
1174
1174
1175 return sorted(matches, key=priority_and_path)
1175 return sorted(matches, key=priority_and_path)
1176
1176
1177 def _sort_paths_according_to_priority(self, paths):
1177 def _sort_paths_according_to_priority(self, paths):
1178
1178
1179 def priority_and_path(path):
1179 def priority_and_path(path):
1180 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1180 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1181
1181
1182 return sorted(paths, key=priority_and_path)
1182 return sorted(paths, key=priority_and_path)
1183
1183
1184
1184
1185 class ReadmeMatch:
1185 class ReadmeMatch:
1186
1186
1187 def __init__(self, node, match, priority):
1187 def __init__(self, node, match, priority):
1188 self.node = node
1188 self.node = node
1189 self._match = match
1189 self._match = match
1190 self.priority = priority
1190 self.priority = priority
1191
1191
1192 @property
1192 @property
1193 def path(self):
1193 def path(self):
1194 return self.node.path
1194 return self.node.path
1195
1195
1196 def __repr__(self):
1196 def __repr__(self):
1197 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1197 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,897 +1,897 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2020 RhodeCode GmbH
3 # Copyright (C) 2011-2020 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 """
22 """
23 repo group model for RhodeCode
23 repo group model for RhodeCode
24 """
24 """
25
25
26 import os
26 import os
27 import datetime
27 import datetime
28 import itertools
28 import itertools
29 import logging
29 import logging
30 import shutil
30 import shutil
31 import time
31 import time
32 import traceback
32 import traceback
33 import string
33 import string
34
34
35 from zope.cachedescriptors.property import Lazy as LazyProperty
35 from zope.cachedescriptors.property import Lazy as LazyProperty
36
36
37 from rhodecode import events
37 from rhodecode import events
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
39 from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
40 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
40 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
41 UserGroup, Repository)
41 UserGroup, Repository)
42 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.permission import PermissionModel
43 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
43 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
44 from rhodecode.lib.caching_query import FromCache
44 from rhodecode.lib.caching_query import FromCache
45 from rhodecode.lib.utils2 import action_logger_generic
45 from rhodecode.lib.utils2 import action_logger_generic
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class RepoGroupModel(BaseModel):
50 class RepoGroupModel(BaseModel):
51
51
52 cls = RepoGroup
52 cls = RepoGroup
53 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
53 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
54 PERSONAL_GROUP_PATTERN = '${username}' # default
54 PERSONAL_GROUP_PATTERN = '${username}' # default
55
55
56 def _get_user_group(self, users_group):
56 def _get_user_group(self, users_group):
57 return self._get_instance(UserGroup, users_group,
57 return self._get_instance(UserGroup, users_group,
58 callback=UserGroup.get_by_group_name)
58 callback=UserGroup.get_by_group_name)
59
59
60 def _get_repo_group(self, repo_group):
60 def _get_repo_group(self, repo_group):
61 return self._get_instance(RepoGroup, repo_group,
61 return self._get_instance(RepoGroup, repo_group,
62 callback=RepoGroup.get_by_group_name)
62 callback=RepoGroup.get_by_group_name)
63
63
64 def get_repo_group(self, repo_group):
64 def get_repo_group(self, repo_group):
65 return self._get_repo_group(repo_group)
65 return self._get_repo_group(repo_group)
66
66
67 @LazyProperty
67 @LazyProperty
68 def repos_path(self):
68 def repos_path(self):
69 """
69 """
70 Gets the repositories root path from database
70 Gets the repositories root path from database
71 """
71 """
72
72
73 settings_model = VcsSettingsModel(sa=self.sa)
73 settings_model = VcsSettingsModel(sa=self.sa)
74 return settings_model.get_repos_location()
74 return settings_model.get_repos_location()
75
75
76 def get_by_group_name(self, repo_group_name, cache=None):
76 def get_by_group_name(self, repo_group_name, cache=None):
77 repo = self.sa.query(RepoGroup) \
77 repo = self.sa.query(RepoGroup) \
78 .filter(RepoGroup.group_name == repo_group_name)
78 .filter(RepoGroup.group_name == repo_group_name)
79
79
80 if cache:
80 if cache:
81 name_key = _hash_key(repo_group_name)
81 name_key = _hash_key(repo_group_name)
82 repo = repo.options(
82 repo = repo.options(
83 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
83 FromCache("sql_cache_short", f"get_repo_group_{name_key}"))
84 return repo.scalar()
84 return repo.scalar()
85
85
86 def get_default_create_personal_repo_group(self):
86 def get_default_create_personal_repo_group(self):
87 value = SettingsModel().get_setting_by_name(
87 value = SettingsModel().get_setting_by_name(
88 'create_personal_repo_group')
88 'create_personal_repo_group')
89 return value.app_settings_value if value else None or False
89 return value.app_settings_value if value else None or False
90
90
91 def get_personal_group_name_pattern(self):
91 def get_personal_group_name_pattern(self):
92 value = SettingsModel().get_setting_by_name(
92 value = SettingsModel().get_setting_by_name(
93 'personal_repo_group_pattern')
93 'personal_repo_group_pattern')
94 val = value.app_settings_value if value else None
94 val = value.app_settings_value if value else None
95 group_template = val or self.PERSONAL_GROUP_PATTERN
95 group_template = val or self.PERSONAL_GROUP_PATTERN
96
96
97 group_template = group_template.lstrip('/')
97 group_template = group_template.lstrip('/')
98 return group_template
98 return group_template
99
99
100 def get_personal_group_name(self, user):
100 def get_personal_group_name(self, user):
101 template = self.get_personal_group_name_pattern()
101 template = self.get_personal_group_name_pattern()
102 return string.Template(template).safe_substitute(
102 return string.Template(template).safe_substitute(
103 username=user.username,
103 username=user.username,
104 user_id=user.user_id,
104 user_id=user.user_id,
105 first_name=user.first_name,
105 first_name=user.first_name,
106 last_name=user.last_name,
106 last_name=user.last_name,
107 )
107 )
108
108
109 def create_personal_repo_group(self, user, commit_early=True):
109 def create_personal_repo_group(self, user, commit_early=True):
110 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
110 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
111 personal_repo_group_name = self.get_personal_group_name(user)
111 personal_repo_group_name = self.get_personal_group_name(user)
112
112
113 # create a new one
113 # create a new one
114 RepoGroupModel().create(
114 RepoGroupModel().create(
115 group_name=personal_repo_group_name,
115 group_name=personal_repo_group_name,
116 group_description=desc,
116 group_description=desc,
117 owner=user.username,
117 owner=user.username,
118 personal=True,
118 personal=True,
119 commit_early=commit_early)
119 commit_early=commit_early)
120
120
121 def _create_default_perms(self, new_group):
121 def _create_default_perms(self, new_group):
122 # create default permission
122 # create default permission
123 default_perm = 'group.read'
123 default_perm = 'group.read'
124 def_user = User.get_default_user()
124 def_user = User.get_default_user()
125 for p in def_user.user_perms:
125 for p in def_user.user_perms:
126 if p.permission.permission_name.startswith('group.'):
126 if p.permission.permission_name.startswith('group.'):
127 default_perm = p.permission.permission_name
127 default_perm = p.permission.permission_name
128 break
128 break
129
129
130 repo_group_to_perm = UserRepoGroupToPerm()
130 repo_group_to_perm = UserRepoGroupToPerm()
131 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
131 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
132
132
133 repo_group_to_perm.group = new_group
133 repo_group_to_perm.group = new_group
134 repo_group_to_perm.user_id = def_user.user_id
134 repo_group_to_perm.user_id = def_user.user_id
135 return repo_group_to_perm
135 return repo_group_to_perm
136
136
137 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
137 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
138 get_object=False):
138 get_object=False):
139 """
139 """
140 Get's the group name and a parent group name from given group name.
140 Get's the group name and a parent group name from given group name.
141 If repo_in_path is set to truth, we asume the full path also includes
141 If repo_in_path is set to truth, we asume the full path also includes
142 repo name, in such case we clean the last element.
142 repo name, in such case we clean the last element.
143
143
144 :param group_name_full:
144 :param group_name_full:
145 """
145 """
146 split_paths = 1
146 split_paths = 1
147 if repo_in_path:
147 if repo_in_path:
148 split_paths = 2
148 split_paths = 2
149 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
149 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
150
150
151 if repo_in_path and len(_parts) > 1:
151 if repo_in_path and len(_parts) > 1:
152 # such case last element is the repo_name
152 # such case last element is the repo_name
153 _parts.pop(-1)
153 _parts.pop(-1)
154 group_name_cleaned = _parts[-1] # just the group name
154 group_name_cleaned = _parts[-1] # just the group name
155 parent_repo_group_name = None
155 parent_repo_group_name = None
156
156
157 if len(_parts) > 1:
157 if len(_parts) > 1:
158 parent_repo_group_name = _parts[0]
158 parent_repo_group_name = _parts[0]
159
159
160 parent_group = None
160 parent_group = None
161 if parent_repo_group_name:
161 if parent_repo_group_name:
162 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
162 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
163
163
164 if get_object:
164 if get_object:
165 return group_name_cleaned, parent_repo_group_name, parent_group
165 return group_name_cleaned, parent_repo_group_name, parent_group
166
166
167 return group_name_cleaned, parent_repo_group_name
167 return group_name_cleaned, parent_repo_group_name
168
168
169 def check_exist_filesystem(self, group_name, exc_on_failure=True):
169 def check_exist_filesystem(self, group_name, exc_on_failure=True):
170 create_path = os.path.join(self.repos_path, group_name)
170 create_path = os.path.join(self.repos_path, group_name)
171 log.debug('creating new group in %s', create_path)
171 log.debug('creating new group in %s', create_path)
172
172
173 if os.path.isdir(create_path):
173 if os.path.isdir(create_path):
174 if exc_on_failure:
174 if exc_on_failure:
175 abs_create_path = os.path.abspath(create_path)
175 abs_create_path = os.path.abspath(create_path)
176 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
176 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
177 return False
177 return False
178 return True
178 return True
179
179
180 def _create_group(self, group_name):
180 def _create_group(self, group_name):
181 """
181 """
182 makes repository group on filesystem
182 makes repository group on filesystem
183
183
184 :param repo_name:
184 :param repo_name:
185 :param parent_id:
185 :param parent_id:
186 """
186 """
187
187
188 self.check_exist_filesystem(group_name)
188 self.check_exist_filesystem(group_name)
189 create_path = os.path.join(self.repos_path, group_name)
189 create_path = os.path.join(self.repos_path, group_name)
190 log.debug('creating new group in %s', create_path)
190 log.debug('creating new group in %s', create_path)
191 os.makedirs(create_path, mode=0o755)
191 os.makedirs(create_path, mode=0o755)
192 log.debug('created group in %s', create_path)
192 log.debug('created group in %s', create_path)
193
193
194 def _rename_group(self, old, new):
194 def _rename_group(self, old, new):
195 """
195 """
196 Renames a group on filesystem
196 Renames a group on filesystem
197
197
198 :param group_name:
198 :param group_name:
199 """
199 """
200
200
201 if old == new:
201 if old == new:
202 log.debug('skipping group rename')
202 log.debug('skipping group rename')
203 return
203 return
204
204
205 log.debug('renaming repository group from %s to %s', old, new)
205 log.debug('renaming repository group from %s to %s', old, new)
206
206
207 old_path = os.path.join(self.repos_path, old)
207 old_path = os.path.join(self.repos_path, old)
208 new_path = os.path.join(self.repos_path, new)
208 new_path = os.path.join(self.repos_path, new)
209
209
210 log.debug('renaming repos paths from %s to %s', old_path, new_path)
210 log.debug('renaming repos paths from %s to %s', old_path, new_path)
211
211
212 if os.path.isdir(new_path):
212 if os.path.isdir(new_path):
213 raise Exception('Was trying to rename to already '
213 raise Exception('Was trying to rename to already '
214 'existing dir %s' % new_path)
214 'existing dir %s' % new_path)
215 shutil.move(old_path, new_path)
215 shutil.move(old_path, new_path)
216
216
217 def _delete_filesystem_group(self, group, force_delete=False):
217 def _delete_filesystem_group(self, group, force_delete=False):
218 """
218 """
219 Deletes a group from a filesystem
219 Deletes a group from a filesystem
220
220
221 :param group: instance of group from database
221 :param group: instance of group from database
222 :param force_delete: use shutil rmtree to remove all objects
222 :param force_delete: use shutil rmtree to remove all objects
223 """
223 """
224 paths = group.full_path.split(RepoGroup.url_sep())
224 paths = group.full_path.split(RepoGroup.url_sep())
225 paths = os.sep.join(paths)
225 paths = os.sep.join(paths)
226
226
227 rm_path = os.path.join(self.repos_path, paths)
227 rm_path = os.path.join(self.repos_path, paths)
228 log.info("Removing group %s", rm_path)
228 log.info("Removing group %s", rm_path)
229 # delete only if that path really exists
229 # delete only if that path really exists
230 if os.path.isdir(rm_path):
230 if os.path.isdir(rm_path):
231 if force_delete:
231 if force_delete:
232 shutil.rmtree(rm_path)
232 shutil.rmtree(rm_path)
233 else:
233 else:
234 # archive that group`
234 # archive that group`
235 _now = datetime.datetime.now()
235 _now = datetime.datetime.now()
236 _ms = str(_now.microsecond).rjust(6, '0')
236 _ms = str(_now.microsecond).rjust(6, '0')
237 _d = 'rm__%s_GROUP_%s' % (
237 _d = 'rm__%s_GROUP_%s' % (
238 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
238 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
239 shutil.move(rm_path, os.path.join(self.repos_path, _d))
239 shutil.move(rm_path, os.path.join(self.repos_path, _d))
240
240
241 def create(self, group_name, group_description, owner, just_db=False,
241 def create(self, group_name, group_description, owner, just_db=False,
242 copy_permissions=False, personal=None, commit_early=True):
242 copy_permissions=False, personal=None, commit_early=True):
243
243
244 (group_name_cleaned,
244 (group_name_cleaned,
245 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
245 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
246
246
247 parent_group = None
247 parent_group = None
248 if parent_group_name:
248 if parent_group_name:
249 parent_group = self._get_repo_group(parent_group_name)
249 parent_group = self._get_repo_group(parent_group_name)
250 if not parent_group:
250 if not parent_group:
251 # we tried to create a nested group, but the parent is not
251 # we tried to create a nested group, but the parent is not
252 # existing
252 # existing
253 raise ValueError(
253 raise ValueError(
254 'Parent group `%s` given in `%s` group name '
254 'Parent group `%s` given in `%s` group name '
255 'is not yet existing.' % (parent_group_name, group_name))
255 'is not yet existing.' % (parent_group_name, group_name))
256
256
257 # because we are doing a cleanup, we need to check if such directory
257 # because we are doing a cleanup, we need to check if such directory
258 # already exists. If we don't do that we can accidentally delete
258 # already exists. If we don't do that we can accidentally delete
259 # existing directory via cleanup that can cause data issues, since
259 # existing directory via cleanup that can cause data issues, since
260 # delete does a folder rename to special syntax later cleanup
260 # delete does a folder rename to special syntax later cleanup
261 # functions can delete this
261 # functions can delete this
262 cleanup_group = self.check_exist_filesystem(group_name,
262 cleanup_group = self.check_exist_filesystem(group_name,
263 exc_on_failure=False)
263 exc_on_failure=False)
264 user = self._get_user(owner)
264 user = self._get_user(owner)
265 if not user:
265 if not user:
266 raise ValueError('Owner %s not found as rhodecode user', owner)
266 raise ValueError('Owner %s not found as rhodecode user', owner)
267
267
268 try:
268 try:
269 new_repo_group = RepoGroup()
269 new_repo_group = RepoGroup()
270 new_repo_group.user = user
270 new_repo_group.user = user
271 new_repo_group.group_description = group_description or group_name
271 new_repo_group.group_description = group_description or group_name
272 new_repo_group.parent_group = parent_group
272 new_repo_group.parent_group = parent_group
273 new_repo_group.group_name = group_name
273 new_repo_group.group_name = group_name
274 new_repo_group.personal = personal
274 new_repo_group.personal = personal
275
275
276 self.sa.add(new_repo_group)
276 self.sa.add(new_repo_group)
277
277
278 # create an ADMIN permission for owner except if we're super admin,
278 # create an ADMIN permission for owner except if we're super admin,
279 # later owner should go into the owner field of groups
279 # later owner should go into the owner field of groups
280 if not user.is_admin:
280 if not user.is_admin:
281 self.grant_user_permission(repo_group=new_repo_group,
281 self.grant_user_permission(repo_group=new_repo_group,
282 user=owner, perm='group.admin')
282 user=owner, perm='group.admin')
283
283
284 if parent_group and copy_permissions:
284 if parent_group and copy_permissions:
285 # copy permissions from parent
285 # copy permissions from parent
286 user_perms = UserRepoGroupToPerm.query() \
286 user_perms = UserRepoGroupToPerm.query() \
287 .filter(UserRepoGroupToPerm.group == parent_group).all()
287 .filter(UserRepoGroupToPerm.group == parent_group).all()
288
288
289 group_perms = UserGroupRepoGroupToPerm.query() \
289 group_perms = UserGroupRepoGroupToPerm.query() \
290 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
290 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
291
291
292 for perm in user_perms:
292 for perm in user_perms:
293 # don't copy over the permission for user who is creating
293 # don't copy over the permission for user who is creating
294 # this group, if he is not super admin he get's admin
294 # this group, if he is not super admin he get's admin
295 # permission set above
295 # permission set above
296 if perm.user != user or user.is_admin:
296 if perm.user != user or user.is_admin:
297 UserRepoGroupToPerm.create(
297 UserRepoGroupToPerm.create(
298 perm.user, new_repo_group, perm.permission)
298 perm.user, new_repo_group, perm.permission)
299
299
300 for perm in group_perms:
300 for perm in group_perms:
301 UserGroupRepoGroupToPerm.create(
301 UserGroupRepoGroupToPerm.create(
302 perm.users_group, new_repo_group, perm.permission)
302 perm.users_group, new_repo_group, perm.permission)
303 else:
303 else:
304 perm_obj = self._create_default_perms(new_repo_group)
304 perm_obj = self._create_default_perms(new_repo_group)
305 self.sa.add(perm_obj)
305 self.sa.add(perm_obj)
306
306
307 # now commit the changes, earlier so we are sure everything is in
307 # now commit the changes, earlier so we are sure everything is in
308 # the database.
308 # the database.
309 if commit_early:
309 if commit_early:
310 self.sa.commit()
310 self.sa.commit()
311 if not just_db:
311 if not just_db:
312 self._create_group(new_repo_group.group_name)
312 self._create_group(new_repo_group.group_name)
313
313
314 # trigger the post hook
314 # trigger the post hook
315 from rhodecode.lib import hooks_base
315 from rhodecode.lib import hooks_base
316 repo_group = RepoGroup.get_by_group_name(group_name)
316 repo_group = RepoGroup.get_by_group_name(group_name)
317
317
318 # update repo group commit caches initially
318 # update repo group commit caches initially
319 repo_group.update_commit_cache()
319 repo_group.update_commit_cache()
320
320
321 hooks_base.create_repository_group(
321 hooks_base.create_repository_group(
322 created_by=user.username, **repo_group.get_dict())
322 created_by=user.username, **repo_group.get_dict())
323
323
324 # Trigger create event.
324 # Trigger create event.
325 events.trigger(events.RepoGroupCreateEvent(repo_group))
325 events.trigger(events.RepoGroupCreateEvent(repo_group))
326
326
327 return new_repo_group
327 return new_repo_group
328 except Exception:
328 except Exception:
329 self.sa.rollback()
329 self.sa.rollback()
330 log.exception('Exception occurred when creating repository group, '
330 log.exception('Exception occurred when creating repository group, '
331 'doing cleanup...')
331 'doing cleanup...')
332 # rollback things manually !
332 # rollback things manually !
333 repo_group = RepoGroup.get_by_group_name(group_name)
333 repo_group = RepoGroup.get_by_group_name(group_name)
334 if repo_group:
334 if repo_group:
335 RepoGroup.delete(repo_group.group_id)
335 RepoGroup.delete(repo_group.group_id)
336 self.sa.commit()
336 self.sa.commit()
337 if cleanup_group:
337 if cleanup_group:
338 RepoGroupModel()._delete_filesystem_group(repo_group)
338 RepoGroupModel()._delete_filesystem_group(repo_group)
339 raise
339 raise
340
340
341 def update_permissions(
341 def update_permissions(
342 self, repo_group, perm_additions=None, perm_updates=None,
342 self, repo_group, perm_additions=None, perm_updates=None,
343 perm_deletions=None, recursive=None, check_perms=True,
343 perm_deletions=None, recursive=None, check_perms=True,
344 cur_user=None):
344 cur_user=None):
345 from rhodecode.model.repo import RepoModel
345 from rhodecode.model.repo import RepoModel
346 from rhodecode.lib.auth import HasUserGroupPermissionAny
346 from rhodecode.lib.auth import HasUserGroupPermissionAny
347
347
348 if not perm_additions:
348 if not perm_additions:
349 perm_additions = []
349 perm_additions = []
350 if not perm_updates:
350 if not perm_updates:
351 perm_updates = []
351 perm_updates = []
352 if not perm_deletions:
352 if not perm_deletions:
353 perm_deletions = []
353 perm_deletions = []
354
354
355 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
355 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
356
356
357 changes = {
357 changes = {
358 'added': [],
358 'added': [],
359 'updated': [],
359 'updated': [],
360 'deleted': [],
360 'deleted': [],
361 'default_user_changed': None
361 'default_user_changed': None
362 }
362 }
363
363
364 def _set_perm_user(obj, user, perm):
364 def _set_perm_user(obj, user, perm):
365 if isinstance(obj, RepoGroup):
365 if isinstance(obj, RepoGroup):
366 self.grant_user_permission(
366 self.grant_user_permission(
367 repo_group=obj, user=user, perm=perm)
367 repo_group=obj, user=user, perm=perm)
368 elif isinstance(obj, Repository):
368 elif isinstance(obj, Repository):
369 # private repos will not allow to change the default
369 # private repos will not allow to change the default
370 # permissions using recursive mode
370 # permissions using recursive mode
371 if obj.private and user == User.DEFAULT_USER:
371 if obj.private and user == User.DEFAULT_USER:
372 return
372 return
373
373
374 # we set group permission but we have to switch to repo
374 # we set group permission but we have to switch to repo
375 # permission
375 # permission
376 perm = perm.replace('group.', 'repository.')
376 perm = perm.replace('group.', 'repository.')
377 RepoModel().grant_user_permission(
377 RepoModel().grant_user_permission(
378 repo=obj, user=user, perm=perm)
378 repo=obj, user=user, perm=perm)
379
379
380 def _set_perm_group(obj, users_group, perm):
380 def _set_perm_group(obj, users_group, perm):
381 if isinstance(obj, RepoGroup):
381 if isinstance(obj, RepoGroup):
382 self.grant_user_group_permission(
382 self.grant_user_group_permission(
383 repo_group=obj, group_name=users_group, perm=perm)
383 repo_group=obj, group_name=users_group, perm=perm)
384 elif isinstance(obj, Repository):
384 elif isinstance(obj, Repository):
385 # we set group permission but we have to switch to repo
385 # we set group permission but we have to switch to repo
386 # permission
386 # permission
387 perm = perm.replace('group.', 'repository.')
387 perm = perm.replace('group.', 'repository.')
388 RepoModel().grant_user_group_permission(
388 RepoModel().grant_user_group_permission(
389 repo=obj, group_name=users_group, perm=perm)
389 repo=obj, group_name=users_group, perm=perm)
390
390
391 def _revoke_perm_user(obj, user):
391 def _revoke_perm_user(obj, user):
392 if isinstance(obj, RepoGroup):
392 if isinstance(obj, RepoGroup):
393 self.revoke_user_permission(repo_group=obj, user=user)
393 self.revoke_user_permission(repo_group=obj, user=user)
394 elif isinstance(obj, Repository):
394 elif isinstance(obj, Repository):
395 RepoModel().revoke_user_permission(repo=obj, user=user)
395 RepoModel().revoke_user_permission(repo=obj, user=user)
396
396
397 def _revoke_perm_group(obj, user_group):
397 def _revoke_perm_group(obj, user_group):
398 if isinstance(obj, RepoGroup):
398 if isinstance(obj, RepoGroup):
399 self.revoke_user_group_permission(
399 self.revoke_user_group_permission(
400 repo_group=obj, group_name=user_group)
400 repo_group=obj, group_name=user_group)
401 elif isinstance(obj, Repository):
401 elif isinstance(obj, Repository):
402 RepoModel().revoke_user_group_permission(
402 RepoModel().revoke_user_group_permission(
403 repo=obj, group_name=user_group)
403 repo=obj, group_name=user_group)
404
404
405 # start updates
405 # start updates
406 log.debug('Now updating permissions for %s in recursive mode:%s',
406 log.debug('Now updating permissions for %s in recursive mode:%s',
407 repo_group, recursive)
407 repo_group, recursive)
408
408
409 # initialize check function, we'll call that multiple times
409 # initialize check function, we'll call that multiple times
410 has_group_perm = HasUserGroupPermissionAny(*req_perms)
410 has_group_perm = HasUserGroupPermissionAny(*req_perms)
411
411
412 for obj in repo_group.recursive_groups_and_repos():
412 for obj in repo_group.recursive_groups_and_repos():
413 # iterated obj is an instance of a repos group or repository in
413 # iterated obj is an instance of a repos group or repository in
414 # that group, recursive option can be: none, repos, groups, all
414 # that group, recursive option can be: none, repos, groups, all
415 if recursive == 'all':
415 if recursive == 'all':
416 obj = obj
416 obj = obj
417 elif recursive == 'repos':
417 elif recursive == 'repos':
418 # skip groups, other than this one
418 # skip groups, other than this one
419 if isinstance(obj, RepoGroup) and not obj == repo_group:
419 if isinstance(obj, RepoGroup) and not obj == repo_group:
420 continue
420 continue
421 elif recursive == 'groups':
421 elif recursive == 'groups':
422 # skip repos
422 # skip repos
423 if isinstance(obj, Repository):
423 if isinstance(obj, Repository):
424 continue
424 continue
425 else: # recursive == 'none':
425 else: # recursive == 'none':
426 # DEFAULT option - don't apply to iterated objects
426 # DEFAULT option - don't apply to iterated objects
427 # also we do a break at the end of this loop. if we are not
427 # also we do a break at the end of this loop. if we are not
428 # in recursive mode
428 # in recursive mode
429 obj = repo_group
429 obj = repo_group
430
430
431 change_obj = obj.get_api_data()
431 change_obj = obj.get_api_data()
432
432
433 # update permissions
433 # update permissions
434 for member_id, perm, member_type in perm_updates:
434 for member_id, perm, member_type in perm_updates:
435 member_id = int(member_id)
435 member_id = int(member_id)
436 if member_type == 'user':
436 if member_type == 'user':
437 member_name = User.get(member_id).username
437 member_name = User.get(member_id).username
438 if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
438 if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
439 # NOTE(dan): detect if we changed permissions for default user
439 # NOTE(dan): detect if we changed permissions for default user
440 perm_obj = self.sa.query(UserRepoGroupToPerm) \
440 perm_obj = self.sa.query(UserRepoGroupToPerm) \
441 .filter(UserRepoGroupToPerm.user_id == member_id) \
441 .filter(UserRepoGroupToPerm.user_id == member_id) \
442 .filter(UserRepoGroupToPerm.group == repo_group) \
442 .filter(UserRepoGroupToPerm.group == repo_group) \
443 .scalar()
443 .scalar()
444 if perm_obj and perm_obj.permission.permission_name != perm:
444 if perm_obj and perm_obj.permission.permission_name != perm:
445 changes['default_user_changed'] = True
445 changes['default_user_changed'] = True
446
446
447 # this updates also current one if found
447 # this updates also current one if found
448 _set_perm_user(obj, user=member_id, perm=perm)
448 _set_perm_user(obj, user=member_id, perm=perm)
449 elif member_type == 'user_group':
449 elif member_type == 'user_group':
450 member_name = UserGroup.get(member_id).users_group_name
450 member_name = UserGroup.get(member_id).users_group_name
451 if not check_perms or has_group_perm(member_name,
451 if not check_perms or has_group_perm(member_name,
452 user=cur_user):
452 user=cur_user):
453 _set_perm_group(obj, users_group=member_id, perm=perm)
453 _set_perm_group(obj, users_group=member_id, perm=perm)
454 else:
454 else:
455 raise ValueError("member_type must be 'user' or 'user_group' "
455 raise ValueError("member_type must be 'user' or 'user_group' "
456 "got {} instead".format(member_type))
456 "got {} instead".format(member_type))
457
457
458 changes['updated'].append(
458 changes['updated'].append(
459 {'change_obj': change_obj, 'type': member_type,
459 {'change_obj': change_obj, 'type': member_type,
460 'id': member_id, 'name': member_name, 'new_perm': perm})
460 'id': member_id, 'name': member_name, 'new_perm': perm})
461
461
462 # set new permissions
462 # set new permissions
463 for member_id, perm, member_type in perm_additions:
463 for member_id, perm, member_type in perm_additions:
464 member_id = int(member_id)
464 member_id = int(member_id)
465 if member_type == 'user':
465 if member_type == 'user':
466 member_name = User.get(member_id).username
466 member_name = User.get(member_id).username
467 _set_perm_user(obj, user=member_id, perm=perm)
467 _set_perm_user(obj, user=member_id, perm=perm)
468 elif member_type == 'user_group':
468 elif member_type == 'user_group':
469 # check if we have permissions to alter this usergroup
469 # check if we have permissions to alter this usergroup
470 member_name = UserGroup.get(member_id).users_group_name
470 member_name = UserGroup.get(member_id).users_group_name
471 if not check_perms or has_group_perm(member_name,
471 if not check_perms or has_group_perm(member_name,
472 user=cur_user):
472 user=cur_user):
473 _set_perm_group(obj, users_group=member_id, perm=perm)
473 _set_perm_group(obj, users_group=member_id, perm=perm)
474 else:
474 else:
475 raise ValueError("member_type must be 'user' or 'user_group' "
475 raise ValueError("member_type must be 'user' or 'user_group' "
476 "got {} instead".format(member_type))
476 "got {} instead".format(member_type))
477
477
478 changes['added'].append(
478 changes['added'].append(
479 {'change_obj': change_obj, 'type': member_type,
479 {'change_obj': change_obj, 'type': member_type,
480 'id': member_id, 'name': member_name, 'new_perm': perm})
480 'id': member_id, 'name': member_name, 'new_perm': perm})
481
481
482 # delete permissions
482 # delete permissions
483 for member_id, perm, member_type in perm_deletions:
483 for member_id, perm, member_type in perm_deletions:
484 member_id = int(member_id)
484 member_id = int(member_id)
485 if member_type == 'user':
485 if member_type == 'user':
486 member_name = User.get(member_id).username
486 member_name = User.get(member_id).username
487 _revoke_perm_user(obj, user=member_id)
487 _revoke_perm_user(obj, user=member_id)
488 elif member_type == 'user_group':
488 elif member_type == 'user_group':
489 # check if we have permissions to alter this usergroup
489 # check if we have permissions to alter this usergroup
490 member_name = UserGroup.get(member_id).users_group_name
490 member_name = UserGroup.get(member_id).users_group_name
491 if not check_perms or has_group_perm(member_name,
491 if not check_perms or has_group_perm(member_name,
492 user=cur_user):
492 user=cur_user):
493 _revoke_perm_group(obj, user_group=member_id)
493 _revoke_perm_group(obj, user_group=member_id)
494 else:
494 else:
495 raise ValueError("member_type must be 'user' or 'user_group' "
495 raise ValueError("member_type must be 'user' or 'user_group' "
496 "got {} instead".format(member_type))
496 "got {} instead".format(member_type))
497
497
498 changes['deleted'].append(
498 changes['deleted'].append(
499 {'change_obj': change_obj, 'type': member_type,
499 {'change_obj': change_obj, 'type': member_type,
500 'id': member_id, 'name': member_name, 'new_perm': perm})
500 'id': member_id, 'name': member_name, 'new_perm': perm})
501
501
502 # if it's not recursive call for all,repos,groups
502 # if it's not recursive call for all,repos,groups
503 # break the loop and don't proceed with other changes
503 # break the loop and don't proceed with other changes
504 if recursive not in ['all', 'repos', 'groups']:
504 if recursive not in ['all', 'repos', 'groups']:
505 break
505 break
506
506
507 return changes
507 return changes
508
508
509 def update(self, repo_group, form_data):
509 def update(self, repo_group, form_data):
510 try:
510 try:
511 repo_group = self._get_repo_group(repo_group)
511 repo_group = self._get_repo_group(repo_group)
512 old_path = repo_group.full_path
512 old_path = repo_group.full_path
513
513
514 # change properties
514 # change properties
515 if 'group_description' in form_data:
515 if 'group_description' in form_data:
516 repo_group.group_description = form_data['group_description']
516 repo_group.group_description = form_data['group_description']
517
517
518 if 'enable_locking' in form_data:
518 if 'enable_locking' in form_data:
519 repo_group.enable_locking = form_data['enable_locking']
519 repo_group.enable_locking = form_data['enable_locking']
520
520
521 if 'group_parent_id' in form_data:
521 if 'group_parent_id' in form_data:
522 parent_group = (
522 parent_group = (
523 self._get_repo_group(form_data['group_parent_id']))
523 self._get_repo_group(form_data['group_parent_id']))
524 repo_group.group_parent_id = (
524 repo_group.group_parent_id = (
525 parent_group.group_id if parent_group else None)
525 parent_group.group_id if parent_group else None)
526 repo_group.parent_group = parent_group
526 repo_group.parent_group = parent_group
527
527
528 # mikhail: to update the full_path, we have to explicitly
528 # mikhail: to update the full_path, we have to explicitly
529 # update group_name
529 # update group_name
530 group_name = form_data.get('group_name', repo_group.name)
530 group_name = form_data.get('group_name', repo_group.name)
531 repo_group.group_name = repo_group.get_new_name(group_name)
531 repo_group.group_name = repo_group.get_new_name(group_name)
532
532
533 new_path = repo_group.full_path
533 new_path = repo_group.full_path
534
534
535 affected_user_ids = []
535 affected_user_ids = []
536 if 'user' in form_data:
536 if 'user' in form_data:
537 old_owner_id = repo_group.user.user_id
537 old_owner_id = repo_group.user.user_id
538 new_owner = User.get_by_username(form_data['user'])
538 new_owner = User.get_by_username(form_data['user'])
539 repo_group.user = new_owner
539 repo_group.user = new_owner
540
540
541 if old_owner_id != new_owner.user_id:
541 if old_owner_id != new_owner.user_id:
542 affected_user_ids = [new_owner.user_id, old_owner_id]
542 affected_user_ids = [new_owner.user_id, old_owner_id]
543
543
544 self.sa.add(repo_group)
544 self.sa.add(repo_group)
545
545
546 # iterate over all members of this groups and do fixes
546 # iterate over all members of this groups and do fixes
547 # set locking if given
547 # set locking if given
548 # if obj is a repoGroup also fix the name of the group according
548 # if obj is a repoGroup also fix the name of the group according
549 # to the parent
549 # to the parent
550 # if obj is a Repo fix it's name
550 # if obj is a Repo fix it's name
551 # this can be potentially heavy operation
551 # this can be potentially heavy operation
552 for obj in repo_group.recursive_groups_and_repos():
552 for obj in repo_group.recursive_groups_and_repos():
553 # set the value from it's parent
553 # set the value from it's parent
554 obj.enable_locking = repo_group.enable_locking
554 obj.enable_locking = repo_group.enable_locking
555 if isinstance(obj, RepoGroup):
555 if isinstance(obj, RepoGroup):
556 new_name = obj.get_new_name(obj.name)
556 new_name = obj.get_new_name(obj.name)
557 log.debug('Fixing group %s to new name %s',
557 log.debug('Fixing group %s to new name %s',
558 obj.group_name, new_name)
558 obj.group_name, new_name)
559 obj.group_name = new_name
559 obj.group_name = new_name
560
560
561 elif isinstance(obj, Repository):
561 elif isinstance(obj, Repository):
562 # we need to get all repositories from this new group and
562 # we need to get all repositories from this new group and
563 # rename them accordingly to new group path
563 # rename them accordingly to new group path
564 new_name = obj.get_new_name(obj.just_name)
564 new_name = obj.get_new_name(obj.just_name)
565 log.debug('Fixing repo %s to new name %s',
565 log.debug('Fixing repo %s to new name %s',
566 obj.repo_name, new_name)
566 obj.repo_name, new_name)
567 obj.repo_name = new_name
567 obj.repo_name = new_name
568
568
569 self.sa.add(obj)
569 self.sa.add(obj)
570
570
571 self._rename_group(old_path, new_path)
571 self._rename_group(old_path, new_path)
572
572
573 # Trigger update event.
573 # Trigger update event.
574 events.trigger(events.RepoGroupUpdateEvent(repo_group))
574 events.trigger(events.RepoGroupUpdateEvent(repo_group))
575
575
576 if affected_user_ids:
576 if affected_user_ids:
577 PermissionModel().trigger_permission_flush(affected_user_ids)
577 PermissionModel().trigger_permission_flush(affected_user_ids)
578
578
579 return repo_group
579 return repo_group
580 except Exception:
580 except Exception:
581 log.error(traceback.format_exc())
581 log.error(traceback.format_exc())
582 raise
582 raise
583
583
584 def delete(self, repo_group, force_delete=False, fs_remove=True):
584 def delete(self, repo_group, force_delete=False, fs_remove=True):
585 repo_group = self._get_repo_group(repo_group)
585 repo_group = self._get_repo_group(repo_group)
586 if not repo_group:
586 if not repo_group:
587 return False
587 return False
588 try:
588 try:
589 self.sa.delete(repo_group)
589 self.sa.delete(repo_group)
590 if fs_remove:
590 if fs_remove:
591 self._delete_filesystem_group(repo_group, force_delete)
591 self._delete_filesystem_group(repo_group, force_delete)
592 else:
592 else:
593 log.debug('skipping removal from filesystem')
593 log.debug('skipping removal from filesystem')
594
594
595 # Trigger delete event.
595 # Trigger delete event.
596 events.trigger(events.RepoGroupDeleteEvent(repo_group))
596 events.trigger(events.RepoGroupDeleteEvent(repo_group))
597 return True
597 return True
598
598
599 except Exception:
599 except Exception:
600 log.error('Error removing repo_group %s', repo_group)
600 log.error('Error removing repo_group %s', repo_group)
601 raise
601 raise
602
602
603 def grant_user_permission(self, repo_group, user, perm):
603 def grant_user_permission(self, repo_group, user, perm):
604 """
604 """
605 Grant permission for user on given repository group, or update
605 Grant permission for user on given repository group, or update
606 existing one if found
606 existing one if found
607
607
608 :param repo_group: Instance of RepoGroup, repositories_group_id,
608 :param repo_group: Instance of RepoGroup, repositories_group_id,
609 or repositories_group name
609 or repositories_group name
610 :param user: Instance of User, user_id or username
610 :param user: Instance of User, user_id or username
611 :param perm: Instance of Permission, or permission_name
611 :param perm: Instance of Permission, or permission_name
612 """
612 """
613
613
614 repo_group = self._get_repo_group(repo_group)
614 repo_group = self._get_repo_group(repo_group)
615 user = self._get_user(user)
615 user = self._get_user(user)
616 permission = self._get_perm(perm)
616 permission = self._get_perm(perm)
617
617
618 # check if we have that permission already
618 # check if we have that permission already
619 obj = self.sa.query(UserRepoGroupToPerm)\
619 obj = self.sa.query(UserRepoGroupToPerm)\
620 .filter(UserRepoGroupToPerm.user == user)\
620 .filter(UserRepoGroupToPerm.user == user)\
621 .filter(UserRepoGroupToPerm.group == repo_group)\
621 .filter(UserRepoGroupToPerm.group == repo_group)\
622 .scalar()
622 .scalar()
623 if obj is None:
623 if obj is None:
624 # create new !
624 # create new !
625 obj = UserRepoGroupToPerm()
625 obj = UserRepoGroupToPerm()
626 obj.group = repo_group
626 obj.group = repo_group
627 obj.user = user
627 obj.user = user
628 obj.permission = permission
628 obj.permission = permission
629 self.sa.add(obj)
629 self.sa.add(obj)
630 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
630 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
631 action_logger_generic(
631 action_logger_generic(
632 'granted permission: {} to user: {} on repogroup: {}'.format(
632 'granted permission: {} to user: {} on repogroup: {}'.format(
633 perm, user, repo_group), namespace='security.repogroup')
633 perm, user, repo_group), namespace='security.repogroup')
634 return obj
634 return obj
635
635
636 def revoke_user_permission(self, repo_group, user):
636 def revoke_user_permission(self, repo_group, user):
637 """
637 """
638 Revoke permission for user on given repository group
638 Revoke permission for user on given repository group
639
639
640 :param repo_group: Instance of RepoGroup, repositories_group_id,
640 :param repo_group: Instance of RepoGroup, repositories_group_id,
641 or repositories_group name
641 or repositories_group name
642 :param user: Instance of User, user_id or username
642 :param user: Instance of User, user_id or username
643 """
643 """
644
644
645 repo_group = self._get_repo_group(repo_group)
645 repo_group = self._get_repo_group(repo_group)
646 user = self._get_user(user)
646 user = self._get_user(user)
647
647
648 obj = self.sa.query(UserRepoGroupToPerm)\
648 obj = self.sa.query(UserRepoGroupToPerm)\
649 .filter(UserRepoGroupToPerm.user == user)\
649 .filter(UserRepoGroupToPerm.user == user)\
650 .filter(UserRepoGroupToPerm.group == repo_group)\
650 .filter(UserRepoGroupToPerm.group == repo_group)\
651 .scalar()
651 .scalar()
652 if obj:
652 if obj:
653 self.sa.delete(obj)
653 self.sa.delete(obj)
654 log.debug('Revoked perm on %s on %s', repo_group, user)
654 log.debug('Revoked perm on %s on %s', repo_group, user)
655 action_logger_generic(
655 action_logger_generic(
656 'revoked permission from user: {} on repogroup: {}'.format(
656 'revoked permission from user: {} on repogroup: {}'.format(
657 user, repo_group), namespace='security.repogroup')
657 user, repo_group), namespace='security.repogroup')
658
658
659 def grant_user_group_permission(self, repo_group, group_name, perm):
659 def grant_user_group_permission(self, repo_group, group_name, perm):
660 """
660 """
661 Grant permission for user group on given repository group, or update
661 Grant permission for user group on given repository group, or update
662 existing one if found
662 existing one if found
663
663
664 :param repo_group: Instance of RepoGroup, repositories_group_id,
664 :param repo_group: Instance of RepoGroup, repositories_group_id,
665 or repositories_group name
665 or repositories_group name
666 :param group_name: Instance of UserGroup, users_group_id,
666 :param group_name: Instance of UserGroup, users_group_id,
667 or user group name
667 or user group name
668 :param perm: Instance of Permission, or permission_name
668 :param perm: Instance of Permission, or permission_name
669 """
669 """
670 repo_group = self._get_repo_group(repo_group)
670 repo_group = self._get_repo_group(repo_group)
671 group_name = self._get_user_group(group_name)
671 group_name = self._get_user_group(group_name)
672 permission = self._get_perm(perm)
672 permission = self._get_perm(perm)
673
673
674 # check if we have that permission already
674 # check if we have that permission already
675 obj = self.sa.query(UserGroupRepoGroupToPerm)\
675 obj = self.sa.query(UserGroupRepoGroupToPerm)\
676 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
676 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
677 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
677 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
678 .scalar()
678 .scalar()
679
679
680 if obj is None:
680 if obj is None:
681 # create new
681 # create new
682 obj = UserGroupRepoGroupToPerm()
682 obj = UserGroupRepoGroupToPerm()
683
683
684 obj.group = repo_group
684 obj.group = repo_group
685 obj.users_group = group_name
685 obj.users_group = group_name
686 obj.permission = permission
686 obj.permission = permission
687 self.sa.add(obj)
687 self.sa.add(obj)
688 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
688 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
689 action_logger_generic(
689 action_logger_generic(
690 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
690 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
691 perm, group_name, repo_group), namespace='security.repogroup')
691 perm, group_name, repo_group), namespace='security.repogroup')
692 return obj
692 return obj
693
693
694 def revoke_user_group_permission(self, repo_group, group_name):
694 def revoke_user_group_permission(self, repo_group, group_name):
695 """
695 """
696 Revoke permission for user group on given repository group
696 Revoke permission for user group on given repository group
697
697
698 :param repo_group: Instance of RepoGroup, repositories_group_id,
698 :param repo_group: Instance of RepoGroup, repositories_group_id,
699 or repositories_group name
699 or repositories_group name
700 :param group_name: Instance of UserGroup, users_group_id,
700 :param group_name: Instance of UserGroup, users_group_id,
701 or user group name
701 or user group name
702 """
702 """
703 repo_group = self._get_repo_group(repo_group)
703 repo_group = self._get_repo_group(repo_group)
704 group_name = self._get_user_group(group_name)
704 group_name = self._get_user_group(group_name)
705
705
706 obj = self.sa.query(UserGroupRepoGroupToPerm)\
706 obj = self.sa.query(UserGroupRepoGroupToPerm)\
707 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
707 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
708 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
708 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
709 .scalar()
709 .scalar()
710 if obj:
710 if obj:
711 self.sa.delete(obj)
711 self.sa.delete(obj)
712 log.debug('Revoked perm to %s on %s', repo_group, group_name)
712 log.debug('Revoked perm to %s on %s', repo_group, group_name)
713 action_logger_generic(
713 action_logger_generic(
714 'revoked permission from usergroup: {} on repogroup: {}'.format(
714 'revoked permission from usergroup: {} on repogroup: {}'.format(
715 group_name, repo_group), namespace='security.repogroup')
715 group_name, repo_group), namespace='security.repogroup')
716
716
717 @classmethod
717 @classmethod
718 def update_commit_cache(cls, repo_groups=None):
718 def update_commit_cache(cls, repo_groups=None):
719 if not repo_groups:
719 if not repo_groups:
720 repo_groups = RepoGroup.getAll()
720 repo_groups = RepoGroup.getAll()
721 for repo_group in repo_groups:
721 for repo_group in repo_groups:
722 repo_group.update_commit_cache()
722 repo_group.update_commit_cache()
723
723
724 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
724 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
725 super_user_actions=False):
725 super_user_actions=False):
726
726
727 from pyramid.threadlocal import get_current_request
727 from pyramid.threadlocal import get_current_request
728 _render = get_current_request().get_partial_renderer(
728 _render = get_current_request().get_partial_renderer(
729 'rhodecode:templates/data_table/_dt_elements.mako')
729 'rhodecode:templates/data_table/_dt_elements.mako')
730 c = _render.get_call_context()
730 c = _render.get_call_context()
731 h = _render.get_helpers()
731 h = _render.get_helpers()
732
732
733 def quick_menu(repo_group_name):
733 def quick_menu(repo_group_name):
734 return _render('quick_repo_group_menu', repo_group_name)
734 return _render('quick_repo_group_menu', repo_group_name)
735
735
736 def repo_group_lnk(repo_group_name):
736 def repo_group_lnk(repo_group_name):
737 return _render('repo_group_name', repo_group_name)
737 return _render('repo_group_name', repo_group_name)
738
738
739 def last_change(last_change):
739 def last_change(last_change):
740 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
740 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
741 ts = time.time()
741 ts = time.time()
742 utc_offset = (datetime.datetime.fromtimestamp(ts)
742 utc_offset = (datetime.datetime.fromtimestamp(ts)
743 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
743 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
744 last_change = last_change + datetime.timedelta(seconds=utc_offset)
744 last_change = last_change + datetime.timedelta(seconds=utc_offset)
745 return _render("last_change", last_change)
745 return _render("last_change", last_change)
746
746
747 def desc(desc, personal):
747 def desc(desc, personal):
748 return _render(
748 return _render(
749 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
749 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
750
750
751 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
751 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
752 return _render(
752 return _render(
753 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
753 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
754
754
755 def repo_group_name(repo_group_name, children_groups):
755 def repo_group_name(repo_group_name, children_groups):
756 return _render("repo_group_name", repo_group_name, children_groups)
756 return _render("repo_group_name", repo_group_name, children_groups)
757
757
758 def user_profile(username):
758 def user_profile(username):
759 return _render('user_profile', username)
759 return _render('user_profile', username)
760
760
761 repo_group_data = []
761 repo_group_data = []
762 for group in repo_group_list:
762 for group in repo_group_list:
763 # NOTE(marcink): because we use only raw column we need to load it like that
763 # NOTE(marcink): because we use only raw column we need to load it like that
764 changeset_cache = RepoGroup._load_changeset_cache(
764 changeset_cache = RepoGroup._load_changeset_cache(
765 '', group._changeset_cache)
765 '', group._changeset_cache)
766 last_commit_change = RepoGroup._load_commit_change(changeset_cache)
766 last_commit_change = RepoGroup._load_commit_change(changeset_cache)
767 row = {
767 row = {
768 "menu": quick_menu(group.group_name),
768 "menu": quick_menu(group.group_name),
769 "name": repo_group_lnk(group.group_name),
769 "name": repo_group_lnk(group.group_name),
770 "name_raw": group.group_name,
770 "name_raw": group.group_name,
771
771
772 "last_change": last_change(last_commit_change),
772 "last_change": last_change(last_commit_change),
773
773
774 "last_changeset": "",
774 "last_changeset": "",
775 "last_changeset_raw": "",
775 "last_changeset_raw": "",
776
776
777 "desc": desc(h.escape(group.group_description), group.personal),
777 "desc": desc(h.escape(group.group_description), group.personal),
778 "top_level_repos": 0,
778 "top_level_repos": 0,
779 "owner": user_profile(group.User.username)
779 "owner": user_profile(group.User.username)
780 }
780 }
781 if admin:
781 if admin:
782 repo_count = group.repositories.count()
782 repo_count = group.repositories.count()
783 children_groups = map(
783 children_groups = map(
784 h.safe_unicode,
784 h.safe_unicode,
785 itertools.chain((g.name for g in group.parents),
785 itertools.chain((g.name for g in group.parents),
786 (x.name for x in [group])))
786 (x.name for x in [group])))
787 row.update({
787 row.update({
788 "action": repo_group_actions(
788 "action": repo_group_actions(
789 group.group_id, group.group_name, repo_count),
789 group.group_id, group.group_name, repo_count),
790 "top_level_repos": repo_count,
790 "top_level_repos": repo_count,
791 "name": repo_group_name(group.group_name, children_groups),
791 "name": repo_group_name(group.group_name, children_groups),
792
792
793 })
793 })
794 repo_group_data.append(row)
794 repo_group_data.append(row)
795
795
796 return repo_group_data
796 return repo_group_data
797
797
798 def get_repo_groups_data_table(
798 def get_repo_groups_data_table(
799 self, draw, start, limit,
799 self, draw, start, limit,
800 search_q, order_by, order_dir,
800 search_q, order_by, order_dir,
801 auth_user, repo_group_id):
801 auth_user, repo_group_id):
802 from rhodecode.model.scm import RepoGroupList
802 from rhodecode.model.scm import RepoGroupList
803
803
804 _perms = ['group.read', 'group.write', 'group.admin']
804 _perms = ['group.read', 'group.write', 'group.admin']
805 repo_groups = RepoGroup.query() \
805 repo_groups = RepoGroup.query() \
806 .filter(RepoGroup.group_parent_id == repo_group_id) \
806 .filter(RepoGroup.group_parent_id == repo_group_id) \
807 .all()
807 .all()
808 auth_repo_group_list = RepoGroupList(
808 auth_repo_group_list = RepoGroupList(
809 repo_groups, perm_set=_perms,
809 repo_groups, perm_set=_perms,
810 extra_kwargs=dict(user=auth_user))
810 extra_kwargs=dict(user=auth_user))
811
811
812 allowed_ids = [-1]
812 allowed_ids = [-1]
813 for repo_group in auth_repo_group_list:
813 for repo_group in auth_repo_group_list:
814 allowed_ids.append(repo_group.group_id)
814 allowed_ids.append(repo_group.group_id)
815
815
816 repo_groups_data_total_count = RepoGroup.query() \
816 repo_groups_data_total_count = RepoGroup.query() \
817 .filter(RepoGroup.group_parent_id == repo_group_id) \
817 .filter(RepoGroup.group_parent_id == repo_group_id) \
818 .filter(or_(
818 .filter(or_(
819 # generate multiple IN to fix limitation problems
819 # generate multiple IN to fix limitation problems
820 *in_filter_generator(RepoGroup.group_id, allowed_ids))
820 *in_filter_generator(RepoGroup.group_id, allowed_ids))
821 ) \
821 ) \
822 .count()
822 .count()
823
823
824 base_q = Session.query(
824 base_q = Session.query(
825 RepoGroup.group_name,
825 RepoGroup.group_name,
826 RepoGroup.group_name_hash,
826 RepoGroup.group_name_hash,
827 RepoGroup.group_description,
827 RepoGroup.group_description,
828 RepoGroup.group_id,
828 RepoGroup.group_id,
829 RepoGroup.personal,
829 RepoGroup.personal,
830 RepoGroup.updated_on,
830 RepoGroup.updated_on,
831 RepoGroup._changeset_cache,
831 RepoGroup._changeset_cache,
832 User,
832 User,
833 ) \
833 ) \
834 .filter(RepoGroup.group_parent_id == repo_group_id) \
834 .filter(RepoGroup.group_parent_id == repo_group_id) \
835 .filter(or_(
835 .filter(or_(
836 # generate multiple IN to fix limitation problems
836 # generate multiple IN to fix limitation problems
837 *in_filter_generator(RepoGroup.group_id, allowed_ids))
837 *in_filter_generator(RepoGroup.group_id, allowed_ids))
838 ) \
838 ) \
839 .join(User, User.user_id == RepoGroup.user_id) \
839 .join(User, User.user_id == RepoGroup.user_id) \
840 .group_by(RepoGroup, User)
840 .group_by(RepoGroup, User)
841
841
842 repo_groups_data_total_filtered_count = base_q.count()
842 repo_groups_data_total_filtered_count = base_q.count()
843
843
844 sort_defined = False
844 sort_defined = False
845
845
846 if order_by == 'group_name':
846 if order_by == 'group_name':
847 sort_col = func.lower(RepoGroup.group_name)
847 sort_col = func.lower(RepoGroup.group_name)
848 sort_defined = True
848 sort_defined = True
849 elif order_by == 'user_username':
849 elif order_by == 'user_username':
850 sort_col = User.username
850 sort_col = User.username
851 else:
851 else:
852 sort_col = getattr(RepoGroup, order_by, None)
852 sort_col = getattr(RepoGroup, order_by, None)
853
853
854 if sort_defined or sort_col:
854 if sort_defined or sort_col:
855 if order_dir == 'asc':
855 if order_dir == 'asc':
856 sort_col = sort_col.asc()
856 sort_col = sort_col.asc()
857 else:
857 else:
858 sort_col = sort_col.desc()
858 sort_col = sort_col.desc()
859
859
860 base_q = base_q.order_by(sort_col)
860 base_q = base_q.order_by(sort_col)
861 base_q = base_q.offset(start).limit(limit)
861 base_q = base_q.offset(start).limit(limit)
862
862
863 repo_group_list = base_q.all()
863 repo_group_list = base_q.all()
864
864
865 repo_groups_data = RepoGroupModel().get_repo_groups_as_dict(
865 repo_groups_data = RepoGroupModel().get_repo_groups_as_dict(
866 repo_group_list=repo_group_list, admin=False)
866 repo_group_list=repo_group_list, admin=False)
867
867
868 data = ({
868 data = ({
869 'draw': draw,
869 'draw': draw,
870 'data': repo_groups_data,
870 'data': repo_groups_data,
871 'recordsTotal': repo_groups_data_total_count,
871 'recordsTotal': repo_groups_data_total_count,
872 'recordsFiltered': repo_groups_data_total_filtered_count,
872 'recordsFiltered': repo_groups_data_total_filtered_count,
873 })
873 })
874 return data
874 return data
875
875
876 def _get_defaults(self, repo_group_name):
876 def _get_defaults(self, repo_group_name):
877 repo_group = RepoGroup.get_by_group_name(repo_group_name)
877 repo_group = RepoGroup.get_by_group_name(repo_group_name)
878
878
879 if repo_group is None:
879 if repo_group is None:
880 return None
880 return None
881
881
882 defaults = repo_group.get_dict()
882 defaults = repo_group.get_dict()
883 defaults['repo_group_name'] = repo_group.name
883 defaults['repo_group_name'] = repo_group.name
884 defaults['repo_group_description'] = repo_group.group_description
884 defaults['repo_group_description'] = repo_group.group_description
885 defaults['repo_group_enable_locking'] = repo_group.enable_locking
885 defaults['repo_group_enable_locking'] = repo_group.enable_locking
886
886
887 # we use -1 as this is how in HTML, we mark an empty group
887 # we use -1 as this is how in HTML, we mark an empty group
888 defaults['repo_group'] = defaults['group_parent_id'] or -1
888 defaults['repo_group'] = defaults['group_parent_id'] or -1
889
889
890 # fill owner
890 # fill owner
891 if repo_group.user:
891 if repo_group.user:
892 defaults.update({'user': repo_group.user.username})
892 defaults.update({'user': repo_group.user.username})
893 else:
893 else:
894 replacement_user = User.get_first_super_admin().username
894 replacement_user = User.get_first_super_admin().username
895 defaults.update({'user': replacement_user})
895 defaults.update({'user': replacement_user})
896
896
897 return defaults
897 return defaults
@@ -1,1047 +1,1047 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 import datetime
27 import datetime
28 import ipaddress
28 import ipaddress
29
29
30 from pyramid.threadlocal import get_current_request
30 from pyramid.threadlocal import get_current_request
31 from sqlalchemy.exc import DatabaseError
31 from sqlalchemy.exc import DatabaseError
32
32
33 from rhodecode import events
33 from rhodecode import events
34 from rhodecode.lib.user_log_filter import user_log_filter
34 from rhodecode.lib.user_log_filter import user_log_filter
35 from rhodecode.lib.utils2 import (
35 from rhodecode.lib.utils2 import (
36 safe_unicode, get_current_rhodecode_user, action_logger_generic,
36 safe_unicode, get_current_rhodecode_user, action_logger_generic,
37 AttributeDict, str2bool)
37 AttributeDict, str2bool)
38 from rhodecode.lib.exceptions import (
38 from rhodecode.lib.exceptions import (
39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
39 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserOwnsUserGroupsException, NotAllowedToCreateUserError,
40 UserOwnsUserGroupsException, NotAllowedToCreateUserError,
41 UserOwnsPullRequestsException, UserOwnsArtifactsException)
41 UserOwnsPullRequestsException, UserOwnsArtifactsException)
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.db import (
44 from rhodecode.model.db import (
45 _hash_key, func, true, false, or_, joinedload, User, UserToPerm,
45 _hash_key, func, true, false, or_, joinedload, User, UserToPerm,
46 UserEmailMap, UserIpMap, UserLog)
46 UserEmailMap, UserIpMap, UserLog)
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.auth_token import AuthTokenModel
48 from rhodecode.model.auth_token import AuthTokenModel
49 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 class UserModel(BaseModel):
54 class UserModel(BaseModel):
55 cls = User
55 cls = User
56
56
57 def get(self, user_id, cache=False):
57 def get(self, user_id, cache=False):
58 user = self.sa.query(User)
58 user = self.sa.query(User)
59 if cache:
59 if cache:
60 user = user.options(
60 user = user.options(
61 FromCache("sql_cache_short", "get_user_%s" % user_id))
61 FromCache("sql_cache_short", f"get_user_{user_id}"))
62 return user.get(user_id)
62 return user.get(user_id)
63
63
64 def get_user(self, user):
64 def get_user(self, user):
65 return self._get_user(user)
65 return self._get_user(user)
66
66
67 def _serialize_user(self, user):
67 def _serialize_user(self, user):
68 import rhodecode.lib.helpers as h
68 import rhodecode.lib.helpers as h
69
69
70 return {
70 return {
71 'id': user.user_id,
71 'id': user.user_id,
72 'first_name': user.first_name,
72 'first_name': user.first_name,
73 'last_name': user.last_name,
73 'last_name': user.last_name,
74 'username': user.username,
74 'username': user.username,
75 'email': user.email,
75 'email': user.email,
76 'icon_link': h.gravatar_url(user.email, 30),
76 'icon_link': h.gravatar_url(user.email, 30),
77 'profile_link': h.link_to_user(user),
77 'profile_link': h.link_to_user(user),
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 # sort by len to have top most matches first
99 # sort by len to have top most matches first
100 query = query.order_by(func.length(User.username))\
100 query = query.order_by(func.length(User.username))\
101 .order_by(User.username)
101 .order_by(User.username)
102 query = query.limit(limit)
102 query = query.limit(limit)
103
103
104 users = query.all()
104 users = query.all()
105
105
106 _users = [
106 _users = [
107 self._serialize_user(user) for user in users
107 self._serialize_user(user) for user in users
108 ]
108 ]
109 return _users
109 return _users
110
110
111 def get_by_username(self, username, cache=False, case_insensitive=False):
111 def get_by_username(self, username, cache=False, case_insensitive=False):
112
112
113 if case_insensitive:
113 if case_insensitive:
114 user = self.sa.query(User).filter(User.username.ilike(username))
114 user = self.sa.query(User).filter(User.username.ilike(username))
115 else:
115 else:
116 user = self.sa.query(User)\
116 user = self.sa.query(User)\
117 .filter(User.username == username)
117 .filter(User.username == username)
118 if cache:
118 if cache:
119 name_key = _hash_key(username)
119 name_key = _hash_key(username)
120 user = user.options(
120 user = user.options(
121 FromCache("sql_cache_short", "get_user_%s" % name_key))
121 FromCache("sql_cache_short", f"get_user_{name_key}"))
122 return user.scalar()
122 return user.scalar()
123
123
124 def get_by_email(self, email, cache=False, case_insensitive=False):
124 def get_by_email(self, email, cache=False, case_insensitive=False):
125 return User.get_by_email(email, case_insensitive, cache)
125 return User.get_by_email(email, case_insensitive, cache)
126
126
127 def get_by_auth_token(self, auth_token, cache=False):
127 def get_by_auth_token(self, auth_token, cache=False):
128 return User.get_by_auth_token(auth_token, cache)
128 return User.get_by_auth_token(auth_token, cache)
129
129
130 def get_active_user_count(self, cache=False):
130 def get_active_user_count(self, cache=False):
131 qry = User.query().filter(
131 qry = User.query().filter(
132 User.active == true()).filter(
132 User.active == true()).filter(
133 User.username != User.DEFAULT_USER)
133 User.username != User.DEFAULT_USER)
134 if cache:
134 if cache:
135 qry = qry.options(
135 qry = qry.options(
136 FromCache("sql_cache_short", "get_active_users"))
136 FromCache("sql_cache_short", "get_active_users"))
137 return qry.count()
137 return qry.count()
138
138
139 def create(self, form_data, cur_user=None):
139 def create(self, form_data, cur_user=None):
140 if not cur_user:
140 if not cur_user:
141 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
141 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
142
142
143 user_data = {
143 user_data = {
144 'username': form_data['username'],
144 'username': form_data['username'],
145 'password': form_data['password'],
145 'password': form_data['password'],
146 'email': form_data['email'],
146 'email': form_data['email'],
147 'firstname': form_data['firstname'],
147 'firstname': form_data['firstname'],
148 'lastname': form_data['lastname'],
148 'lastname': form_data['lastname'],
149 'active': form_data['active'],
149 'active': form_data['active'],
150 'extern_type': form_data['extern_type'],
150 'extern_type': form_data['extern_type'],
151 'extern_name': form_data['extern_name'],
151 'extern_name': form_data['extern_name'],
152 'admin': False,
152 'admin': False,
153 'cur_user': cur_user
153 'cur_user': cur_user
154 }
154 }
155
155
156 if 'create_repo_group' in form_data:
156 if 'create_repo_group' in form_data:
157 user_data['create_repo_group'] = str2bool(
157 user_data['create_repo_group'] = str2bool(
158 form_data.get('create_repo_group'))
158 form_data.get('create_repo_group'))
159
159
160 try:
160 try:
161 if form_data.get('password_change'):
161 if form_data.get('password_change'):
162 user_data['force_password_change'] = True
162 user_data['force_password_change'] = True
163 return UserModel().create_or_update(**user_data)
163 return UserModel().create_or_update(**user_data)
164 except Exception:
164 except Exception:
165 log.error(traceback.format_exc())
165 log.error(traceback.format_exc())
166 raise
166 raise
167
167
168 def update_user(self, user, skip_attrs=None, **kwargs):
168 def update_user(self, user, skip_attrs=None, **kwargs):
169 from rhodecode.lib.auth import get_crypt_password
169 from rhodecode.lib.auth import get_crypt_password
170
170
171 user = self._get_user(user)
171 user = self._get_user(user)
172 if user.username == User.DEFAULT_USER:
172 if user.username == User.DEFAULT_USER:
173 raise DefaultUserException(
173 raise DefaultUserException(
174 "You can't edit this user (`%(username)s`) since it's "
174 "You can't edit this user (`%(username)s`) since it's "
175 "crucial for entire application" % {
175 "crucial for entire application" % {
176 'username': user.username})
176 'username': user.username})
177
177
178 # first store only defaults
178 # first store only defaults
179 user_attrs = {
179 user_attrs = {
180 'updating_user_id': user.user_id,
180 'updating_user_id': user.user_id,
181 'username': user.username,
181 'username': user.username,
182 'password': user.password,
182 'password': user.password,
183 'email': user.email,
183 'email': user.email,
184 'firstname': user.name,
184 'firstname': user.name,
185 'lastname': user.lastname,
185 'lastname': user.lastname,
186 'description': user.description,
186 'description': user.description,
187 'active': user.active,
187 'active': user.active,
188 'admin': user.admin,
188 'admin': user.admin,
189 'extern_name': user.extern_name,
189 'extern_name': user.extern_name,
190 'extern_type': user.extern_type,
190 'extern_type': user.extern_type,
191 'language': user.user_data.get('language')
191 'language': user.user_data.get('language')
192 }
192 }
193
193
194 # in case there's new_password, that comes from form, use it to
194 # in case there's new_password, that comes from form, use it to
195 # store password
195 # store password
196 if kwargs.get('new_password'):
196 if kwargs.get('new_password'):
197 kwargs['password'] = kwargs['new_password']
197 kwargs['password'] = kwargs['new_password']
198
198
199 # cleanups, my_account password change form
199 # cleanups, my_account password change form
200 kwargs.pop('current_password', None)
200 kwargs.pop('current_password', None)
201 kwargs.pop('new_password', None)
201 kwargs.pop('new_password', None)
202
202
203 # cleanups, user edit password change form
203 # cleanups, user edit password change form
204 kwargs.pop('password_confirmation', None)
204 kwargs.pop('password_confirmation', None)
205 kwargs.pop('password_change', None)
205 kwargs.pop('password_change', None)
206
206
207 # create repo group on user creation
207 # create repo group on user creation
208 kwargs.pop('create_repo_group', None)
208 kwargs.pop('create_repo_group', None)
209
209
210 # legacy forms send name, which is the firstname
210 # legacy forms send name, which is the firstname
211 firstname = kwargs.pop('name', None)
211 firstname = kwargs.pop('name', None)
212 if firstname:
212 if firstname:
213 kwargs['firstname'] = firstname
213 kwargs['firstname'] = firstname
214
214
215 for k, v in kwargs.items():
215 for k, v in kwargs.items():
216 # skip if we don't want to update this
216 # skip if we don't want to update this
217 if skip_attrs and k in skip_attrs:
217 if skip_attrs and k in skip_attrs:
218 continue
218 continue
219
219
220 user_attrs[k] = v
220 user_attrs[k] = v
221
221
222 try:
222 try:
223 return self.create_or_update(**user_attrs)
223 return self.create_or_update(**user_attrs)
224 except Exception:
224 except Exception:
225 log.error(traceback.format_exc())
225 log.error(traceback.format_exc())
226 raise
226 raise
227
227
228 def create_or_update(
228 def create_or_update(
229 self, username, password, email, firstname='', lastname='',
229 self, username, password, email, firstname='', lastname='',
230 active=True, admin=False, extern_type=None, extern_name=None,
230 active=True, admin=False, extern_type=None, extern_name=None,
231 cur_user=None, plugin=None, force_password_change=False,
231 cur_user=None, plugin=None, force_password_change=False,
232 allow_to_create_user=True, create_repo_group=None,
232 allow_to_create_user=True, create_repo_group=None,
233 updating_user_id=None, language=None, description='',
233 updating_user_id=None, language=None, description='',
234 strict_creation_check=True):
234 strict_creation_check=True):
235 """
235 """
236 Creates a new instance if not found, or updates current one
236 Creates a new instance if not found, or updates current one
237
237
238 :param username:
238 :param username:
239 :param password:
239 :param password:
240 :param email:
240 :param email:
241 :param firstname:
241 :param firstname:
242 :param lastname:
242 :param lastname:
243 :param active:
243 :param active:
244 :param admin:
244 :param admin:
245 :param extern_type:
245 :param extern_type:
246 :param extern_name:
246 :param extern_name:
247 :param cur_user:
247 :param cur_user:
248 :param plugin: optional plugin this method was called from
248 :param plugin: optional plugin this method was called from
249 :param force_password_change: toggles new or existing user flag
249 :param force_password_change: toggles new or existing user flag
250 for password change
250 for password change
251 :param allow_to_create_user: Defines if the method can actually create
251 :param allow_to_create_user: Defines if the method can actually create
252 new users
252 new users
253 :param create_repo_group: Defines if the method should also
253 :param create_repo_group: Defines if the method should also
254 create an repo group with user name, and owner
254 create an repo group with user name, and owner
255 :param updating_user_id: if we set it up this is the user we want to
255 :param updating_user_id: if we set it up this is the user we want to
256 update this allows to editing username.
256 update this allows to editing username.
257 :param language: language of user from interface.
257 :param language: language of user from interface.
258 :param description: user description
258 :param description: user description
259 :param strict_creation_check: checks for allowed creation license wise etc.
259 :param strict_creation_check: checks for allowed creation license wise etc.
260
260
261 :returns: new User object with injected `is_new_user` attribute.
261 :returns: new User object with injected `is_new_user` attribute.
262 """
262 """
263
263
264 if not cur_user:
264 if not cur_user:
265 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
265 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
266
266
267 from rhodecode.lib.auth import (
267 from rhodecode.lib.auth import (
268 get_crypt_password, check_password)
268 get_crypt_password, check_password)
269 from rhodecode.lib import hooks_base
269 from rhodecode.lib import hooks_base
270
270
271 def _password_change(new_user, password):
271 def _password_change(new_user, password):
272 old_password = new_user.password or ''
272 old_password = new_user.password or ''
273 # empty password
273 # empty password
274 if not old_password:
274 if not old_password:
275 return False
275 return False
276
276
277 # password check is only needed for RhodeCode internal auth calls
277 # password check is only needed for RhodeCode internal auth calls
278 # in case it's a plugin we don't care
278 # in case it's a plugin we don't care
279 if not plugin:
279 if not plugin:
280
280
281 # first check if we gave crypted password back, and if it
281 # first check if we gave crypted password back, and if it
282 # matches it's not password change
282 # matches it's not password change
283 if new_user.password == password:
283 if new_user.password == password:
284 return False
284 return False
285
285
286 password_match = check_password(password, old_password)
286 password_match = check_password(password, old_password)
287 if not password_match:
287 if not password_match:
288 return True
288 return True
289
289
290 return False
290 return False
291
291
292 # read settings on default personal repo group creation
292 # read settings on default personal repo group creation
293 if create_repo_group is None:
293 if create_repo_group is None:
294 default_create_repo_group = RepoGroupModel()\
294 default_create_repo_group = RepoGroupModel()\
295 .get_default_create_personal_repo_group()
295 .get_default_create_personal_repo_group()
296 create_repo_group = default_create_repo_group
296 create_repo_group = default_create_repo_group
297
297
298 user_data = {
298 user_data = {
299 'username': username,
299 'username': username,
300 'password': password,
300 'password': password,
301 'email': email,
301 'email': email,
302 'firstname': firstname,
302 'firstname': firstname,
303 'lastname': lastname,
303 'lastname': lastname,
304 'active': active,
304 'active': active,
305 'admin': admin
305 'admin': admin
306 }
306 }
307
307
308 if updating_user_id:
308 if updating_user_id:
309 log.debug('Checking for existing account in RhodeCode '
309 log.debug('Checking for existing account in RhodeCode '
310 'database with user_id `%s` ', updating_user_id)
310 'database with user_id `%s` ', updating_user_id)
311 user = User.get(updating_user_id)
311 user = User.get(updating_user_id)
312 else:
312 else:
313 log.debug('Checking for existing account in RhodeCode '
313 log.debug('Checking for existing account in RhodeCode '
314 'database with username `%s` ', username)
314 'database with username `%s` ', username)
315 user = User.get_by_username(username, case_insensitive=True)
315 user = User.get_by_username(username, case_insensitive=True)
316
316
317 if user is None:
317 if user is None:
318 # we check internal flag if this method is actually allowed to
318 # we check internal flag if this method is actually allowed to
319 # create new user
319 # create new user
320 if not allow_to_create_user:
320 if not allow_to_create_user:
321 msg = ('Method wants to create new user, but it is not '
321 msg = ('Method wants to create new user, but it is not '
322 'allowed to do so')
322 'allowed to do so')
323 log.warning(msg)
323 log.warning(msg)
324 raise NotAllowedToCreateUserError(msg)
324 raise NotAllowedToCreateUserError(msg)
325
325
326 log.debug('Creating new user %s', username)
326 log.debug('Creating new user %s', username)
327
327
328 # only if we create user that is active
328 # only if we create user that is active
329 new_active_user = active
329 new_active_user = active
330 if new_active_user and strict_creation_check:
330 if new_active_user and strict_creation_check:
331 # raises UserCreationError if it's not allowed for any reason to
331 # raises UserCreationError if it's not allowed for any reason to
332 # create new active user, this also executes pre-create hooks
332 # create new active user, this also executes pre-create hooks
333 hooks_base.check_allowed_create_user(user_data, cur_user, strict_check=True)
333 hooks_base.check_allowed_create_user(user_data, cur_user, strict_check=True)
334 events.trigger(events.UserPreCreate(user_data))
334 events.trigger(events.UserPreCreate(user_data))
335 new_user = User()
335 new_user = User()
336 edit = False
336 edit = False
337 else:
337 else:
338 log.debug('updating user `%s`', username)
338 log.debug('updating user `%s`', username)
339 events.trigger(events.UserPreUpdate(user, user_data))
339 events.trigger(events.UserPreUpdate(user, user_data))
340 new_user = user
340 new_user = user
341 edit = True
341 edit = True
342
342
343 # we're not allowed to edit default user
343 # we're not allowed to edit default user
344 if user.username == User.DEFAULT_USER:
344 if user.username == User.DEFAULT_USER:
345 raise DefaultUserException(
345 raise DefaultUserException(
346 "You can't edit this user (`%(username)s`) since it's "
346 "You can't edit this user (`%(username)s`) since it's "
347 "crucial for entire application"
347 "crucial for entire application"
348 % {'username': user.username})
348 % {'username': user.username})
349
349
350 # inject special attribute that will tell us if User is new or old
350 # inject special attribute that will tell us if User is new or old
351 new_user.is_new_user = not edit
351 new_user.is_new_user = not edit
352 # for users that didn's specify auth type, we use RhodeCode built in
352 # for users that didn's specify auth type, we use RhodeCode built in
353 from rhodecode.authentication.plugins import auth_rhodecode
353 from rhodecode.authentication.plugins import auth_rhodecode
354 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.uid
354 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.uid
355 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.uid
355 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.uid
356
356
357 try:
357 try:
358 new_user.username = username
358 new_user.username = username
359 new_user.admin = admin
359 new_user.admin = admin
360 new_user.email = email
360 new_user.email = email
361 new_user.active = active
361 new_user.active = active
362 new_user.extern_name = safe_unicode(extern_name)
362 new_user.extern_name = safe_unicode(extern_name)
363 new_user.extern_type = safe_unicode(extern_type)
363 new_user.extern_type = safe_unicode(extern_type)
364 new_user.name = firstname
364 new_user.name = firstname
365 new_user.lastname = lastname
365 new_user.lastname = lastname
366 new_user.description = description
366 new_user.description = description
367
367
368 # set password only if creating an user or password is changed
368 # set password only if creating an user or password is changed
369 if not edit or _password_change(new_user, password):
369 if not edit or _password_change(new_user, password):
370 reason = 'new password' if edit else 'new user'
370 reason = 'new password' if edit else 'new user'
371 log.debug('Updating password reason=>%s', reason)
371 log.debug('Updating password reason=>%s', reason)
372 new_user.password = get_crypt_password(password) if password else None
372 new_user.password = get_crypt_password(password) if password else None
373
373
374 if force_password_change:
374 if force_password_change:
375 new_user.update_userdata(force_password_change=True)
375 new_user.update_userdata(force_password_change=True)
376 if language:
376 if language:
377 new_user.update_userdata(language=language)
377 new_user.update_userdata(language=language)
378 new_user.update_userdata(notification_status=True)
378 new_user.update_userdata(notification_status=True)
379
379
380 self.sa.add(new_user)
380 self.sa.add(new_user)
381
381
382 if not edit and create_repo_group:
382 if not edit and create_repo_group:
383 RepoGroupModel().create_personal_repo_group(
383 RepoGroupModel().create_personal_repo_group(
384 new_user, commit_early=False)
384 new_user, commit_early=False)
385
385
386 if not edit:
386 if not edit:
387 # add the RSS token
387 # add the RSS token
388 self.add_auth_token(
388 self.add_auth_token(
389 user=username, lifetime_minutes=-1,
389 user=username, lifetime_minutes=-1,
390 role=self.auth_token_role.ROLE_FEED,
390 role=self.auth_token_role.ROLE_FEED,
391 description=u'Generated feed token')
391 description=u'Generated feed token')
392
392
393 kwargs = new_user.get_dict()
393 kwargs = new_user.get_dict()
394 # backward compat, require api_keys present
394 # backward compat, require api_keys present
395 kwargs['api_keys'] = kwargs['auth_tokens']
395 kwargs['api_keys'] = kwargs['auth_tokens']
396 hooks_base.create_user(created_by=cur_user, **kwargs)
396 hooks_base.create_user(created_by=cur_user, **kwargs)
397 events.trigger(events.UserPostCreate(user_data))
397 events.trigger(events.UserPostCreate(user_data))
398 return new_user
398 return new_user
399 except (DatabaseError,):
399 except (DatabaseError,):
400 log.error(traceback.format_exc())
400 log.error(traceback.format_exc())
401 raise
401 raise
402
402
403 def create_registration(self, form_data,
403 def create_registration(self, form_data,
404 extern_name='rhodecode', extern_type='rhodecode'):
404 extern_name='rhodecode', extern_type='rhodecode'):
405 from rhodecode.model.notification import NotificationModel
405 from rhodecode.model.notification import NotificationModel
406 from rhodecode.model.notification import EmailNotificationModel
406 from rhodecode.model.notification import EmailNotificationModel
407
407
408 try:
408 try:
409 form_data['admin'] = False
409 form_data['admin'] = False
410 form_data['extern_name'] = extern_name
410 form_data['extern_name'] = extern_name
411 form_data['extern_type'] = extern_type
411 form_data['extern_type'] = extern_type
412 new_user = self.create(form_data)
412 new_user = self.create(form_data)
413
413
414 self.sa.add(new_user)
414 self.sa.add(new_user)
415 self.sa.flush()
415 self.sa.flush()
416
416
417 user_data = new_user.get_dict()
417 user_data = new_user.get_dict()
418 user_data.update({
418 user_data.update({
419 'first_name': user_data.get('firstname'),
419 'first_name': user_data.get('firstname'),
420 'last_name': user_data.get('lastname'),
420 'last_name': user_data.get('lastname'),
421 })
421 })
422 kwargs = {
422 kwargs = {
423 # use SQLALCHEMY safe dump of user data
423 # use SQLALCHEMY safe dump of user data
424 'user': AttributeDict(user_data),
424 'user': AttributeDict(user_data),
425 'date': datetime.datetime.now()
425 'date': datetime.datetime.now()
426 }
426 }
427 notification_type = EmailNotificationModel.TYPE_REGISTRATION
427 notification_type = EmailNotificationModel.TYPE_REGISTRATION
428
428
429 # create notification objects, and emails
429 # create notification objects, and emails
430 NotificationModel().create(
430 NotificationModel().create(
431 created_by=new_user,
431 created_by=new_user,
432 notification_subject='', # Filled in based on the notification_type
432 notification_subject='', # Filled in based on the notification_type
433 notification_body='', # Filled in based on the notification_type
433 notification_body='', # Filled in based on the notification_type
434 notification_type=notification_type,
434 notification_type=notification_type,
435 recipients=None, # all admins
435 recipients=None, # all admins
436 email_kwargs=kwargs,
436 email_kwargs=kwargs,
437 )
437 )
438
438
439 return new_user
439 return new_user
440 except Exception:
440 except Exception:
441 log.error(traceback.format_exc())
441 log.error(traceback.format_exc())
442 raise
442 raise
443
443
444 def _handle_user_repos(self, username, repositories, handle_user,
444 def _handle_user_repos(self, username, repositories, handle_user,
445 handle_mode=None):
445 handle_mode=None):
446
446
447 left_overs = True
447 left_overs = True
448
448
449 from rhodecode.model.repo import RepoModel
449 from rhodecode.model.repo import RepoModel
450
450
451 if handle_mode == 'detach':
451 if handle_mode == 'detach':
452 for obj in repositories:
452 for obj in repositories:
453 obj.user = handle_user
453 obj.user = handle_user
454 # set description we know why we super admin now owns
454 # set description we know why we super admin now owns
455 # additional repositories that were orphaned !
455 # additional repositories that were orphaned !
456 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
456 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
457 self.sa.add(obj)
457 self.sa.add(obj)
458 left_overs = False
458 left_overs = False
459 elif handle_mode == 'delete':
459 elif handle_mode == 'delete':
460 for obj in repositories:
460 for obj in repositories:
461 RepoModel().delete(obj, forks='detach')
461 RepoModel().delete(obj, forks='detach')
462 left_overs = False
462 left_overs = False
463
463
464 # if nothing is done we have left overs left
464 # if nothing is done we have left overs left
465 return left_overs
465 return left_overs
466
466
467 def _handle_user_repo_groups(self, username, repository_groups, handle_user,
467 def _handle_user_repo_groups(self, username, repository_groups, handle_user,
468 handle_mode=None):
468 handle_mode=None):
469
469
470 left_overs = True
470 left_overs = True
471
471
472 from rhodecode.model.repo_group import RepoGroupModel
472 from rhodecode.model.repo_group import RepoGroupModel
473
473
474 if handle_mode == 'detach':
474 if handle_mode == 'detach':
475 for r in repository_groups:
475 for r in repository_groups:
476 r.user = handle_user
476 r.user = handle_user
477 # set description we know why we super admin now owns
477 # set description we know why we super admin now owns
478 # additional repositories that were orphaned !
478 # additional repositories that were orphaned !
479 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
479 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
480 r.personal = False
480 r.personal = False
481 self.sa.add(r)
481 self.sa.add(r)
482 left_overs = False
482 left_overs = False
483 elif handle_mode == 'delete':
483 elif handle_mode == 'delete':
484 for r in repository_groups:
484 for r in repository_groups:
485 RepoGroupModel().delete(r)
485 RepoGroupModel().delete(r)
486 left_overs = False
486 left_overs = False
487
487
488 # if nothing is done we have left overs left
488 # if nothing is done we have left overs left
489 return left_overs
489 return left_overs
490
490
491 def _handle_user_user_groups(self, username, user_groups, handle_user,
491 def _handle_user_user_groups(self, username, user_groups, handle_user,
492 handle_mode=None):
492 handle_mode=None):
493
493
494 left_overs = True
494 left_overs = True
495
495
496 from rhodecode.model.user_group import UserGroupModel
496 from rhodecode.model.user_group import UserGroupModel
497
497
498 if handle_mode == 'detach':
498 if handle_mode == 'detach':
499 for r in user_groups:
499 for r in user_groups:
500 for user_user_group_to_perm in r.user_user_group_to_perm:
500 for user_user_group_to_perm in r.user_user_group_to_perm:
501 if user_user_group_to_perm.user.username == username:
501 if user_user_group_to_perm.user.username == username:
502 user_user_group_to_perm.user = handle_user
502 user_user_group_to_perm.user = handle_user
503 r.user = handle_user
503 r.user = handle_user
504 # set description we know why we super admin now owns
504 # set description we know why we super admin now owns
505 # additional repositories that were orphaned !
505 # additional repositories that were orphaned !
506 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
506 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
507 self.sa.add(r)
507 self.sa.add(r)
508 left_overs = False
508 left_overs = False
509 elif handle_mode == 'delete':
509 elif handle_mode == 'delete':
510 for r in user_groups:
510 for r in user_groups:
511 UserGroupModel().delete(r)
511 UserGroupModel().delete(r)
512 left_overs = False
512 left_overs = False
513
513
514 # if nothing is done we have left overs left
514 # if nothing is done we have left overs left
515 return left_overs
515 return left_overs
516
516
517 def _handle_user_pull_requests(self, username, pull_requests, handle_user,
517 def _handle_user_pull_requests(self, username, pull_requests, handle_user,
518 handle_mode=None):
518 handle_mode=None):
519 left_overs = True
519 left_overs = True
520
520
521 from rhodecode.model.pull_request import PullRequestModel
521 from rhodecode.model.pull_request import PullRequestModel
522
522
523 if handle_mode == 'detach':
523 if handle_mode == 'detach':
524 for pr in pull_requests:
524 for pr in pull_requests:
525 pr.user_id = handle_user.user_id
525 pr.user_id = handle_user.user_id
526 # set description we know why we super admin now owns
526 # set description we know why we super admin now owns
527 # additional repositories that were orphaned !
527 # additional repositories that were orphaned !
528 pr.description += ' \n::detached pull requests from deleted user: %s' % (username,)
528 pr.description += ' \n::detached pull requests from deleted user: %s' % (username,)
529 self.sa.add(pr)
529 self.sa.add(pr)
530 left_overs = False
530 left_overs = False
531 elif handle_mode == 'delete':
531 elif handle_mode == 'delete':
532 for pr in pull_requests:
532 for pr in pull_requests:
533 PullRequestModel().delete(pr)
533 PullRequestModel().delete(pr)
534
534
535 left_overs = False
535 left_overs = False
536
536
537 # if nothing is done we have left overs left
537 # if nothing is done we have left overs left
538 return left_overs
538 return left_overs
539
539
540 def _handle_user_artifacts(self, username, artifacts, handle_user,
540 def _handle_user_artifacts(self, username, artifacts, handle_user,
541 handle_mode=None):
541 handle_mode=None):
542
542
543 left_overs = True
543 left_overs = True
544
544
545 if handle_mode == 'detach':
545 if handle_mode == 'detach':
546 for a in artifacts:
546 for a in artifacts:
547 a.upload_user = handle_user
547 a.upload_user = handle_user
548 # set description we know why we super admin now owns
548 # set description we know why we super admin now owns
549 # additional artifacts that were orphaned !
549 # additional artifacts that were orphaned !
550 a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
550 a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
551 self.sa.add(a)
551 self.sa.add(a)
552 left_overs = False
552 left_overs = False
553 elif handle_mode == 'delete':
553 elif handle_mode == 'delete':
554 from rhodecode.apps.file_store import utils as store_utils
554 from rhodecode.apps.file_store import utils as store_utils
555 request = get_current_request()
555 request = get_current_request()
556 storage = store_utils.get_file_storage(request.registry.settings)
556 storage = store_utils.get_file_storage(request.registry.settings)
557 for a in artifacts:
557 for a in artifacts:
558 file_uid = a.file_uid
558 file_uid = a.file_uid
559 storage.delete(file_uid)
559 storage.delete(file_uid)
560 self.sa.delete(a)
560 self.sa.delete(a)
561
561
562 left_overs = False
562 left_overs = False
563
563
564 # if nothing is done we have left overs left
564 # if nothing is done we have left overs left
565 return left_overs
565 return left_overs
566
566
567 def delete(self, user, cur_user=None, handle_repos=None,
567 def delete(self, user, cur_user=None, handle_repos=None,
568 handle_repo_groups=None, handle_user_groups=None,
568 handle_repo_groups=None, handle_user_groups=None,
569 handle_pull_requests=None, handle_artifacts=None, handle_new_owner=None):
569 handle_pull_requests=None, handle_artifacts=None, handle_new_owner=None):
570 from rhodecode.lib import hooks_base
570 from rhodecode.lib import hooks_base
571
571
572 if not cur_user:
572 if not cur_user:
573 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
573 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
574
574
575 user = self._get_user(user)
575 user = self._get_user(user)
576
576
577 try:
577 try:
578 if user.username == User.DEFAULT_USER:
578 if user.username == User.DEFAULT_USER:
579 raise DefaultUserException(
579 raise DefaultUserException(
580 u"You can't remove this user since it's"
580 u"You can't remove this user since it's"
581 u" crucial for entire application")
581 u" crucial for entire application")
582 handle_user = handle_new_owner or self.cls.get_first_super_admin()
582 handle_user = handle_new_owner or self.cls.get_first_super_admin()
583 log.debug('New detached objects owner %s', handle_user)
583 log.debug('New detached objects owner %s', handle_user)
584
584
585 left_overs = self._handle_user_repos(
585 left_overs = self._handle_user_repos(
586 user.username, user.repositories, handle_user, handle_repos)
586 user.username, user.repositories, handle_user, handle_repos)
587 if left_overs and user.repositories:
587 if left_overs and user.repositories:
588 repos = [x.repo_name for x in user.repositories]
588 repos = [x.repo_name for x in user.repositories]
589 raise UserOwnsReposException(
589 raise UserOwnsReposException(
590 u'user "%(username)s" still owns %(len_repos)s repositories and cannot be '
590 u'user "%(username)s" still owns %(len_repos)s repositories and cannot be '
591 u'removed. Switch owners or remove those repositories:%(list_repos)s'
591 u'removed. Switch owners or remove those repositories:%(list_repos)s'
592 % {'username': user.username, 'len_repos': len(repos),
592 % {'username': user.username, 'len_repos': len(repos),
593 'list_repos': ', '.join(repos)})
593 'list_repos': ', '.join(repos)})
594
594
595 left_overs = self._handle_user_repo_groups(
595 left_overs = self._handle_user_repo_groups(
596 user.username, user.repository_groups, handle_user, handle_repo_groups)
596 user.username, user.repository_groups, handle_user, handle_repo_groups)
597 if left_overs and user.repository_groups:
597 if left_overs and user.repository_groups:
598 repo_groups = [x.group_name for x in user.repository_groups]
598 repo_groups = [x.group_name for x in user.repository_groups]
599 raise UserOwnsRepoGroupsException(
599 raise UserOwnsRepoGroupsException(
600 u'user "%(username)s" still owns %(len_repo_groups)s repository groups and cannot be '
600 u'user "%(username)s" still owns %(len_repo_groups)s repository groups and cannot be '
601 u'removed. Switch owners or remove those repository groups:%(list_repo_groups)s'
601 u'removed. Switch owners or remove those repository groups:%(list_repo_groups)s'
602 % {'username': user.username, 'len_repo_groups': len(repo_groups),
602 % {'username': user.username, 'len_repo_groups': len(repo_groups),
603 'list_repo_groups': ', '.join(repo_groups)})
603 'list_repo_groups': ', '.join(repo_groups)})
604
604
605 left_overs = self._handle_user_user_groups(
605 left_overs = self._handle_user_user_groups(
606 user.username, user.user_groups, handle_user, handle_user_groups)
606 user.username, user.user_groups, handle_user, handle_user_groups)
607 if left_overs and user.user_groups:
607 if left_overs and user.user_groups:
608 user_groups = [x.users_group_name for x in user.user_groups]
608 user_groups = [x.users_group_name for x in user.user_groups]
609 raise UserOwnsUserGroupsException(
609 raise UserOwnsUserGroupsException(
610 u'user "%s" still owns %s user groups and cannot be '
610 u'user "%s" still owns %s user groups and cannot be '
611 u'removed. Switch owners or remove those user groups:%s'
611 u'removed. Switch owners or remove those user groups:%s'
612 % (user.username, len(user_groups), ', '.join(user_groups)))
612 % (user.username, len(user_groups), ', '.join(user_groups)))
613
613
614 left_overs = self._handle_user_pull_requests(
614 left_overs = self._handle_user_pull_requests(
615 user.username, user.user_pull_requests, handle_user, handle_pull_requests)
615 user.username, user.user_pull_requests, handle_user, handle_pull_requests)
616 if left_overs and user.user_pull_requests:
616 if left_overs and user.user_pull_requests:
617 pull_requests = ['!{}'.format(x.pull_request_id) for x in user.user_pull_requests]
617 pull_requests = ['!{}'.format(x.pull_request_id) for x in user.user_pull_requests]
618 raise UserOwnsPullRequestsException(
618 raise UserOwnsPullRequestsException(
619 u'user "%s" still owns %s pull requests and cannot be '
619 u'user "%s" still owns %s pull requests and cannot be '
620 u'removed. Switch owners or remove those pull requests:%s'
620 u'removed. Switch owners or remove those pull requests:%s'
621 % (user.username, len(pull_requests), ', '.join(pull_requests)))
621 % (user.username, len(pull_requests), ', '.join(pull_requests)))
622
622
623 left_overs = self._handle_user_artifacts(
623 left_overs = self._handle_user_artifacts(
624 user.username, user.artifacts, handle_user, handle_artifacts)
624 user.username, user.artifacts, handle_user, handle_artifacts)
625 if left_overs and user.artifacts:
625 if left_overs and user.artifacts:
626 artifacts = [x.file_uid for x in user.artifacts]
626 artifacts = [x.file_uid for x in user.artifacts]
627 raise UserOwnsArtifactsException(
627 raise UserOwnsArtifactsException(
628 u'user "%s" still owns %s artifacts and cannot be '
628 u'user "%s" still owns %s artifacts and cannot be '
629 u'removed. Switch owners or remove those artifacts:%s'
629 u'removed. Switch owners or remove those artifacts:%s'
630 % (user.username, len(artifacts), ', '.join(artifacts)))
630 % (user.username, len(artifacts), ', '.join(artifacts)))
631
631
632 user_data = user.get_dict() # fetch user data before expire
632 user_data = user.get_dict() # fetch user data before expire
633
633
634 # we might change the user data with detach/delete, make sure
634 # we might change the user data with detach/delete, make sure
635 # the object is marked as expired before actually deleting !
635 # the object is marked as expired before actually deleting !
636 self.sa.expire(user)
636 self.sa.expire(user)
637 self.sa.delete(user)
637 self.sa.delete(user)
638
638
639 hooks_base.delete_user(deleted_by=cur_user, **user_data)
639 hooks_base.delete_user(deleted_by=cur_user, **user_data)
640 except Exception:
640 except Exception:
641 log.error(traceback.format_exc())
641 log.error(traceback.format_exc())
642 raise
642 raise
643
643
644 def reset_password_link(self, data, pwd_reset_url):
644 def reset_password_link(self, data, pwd_reset_url):
645 from rhodecode.lib.celerylib import tasks, run_task
645 from rhodecode.lib.celerylib import tasks, run_task
646 from rhodecode.model.notification import EmailNotificationModel
646 from rhodecode.model.notification import EmailNotificationModel
647 user_email = data['email']
647 user_email = data['email']
648 try:
648 try:
649 user = User.get_by_email(user_email)
649 user = User.get_by_email(user_email)
650 if user:
650 if user:
651 log.debug('password reset user found %s', user)
651 log.debug('password reset user found %s', user)
652
652
653 email_kwargs = {
653 email_kwargs = {
654 'password_reset_url': pwd_reset_url,
654 'password_reset_url': pwd_reset_url,
655 'user': user,
655 'user': user,
656 'email': user_email,
656 'email': user_email,
657 'date': datetime.datetime.now(),
657 'date': datetime.datetime.now(),
658 'first_admin_email': User.get_first_super_admin().email
658 'first_admin_email': User.get_first_super_admin().email
659 }
659 }
660
660
661 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
661 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
662 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
662 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
663
663
664 recipients = [user_email]
664 recipients = [user_email]
665
665
666 action_logger_generic(
666 action_logger_generic(
667 'sending password reset email to user: {}'.format(
667 'sending password reset email to user: {}'.format(
668 user), namespace='security.password_reset')
668 user), namespace='security.password_reset')
669
669
670 run_task(tasks.send_email, recipients, subject,
670 run_task(tasks.send_email, recipients, subject,
671 email_body_plaintext, email_body)
671 email_body_plaintext, email_body)
672
672
673 else:
673 else:
674 log.debug("password reset email %s not found", user_email)
674 log.debug("password reset email %s not found", user_email)
675 except Exception:
675 except Exception:
676 log.error(traceback.format_exc())
676 log.error(traceback.format_exc())
677 return False
677 return False
678
678
679 return True
679 return True
680
680
681 def reset_password(self, data):
681 def reset_password(self, data):
682 from rhodecode.lib.celerylib import tasks, run_task
682 from rhodecode.lib.celerylib import tasks, run_task
683 from rhodecode.model.notification import EmailNotificationModel
683 from rhodecode.model.notification import EmailNotificationModel
684 from rhodecode.lib import auth
684 from rhodecode.lib import auth
685 user_email = data['email']
685 user_email = data['email']
686 pre_db = True
686 pre_db = True
687 try:
687 try:
688 user = User.get_by_email(user_email)
688 user = User.get_by_email(user_email)
689 new_passwd = auth.PasswordGenerator().gen_password(
689 new_passwd = auth.PasswordGenerator().gen_password(
690 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
690 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
691 if user:
691 if user:
692 user.password = auth.get_crypt_password(new_passwd)
692 user.password = auth.get_crypt_password(new_passwd)
693 # also force this user to reset his password !
693 # also force this user to reset his password !
694 user.update_userdata(force_password_change=True)
694 user.update_userdata(force_password_change=True)
695
695
696 Session().add(user)
696 Session().add(user)
697
697
698 # now delete the token in question
698 # now delete the token in question
699 UserApiKeys = AuthTokenModel.cls
699 UserApiKeys = AuthTokenModel.cls
700 UserApiKeys().query().filter(
700 UserApiKeys().query().filter(
701 UserApiKeys.api_key == data['token']).delete()
701 UserApiKeys.api_key == data['token']).delete()
702
702
703 Session().commit()
703 Session().commit()
704 log.info('successfully reset password for `%s`', user_email)
704 log.info('successfully reset password for `%s`', user_email)
705
705
706 if new_passwd is None:
706 if new_passwd is None:
707 raise Exception('unable to generate new password')
707 raise Exception('unable to generate new password')
708
708
709 pre_db = False
709 pre_db = False
710
710
711 email_kwargs = {
711 email_kwargs = {
712 'new_password': new_passwd,
712 'new_password': new_passwd,
713 'user': user,
713 'user': user,
714 'email': user_email,
714 'email': user_email,
715 'date': datetime.datetime.now(),
715 'date': datetime.datetime.now(),
716 'first_admin_email': User.get_first_super_admin().email
716 'first_admin_email': User.get_first_super_admin().email
717 }
717 }
718
718
719 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
719 (subject, email_body, email_body_plaintext) = EmailNotificationModel().render_email(
720 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
720 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
721 **email_kwargs)
721 **email_kwargs)
722
722
723 recipients = [user_email]
723 recipients = [user_email]
724
724
725 action_logger_generic(
725 action_logger_generic(
726 'sent new password to user: {} with email: {}'.format(
726 'sent new password to user: {} with email: {}'.format(
727 user, user_email), namespace='security.password_reset')
727 user, user_email), namespace='security.password_reset')
728
728
729 run_task(tasks.send_email, recipients, subject,
729 run_task(tasks.send_email, recipients, subject,
730 email_body_plaintext, email_body)
730 email_body_plaintext, email_body)
731
731
732 except Exception:
732 except Exception:
733 log.error('Failed to update user password')
733 log.error('Failed to update user password')
734 log.error(traceback.format_exc())
734 log.error(traceback.format_exc())
735 if pre_db:
735 if pre_db:
736 # we rollback only if local db stuff fails. If it goes into
736 # we rollback only if local db stuff fails. If it goes into
737 # run_task, we're pass rollback state this wouldn't work then
737 # run_task, we're pass rollback state this wouldn't work then
738 Session().rollback()
738 Session().rollback()
739
739
740 return True
740 return True
741
741
742 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
742 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
743 """
743 """
744 Fetches auth_user by user_id,or api_key if present.
744 Fetches auth_user by user_id,or api_key if present.
745 Fills auth_user attributes with those taken from database.
745 Fills auth_user attributes with those taken from database.
746 Additionally set's is_authenitated if lookup fails
746 Additionally set's is_authenitated if lookup fails
747 present in database
747 present in database
748
748
749 :param auth_user: instance of user to set attributes
749 :param auth_user: instance of user to set attributes
750 :param user_id: user id to fetch by
750 :param user_id: user id to fetch by
751 :param api_key: api key to fetch by
751 :param api_key: api key to fetch by
752 :param username: username to fetch by
752 :param username: username to fetch by
753 """
753 """
754 def token_obfuscate(token):
754 def token_obfuscate(token):
755 if token:
755 if token:
756 return token[:4] + "****"
756 return token[:4] + "****"
757
757
758 if user_id is None and api_key is None and username is None:
758 if user_id is None and api_key is None and username is None:
759 raise Exception('You need to pass user_id, api_key or username')
759 raise Exception('You need to pass user_id, api_key or username')
760
760
761 log.debug(
761 log.debug(
762 'AuthUser: fill data execution based on: '
762 'AuthUser: fill data execution based on: '
763 'user_id:%s api_key:%s username:%s', user_id, api_key, username)
763 'user_id:%s api_key:%s username:%s', user_id, api_key, username)
764 try:
764 try:
765 dbuser = None
765 dbuser = None
766 if user_id:
766 if user_id:
767 dbuser = self.get(user_id)
767 dbuser = self.get(user_id)
768 elif api_key:
768 elif api_key:
769 dbuser = self.get_by_auth_token(api_key)
769 dbuser = self.get_by_auth_token(api_key)
770 elif username:
770 elif username:
771 dbuser = self.get_by_username(username)
771 dbuser = self.get_by_username(username)
772
772
773 if not dbuser:
773 if not dbuser:
774 log.warning(
774 log.warning(
775 'Unable to lookup user by id:%s api_key:%s username:%s',
775 'Unable to lookup user by id:%s api_key:%s username:%s',
776 user_id, token_obfuscate(api_key), username)
776 user_id, token_obfuscate(api_key), username)
777 return False
777 return False
778 if not dbuser.active:
778 if not dbuser.active:
779 log.debug('User `%s:%s` is inactive, skipping fill data',
779 log.debug('User `%s:%s` is inactive, skipping fill data',
780 username, user_id)
780 username, user_id)
781 return False
781 return False
782
782
783 log.debug('AuthUser: filling found user:%s data', dbuser)
783 log.debug('AuthUser: filling found user:%s data', dbuser)
784
784
785 attrs = {
785 attrs = {
786 'user_id': dbuser.user_id,
786 'user_id': dbuser.user_id,
787 'username': dbuser.username,
787 'username': dbuser.username,
788 'name': dbuser.name,
788 'name': dbuser.name,
789 'first_name': dbuser.first_name,
789 'first_name': dbuser.first_name,
790 'firstname': dbuser.firstname,
790 'firstname': dbuser.firstname,
791 'last_name': dbuser.last_name,
791 'last_name': dbuser.last_name,
792 'lastname': dbuser.lastname,
792 'lastname': dbuser.lastname,
793 'admin': dbuser.admin,
793 'admin': dbuser.admin,
794 'active': dbuser.active,
794 'active': dbuser.active,
795
795
796 'email': dbuser.email,
796 'email': dbuser.email,
797 'emails': dbuser.emails_cached(),
797 'emails': dbuser.emails_cached(),
798 'short_contact': dbuser.short_contact,
798 'short_contact': dbuser.short_contact,
799 'full_contact': dbuser.full_contact,
799 'full_contact': dbuser.full_contact,
800 'full_name': dbuser.full_name,
800 'full_name': dbuser.full_name,
801 'full_name_or_username': dbuser.full_name_or_username,
801 'full_name_or_username': dbuser.full_name_or_username,
802
802
803 '_api_key': dbuser._api_key,
803 '_api_key': dbuser._api_key,
804 '_user_data': dbuser._user_data,
804 '_user_data': dbuser._user_data,
805
805
806 'created_on': dbuser.created_on,
806 'created_on': dbuser.created_on,
807 'extern_name': dbuser.extern_name,
807 'extern_name': dbuser.extern_name,
808 'extern_type': dbuser.extern_type,
808 'extern_type': dbuser.extern_type,
809
809
810 'inherit_default_permissions': dbuser.inherit_default_permissions,
810 'inherit_default_permissions': dbuser.inherit_default_permissions,
811
811
812 'language': dbuser.language,
812 'language': dbuser.language,
813 'last_activity': dbuser.last_activity,
813 'last_activity': dbuser.last_activity,
814 'last_login': dbuser.last_login,
814 'last_login': dbuser.last_login,
815 'password': dbuser.password,
815 'password': dbuser.password,
816 }
816 }
817 auth_user.__dict__.update(attrs)
817 auth_user.__dict__.update(attrs)
818 except Exception:
818 except Exception:
819 log.error(traceback.format_exc())
819 log.error(traceback.format_exc())
820 auth_user.is_authenticated = False
820 auth_user.is_authenticated = False
821 return False
821 return False
822
822
823 return True
823 return True
824
824
825 def has_perm(self, user, perm):
825 def has_perm(self, user, perm):
826 perm = self._get_perm(perm)
826 perm = self._get_perm(perm)
827 user = self._get_user(user)
827 user = self._get_user(user)
828
828
829 return UserToPerm.query().filter(UserToPerm.user == user)\
829 return UserToPerm.query().filter(UserToPerm.user == user)\
830 .filter(UserToPerm.permission == perm).scalar() is not None
830 .filter(UserToPerm.permission == perm).scalar() is not None
831
831
832 def grant_perm(self, user, perm):
832 def grant_perm(self, user, perm):
833 """
833 """
834 Grant user global permissions
834 Grant user global permissions
835
835
836 :param user:
836 :param user:
837 :param perm:
837 :param perm:
838 """
838 """
839 user = self._get_user(user)
839 user = self._get_user(user)
840 perm = self._get_perm(perm)
840 perm = self._get_perm(perm)
841 # if this permission is already granted skip it
841 # if this permission is already granted skip it
842 _perm = UserToPerm.query()\
842 _perm = UserToPerm.query()\
843 .filter(UserToPerm.user == user)\
843 .filter(UserToPerm.user == user)\
844 .filter(UserToPerm.permission == perm)\
844 .filter(UserToPerm.permission == perm)\
845 .scalar()
845 .scalar()
846 if _perm:
846 if _perm:
847 return
847 return
848 new = UserToPerm()
848 new = UserToPerm()
849 new.user = user
849 new.user = user
850 new.permission = perm
850 new.permission = perm
851 self.sa.add(new)
851 self.sa.add(new)
852 return new
852 return new
853
853
854 def revoke_perm(self, user, perm):
854 def revoke_perm(self, user, perm):
855 """
855 """
856 Revoke users global permissions
856 Revoke users global permissions
857
857
858 :param user:
858 :param user:
859 :param perm:
859 :param perm:
860 """
860 """
861 user = self._get_user(user)
861 user = self._get_user(user)
862 perm = self._get_perm(perm)
862 perm = self._get_perm(perm)
863
863
864 obj = UserToPerm.query()\
864 obj = UserToPerm.query()\
865 .filter(UserToPerm.user == user)\
865 .filter(UserToPerm.user == user)\
866 .filter(UserToPerm.permission == perm)\
866 .filter(UserToPerm.permission == perm)\
867 .scalar()
867 .scalar()
868 if obj:
868 if obj:
869 self.sa.delete(obj)
869 self.sa.delete(obj)
870
870
871 def add_extra_email(self, user, email):
871 def add_extra_email(self, user, email):
872 """
872 """
873 Adds email address to UserEmailMap
873 Adds email address to UserEmailMap
874
874
875 :param user:
875 :param user:
876 :param email:
876 :param email:
877 """
877 """
878
878
879 user = self._get_user(user)
879 user = self._get_user(user)
880
880
881 obj = UserEmailMap()
881 obj = UserEmailMap()
882 obj.user = user
882 obj.user = user
883 obj.email = email
883 obj.email = email
884 self.sa.add(obj)
884 self.sa.add(obj)
885 return obj
885 return obj
886
886
887 def delete_extra_email(self, user, email_id):
887 def delete_extra_email(self, user, email_id):
888 """
888 """
889 Removes email address from UserEmailMap
889 Removes email address from UserEmailMap
890
890
891 :param user:
891 :param user:
892 :param email_id:
892 :param email_id:
893 """
893 """
894 user = self._get_user(user)
894 user = self._get_user(user)
895 obj = UserEmailMap.query().get(email_id)
895 obj = UserEmailMap.query().get(email_id)
896 if obj and obj.user_id == user.user_id:
896 if obj and obj.user_id == user.user_id:
897 self.sa.delete(obj)
897 self.sa.delete(obj)
898
898
899 def parse_ip_range(self, ip_range):
899 def parse_ip_range(self, ip_range):
900 ip_list = []
900 ip_list = []
901
901
902 def make_unique(value):
902 def make_unique(value):
903 seen = []
903 seen = []
904 return [c for c in value if not (c in seen or seen.append(c))]
904 return [c for c in value if not (c in seen or seen.append(c))]
905
905
906 # firsts split by commas
906 # firsts split by commas
907 for ip_range in ip_range.split(','):
907 for ip_range in ip_range.split(','):
908 if not ip_range:
908 if not ip_range:
909 continue
909 continue
910 ip_range = ip_range.strip()
910 ip_range = ip_range.strip()
911 if '-' in ip_range:
911 if '-' in ip_range:
912 start_ip, end_ip = ip_range.split('-', 1)
912 start_ip, end_ip = ip_range.split('-', 1)
913 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
913 start_ip = ipaddress.ip_address(safe_unicode(start_ip.strip()))
914 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
914 end_ip = ipaddress.ip_address(safe_unicode(end_ip.strip()))
915 parsed_ip_range = []
915 parsed_ip_range = []
916
916
917 for index in range(int(start_ip), int(end_ip) + 1):
917 for index in range(int(start_ip), int(end_ip) + 1):
918 new_ip = ipaddress.ip_address(index)
918 new_ip = ipaddress.ip_address(index)
919 parsed_ip_range.append(str(new_ip))
919 parsed_ip_range.append(str(new_ip))
920 ip_list.extend(parsed_ip_range)
920 ip_list.extend(parsed_ip_range)
921 else:
921 else:
922 ip_list.append(ip_range)
922 ip_list.append(ip_range)
923
923
924 return make_unique(ip_list)
924 return make_unique(ip_list)
925
925
926 def add_extra_ip(self, user, ip, description=None):
926 def add_extra_ip(self, user, ip, description=None):
927 """
927 """
928 Adds ip address to UserIpMap
928 Adds ip address to UserIpMap
929
929
930 :param user:
930 :param user:
931 :param ip:
931 :param ip:
932 """
932 """
933
933
934 user = self._get_user(user)
934 user = self._get_user(user)
935 obj = UserIpMap()
935 obj = UserIpMap()
936 obj.user = user
936 obj.user = user
937 obj.ip_addr = ip
937 obj.ip_addr = ip
938 obj.description = description
938 obj.description = description
939 self.sa.add(obj)
939 self.sa.add(obj)
940 return obj
940 return obj
941
941
942 auth_token_role = AuthTokenModel.cls
942 auth_token_role = AuthTokenModel.cls
943
943
944 def add_auth_token(self, user, lifetime_minutes, role, description=u'',
944 def add_auth_token(self, user, lifetime_minutes, role, description=u'',
945 scope_callback=None):
945 scope_callback=None):
946 """
946 """
947 Add AuthToken for user.
947 Add AuthToken for user.
948
948
949 :param user: username/user_id
949 :param user: username/user_id
950 :param lifetime_minutes: in minutes the lifetime for token, -1 equals no limit
950 :param lifetime_minutes: in minutes the lifetime for token, -1 equals no limit
951 :param role: one of AuthTokenModel.cls.ROLE_*
951 :param role: one of AuthTokenModel.cls.ROLE_*
952 :param description: optional string description
952 :param description: optional string description
953 """
953 """
954
954
955 token = AuthTokenModel().create(
955 token = AuthTokenModel().create(
956 user, description, lifetime_minutes, role)
956 user, description, lifetime_minutes, role)
957 if scope_callback and callable(scope_callback):
957 if scope_callback and callable(scope_callback):
958 # call the callback if we provide, used to attach scope for EE edition
958 # call the callback if we provide, used to attach scope for EE edition
959 scope_callback(token)
959 scope_callback(token)
960 return token
960 return token
961
961
962 def delete_extra_ip(self, user, ip_id):
962 def delete_extra_ip(self, user, ip_id):
963 """
963 """
964 Removes ip address from UserIpMap
964 Removes ip address from UserIpMap
965
965
966 :param user:
966 :param user:
967 :param ip_id:
967 :param ip_id:
968 """
968 """
969 user = self._get_user(user)
969 user = self._get_user(user)
970 obj = UserIpMap.query().get(ip_id)
970 obj = UserIpMap.query().get(ip_id)
971 if obj and obj.user_id == user.user_id:
971 if obj and obj.user_id == user.user_id:
972 self.sa.delete(obj)
972 self.sa.delete(obj)
973
973
974 def get_accounts_in_creation_order(self, current_user=None):
974 def get_accounts_in_creation_order(self, current_user=None):
975 """
975 """
976 Get accounts in order of creation for deactivation for license limits
976 Get accounts in order of creation for deactivation for license limits
977
977
978 pick currently logged in user, and append to the list in position 0
978 pick currently logged in user, and append to the list in position 0
979 pick all super-admins in order of creation date and add it to the list
979 pick all super-admins in order of creation date and add it to the list
980 pick all other accounts in order of creation and add it to the list.
980 pick all other accounts in order of creation and add it to the list.
981
981
982 Based on that list, the last accounts can be disabled as they are
982 Based on that list, the last accounts can be disabled as they are
983 created at the end and don't include any of the super admins as well
983 created at the end and don't include any of the super admins as well
984 as the current user.
984 as the current user.
985
985
986 :param current_user: optionally current user running this operation
986 :param current_user: optionally current user running this operation
987 """
987 """
988
988
989 if not current_user:
989 if not current_user:
990 current_user = get_current_rhodecode_user()
990 current_user = get_current_rhodecode_user()
991 active_super_admins = [
991 active_super_admins = [
992 x.user_id for x in User.query()
992 x.user_id for x in User.query()
993 .filter(User.user_id != current_user.user_id)
993 .filter(User.user_id != current_user.user_id)
994 .filter(User.active == true())
994 .filter(User.active == true())
995 .filter(User.admin == true())
995 .filter(User.admin == true())
996 .order_by(User.created_on.asc())]
996 .order_by(User.created_on.asc())]
997
997
998 active_regular_users = [
998 active_regular_users = [
999 x.user_id for x in User.query()
999 x.user_id for x in User.query()
1000 .filter(User.user_id != current_user.user_id)
1000 .filter(User.user_id != current_user.user_id)
1001 .filter(User.active == true())
1001 .filter(User.active == true())
1002 .filter(User.admin == false())
1002 .filter(User.admin == false())
1003 .order_by(User.created_on.asc())]
1003 .order_by(User.created_on.asc())]
1004
1004
1005 list_of_accounts = [current_user.user_id]
1005 list_of_accounts = [current_user.user_id]
1006 list_of_accounts += active_super_admins
1006 list_of_accounts += active_super_admins
1007 list_of_accounts += active_regular_users
1007 list_of_accounts += active_regular_users
1008
1008
1009 return list_of_accounts
1009 return list_of_accounts
1010
1010
1011 def deactivate_last_users(self, expected_users, current_user=None):
1011 def deactivate_last_users(self, expected_users, current_user=None):
1012 """
1012 """
1013 Deactivate accounts that are over the license limits.
1013 Deactivate accounts that are over the license limits.
1014 Algorithm of which accounts to disabled is based on the formula:
1014 Algorithm of which accounts to disabled is based on the formula:
1015
1015
1016 Get current user, then super admins in creation order, then regular
1016 Get current user, then super admins in creation order, then regular
1017 active users in creation order.
1017 active users in creation order.
1018
1018
1019 Using that list we mark all accounts from the end of it as inactive.
1019 Using that list we mark all accounts from the end of it as inactive.
1020 This way we block only latest created accounts.
1020 This way we block only latest created accounts.
1021
1021
1022 :param expected_users: list of users in special order, we deactivate
1022 :param expected_users: list of users in special order, we deactivate
1023 the end N amount of users from that list
1023 the end N amount of users from that list
1024 """
1024 """
1025
1025
1026 list_of_accounts = self.get_accounts_in_creation_order(
1026 list_of_accounts = self.get_accounts_in_creation_order(
1027 current_user=current_user)
1027 current_user=current_user)
1028
1028
1029 for acc_id in list_of_accounts[expected_users + 1:]:
1029 for acc_id in list_of_accounts[expected_users + 1:]:
1030 user = User.get(acc_id)
1030 user = User.get(acc_id)
1031 log.info('Deactivating account %s for license unlock', user)
1031 log.info('Deactivating account %s for license unlock', user)
1032 user.active = False
1032 user.active = False
1033 Session().add(user)
1033 Session().add(user)
1034 Session().commit()
1034 Session().commit()
1035
1035
1036 return
1036 return
1037
1037
1038 def get_user_log(self, user, filter_term):
1038 def get_user_log(self, user, filter_term):
1039 user_log = UserLog.query()\
1039 user_log = UserLog.query()\
1040 .filter(or_(UserLog.user_id == user.user_id,
1040 .filter(or_(UserLog.user_id == user.user_id,
1041 UserLog.username == user.username))\
1041 UserLog.username == user.username))\
1042 .options(joinedload(UserLog.user))\
1042 .options(joinedload(UserLog.user))\
1043 .options(joinedload(UserLog.repository))\
1043 .options(joinedload(UserLog.repository))\
1044 .order_by(UserLog.action_date.desc())
1044 .order_by(UserLog.action_date.desc())
1045
1045
1046 user_log = user_log_filter(user_log, filter_term)
1046 user_log = user_log_filter(user_log, filter_term)
1047 return user_log
1047 return user_log
General Comments 0
You need to be logged in to leave comments. Login now