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