##// END OF EJS Templates
security fix, inspired by django security...
marcink -
r2678:04d2bcfb beta
parent child Browse files
Show More
@@ -1,183 +1,197 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import formencode
27 import formencode
28 import datetime
28 import datetime
29 import urlparse
29
30
30 from formencode import htmlfill
31 from formencode import htmlfill
31 from webob.exc import HTTPFound
32 from webob.exc import HTTPFound
32 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
33 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
34 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
35
36
36 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
37 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.model.db import User
40 from rhodecode.model.db import User
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
43
44
44
45
45
46
46 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
47
48
48
49
49 class LoginController(BaseController):
50 class LoginController(BaseController):
50
51
51 def __before__(self):
52 def __before__(self):
52 super(LoginController, self).__before__()
53 super(LoginController, self).__before__()
53
54
54 def index(self):
55 def index(self):
55 # redirect if already logged in
56 # redirect if already logged in
56 c.came_from = request.GET.get('came_from', None)
57 c.came_from = request.GET.get('came_from', None)
57
58
58 if self.rhodecode_user.is_authenticated \
59 if self.rhodecode_user.is_authenticated \
59 and self.rhodecode_user.username != 'default':
60 and self.rhodecode_user.username != 'default':
60
61
61 return redirect(url('home'))
62 return redirect(url('home'))
62
63
63 if request.POST:
64 if request.POST:
64 # import Login Form validator class
65 # import Login Form validator class
65 login_form = LoginForm()
66 login_form = LoginForm()
66 try:
67 try:
67 session.invalidate()
68 session.invalidate()
68 c.form_result = login_form.to_python(dict(request.POST))
69 c.form_result = login_form.to_python(dict(request.POST))
69 # form checks for username/password, now we're authenticated
70 # form checks for username/password, now we're authenticated
70 username = c.form_result['username']
71 username = c.form_result['username']
71 user = User.get_by_username(username, case_insensitive=True)
72 user = User.get_by_username(username, case_insensitive=True)
72 auth_user = AuthUser(user.user_id)
73 auth_user = AuthUser(user.user_id)
73 auth_user.set_authenticated()
74 auth_user.set_authenticated()
74 cs = auth_user.get_cookie_store()
75 cs = auth_user.get_cookie_store()
75 session['rhodecode_user'] = cs
76 session['rhodecode_user'] = cs
76 user.update_lastlogin()
77 user.update_lastlogin()
77 Session().commit()
78 Session().commit()
78
79
79 # If they want to be remembered, update the cookie
80 # If they want to be remembered, update the cookie
80 if c.form_result['remember'] is not False:
81 if c.form_result['remember'] is not False:
81 _year = (datetime.datetime.now() +
82 _year = (datetime.datetime.now() +
82 datetime.timedelta(seconds=60 * 60 * 24 * 365))
83 datetime.timedelta(seconds=60 * 60 * 24 * 365))
83 session._set_cookie_expires(_year)
84 session._set_cookie_expires(_year)
84
85
85 session.save()
86 session.save()
86
87
87 log.info('user %s is now authenticated and stored in '
88 log.info('user %s is now authenticated and stored in '
88 'session, session attrs %s' % (username, cs))
89 'session, session attrs %s' % (username, cs))
89
90
90 # dumps session attrs back to cookie
91 # dumps session attrs back to cookie
91 session._update_cookie_out()
92 session._update_cookie_out()
92
93
93 # we set new cookie
94 # we set new cookie
94 headers = None
95 headers = None
95 if session.request['set_cookie']:
96 if session.request['set_cookie']:
96 # send set-cookie headers back to response to update cookie
97 # send set-cookie headers back to response to update cookie
97 headers = [('Set-Cookie', session.request['cookie_out'])]
98 headers = [('Set-Cookie', session.request['cookie_out'])]
98
99
100 allowed_schemes = ['http', 'https', 'ftp']
101 parsed = urlparse.urlparse(c.came_from)
102 server_parsed = urlparse.urlparse(url.current())
103
104 if parsed.scheme and parsed.scheme not in allowed_schemes:
105 log.error('Suspicious URL scheme detected %s for url %s' %
106 (parsed.scheme, parsed))
107 c.came_from = url('home')
108 elif server_parsed.netloc != parsed.netloc:
109 log.error('Suspicious NETLOC detected %s for url %s'
110 'server url is: %s' %
111 (parsed.netloc, parsed, server_parsed))
112 c.came_from = url('home')
99 if c.came_from:
113 if c.came_from:
100 raise HTTPFound(location=c.came_from, headers=headers)
114 raise HTTPFound(location=c.came_from, headers=headers)
101 else:
115 else:
102 raise HTTPFound(location=url('home'), headers=headers)
116 raise HTTPFound(location=url('home'), headers=headers)
103
117
104 except formencode.Invalid, errors:
118 except formencode.Invalid, errors:
105 return htmlfill.render(
119 return htmlfill.render(
106 render('/login.html'),
120 render('/login.html'),
107 defaults=errors.value,
121 defaults=errors.value,
108 errors=errors.error_dict or {},
122 errors=errors.error_dict or {},
109 prefix_error=False,
123 prefix_error=False,
110 encoding="UTF-8")
124 encoding="UTF-8")
111
125
112 return render('/login.html')
126 return render('/login.html')
113
127
114 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
128 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
115 'hg.register.manual_activate')
129 'hg.register.manual_activate')
116 def register(self):
130 def register(self):
117 c.auto_active = False
131 c.auto_active = False
118 for perm in User.get_by_username('default').user_perms:
132 for perm in User.get_by_username('default').user_perms:
119 if perm.permission.permission_name == 'hg.register.auto_activate':
133 if perm.permission.permission_name == 'hg.register.auto_activate':
120 c.auto_active = True
134 c.auto_active = True
121 break
135 break
122
136
123 if request.POST:
137 if request.POST:
124
138
125 register_form = RegisterForm()()
139 register_form = RegisterForm()()
126 try:
140 try:
127 form_result = register_form.to_python(dict(request.POST))
141 form_result = register_form.to_python(dict(request.POST))
128 form_result['active'] = c.auto_active
142 form_result['active'] = c.auto_active
129 UserModel().create_registration(form_result)
143 UserModel().create_registration(form_result)
130 h.flash(_('You have successfully registered into rhodecode'),
144 h.flash(_('You have successfully registered into rhodecode'),
131 category='success')
145 category='success')
132 Session().commit()
146 Session().commit()
133 return redirect(url('login_home'))
147 return redirect(url('login_home'))
134
148
135 except formencode.Invalid, errors:
149 except formencode.Invalid, errors:
136 return htmlfill.render(
150 return htmlfill.render(
137 render('/register.html'),
151 render('/register.html'),
138 defaults=errors.value,
152 defaults=errors.value,
139 errors=errors.error_dict or {},
153 errors=errors.error_dict or {},
140 prefix_error=False,
154 prefix_error=False,
141 encoding="UTF-8")
155 encoding="UTF-8")
142
156
143 return render('/register.html')
157 return render('/register.html')
144
158
145 def password_reset(self):
159 def password_reset(self):
146 if request.POST:
160 if request.POST:
147 password_reset_form = PasswordResetForm()()
161 password_reset_form = PasswordResetForm()()
148 try:
162 try:
149 form_result = password_reset_form.to_python(dict(request.POST))
163 form_result = password_reset_form.to_python(dict(request.POST))
150 UserModel().reset_password_link(form_result)
164 UserModel().reset_password_link(form_result)
151 h.flash(_('Your password reset link was sent'),
165 h.flash(_('Your password reset link was sent'),
152 category='success')
166 category='success')
153 return redirect(url('login_home'))
167 return redirect(url('login_home'))
154
168
155 except formencode.Invalid, errors:
169 except formencode.Invalid, errors:
156 return htmlfill.render(
170 return htmlfill.render(
157 render('/password_reset.html'),
171 render('/password_reset.html'),
158 defaults=errors.value,
172 defaults=errors.value,
159 errors=errors.error_dict or {},
173 errors=errors.error_dict or {},
160 prefix_error=False,
174 prefix_error=False,
161 encoding="UTF-8")
175 encoding="UTF-8")
162
176
163 return render('/password_reset.html')
177 return render('/password_reset.html')
164
178
165 def password_reset_confirmation(self):
179 def password_reset_confirmation(self):
166 if request.GET and request.GET.get('key'):
180 if request.GET and request.GET.get('key'):
167 try:
181 try:
168 user = User.get_by_api_key(request.GET.get('key'))
182 user = User.get_by_api_key(request.GET.get('key'))
169 data = dict(email=user.email)
183 data = dict(email=user.email)
170 UserModel().reset_password(data)
184 UserModel().reset_password(data)
171 h.flash(_('Your password reset was successful, '
185 h.flash(_('Your password reset was successful, '
172 'new password has been sent to your email'),
186 'new password has been sent to your email'),
173 category='success')
187 category='success')
174 except Exception, e:
188 except Exception, e:
175 log.error(e)
189 log.error(e)
176 return redirect(url('reset_password'))
190 return redirect(url('reset_password'))
177
191
178 return redirect(url('login_home'))
192 return redirect(url('login_home'))
179
193
180 def logout(self):
194 def logout(self):
181 session.delete()
195 session.delete()
182 log.info('Logging out and deleting session for user')
196 log.info('Logging out and deleting session for user')
183 redirect(url('home'))
197 redirect(url('home'))
General Comments 0
You need to be logged in to leave comments. Login now