##// END OF EJS Templates
permissions: flush cache when creating a new fork.
marcink -
r2874:b71876f9 default
parent child Browse files
Show More
@@ -1,43 +1,43 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from rhodecode import events
23 from rhodecode import events
24 from rhodecode.lib import rc_cache
24 from rhodecode.lib import rc_cache
25
25
26 log = logging.getLogger(__name__)
26 log = logging.getLogger(__name__)
27
27
28
28
29 def trigger_user_permission_flush(event):
29 def trigger_user_permission_flush(event):
30 """
30 """
31 Subscriber to the `UserPermissionChange`. This triggers the
31 Subscriber to the `UserPermissionsChange`. This triggers the
32 automatic flush of permission caches, so the users affected receive new permissions
32 automatic flush of permission caches, so the users affected receive new permissions
33 Right Away
33 Right Away
34 """
34 """
35 affected_user_ids = event.user_ids
35 affected_user_ids = event.user_ids
36 for user_id in affected_user_ids:
36 for user_id in affected_user_ids:
37 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
37 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
38 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
38 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
39 log.debug('Deleted %s cache keys for user_id: %s', del_keys, user_id)
39 log.debug('Deleted %s cache keys for user_id: %s', del_keys, user_id)
40
40
41
41
42 def includeme(config):
42 def includeme(config):
43 config.add_subscriber(trigger_user_permission_flush, events.UserPermissionsChange)
43 config.add_subscriber(trigger_user_permission_flush, events.UserPermissionsChange)
@@ -1,259 +1,263 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2018 RhodeCode GmbH
3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode import events
31 from rhodecode.apps._base import RepoAppView, DataGridAppView
32 from rhodecode.apps._base import RepoAppView, DataGridAppView
32 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
33 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
34 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
35 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
36 from rhodecode.lib.celerylib.utils import get_task_id
37 from rhodecode.lib.celerylib.utils import get_task_id
37 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
38 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.forms import RepoForkForm
40 from rhodecode.model.forms import RepoForkForm
40 from rhodecode.model.scm import ScmModel, RepoGroupList
41 from rhodecode.model.scm import ScmModel, RepoGroupList
41 from rhodecode.lib.utils2 import safe_int, safe_unicode
42 from rhodecode.lib.utils2 import safe_int, safe_unicode
42
43
43 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
44
45
45
46
46 class RepoForksView(RepoAppView, DataGridAppView):
47 class RepoForksView(RepoAppView, DataGridAppView):
47
48
48 def load_default_context(self):
49 def load_default_context(self):
49 c = self._get_local_tmpl_context(include_app_defaults=True)
50 c = self._get_local_tmpl_context(include_app_defaults=True)
50 c.rhodecode_repo = self.rhodecode_vcs_repo
51 c.rhodecode_repo = self.rhodecode_vcs_repo
51
52
52 acl_groups = RepoGroupList(
53 acl_groups = RepoGroupList(
53 RepoGroup.query().all(),
54 RepoGroup.query().all(),
54 perm_set=['group.write', 'group.admin'])
55 perm_set=['group.write', 'group.admin'])
55 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
56 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
56 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
57 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
57 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
58 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
58 self.request.translate)
59 self.request.translate)
59 c.landing_revs_choices = choices
60 c.landing_revs_choices = choices
60 c.personal_repo_group = c.rhodecode_user.personal_repo_group
61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
61
62
62 return c
63 return c
63
64
64 @LoginRequired()
65 @LoginRequired()
65 @HasRepoPermissionAnyDecorator(
66 @HasRepoPermissionAnyDecorator(
66 'repository.read', 'repository.write', 'repository.admin')
67 'repository.read', 'repository.write', 'repository.admin')
67 @view_config(
68 @view_config(
68 route_name='repo_forks_show_all', request_method='GET',
69 route_name='repo_forks_show_all', request_method='GET',
69 renderer='rhodecode:templates/forks/forks.mako')
70 renderer='rhodecode:templates/forks/forks.mako')
70 def repo_forks_show_all(self):
71 def repo_forks_show_all(self):
71 c = self.load_default_context()
72 c = self.load_default_context()
72 return self._get_template_context(c)
73 return self._get_template_context(c)
73
74
74 @LoginRequired()
75 @LoginRequired()
75 @HasRepoPermissionAnyDecorator(
76 @HasRepoPermissionAnyDecorator(
76 'repository.read', 'repository.write', 'repository.admin')
77 'repository.read', 'repository.write', 'repository.admin')
77 @view_config(
78 @view_config(
78 route_name='repo_forks_data', request_method='GET',
79 route_name='repo_forks_data', request_method='GET',
79 renderer='json_ext', xhr=True)
80 renderer='json_ext', xhr=True)
80 def repo_forks_data(self):
81 def repo_forks_data(self):
81 _ = self.request.translate
82 _ = self.request.translate
82 self.load_default_context()
83 self.load_default_context()
83 column_map = {
84 column_map = {
84 'fork_name': 'repo_name',
85 'fork_name': 'repo_name',
85 'fork_date': 'created_on',
86 'fork_date': 'created_on',
86 'last_activity': 'updated_on'
87 'last_activity': 'updated_on'
87 }
88 }
88 draw, start, limit = self._extract_chunk(self.request)
89 draw, start, limit = self._extract_chunk(self.request)
89 search_q, order_by, order_dir = self._extract_ordering(
90 search_q, order_by, order_dir = self._extract_ordering(
90 self.request, column_map=column_map)
91 self.request, column_map=column_map)
91
92
92 acl_check = HasRepoPermissionAny(
93 acl_check = HasRepoPermissionAny(
93 'repository.read', 'repository.write', 'repository.admin')
94 'repository.read', 'repository.write', 'repository.admin')
94 repo_id = self.db_repo.repo_id
95 repo_id = self.db_repo.repo_id
95 allowed_ids = [-1]
96 allowed_ids = [-1]
96 for f in Repository.query().filter(Repository.fork_id == repo_id):
97 for f in Repository.query().filter(Repository.fork_id == repo_id):
97 if acl_check(f.repo_name, 'get forks check'):
98 if acl_check(f.repo_name, 'get forks check'):
98 allowed_ids.append(f.repo_id)
99 allowed_ids.append(f.repo_id)
99
100
100 forks_data_total_count = Repository.query()\
101 forks_data_total_count = Repository.query()\
101 .filter(Repository.fork_id == repo_id)\
102 .filter(Repository.fork_id == repo_id)\
102 .filter(Repository.repo_id.in_(allowed_ids))\
103 .filter(Repository.repo_id.in_(allowed_ids))\
103 .count()
104 .count()
104
105
105 # json generate
106 # json generate
106 base_q = Repository.query()\
107 base_q = Repository.query()\
107 .filter(Repository.fork_id == repo_id)\
108 .filter(Repository.fork_id == repo_id)\
108 .filter(Repository.repo_id.in_(allowed_ids))\
109 .filter(Repository.repo_id.in_(allowed_ids))\
109
110
110 if search_q:
111 if search_q:
111 like_expression = u'%{}%'.format(safe_unicode(search_q))
112 like_expression = u'%{}%'.format(safe_unicode(search_q))
112 base_q = base_q.filter(or_(
113 base_q = base_q.filter(or_(
113 Repository.repo_name.ilike(like_expression),
114 Repository.repo_name.ilike(like_expression),
114 Repository.description.ilike(like_expression),
115 Repository.description.ilike(like_expression),
115 ))
116 ))
116
117
117 forks_data_total_filtered_count = base_q.count()
118 forks_data_total_filtered_count = base_q.count()
118
119
119 sort_col = getattr(Repository, order_by, None)
120 sort_col = getattr(Repository, order_by, None)
120 if sort_col:
121 if sort_col:
121 if order_dir == 'asc':
122 if order_dir == 'asc':
122 # handle null values properly to order by NULL last
123 # handle null values properly to order by NULL last
123 if order_by in ['last_activity']:
124 if order_by in ['last_activity']:
124 sort_col = coalesce(sort_col, datetime.date.max)
125 sort_col = coalesce(sort_col, datetime.date.max)
125 sort_col = sort_col.asc()
126 sort_col = sort_col.asc()
126 else:
127 else:
127 # handle null values properly to order by NULL last
128 # handle null values properly to order by NULL last
128 if order_by in ['last_activity']:
129 if order_by in ['last_activity']:
129 sort_col = coalesce(sort_col, datetime.date.min)
130 sort_col = coalesce(sort_col, datetime.date.min)
130 sort_col = sort_col.desc()
131 sort_col = sort_col.desc()
131
132
132 base_q = base_q.order_by(sort_col)
133 base_q = base_q.order_by(sort_col)
133 base_q = base_q.offset(start).limit(limit)
134 base_q = base_q.offset(start).limit(limit)
134
135
135 fork_list = base_q.all()
136 fork_list = base_q.all()
136
137
137 def fork_actions(fork):
138 def fork_actions(fork):
138 url_link = h.route_path(
139 url_link = h.route_path(
139 'repo_compare',
140 'repo_compare',
140 repo_name=fork.repo_name,
141 repo_name=fork.repo_name,
141 source_ref_type=self.db_repo.landing_rev[0],
142 source_ref_type=self.db_repo.landing_rev[0],
142 source_ref=self.db_repo.landing_rev[1],
143 source_ref=self.db_repo.landing_rev[1],
143 target_ref_type=self.db_repo.landing_rev[0],
144 target_ref_type=self.db_repo.landing_rev[0],
144 target_ref=self.db_repo.landing_rev[1],
145 target_ref=self.db_repo.landing_rev[1],
145 _query=dict(merge=1, target_repo=f.repo_name))
146 _query=dict(merge=1, target_repo=f.repo_name))
146 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
147
148
148 def fork_name(fork):
149 def fork_name(fork):
149 return h.link_to(fork.repo_name,
150 return h.link_to(fork.repo_name,
150 h.route_path('repo_summary', repo_name=fork.repo_name))
151 h.route_path('repo_summary', repo_name=fork.repo_name))
151
152
152 forks_data = []
153 forks_data = []
153 for fork in fork_list:
154 for fork in fork_list:
154 forks_data.append({
155 forks_data.append({
155 "username": h.gravatar_with_user(self.request, fork.user.username),
156 "username": h.gravatar_with_user(self.request, fork.user.username),
156 "fork_name": fork_name(fork),
157 "fork_name": fork_name(fork),
157 "description": fork.description,
158 "description": fork.description,
158 "fork_date": h.age_component(fork.created_on, time_is_local=True),
159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
159 "last_activity": h.format_date(fork.updated_on),
160 "last_activity": h.format_date(fork.updated_on),
160 "action": fork_actions(fork),
161 "action": fork_actions(fork),
161 })
162 })
162
163
163 data = ({
164 data = ({
164 'draw': draw,
165 'draw': draw,
165 'data': forks_data,
166 'data': forks_data,
166 'recordsTotal': forks_data_total_count,
167 'recordsTotal': forks_data_total_count,
167 'recordsFiltered': forks_data_total_filtered_count,
168 'recordsFiltered': forks_data_total_filtered_count,
168 })
169 })
169
170
170 return data
171 return data
171
172
172 @LoginRequired()
173 @LoginRequired()
173 @NotAnonymous()
174 @NotAnonymous()
174 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
175 @HasRepoPermissionAnyDecorator(
176 @HasRepoPermissionAnyDecorator(
176 'repository.read', 'repository.write', 'repository.admin')
177 'repository.read', 'repository.write', 'repository.admin')
177 @view_config(
178 @view_config(
178 route_name='repo_fork_new', request_method='GET',
179 route_name='repo_fork_new', request_method='GET',
179 renderer='rhodecode:templates/forks/forks.mako')
180 renderer='rhodecode:templates/forks/forks.mako')
180 def repo_fork_new(self):
181 def repo_fork_new(self):
181 c = self.load_default_context()
182 c = self.load_default_context()
182
183
183 defaults = RepoModel()._get_defaults(self.db_repo_name)
184 defaults = RepoModel()._get_defaults(self.db_repo_name)
184 # alter the description to indicate a fork
185 # alter the description to indicate a fork
185 defaults['description'] = (
186 defaults['description'] = (
186 'fork of repository: %s \n%s' % (
187 'fork of repository: %s \n%s' % (
187 defaults['repo_name'], defaults['description']))
188 defaults['repo_name'], defaults['description']))
188 # add suffix to fork
189 # add suffix to fork
189 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
190
191
191 data = render('rhodecode:templates/forks/fork.mako',
192 data = render('rhodecode:templates/forks/fork.mako',
192 self._get_template_context(c), self.request)
193 self._get_template_context(c), self.request)
193 html = formencode.htmlfill.render(
194 html = formencode.htmlfill.render(
194 data,
195 data,
195 defaults=defaults,
196 defaults=defaults,
196 encoding="UTF-8",
197 encoding="UTF-8",
197 force_defaults=False
198 force_defaults=False
198 )
199 )
199 return Response(html)
200 return Response(html)
200
201
201 @LoginRequired()
202 @LoginRequired()
202 @NotAnonymous()
203 @NotAnonymous()
203 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
204 @HasRepoPermissionAnyDecorator(
205 @HasRepoPermissionAnyDecorator(
205 'repository.read', 'repository.write', 'repository.admin')
206 'repository.read', 'repository.write', 'repository.admin')
206 @CSRFRequired()
207 @CSRFRequired()
207 @view_config(
208 @view_config(
208 route_name='repo_fork_create', request_method='POST',
209 route_name='repo_fork_create', request_method='POST',
209 renderer='rhodecode:templates/forks/fork.mako')
210 renderer='rhodecode:templates/forks/fork.mako')
210 def repo_fork_create(self):
211 def repo_fork_create(self):
211 _ = self.request.translate
212 _ = self.request.translate
212 c = self.load_default_context()
213 c = self.load_default_context()
213
214
214 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
215 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
215 repo_groups=c.repo_groups_choices,
216 repo_groups=c.repo_groups_choices,
216 landing_revs=c.landing_revs_choices)()
217 landing_revs=c.landing_revs_choices)()
217 post_data = dict(self.request.POST)
218 post_data = dict(self.request.POST)
218
219
219 # forbid injecting other repo by forging a request
220 # forbid injecting other repo by forging a request
220 post_data['fork_parent_id'] = self.db_repo.repo_id
221 post_data['fork_parent_id'] = self.db_repo.repo_id
221
222
222 form_result = {}
223 form_result = {}
223 task_id = None
224 task_id = None
224 try:
225 try:
225 form_result = _form.to_python(post_data)
226 form_result = _form.to_python(post_data)
226 # create fork is done sometimes async on celery, db transaction
227 # create fork is done sometimes async on celery, db transaction
227 # management is handled there.
228 # management is handled there.
228 task = RepoModel().create_fork(
229 task = RepoModel().create_fork(
229 form_result, c.rhodecode_user.user_id)
230 form_result, c.rhodecode_user.user_id)
230
231
231 task_id = get_task_id(task)
232 task_id = get_task_id(task)
232 except formencode.Invalid as errors:
233 except formencode.Invalid as errors:
233 c.rhodecode_db_repo = self.db_repo
234 c.rhodecode_db_repo = self.db_repo
234
235
235 data = render('rhodecode:templates/forks/fork.mako',
236 data = render('rhodecode:templates/forks/fork.mako',
236 self._get_template_context(c), self.request)
237 self._get_template_context(c), self.request)
237 html = formencode.htmlfill.render(
238 html = formencode.htmlfill.render(
238 data,
239 data,
239 defaults=errors.value,
240 defaults=errors.value,
240 errors=errors.error_dict or {},
241 errors=errors.error_dict or {},
241 prefix_error=False,
242 prefix_error=False,
242 encoding="UTF-8",
243 encoding="UTF-8",
243 force_defaults=False
244 force_defaults=False
244 )
245 )
245 return Response(html)
246 return Response(html)
246 except Exception:
247 except Exception:
247 log.exception(
248 log.exception(
248 u'Exception while trying to fork the repository %s',
249 u'Exception while trying to fork the repository %s',
249 self.db_repo_name)
250 self.db_repo_name)
250 msg = (
251 msg = (
251 _('An error occurred during repository forking %s') % (
252 _('An error occurred during repository forking %s') % (
252 self.db_repo_name, ))
253 self.db_repo_name, ))
253 h.flash(msg, category='error')
254 h.flash(msg, category='error')
254
255
255 repo_name = form_result.get('repo_name_full', self.db_repo_name)
256 repo_name = form_result.get('repo_name_full', self.db_repo_name)
257
258 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
259
256 raise HTTPFound(
260 raise HTTPFound(
257 h.route_path('repo_creating',
261 h.route_path('repo_creating',
258 repo_name=repo_name,
262 repo_name=repo_name,
259 _query=dict(task_id=task_id)))
263 _query=dict(task_id=task_id)))
General Comments 0
You need to be logged in to leave comments. Login now