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