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