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