##// END OF EJS Templates
base: fix problem that in some view context we don't have matchdict.
marcink -
r4007:e35e4c77 default
parent child Browse files
Show More
@@ -1,601 +1,601 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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):
214 initial_call_detection=False, acl_repo_name=None):
215 self.realm = realm
215 self.realm = realm
216 self.initial_call = initial_call_detection
216 self.initial_call = initial_call_detection
217 self.authfunc = authfunc
217 self.authfunc = authfunc
218 self.registry = registry
218 self.registry = registry
219 self.acl_repo_name = acl_repo_name
219 self.acl_repo_name = acl_repo_name
220 self._rc_auth_http_code = auth_http_code
220 self._rc_auth_http_code = auth_http_code
221
221
222 def _get_response_from_code(self, http_code):
222 def _get_response_from_code(self, http_code):
223 try:
223 try:
224 return get_exception(safe_int(http_code))
224 return get_exception(safe_int(http_code))
225 except Exception:
225 except Exception:
226 log.exception('Failed to fetch response for code %s', http_code)
226 log.exception('Failed to fetch response for code %s', http_code)
227 return HTTPForbidden
227 return HTTPForbidden
228
228
229 def get_rc_realm(self):
229 def get_rc_realm(self):
230 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
230 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
231
231
232 def build_authentication(self):
232 def build_authentication(self):
233 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
233 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 if self._rc_auth_http_code and not self.initial_call:
234 if self._rc_auth_http_code and not self.initial_call:
235 # return alternative HTTP code if alternative http return code
235 # return alternative HTTP code if alternative http return code
236 # is specified in RhodeCode config, but ONLY if it's not the
236 # is specified in RhodeCode config, but ONLY if it's not the
237 # FIRST call
237 # FIRST call
238 custom_response_klass = self._get_response_from_code(
238 custom_response_klass = self._get_response_from_code(
239 self._rc_auth_http_code)
239 self._rc_auth_http_code)
240 return custom_response_klass(headers=head)
240 return custom_response_klass(headers=head)
241 return HTTPUnauthorized(headers=head)
241 return HTTPUnauthorized(headers=head)
242
242
243 def authenticate(self, environ):
243 def authenticate(self, environ):
244 authorization = AUTHORIZATION(environ)
244 authorization = AUTHORIZATION(environ)
245 if not authorization:
245 if not authorization:
246 return self.build_authentication()
246 return self.build_authentication()
247 (authmeth, auth) = authorization.split(' ', 1)
247 (authmeth, auth) = authorization.split(' ', 1)
248 if 'basic' != authmeth.lower():
248 if 'basic' != authmeth.lower():
249 return self.build_authentication()
249 return self.build_authentication()
250 auth = auth.strip().decode('base64')
250 auth = auth.strip().decode('base64')
251 _parts = auth.split(':', 1)
251 _parts = auth.split(':', 1)
252 if len(_parts) == 2:
252 if len(_parts) == 2:
253 username, password = _parts
253 username, password = _parts
254 auth_data = self.authfunc(
254 auth_data = self.authfunc(
255 username, password, environ, VCS_TYPE,
255 username, password, environ, VCS_TYPE,
256 registry=self.registry, acl_repo_name=self.acl_repo_name)
256 registry=self.registry, acl_repo_name=self.acl_repo_name)
257 if auth_data:
257 if auth_data:
258 return {'username': username, 'auth_data': auth_data}
258 return {'username': username, 'auth_data': auth_data}
259 if username and password:
259 if username and password:
260 # we mark that we actually executed authentication once, at
260 # we mark that we actually executed authentication once, at
261 # that point we can use the alternative auth code
261 # that point we can use the alternative auth code
262 self.initial_call = False
262 self.initial_call = False
263
263
264 return self.build_authentication()
264 return self.build_authentication()
265
265
266 __call__ = authenticate
266 __call__ = authenticate
267
267
268
268
269 def calculate_version_hash(config):
269 def calculate_version_hash(config):
270 return sha1(
270 return sha1(
271 config.get('beaker.session.secret', '') +
271 config.get('beaker.session.secret', '') +
272 rhodecode.__version__)[:8]
272 rhodecode.__version__)[:8]
273
273
274
274
275 def get_current_lang(request):
275 def get_current_lang(request):
276 # NOTE(marcink): remove after pyramid move
276 # NOTE(marcink): remove after pyramid move
277 try:
277 try:
278 return translation.get_lang()[0]
278 return translation.get_lang()[0]
279 except:
279 except:
280 pass
280 pass
281
281
282 return getattr(request, '_LOCALE_', request.locale_name)
282 return getattr(request, '_LOCALE_', request.locale_name)
283
283
284
284
285 def attach_context_attributes(context, request, user_id=None):
285 def attach_context_attributes(context, request, user_id=None):
286 """
286 """
287 Attach variables into template context called `c`.
287 Attach variables into template context called `c`.
288 """
288 """
289 config = request.registry.settings
289 config = request.registry.settings
290
290
291 rc_config = SettingsModel().get_all_settings(cache=True)
291 rc_config = SettingsModel().get_all_settings(cache=True)
292 context.rc_config = rc_config
292 context.rc_config = rc_config
293 context.rhodecode_version = rhodecode.__version__
293 context.rhodecode_version = rhodecode.__version__
294 context.rhodecode_edition = config.get('rhodecode.edition')
294 context.rhodecode_edition = config.get('rhodecode.edition')
295 # unique secret + version does not leak the version but keep consistency
295 # unique secret + version does not leak the version but keep consistency
296 context.rhodecode_version_hash = calculate_version_hash(config)
296 context.rhodecode_version_hash = calculate_version_hash(config)
297
297
298 # Default language set for the incoming request
298 # Default language set for the incoming request
299 context.language = get_current_lang(request)
299 context.language = get_current_lang(request)
300
300
301 # Visual options
301 # Visual options
302 context.visual = AttributeDict({})
302 context.visual = AttributeDict({})
303
303
304 # DB stored Visual Items
304 # DB stored Visual Items
305 context.visual.show_public_icon = str2bool(
305 context.visual.show_public_icon = str2bool(
306 rc_config.get('rhodecode_show_public_icon'))
306 rc_config.get('rhodecode_show_public_icon'))
307 context.visual.show_private_icon = str2bool(
307 context.visual.show_private_icon = str2bool(
308 rc_config.get('rhodecode_show_private_icon'))
308 rc_config.get('rhodecode_show_private_icon'))
309 context.visual.stylify_metatags = str2bool(
309 context.visual.stylify_metatags = str2bool(
310 rc_config.get('rhodecode_stylify_metatags'))
310 rc_config.get('rhodecode_stylify_metatags'))
311 context.visual.dashboard_items = safe_int(
311 context.visual.dashboard_items = safe_int(
312 rc_config.get('rhodecode_dashboard_items', 100))
312 rc_config.get('rhodecode_dashboard_items', 100))
313 context.visual.admin_grid_items = safe_int(
313 context.visual.admin_grid_items = safe_int(
314 rc_config.get('rhodecode_admin_grid_items', 100))
314 rc_config.get('rhodecode_admin_grid_items', 100))
315 context.visual.show_revision_number = str2bool(
315 context.visual.show_revision_number = str2bool(
316 rc_config.get('rhodecode_show_revision_number', True))
316 rc_config.get('rhodecode_show_revision_number', True))
317 context.visual.show_sha_length = safe_int(
317 context.visual.show_sha_length = safe_int(
318 rc_config.get('rhodecode_show_sha_length', 100))
318 rc_config.get('rhodecode_show_sha_length', 100))
319 context.visual.repository_fields = str2bool(
319 context.visual.repository_fields = str2bool(
320 rc_config.get('rhodecode_repository_fields'))
320 rc_config.get('rhodecode_repository_fields'))
321 context.visual.show_version = str2bool(
321 context.visual.show_version = str2bool(
322 rc_config.get('rhodecode_show_version'))
322 rc_config.get('rhodecode_show_version'))
323 context.visual.use_gravatar = str2bool(
323 context.visual.use_gravatar = str2bool(
324 rc_config.get('rhodecode_use_gravatar'))
324 rc_config.get('rhodecode_use_gravatar'))
325 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
325 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
326 context.visual.default_renderer = rc_config.get(
326 context.visual.default_renderer = rc_config.get(
327 'rhodecode_markup_renderer', 'rst')
327 'rhodecode_markup_renderer', 'rst')
328 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
328 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
329 context.visual.rhodecode_support_url = \
329 context.visual.rhodecode_support_url = \
330 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
330 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
331
331
332 context.visual.affected_files_cut_off = 60
332 context.visual.affected_files_cut_off = 60
333
333
334 context.pre_code = rc_config.get('rhodecode_pre_code')
334 context.pre_code = rc_config.get('rhodecode_pre_code')
335 context.post_code = rc_config.get('rhodecode_post_code')
335 context.post_code = rc_config.get('rhodecode_post_code')
336 context.rhodecode_name = rc_config.get('rhodecode_title')
336 context.rhodecode_name = rc_config.get('rhodecode_title')
337 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
337 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
338 # if we have specified default_encoding in the request, it has more
338 # if we have specified default_encoding in the request, it has more
339 # priority
339 # priority
340 if request.GET.get('default_encoding'):
340 if request.GET.get('default_encoding'):
341 context.default_encodings.insert(0, request.GET.get('default_encoding'))
341 context.default_encodings.insert(0, request.GET.get('default_encoding'))
342 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
342 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
343 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
343 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
344
344
345 # INI stored
345 # INI stored
346 context.labs_active = str2bool(
346 context.labs_active = str2bool(
347 config.get('labs_settings_active', 'false'))
347 config.get('labs_settings_active', 'false'))
348 context.ssh_enabled = str2bool(
348 context.ssh_enabled = str2bool(
349 config.get('ssh.generate_authorized_keyfile', 'false'))
349 config.get('ssh.generate_authorized_keyfile', 'false'))
350 context.ssh_key_generator_enabled = str2bool(
350 context.ssh_key_generator_enabled = str2bool(
351 config.get('ssh.enable_ui_key_generator', 'true'))
351 config.get('ssh.enable_ui_key_generator', 'true'))
352
352
353 context.visual.allow_repo_location_change = str2bool(
353 context.visual.allow_repo_location_change = str2bool(
354 config.get('allow_repo_location_change', True))
354 config.get('allow_repo_location_change', True))
355 context.visual.allow_custom_hooks_settings = str2bool(
355 context.visual.allow_custom_hooks_settings = str2bool(
356 config.get('allow_custom_hooks_settings', True))
356 config.get('allow_custom_hooks_settings', True))
357 context.debug_style = str2bool(config.get('debug_style', False))
357 context.debug_style = str2bool(config.get('debug_style', False))
358
358
359 context.rhodecode_instanceid = config.get('instance_id')
359 context.rhodecode_instanceid = config.get('instance_id')
360
360
361 context.visual.cut_off_limit_diff = safe_int(
361 context.visual.cut_off_limit_diff = safe_int(
362 config.get('cut_off_limit_diff'))
362 config.get('cut_off_limit_diff'))
363 context.visual.cut_off_limit_file = safe_int(
363 context.visual.cut_off_limit_file = safe_int(
364 config.get('cut_off_limit_file'))
364 config.get('cut_off_limit_file'))
365
365
366 context.license = AttributeDict({})
366 context.license = AttributeDict({})
367 context.license.hide_license_info = str2bool(
367 context.license.hide_license_info = str2bool(
368 config.get('license.hide_license_info', False))
368 config.get('license.hide_license_info', False))
369
369
370 # AppEnlight
370 # AppEnlight
371 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
371 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
372 context.appenlight_api_public_key = config.get(
372 context.appenlight_api_public_key = config.get(
373 'appenlight.api_public_key', '')
373 'appenlight.api_public_key', '')
374 context.appenlight_server_url = config.get('appenlight.server_url', '')
374 context.appenlight_server_url = config.get('appenlight.server_url', '')
375
375
376 diffmode = {
376 diffmode = {
377 "unified": "unified",
377 "unified": "unified",
378 "sideside": "sideside"
378 "sideside": "sideside"
379 }.get(request.GET.get('diffmode'))
379 }.get(request.GET.get('diffmode'))
380
380
381 is_api = hasattr(request, 'rpc_user')
381 is_api = hasattr(request, 'rpc_user')
382 session_attrs = {
382 session_attrs = {
383 # defaults
383 # defaults
384 "clone_url_format": "http",
384 "clone_url_format": "http",
385 "diffmode": "sideside"
385 "diffmode": "sideside"
386 }
386 }
387
387
388 if not is_api:
388 if not is_api:
389 # don't access pyramid session for API calls
389 # don't access pyramid session for API calls
390 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
390 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
391 request.session['rc_user_session_attr.diffmode'] = diffmode
391 request.session['rc_user_session_attr.diffmode'] = diffmode
392
392
393 # session settings per user
393 # session settings per user
394
394
395 for k, v in request.session.items():
395 for k, v in request.session.items():
396 pref = 'rc_user_session_attr.'
396 pref = 'rc_user_session_attr.'
397 if k and k.startswith(pref):
397 if k and k.startswith(pref):
398 k = k[len(pref):]
398 k = k[len(pref):]
399 session_attrs[k] = v
399 session_attrs[k] = v
400
400
401 context.user_session_attrs = session_attrs
401 context.user_session_attrs = session_attrs
402
402
403 # JS template context
403 # JS template context
404 context.template_context = {
404 context.template_context = {
405 'repo_name': None,
405 'repo_name': None,
406 'repo_type': None,
406 'repo_type': None,
407 'repo_landing_commit': None,
407 'repo_landing_commit': None,
408 'rhodecode_user': {
408 'rhodecode_user': {
409 'username': None,
409 'username': None,
410 'email': None,
410 'email': None,
411 'notification_status': False
411 'notification_status': False
412 },
412 },
413 'session_attrs': session_attrs,
413 'session_attrs': session_attrs,
414 'visual': {
414 'visual': {
415 'default_renderer': None
415 'default_renderer': None
416 },
416 },
417 'commit_data': {
417 'commit_data': {
418 'commit_id': None
418 'commit_id': None
419 },
419 },
420 'pull_request_data': {'pull_request_id': None},
420 'pull_request_data': {'pull_request_id': None},
421 'timeago': {
421 'timeago': {
422 'refresh_time': 120 * 1000,
422 'refresh_time': 120 * 1000,
423 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
423 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
424 },
424 },
425 'pyramid_dispatch': {
425 'pyramid_dispatch': {
426
426
427 },
427 },
428 'extra': {'plugins': {}}
428 'extra': {'plugins': {}}
429 }
429 }
430 # END CONFIG VARS
430 # END CONFIG VARS
431 if is_api:
431 if is_api:
432 csrf_token = None
432 csrf_token = None
433 else:
433 else:
434 csrf_token = auth.get_csrf_token(session=request.session)
434 csrf_token = auth.get_csrf_token(session=request.session)
435
435
436 context.csrf_token = csrf_token
436 context.csrf_token = csrf_token
437 context.backends = rhodecode.BACKENDS.keys()
437 context.backends = rhodecode.BACKENDS.keys()
438 context.backends.sort()
438 context.backends.sort()
439 unread_count = 0
439 unread_count = 0
440 user_bookmark_list = []
440 user_bookmark_list = []
441 if user_id:
441 if user_id:
442 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
442 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
443 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
443 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
444 context.unread_notifications = unread_count
444 context.unread_notifications = unread_count
445 context.bookmark_items = user_bookmark_list
445 context.bookmark_items = user_bookmark_list
446
446
447 # web case
447 # web case
448 if hasattr(request, 'user'):
448 if hasattr(request, 'user'):
449 context.auth_user = request.user
449 context.auth_user = request.user
450 context.rhodecode_user = request.user
450 context.rhodecode_user = request.user
451
451
452 # api case
452 # api case
453 if hasattr(request, 'rpc_user'):
453 if hasattr(request, 'rpc_user'):
454 context.auth_user = request.rpc_user
454 context.auth_user = request.rpc_user
455 context.rhodecode_user = request.rpc_user
455 context.rhodecode_user = request.rpc_user
456
456
457 # attach the whole call context to the request
457 # attach the whole call context to the request
458 request.call_context = context
458 request.call_context = context
459
459
460
460
461 def get_auth_user(request):
461 def get_auth_user(request):
462 environ = request.environ
462 environ = request.environ
463 session = request.session
463 session = request.session
464
464
465 ip_addr = get_ip_addr(environ)
465 ip_addr = get_ip_addr(environ)
466
466
467 # make sure that we update permissions each time we call controller
467 # make sure that we update permissions each time we call controller
468 _auth_token = (request.GET.get('auth_token', '') or request.GET.get('api_key', ''))
468 _auth_token = (request.GET.get('auth_token', '') or request.GET.get('api_key', ''))
469 if not _auth_token:
469 if not _auth_token and request.matchdict:
470 url_auth_token = request.matchdict.get('_auth_token')
470 url_auth_token = request.matchdict.get('_auth_token')
471 _auth_token = url_auth_token
471 _auth_token = url_auth_token
472 if _auth_token:
472 if _auth_token:
473 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
473 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
474
474
475 if _auth_token:
475 if _auth_token:
476 # when using API_KEY we assume user exists, and
476 # when using API_KEY we assume user exists, and
477 # doesn't need auth based on cookies.
477 # doesn't need auth based on cookies.
478 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
478 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
479 authenticated = False
479 authenticated = False
480 else:
480 else:
481 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
481 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
482 try:
482 try:
483 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
483 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
484 ip_addr=ip_addr)
484 ip_addr=ip_addr)
485 except UserCreationError as e:
485 except UserCreationError as e:
486 h.flash(e, 'error')
486 h.flash(e, 'error')
487 # container auth or other auth functions that create users
487 # container auth or other auth functions that create users
488 # on the fly can throw this exception signaling that there's
488 # on the fly can throw this exception signaling that there's
489 # issue with user creation, explanation should be provided
489 # issue with user creation, explanation should be provided
490 # in Exception itself. We then create a simple blank
490 # in Exception itself. We then create a simple blank
491 # AuthUser
491 # AuthUser
492 auth_user = AuthUser(ip_addr=ip_addr)
492 auth_user = AuthUser(ip_addr=ip_addr)
493
493
494 # in case someone changes a password for user it triggers session
494 # in case someone changes a password for user it triggers session
495 # flush and forces a re-login
495 # flush and forces a re-login
496 if password_changed(auth_user, session):
496 if password_changed(auth_user, session):
497 session.invalidate()
497 session.invalidate()
498 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
498 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
499 auth_user = AuthUser(ip_addr=ip_addr)
499 auth_user = AuthUser(ip_addr=ip_addr)
500
500
501 authenticated = cookie_store.get('is_authenticated')
501 authenticated = cookie_store.get('is_authenticated')
502
502
503 if not auth_user.is_authenticated and auth_user.is_user_object:
503 if not auth_user.is_authenticated and auth_user.is_user_object:
504 # user is not authenticated and not empty
504 # user is not authenticated and not empty
505 auth_user.set_authenticated(authenticated)
505 auth_user.set_authenticated(authenticated)
506
506
507 return auth_user, _auth_token
507 return auth_user, _auth_token
508
508
509
509
510 def h_filter(s):
510 def h_filter(s):
511 """
511 """
512 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
512 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
513 we wrap this with additional functionality that converts None to empty
513 we wrap this with additional functionality that converts None to empty
514 strings
514 strings
515 """
515 """
516 if s is None:
516 if s is None:
517 return markupsafe.Markup()
517 return markupsafe.Markup()
518 return markupsafe.escape(s)
518 return markupsafe.escape(s)
519
519
520
520
521 def add_events_routes(config):
521 def add_events_routes(config):
522 """
522 """
523 Adds routing that can be used in events. Because some events are triggered
523 Adds routing that can be used in events. Because some events are triggered
524 outside of pyramid context, we need to bootstrap request with some
524 outside of pyramid context, we need to bootstrap request with some
525 routing registered
525 routing registered
526 """
526 """
527
527
528 from rhodecode.apps._base import ADMIN_PREFIX
528 from rhodecode.apps._base import ADMIN_PREFIX
529
529
530 config.add_route(name='home', pattern='/')
530 config.add_route(name='home', pattern='/')
531
531
532 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
532 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
533 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
533 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
534 config.add_route(name='repo_summary', pattern='/{repo_name}')
534 config.add_route(name='repo_summary', pattern='/{repo_name}')
535 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
535 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
536 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
536 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
537
537
538 config.add_route(name='pullrequest_show',
538 config.add_route(name='pullrequest_show',
539 pattern='/{repo_name}/pull-request/{pull_request_id}')
539 pattern='/{repo_name}/pull-request/{pull_request_id}')
540 config.add_route(name='pull_requests_global',
540 config.add_route(name='pull_requests_global',
541 pattern='/pull-request/{pull_request_id}')
541 pattern='/pull-request/{pull_request_id}')
542 config.add_route(name='repo_commit',
542 config.add_route(name='repo_commit',
543 pattern='/{repo_name}/changeset/{commit_id}')
543 pattern='/{repo_name}/changeset/{commit_id}')
544
544
545 config.add_route(name='repo_files',
545 config.add_route(name='repo_files',
546 pattern='/{repo_name}/files/{commit_id}/{f_path}')
546 pattern='/{repo_name}/files/{commit_id}/{f_path}')
547
547
548
548
549 def bootstrap_config(request):
549 def bootstrap_config(request):
550 import pyramid.testing
550 import pyramid.testing
551 registry = pyramid.testing.Registry('RcTestRegistry')
551 registry = pyramid.testing.Registry('RcTestRegistry')
552
552
553 config = pyramid.testing.setUp(registry=registry, request=request)
553 config = pyramid.testing.setUp(registry=registry, request=request)
554
554
555 # allow pyramid lookup in testing
555 # allow pyramid lookup in testing
556 config.include('pyramid_mako')
556 config.include('pyramid_mako')
557 config.include('rhodecode.lib.rc_beaker')
557 config.include('rhodecode.lib.rc_beaker')
558 config.include('rhodecode.lib.rc_cache')
558 config.include('rhodecode.lib.rc_cache')
559
559
560 add_events_routes(config)
560 add_events_routes(config)
561
561
562 return config
562 return config
563
563
564
564
565 def bootstrap_request(**kwargs):
565 def bootstrap_request(**kwargs):
566 import pyramid.testing
566 import pyramid.testing
567
567
568 class TestRequest(pyramid.testing.DummyRequest):
568 class TestRequest(pyramid.testing.DummyRequest):
569 application_url = kwargs.pop('application_url', 'http://example.com')
569 application_url = kwargs.pop('application_url', 'http://example.com')
570 host = kwargs.pop('host', 'example.com:80')
570 host = kwargs.pop('host', 'example.com:80')
571 domain = kwargs.pop('domain', 'example.com')
571 domain = kwargs.pop('domain', 'example.com')
572
572
573 def translate(self, msg):
573 def translate(self, msg):
574 return msg
574 return msg
575
575
576 def plularize(self, singular, plural, n):
576 def plularize(self, singular, plural, n):
577 return singular
577 return singular
578
578
579 def get_partial_renderer(self, tmpl_name):
579 def get_partial_renderer(self, tmpl_name):
580
580
581 from rhodecode.lib.partial_renderer import get_partial_renderer
581 from rhodecode.lib.partial_renderer import get_partial_renderer
582 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
582 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
583
583
584 _call_context = TemplateArgs()
584 _call_context = TemplateArgs()
585 _call_context.visual = TemplateArgs()
585 _call_context.visual = TemplateArgs()
586 _call_context.visual.show_sha_length = 12
586 _call_context.visual.show_sha_length = 12
587 _call_context.visual.show_revision_number = True
587 _call_context.visual.show_revision_number = True
588
588
589 @property
589 @property
590 def call_context(self):
590 def call_context(self):
591 return self._call_context
591 return self._call_context
592
592
593 class TestDummySession(pyramid.testing.DummySession):
593 class TestDummySession(pyramid.testing.DummySession):
594 def save(*arg, **kw):
594 def save(*arg, **kw):
595 pass
595 pass
596
596
597 request = TestRequest(**kwargs)
597 request = TestRequest(**kwargs)
598 request.session = TestDummySession()
598 request.session = TestDummySession()
599
599
600 return request
600 return request
601
601
General Comments 0
You need to be logged in to leave comments. Login now