##// END OF EJS Templates
merge
marcink -
r1419:febb3f95 merge beta
parent child Browse files
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -294,6 +294,10 b' def make_map(config):'
294 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
294 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
295 controller='login', action='password_reset')
295 controller='login', action='password_reset')
296
296
297 rmap.connect('reset_password_confirmation',
298 '%s/password_reset_confirmation' % ADMIN_PREFIX,
299 controller='login', action='password_reset_confirmation')
300
297 #FEEDS
301 #FEEDS
298 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
302 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
299 controller='feed', action='rss',
303 controller='feed', action='rss',
@@ -129,8 +129,8 b' class LoginController(BaseController):'
129 password_reset_form = PasswordResetForm()()
129 password_reset_form = PasswordResetForm()()
130 try:
130 try:
131 form_result = password_reset_form.to_python(dict(request.POST))
131 form_result = password_reset_form.to_python(dict(request.POST))
132 user_model.reset_password(form_result)
132 user_model.reset_password_link(form_result)
133 h.flash(_('Your new password was sent'),
133 h.flash(_('Your password reset link was sent'),
134 category='success')
134 category='success')
135 return redirect(url('login_home'))
135 return redirect(url('login_home'))
136
136
@@ -144,6 +144,23 b' class LoginController(BaseController):'
144
144
145 return render('/password_reset.html')
145 return render('/password_reset.html')
146
146
147 def password_reset_confirmation(self):
148
149 if request.GET and request.GET.get('key'):
150 try:
151 user_model = UserModel()
152 user = User.get_by_api_key(request.GET.get('key'))
153 data = dict(email=user.email)
154 user_model.reset_password(data)
155 h.flash(_('Your password reset was successful, '
156 'new password has been sent to your email'),
157 category='success')
158 except Exception, e:
159 log.error(e)
160 return redirect(url('reset_password'))
161
162 return redirect(url('login_home'))
163
147 def logout(self):
164 def logout(self):
148 del session['rhodecode_user']
165 del session['rhodecode_user']
149 session.save()
166 session.save()
@@ -34,7 +34,7 b' from time import mktime'
34 from operator import itemgetter
34 from operator import itemgetter
35 from string import lower
35 from string import lower
36
36
37 from pylons import config
37 from pylons import config, url
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
40 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
@@ -249,6 +249,45 b' def get_commits_stats(repo_name, ts_min_'
249 log.info('LockHeld')
249 log.info('LockHeld')
250 return 'Task with key %s already running' % lockkey
250 return 'Task with key %s already running' % lockkey
251
251
252 @task(ignore_result=True)
253 def send_password_link(user_email):
254 try:
255 log = reset_user_password.get_logger()
256 except:
257 log = logging.getLogger(__name__)
258
259 from rhodecode.lib import auth
260 from rhodecode.model.db import User
261
262 try:
263 sa = get_session()
264 user = sa.query(User).filter(User.email == user_email).scalar()
265
266 if user:
267 link = url('reset_password_confirmation', key=user.api_key,
268 qualified=True)
269 tmpl = """
270 Hello %s
271
272 We received a request to create a new password for your account.
273
274 You can generate it by clicking following URL:
275
276 %s
277
278 If you didn't request new password please ignore this email.
279 """
280 run_task(send_email, user_email,
281 "RhodeCode password reset link",
282 tmpl % (user.short_contact, link))
283 log.info('send new password mail to %s', user_email)
284
285 except:
286 log.error('Failed to update user password')
287 log.error(traceback.format_exc())
288 return False
289
290 return True
252
291
253 @task(ignore_result=True)
292 @task(ignore_result=True)
254 def reset_user_password(user_email):
293 def reset_user_password(user_email):
@@ -280,8 +319,8 b' def reset_user_password(user_email):'
280 sa.rollback()
319 sa.rollback()
281
320
282 run_task(send_email, user_email,
321 run_task(send_email, user_email,
283 "Your new rhodecode password",
322 "Your new RhodeCode password",
284 'Your new rhodecode password:%s' % (new_passwd))
323 'Your new RhodeCode password:%s' % (new_passwd))
285 log.info('send new password mail to %s', user_email)
324 log.info('send new password mail to %s', user_email)
286
325
287 except:
326 except:
@@ -74,13 +74,19 b' class SmtpMailer(object):'
74
74
75 date_ = formatdate(localtime=True)
75 date_ = formatdate(localtime=True)
76 msg = MIMEMultipart()
76 msg = MIMEMultipart()
77 msg.set_type('multipart/alternative')
78 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
79
80 text_msg = MIMEText(body)
81 text_msg.set_type('text/plain')
82 text_msg.set_param('charset', 'UTF-8')
83
77 msg['From'] = self.mail_from
84 msg['From'] = self.mail_from
78 msg['To'] = ','.join(recipients)
85 msg['To'] = ','.join(recipients)
79 msg['Date'] = date_
86 msg['Date'] = date_
80 msg['Subject'] = subject
87 msg['Subject'] = subject
81 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
82
88
83 msg.attach(MIMEText(body))
89 msg.attach(text_msg)
84
90
85 if attachment_files:
91 if attachment_files:
86 self.__atach_files(msg, attachment_files)
92 self.__atach_files(msg, attachment_files)
@@ -243,6 +243,11 b' class User(Base, BaseModel):'
243 else:
243 else:
244 return Session.query(cls).filter(cls.username == username).one()
244 return Session.query(cls).filter(cls.username == username).one()
245
245
246 @classmethod
247 def get_by_api_key(cls, api_key):
248 return Session.query(cls).filter(cls.api_key == api_key).one()
249
250
246 def update_lastlogin(self):
251 def update_lastlogin(self):
247 """Update user lastlogin"""
252 """Update user lastlogin"""
248
253
@@ -213,6 +213,10 b' class UserModel(BaseModel):'
213 self.sa.rollback()
213 self.sa.rollback()
214 raise
214 raise
215
215
216 def reset_password_link(self, data):
217 from rhodecode.lib.celerylib import tasks, run_task
218 run_task(tasks.send_password_link, data['email'])
219
216 def reset_password(self, data):
220 def reset_password(self, data):
217 from rhodecode.lib.celerylib import tasks, run_task
221 from rhodecode.lib.celerylib import tasks, run_task
218 run_task(tasks.reset_user_password, data['email'])
222 run_task(tasks.reset_user_password, data['email'])
@@ -198,7 +198,8 b' margin-bottom:5px !important;'
198 -moz-border-radius: 0px 0px 8px 8px;
198 -moz-border-radius: 0px 0px 8px 8px;
199 border-radius: 0px 0px 8px 8px;
199 border-radius: 0px 0px 8px 8px;
200 height:37px;
200 height:37px;
201 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367
201 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
202 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
202 }
203 }
203
204
204 #header ul#logged-user li {
205 #header ul#logged-user li {
@@ -1383,6 +1384,13 b' position: absolute;'
1383 margin-left: -16px;
1384 margin-left: -16px;
1384 width: 281px;
1385 width: 281px;
1385 border-radius: 0 0 8px 8px;
1386 border-radius: 0 0 8px 8px;
1387 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1388 }
1389
1390 #quick_login .password_forgoten{
1391 padding-right:10px;
1392 padding-top:10px;
1393 float:left;
1386 }
1394 }
1387
1395
1388 #quick_login div.form div.fields{
1396 #quick_login div.form div.fields{
@@ -30,7 +30,7 b''
30
30
31 </div>
31 </div>
32 <div class="buttons">
32 <div class="buttons">
33 ${h.submit('sign_in','Sign In',class_="ui-button")}
33 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>${h.submit('sign_in','Sign In',class_="ui-button")}
34 </div>
34 </div>
35 </div>
35 </div>
36 </div>
36 </div>
@@ -28,7 +28,7 b''
28 <div class="buttons">
28 <div class="buttons">
29 <div class="nohighlight">
29 <div class="nohighlight">
30 ${h.submit('send','Reset my password',class_="ui-button")}
30 ${h.submit('send','Reset my password',class_="ui-button")}
31 <div class="activation_msg">${_('Your new password will be send to matching email address')}</div>
31 <div class="activation_msg">${_('Password reset link will be send to matching email address')}</div>
32 </div>
32 </div>
33 </div>
33 </div>
34 </div>
34 </div>
@@ -1,6 +1,7 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 from rhodecode.tests import *
2 from rhodecode.tests import *
3 from rhodecode.model.db import User
3 from rhodecode.model.db import User
4 from rhodecode.lib import generate_api_key
4 from rhodecode.lib.auth import check_password
5 from rhodecode.lib.auth import check_password
5
6
6
7
@@ -8,39 +9,42 b' class TestLoginController(TestController'
8
9
9 def test_index(self):
10 def test_index(self):
10 response = self.app.get(url(controller='login', action='index'))
11 response = self.app.get(url(controller='login', action='index'))
11 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
12 self.assertEqual(response.status, '200 OK')
12 # Test response...
13 # Test response...
13
14
14 def test_login_admin_ok(self):
15 def test_login_admin_ok(self):
15 response = self.app.post(url(controller='login', action='index'),
16 response = self.app.post(url(controller='login', action='index'),
16 {'username':'test_admin',
17 {'username':'test_admin',
17 'password':'test12'})
18 'password':'test12'})
18 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
19 self.assertEqual(response.status, '302 Found')
19 assert response.session['rhodecode_user'].username == 'test_admin', 'wrong logged in user'
20 self.assertEqual(response.session['rhodecode_user'].username ,
21 'test_admin')
20 response = response.follow()
22 response = response.follow()
21 assert '%s repository' % HG_REPO in response.body
23 self.assertTrue('%s repository' % HG_REPO in response.body)
22
24
23 def test_login_regular_ok(self):
25 def test_login_regular_ok(self):
24 response = self.app.post(url(controller='login', action='index'),
26 response = self.app.post(url(controller='login', action='index'),
25 {'username':'test_regular',
27 {'username':'test_regular',
26 'password':'test12'})
28 'password':'test12'})
27 print response
29
28 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
30 self.assertEqual(response.status, '302 Found')
29 assert response.session['rhodecode_user'].username == 'test_regular', 'wrong logged in user'
31 self.assertEqual(response.session['rhodecode_user'].username ,
32 'test_regular')
30 response = response.follow()
33 response = response.follow()
31 assert '%s repository' % HG_REPO in response.body
34 self.assertTrue('%s repository' % HG_REPO in response.body)
32 assert '<a title="Admin" href="/_admin">' not in response.body
35 self.assertTrue('<a title="Admin" href="/_admin">' not in response.body)
33
36
34 def test_login_ok_came_from(self):
37 def test_login_ok_came_from(self):
35 test_came_from = '/_admin/users'
38 test_came_from = '/_admin/users'
36 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
39 response = self.app.post(url(controller='login', action='index',
40 came_from=test_came_from),
37 {'username':'test_admin',
41 {'username':'test_admin',
38 'password':'test12'})
42 'password':'test12'})
39 assert response.status == '302 Found', 'Wrong response code from came from redirection'
43 self.assertEqual(response.status, '302 Found')
40 response = response.follow()
44 response = response.follow()
41
45
42 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
46 self.assertEqual(response.status, '200 OK')
43 assert 'Users administration' in response.body, 'No proper title in response'
47 self.assertTrue('Users administration' in response.body)
44
48
45
49
46 def test_login_short_password(self):
50 def test_login_short_password(self):
@@ -48,24 +52,24 b' class TestLoginController(TestController'
48 {'username':'test_admin',
52 {'username':'test_admin',
49 'password':'as'})
53 'password':'as'})
50 self.assertEqual(response.status, '200 OK')
54 self.assertEqual(response.status, '200 OK')
51 print response.body
55
52 self.assertTrue('Enter 3 characters or more' in response.body)
56 self.assertTrue('Enter 3 characters or more' in response.body)
53
57
54 def test_login_wrong_username_password(self):
58 def test_login_wrong_username_password(self):
55 response = self.app.post(url(controller='login', action='index'),
59 response = self.app.post(url(controller='login', action='index'),
56 {'username':'error',
60 {'username':'error',
57 'password':'test12'})
61 'password':'test12'})
58 assert response.status == '200 OK', 'Wrong response from login page'
62 self.assertEqual(response.status , '200 OK')
59
63
60 assert 'invalid user name' in response.body, 'No error username message in response'
64 self.assertTrue('invalid user name' in response.body)
61 assert 'invalid password' in response.body, 'No error password message in response'
65 self.assertTrue('invalid password' in response.body)
62
66
63 #==========================================================================
67 #==========================================================================
64 # REGISTRATIONS
68 # REGISTRATIONS
65 #==========================================================================
69 #==========================================================================
66 def test_register(self):
70 def test_register(self):
67 response = self.app.get(url(controller='login', action='register'))
71 response = self.app.get(url(controller='login', action='register'))
68 assert 'Sign Up to RhodeCode' in response.body, 'wrong page for user registration'
72 self.assertTrue('Sign Up to RhodeCode' in response.body)
69
73
70 def test_register_err_same_username(self):
74 def test_register_err_same_username(self):
71 response = self.app.post(url(controller='login', action='register'),
75 response = self.app.post(url(controller='login', action='register'),
@@ -76,8 +80,8 b' class TestLoginController(TestController'
76 'name':'test',
80 'name':'test',
77 'lastname':'test'})
81 'lastname':'test'})
78
82
79 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
83 self.assertEqual(response.status , '200 OK')
80 assert 'This username already exists' in response.body
84 self.assertTrue('This username already exists' in response.body)
81
85
82 def test_register_err_same_email(self):
86 def test_register_err_same_email(self):
83 response = self.app.post(url(controller='login', action='register'),
87 response = self.app.post(url(controller='login', action='register'),
@@ -88,7 +92,7 b' class TestLoginController(TestController'
88 'name':'test',
92 'name':'test',
89 'lastname':'test'})
93 'lastname':'test'})
90
94
91 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
95 self.assertEqual(response.status , '200 OK')
92 assert 'This e-mail address is already taken' in response.body
96 assert 'This e-mail address is already taken' in response.body
93
97
94 def test_register_err_same_email_case_sensitive(self):
98 def test_register_err_same_email_case_sensitive(self):
@@ -99,7 +103,7 b' class TestLoginController(TestController'
99 'email':'TesT_Admin@mail.COM',
103 'email':'TesT_Admin@mail.COM',
100 'name':'test',
104 'name':'test',
101 'lastname':'test'})
105 'lastname':'test'})
102 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
106 self.assertEqual(response.status , '200 OK')
103 assert 'This e-mail address is already taken' in response.body
107 assert 'This e-mail address is already taken' in response.body
104
108
105 def test_register_err_wrong_data(self):
109 def test_register_err_wrong_data(self):
@@ -110,7 +114,7 b' class TestLoginController(TestController'
110 'email':'goodmailm',
114 'email':'goodmailm',
111 'name':'test',
115 'name':'test',
112 'lastname':'test'})
116 'lastname':'test'})
113 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
117 self.assertEqual(response.status , '200 OK')
114 assert 'An email address must contain a single @' in response.body
118 assert 'An email address must contain a single @' in response.body
115 assert 'Enter a value 6 characters long or more' in response.body
119 assert 'Enter a value 6 characters long or more' in response.body
116
120
@@ -124,8 +128,7 b' class TestLoginController(TestController'
124 'name':'test',
128 'name':'test',
125 'lastname':'test'})
129 'lastname':'test'})
126
130
127 print response.body
131 self.assertEqual(response.status , '200 OK')
128 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
129 assert 'An email address must contain a single @' in response.body
132 assert 'An email address must contain a single @' in response.body
130 assert ('Username may only contain '
133 assert ('Username may only contain '
131 'alphanumeric characters underscores, '
134 'alphanumeric characters underscores, '
@@ -141,7 +144,7 b' class TestLoginController(TestController'
141 'name':'test',
144 'name':'test',
142 'lastname':'test'})
145 'lastname':'test'})
143
146
144 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
147 self.assertEqual(response.status , '200 OK')
145 assert 'An email address must contain a single @' in response.body
148 assert 'An email address must contain a single @' in response.body
146 assert 'This username already exists' in response.body
149 assert 'This username already exists' in response.body
147
150
@@ -156,8 +159,7 b' class TestLoginController(TestController'
156 'name':'test',
159 'name':'test',
157 'lastname':'test'})
160 'lastname':'test'})
158
161
159 print response.body
162 self.assertEqual(response.status , '200 OK')
160 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
161 assert 'Invalid characters in password' in response.body
163 assert 'Invalid characters in password' in response.body
162
164
163
165
@@ -170,8 +172,7 b' class TestLoginController(TestController'
170 'name':'test',
172 'name':'test',
171 'lastname':'test'})
173 'lastname':'test'})
172
174
173 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
175 self.assertEqual(response.status , '200 OK')
174 print response.body
175 assert 'Password do not match' in response.body
176 assert 'Password do not match' in response.body
176
177
177 def test_register_ok(self):
178 def test_register_ok(self):
@@ -188,7 +189,7 b' class TestLoginController(TestController'
188 'email':email,
189 'email':email,
189 'name':name,
190 'name':name,
190 'lastname':lastname})
191 'lastname':lastname})
191 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
192 self.assertEqual(response.status , '302 Found')
192 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
193 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
193
194
194 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
195 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
@@ -206,8 +207,9 b' class TestLoginController(TestController'
206 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
207 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
207
208
208 def test_forgot_password(self):
209 def test_forgot_password(self):
209 response = self.app.get(url(controller='login', action='password_reset'))
210 response = self.app.get(url(controller='login',
210 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
211 action='password_reset'))
212 self.assertEqual(response.status , '200 OK')
211
213
212 username = 'test_password_reset_1'
214 username = 'test_password_reset_1'
213 password = 'qweqwe'
215 password = 'qweqwe'
@@ -215,16 +217,45 b' class TestLoginController(TestController'
215 name = 'passwd'
217 name = 'passwd'
216 lastname = 'reset'
218 lastname = 'reset'
217
219
218 response = self.app.post(url(controller='login', action='register'),
220 new = User()
219 {'username':username,
221 new.username = username
220 'password':password,
222 new.password = password
221 'password_confirmation':password,
223 new.email = email
222 'email':email,
224 new.name = name
223 'name':name,
225 new.lastname = lastname
224 'lastname':lastname})
226 new.api_key = generate_api_key(username)
225 #register new user for email test
227 self.sa.add(new)
226 response = self.app.post(url(controller='login', action='password_reset'),
228 self.sa.commit()
227 {'email':email, })
229
228 print response.session['flash']
230 response = self.app.post(url(controller='login',
229 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
231 action='password_reset'),
230 assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
232 {'email':email, })
233
234 self.checkSessionFlash(response, 'Your password reset link was sent')
235
236 response = response.follow()
237
238 # BAD KEY
239
240 key = "bad"
241 response = self.app.get(url(controller='login',
242 action='password_reset_confirmation',
243 key=key))
244 self.assertEqual(response.status, '302 Found')
245 self.assertTrue(response.location.endswith(url('reset_password')))
246
247 # GOOD KEY
248
249 key = User.by_username(username).api_key
250
251 response = self.app.get(url(controller='login',
252 action='password_reset_confirmation',
253 key=key))
254 self.assertEqual(response.status, '302 Found')
255 self.assertTrue(response.location.endswith(url('login_home')))
256
257 self.checkSessionFlash(response,
258 ('Your password reset was successful, '
259 'new password has been sent to your email'))
260
261 response = response.follow()
@@ -6,10 +6,10 b' tag_svn_revision = true'
6 find_links = http://www.pylonshq.com/download/
6 find_links = http://www.pylonshq.com/download/
7
7
8 [nosetests]
8 [nosetests]
9 verbose=False
9 verbose=True
10 verbosity=2
10 verbosity=2
11 with-pylons=test.ini
11 with-pylons=test.ini
12 detailed-errors=0
12 detailed-errors=1
13 nologcapture=1
13 nologcapture=1
14
14
15 # Babel configuration
15 # Babel configuration
General Comments 0
You need to be logged in to leave comments. Login now