##// END OF EJS Templates
release: merge back stable branch into default
marcink -
r210:57b11684 merge default
parent child Browse files
Show More
@@ -0,0 +1,16 b''
1 |RCE| 4.1.2 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2016-06-16
8
9 Fixes
10 ^^^^^
11
12 - ssl: fixed http middleware so it works correctly with pyramid views. This
13 fixed http -> https redirection problems on login.
14
15 - ldap: fixed ldap usergroup authentication plugin so after upgrade it's
16 possible to change the settings again (EE only).
@@ -1,4 +1,5 b''
1 1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
2 2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
3 3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
4 4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
@@ -1,81 +1,82 b''
1 1 .. _rhodecode-release-notes-ref:
2 2
3 3 Release Notes
4 4 =============
5 5
6 6 |RCE| 4.x Versions
7 7 ------------------
8 8
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-4.1.2.rst
12 13 release-notes-4.1.1.rst
13 14 release-notes-4.1.0.rst
14 15 release-notes-4.0.1.rst
15 16 release-notes-4.0.0.rst
16 17
17 18 |RCE| 3.x Versions
18 19 ------------------
19 20
20 21 .. toctree::
21 22 :maxdepth: 1
22 23
23 24 release-notes-3.8.4.rst
24 25 release-notes-3.8.3.rst
25 26 release-notes-3.8.2.rst
26 27 release-notes-3.8.1.rst
27 28 release-notes-3.8.0.rst
28 29 release-notes-3.7.1.rst
29 30 release-notes-3.7.0.rst
30 31 release-notes-3.6.1.rst
31 32 release-notes-3.6.0.rst
32 33 release-notes-3.5.2.rst
33 34 release-notes-3.5.1.rst
34 35 release-notes-3.5.0.rst
35 36 release-notes-3.4.1.rst
36 37 release-notes-3.4.0.rst
37 38 release-notes-3.3.4.rst
38 39 release-notes-3.3.3.rst
39 40 release-notes-3.3.2.rst
40 41 release-notes-3.3.1.rst
41 42 release-notes-3.3.0.rst
42 43 release-notes-3.2.3.rst
43 44 release-notes-3.2.2.rst
44 45 release-notes-3.2.1.rst
45 46 release-notes-3.2.0.rst
46 47 release-notes-3.1.1.rst
47 48 release-notes-3.1.0.rst
48 49 release-notes-3.0.2.rst
49 50 release-notes-3.0.1.rst
50 51 release-notes-3.0.0.rst
51 52
52 53 |RCE| 2.x Versions
53 54 ------------------
54 55
55 56 .. toctree::
56 57 :maxdepth: 1
57 58
58 59 release-notes-2.2.8.rst
59 60 release-notes-2.2.7.rst
60 61 release-notes-2.2.6.rst
61 62 release-notes-2.2.5.rst
62 63 release-notes-2.2.4.rst
63 64 release-notes-2.2.3.rst
64 65 release-notes-2.2.2.rst
65 66 release-notes-2.2.1.rst
66 67 release-notes-2.2.0.rst
67 68 release-notes-2.1.0.rst
68 69 release-notes-2.0.2.rst
69 70 release-notes-2.0.1.rst
70 71 release-notes-2.0.0.rst
71 72
72 73 |RCE| 1.x Versions
73 74 ------------------
74 75
75 76 .. toctree::
76 77 :maxdepth: 1
77 78
78 79 release-notes-1.7.2.rst
79 80 release-notes-1.7.1.rst
80 81 release-notes-1.7.0.rst
81 82 release-notes-1.6.0.rst
@@ -1,386 +1,387 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Pylons middleware initialization
23 23 """
24 24 import logging
25 25
26 26 from paste.registry import RegistryManager
27 27 from paste.gzipper import make_gzip_middleware
28 28 from pylons.wsgiapp import PylonsApp
29 29 from pyramid.authorization import ACLAuthorizationPolicy
30 30 from pyramid.config import Configurator
31 31 from pyramid.static import static_view
32 32 from pyramid.settings import asbool, aslist
33 33 from pyramid.wsgi import wsgiapp
34 34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
35 35 import pyramid.httpexceptions as httpexceptions
36 36 from pyramid.renderers import render_to_response, render
37 37 from routes.middleware import RoutesMiddleware
38 38 import routes.util
39 39
40 40 import rhodecode
41 41 from rhodecode.config import patches
42 42 from rhodecode.config.environment import (
43 43 load_environment, load_pyramid_environment)
44 44 from rhodecode.lib.middleware import csrf
45 45 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
46 46 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
47 47 from rhodecode.lib.middleware.https_fixup import HttpsFixup
48 48 from rhodecode.lib.middleware.vcs import VCSMiddleware
49 49 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
50 50
51 51
52 52 log = logging.getLogger(__name__)
53 53
54 54
55 55 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
56 56 """Create a Pylons WSGI application and return it
57 57
58 58 ``global_conf``
59 59 The inherited configuration for this application. Normally from
60 60 the [DEFAULT] section of the Paste ini file.
61 61
62 62 ``full_stack``
63 63 Whether or not this application provides a full WSGI stack (by
64 64 default, meaning it handles its own exceptions and errors).
65 65 Disable full_stack when this application is "managed" by
66 66 another WSGI middleware.
67 67
68 68 ``app_conf``
69 69 The application's local configuration. Normally specified in
70 70 the [app:<name>] section of the Paste ini file (where <name>
71 71 defaults to main).
72 72
73 73 """
74 74 # Apply compatibility patches
75 75 patches.kombu_1_5_1_python_2_7_11()
76 76 patches.inspect_getargspec()
77 77
78 78 # Configure the Pylons environment
79 79 config = load_environment(global_conf, app_conf)
80 80
81 81 # The Pylons WSGI app
82 82 app = PylonsApp(config=config)
83 83 if rhodecode.is_test:
84 84 app = csrf.CSRFDetector(app)
85 85
86 86 expected_origin = config.get('expected_origin')
87 87 if expected_origin:
88 88 # The API can be accessed from other Origins.
89 89 app = csrf.OriginChecker(app, expected_origin,
90 90 skip_urls=[routes.util.url_for('api')])
91 91
92 92
93 93 if asbool(full_stack):
94 94
95 95 # Appenlight monitoring and error handler
96 96 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
97 97
98 98 # we want our low level middleware to get to the request ASAP. We don't
99 99 # need any pylons stack middleware in them
100 100 app = VCSMiddleware(app, config, appenlight_client)
101 101
102 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
103 app = HttpsFixup(app, config)
104
105 102 # Establish the Registry for this application
106 103 app = RegistryManager(app)
107 104
108 105 app.config = config
109 106
110 107 return app
111 108
112 109
113 110 def make_pyramid_app(global_config, **settings):
114 111 """
115 112 Constructs the WSGI application based on Pyramid and wraps the Pylons based
116 113 application.
117 114
118 115 Specials:
119 116
120 117 * We migrate from Pylons to Pyramid. While doing this, we keep both
121 118 frameworks functional. This involves moving some WSGI middlewares around
122 119 and providing access to some data internals, so that the old code is
123 120 still functional.
124 121
125 122 * The application can also be integrated like a plugin via the call to
126 123 `includeme`. This is accompanied with the other utility functions which
127 124 are called. Changing this should be done with great care to not break
128 125 cases when these fragments are assembled from another place.
129 126
130 127 """
131 128 # The edition string should be available in pylons too, so we add it here
132 129 # before copying the settings.
133 130 settings.setdefault('rhodecode.edition', 'Community Edition')
134 131
135 132 # As long as our Pylons application does expect "unprepared" settings, make
136 133 # sure that we keep an unmodified copy. This avoids unintentional change of
137 134 # behavior in the old application.
138 135 settings_pylons = settings.copy()
139 136
140 137 sanitize_settings_and_apply_defaults(settings)
141 138 config = Configurator(settings=settings)
142 139 add_pylons_compat_data(config.registry, global_config, settings_pylons)
143 140
144 141 load_pyramid_environment(global_config, settings)
145 142
146 143 includeme(config)
147 144 includeme_last(config)
148 145 pyramid_app = config.make_wsgi_app()
149 146 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
150 147 return pyramid_app
151 148
152 149
153 150 def add_pylons_compat_data(registry, global_config, settings):
154 151 """
155 152 Attach data to the registry to support the Pylons integration.
156 153 """
157 154 registry._pylons_compat_global_config = global_config
158 155 registry._pylons_compat_settings = settings
159 156
160 157
161 158 def webob_to_pyramid_http_response(webob_response):
162 159 ResponseClass = httpexceptions.status_map[webob_response.status_int]
163 160 pyramid_response = ResponseClass(webob_response.status)
164 161 pyramid_response.status = webob_response.status
165 162 pyramid_response.headers.update(webob_response.headers)
166 163 if pyramid_response.headers['content-type'] == 'text/html':
167 164 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
168 165 return pyramid_response
169 166
170 167
171 168 def error_handler(exception, request):
172 169 # TODO: dan: replace the old pylons error controller with this
173 170 from rhodecode.model.settings import SettingsModel
174 171 from rhodecode.lib.utils2 import AttributeDict
175 172
176 173 try:
177 174 rc_config = SettingsModel().get_all_settings()
178 175 except Exception:
179 176 log.exception('failed to fetch settings')
180 177 rc_config = {}
181 178
182 179 base_response = HTTPInternalServerError()
183 180 # prefer original exception for the response since it may have headers set
184 181 if isinstance(exception, HTTPError):
185 182 base_response = exception
186 183
187 184 c = AttributeDict()
188 185 c.error_message = base_response.status
189 186 c.error_explanation = base_response.explanation or str(base_response)
190 187 c.visual = AttributeDict()
191 188
192 189 c.visual.rhodecode_support_url = (
193 190 request.registry.settings.get('rhodecode_support_url') or
194 191 request.route_url('rhodecode_support')
195 192 )
196 193 c.redirect_time = 0
197 194 c.rhodecode_name = rc_config.get('rhodecode_title', '')
198 195 if not c.rhodecode_name:
199 196 c.rhodecode_name = 'Rhodecode'
200 197
201 198 response = render_to_response(
202 199 '/errors/error_document.html', {'c': c}, request=request,
203 200 response=base_response)
204 201
205 202 return response
206 203
207 204
208 205 def includeme(config):
209 206 settings = config.registry.settings
210 207
211 208 if asbool(settings.get('appenlight', 'false')):
212 209 config.include('appenlight_client.ext.pyramid_tween')
213 210
214 211 # Includes which are required. The application would fail without them.
215 212 config.include('pyramid_mako')
216 213 config.include('pyramid_beaker')
217 214 config.include('rhodecode.admin')
218 215 config.include('rhodecode.authentication')
219 216 config.include('rhodecode.login')
220 217 config.include('rhodecode.tweens')
221 218 config.include('rhodecode.api')
222 219 config.add_route(
223 220 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
224 221
225 222 # Set the authorization policy.
226 223 authz_policy = ACLAuthorizationPolicy()
227 224 config.set_authorization_policy(authz_policy)
228 225
229 226 # Set the default renderer for HTML templates to mako.
230 227 config.add_mako_renderer('.html')
231 228
232 229 # plugin information
233 230 config.registry.rhodecode_plugins = {}
234 231
235 232 config.add_directive(
236 233 'register_rhodecode_plugin', register_rhodecode_plugin)
237 234 # include RhodeCode plugins
238 235 includes = aslist(settings.get('rhodecode.includes', []))
239 236 for inc in includes:
240 237 config.include(inc)
241 238
242 239 pylons_app = make_app(
243 240 config.registry._pylons_compat_global_config,
244 241 **config.registry._pylons_compat_settings)
245 242 config.registry._pylons_compat_config = pylons_app.config
246 243
247 244 pylons_app_as_view = wsgiapp(pylons_app)
248 245
249 246 # Protect from VCS Server error related pages when server is not available
250 247 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
251 248 if not vcs_server_enabled:
252 249 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
253 250
254 251
255 252 def pylons_app_with_error_handler(context, request):
256 253 """
257 254 Handle exceptions from rc pylons app:
258 255
259 256 - old webob type exceptions get converted to pyramid exceptions
260 257 - pyramid exceptions are passed to the error handler view
261 258 """
262 259 try:
263 260 response = pylons_app_as_view(context, request)
264 261 if 400 <= response.status_int <= 599: # webob type error responses
265 262 return error_handler(
266 263 webob_to_pyramid_http_response(response), request)
267 264 except HTTPError as e: # pyramid type exceptions
268 265 return error_handler(e, request)
269 266 except Exception:
270 267 if settings.get('debugtoolbar.enabled', False):
271 268 raise
272 269 return error_handler(HTTPInternalServerError(), request)
273 270 return response
274 271
275 272 # This is the glue which allows us to migrate in chunks. By registering the
276 273 # pylons based application as the "Not Found" view in Pyramid, we will
277 274 # fallback to the old application each time the new one does not yet know
278 275 # how to handle a request.
279 276 config.add_notfound_view(pylons_app_with_error_handler)
280 277
281 278 if settings.get('debugtoolbar.enabled', False):
282 279 # if toolbar, then only http type exceptions get caught and rendered
283 280 ExcClass = HTTPError
284 281 else:
285 282 # if no toolbar, then any exception gets caught and rendered
286 283 ExcClass = Exception
287 284 config.add_view(error_handler, context=ExcClass)
288 285
289 286
290 287 def includeme_last(config):
291 288 """
292 289 The static file catchall needs to be last in the view configuration.
293 290 """
294 291 settings = config.registry.settings
295 292
296 293 # Note: johbo: I would prefer to register a prefix for static files at some
297 294 # point, e.g. move them under '_static/'. This would fully avoid that we
298 295 # can have name clashes with a repository name. Imaging someone calling his
299 296 # repo "css" ;-) Also having an external web server to serve out the static
300 297 # files seems to be easier to set up if they have a common prefix.
301 298 #
302 299 # Example: config.add_static_view('_static', path='rhodecode:public')
303 300 #
304 301 # It might be an option to register both paths for a while and then migrate
305 302 # over to the new location.
306 303
307 304 # Serving static files with a catchall.
308 305 if settings['static_files']:
309 306 config.add_route('catchall_static', '/*subpath')
310 307 config.add_view(
311 308 static_view('rhodecode:public'), route_name='catchall_static')
312 309
313 310
314 311 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
315 312 """
316 313 Apply outer WSGI middlewares around the application.
317 314
318 315 Part of this has been moved up from the Pylons layer, so that the
319 316 data is also available if old Pylons code is hit through an already ported
320 317 view.
321 318 """
322 319 settings = config.registry.settings
323 320
321 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
322 pyramid_app = HttpsFixup(pyramid_app, settings)
323
324 324 # Add RoutesMiddleware to support the pylons compatibility tween during
325
325 326 # migration to pyramid.
326 327 pyramid_app = RoutesMiddleware(
327 328 pyramid_app, config.registry._pylons_compat_config['routes.map'])
328 329
329 330 if asbool(settings.get('appenlight', 'false')):
330 331 pyramid_app, _ = wrap_in_appenlight_if_enabled(
331 332 pyramid_app, config.registry._pylons_compat_config)
332 333
333 334 # TODO: johbo: Don't really see why we enable the gzip middleware when
334 335 # serving static files, might be something that should have its own setting
335 336 # as well?
336 337 if settings['static_files']:
337 338 pyramid_app = make_gzip_middleware(
338 339 pyramid_app, settings, compress_level=1)
339 340
340 341 return pyramid_app
341 342
342 343
343 344 def sanitize_settings_and_apply_defaults(settings):
344 345 """
345 346 Applies settings defaults and does all type conversion.
346 347
347 348 We would move all settings parsing and preparation into this place, so that
348 349 we have only one place left which deals with this part. The remaining parts
349 350 of the application would start to rely fully on well prepared settings.
350 351
351 352 This piece would later be split up per topic to avoid a big fat monster
352 353 function.
353 354 """
354 355
355 356 # Pyramid's mako renderer has to search in the templates folder so that the
356 357 # old templates still work. Ported and new templates are expected to use
357 358 # real asset specifications for the includes.
358 359 mako_directories = settings.setdefault('mako.directories', [
359 360 # Base templates of the original Pylons application
360 361 'rhodecode:templates',
361 362 ])
362 363 log.debug(
363 364 "Using the following Mako template directories: %s",
364 365 mako_directories)
365 366
366 367 # Default includes, possible to change as a user
367 368 pyramid_includes = settings.setdefault('pyramid.includes', [
368 369 'rhodecode.lib.middleware.request_wrapper',
369 370 ])
370 371 log.debug(
371 372 "Using the following pyramid.includes: %s",
372 373 pyramid_includes)
373 374
374 375 # TODO: johbo: Re-think this, usually the call to config.include
375 376 # should allow to pass in a prefix.
376 377 settings.setdefault('rhodecode.api.url', '/_admin/api')
377 378
378 379 _bool_setting(settings, 'vcs.server.enable', 'true')
379 380 _bool_setting(settings, 'static_files', 'true')
380 381 _bool_setting(settings, 'is_test', 'false')
381 382
382 383 return settings
383 384
384 385
385 386 def _bool_setting(settings, name, default):
386 387 settings[name] = asbool(settings.get(name, default))
@@ -1,339 +1,340 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import datetime
22 22 import formencode
23 23 import logging
24 24 import urlparse
25 25
26 26 from pylons import url
27 27 from pyramid.httpexceptions import HTTPFound
28 28 from pyramid.view import view_config
29 29 from recaptcha.client.captcha import submit
30 30
31 31 from rhodecode.authentication.base import authenticate, HTTP_TYPE
32 32 from rhodecode.events import UserRegistered
33 33 from rhodecode.lib.auth import (
34 34 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
35 35 from rhodecode.lib.base import get_ip_addr
36 36 from rhodecode.lib.exceptions import UserCreationError
37 37 from rhodecode.lib.utils2 import safe_str
38 38 from rhodecode.model.db import User
39 39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 40 from rhodecode.model.login_session import LoginSession
41 41 from rhodecode.model.meta import Session
42 42 from rhodecode.model.settings import SettingsModel
43 43 from rhodecode.model.user import UserModel
44 44 from rhodecode.translation import _
45 45
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 def _store_user_in_session(session, username, remember=False):
51 51 user = User.get_by_username(username, case_insensitive=True)
52 52 auth_user = AuthUser(user.user_id)
53 53 auth_user.set_authenticated()
54 54 cs = auth_user.get_cookie_store()
55 55 session['rhodecode_user'] = cs
56 56 user.update_lastlogin()
57 57 Session().commit()
58 58
59 59 # If they want to be remembered, update the cookie
60 60 if remember:
61 61 _year = (datetime.datetime.now() +
62 62 datetime.timedelta(seconds=60 * 60 * 24 * 365))
63 63 session._set_cookie_expires(_year)
64 64
65 65 session.save()
66 66
67 67 log.info('user %s is now authenticated and stored in '
68 68 'session, session attrs %s', username, cs)
69 69
70 70 # dumps session attrs back to cookie
71 71 session._update_cookie_out()
72 72 # we set new cookie
73 73 headers = None
74 74 if session.request['set_cookie']:
75 75 # send set-cookie headers back to response to update cookie
76 76 headers = [('Set-Cookie', session.request['cookie_out'])]
77 77 return headers
78 78
79 79
80 80 def get_came_from(request):
81 81 came_from = safe_str(request.GET.get('came_from', ''))
82 82 parsed = urlparse.urlparse(came_from)
83 83 allowed_schemes = ['http', 'https']
84 84 if parsed.scheme and parsed.scheme not in allowed_schemes:
85 85 log.error('Suspicious URL scheme detected %s for url %s' %
86 86 (parsed.scheme, parsed))
87 87 came_from = url('home')
88 88 elif parsed.netloc and request.host != parsed.netloc:
89 89 log.error('Suspicious NETLOC detected %s for url %s server url '
90 90 'is: %s' % (parsed.netloc, parsed, request.host))
91 91 came_from = url('home')
92 92 elif any(bad_str in parsed.path for bad_str in ('\r', '\n')):
93 93 log.error('Header injection detected `%s` for url %s server url ' %
94 94 (parsed.path, parsed))
95 95 came_from = url('home')
96 96
97 97 return came_from or url('home')
98 98
99 99
100 100 class LoginView(object):
101 101
102 102 def __init__(self, context, request):
103 103 self.request = request
104 104 self.context = context
105 105 self.session = request.session
106 106 self._rhodecode_user = request.user
107 107
108 108 def _get_template_context(self):
109 109 return {
110 110 'came_from': get_came_from(self.request),
111 111 'defaults': {},
112 112 'errors': {},
113 113 }
114 114
115 115 @view_config(
116 116 route_name='login', request_method='GET',
117 117 renderer='rhodecode:templates/login.html')
118 118 def login(self):
119 119 came_from = get_came_from(self.request)
120 120 user = self.request.user
121 121
122 122 # redirect if already logged in
123 123 if user.is_authenticated and not user.is_default and user.ip_allowed:
124 124 raise HTTPFound(came_from)
125 125
126 126 # check if we use headers plugin, and try to login using it.
127 127 try:
128 128 log.debug('Running PRE-AUTH for headers based authentication')
129 129 auth_info = authenticate(
130 130 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
131 131 if auth_info:
132 132 headers = _store_user_in_session(
133 133 self.session, auth_info.get('username'))
134 134 raise HTTPFound(came_from, headers=headers)
135 135 except UserCreationError as e:
136 136 log.error(e)
137 137 self.session.flash(e, queue='error')
138 138
139 139 return self._get_template_context()
140 140
141 141 @view_config(
142 142 route_name='login', request_method='POST',
143 143 renderer='rhodecode:templates/login.html')
144 144 def login_post(self):
145 145 came_from = get_came_from(self.request)
146 146 session = self.request.session
147 147 login_form = LoginForm()()
148 148
149 149 try:
150 150 session.invalidate()
151 151 form_result = login_form.to_python(self.request.params)
152 152 # form checks for username/password, now we're authenticated
153 153 headers = _store_user_in_session(
154 154 self.session,
155 155 username=form_result['username'],
156 156 remember=form_result['remember'])
157 log.debug('Redirecting to "%s" after login.', came_from)
157 158 raise HTTPFound(came_from, headers=headers)
158 159 except formencode.Invalid as errors:
159 160 defaults = errors.value
160 161 # remove password from filling in form again
161 162 del defaults['password']
162 163 render_ctx = self._get_template_context()
163 164 render_ctx.update({
164 165 'errors': errors.error_dict,
165 166 'defaults': defaults,
166 167 })
167 168 return render_ctx
168 169
169 170 except UserCreationError as e:
170 171 # headers auth or other auth functions that create users on
171 172 # the fly can throw this exception signaling that there's issue
172 173 # with user creation, explanation should be provided in
173 174 # Exception itself
174 175 session.flash(e, queue='error')
175 176 return self._get_template_context()
176 177
177 178 @CSRFRequired()
178 179 @view_config(route_name='logout', request_method='POST')
179 180 def logout(self):
180 181 LoginSession().destroy_user_session()
181 182 return HTTPFound(url('home'))
182 183
183 184 @HasPermissionAnyDecorator(
184 185 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
185 186 @view_config(
186 187 route_name='register', request_method='GET',
187 188 renderer='rhodecode:templates/register.html',)
188 189 def register(self, defaults=None, errors=None):
189 190 defaults = defaults or {}
190 191 errors = errors or {}
191 192
192 193 settings = SettingsModel().get_all_settings()
193 194 captcha_public_key = settings.get('rhodecode_captcha_public_key')
194 195 captcha_private_key = settings.get('rhodecode_captcha_private_key')
195 196 captcha_active = bool(captcha_private_key)
196 197 register_message = settings.get('rhodecode_register_message') or ''
197 198 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
198 199 .AuthUser.permissions['global']
199 200
200 201 render_ctx = self._get_template_context()
201 202 render_ctx.update({
202 203 'defaults': defaults,
203 204 'errors': errors,
204 205 'auto_active': auto_active,
205 206 'captcha_active': captcha_active,
206 207 'captcha_public_key': captcha_public_key,
207 208 'register_message': register_message,
208 209 })
209 210 return render_ctx
210 211
211 212 @HasPermissionAnyDecorator(
212 213 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
213 214 @view_config(
214 215 route_name='register', request_method='POST',
215 216 renderer='rhodecode:templates/register.html')
216 217 def register_post(self):
217 218 captcha_private_key = SettingsModel().get_setting_by_name(
218 219 'rhodecode_captcha_private_key')
219 220 captcha_active = bool(captcha_private_key)
220 221 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
221 222 .AuthUser.permissions['global']
222 223
223 224 register_form = RegisterForm()()
224 225 try:
225 226 form_result = register_form.to_python(self.request.params)
226 227 form_result['active'] = auto_active
227 228
228 229 if captcha_active:
229 230 response = submit(
230 231 self.request.params.get('recaptcha_challenge_field'),
231 232 self.request.params.get('recaptcha_response_field'),
232 233 private_key=captcha_private_key,
233 234 remoteip=get_ip_addr(self.request.environ))
234 235 if captcha_active and not response.is_valid:
235 236 _value = form_result
236 237 _msg = _('bad captcha')
237 238 error_dict = {'recaptcha_field': _msg}
238 239 raise formencode.Invalid(_msg, _value, None,
239 240 error_dict=error_dict)
240 241
241 242 new_user = UserModel().create_registration(form_result)
242 243 event = UserRegistered(user=new_user, session=self.session)
243 244 self.request.registry.notify(event)
244 245 self.session.flash(
245 246 _('You have successfully registered with RhodeCode'),
246 247 queue='success')
247 248 Session().commit()
248 249
249 250 redirect_ro = self.request.route_path('login')
250 251 raise HTTPFound(redirect_ro)
251 252
252 253 except formencode.Invalid as errors:
253 254 del errors.value['password']
254 255 del errors.value['password_confirmation']
255 256 return self.register(
256 257 defaults=errors.value, errors=errors.error_dict)
257 258
258 259 except UserCreationError as e:
259 260 # container auth or other auth functions that create users on
260 261 # the fly can throw this exception signaling that there's issue
261 262 # with user creation, explanation should be provided in
262 263 # Exception itself
263 264 self.session.flash(e, queue='error')
264 265 return self.register()
265 266
266 267 @view_config(
267 268 route_name='reset_password', request_method=('GET', 'POST'),
268 269 renderer='rhodecode:templates/password_reset.html')
269 270 def password_reset(self):
270 271 settings = SettingsModel().get_all_settings()
271 272 captcha_private_key = settings.get('rhodecode_captcha_private_key')
272 273 captcha_active = bool(captcha_private_key)
273 274 captcha_public_key = settings.get('rhodecode_captcha_public_key')
274 275
275 276 render_ctx = {
276 277 'captcha_active': captcha_active,
277 278 'captcha_public_key': captcha_public_key,
278 279 'defaults': {},
279 280 'errors': {},
280 281 }
281 282
282 283 if self.request.POST:
283 284 password_reset_form = PasswordResetForm()()
284 285 try:
285 286 form_result = password_reset_form.to_python(
286 287 self.request.params)
287 288 if captcha_active:
288 289 response = submit(
289 290 self.request.params.get('recaptcha_challenge_field'),
290 291 self.request.params.get('recaptcha_response_field'),
291 292 private_key=captcha_private_key,
292 293 remoteip=get_ip_addr(self.request.environ))
293 294 if captcha_active and not response.is_valid:
294 295 _value = form_result
295 296 _msg = _('bad captcha')
296 297 error_dict = {'recaptcha_field': _msg}
297 298 raise formencode.Invalid(_msg, _value, None,
298 299 error_dict=error_dict)
299 300
300 301 # Generate reset URL and send mail.
301 302 user_email = form_result['email']
302 303 user = User.get_by_email(user_email)
303 304 password_reset_url = self.request.route_url(
304 305 'reset_password_confirmation',
305 306 _query={'key': user.api_key})
306 307 UserModel().reset_password_link(
307 308 form_result, password_reset_url)
308 309
309 310 # Display success message and redirect.
310 311 self.session.flash(
311 312 _('Your password reset link was sent'),
312 313 queue='success')
313 314 return HTTPFound(self.request.route_path('login'))
314 315
315 316 except formencode.Invalid as errors:
316 317 render_ctx.update({
317 318 'defaults': errors.value,
318 319 'errors': errors.error_dict,
319 320 })
320 321
321 322 return render_ctx
322 323
323 324 @view_config(route_name='reset_password_confirmation',
324 325 request_method='GET')
325 326 def password_reset_confirmation(self):
326 327 if self.request.GET and self.request.GET.get('key'):
327 328 try:
328 329 user = User.get_by_auth_token(self.request.GET.get('key'))
329 330 data = {'email': user.email}
330 331 UserModel().reset_password(data)
331 332 self.session.flash(
332 333 _('Your password reset was successful, '
333 334 'a new password has been sent to your email'),
334 335 queue='success')
335 336 except Exception as e:
336 337 log.error(e)
337 338 return HTTPFound(self.request.route_path('reset_password'))
338 339
339 340 return HTTPFound(self.request.route_path('login'))
General Comments 0
You need to be logged in to leave comments. Login now