##// END OF EJS Templates
tests: remove usage of pylons context var in home app test.
marcink -
r1893:eb462cc2 default
parent child Browse files
Show More
@@ -1,140 +1,141 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-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
22 22 import pytest
23 23
24 24 import rhodecode
25 25 from rhodecode.model.db import Repository
26 26 from rhodecode.model.meta import Session
27 27 from rhodecode.model.repo import RepoModel
28 28 from rhodecode.model.repo_group import RepoGroupModel
29 29 from rhodecode.model.settings import SettingsModel
30 30 from rhodecode.tests import TestController
31 31 from rhodecode.tests.fixture import Fixture
32 32 from rhodecode.lib import helpers as h
33 33
34 34 fixture = Fixture()
35 35
36 36
37 37 def route_path(name, **kwargs):
38 38 return {
39 39 'home': '/',
40 40 'repo_group_home': '/{repo_group_name}'
41 41 }[name].format(**kwargs)
42 42
43 43
44 44 class TestHomeController(TestController):
45 45
46 46 def test_index(self):
47 47 self.log_user()
48 48 response = self.app.get(route_path('home'))
49 49 # if global permission is set
50 50 response.mustcontain('Add Repository')
51 51
52 52 # search for objects inside the JavaScript JSON
53 53 for repo in Repository.getAll():
54 54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
55 55
56 56 def test_index_contains_statics_with_ver(self):
57 from pylons import tmpl_context as c
57 from rhodecode.lib.base import calculate_version_hash
58 58
59 59 self.log_user()
60 60 response = self.app.get(route_path('home'))
61 61
62 rhodecode_version_hash = c.rhodecode_version_hash
62 rhodecode_version_hash = calculate_version_hash()
63 63 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
64 response.mustcontain('rhodecode-components.js?ver={0}'.format(rhodecode_version_hash))
64 response.mustcontain('rhodecode-components.js?ver={0}'.format(
65 rhodecode_version_hash))
65 66
66 67 def test_index_contains_backend_specific_details(self, backend):
67 68 self.log_user()
68 69 response = self.app.get(route_path('home'))
69 70 tip = backend.repo.get_commit().raw_id
70 71
71 72 # html in javascript variable:
72 73 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
73 74 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
74 75
75 76 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
76 77 response.mustcontain("""Added a symlink""")
77 78
78 79 def test_index_with_anonymous_access_disabled(self):
79 80 with fixture.anon_access(False):
80 81 response = self.app.get(route_path('home'), status=302)
81 82 assert 'login' in response.location
82 83
83 84 def test_index_page_on_groups(self, autologin_user, repo_group):
84 85 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1'))
85 86 response.mustcontain("gr1/repo_in_group")
86 87
87 88 def test_index_page_on_group_with_trailing_slash(
88 89 self, autologin_user, repo_group):
89 90 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1') + '/')
90 91 response.mustcontain("gr1/repo_in_group")
91 92
92 93 @pytest.fixture(scope='class')
93 94 def repo_group(self, request):
94 95 gr = fixture.create_repo_group('gr1')
95 96 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
96 97
97 98 @request.addfinalizer
98 99 def cleanup():
99 100 RepoModel().delete('gr1/repo_in_group')
100 101 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
101 102 Session().commit()
102 103
103 104 def test_index_with_name_with_tags(self, user_util, autologin_user):
104 105 user = user_util.create_user()
105 106 username = user.username
106 107 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
107 108 user.lastname = '#"><img src=x onerror=prompt(document.cookie);>'
108 109
109 110 Session().add(user)
110 111 Session().commit()
111 112 user_util.create_repo(owner=username)
112 113
113 114 response = self.app.get(route_path('home'))
114 115 response.mustcontain(h.html_escape(user.first_name))
115 116 response.mustcontain(h.html_escape(user.last_name))
116 117
117 118 @pytest.mark.parametrize("name, state", [
118 119 ('Disabled', False),
119 120 ('Enabled', True),
120 121 ])
121 122 def test_index_show_version(self, autologin_user, name, state):
122 123 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
123 124
124 125 sett = SettingsModel().create_or_update_setting(
125 126 'show_version', state, 'bool')
126 127 Session().add(sett)
127 128 Session().commit()
128 129 SettingsModel().invalidate_settings_cache()
129 130
130 131 response = self.app.get(route_path('home'))
131 132 if state is True:
132 133 response.mustcontain(version_string)
133 134 if state is False:
134 135 response.mustcontain(no=[version_string])
135 136
136 137 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
137 138 response = self.app.get(route_path('home'))
138 139 assert_response = response.assert_response()
139 140 element = assert_response.get_element('.logout #csrf_token')
140 141 assert element.value == csrf_token
@@ -1,601 +1,605 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-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 """
22 22 The base Controller API
23 23 Provides the BaseController class for subclassing. And usage in different
24 24 controllers
25 25 """
26 26
27 27 import logging
28 28 import socket
29 29
30 30 import ipaddress
31 31 import pyramid.threadlocal
32 32
33 33 from paste.auth.basic import AuthBasicAuthenticator
34 34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36 36 from pylons import config, tmpl_context as c, request, session, url
37 37 from pylons.controllers import WSGIController
38 38 from pylons.controllers.util import redirect
39 39 from pylons.i18n import translation
40 40 # marcink: don't remove this import
41 41 from pylons.templating import render_mako as render # noqa
42 42 from pylons.i18n.translation import _
43 43 from webob.exc import HTTPFound
44 44
45 45
46 46 import rhodecode
47 47 from rhodecode.authentication.base import VCS_TYPE
48 48 from rhodecode.lib import auth, utils2
49 49 from rhodecode.lib import helpers as h
50 50 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
51 51 from rhodecode.lib.exceptions import UserCreationError
52 52 from rhodecode.lib.utils import (
53 53 get_repo_slug, set_rhodecode_config, password_changed,
54 54 get_enabled_hook_classes)
55 55 from rhodecode.lib.utils2 import (
56 56 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
57 57 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
58 58 from rhodecode.model import meta
59 59 from rhodecode.model.db import Repository, User, ChangesetComment
60 60 from rhodecode.model.notification import NotificationModel
61 61 from rhodecode.model.scm import ScmModel
62 62 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
63 63
64 64
65 65 log = logging.getLogger(__name__)
66 66
67 67
68 68 def _filter_proxy(ip):
69 69 """
70 70 Passed in IP addresses in HEADERS can be in a special format of multiple
71 71 ips. Those comma separated IPs are passed from various proxies in the
72 72 chain of request processing. The left-most being the original client.
73 73 We only care about the first IP which came from the org. client.
74 74
75 75 :param ip: ip string from headers
76 76 """
77 77 if ',' in ip:
78 78 _ips = ip.split(',')
79 79 _first_ip = _ips[0].strip()
80 80 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
81 81 return _first_ip
82 82 return ip
83 83
84 84
85 85 def _filter_port(ip):
86 86 """
87 87 Removes a port from ip, there are 4 main cases to handle here.
88 88 - ipv4 eg. 127.0.0.1
89 89 - ipv6 eg. ::1
90 90 - ipv4+port eg. 127.0.0.1:8080
91 91 - ipv6+port eg. [::1]:8080
92 92
93 93 :param ip:
94 94 """
95 95 def is_ipv6(ip_addr):
96 96 if hasattr(socket, 'inet_pton'):
97 97 try:
98 98 socket.inet_pton(socket.AF_INET6, ip_addr)
99 99 except socket.error:
100 100 return False
101 101 else:
102 102 # fallback to ipaddress
103 103 try:
104 104 ipaddress.IPv6Address(ip_addr)
105 105 except Exception:
106 106 return False
107 107 return True
108 108
109 109 if ':' not in ip: # must be ipv4 pure ip
110 110 return ip
111 111
112 112 if '[' in ip and ']' in ip: # ipv6 with port
113 113 return ip.split(']')[0][1:].lower()
114 114
115 115 # must be ipv6 or ipv4 with port
116 116 if is_ipv6(ip):
117 117 return ip
118 118 else:
119 119 ip, _port = ip.split(':')[:2] # means ipv4+port
120 120 return ip
121 121
122 122
123 123 def get_ip_addr(environ):
124 124 proxy_key = 'HTTP_X_REAL_IP'
125 125 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
126 126 def_key = 'REMOTE_ADDR'
127 127 _filters = lambda x: _filter_port(_filter_proxy(x))
128 128
129 129 ip = environ.get(proxy_key)
130 130 if ip:
131 131 return _filters(ip)
132 132
133 133 ip = environ.get(proxy_key2)
134 134 if ip:
135 135 return _filters(ip)
136 136
137 137 ip = environ.get(def_key, '0.0.0.0')
138 138 return _filters(ip)
139 139
140 140
141 141 def get_server_ip_addr(environ, log_errors=True):
142 142 hostname = environ.get('SERVER_NAME')
143 143 try:
144 144 return socket.gethostbyname(hostname)
145 145 except Exception as e:
146 146 if log_errors:
147 147 # in some cases this lookup is not possible, and we don't want to
148 148 # make it an exception in logs
149 149 log.exception('Could not retrieve server ip address: %s', e)
150 150 return hostname
151 151
152 152
153 153 def get_server_port(environ):
154 154 return environ.get('SERVER_PORT')
155 155
156 156
157 157 def get_access_path(environ):
158 158 path = environ.get('PATH_INFO')
159 159 org_req = environ.get('pylons.original_request')
160 160 if org_req:
161 161 path = org_req.environ.get('PATH_INFO')
162 162 return path
163 163
164 164
165 165 def get_user_agent(environ):
166 166 return environ.get('HTTP_USER_AGENT')
167 167
168 168
169 169 def vcs_operation_context(
170 170 environ, repo_name, username, action, scm, check_locking=True,
171 171 is_shadow_repo=False):
172 172 """
173 173 Generate the context for a vcs operation, e.g. push or pull.
174 174
175 175 This context is passed over the layers so that hooks triggered by the
176 176 vcs operation know details like the user, the user's IP address etc.
177 177
178 178 :param check_locking: Allows to switch of the computation of the locking
179 179 data. This serves mainly the need of the simplevcs middleware to be
180 180 able to disable this for certain operations.
181 181
182 182 """
183 183 # Tri-state value: False: unlock, None: nothing, True: lock
184 184 make_lock = None
185 185 locked_by = [None, None, None]
186 186 is_anonymous = username == User.DEFAULT_USER
187 187 if not is_anonymous and check_locking:
188 188 log.debug('Checking locking on repository "%s"', repo_name)
189 189 user = User.get_by_username(username)
190 190 repo = Repository.get_by_repo_name(repo_name)
191 191 make_lock, __, locked_by = repo.get_locking_state(
192 192 action, user.user_id)
193 193
194 194 settings_model = VcsSettingsModel(repo=repo_name)
195 195 ui_settings = settings_model.get_ui_settings()
196 196
197 197 extras = {
198 198 'ip': get_ip_addr(environ),
199 199 'username': username,
200 200 'action': action,
201 201 'repository': repo_name,
202 202 'scm': scm,
203 203 'config': rhodecode.CONFIG['__file__'],
204 204 'make_lock': make_lock,
205 205 'locked_by': locked_by,
206 206 'server_url': utils2.get_server_url(environ),
207 207 'user_agent': get_user_agent(environ),
208 208 'hooks': get_enabled_hook_classes(ui_settings),
209 209 'is_shadow_repo': is_shadow_repo,
210 210 }
211 211 return extras
212 212
213 213
214 214 class BasicAuth(AuthBasicAuthenticator):
215 215
216 216 def __init__(self, realm, authfunc, registry, auth_http_code=None,
217 217 initial_call_detection=False, acl_repo_name=None):
218 218 self.realm = realm
219 219 self.initial_call = initial_call_detection
220 220 self.authfunc = authfunc
221 221 self.registry = registry
222 222 self.acl_repo_name = acl_repo_name
223 223 self._rc_auth_http_code = auth_http_code
224 224
225 225 def _get_response_from_code(self, http_code):
226 226 try:
227 227 return get_exception(safe_int(http_code))
228 228 except Exception:
229 229 log.exception('Failed to fetch response for code %s' % http_code)
230 230 return HTTPForbidden
231 231
232 232 def build_authentication(self):
233 233 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 234 if self._rc_auth_http_code and not self.initial_call:
235 235 # return alternative HTTP code if alternative http return code
236 236 # is specified in RhodeCode config, but ONLY if it's not the
237 237 # FIRST call
238 238 custom_response_klass = self._get_response_from_code(
239 239 self._rc_auth_http_code)
240 240 return custom_response_klass(headers=head)
241 241 return HTTPUnauthorized(headers=head)
242 242
243 243 def authenticate(self, environ):
244 244 authorization = AUTHORIZATION(environ)
245 245 if not authorization:
246 246 return self.build_authentication()
247 247 (authmeth, auth) = authorization.split(' ', 1)
248 248 if 'basic' != authmeth.lower():
249 249 return self.build_authentication()
250 250 auth = auth.strip().decode('base64')
251 251 _parts = auth.split(':', 1)
252 252 if len(_parts) == 2:
253 253 username, password = _parts
254 254 if self.authfunc(
255 255 username, password, environ, VCS_TYPE,
256 256 registry=self.registry, acl_repo_name=self.acl_repo_name):
257 257 return username
258 258 if username and password:
259 259 # we mark that we actually executed authentication once, at
260 260 # that point we can use the alternative auth code
261 261 self.initial_call = False
262 262
263 263 return self.build_authentication()
264 264
265 265 __call__ = authenticate
266 266
267 267
268 268 def attach_context_attributes(context, request, user_id, attach_to_request=False):
269 def calculate_version_hash():
270 return md5(
271 config.get('beaker.session.secret', '') +
272 rhodecode.__version__)[:8]
273
274
269 275 """
270 276 Attach variables into template context called `c`, please note that
271 277 request could be pylons or pyramid request in here.
272 278 """
273 279 rc_config = SettingsModel().get_all_settings(cache=True)
274 280
275 281 context.rhodecode_version = rhodecode.__version__
276 282 context.rhodecode_edition = config.get('rhodecode.edition')
277 283 # unique secret + version does not leak the version but keep consistency
278 context.rhodecode_version_hash = md5(
279 config.get('beaker.session.secret', '') +
280 rhodecode.__version__)[:8]
284 context.rhodecode_version_hash = calculate_version_hash()
281 285
282 286 # Default language set for the incoming request
283 287 context.language = translation.get_lang()[0]
284 288
285 289 # Visual options
286 290 context.visual = AttributeDict({})
287 291
288 292 # DB stored Visual Items
289 293 context.visual.show_public_icon = str2bool(
290 294 rc_config.get('rhodecode_show_public_icon'))
291 295 context.visual.show_private_icon = str2bool(
292 296 rc_config.get('rhodecode_show_private_icon'))
293 297 context.visual.stylify_metatags = str2bool(
294 298 rc_config.get('rhodecode_stylify_metatags'))
295 299 context.visual.dashboard_items = safe_int(
296 300 rc_config.get('rhodecode_dashboard_items', 100))
297 301 context.visual.admin_grid_items = safe_int(
298 302 rc_config.get('rhodecode_admin_grid_items', 100))
299 303 context.visual.repository_fields = str2bool(
300 304 rc_config.get('rhodecode_repository_fields'))
301 305 context.visual.show_version = str2bool(
302 306 rc_config.get('rhodecode_show_version'))
303 307 context.visual.use_gravatar = str2bool(
304 308 rc_config.get('rhodecode_use_gravatar'))
305 309 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
306 310 context.visual.default_renderer = rc_config.get(
307 311 'rhodecode_markup_renderer', 'rst')
308 312 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
309 313 context.visual.rhodecode_support_url = \
310 314 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
311 315
312 316 context.pre_code = rc_config.get('rhodecode_pre_code')
313 317 context.post_code = rc_config.get('rhodecode_post_code')
314 318 context.rhodecode_name = rc_config.get('rhodecode_title')
315 319 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
316 320 # if we have specified default_encoding in the request, it has more
317 321 # priority
318 322 if request.GET.get('default_encoding'):
319 323 context.default_encodings.insert(0, request.GET.get('default_encoding'))
320 324 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
321 325
322 326 # INI stored
323 327 context.labs_active = str2bool(
324 328 config.get('labs_settings_active', 'false'))
325 329 context.visual.allow_repo_location_change = str2bool(
326 330 config.get('allow_repo_location_change', True))
327 331 context.visual.allow_custom_hooks_settings = str2bool(
328 332 config.get('allow_custom_hooks_settings', True))
329 333 context.debug_style = str2bool(config.get('debug_style', False))
330 334
331 335 context.rhodecode_instanceid = config.get('instance_id')
332 336
333 337 context.visual.cut_off_limit_diff = safe_int(
334 338 config.get('cut_off_limit_diff'))
335 339 context.visual.cut_off_limit_file = safe_int(
336 340 config.get('cut_off_limit_file'))
337 341
338 342 # AppEnlight
339 343 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
340 344 context.appenlight_api_public_key = config.get(
341 345 'appenlight.api_public_key', '')
342 346 context.appenlight_server_url = config.get('appenlight.server_url', '')
343 347
344 348 # JS template context
345 349 context.template_context = {
346 350 'repo_name': None,
347 351 'repo_type': None,
348 352 'repo_landing_commit': None,
349 353 'rhodecode_user': {
350 354 'username': None,
351 355 'email': None,
352 356 'notification_status': False
353 357 },
354 358 'visual': {
355 359 'default_renderer': None
356 360 },
357 361 'commit_data': {
358 362 'commit_id': None
359 363 },
360 364 'pull_request_data': {'pull_request_id': None},
361 365 'timeago': {
362 366 'refresh_time': 120 * 1000,
363 367 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
364 368 },
365 369 'pylons_dispatch': {
366 370 # 'controller': request.environ['pylons.routes_dict']['controller'],
367 371 # 'action': request.environ['pylons.routes_dict']['action'],
368 372 },
369 373 'pyramid_dispatch': {
370 374
371 375 },
372 376 'extra': {'plugins': {}}
373 377 }
374 378 # END CONFIG VARS
375 379
376 380 # TODO: This dosn't work when called from pylons compatibility tween.
377 381 # Fix this and remove it from base controller.
378 382 # context.repo_name = get_repo_slug(request) # can be empty
379 383
380 384 diffmode = 'sideside'
381 385 if request.GET.get('diffmode'):
382 386 if request.GET['diffmode'] == 'unified':
383 387 diffmode = 'unified'
384 388 elif request.session.get('diffmode'):
385 389 diffmode = request.session['diffmode']
386 390
387 391 context.diffmode = diffmode
388 392
389 393 if request.session.get('diffmode') != diffmode:
390 394 request.session['diffmode'] = diffmode
391 395
392 396 context.csrf_token = auth.get_csrf_token()
393 397 context.backends = rhodecode.BACKENDS.keys()
394 398 context.backends.sort()
395 399 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(user_id)
396 400 if attach_to_request:
397 401 request.call_context = context
398 402 else:
399 403 context.pyramid_request = pyramid.threadlocal.get_current_request()
400 404
401 405
402 406
403 407 def get_auth_user(environ):
404 408 ip_addr = get_ip_addr(environ)
405 409 # make sure that we update permissions each time we call controller
406 410 _auth_token = (request.GET.get('auth_token', '') or
407 411 request.GET.get('api_key', ''))
408 412
409 413 if _auth_token:
410 414 # when using API_KEY we assume user exists, and
411 415 # doesn't need auth based on cookies.
412 416 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
413 417 authenticated = False
414 418 else:
415 419 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
416 420 try:
417 421 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
418 422 ip_addr=ip_addr)
419 423 except UserCreationError as e:
420 424 h.flash(e, 'error')
421 425 # container auth or other auth functions that create users
422 426 # on the fly can throw this exception signaling that there's
423 427 # issue with user creation, explanation should be provided
424 428 # in Exception itself. We then create a simple blank
425 429 # AuthUser
426 430 auth_user = AuthUser(ip_addr=ip_addr)
427 431
428 432 if password_changed(auth_user, session):
429 433 session.invalidate()
430 434 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
431 435 auth_user = AuthUser(ip_addr=ip_addr)
432 436
433 437 authenticated = cookie_store.get('is_authenticated')
434 438
435 439 if not auth_user.is_authenticated and auth_user.is_user_object:
436 440 # user is not authenticated and not empty
437 441 auth_user.set_authenticated(authenticated)
438 442
439 443 return auth_user
440 444
441 445
442 446 class BaseController(WSGIController):
443 447
444 448 def __before__(self):
445 449 """
446 450 __before__ is called before controller methods and after __call__
447 451 """
448 452 # on each call propagate settings calls into global settings.
449 453 set_rhodecode_config(config)
450 454 attach_context_attributes(c, request, c.rhodecode_user.user_id)
451 455
452 456 # TODO: Remove this when fixed in attach_context_attributes()
453 457 c.repo_name = get_repo_slug(request) # can be empty
454 458
455 459 self.cut_off_limit_diff = safe_int(config.get('cut_off_limit_diff'))
456 460 self.cut_off_limit_file = safe_int(config.get('cut_off_limit_file'))
457 461 self.sa = meta.Session
458 462 self.scm_model = ScmModel(self.sa)
459 463
460 464 # set user language
461 465 user_lang = getattr(c.pyramid_request, '_LOCALE_', None)
462 466 if user_lang:
463 467 translation.set_lang(user_lang)
464 468 log.debug('set language to %s for user %s',
465 469 user_lang, self._rhodecode_user)
466 470
467 471 def _dispatch_redirect(self, with_url, environ, start_response):
468 472 resp = HTTPFound(with_url)
469 473 environ['SCRIPT_NAME'] = '' # handle prefix middleware
470 474 environ['PATH_INFO'] = with_url
471 475 return resp(environ, start_response)
472 476
473 477 def __call__(self, environ, start_response):
474 478 """Invoke the Controller"""
475 479 # WSGIController.__call__ dispatches to the Controller method
476 480 # the request is routed to. This routing information is
477 481 # available in environ['pylons.routes_dict']
478 482 from rhodecode.lib import helpers as h
479 483
480 484 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
481 485 if environ.get('debugtoolbar.wants_pylons_context', False):
482 486 environ['debugtoolbar.pylons_context'] = c._current_obj()
483 487
484 488 _route_name = '.'.join([environ['pylons.routes_dict']['controller'],
485 489 environ['pylons.routes_dict']['action']])
486 490
487 491 self.rc_config = SettingsModel().get_all_settings(cache=True)
488 492 self.ip_addr = get_ip_addr(environ)
489 493
490 494 # The rhodecode auth user is looked up and passed through the
491 495 # environ by the pylons compatibility tween in pyramid.
492 496 # So we can just grab it from there.
493 497 auth_user = environ['rc_auth_user']
494 498
495 499 # set globals for auth user
496 500 request.user = auth_user
497 501 c.rhodecode_user = self._rhodecode_user = auth_user
498 502
499 503 log.info('IP: %s User: %s accessed %s [%s]' % (
500 504 self.ip_addr, auth_user, safe_unicode(get_access_path(environ)),
501 505 _route_name)
502 506 )
503 507
504 508 user_obj = auth_user.get_instance()
505 509 if user_obj and user_obj.user_data.get('force_password_change'):
506 510 h.flash('You are required to change your password', 'warning',
507 511 ignore_duplicate=True)
508 512 return self._dispatch_redirect(
509 513 url('my_account_password'), environ, start_response)
510 514
511 515 return WSGIController.__call__(self, environ, start_response)
512 516
513 517
514 518 class BaseRepoController(BaseController):
515 519 """
516 520 Base class for controllers responsible for loading all needed data for
517 521 repository loaded items are
518 522
519 523 c.rhodecode_repo: instance of scm repository
520 524 c.rhodecode_db_repo: instance of db
521 525 c.repository_requirements_missing: shows that repository specific data
522 526 could not be displayed due to the missing requirements
523 527 c.repository_pull_requests: show number of open pull requests
524 528 """
525 529
526 530 def __before__(self):
527 531 super(BaseRepoController, self).__before__()
528 532 if c.repo_name: # extracted from routes
529 533 db_repo = Repository.get_by_repo_name(c.repo_name)
530 534 if not db_repo:
531 535 return
532 536
533 537 log.debug(
534 538 'Found repository in database %s with state `%s`',
535 539 safe_unicode(db_repo), safe_unicode(db_repo.repo_state))
536 540 route = getattr(request.environ.get('routes.route'), 'name', '')
537 541
538 542 # allow to delete repos that are somehow damages in filesystem
539 543 if route in ['delete_repo']:
540 544 return
541 545
542 546 if db_repo.repo_state in [Repository.STATE_PENDING]:
543 547 if route in ['repo_creating_home']:
544 548 return
545 549 check_url = url('repo_creating_home', repo_name=c.repo_name)
546 550 return redirect(check_url)
547 551
548 552 self.rhodecode_db_repo = db_repo
549 553
550 554 missing_requirements = False
551 555 try:
552 556 self.rhodecode_repo = self.rhodecode_db_repo.scm_instance()
553 557 except RepositoryRequirementError as e:
554 558 missing_requirements = True
555 559 self._handle_missing_requirements(e)
556 560
557 561 if self.rhodecode_repo is None and not missing_requirements:
558 562 log.error('%s this repository is present in database but it '
559 563 'cannot be created as an scm instance', c.repo_name)
560 564
561 565 h.flash(_(
562 566 "The repository at %(repo_name)s cannot be located.") %
563 567 {'repo_name': c.repo_name},
564 568 category='error', ignore_duplicate=True)
565 569 redirect(h.route_path('home'))
566 570
567 571 # update last change according to VCS data
568 572 if not missing_requirements:
569 573 commit = db_repo.get_commit(
570 574 pre_load=["author", "date", "message", "parents"])
571 575 db_repo.update_commit_cache(commit)
572 576
573 577 # Prepare context
574 578 c.rhodecode_db_repo = db_repo
575 579 c.rhodecode_repo = self.rhodecode_repo
576 580 c.repository_requirements_missing = missing_requirements
577 581
578 582 self._update_global_counters(self.scm_model, db_repo)
579 583
580 584 def _update_global_counters(self, scm_model, db_repo):
581 585 """
582 586 Base variables that are exposed to every page of repository
583 587 """
584 588 c.repository_pull_requests = scm_model.get_pull_requests(db_repo)
585 589
586 590 def _handle_missing_requirements(self, error):
587 591 self.rhodecode_repo = None
588 592 log.error(
589 593 'Requirements are missing for repository %s: %s',
590 594 c.repo_name, error.message)
591 595
592 596 summary_url = h.route_path('repo_summary', repo_name=c.repo_name)
593 597 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
594 598 settings_update_url = url('repo', repo_name=c.repo_name)
595 599 path = request.path
596 600 should_redirect = (
597 601 path not in (summary_url, settings_update_url)
598 602 and '/settings' not in path or path == statistics_url
599 603 )
600 604 if should_redirect:
601 605 redirect(summary_url)
General Comments 0
You need to be logged in to leave comments. Login now