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