##// END OF EJS Templates
repo-model: fixed permalink generation for repositories.
marcink -
r2413:4576e184 default
parent child Browse files
Show More
@@ -1,1032 +1,1032 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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=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'))
232 cs_cache.get('message'))
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': 'repo_private', 'strip': True},
307 {'k': 'repo_private', 'strip': True},
308 {'k': 'repo_enable_statistics', 'strip': True}
308 {'k': 'repo_enable_statistics', 'strip': True}
309 )
309 )
310
310
311 for item in keys_to_process:
311 for item in keys_to_process:
312 attr = item['k']
312 attr = item['k']
313 if item['strip']:
313 if item['strip']:
314 attr = remove_prefix(item['k'], 'repo_')
314 attr = remove_prefix(item['k'], 'repo_')
315
315
316 val = defaults[attr]
316 val = defaults[attr]
317 if item['k'] == 'repo_landing_rev':
317 if item['k'] == 'repo_landing_rev':
318 val = ':'.join(defaults[attr])
318 val = ':'.join(defaults[attr])
319 defaults[item['k']] = val
319 defaults[item['k']] = val
320 if item['k'] == 'clone_uri':
320 if item['k'] == 'clone_uri':
321 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
321 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
322
322
323 # fill owner
323 # fill owner
324 if repo_info.user:
324 if repo_info.user:
325 defaults.update({'user': repo_info.user.username})
325 defaults.update({'user': repo_info.user.username})
326 else:
326 else:
327 replacement_user = User.get_first_super_admin().username
327 replacement_user = User.get_first_super_admin().username
328 defaults.update({'user': replacement_user})
328 defaults.update({'user': replacement_user})
329
329
330 return defaults
330 return defaults
331
331
332 def update(self, repo, **kwargs):
332 def update(self, repo, **kwargs):
333 try:
333 try:
334 cur_repo = self._get_repo(repo)
334 cur_repo = self._get_repo(repo)
335 source_repo_name = cur_repo.repo_name
335 source_repo_name = cur_repo.repo_name
336 if 'user' in kwargs:
336 if 'user' in kwargs:
337 cur_repo.user = User.get_by_username(kwargs['user'])
337 cur_repo.user = User.get_by_username(kwargs['user'])
338
338
339 if 'repo_group' in kwargs:
339 if 'repo_group' in kwargs:
340 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
340 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
341 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
341 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
342
342
343 update_keys = [
343 update_keys = [
344 (1, 'repo_description'),
344 (1, 'repo_description'),
345 (1, 'repo_landing_rev'),
345 (1, 'repo_landing_rev'),
346 (1, 'repo_private'),
346 (1, 'repo_private'),
347 (1, 'repo_enable_downloads'),
347 (1, 'repo_enable_downloads'),
348 (1, 'repo_enable_locking'),
348 (1, 'repo_enable_locking'),
349 (1, 'repo_enable_statistics'),
349 (1, 'repo_enable_statistics'),
350 (0, 'clone_uri'),
350 (0, 'clone_uri'),
351 (0, 'fork_id')
351 (0, 'fork_id')
352 ]
352 ]
353 for strip, k in update_keys:
353 for strip, k in update_keys:
354 if k in kwargs:
354 if k in kwargs:
355 val = kwargs[k]
355 val = kwargs[k]
356 if strip:
356 if strip:
357 k = remove_prefix(k, 'repo_')
357 k = remove_prefix(k, 'repo_')
358
358
359 setattr(cur_repo, k, val)
359 setattr(cur_repo, k, val)
360
360
361 new_name = cur_repo.get_new_name(kwargs['repo_name'])
361 new_name = cur_repo.get_new_name(kwargs['repo_name'])
362 cur_repo.repo_name = new_name
362 cur_repo.repo_name = new_name
363
363
364 # if private flag is set, reset default permission to NONE
364 # if private flag is set, reset default permission to NONE
365 if kwargs.get('repo_private'):
365 if kwargs.get('repo_private'):
366 EMPTY_PERM = 'repository.none'
366 EMPTY_PERM = 'repository.none'
367 RepoModel().grant_user_permission(
367 RepoModel().grant_user_permission(
368 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
368 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
369 )
369 )
370
370
371 # handle extra fields
371 # handle extra fields
372 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
372 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
373 kwargs):
373 kwargs):
374 k = RepositoryField.un_prefix_key(field)
374 k = RepositoryField.un_prefix_key(field)
375 ex_field = RepositoryField.get_by_key_name(
375 ex_field = RepositoryField.get_by_key_name(
376 key=k, repo=cur_repo)
376 key=k, repo=cur_repo)
377 if ex_field:
377 if ex_field:
378 ex_field.field_value = kwargs[field]
378 ex_field.field_value = kwargs[field]
379 self.sa.add(ex_field)
379 self.sa.add(ex_field)
380 cur_repo.updated_on = datetime.datetime.now()
380 cur_repo.updated_on = datetime.datetime.now()
381 self.sa.add(cur_repo)
381 self.sa.add(cur_repo)
382
382
383 if source_repo_name != new_name:
383 if source_repo_name != new_name:
384 # rename repository
384 # rename repository
385 self._rename_filesystem_repo(
385 self._rename_filesystem_repo(
386 old=source_repo_name, new=new_name)
386 old=source_repo_name, new=new_name)
387
387
388 return cur_repo
388 return cur_repo
389 except Exception:
389 except Exception:
390 log.error(traceback.format_exc())
390 log.error(traceback.format_exc())
391 raise
391 raise
392
392
393 def _create_repo(self, repo_name, repo_type, description, owner,
393 def _create_repo(self, repo_name, repo_type, description, owner,
394 private=False, clone_uri=None, repo_group=None,
394 private=False, clone_uri=None, repo_group=None,
395 landing_rev='rev:tip', fork_of=None,
395 landing_rev='rev:tip', fork_of=None,
396 copy_fork_permissions=False, enable_statistics=False,
396 copy_fork_permissions=False, enable_statistics=False,
397 enable_locking=False, enable_downloads=False,
397 enable_locking=False, enable_downloads=False,
398 copy_group_permissions=False,
398 copy_group_permissions=False,
399 state=Repository.STATE_PENDING):
399 state=Repository.STATE_PENDING):
400 """
400 """
401 Create repository inside database with PENDING state, this should be
401 Create repository inside database with PENDING state, this should be
402 only executed by create() repo. With exception of importing existing
402 only executed by create() repo. With exception of importing existing
403 repos
403 repos
404 """
404 """
405 from rhodecode.model.scm import ScmModel
405 from rhodecode.model.scm import ScmModel
406
406
407 owner = self._get_user(owner)
407 owner = self._get_user(owner)
408 fork_of = self._get_repo(fork_of)
408 fork_of = self._get_repo(fork_of)
409 repo_group = self._get_repo_group(safe_int(repo_group))
409 repo_group = self._get_repo_group(safe_int(repo_group))
410
410
411 try:
411 try:
412 repo_name = safe_unicode(repo_name)
412 repo_name = safe_unicode(repo_name)
413 description = safe_unicode(description)
413 description = safe_unicode(description)
414 # repo name is just a name of repository
414 # repo name is just a name of repository
415 # while repo_name_full is a full qualified name that is combined
415 # while repo_name_full is a full qualified name that is combined
416 # with name and path of group
416 # with name and path of group
417 repo_name_full = repo_name
417 repo_name_full = repo_name
418 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
418 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
419
419
420 new_repo = Repository()
420 new_repo = Repository()
421 new_repo.repo_state = state
421 new_repo.repo_state = state
422 new_repo.enable_statistics = False
422 new_repo.enable_statistics = False
423 new_repo.repo_name = repo_name_full
423 new_repo.repo_name = repo_name_full
424 new_repo.repo_type = repo_type
424 new_repo.repo_type = repo_type
425 new_repo.user = owner
425 new_repo.user = owner
426 new_repo.group = repo_group
426 new_repo.group = repo_group
427 new_repo.description = description or repo_name
427 new_repo.description = description or repo_name
428 new_repo.private = private
428 new_repo.private = private
429 new_repo.clone_uri = clone_uri
429 new_repo.clone_uri = clone_uri
430 new_repo.landing_rev = landing_rev
430 new_repo.landing_rev = landing_rev
431
431
432 new_repo.enable_statistics = enable_statistics
432 new_repo.enable_statistics = enable_statistics
433 new_repo.enable_locking = enable_locking
433 new_repo.enable_locking = enable_locking
434 new_repo.enable_downloads = enable_downloads
434 new_repo.enable_downloads = enable_downloads
435
435
436 if repo_group:
436 if repo_group:
437 new_repo.enable_locking = repo_group.enable_locking
437 new_repo.enable_locking = repo_group.enable_locking
438
438
439 if fork_of:
439 if fork_of:
440 parent_repo = fork_of
440 parent_repo = fork_of
441 new_repo.fork = parent_repo
441 new_repo.fork = parent_repo
442
442
443 events.trigger(events.RepoPreCreateEvent(new_repo))
443 events.trigger(events.RepoPreCreateEvent(new_repo))
444
444
445 self.sa.add(new_repo)
445 self.sa.add(new_repo)
446
446
447 EMPTY_PERM = 'repository.none'
447 EMPTY_PERM = 'repository.none'
448 if fork_of and copy_fork_permissions:
448 if fork_of and copy_fork_permissions:
449 repo = fork_of
449 repo = fork_of
450 user_perms = UserRepoToPerm.query() \
450 user_perms = UserRepoToPerm.query() \
451 .filter(UserRepoToPerm.repository == repo).all()
451 .filter(UserRepoToPerm.repository == repo).all()
452 group_perms = UserGroupRepoToPerm.query() \
452 group_perms = UserGroupRepoToPerm.query() \
453 .filter(UserGroupRepoToPerm.repository == repo).all()
453 .filter(UserGroupRepoToPerm.repository == repo).all()
454
454
455 for perm in user_perms:
455 for perm in user_perms:
456 UserRepoToPerm.create(
456 UserRepoToPerm.create(
457 perm.user, new_repo, perm.permission)
457 perm.user, new_repo, perm.permission)
458
458
459 for perm in group_perms:
459 for perm in group_perms:
460 UserGroupRepoToPerm.create(
460 UserGroupRepoToPerm.create(
461 perm.users_group, new_repo, perm.permission)
461 perm.users_group, new_repo, perm.permission)
462 # in case we copy permissions and also set this repo to private
462 # in case we copy permissions and also set this repo to private
463 # override the default user permission to make it a private
463 # override the default user permission to make it a private
464 # repo
464 # repo
465 if private:
465 if private:
466 RepoModel(self.sa).grant_user_permission(
466 RepoModel(self.sa).grant_user_permission(
467 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
467 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
468
468
469 elif repo_group and copy_group_permissions:
469 elif repo_group and copy_group_permissions:
470 user_perms = UserRepoGroupToPerm.query() \
470 user_perms = UserRepoGroupToPerm.query() \
471 .filter(UserRepoGroupToPerm.group == repo_group).all()
471 .filter(UserRepoGroupToPerm.group == repo_group).all()
472
472
473 group_perms = UserGroupRepoGroupToPerm.query() \
473 group_perms = UserGroupRepoGroupToPerm.query() \
474 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
474 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
475
475
476 for perm in user_perms:
476 for perm in user_perms:
477 perm_name = perm.permission.permission_name.replace(
477 perm_name = perm.permission.permission_name.replace(
478 'group.', 'repository.')
478 'group.', 'repository.')
479 perm_obj = Permission.get_by_key(perm_name)
479 perm_obj = Permission.get_by_key(perm_name)
480 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
480 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
481
481
482 for perm in group_perms:
482 for perm in group_perms:
483 perm_name = perm.permission.permission_name.replace(
483 perm_name = perm.permission.permission_name.replace(
484 'group.', 'repository.')
484 'group.', 'repository.')
485 perm_obj = Permission.get_by_key(perm_name)
485 perm_obj = Permission.get_by_key(perm_name)
486 UserGroupRepoToPerm.create(
486 UserGroupRepoToPerm.create(
487 perm.users_group, new_repo, perm_obj)
487 perm.users_group, new_repo, perm_obj)
488
488
489 if private:
489 if private:
490 RepoModel(self.sa).grant_user_permission(
490 RepoModel(self.sa).grant_user_permission(
491 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
491 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
492
492
493 else:
493 else:
494 perm_obj = self._create_default_perms(new_repo, private)
494 perm_obj = self._create_default_perms(new_repo, private)
495 self.sa.add(perm_obj)
495 self.sa.add(perm_obj)
496
496
497 # now automatically start following this repository as owner
497 # now automatically start following this repository as owner
498 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
498 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
499 owner.user_id)
499 owner.user_id)
500
500
501 # we need to flush here, in order to check if database won't
501 # we need to flush here, in order to check if database won't
502 # throw any exceptions, create filesystem dirs at the very end
502 # throw any exceptions, create filesystem dirs at the very end
503 self.sa.flush()
503 self.sa.flush()
504 events.trigger(events.RepoCreateEvent(new_repo))
504 events.trigger(events.RepoCreateEvent(new_repo))
505 return new_repo
505 return new_repo
506
506
507 except Exception:
507 except Exception:
508 log.error(traceback.format_exc())
508 log.error(traceback.format_exc())
509 raise
509 raise
510
510
511 def create(self, form_data, cur_user):
511 def create(self, form_data, cur_user):
512 """
512 """
513 Create repository using celery tasks
513 Create repository using celery tasks
514
514
515 :param form_data:
515 :param form_data:
516 :param cur_user:
516 :param cur_user:
517 """
517 """
518 from rhodecode.lib.celerylib import tasks, run_task
518 from rhodecode.lib.celerylib import tasks, run_task
519 return run_task(tasks.create_repo, form_data, cur_user)
519 return run_task(tasks.create_repo, form_data, cur_user)
520
520
521 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
521 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
522 perm_deletions=None, check_perms=True,
522 perm_deletions=None, check_perms=True,
523 cur_user=None):
523 cur_user=None):
524 if not perm_additions:
524 if not perm_additions:
525 perm_additions = []
525 perm_additions = []
526 if not perm_updates:
526 if not perm_updates:
527 perm_updates = []
527 perm_updates = []
528 if not perm_deletions:
528 if not perm_deletions:
529 perm_deletions = []
529 perm_deletions = []
530
530
531 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
531 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
532
532
533 changes = {
533 changes = {
534 'added': [],
534 'added': [],
535 'updated': [],
535 'updated': [],
536 'deleted': []
536 'deleted': []
537 }
537 }
538 # update permissions
538 # update permissions
539 for member_id, perm, member_type in perm_updates:
539 for member_id, perm, member_type in perm_updates:
540 member_id = int(member_id)
540 member_id = int(member_id)
541 if member_type == 'user':
541 if member_type == 'user':
542 member_name = User.get(member_id).username
542 member_name = User.get(member_id).username
543 # this updates also current one if found
543 # this updates also current one if found
544 self.grant_user_permission(
544 self.grant_user_permission(
545 repo=repo, user=member_id, perm=perm)
545 repo=repo, user=member_id, perm=perm)
546 else: # set for user group
546 else: # set for user group
547 # check if we have permissions to alter this usergroup
547 # check if we have permissions to alter this usergroup
548 member_name = UserGroup.get(member_id).users_group_name
548 member_name = UserGroup.get(member_id).users_group_name
549 if not check_perms or HasUserGroupPermissionAny(
549 if not check_perms or HasUserGroupPermissionAny(
550 *req_perms)(member_name, user=cur_user):
550 *req_perms)(member_name, user=cur_user):
551 self.grant_user_group_permission(
551 self.grant_user_group_permission(
552 repo=repo, group_name=member_id, perm=perm)
552 repo=repo, group_name=member_id, perm=perm)
553
553
554 changes['updated'].append({'type': member_type, 'id': member_id,
554 changes['updated'].append({'type': member_type, 'id': member_id,
555 'name': member_name, 'new_perm': perm})
555 'name': member_name, 'new_perm': perm})
556
556
557 # set new permissions
557 # set new permissions
558 for member_id, perm, member_type in perm_additions:
558 for member_id, perm, member_type in perm_additions:
559 member_id = int(member_id)
559 member_id = int(member_id)
560 if member_type == 'user':
560 if member_type == 'user':
561 member_name = User.get(member_id).username
561 member_name = User.get(member_id).username
562 self.grant_user_permission(
562 self.grant_user_permission(
563 repo=repo, user=member_id, perm=perm)
563 repo=repo, user=member_id, perm=perm)
564 else: # set for user group
564 else: # set for user group
565 # check if we have permissions to alter this usergroup
565 # check if we have permissions to alter this usergroup
566 member_name = UserGroup.get(member_id).users_group_name
566 member_name = UserGroup.get(member_id).users_group_name
567 if not check_perms or HasUserGroupPermissionAny(
567 if not check_perms or HasUserGroupPermissionAny(
568 *req_perms)(member_name, user=cur_user):
568 *req_perms)(member_name, user=cur_user):
569 self.grant_user_group_permission(
569 self.grant_user_group_permission(
570 repo=repo, group_name=member_id, perm=perm)
570 repo=repo, group_name=member_id, perm=perm)
571 changes['added'].append({'type': member_type, 'id': member_id,
571 changes['added'].append({'type': member_type, 'id': member_id,
572 'name': member_name, 'new_perm': perm})
572 'name': member_name, 'new_perm': perm})
573 # delete permissions
573 # delete permissions
574 for member_id, perm, member_type in perm_deletions:
574 for member_id, perm, member_type in perm_deletions:
575 member_id = int(member_id)
575 member_id = int(member_id)
576 if member_type == 'user':
576 if member_type == 'user':
577 member_name = User.get(member_id).username
577 member_name = User.get(member_id).username
578 self.revoke_user_permission(repo=repo, user=member_id)
578 self.revoke_user_permission(repo=repo, user=member_id)
579 else: # set for user group
579 else: # set for user group
580 # check if we have permissions to alter this usergroup
580 # check if we have permissions to alter this usergroup
581 member_name = UserGroup.get(member_id).users_group_name
581 member_name = UserGroup.get(member_id).users_group_name
582 if not check_perms or HasUserGroupPermissionAny(
582 if not check_perms or HasUserGroupPermissionAny(
583 *req_perms)(member_name, user=cur_user):
583 *req_perms)(member_name, user=cur_user):
584 self.revoke_user_group_permission(
584 self.revoke_user_group_permission(
585 repo=repo, group_name=member_id)
585 repo=repo, group_name=member_id)
586
586
587 changes['deleted'].append({'type': member_type, 'id': member_id,
587 changes['deleted'].append({'type': member_type, 'id': member_id,
588 'name': member_name, 'new_perm': perm})
588 'name': member_name, 'new_perm': perm})
589 return changes
589 return changes
590
590
591 def create_fork(self, form_data, cur_user):
591 def create_fork(self, form_data, cur_user):
592 """
592 """
593 Simple wrapper into executing celery task for fork creation
593 Simple wrapper into executing celery task for fork creation
594
594
595 :param form_data:
595 :param form_data:
596 :param cur_user:
596 :param cur_user:
597 """
597 """
598 from rhodecode.lib.celerylib import tasks, run_task
598 from rhodecode.lib.celerylib import tasks, run_task
599 return run_task(tasks.create_repo_fork, form_data, cur_user)
599 return run_task(tasks.create_repo_fork, form_data, cur_user)
600
600
601 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
601 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
602 """
602 """
603 Delete given repository, forks parameter defines what do do with
603 Delete given repository, forks parameter defines what do do with
604 attached forks. Throws AttachedForksError if deleted repo has attached
604 attached forks. Throws AttachedForksError if deleted repo has attached
605 forks
605 forks
606
606
607 :param repo:
607 :param repo:
608 :param forks: str 'delete' or 'detach'
608 :param forks: str 'delete' or 'detach'
609 :param fs_remove: remove(archive) repo from filesystem
609 :param fs_remove: remove(archive) repo from filesystem
610 """
610 """
611 if not cur_user:
611 if not cur_user:
612 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
612 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
613 repo = self._get_repo(repo)
613 repo = self._get_repo(repo)
614 if repo:
614 if repo:
615 if forks == 'detach':
615 if forks == 'detach':
616 for r in repo.forks:
616 for r in repo.forks:
617 r.fork = None
617 r.fork = None
618 self.sa.add(r)
618 self.sa.add(r)
619 elif forks == 'delete':
619 elif forks == 'delete':
620 for r in repo.forks:
620 for r in repo.forks:
621 self.delete(r, forks='delete')
621 self.delete(r, forks='delete')
622 elif [f for f in repo.forks]:
622 elif [f for f in repo.forks]:
623 raise AttachedForksError()
623 raise AttachedForksError()
624
624
625 old_repo_dict = repo.get_dict()
625 old_repo_dict = repo.get_dict()
626 events.trigger(events.RepoPreDeleteEvent(repo))
626 events.trigger(events.RepoPreDeleteEvent(repo))
627 try:
627 try:
628 self.sa.delete(repo)
628 self.sa.delete(repo)
629 if fs_remove:
629 if fs_remove:
630 self._delete_filesystem_repo(repo)
630 self._delete_filesystem_repo(repo)
631 else:
631 else:
632 log.debug('skipping removal from filesystem')
632 log.debug('skipping removal from filesystem')
633 old_repo_dict.update({
633 old_repo_dict.update({
634 'deleted_by': cur_user,
634 'deleted_by': cur_user,
635 'deleted_on': time.time(),
635 'deleted_on': time.time(),
636 })
636 })
637 log_delete_repository(**old_repo_dict)
637 log_delete_repository(**old_repo_dict)
638 events.trigger(events.RepoDeleteEvent(repo))
638 events.trigger(events.RepoDeleteEvent(repo))
639 except Exception:
639 except Exception:
640 log.error(traceback.format_exc())
640 log.error(traceback.format_exc())
641 raise
641 raise
642
642
643 def grant_user_permission(self, repo, user, perm):
643 def grant_user_permission(self, repo, user, perm):
644 """
644 """
645 Grant permission for user on given repository, or update existing one
645 Grant permission for user on given repository, or update existing one
646 if found
646 if found
647
647
648 :param repo: Instance of Repository, repository_id, or repository name
648 :param repo: Instance of Repository, repository_id, or repository name
649 :param user: Instance of User, user_id or username
649 :param user: Instance of User, user_id or username
650 :param perm: Instance of Permission, or permission_name
650 :param perm: Instance of Permission, or permission_name
651 """
651 """
652 user = self._get_user(user)
652 user = self._get_user(user)
653 repo = self._get_repo(repo)
653 repo = self._get_repo(repo)
654 permission = self._get_perm(perm)
654 permission = self._get_perm(perm)
655
655
656 # check if we have that permission already
656 # check if we have that permission already
657 obj = self.sa.query(UserRepoToPerm) \
657 obj = self.sa.query(UserRepoToPerm) \
658 .filter(UserRepoToPerm.user == user) \
658 .filter(UserRepoToPerm.user == user) \
659 .filter(UserRepoToPerm.repository == repo) \
659 .filter(UserRepoToPerm.repository == repo) \
660 .scalar()
660 .scalar()
661 if obj is None:
661 if obj is None:
662 # create new !
662 # create new !
663 obj = UserRepoToPerm()
663 obj = UserRepoToPerm()
664 obj.repository = repo
664 obj.repository = repo
665 obj.user = user
665 obj.user = user
666 obj.permission = permission
666 obj.permission = permission
667 self.sa.add(obj)
667 self.sa.add(obj)
668 log.debug('Granted perm %s to %s on %s', perm, user, repo)
668 log.debug('Granted perm %s to %s on %s', perm, user, repo)
669 action_logger_generic(
669 action_logger_generic(
670 'granted permission: {} to user: {} on repo: {}'.format(
670 'granted permission: {} to user: {} on repo: {}'.format(
671 perm, user, repo), namespace='security.repo')
671 perm, user, repo), namespace='security.repo')
672 return obj
672 return obj
673
673
674 def revoke_user_permission(self, repo, user):
674 def revoke_user_permission(self, repo, user):
675 """
675 """
676 Revoke permission for user on given repository
676 Revoke permission for user on given repository
677
677
678 :param repo: Instance of Repository, repository_id, or repository name
678 :param repo: Instance of Repository, repository_id, or repository name
679 :param user: Instance of User, user_id or username
679 :param user: Instance of User, user_id or username
680 """
680 """
681
681
682 user = self._get_user(user)
682 user = self._get_user(user)
683 repo = self._get_repo(repo)
683 repo = self._get_repo(repo)
684
684
685 obj = self.sa.query(UserRepoToPerm) \
685 obj = self.sa.query(UserRepoToPerm) \
686 .filter(UserRepoToPerm.repository == repo) \
686 .filter(UserRepoToPerm.repository == repo) \
687 .filter(UserRepoToPerm.user == user) \
687 .filter(UserRepoToPerm.user == user) \
688 .scalar()
688 .scalar()
689 if obj:
689 if obj:
690 self.sa.delete(obj)
690 self.sa.delete(obj)
691 log.debug('Revoked perm on %s on %s', repo, user)
691 log.debug('Revoked perm on %s on %s', repo, user)
692 action_logger_generic(
692 action_logger_generic(
693 'revoked permission from user: {} on repo: {}'.format(
693 'revoked permission from user: {} on repo: {}'.format(
694 user, repo), namespace='security.repo')
694 user, repo), namespace='security.repo')
695
695
696 def grant_user_group_permission(self, repo, group_name, perm):
696 def grant_user_group_permission(self, repo, group_name, perm):
697 """
697 """
698 Grant permission for user group on given repository, or update
698 Grant permission for user group on given repository, or update
699 existing one if found
699 existing one if found
700
700
701 :param repo: Instance of Repository, repository_id, or repository name
701 :param repo: Instance of Repository, repository_id, or repository name
702 :param group_name: Instance of UserGroup, users_group_id,
702 :param group_name: Instance of UserGroup, users_group_id,
703 or user group name
703 or user group name
704 :param perm: Instance of Permission, or permission_name
704 :param perm: Instance of Permission, or permission_name
705 """
705 """
706 repo = self._get_repo(repo)
706 repo = self._get_repo(repo)
707 group_name = self._get_user_group(group_name)
707 group_name = self._get_user_group(group_name)
708 permission = self._get_perm(perm)
708 permission = self._get_perm(perm)
709
709
710 # check if we have that permission already
710 # check if we have that permission already
711 obj = self.sa.query(UserGroupRepoToPerm) \
711 obj = self.sa.query(UserGroupRepoToPerm) \
712 .filter(UserGroupRepoToPerm.users_group == group_name) \
712 .filter(UserGroupRepoToPerm.users_group == group_name) \
713 .filter(UserGroupRepoToPerm.repository == repo) \
713 .filter(UserGroupRepoToPerm.repository == repo) \
714 .scalar()
714 .scalar()
715
715
716 if obj is None:
716 if obj is None:
717 # create new
717 # create new
718 obj = UserGroupRepoToPerm()
718 obj = UserGroupRepoToPerm()
719
719
720 obj.repository = repo
720 obj.repository = repo
721 obj.users_group = group_name
721 obj.users_group = group_name
722 obj.permission = permission
722 obj.permission = permission
723 self.sa.add(obj)
723 self.sa.add(obj)
724 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
724 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
725 action_logger_generic(
725 action_logger_generic(
726 'granted permission: {} to usergroup: {} on repo: {}'.format(
726 'granted permission: {} to usergroup: {} on repo: {}'.format(
727 perm, group_name, repo), namespace='security.repo')
727 perm, group_name, repo), namespace='security.repo')
728
728
729 return obj
729 return obj
730
730
731 def revoke_user_group_permission(self, repo, group_name):
731 def revoke_user_group_permission(self, repo, group_name):
732 """
732 """
733 Revoke permission for user group on given repository
733 Revoke permission for user group on given repository
734
734
735 :param repo: Instance of Repository, repository_id, or repository name
735 :param repo: Instance of Repository, repository_id, or repository name
736 :param group_name: Instance of UserGroup, users_group_id,
736 :param group_name: Instance of UserGroup, users_group_id,
737 or user group name
737 or user group name
738 """
738 """
739 repo = self._get_repo(repo)
739 repo = self._get_repo(repo)
740 group_name = self._get_user_group(group_name)
740 group_name = self._get_user_group(group_name)
741
741
742 obj = self.sa.query(UserGroupRepoToPerm) \
742 obj = self.sa.query(UserGroupRepoToPerm) \
743 .filter(UserGroupRepoToPerm.repository == repo) \
743 .filter(UserGroupRepoToPerm.repository == repo) \
744 .filter(UserGroupRepoToPerm.users_group == group_name) \
744 .filter(UserGroupRepoToPerm.users_group == group_name) \
745 .scalar()
745 .scalar()
746 if obj:
746 if obj:
747 self.sa.delete(obj)
747 self.sa.delete(obj)
748 log.debug('Revoked perm to %s on %s', repo, group_name)
748 log.debug('Revoked perm to %s on %s', repo, group_name)
749 action_logger_generic(
749 action_logger_generic(
750 'revoked permission from usergroup: {} on repo: {}'.format(
750 'revoked permission from usergroup: {} on repo: {}'.format(
751 group_name, repo), namespace='security.repo')
751 group_name, repo), namespace='security.repo')
752
752
753 def delete_stats(self, repo_name):
753 def delete_stats(self, repo_name):
754 """
754 """
755 removes stats for given repo
755 removes stats for given repo
756
756
757 :param repo_name:
757 :param repo_name:
758 """
758 """
759 repo = self._get_repo(repo_name)
759 repo = self._get_repo(repo_name)
760 try:
760 try:
761 obj = self.sa.query(Statistics) \
761 obj = self.sa.query(Statistics) \
762 .filter(Statistics.repository == repo).scalar()
762 .filter(Statistics.repository == repo).scalar()
763 if obj:
763 if obj:
764 self.sa.delete(obj)
764 self.sa.delete(obj)
765 except Exception:
765 except Exception:
766 log.error(traceback.format_exc())
766 log.error(traceback.format_exc())
767 raise
767 raise
768
768
769 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
769 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
770 field_type='str', field_desc=''):
770 field_type='str', field_desc=''):
771
771
772 repo = self._get_repo(repo_name)
772 repo = self._get_repo(repo_name)
773
773
774 new_field = RepositoryField()
774 new_field = RepositoryField()
775 new_field.repository = repo
775 new_field.repository = repo
776 new_field.field_key = field_key
776 new_field.field_key = field_key
777 new_field.field_type = field_type # python type
777 new_field.field_type = field_type # python type
778 new_field.field_value = field_value
778 new_field.field_value = field_value
779 new_field.field_desc = field_desc
779 new_field.field_desc = field_desc
780 new_field.field_label = field_label
780 new_field.field_label = field_label
781 self.sa.add(new_field)
781 self.sa.add(new_field)
782 return new_field
782 return new_field
783
783
784 def delete_repo_field(self, repo_name, field_key):
784 def delete_repo_field(self, repo_name, field_key):
785 repo = self._get_repo(repo_name)
785 repo = self._get_repo(repo_name)
786 field = RepositoryField.get_by_key_name(field_key, repo)
786 field = RepositoryField.get_by_key_name(field_key, repo)
787 if field:
787 if field:
788 self.sa.delete(field)
788 self.sa.delete(field)
789
789
790 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
790 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
791 clone_uri=None, repo_store_location=None,
791 clone_uri=None, repo_store_location=None,
792 use_global_config=False):
792 use_global_config=False):
793 """
793 """
794 makes repository on filesystem. It's group aware means it'll create
794 makes repository on filesystem. It's group aware means it'll create
795 a repository within a group, and alter the paths accordingly of
795 a repository within a group, and alter the paths accordingly of
796 group location
796 group location
797
797
798 :param repo_name:
798 :param repo_name:
799 :param alias:
799 :param alias:
800 :param parent:
800 :param parent:
801 :param clone_uri:
801 :param clone_uri:
802 :param repo_store_location:
802 :param repo_store_location:
803 """
803 """
804 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
804 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
805 from rhodecode.model.scm import ScmModel
805 from rhodecode.model.scm import ScmModel
806
806
807 if Repository.NAME_SEP in repo_name:
807 if Repository.NAME_SEP in repo_name:
808 raise ValueError(
808 raise ValueError(
809 'repo_name must not contain groups got `%s`' % repo_name)
809 'repo_name must not contain groups got `%s`' % repo_name)
810
810
811 if isinstance(repo_group, RepoGroup):
811 if isinstance(repo_group, RepoGroup):
812 new_parent_path = os.sep.join(repo_group.full_path_splitted)
812 new_parent_path = os.sep.join(repo_group.full_path_splitted)
813 else:
813 else:
814 new_parent_path = repo_group or ''
814 new_parent_path = repo_group or ''
815
815
816 if repo_store_location:
816 if repo_store_location:
817 _paths = [repo_store_location]
817 _paths = [repo_store_location]
818 else:
818 else:
819 _paths = [self.repos_path, new_parent_path, repo_name]
819 _paths = [self.repos_path, new_parent_path, repo_name]
820 # we need to make it str for mercurial
820 # we need to make it str for mercurial
821 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
821 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
822
822
823 # check if this path is not a repository
823 # check if this path is not a repository
824 if is_valid_repo(repo_path, self.repos_path):
824 if is_valid_repo(repo_path, self.repos_path):
825 raise Exception('This path %s is a valid repository' % repo_path)
825 raise Exception('This path %s is a valid repository' % repo_path)
826
826
827 # check if this path is a group
827 # check if this path is a group
828 if is_valid_repo_group(repo_path, self.repos_path):
828 if is_valid_repo_group(repo_path, self.repos_path):
829 raise Exception('This path %s is a valid group' % repo_path)
829 raise Exception('This path %s is a valid group' % repo_path)
830
830
831 log.info('creating repo %s in %s from url: `%s`',
831 log.info('creating repo %s in %s from url: `%s`',
832 repo_name, safe_unicode(repo_path),
832 repo_name, safe_unicode(repo_path),
833 obfuscate_url_pw(clone_uri))
833 obfuscate_url_pw(clone_uri))
834
834
835 backend = get_backend(repo_type)
835 backend = get_backend(repo_type)
836
836
837 config_repo = None if use_global_config else repo_name
837 config_repo = None if use_global_config else repo_name
838 if config_repo and new_parent_path:
838 if config_repo and new_parent_path:
839 config_repo = Repository.NAME_SEP.join(
839 config_repo = Repository.NAME_SEP.join(
840 (new_parent_path, config_repo))
840 (new_parent_path, config_repo))
841 config = make_db_config(clear_session=False, repo=config_repo)
841 config = make_db_config(clear_session=False, repo=config_repo)
842 config.set('extensions', 'largefiles', '')
842 config.set('extensions', 'largefiles', '')
843
843
844 # patch and reset hooks section of UI config to not run any
844 # patch and reset hooks section of UI config to not run any
845 # hooks on creating remote repo
845 # hooks on creating remote repo
846 config.clear_section('hooks')
846 config.clear_section('hooks')
847
847
848 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
848 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
849 if repo_type == 'git':
849 if repo_type == 'git':
850 repo = backend(
850 repo = backend(
851 repo_path, config=config, create=True, src_url=clone_uri,
851 repo_path, config=config, create=True, src_url=clone_uri,
852 bare=True)
852 bare=True)
853 else:
853 else:
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
856
857 ScmModel().install_hooks(repo, repo_type=repo_type)
857 ScmModel().install_hooks(repo, repo_type=repo_type)
858
858
859 log.debug('Created repo %s with %s backend',
859 log.debug('Created repo %s with %s backend',
860 safe_unicode(repo_name), safe_unicode(repo_type))
860 safe_unicode(repo_name), safe_unicode(repo_type))
861 return repo
861 return repo
862
862
863 def _rename_filesystem_repo(self, old, new):
863 def _rename_filesystem_repo(self, old, new):
864 """
864 """
865 renames repository on filesystem
865 renames repository on filesystem
866
866
867 :param old: old name
867 :param old: old name
868 :param new: new name
868 :param new: new name
869 """
869 """
870 log.info('renaming repo from %s to %s', old, new)
870 log.info('renaming repo from %s to %s', old, new)
871
871
872 old_path = os.path.join(self.repos_path, old)
872 old_path = os.path.join(self.repos_path, old)
873 new_path = os.path.join(self.repos_path, new)
873 new_path = os.path.join(self.repos_path, new)
874 if os.path.isdir(new_path):
874 if os.path.isdir(new_path):
875 raise Exception(
875 raise Exception(
876 'Was trying to rename to already existing dir %s' % new_path
876 'Was trying to rename to already existing dir %s' % new_path
877 )
877 )
878 shutil.move(old_path, new_path)
878 shutil.move(old_path, new_path)
879
879
880 def _delete_filesystem_repo(self, repo):
880 def _delete_filesystem_repo(self, repo):
881 """
881 """
882 removes repo from filesystem, the removal is acctually made by
882 removes repo from filesystem, the removal is acctually made by
883 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
883 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
884 repository is no longer valid for rhodecode, can be undeleted later on
884 repository is no longer valid for rhodecode, can be undeleted later on
885 by reverting the renames on this repository
885 by reverting the renames on this repository
886
886
887 :param repo: repo object
887 :param repo: repo object
888 """
888 """
889 rm_path = os.path.join(self.repos_path, repo.repo_name)
889 rm_path = os.path.join(self.repos_path, repo.repo_name)
890 repo_group = repo.group
890 repo_group = repo.group
891 log.info("Removing repository %s", rm_path)
891 log.info("Removing repository %s", rm_path)
892 # disable hg/git internal that it doesn't get detected as repo
892 # disable hg/git internal that it doesn't get detected as repo
893 alias = repo.repo_type
893 alias = repo.repo_type
894
894
895 config = make_db_config(clear_session=False)
895 config = make_db_config(clear_session=False)
896 config.set('extensions', 'largefiles', '')
896 config.set('extensions', 'largefiles', '')
897 bare = getattr(repo.scm_instance(config=config), 'bare', False)
897 bare = getattr(repo.scm_instance(config=config), 'bare', False)
898
898
899 # skip this for bare git repos
899 # skip this for bare git repos
900 if not bare:
900 if not bare:
901 # disable VCS repo
901 # disable VCS repo
902 vcs_path = os.path.join(rm_path, '.%s' % alias)
902 vcs_path = os.path.join(rm_path, '.%s' % alias)
903 if os.path.exists(vcs_path):
903 if os.path.exists(vcs_path):
904 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
904 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
905
905
906 _now = datetime.datetime.now()
906 _now = datetime.datetime.now()
907 _ms = str(_now.microsecond).rjust(6, '0')
907 _ms = str(_now.microsecond).rjust(6, '0')
908 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
908 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
909 repo.just_name)
909 repo.just_name)
910 if repo_group:
910 if repo_group:
911 # if repository is in group, prefix the removal path with the group
911 # if repository is in group, prefix the removal path with the group
912 args = repo_group.full_path_splitted + [_d]
912 args = repo_group.full_path_splitted + [_d]
913 _d = os.path.join(*args)
913 _d = os.path.join(*args)
914
914
915 if os.path.isdir(rm_path):
915 if os.path.isdir(rm_path):
916 shutil.move(rm_path, os.path.join(self.repos_path, _d))
916 shutil.move(rm_path, os.path.join(self.repos_path, _d))
917
917
918
918
919 class ReadmeFinder:
919 class ReadmeFinder:
920 """
920 """
921 Utility which knows how to find a readme for a specific commit.
921 Utility which knows how to find a readme for a specific commit.
922
922
923 The main idea is that this is a configurable algorithm. When creating an
923 The main idea is that this is a configurable algorithm. When creating an
924 instance you can define parameters, currently only the `default_renderer`.
924 instance you can define parameters, currently only the `default_renderer`.
925 Based on this configuration the method :meth:`search` behaves slightly
925 Based on this configuration the method :meth:`search` behaves slightly
926 different.
926 different.
927 """
927 """
928
928
929 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
929 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
930 path_re = re.compile(r'^docs?', re.IGNORECASE)
930 path_re = re.compile(r'^docs?', re.IGNORECASE)
931
931
932 default_priorities = {
932 default_priorities = {
933 None: 0,
933 None: 0,
934 '.text': 2,
934 '.text': 2,
935 '.txt': 3,
935 '.txt': 3,
936 '.rst': 1,
936 '.rst': 1,
937 '.rest': 2,
937 '.rest': 2,
938 '.md': 1,
938 '.md': 1,
939 '.mkdn': 2,
939 '.mkdn': 2,
940 '.mdown': 3,
940 '.mdown': 3,
941 '.markdown': 4,
941 '.markdown': 4,
942 }
942 }
943
943
944 path_priority = {
944 path_priority = {
945 'doc': 0,
945 'doc': 0,
946 'docs': 1,
946 'docs': 1,
947 }
947 }
948
948
949 FALLBACK_PRIORITY = 99
949 FALLBACK_PRIORITY = 99
950
950
951 RENDERER_TO_EXTENSION = {
951 RENDERER_TO_EXTENSION = {
952 'rst': ['.rst', '.rest'],
952 'rst': ['.rst', '.rest'],
953 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
953 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
954 }
954 }
955
955
956 def __init__(self, default_renderer=None):
956 def __init__(self, default_renderer=None):
957 self._default_renderer = default_renderer
957 self._default_renderer = default_renderer
958 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
958 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
959 default_renderer, [])
959 default_renderer, [])
960
960
961 def search(self, commit, path='/'):
961 def search(self, commit, path='/'):
962 """
962 """
963 Find a readme in the given `commit`.
963 Find a readme in the given `commit`.
964 """
964 """
965 nodes = commit.get_nodes(path)
965 nodes = commit.get_nodes(path)
966 matches = self._match_readmes(nodes)
966 matches = self._match_readmes(nodes)
967 matches = self._sort_according_to_priority(matches)
967 matches = self._sort_according_to_priority(matches)
968 if matches:
968 if matches:
969 return matches[0].node
969 return matches[0].node
970
970
971 paths = self._match_paths(nodes)
971 paths = self._match_paths(nodes)
972 paths = self._sort_paths_according_to_priority(paths)
972 paths = self._sort_paths_according_to_priority(paths)
973 for path in paths:
973 for path in paths:
974 match = self.search(commit, path=path)
974 match = self.search(commit, path=path)
975 if match:
975 if match:
976 return match
976 return match
977
977
978 return None
978 return None
979
979
980 def _match_readmes(self, nodes):
980 def _match_readmes(self, nodes):
981 for node in nodes:
981 for node in nodes:
982 if not node.is_file():
982 if not node.is_file():
983 continue
983 continue
984 path = node.path.rsplit('/', 1)[-1]
984 path = node.path.rsplit('/', 1)[-1]
985 match = self.readme_re.match(path)
985 match = self.readme_re.match(path)
986 if match:
986 if match:
987 extension = match.group(1)
987 extension = match.group(1)
988 yield ReadmeMatch(node, match, self._priority(extension))
988 yield ReadmeMatch(node, match, self._priority(extension))
989
989
990 def _match_paths(self, nodes):
990 def _match_paths(self, nodes):
991 for node in nodes:
991 for node in nodes:
992 if not node.is_dir():
992 if not node.is_dir():
993 continue
993 continue
994 match = self.path_re.match(node.path)
994 match = self.path_re.match(node.path)
995 if match:
995 if match:
996 yield node.path
996 yield node.path
997
997
998 def _priority(self, extension):
998 def _priority(self, extension):
999 renderer_priority = (
999 renderer_priority = (
1000 0 if extension in self._renderer_extensions else 1)
1000 0 if extension in self._renderer_extensions else 1)
1001 extension_priority = self.default_priorities.get(
1001 extension_priority = self.default_priorities.get(
1002 extension, self.FALLBACK_PRIORITY)
1002 extension, self.FALLBACK_PRIORITY)
1003 return (renderer_priority, extension_priority)
1003 return (renderer_priority, extension_priority)
1004
1004
1005 def _sort_according_to_priority(self, matches):
1005 def _sort_according_to_priority(self, matches):
1006
1006
1007 def priority_and_path(match):
1007 def priority_and_path(match):
1008 return (match.priority, match.path)
1008 return (match.priority, match.path)
1009
1009
1010 return sorted(matches, key=priority_and_path)
1010 return sorted(matches, key=priority_and_path)
1011
1011
1012 def _sort_paths_according_to_priority(self, paths):
1012 def _sort_paths_according_to_priority(self, paths):
1013
1013
1014 def priority_and_path(path):
1014 def priority_and_path(path):
1015 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1015 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1016
1016
1017 return sorted(paths, key=priority_and_path)
1017 return sorted(paths, key=priority_and_path)
1018
1018
1019
1019
1020 class ReadmeMatch:
1020 class ReadmeMatch:
1021
1021
1022 def __init__(self, node, match, priority):
1022 def __init__(self, node, match, priority):
1023 self.node = node
1023 self.node = node
1024 self._match = match
1024 self._match = match
1025 self.priority = priority
1025 self.priority = priority
1026
1026
1027 @property
1027 @property
1028 def path(self):
1028 def path(self):
1029 return self.node.path
1029 return self.node.path
1030
1030
1031 def __repr__(self):
1031 def __repr__(self):
1032 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1032 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
General Comments 0
You need to be logged in to leave comments. Login now