##// END OF EJS Templates
error-reporting: fix setting call_context on dummy-request available in sentry
super-admin -
r4865:2ecd5d81 default
parent child Browse files
Show More
@@ -1,627 +1,635 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 The base Controller API
22 The base Controller API
23 Provides the BaseController class for subclassing. And usage in different
23 Provides the BaseController class for subclassing. And usage in different
24 controllers
24 controllers
25 """
25 """
26
26
27 import logging
27 import logging
28 import socket
28 import socket
29
29
30 import markupsafe
30 import markupsafe
31 import ipaddress
31 import ipaddress
32
32
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.apps._base import TemplateArgs
38 from rhodecode.apps._base import TemplateArgs
39 from rhodecode.authentication.base import VCS_TYPE
39 from rhodecode.authentication.base import VCS_TYPE
40 from rhodecode.lib import auth, utils2
40 from rhodecode.lib import auth, utils2
41 from rhodecode.lib import helpers as h
41 from rhodecode.lib import helpers as h
42 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
42 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
43 from rhodecode.lib.exceptions import UserCreationError
43 from rhodecode.lib.exceptions import UserCreationError
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
45 from rhodecode.lib.utils2 import (
45 from rhodecode.lib.utils2 import (
46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
48 from rhodecode.model.notification import NotificationModel
48 from rhodecode.model.notification import NotificationModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 def _filter_proxy(ip):
54 def _filter_proxy(ip):
55 """
55 """
56 Passed in IP addresses in HEADERS can be in a special format of multiple
56 Passed in IP addresses in HEADERS can be in a special format of multiple
57 ips. Those comma separated IPs are passed from various proxies in the
57 ips. Those comma separated IPs are passed from various proxies in the
58 chain of request processing. The left-most being the original client.
58 chain of request processing. The left-most being the original client.
59 We only care about the first IP which came from the org. client.
59 We only care about the first IP which came from the org. client.
60
60
61 :param ip: ip string from headers
61 :param ip: ip string from headers
62 """
62 """
63 if ',' in ip:
63 if ',' in ip:
64 _ips = ip.split(',')
64 _ips = ip.split(',')
65 _first_ip = _ips[0].strip()
65 _first_ip = _ips[0].strip()
66 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
66 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
67 return _first_ip
67 return _first_ip
68 return ip
68 return ip
69
69
70
70
71 def _filter_port(ip):
71 def _filter_port(ip):
72 """
72 """
73 Removes a port from ip, there are 4 main cases to handle here.
73 Removes a port from ip, there are 4 main cases to handle here.
74 - ipv4 eg. 127.0.0.1
74 - ipv4 eg. 127.0.0.1
75 - ipv6 eg. ::1
75 - ipv6 eg. ::1
76 - ipv4+port eg. 127.0.0.1:8080
76 - ipv4+port eg. 127.0.0.1:8080
77 - ipv6+port eg. [::1]:8080
77 - ipv6+port eg. [::1]:8080
78
78
79 :param ip:
79 :param ip:
80 """
80 """
81 def is_ipv6(ip_addr):
81 def is_ipv6(ip_addr):
82 if hasattr(socket, 'inet_pton'):
82 if hasattr(socket, 'inet_pton'):
83 try:
83 try:
84 socket.inet_pton(socket.AF_INET6, ip_addr)
84 socket.inet_pton(socket.AF_INET6, ip_addr)
85 except socket.error:
85 except socket.error:
86 return False
86 return False
87 else:
87 else:
88 # fallback to ipaddress
88 # fallback to ipaddress
89 try:
89 try:
90 ipaddress.IPv6Address(safe_unicode(ip_addr))
90 ipaddress.IPv6Address(safe_unicode(ip_addr))
91 except Exception:
91 except Exception:
92 return False
92 return False
93 return True
93 return True
94
94
95 if ':' not in ip: # must be ipv4 pure ip
95 if ':' not in ip: # must be ipv4 pure ip
96 return ip
96 return ip
97
97
98 if '[' in ip and ']' in ip: # ipv6 with port
98 if '[' in ip and ']' in ip: # ipv6 with port
99 return ip.split(']')[0][1:].lower()
99 return ip.split(']')[0][1:].lower()
100
100
101 # must be ipv6 or ipv4 with port
101 # must be ipv6 or ipv4 with port
102 if is_ipv6(ip):
102 if is_ipv6(ip):
103 return ip
103 return ip
104 else:
104 else:
105 ip, _port = ip.split(':')[:2] # means ipv4+port
105 ip, _port = ip.split(':')[:2] # means ipv4+port
106 return ip
106 return ip
107
107
108
108
109 def get_ip_addr(environ):
109 def get_ip_addr(environ):
110 proxy_key = 'HTTP_X_REAL_IP'
110 proxy_key = 'HTTP_X_REAL_IP'
111 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
111 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
112 def_key = 'REMOTE_ADDR'
112 def_key = 'REMOTE_ADDR'
113 _filters = lambda x: _filter_port(_filter_proxy(x))
113 _filters = lambda x: _filter_port(_filter_proxy(x))
114
114
115 ip = environ.get(proxy_key)
115 ip = environ.get(proxy_key)
116 if ip:
116 if ip:
117 return _filters(ip)
117 return _filters(ip)
118
118
119 ip = environ.get(proxy_key2)
119 ip = environ.get(proxy_key2)
120 if ip:
120 if ip:
121 return _filters(ip)
121 return _filters(ip)
122
122
123 ip = environ.get(def_key, '0.0.0.0')
123 ip = environ.get(def_key, '0.0.0.0')
124 return _filters(ip)
124 return _filters(ip)
125
125
126
126
127 def get_server_ip_addr(environ, log_errors=True):
127 def get_server_ip_addr(environ, log_errors=True):
128 hostname = environ.get('SERVER_NAME')
128 hostname = environ.get('SERVER_NAME')
129 try:
129 try:
130 return socket.gethostbyname(hostname)
130 return socket.gethostbyname(hostname)
131 except Exception as e:
131 except Exception as e:
132 if log_errors:
132 if log_errors:
133 # in some cases this lookup is not possible, and we don't want to
133 # in some cases this lookup is not possible, and we don't want to
134 # make it an exception in logs
134 # make it an exception in logs
135 log.exception('Could not retrieve server ip address: %s', e)
135 log.exception('Could not retrieve server ip address: %s', e)
136 return hostname
136 return hostname
137
137
138
138
139 def get_server_port(environ):
139 def get_server_port(environ):
140 return environ.get('SERVER_PORT')
140 return environ.get('SERVER_PORT')
141
141
142
142
143 def get_access_path(environ):
143 def get_access_path(environ):
144 path = environ.get('PATH_INFO')
144 path = environ.get('PATH_INFO')
145 org_req = environ.get('pylons.original_request')
145 org_req = environ.get('pylons.original_request')
146 if org_req:
146 if org_req:
147 path = org_req.environ.get('PATH_INFO')
147 path = org_req.environ.get('PATH_INFO')
148 return path
148 return path
149
149
150
150
151 def get_user_agent(environ):
151 def get_user_agent(environ):
152 return environ.get('HTTP_USER_AGENT')
152 return environ.get('HTTP_USER_AGENT')
153
153
154
154
155 def vcs_operation_context(
155 def vcs_operation_context(
156 environ, repo_name, username, action, scm, check_locking=True,
156 environ, repo_name, username, action, scm, check_locking=True,
157 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
157 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
158 """
158 """
159 Generate the context for a vcs operation, e.g. push or pull.
159 Generate the context for a vcs operation, e.g. push or pull.
160
160
161 This context is passed over the layers so that hooks triggered by the
161 This context is passed over the layers so that hooks triggered by the
162 vcs operation know details like the user, the user's IP address etc.
162 vcs operation know details like the user, the user's IP address etc.
163
163
164 :param check_locking: Allows to switch of the computation of the locking
164 :param check_locking: Allows to switch of the computation of the locking
165 data. This serves mainly the need of the simplevcs middleware to be
165 data. This serves mainly the need of the simplevcs middleware to be
166 able to disable this for certain operations.
166 able to disable this for certain operations.
167
167
168 """
168 """
169 # Tri-state value: False: unlock, None: nothing, True: lock
169 # Tri-state value: False: unlock, None: nothing, True: lock
170 make_lock = None
170 make_lock = None
171 locked_by = [None, None, None]
171 locked_by = [None, None, None]
172 is_anonymous = username == User.DEFAULT_USER
172 is_anonymous = username == User.DEFAULT_USER
173 user = User.get_by_username(username)
173 user = User.get_by_username(username)
174 if not is_anonymous and check_locking:
174 if not is_anonymous and check_locking:
175 log.debug('Checking locking on repository "%s"', repo_name)
175 log.debug('Checking locking on repository "%s"', repo_name)
176 repo = Repository.get_by_repo_name(repo_name)
176 repo = Repository.get_by_repo_name(repo_name)
177 make_lock, __, locked_by = repo.get_locking_state(
177 make_lock, __, locked_by = repo.get_locking_state(
178 action, user.user_id)
178 action, user.user_id)
179 user_id = user.user_id
179 user_id = user.user_id
180 settings_model = VcsSettingsModel(repo=repo_name)
180 settings_model = VcsSettingsModel(repo=repo_name)
181 ui_settings = settings_model.get_ui_settings()
181 ui_settings = settings_model.get_ui_settings()
182
182
183 # NOTE(marcink): This should be also in sync with
183 # NOTE(marcink): This should be also in sync with
184 # rhodecode/apps/ssh_support/lib/backends/base.py:update_environment scm_data
184 # rhodecode/apps/ssh_support/lib/backends/base.py:update_environment scm_data
185 store = [x for x in ui_settings if x.key == '/']
185 store = [x for x in ui_settings if x.key == '/']
186 repo_store = ''
186 repo_store = ''
187 if store:
187 if store:
188 repo_store = store[0].value
188 repo_store = store[0].value
189
189
190 scm_data = {
190 scm_data = {
191 'ip': get_ip_addr(environ),
191 'ip': get_ip_addr(environ),
192 'username': username,
192 'username': username,
193 'user_id': user_id,
193 'user_id': user_id,
194 'action': action,
194 'action': action,
195 'repository': repo_name,
195 'repository': repo_name,
196 'scm': scm,
196 'scm': scm,
197 'config': rhodecode.CONFIG['__file__'],
197 'config': rhodecode.CONFIG['__file__'],
198 'repo_store': repo_store,
198 'repo_store': repo_store,
199 'make_lock': make_lock,
199 'make_lock': make_lock,
200 'locked_by': locked_by,
200 'locked_by': locked_by,
201 'server_url': utils2.get_server_url(environ),
201 'server_url': utils2.get_server_url(environ),
202 'user_agent': get_user_agent(environ),
202 'user_agent': get_user_agent(environ),
203 'hooks': get_enabled_hook_classes(ui_settings),
203 'hooks': get_enabled_hook_classes(ui_settings),
204 'is_shadow_repo': is_shadow_repo,
204 'is_shadow_repo': is_shadow_repo,
205 'detect_force_push': detect_force_push,
205 'detect_force_push': detect_force_push,
206 'check_branch_perms': check_branch_perms,
206 'check_branch_perms': check_branch_perms,
207 }
207 }
208 return scm_data
208 return scm_data
209
209
210
210
211 class BasicAuth(AuthBasicAuthenticator):
211 class BasicAuth(AuthBasicAuthenticator):
212
212
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
215 self.realm = realm
215 self.realm = realm
216 self.rc_realm = rc_realm
216 self.rc_realm = rc_realm
217 self.initial_call = initial_call_detection
217 self.initial_call = initial_call_detection
218 self.authfunc = authfunc
218 self.authfunc = authfunc
219 self.registry = registry
219 self.registry = registry
220 self.acl_repo_name = acl_repo_name
220 self.acl_repo_name = acl_repo_name
221 self._rc_auth_http_code = auth_http_code
221 self._rc_auth_http_code = auth_http_code
222
222
223 def _get_response_from_code(self, http_code):
223 def _get_response_from_code(self, http_code):
224 try:
224 try:
225 return get_exception(safe_int(http_code))
225 return get_exception(safe_int(http_code))
226 except Exception:
226 except Exception:
227 log.exception('Failed to fetch response for code %s', http_code)
227 log.exception('Failed to fetch response for code %s', http_code)
228 return HTTPForbidden
228 return HTTPForbidden
229
229
230 def get_rc_realm(self):
230 def get_rc_realm(self):
231 return safe_str(self.rc_realm)
231 return safe_str(self.rc_realm)
232
232
233 def build_authentication(self):
233 def build_authentication(self):
234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
235 if self._rc_auth_http_code and not self.initial_call:
235 if self._rc_auth_http_code and not self.initial_call:
236 # return alternative HTTP code if alternative http return code
236 # return alternative HTTP code if alternative http return code
237 # is specified in RhodeCode config, but ONLY if it's not the
237 # is specified in RhodeCode config, but ONLY if it's not the
238 # FIRST call
238 # FIRST call
239 custom_response_klass = self._get_response_from_code(
239 custom_response_klass = self._get_response_from_code(
240 self._rc_auth_http_code)
240 self._rc_auth_http_code)
241 return custom_response_klass(headers=head)
241 return custom_response_klass(headers=head)
242 return HTTPUnauthorized(headers=head)
242 return HTTPUnauthorized(headers=head)
243
243
244 def authenticate(self, environ):
244 def authenticate(self, environ):
245 authorization = AUTHORIZATION(environ)
245 authorization = AUTHORIZATION(environ)
246 if not authorization:
246 if not authorization:
247 return self.build_authentication()
247 return self.build_authentication()
248 (authmeth, auth) = authorization.split(' ', 1)
248 (authmeth, auth) = authorization.split(' ', 1)
249 if 'basic' != authmeth.lower():
249 if 'basic' != authmeth.lower():
250 return self.build_authentication()
250 return self.build_authentication()
251 auth = auth.strip().decode('base64')
251 auth = auth.strip().decode('base64')
252 _parts = auth.split(':', 1)
252 _parts = auth.split(':', 1)
253 if len(_parts) == 2:
253 if len(_parts) == 2:
254 username, password = _parts
254 username, password = _parts
255 auth_data = self.authfunc(
255 auth_data = self.authfunc(
256 username, password, environ, VCS_TYPE,
256 username, password, environ, VCS_TYPE,
257 registry=self.registry, acl_repo_name=self.acl_repo_name)
257 registry=self.registry, acl_repo_name=self.acl_repo_name)
258 if auth_data:
258 if auth_data:
259 return {'username': username, 'auth_data': auth_data}
259 return {'username': username, 'auth_data': auth_data}
260 if username and password:
260 if username and password:
261 # we mark that we actually executed authentication once, at
261 # we mark that we actually executed authentication once, at
262 # that point we can use the alternative auth code
262 # that point we can use the alternative auth code
263 self.initial_call = False
263 self.initial_call = False
264
264
265 return self.build_authentication()
265 return self.build_authentication()
266
266
267 __call__ = authenticate
267 __call__ = authenticate
268
268
269
269
270 def calculate_version_hash(config):
270 def calculate_version_hash(config):
271 return sha1(
271 return sha1(
272 config.get('beaker.session.secret', '') +
272 config.get('beaker.session.secret', '') +
273 rhodecode.__version__)[:8]
273 rhodecode.__version__)[:8]
274
274
275
275
276 def get_current_lang(request):
276 def get_current_lang(request):
277 # NOTE(marcink): remove after pyramid move
277 # NOTE(marcink): remove after pyramid move
278 try:
278 try:
279 return translation.get_lang()[0]
279 return translation.get_lang()[0]
280 except:
280 except:
281 pass
281 pass
282
282
283 return getattr(request, '_LOCALE_', request.locale_name)
283 return getattr(request, '_LOCALE_', request.locale_name)
284
284
285
285
286 def attach_context_attributes(context, request, user_id=None, is_api=None):
286 def attach_context_attributes(context, request, user_id=None, is_api=None):
287 """
287 """
288 Attach variables into template context called `c`.
288 Attach variables into template context called `c`.
289 """
289 """
290 config = request.registry.settings
290 config = request.registry.settings
291
291
292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
293 context.rc_config = rc_config
293 context.rc_config = rc_config
294 context.rhodecode_version = rhodecode.__version__
294 context.rhodecode_version = rhodecode.__version__
295 context.rhodecode_edition = config.get('rhodecode.edition')
295 context.rhodecode_edition = config.get('rhodecode.edition')
296 context.rhodecode_edition_id = config.get('rhodecode.edition_id')
296 context.rhodecode_edition_id = config.get('rhodecode.edition_id')
297 # unique secret + version does not leak the version but keep consistency
297 # unique secret + version does not leak the version but keep consistency
298 context.rhodecode_version_hash = calculate_version_hash(config)
298 context.rhodecode_version_hash = calculate_version_hash(config)
299
299
300 # Default language set for the incoming request
300 # Default language set for the incoming request
301 context.language = get_current_lang(request)
301 context.language = get_current_lang(request)
302
302
303 # Visual options
303 # Visual options
304 context.visual = AttributeDict({})
304 context.visual = AttributeDict({})
305
305
306 # DB stored Visual Items
306 # DB stored Visual Items
307 context.visual.show_public_icon = str2bool(
307 context.visual.show_public_icon = str2bool(
308 rc_config.get('rhodecode_show_public_icon'))
308 rc_config.get('rhodecode_show_public_icon'))
309 context.visual.show_private_icon = str2bool(
309 context.visual.show_private_icon = str2bool(
310 rc_config.get('rhodecode_show_private_icon'))
310 rc_config.get('rhodecode_show_private_icon'))
311 context.visual.stylify_metatags = str2bool(
311 context.visual.stylify_metatags = str2bool(
312 rc_config.get('rhodecode_stylify_metatags'))
312 rc_config.get('rhodecode_stylify_metatags'))
313 context.visual.dashboard_items = safe_int(
313 context.visual.dashboard_items = safe_int(
314 rc_config.get('rhodecode_dashboard_items', 100))
314 rc_config.get('rhodecode_dashboard_items', 100))
315 context.visual.admin_grid_items = safe_int(
315 context.visual.admin_grid_items = safe_int(
316 rc_config.get('rhodecode_admin_grid_items', 100))
316 rc_config.get('rhodecode_admin_grid_items', 100))
317 context.visual.show_revision_number = str2bool(
317 context.visual.show_revision_number = str2bool(
318 rc_config.get('rhodecode_show_revision_number', True))
318 rc_config.get('rhodecode_show_revision_number', True))
319 context.visual.show_sha_length = safe_int(
319 context.visual.show_sha_length = safe_int(
320 rc_config.get('rhodecode_show_sha_length', 100))
320 rc_config.get('rhodecode_show_sha_length', 100))
321 context.visual.repository_fields = str2bool(
321 context.visual.repository_fields = str2bool(
322 rc_config.get('rhodecode_repository_fields'))
322 rc_config.get('rhodecode_repository_fields'))
323 context.visual.show_version = str2bool(
323 context.visual.show_version = str2bool(
324 rc_config.get('rhodecode_show_version'))
324 rc_config.get('rhodecode_show_version'))
325 context.visual.use_gravatar = str2bool(
325 context.visual.use_gravatar = str2bool(
326 rc_config.get('rhodecode_use_gravatar'))
326 rc_config.get('rhodecode_use_gravatar'))
327 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
327 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
328 context.visual.default_renderer = rc_config.get(
328 context.visual.default_renderer = rc_config.get(
329 'rhodecode_markup_renderer', 'rst')
329 'rhodecode_markup_renderer', 'rst')
330 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
330 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
331 context.visual.rhodecode_support_url = \
331 context.visual.rhodecode_support_url = \
332 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
332 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
333
333
334 context.visual.affected_files_cut_off = 60
334 context.visual.affected_files_cut_off = 60
335
335
336 context.pre_code = rc_config.get('rhodecode_pre_code')
336 context.pre_code = rc_config.get('rhodecode_pre_code')
337 context.post_code = rc_config.get('rhodecode_post_code')
337 context.post_code = rc_config.get('rhodecode_post_code')
338 context.rhodecode_name = rc_config.get('rhodecode_title')
338 context.rhodecode_name = rc_config.get('rhodecode_title')
339 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
339 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
340 # if we have specified default_encoding in the request, it has more
340 # if we have specified default_encoding in the request, it has more
341 # priority
341 # priority
342 if request.GET.get('default_encoding'):
342 if request.GET.get('default_encoding'):
343 context.default_encodings.insert(0, request.GET.get('default_encoding'))
343 context.default_encodings.insert(0, request.GET.get('default_encoding'))
344 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
344 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
345 context.clone_uri_id_tmpl = rc_config.get('rhodecode_clone_uri_id_tmpl')
345 context.clone_uri_id_tmpl = rc_config.get('rhodecode_clone_uri_id_tmpl')
346 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
346 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
347
347
348 # INI stored
348 # INI stored
349 context.labs_active = str2bool(
349 context.labs_active = str2bool(
350 config.get('labs_settings_active', 'false'))
350 config.get('labs_settings_active', 'false'))
351 context.ssh_enabled = str2bool(
351 context.ssh_enabled = str2bool(
352 config.get('ssh.generate_authorized_keyfile', 'false'))
352 config.get('ssh.generate_authorized_keyfile', 'false'))
353 context.ssh_key_generator_enabled = str2bool(
353 context.ssh_key_generator_enabled = str2bool(
354 config.get('ssh.enable_ui_key_generator', 'true'))
354 config.get('ssh.enable_ui_key_generator', 'true'))
355
355
356 context.visual.allow_repo_location_change = str2bool(
356 context.visual.allow_repo_location_change = str2bool(
357 config.get('allow_repo_location_change', True))
357 config.get('allow_repo_location_change', True))
358 context.visual.allow_custom_hooks_settings = str2bool(
358 context.visual.allow_custom_hooks_settings = str2bool(
359 config.get('allow_custom_hooks_settings', True))
359 config.get('allow_custom_hooks_settings', True))
360 context.debug_style = str2bool(config.get('debug_style', False))
360 context.debug_style = str2bool(config.get('debug_style', False))
361
361
362 context.rhodecode_instanceid = config.get('instance_id')
362 context.rhodecode_instanceid = config.get('instance_id')
363
363
364 context.visual.cut_off_limit_diff = safe_int(
364 context.visual.cut_off_limit_diff = safe_int(
365 config.get('cut_off_limit_diff'))
365 config.get('cut_off_limit_diff'))
366 context.visual.cut_off_limit_file = safe_int(
366 context.visual.cut_off_limit_file = safe_int(
367 config.get('cut_off_limit_file'))
367 config.get('cut_off_limit_file'))
368
368
369 context.license = AttributeDict({})
369 context.license = AttributeDict({})
370 context.license.hide_license_info = str2bool(
370 context.license.hide_license_info = str2bool(
371 config.get('license.hide_license_info', False))
371 config.get('license.hide_license_info', False))
372
372
373 # AppEnlight
373 # AppEnlight
374 context.appenlight_enabled = config.get('appenlight', False)
374 context.appenlight_enabled = config.get('appenlight', False)
375 context.appenlight_api_public_key = config.get(
375 context.appenlight_api_public_key = config.get(
376 'appenlight.api_public_key', '')
376 'appenlight.api_public_key', '')
377 context.appenlight_server_url = config.get('appenlight.server_url', '')
377 context.appenlight_server_url = config.get('appenlight.server_url', '')
378
378
379 diffmode = {
379 diffmode = {
380 "unified": "unified",
380 "unified": "unified",
381 "sideside": "sideside"
381 "sideside": "sideside"
382 }.get(request.GET.get('diffmode'))
382 }.get(request.GET.get('diffmode'))
383
383
384 if is_api is not None:
384 if is_api is not None:
385 is_api = hasattr(request, 'rpc_user')
385 is_api = hasattr(request, 'rpc_user')
386 session_attrs = {
386 session_attrs = {
387 # defaults
387 # defaults
388 "clone_url_format": "http",
388 "clone_url_format": "http",
389 "diffmode": "sideside",
389 "diffmode": "sideside",
390 "license_fingerprint": request.session.get('license_fingerprint')
390 "license_fingerprint": request.session.get('license_fingerprint')
391 }
391 }
392
392
393 if not is_api:
393 if not is_api:
394 # don't access pyramid session for API calls
394 # don't access pyramid session for API calls
395 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
395 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
396 request.session['rc_user_session_attr.diffmode'] = diffmode
396 request.session['rc_user_session_attr.diffmode'] = diffmode
397
397
398 # session settings per user
398 # session settings per user
399
399
400 for k, v in request.session.items():
400 for k, v in request.session.items():
401 pref = 'rc_user_session_attr.'
401 pref = 'rc_user_session_attr.'
402 if k and k.startswith(pref):
402 if k and k.startswith(pref):
403 k = k[len(pref):]
403 k = k[len(pref):]
404 session_attrs[k] = v
404 session_attrs[k] = v
405
405
406 context.user_session_attrs = session_attrs
406 context.user_session_attrs = session_attrs
407
407
408 # JS template context
408 # JS template context
409 context.template_context = {
409 context.template_context = {
410 'repo_name': None,
410 'repo_name': None,
411 'repo_type': None,
411 'repo_type': None,
412 'repo_landing_commit': None,
412 'repo_landing_commit': None,
413 'rhodecode_user': {
413 'rhodecode_user': {
414 'username': None,
414 'username': None,
415 'email': None,
415 'email': None,
416 'notification_status': False
416 'notification_status': False
417 },
417 },
418 'session_attrs': session_attrs,
418 'session_attrs': session_attrs,
419 'visual': {
419 'visual': {
420 'default_renderer': None
420 'default_renderer': None
421 },
421 },
422 'commit_data': {
422 'commit_data': {
423 'commit_id': None
423 'commit_id': None
424 },
424 },
425 'pull_request_data': {'pull_request_id': None},
425 'pull_request_data': {'pull_request_id': None},
426 'timeago': {
426 'timeago': {
427 'refresh_time': 120 * 1000,
427 'refresh_time': 120 * 1000,
428 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
428 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
429 },
429 },
430 'pyramid_dispatch': {
430 'pyramid_dispatch': {
431
431
432 },
432 },
433 'extra': {'plugins': {}}
433 'extra': {'plugins': {}}
434 }
434 }
435 # END CONFIG VARS
435 # END CONFIG VARS
436 if is_api:
436 if is_api:
437 csrf_token = None
437 csrf_token = None
438 else:
438 else:
439 csrf_token = auth.get_csrf_token(session=request.session)
439 csrf_token = auth.get_csrf_token(session=request.session)
440
440
441 context.csrf_token = csrf_token
441 context.csrf_token = csrf_token
442 context.backends = rhodecode.BACKENDS.keys()
442 context.backends = rhodecode.BACKENDS.keys()
443
443
444 unread_count = 0
444 unread_count = 0
445 user_bookmark_list = []
445 user_bookmark_list = []
446 if user_id:
446 if user_id:
447 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
447 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
448 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
448 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
449 context.unread_notifications = unread_count
449 context.unread_notifications = unread_count
450 context.bookmark_items = user_bookmark_list
450 context.bookmark_items = user_bookmark_list
451
451
452 # web case
452 # web case
453 if hasattr(request, 'user'):
453 if hasattr(request, 'user'):
454 context.auth_user = request.user
454 context.auth_user = request.user
455 context.rhodecode_user = request.user
455 context.rhodecode_user = request.user
456
456
457 # api case
457 # api case
458 if hasattr(request, 'rpc_user'):
458 if hasattr(request, 'rpc_user'):
459 context.auth_user = request.rpc_user
459 context.auth_user = request.rpc_user
460 context.rhodecode_user = request.rpc_user
460 context.rhodecode_user = request.rpc_user
461
461
462 # attach the whole call context to the request
462 # attach the whole call context to the request
463 # use set_call_context method if request has it
464 # sometimes in Celery context requests is "different"
465 if hasattr(request, 'set_call_context'):
466 request.set_call_context(context)
467 else:
463 request.call_context = context
468 request.call_context = context
464
469
465
470
466 def get_auth_user(request):
471 def get_auth_user(request):
467 environ = request.environ
472 environ = request.environ
468 session = request.session
473 session = request.session
469
474
470 ip_addr = get_ip_addr(environ)
475 ip_addr = get_ip_addr(environ)
471
476
472 # make sure that we update permissions each time we call controller
477 # make sure that we update permissions each time we call controller
473 _auth_token = (
478 _auth_token = (
474 # ?auth_token=XXX
479 # ?auth_token=XXX
475 request.GET.get('auth_token', '')
480 request.GET.get('auth_token', '')
476 # ?api_key=XXX !LEGACY
481 # ?api_key=XXX !LEGACY
477 or request.GET.get('api_key', '')
482 or request.GET.get('api_key', '')
478 # or headers....
483 # or headers....
479 or request.headers.get('X-Rc-Auth-Token', '')
484 or request.headers.get('X-Rc-Auth-Token', '')
480 )
485 )
481 if not _auth_token and request.matchdict:
486 if not _auth_token and request.matchdict:
482 url_auth_token = request.matchdict.get('_auth_token')
487 url_auth_token = request.matchdict.get('_auth_token')
483 _auth_token = url_auth_token
488 _auth_token = url_auth_token
484 if _auth_token:
489 if _auth_token:
485 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
490 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
486
491
487 if _auth_token:
492 if _auth_token:
488 # when using API_KEY we assume user exists, and
493 # when using API_KEY we assume user exists, and
489 # doesn't need auth based on cookies.
494 # doesn't need auth based on cookies.
490 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
495 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
491 authenticated = False
496 authenticated = False
492 else:
497 else:
493 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
498 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
494 try:
499 try:
495 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
500 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
496 ip_addr=ip_addr)
501 ip_addr=ip_addr)
497 except UserCreationError as e:
502 except UserCreationError as e:
498 h.flash(e, 'error')
503 h.flash(e, 'error')
499 # container auth or other auth functions that create users
504 # container auth or other auth functions that create users
500 # on the fly can throw this exception signaling that there's
505 # on the fly can throw this exception signaling that there's
501 # issue with user creation, explanation should be provided
506 # issue with user creation, explanation should be provided
502 # in Exception itself. We then create a simple blank
507 # in Exception itself. We then create a simple blank
503 # AuthUser
508 # AuthUser
504 auth_user = AuthUser(ip_addr=ip_addr)
509 auth_user = AuthUser(ip_addr=ip_addr)
505
510
506 # in case someone changes a password for user it triggers session
511 # in case someone changes a password for user it triggers session
507 # flush and forces a re-login
512 # flush and forces a re-login
508 if password_changed(auth_user, session):
513 if password_changed(auth_user, session):
509 session.invalidate()
514 session.invalidate()
510 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
515 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
511 auth_user = AuthUser(ip_addr=ip_addr)
516 auth_user = AuthUser(ip_addr=ip_addr)
512
517
513 authenticated = cookie_store.get('is_authenticated')
518 authenticated = cookie_store.get('is_authenticated')
514
519
515 if not auth_user.is_authenticated and auth_user.is_user_object:
520 if not auth_user.is_authenticated and auth_user.is_user_object:
516 # user is not authenticated and not empty
521 # user is not authenticated and not empty
517 auth_user.set_authenticated(authenticated)
522 auth_user.set_authenticated(authenticated)
518
523
519 return auth_user, _auth_token
524 return auth_user, _auth_token
520
525
521
526
522 def h_filter(s):
527 def h_filter(s):
523 """
528 """
524 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
529 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
525 we wrap this with additional functionality that converts None to empty
530 we wrap this with additional functionality that converts None to empty
526 strings
531 strings
527 """
532 """
528 if s is None:
533 if s is None:
529 return markupsafe.Markup()
534 return markupsafe.Markup()
530 return markupsafe.escape(s)
535 return markupsafe.escape(s)
531
536
532
537
533 def add_events_routes(config):
538 def add_events_routes(config):
534 """
539 """
535 Adds routing that can be used in events. Because some events are triggered
540 Adds routing that can be used in events. Because some events are triggered
536 outside of pyramid context, we need to bootstrap request with some
541 outside of pyramid context, we need to bootstrap request with some
537 routing registered
542 routing registered
538 """
543 """
539
544
540 from rhodecode.apps._base import ADMIN_PREFIX
545 from rhodecode.apps._base import ADMIN_PREFIX
541
546
542 config.add_route(name='home', pattern='/')
547 config.add_route(name='home', pattern='/')
543 config.add_route(name='main_page_repos_data', pattern='/_home_repos')
548 config.add_route(name='main_page_repos_data', pattern='/_home_repos')
544 config.add_route(name='main_page_repo_groups_data', pattern='/_home_repo_groups')
549 config.add_route(name='main_page_repo_groups_data', pattern='/_home_repo_groups')
545
550
546 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
551 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
547 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
552 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
548 config.add_route(name='repo_summary', pattern='/{repo_name}')
553 config.add_route(name='repo_summary', pattern='/{repo_name}')
549 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
554 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
550 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
555 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
551
556
552 config.add_route(name='pullrequest_show',
557 config.add_route(name='pullrequest_show',
553 pattern='/{repo_name}/pull-request/{pull_request_id}')
558 pattern='/{repo_name}/pull-request/{pull_request_id}')
554 config.add_route(name='pull_requests_global',
559 config.add_route(name='pull_requests_global',
555 pattern='/pull-request/{pull_request_id}')
560 pattern='/pull-request/{pull_request_id}')
556
561
557 config.add_route(name='repo_commit',
562 config.add_route(name='repo_commit',
558 pattern='/{repo_name}/changeset/{commit_id}')
563 pattern='/{repo_name}/changeset/{commit_id}')
559 config.add_route(name='repo_files',
564 config.add_route(name='repo_files',
560 pattern='/{repo_name}/files/{commit_id}/{f_path}')
565 pattern='/{repo_name}/files/{commit_id}/{f_path}')
561
566
562 config.add_route(name='hovercard_user',
567 config.add_route(name='hovercard_user',
563 pattern='/_hovercard/user/{user_id}')
568 pattern='/_hovercard/user/{user_id}')
564
569
565 config.add_route(name='hovercard_user_group',
570 config.add_route(name='hovercard_user_group',
566 pattern='/_hovercard/user_group/{user_group_id}')
571 pattern='/_hovercard/user_group/{user_group_id}')
567
572
568 config.add_route(name='hovercard_pull_request',
573 config.add_route(name='hovercard_pull_request',
569 pattern='/_hovercard/pull_request/{pull_request_id}')
574 pattern='/_hovercard/pull_request/{pull_request_id}')
570
575
571 config.add_route(name='hovercard_repo_commit',
576 config.add_route(name='hovercard_repo_commit',
572 pattern='/_hovercard/commit/{repo_name}/{commit_id}')
577 pattern='/_hovercard/commit/{repo_name}/{commit_id}')
573
578
574
579
575 def bootstrap_config(request):
580 def bootstrap_config(request):
576 import pyramid.testing
581 import pyramid.testing
577 registry = pyramid.testing.Registry('RcTestRegistry')
582 registry = pyramid.testing.Registry('RcTestRegistry')
578
583
579 config = pyramid.testing.setUp(registry=registry, request=request)
584 config = pyramid.testing.setUp(registry=registry, request=request)
580
585
581 # allow pyramid lookup in testing
586 # allow pyramid lookup in testing
582 config.include('pyramid_mako')
587 config.include('pyramid_mako')
583 config.include('rhodecode.lib.rc_beaker')
588 config.include('rhodecode.lib.rc_beaker')
584 config.include('rhodecode.lib.rc_cache')
589 config.include('rhodecode.lib.rc_cache')
585
590
586 add_events_routes(config)
591 add_events_routes(config)
587
592
588 return config
593 return config
589
594
590
595
591 def bootstrap_request(**kwargs):
596 def bootstrap_request(**kwargs):
592 import pyramid.testing
597 import pyramid.testing
593
598
594 class TestRequest(pyramid.testing.DummyRequest):
599 class TestRequest(pyramid.testing.DummyRequest):
595 application_url = kwargs.pop('application_url', 'http://example.com')
600 application_url = kwargs.pop('application_url', 'http://example.com')
596 host = kwargs.pop('host', 'example.com:80')
601 host = kwargs.pop('host', 'example.com:80')
597 domain = kwargs.pop('domain', 'example.com')
602 domain = kwargs.pop('domain', 'example.com')
598
603
599 def translate(self, msg):
604 def translate(self, msg):
600 return msg
605 return msg
601
606
602 def plularize(self, singular, plural, n):
607 def plularize(self, singular, plural, n):
603 return singular
608 return singular
604
609
605 def get_partial_renderer(self, tmpl_name):
610 def get_partial_renderer(self, tmpl_name):
606
611
607 from rhodecode.lib.partial_renderer import get_partial_renderer
612 from rhodecode.lib.partial_renderer import get_partial_renderer
608 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
613 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
609
614
610 _call_context = TemplateArgs()
615 _call_context = TemplateArgs()
611 _call_context.visual = TemplateArgs()
616 _call_context.visual = TemplateArgs()
612 _call_context.visual.show_sha_length = 12
617 _call_context.visual.show_sha_length = 12
613 _call_context.visual.show_revision_number = True
618 _call_context.visual.show_revision_number = True
614
619
615 @property
620 @property
616 def call_context(self):
621 def call_context(self):
617 return self._call_context
622 return self._call_context
618
623
624 def set_call_context(self, new_context):
625 self._call_context = new_context
626
619 class TestDummySession(pyramid.testing.DummySession):
627 class TestDummySession(pyramid.testing.DummySession):
620 def save(*arg, **kw):
628 def save(*arg, **kw):
621 pass
629 pass
622
630
623 request = TestRequest(**kwargs)
631 request = TestRequest(**kwargs)
624 request.session = TestDummySession()
632 request.session = TestDummySession()
625
633
626 return request
634 return request
627
635
General Comments 0
You need to be logged in to leave comments. Login now