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