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