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