##// END OF EJS Templates
security: added missing csrf checks in few missing views.
ergo -
r1811:d57dfc88 default
parent child Browse files
Show More
@@ -1,102 +1,102 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from pyramid.view import view_config
24 24 from pyramid.httpexceptions import HTTPFound
25 25
26 26 from rhodecode.apps._base import BaseAppView
27 27 from rhodecode.apps.admin.navigation import navigation_list
28 28 from rhodecode.lib.auth import (
29 29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30 30 from rhodecode.lib.utils2 import safe_int
31 31 from rhodecode.lib import system_info
32 32 from rhodecode.lib import user_sessions
33 33
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class AdminSessionSettingsView(BaseAppView):
39 39 def load_default_context(self):
40 40 c = self._get_local_tmpl_context()
41 41
42 42 self._register_global_c(c)
43 43 return c
44 44
45 45 @LoginRequired()
46 46 @HasPermissionAllDecorator('hg.admin')
47 47 @view_config(
48 48 route_name='admin_settings_sessions', request_method='GET',
49 49 renderer='rhodecode:templates/admin/settings/settings.mako')
50 50 def settings_sessions(self):
51 51 c = self.load_default_context()
52 52
53 53 c.active = 'sessions'
54 54 c.navlist = navigation_list(self.request)
55 55
56 56 c.cleanup_older_days = 60
57 57 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
58 58
59 59 config = system_info.rhodecode_config().get_value()['value']['config']
60 60 c.session_model = user_sessions.get_session_handler(
61 61 config.get('beaker.session.type', 'memory'))(config)
62 62
63 63 c.session_conf = c.session_model.config
64 64 c.session_count = c.session_model.get_count()
65 65 c.session_expired_count = c.session_model.get_expired_count(
66 66 older_than_seconds)
67 67
68 68 return self._get_template_context(c)
69 69
70 70 @LoginRequired()
71 @HasPermissionAllDecorator('hg.admin')
71 72 @CSRFRequired()
72 @HasPermissionAllDecorator('hg.admin')
73 73 @view_config(
74 74 route_name='admin_settings_sessions_cleanup', request_method='POST')
75 75 def settings_sessions_cleanup(self):
76 76 _ = self.request.translate
77 77 expire_days = safe_int(self.request.params.get('expire_days'))
78 78
79 79 if expire_days is None:
80 80 expire_days = 60
81 81
82 82 older_than_seconds = 60 * 60 * 24 * expire_days
83 83
84 84 config = system_info.rhodecode_config().get_value()['value']['config']
85 85 session_model = user_sessions.get_session_handler(
86 86 config.get('beaker.session.type', 'memory'))(config)
87 87
88 88 try:
89 89 session_model.clean_sessions(
90 90 older_than_seconds=older_than_seconds)
91 91 self.request.session.flash(
92 92 _('Cleaned up old sessions'), queue='success')
93 93 except user_sessions.CleanupCommand as msg:
94 94 self.request.session.flash(msg.message, queue='warning')
95 95 except Exception as e:
96 96 log.exception('Failed session cleanup')
97 97 self.request.session.flash(
98 98 _('Failed to cleanup up old sessions'), queue='error')
99 99
100 100 redirect_to = self.request.resource_path(
101 101 self.context, route_name='admin_settings_sessions')
102 102 return HTTPFound(redirect_to)
@@ -1,59 +1,59 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from pyramid.view import view_config
24 24
25 25 from rhodecode.apps._base import BaseAppView
26 26 from rhodecode.apps.svn_support.utils import generate_mod_dav_svn_config
27 27 from rhodecode.lib.auth import (
28 28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 29
30 30 log = logging.getLogger(__name__)
31 31
32 32
33 33 class SvnConfigAdminSettingsView(BaseAppView):
34 34
35 35 @LoginRequired()
36 @HasPermissionAllDecorator('hg.admin')
36 37 @CSRFRequired()
37 @HasPermissionAllDecorator('hg.admin')
38 38 @view_config(
39 39 route_name='admin_settings_vcs_svn_generate_cfg',
40 40 request_method='POST', renderer='json')
41 41 def vcs_svn_generate_config(self):
42 42 _ = self.request.translate
43 43 try:
44 44 generate_mod_dav_svn_config(self.request.registry)
45 45 msg = {
46 46 'message': _('Apache configuration for Subversion generated.'),
47 47 'level': 'success',
48 48 }
49 49 except Exception:
50 50 log.exception(
51 51 'Exception while generating the Apache '
52 52 'configuration for Subversion.')
53 53 msg = {
54 54 'message': _('Failed to generate the Apache configuration for Subversion.'),
55 55 'level': 'error',
56 56 }
57 57
58 58 data = {'message': msg}
59 59 return data
@@ -1,307 +1,308 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23
24 24 from pyramid.httpexceptions import HTTPFound
25 25 from pyramid.view import view_config
26 26 from sqlalchemy.sql.functions import coalesce
27 27
28 28 from rhodecode.lib.helpers import Page
29 29 from rhodecode.lib.ext_json import json
30 30
31 31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 32 from rhodecode.lib.auth import (
33 33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 34 from rhodecode.lib import helpers as h
35 35 from rhodecode.lib.utils import PartialRenderer
36 36 from rhodecode.lib.utils2 import safe_int, safe_unicode
37 37 from rhodecode.model.auth_token import AuthTokenModel
38 38 from rhodecode.model.user import UserModel
39 39 from rhodecode.model.user_group import UserGroupModel
40 40 from rhodecode.model.db import User, or_
41 41 from rhodecode.model.meta import Session
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class AdminUsersView(BaseAppView, DataGridAppView):
47 47 ALLOW_SCOPED_TOKENS = False
48 48 """
49 49 This view has alternative version inside EE, if modified please take a look
50 50 in there as well.
51 51 """
52 52
53 53 def load_default_context(self):
54 54 c = self._get_local_tmpl_context()
55 55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
56 56 self._register_global_c(c)
57 57 return c
58 58
59 59 def _redirect_for_default_user(self, username):
60 60 _ = self.request.translate
61 61 if username == User.DEFAULT_USER:
62 62 h.flash(_("You can't edit this user"), category='warning')
63 63 # TODO(marcink): redirect to 'users' admin panel once this
64 64 # is a pyramid view
65 65 raise HTTPFound('/')
66 66
67 67 @HasPermissionAllDecorator('hg.admin')
68 68 @view_config(
69 69 route_name='users', request_method='GET',
70 70 renderer='rhodecode:templates/admin/users/users.mako')
71 71 def users_list(self):
72 72 c = self.load_default_context()
73 73 return self._get_template_context(c)
74 74
75 75 @HasPermissionAllDecorator('hg.admin')
76 76 @view_config(
77 77 # renderer defined below
78 78 route_name='users_data', request_method='GET',
79 79 renderer='json_ext', xhr=True)
80 80 def users_list_data(self):
81 81 draw, start, limit = self._extract_chunk(self.request)
82 82 search_q, order_by, order_dir = self._extract_ordering(self.request)
83 83
84 84 _render = PartialRenderer('data_table/_dt_elements.mako')
85 85
86 86 def user_actions(user_id, username):
87 87 return _render("user_actions", user_id, username)
88 88
89 89 users_data_total_count = User.query()\
90 90 .filter(User.username != User.DEFAULT_USER) \
91 91 .count()
92 92
93 93 # json generate
94 94 base_q = User.query().filter(User.username != User.DEFAULT_USER)
95 95
96 96 if search_q:
97 97 like_expression = u'%{}%'.format(safe_unicode(search_q))
98 98 base_q = base_q.filter(or_(
99 99 User.username.ilike(like_expression),
100 100 User._email.ilike(like_expression),
101 101 User.name.ilike(like_expression),
102 102 User.lastname.ilike(like_expression),
103 103 ))
104 104
105 105 users_data_total_filtered_count = base_q.count()
106 106
107 107 sort_col = getattr(User, order_by, None)
108 108 if sort_col:
109 109 if order_dir == 'asc':
110 110 # handle null values properly to order by NULL last
111 111 if order_by in ['last_activity']:
112 112 sort_col = coalesce(sort_col, datetime.date.max)
113 113 sort_col = sort_col.asc()
114 114 else:
115 115 # handle null values properly to order by NULL last
116 116 if order_by in ['last_activity']:
117 117 sort_col = coalesce(sort_col, datetime.date.min)
118 118 sort_col = sort_col.desc()
119 119
120 120 base_q = base_q.order_by(sort_col)
121 121 base_q = base_q.offset(start).limit(limit)
122 122
123 123 users_list = base_q.all()
124 124
125 125 users_data = []
126 126 for user in users_list:
127 127 users_data.append({
128 128 "username": h.gravatar_with_user(user.username),
129 129 "email": user.email,
130 130 "first_name": h.escape(user.name),
131 131 "last_name": h.escape(user.lastname),
132 132 "last_login": h.format_date(user.last_login),
133 133 "last_activity": h.format_date(user.last_activity),
134 134 "active": h.bool2icon(user.active),
135 135 "active_raw": user.active,
136 136 "admin": h.bool2icon(user.admin),
137 137 "extern_type": user.extern_type,
138 138 "extern_name": user.extern_name,
139 139 "action": user_actions(user.user_id, user.username),
140 140 })
141 141
142 142 data = ({
143 143 'draw': draw,
144 144 'data': users_data,
145 145 'recordsTotal': users_data_total_count,
146 146 'recordsFiltered': users_data_total_filtered_count,
147 147 })
148 148
149 149 return data
150 150
151 151 @LoginRequired()
152 152 @HasPermissionAllDecorator('hg.admin')
153 153 @view_config(
154 154 route_name='edit_user_auth_tokens', request_method='GET',
155 155 renderer='rhodecode:templates/admin/users/user_edit.mako')
156 156 def auth_tokens(self):
157 157 _ = self.request.translate
158 158 c = self.load_default_context()
159 159
160 160 user_id = self.request.matchdict.get('user_id')
161 161 c.user = User.get_or_404(user_id, pyramid_exc=True)
162 162 self._redirect_for_default_user(c.user.username)
163 163
164 164 c.active = 'auth_tokens'
165 165
166 166 c.lifetime_values = [
167 167 (str(-1), _('forever')),
168 168 (str(5), _('5 minutes')),
169 169 (str(60), _('1 hour')),
170 170 (str(60 * 24), _('1 day')),
171 171 (str(60 * 24 * 30), _('1 month')),
172 172 ]
173 173 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
174 174 c.role_values = [
175 175 (x, AuthTokenModel.cls._get_role_name(x))
176 176 for x in AuthTokenModel.cls.ROLES]
177 177 c.role_options = [(c.role_values, _("Role"))]
178 178 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
179 179 c.user.user_id, show_expired=True)
180 180 return self._get_template_context(c)
181 181
182 182 def maybe_attach_token_scope(self, token):
183 183 # implemented in EE edition
184 184 pass
185 185
186 186 @LoginRequired()
187 187 @HasPermissionAllDecorator('hg.admin')
188 188 @CSRFRequired()
189 189 @view_config(
190 190 route_name='edit_user_auth_tokens_add', request_method='POST')
191 191 def auth_tokens_add(self):
192 192 _ = self.request.translate
193 193 c = self.load_default_context()
194 194
195 195 user_id = self.request.matchdict.get('user_id')
196 196 c.user = User.get_or_404(user_id, pyramid_exc=True)
197 197 self._redirect_for_default_user(c.user.username)
198 198
199 199 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
200 200 description = self.request.POST.get('description')
201 201 role = self.request.POST.get('role')
202 202
203 203 token = AuthTokenModel().create(
204 204 c.user.user_id, description, lifetime, role)
205 205 self.maybe_attach_token_scope(token)
206 206 Session().commit()
207 207
208 208 h.flash(_("Auth token successfully created"), category='success')
209 209 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
210 210
211 211 @LoginRequired()
212 212 @HasPermissionAllDecorator('hg.admin')
213 213 @CSRFRequired()
214 214 @view_config(
215 215 route_name='edit_user_auth_tokens_delete', request_method='POST')
216 216 def auth_tokens_delete(self):
217 217 _ = self.request.translate
218 218 c = self.load_default_context()
219 219
220 220 user_id = self.request.matchdict.get('user_id')
221 221 c.user = User.get_or_404(user_id, pyramid_exc=True)
222 222 self._redirect_for_default_user(c.user.username)
223 223
224 224 del_auth_token = self.request.POST.get('del_auth_token')
225 225
226 226 if del_auth_token:
227 227 AuthTokenModel().delete(del_auth_token, c.user.user_id)
228 228 Session().commit()
229 229 h.flash(_("Auth token successfully deleted"), category='success')
230 230
231 231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
232 232
233 233 @LoginRequired()
234 234 @HasPermissionAllDecorator('hg.admin')
235 235 @view_config(
236 236 route_name='edit_user_groups_management', request_method='GET',
237 237 renderer='rhodecode:templates/admin/users/user_edit.mako')
238 238 def groups_management(self):
239 239 c = self.load_default_context()
240 240
241 241 user_id = self.request.matchdict.get('user_id')
242 242 c.user = User.get_or_404(user_id, pyramid_exc=True)
243 243 c.data = c.user.group_member
244 244 self._redirect_for_default_user(c.user.username)
245 245 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
246 246 for group in c.user.group_member]
247 247 c.groups = json.dumps(groups)
248 248 c.active = 'groups'
249 249
250 250 return self._get_template_context(c)
251 251
252 252 @LoginRequired()
253 253 @HasPermissionAllDecorator('hg.admin')
254 @CSRFRequired()
254 255 @view_config(
255 256 route_name='edit_user_groups_management_updates', request_method='POST')
256 257 def groups_management_updates(self):
257 258 _ = self.request.translate
258 259 c = self.load_default_context()
259 260
260 261 user_id = self.request.matchdict.get('user_id')
261 262 c.user = User.get_or_404(user_id, pyramid_exc=True)
262 263 self._redirect_for_default_user(c.user.username)
263 264
264 265 users_groups = set(self.request.POST.getall('users_group_id'))
265 266 users_groups_model = []
266 267
267 268 for ugid in users_groups:
268 269 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
269 270 user_group_model = UserGroupModel()
270 271 user_group_model.change_groups(c.user, users_groups_model)
271 272
272 273 Session().commit()
273 274 c.active = 'user_groups_management'
274 275 h.flash(_("Groups successfully changed"), category='success')
275 276
276 277 return HTTPFound(h.route_path(
277 278 'edit_user_groups_management', user_id=user_id))
278 279
279 280 @LoginRequired()
280 281 @HasPermissionAllDecorator('hg.admin')
281 282 @view_config(
282 283 route_name='edit_user_audit_logs', request_method='GET',
283 284 renderer='rhodecode:templates/admin/users/user_edit.mako')
284 285 def user_audit_logs(self):
285 286 _ = self.request.translate
286 287 c = self.load_default_context()
287 288
288 289 user_id = self.request.matchdict.get('user_id')
289 290 c.user = User.get_or_404(user_id, pyramid_exc=True)
290 291 self._redirect_for_default_user(c.user.username)
291 292 c.active = 'audit'
292 293
293 294 p = safe_int(self.request.GET.get('page', 1), 1)
294 295
295 296 filter_term = self.request.GET.get('filter')
296 297 user_log = UserModel().get_user_log(c.user, filter_term)
297 298
298 299 def url_generator(**kw):
299 300 if filter_term:
300 301 kw['filter'] = filter_term
301 302 return self.request.current_route_path(_query=kw)
302 303
303 304 c.audit_logs = Page(user_log, page=p, items_per_page=10,
304 305 url=url_generator)
305 306 c.filter_term = filter_term
306 307 return self._get_template_context(c)
307 308
@@ -1,76 +1,78 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from pyramid.httpexceptions import HTTPFound
24 24 from pyramid.view import view_config
25 25
26 26 from rhodecode.apps._base import RepoAppView
27 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
28 CSRFRequired
28 29 from rhodecode.lib import helpers as h
29 30 from rhodecode.model.meta import Session
30 31 from rhodecode.model.scm import ScmModel
31 32
32 33 log = logging.getLogger(__name__)
33 34
34 35
35 36 class RepoCachesView(RepoAppView):
36 37 def load_default_context(self):
37 38 c = self._get_local_tmpl_context()
38 39
39 40 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
40 41 c.repo_info = self.db_repo
41 42
42 43 self._register_global_c(c)
43 44 return c
44 45
45 46 @LoginRequired()
46 47 @HasRepoPermissionAnyDecorator('repository.admin')
47 48 @view_config(
48 49 route_name='edit_repo_caches', request_method='GET',
49 50 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
50 51 def repo_caches(self):
51 52 c = self.load_default_context()
52 53 c.active = 'caches'
53 54
54 55 return self._get_template_context(c)
55 56
56 57 @LoginRequired()
57 58 @HasRepoPermissionAnyDecorator('repository.admin')
59 @CSRFRequired()
58 60 @view_config(
59 61 route_name='edit_repo_caches', request_method='POST')
60 62 def repo_caches_purge(self):
61 63 _ = self.request.translate
62 64 c = self.load_default_context()
63 65 c.active = 'caches'
64 66
65 67 try:
66 68 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
67 69 Session().commit()
68 70 h.flash(_('Cache invalidation successful'),
69 71 category='success')
70 72 except Exception:
71 73 log.exception("Exception during cache invalidation")
72 74 h.flash(_('An error occurred during cache invalidation'),
73 75 category='error')
74 76
75 77 raise HTTPFound(h.route_path(
76 78 'edit_repo_caches', repo_name=self.db_repo_name)) No newline at end of file
@@ -1,226 +1,227 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from pyramid.view import view_config
24 24 from pyramid.httpexceptions import HTTPFound
25 25
26 26 from rhodecode.apps._base import RepoAppView
27 27 from rhodecode.lib import helpers as h
28 28 from rhodecode.lib import audit_logger
29 29 from rhodecode.lib.auth import (
30 30 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
31 31 from rhodecode.lib.exceptions import AttachedForksError
32 32 from rhodecode.lib.utils2 import safe_int
33 33 from rhodecode.lib.vcs import RepositoryError
34 34 from rhodecode.model.db import Session, UserFollowing, User, Repository
35 35 from rhodecode.model.repo import RepoModel
36 36 from rhodecode.model.scm import ScmModel
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class RepoSettingsView(RepoAppView):
42 42
43 43 def load_default_context(self):
44 44 c = self._get_local_tmpl_context()
45 45
46 46 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
47 47 c.repo_info = self.db_repo
48 48
49 49 self._register_global_c(c)
50 50 return c
51 51
52 52 @LoginRequired()
53 53 @HasRepoPermissionAnyDecorator('repository.admin')
54 54 @view_config(
55 55 route_name='edit_repo_advanced', request_method='GET',
56 56 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
57 57 def edit_advanced(self):
58 58 c = self.load_default_context()
59 59 c.active = 'advanced'
60 60
61 61 c.default_user_id = User.get_default_user().user_id
62 62 c.in_public_journal = UserFollowing.query() \
63 63 .filter(UserFollowing.user_id == c.default_user_id) \
64 64 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
65 65
66 66 c.has_origin_repo_read_perm = False
67 67 if self.db_repo.fork:
68 68 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
69 69 'repository.write', 'repository.read', 'repository.admin')(
70 70 self.db_repo.fork.repo_name, 'repo set as fork page')
71 71
72 72 return self._get_template_context(c)
73 73
74 74 @LoginRequired()
75 75 @HasRepoPermissionAnyDecorator('repository.admin')
76 @CSRFRequired()
76 77 @view_config(
77 78 route_name='edit_repo_advanced_delete', request_method='POST',
78 79 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
79 80 def edit_advanced_delete(self):
80 81 """
81 82 Deletes the repository, or shows warnings if deletion is not possible
82 83 because of attached forks or other errors.
83 84 """
84 85 _ = self.request.translate
85 86 handle_forks = self.request.POST.get('forks', None)
86 87
87 88 try:
88 89 _forks = self.db_repo.forks.count()
89 90 if _forks and handle_forks:
90 91 if handle_forks == 'detach_forks':
91 92 handle_forks = 'detach'
92 93 h.flash(_('Detached %s forks') % _forks, category='success')
93 94 elif handle_forks == 'delete_forks':
94 95 handle_forks = 'delete'
95 96 h.flash(_('Deleted %s forks') % _forks, category='success')
96 97
97 98 repo_data = self.db_repo.get_api_data()
98 99 RepoModel().delete(self.db_repo, forks=handle_forks)
99 100
100 101 repo = audit_logger.RepoWrap(repo_id=None,
101 102 repo_name=self.db_repo.repo_name)
102 103 audit_logger.store_web(
103 104 action='repo.delete',
104 105 action_data={'data': repo_data},
105 106 user=self._rhodecode_user, repo=repo)
106 107
107 108 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
108 109 h.flash(
109 110 _('Deleted repository `%s`') % self.db_repo_name,
110 111 category='success')
111 112 Session().commit()
112 113 except AttachedForksError:
113 114 repo_advanced_url = h.route_path(
114 115 'edit_repo_advanced', repo_name=self.db_repo_name,
115 116 _anchor='advanced-delete')
116 117 delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url)
117 118 h.flash(_('Cannot delete `{repo}` it still contains attached forks. '
118 119 'Try using {delete_or_detach} option.')
119 120 .format(repo=self.db_repo_name, delete_or_detach=delete_anchor),
120 121 category='warning')
121 122
122 123 # redirect to advanced for forks handle action ?
123 124 raise HTTPFound(repo_advanced_url)
124 125
125 126 except Exception:
126 127 log.exception("Exception during deletion of repository")
127 128 h.flash(_('An error occurred during deletion of `%s`')
128 129 % self.db_repo_name, category='error')
129 130 # redirect to advanced for more deletion options
130 131 raise HTTPFound(
131 132 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name),
132 133 _anchor='advanced-delete')
133 134
134 135 raise HTTPFound(h.route_path('home'))
135 136
136 137 @LoginRequired()
137 138 @HasRepoPermissionAnyDecorator('repository.admin')
138 139 @CSRFRequired()
139 140 @view_config(
140 141 route_name='edit_repo_advanced_journal', request_method='POST',
141 142 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
142 143 def edit_advanced_journal(self):
143 144 """
144 145 Set's this repository to be visible in public journal,
145 146 in other words making default user to follow this repo
146 147 """
147 148 _ = self.request.translate
148 149
149 150 try:
150 151 user_id = User.get_default_user().user_id
151 152 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
152 153 h.flash(_('Updated repository visibility in public journal'),
153 154 category='success')
154 155 Session().commit()
155 156 except Exception:
156 157 h.flash(_('An error occurred during setting this '
157 158 'repository in public journal'),
158 159 category='error')
159 160
160 161 raise HTTPFound(
161 162 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
162 163
163 164 @LoginRequired()
164 165 @HasRepoPermissionAnyDecorator('repository.admin')
165 166 @CSRFRequired()
166 167 @view_config(
167 168 route_name='edit_repo_advanced_fork', request_method='POST',
168 169 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
169 170 def edit_advanced_fork(self):
170 171 """
171 172 Mark given repository as a fork of another
172 173 """
173 174 _ = self.request.translate
174 175
175 176 new_fork_id = self.request.POST.get('id_fork_of')
176 177 try:
177 178
178 179 if new_fork_id and not new_fork_id.isdigit():
179 180 log.error('Given fork id %s is not an INT', new_fork_id)
180 181
181 182 fork_id = safe_int(new_fork_id)
182 183 repo = ScmModel().mark_as_fork(
183 184 self.db_repo_name, fork_id, self._rhodecode_user.user_id)
184 185 fork = repo.fork.repo_name if repo.fork else _('Nothing')
185 186 Session().commit()
186 187 h.flash(_('Marked repo %s as fork of %s') % (self.db_repo_name, fork),
187 188 category='success')
188 189 except RepositoryError as e:
189 190 log.exception("Repository Error occurred")
190 191 h.flash(str(e), category='error')
191 192 except Exception as e:
192 193 log.exception("Exception while editing fork")
193 194 h.flash(_('An error occurred during this operation'),
194 195 category='error')
195 196
196 197 raise HTTPFound(
197 198 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
198 199
199 200 @LoginRequired()
200 201 @HasRepoPermissionAnyDecorator('repository.admin')
201 202 @CSRFRequired()
202 203 @view_config(
203 204 route_name='edit_repo_advanced_locking', request_method='POST',
204 205 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
205 206 def edit_advanced_locking(self):
206 207 """
207 208 Toggle locking of repository
208 209 """
209 210 _ = self.request.translate
210 211 set_lock = self.request.POST.get('set_lock')
211 212 set_unlock = self.request.POST.get('set_unlock')
212 213
213 214 try:
214 215 if set_lock:
215 216 Repository.lock(self.db_repo, self._rhodecode_user.user_id,
216 217 lock_reason=Repository.LOCK_WEB)
217 218 h.flash(_('Locked repository'), category='success')
218 219 elif set_unlock:
219 220 Repository.unlock(self.db_repo)
220 221 h.flash(_('Unlocked repository'), category='success')
221 222 except Exception as e:
222 223 log.exception("Exception during unlocking")
223 224 h.flash(_('An error occurred during unlocking'), category='error')
224 225
225 226 raise HTTPFound(
226 227 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
@@ -1,116 +1,118 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 from pyramid.view import view_config
23 23
24 24 from rhodecode.apps._base import RepoAppView
25 25 from rhodecode.lib import audit_logger
26 26 from rhodecode.lib import helpers as h
27 27 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
28 NotAnonymous)
28 NotAnonymous, CSRFRequired)
29 29 from rhodecode.lib.ext_json import json
30 30
31 31 log = logging.getLogger(__name__)
32 32
33 33
34 34 class StripView(RepoAppView):
35 35 def load_default_context(self):
36 36 c = self._get_local_tmpl_context()
37 37
38 38 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
39 39 c.repo_info = self.db_repo
40 40
41 41 self._register_global_c(c)
42 42 return c
43 43
44 44 @LoginRequired()
45 45 @HasRepoPermissionAnyDecorator('repository.admin')
46 46 @view_config(
47 47 route_name='strip', request_method='GET',
48 48 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
49 49 def strip(self):
50 50 c = self.load_default_context()
51 51 c.active = 'strip'
52 52 c.strip_limit = 10
53 53
54 54 return self._get_template_context(c)
55 55
56 56 @LoginRequired()
57 57 @HasRepoPermissionAnyDecorator('repository.admin')
58 @CSRFRequired()
58 59 @view_config(
59 60 route_name='strip_check', request_method='POST',
60 61 renderer='json', xhr=True)
61 62 def strip_check(self):
62 63 from rhodecode.lib.vcs.backends.base import EmptyCommit
63 64 data = {}
64 65 rp = self.request.POST
65 66 for i in range(1, 11):
66 67 chset = 'changeset_id-%d' % (i,)
67 68 check = rp.get(chset)
68 69
69 70 if check:
70 71 data[i] = self.db_repo.get_changeset(rp[chset])
71 72 if isinstance(data[i], EmptyCommit):
72 73 data[i] = {'rev': None, 'commit': h.escape(rp[chset])}
73 74 else:
74 75 data[i] = {'rev': data[i].raw_id, 'branch': data[i].branch,
75 76 'author': data[i].author,
76 77 'comment': data[i].message}
77 78 else:
78 79 break
79 80 return data
80 81
81 82 @LoginRequired()
82 83 @HasRepoPermissionAnyDecorator('repository.admin')
84 @CSRFRequired()
83 85 @view_config(
84 86 route_name='strip_execute', request_method='POST',
85 87 renderer='json', xhr=True)
86 88 def strip_execute(self):
87 89 from rhodecode.model.scm import ScmModel
88 90
89 91 c = self.load_default_context()
90 92 user = self._rhodecode_user
91 93 rp = self.request.POST
92 94 data = {}
93 95 for idx in rp:
94 96 commit = json.loads(rp[idx])
95 97 # If someone put two times the same branch
96 98 if commit['branch'] in data.keys():
97 99 continue
98 100 try:
99 101 ScmModel().strip(
100 102 repo=c.repo_info,
101 103 commit_id=commit['rev'], branch=commit['branch'])
102 104 log.info('Stripped commit %s from repo `%s` by %s' % (
103 105 commit['rev'], c.repo_info.repo_name, user))
104 106 data[commit['rev']] = True
105 107
106 108 audit_logger.store_web(
107 109 action='repo.commit.strip',
108 110 action_data={'commit_id': commit['rev']},
109 111 repo=self.db_repo,
110 112 user=self._rhodecode_user, commit=True)
111 113
112 114 except Exception as e:
113 115 data[commit['rev']] = False
114 116 log.debug('Stripped commit %s from repo `%s` failed by %s, exeption %s' % (
115 117 commit['rev'], self.db_repo_name, user, e.message))
116 118 return data
@@ -1,193 +1,197 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Strip commits from repository')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 %if c.repo_info.repo_type != 'svn':
7 7 <h4>${_('Please provide up to %d commits commits to strip') % c.strip_limit}</h4>
8 8 <p>
9 9 ${_('In the first step commits will be verified for existance in the repository')}. </br>
10 10 ${_('In the second step, correct commits will be available for stripping')}.
11 11 </p>
12 12 ${h.secure_form(h.route_path('strip_check', repo_name=c.repo_info.repo_name), method='post')}
13 13 <div id="change_body" class="field">
14 14 <div id="box-1" class="inputx locked_input">
15 15 <input class="text" id="changeset_id-1" name="changeset_id-1" size="59"
16 16 placeholder="${_('Enter full 40 character commit sha')}" type="text" value="">
17 17 <div id="plus_icon-1" class="btn btn-default plus_input_button" onclick="addNew(1);return false">
18 18 <i class="icon-plus">${_('Add another commit')}</i>
19 19 </div>
20 20 </div>
21 21 </div>
22 22
23 23 <div id="results" style="display:none; padding: 10px 0px;"></div>
24 24
25 25 <div class="buttons">
26 26 <button id="strip_action" class="btn btn-small btn-primary" onclick="checkCommits();return false">
27 27 ${_('Check commits')}
28 28 </button>
29 29 </div>
30 30
31 31 ${h.end_form()}
32 32 %else:
33 33 <h4>${_('Sorry this functionality is not available for SVN repository')}</h4>
34 34 %endif
35 35 </div>
36 36 </div>
37 37
38 38
39 39 <script>
40 40 var plus_leaf = 1;
41 41
42 42 addNew = function(number){
43 43 if (number >= ${c.strip_limit}){
44 44 return;
45 45 }
46 46 var minus = '<i class="icon-minus">${_('Remove')}</i>';
47 47 $('#plus_icon-'+number).detach();
48 48 number++;
49 49
50 50 var input = '<div id="box-'+number+'" class="inputx locked_input">'+
51 51 '<input class="text" id="changeset_id-'+number+'" name="changeset_id-'+number+'" size="59" type="text" value=""' +
52 52 'placeholder="${_('Enter full 40 character commit sha')}">'+
53 53 '<div id="plus_icon-'+number+'" class="btn btn-default plus_input_button" onclick="addNew('+number+');return false">'+
54 54 '<i class="icon-plus">${_('Add another commit')}</i>'+
55 55 '</div>'+
56 56 '<div id="minus_icon-'+number+'" class="btn btn-default minus_input_button" onclick="delOld('+(number)+');return false">'+
57 57 minus +
58 58 '</div>' +
59 59 '</div>';
60 60 $('#change_body').append(input);
61 61 plus_leaf++;
62 62 };
63 63
64 64 reIndex = function(number){
65 65 for(var i=number;i<=plus_leaf;i++){
66 66 var check = $('#box-'+i);
67 67 if (check.length == 0){
68 68 var change = $('#box-'+(i+1));
69 69 change.attr('id','box-'+i);
70 70 var plus = $('#plus_icon-'+(i+1));
71 71
72 72 if (plus.length != 0){
73 73 plus.attr('id','plus_icon-'+i);
74 74 plus.attr('onclick','addNew('+i+');return false');
75 75 plus_leaf--;
76 76 }
77 77 var minus = $('#minus_icon-'+(i+1));
78 78
79 79 minus.attr('id','minus_icon-'+i);
80 80
81 81 minus.attr('onclick','delOld('+i+');re' +
82 82 'turn false');
83 83 var input = $('input#changeset_id-'+(i+1));
84 84 input.attr('name','changeset_id-'+i);
85 85 input.attr('id','changeset_id-'+i);
86 86 }
87 87 }
88 88 };
89 89
90 90 delOld = function(number){
91 91 $('#box-'+number).remove();
92 92 number = number - 1;
93 93 var box = $('#box-'+number);
94 94 var plus = '<div id="plus_icon-'+number+'" class="btn btn-default plus_input_button" onclick="addNew('+number +');return false">'+
95 95 '<i id="i_plus_icon-'+number+'" class="icon-plus">${_('Add another commit')}</i></div>';
96 96 var minus = $('#minus_icon-'+number);
97 97 if(number +1 == plus_leaf){
98 98 minus.detach();
99 99 box.append(plus);
100 100 box.append(minus);
101 101 plus_leaf --;
102 102 }
103 103 reIndex(number+1);
104 104
105 105 };
106 106
107 var result_data;
107 var resultData = {
108 'csrf_token': CSRF_TOKEN
109 };
108 110
109 111 checkCommits = function() {
110 112 var postData = $('form').serialize();
111 113 $('#results').show();
112 114 $('#results').html('<h4>${_('Checking commits')}...</h4>');
113 115 var url = "${h.route_path('strip_check', repo_name=c.repo_info.repo_name)}";
114 116 var btn = $('#strip_action');
115 117 btn.attr('disabled', 'disabled');
116 118 btn.addClass('disabled');
117 119
118 120 var success = function (data) {
119 result_data = {};
121 resultData = {
122 'csrf_token': CSRF_TOKEN
123 };
120 124 var i = 0;
121 125 var result = '<ol>';
122 126 $.each(data, function(index, value){
123 127 i= index;
124 128 var box = $('#box-'+index);
125 129 if (value.rev){
126 result_data[index] = JSON.stringify(value);
130 resultData[index] = JSON.stringify(value);
127 131
128 132 var verifiedHtml = (
129 133 '<li style="line-height:1.2em">' +
130 134 '<code>{0}</code>' +
131 135 '{1}' +
132 136 '<div style="white-space:pre">' +
133 137 'author: {2}\n' +
134 138 'description: {3}' +
135 139 '</div>' +
136 140 '</li>').format(
137 141 value.rev,
138 142 "${_(' commit verified positive')}",
139 143 value.author, value.comment
140 144 );
141 145 result += verifiedHtml;
142 146 }
143 147 else {
144 148 var verifiedHtml = (
145 149 '<li style="line-height:1.2em">' +
146 150 '<code><strike>{0}</strike></code>' +
147 151 '{1}' +
148 152 '</li>').format(
149 153 value.commit,
150 154 "${_(' commit verified negative')}"
151 155 );
152 156 result += verifiedHtml;
153 157 }
154 158 box.remove();
155 159 });
156 160 result += '</ol>';
157 161 var box = $('#box-'+(parseInt(i)+1));
158 162 box.remove();
159 163 $('#results').html(result);
160 164 };
161 165
162 166 btn.html('Strip');
163 167 btn.removeAttr('disabled');
164 168 btn.removeClass('disabled');
165 169 btn.attr('onclick','strip();return false;');
166 170 ajaxPOST(url, postData, success, null);
167 171 };
168 172
169 173 strip = function() {
170 174 var url = "${h.route_path('strip_execute', repo_name=c.repo_info.repo_name)}";
171 175 var success = function(data) {
172 176 var result = '<h4>Strip executed</h4><ol>';
173 177 $.each(data, function(index, value){
174 178 if(data[index]) {
175 179 result += '<li><code>' +index+ '</code> ${_(' commit striped successfully')}' + '</li>';
176 180 }
177 181 else {
178 182 result += '<li><code>' +index+ '</code> ${_(' commit strip failed')}' + '</li>';
179 183 }
180 184 });
181 185 if ($.isEmptyObject(data)) {
182 186 result += '<li>Nothing done...</li>'
183 187 }
184 188 result += '</ol>';
185 189 $('#results').html(result);
186 190
187 191 };
188 ajaxPOST(url, result_data, success, null);
192 ajaxPOST(url, resultData, success, null);
189 193 var btn = $('#strip_action');
190 194 btn.remove();
191 195
192 196 };
193 197 </script>
General Comments 0
You need to be logged in to leave comments. Login now