##// END OF EJS Templates
feat(login by email option): added ability to log in with user primary email. Fixes: RCCE-63
ilin.s -
r5358:2095c653 default
parent child Browse files
Show More
@@ -80,6 +80,18 b' class TestLoginController(object):'
80 assert username == 'test_regular'
80 assert username == 'test_regular'
81 response.mustcontain('logout')
81 response.mustcontain('logout')
82
82
83 def test_login_with_primary_email(self):
84 user_email = 'test_regular@mail.com'
85 response = self.app.post(route_path('login'),
86 {'username': user_email,
87 'password': 'test12'}, status=302)
88 response = response.follow()
89 session = response.get_session_from_response()
90 user = session['rhodecode_user']
91 assert user['username'] == user_email.split('@')[0]
92 assert user['is_authenticated']
93 response.mustcontain('logout')
94
83 def test_login_regular_forbidden_when_super_admin_restriction(self):
95 def test_login_regular_forbidden_when_super_admin_restriction(self):
84 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
96 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
85 with fixture.auth_restriction(self.app._pyramid_registry,
97 with fixture.auth_restriction(self.app._pyramid_registry,
@@ -54,8 +54,8 b' CaptchaData = collections.namedtuple('
54 'CaptchaData', 'active, private_key, public_key')
54 'CaptchaData', 'active, private_key, public_key')
55
55
56
56
57 def store_user_in_session(session, username, remember=False):
57 def store_user_in_session(session, user_identifier, remember=False):
58 user = User.get_by_username(username, case_insensitive=True)
58 user = User.get_by_username_or_primary_email(user_identifier)
59 auth_user = AuthUser(user.user_id)
59 auth_user = AuthUser(user.user_id)
60 auth_user.set_authenticated()
60 auth_user.set_authenticated()
61 cs = auth_user.get_cookie_store()
61 cs = auth_user.get_cookie_store()
@@ -74,7 +74,7 b' def store_user_in_session(session, usern'
74 safe_cs = cs.copy()
74 safe_cs = cs.copy()
75 safe_cs['password'] = '****'
75 safe_cs['password'] = '****'
76 log.info('user %s is now authenticated and stored in '
76 log.info('user %s is now authenticated and stored in '
77 'session, session attrs %s', username, safe_cs)
77 'session, session attrs %s', user_identifier, safe_cs)
78
78
79 # dumps session attrs back to cookie
79 # dumps session attrs back to cookie
80 session._update_cookie_out()
80 session._update_cookie_out()
@@ -181,7 +181,7 b' class LoginView(BaseAppView):'
181 # form checks for username/password, now we're authenticated
181 # form checks for username/password, now we're authenticated
182 headers = store_user_in_session(
182 headers = store_user_in_session(
183 self.session,
183 self.session,
184 username=form_result['username'],
184 user_identifier=form_result['username'],
185 remember=form_result['remember'])
185 remember=form_result['remember'])
186 log.debug('Redirecting to "%s" after login.', c.came_from)
186 log.debug('Redirecting to "%s" after login.', c.came_from)
187
187
@@ -389,11 +389,7 b' class RhodeCodeAuthPluginBase(object):'
389 log.debug(
389 log.debug(
390 'Trying to fetch user `%s` from RhodeCode database', username)
390 'Trying to fetch user `%s` from RhodeCode database', username)
391 if username:
391 if username:
392 user = User.get_by_username(username)
392 user = User.get_by_username_or_primary_email(username)
393 if not user:
394 log.debug('User not found, fallback to fetch user in '
395 'case insensitive mode')
396 user = User.get_by_username(username, case_insensitive=True)
397 else:
393 else:
398 log.debug('provided username:`%s` is empty skipping...', username)
394 log.debug('provided username:`%s` is empty skipping...', username)
399 if not user:
395 if not user:
@@ -169,7 +169,7 b' class RhodeCodeAuthPlugin(RhodeCodeAuthP'
169 extra={"action": "user_auth_ok", "auth_module": "auth_rhodecode_anon", "username": userobj.username})
169 extra={"action": "user_auth_ok", "auth_module": "auth_rhodecode_anon", "username": userobj.username})
170 return user_attrs
170 return user_attrs
171
171
172 elif userobj.username == username and password_match:
172 elif (userobj.username == username or userobj.email == username) and password_match:
173 log.info('user `%s` authenticated correctly', userobj.username,
173 log.info('user `%s` authenticated correctly', userobj.username,
174 extra={"action": "user_auth_ok", "auth_module": "auth_rhodecode", "username": userobj.username})
174 extra={"action": "user_auth_ok", "auth_module": "auth_rhodecode", "username": userobj.username})
175 return user_attrs
175 return user_attrs
@@ -35,7 +35,7 b' import collections'
35
35
36 from sqlalchemy import (
36 from sqlalchemy import (
37 or_, and_, not_, func, cast, TypeDecorator, event, select,
37 or_, and_, not_, func, cast, TypeDecorator, event, select,
38 true, false, null,
38 true, false, null, union_all,
39 Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column,
39 Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column,
40 Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary,
40 Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary,
41 Text, Float, PickleType, BigInteger)
41 Text, Float, PickleType, BigInteger)
@@ -954,6 +954,12 b' class User(Base, BaseModel):'
954 return cls.execute(q).scalar_one_or_none()
954 return cls.execute(q).scalar_one_or_none()
955
955
956 @classmethod
956 @classmethod
957 def get_by_username_or_primary_email(cls, user_identifier):
958 qs = union_all(cls.select().where(func.lower(cls.username) == func.lower(user_identifier)),
959 cls.select().where(func.lower(cls.email) == func.lower(user_identifier)))
960 return cls.execute(cls.select(User).from_statement(qs)).scalar_one_or_none()
961
962 @classmethod
957 def get_by_auth_token(cls, auth_token, cache=False):
963 def get_by_auth_token(cls, auth_token, cache=False):
958
964
959 q = cls.select(User)\
965 q = cls.select(User)\
@@ -432,7 +432,7 b' def ValidAuth(localizer):'
432
432
433 if not authenticate(username, password, '', HTTP_TYPE,
433 if not authenticate(username, password, '', HTTP_TYPE,
434 skip_missing=True):
434 skip_missing=True):
435 user = User.get_by_username(username)
435 user = User.get_by_username_or_primary_email(username)
436 if user and not user.active:
436 if user and not user.active:
437 log.warning('user %s is disabled', username)
437 log.warning('user %s is disabled', username)
438 msg = M(self, 'disabled_account', state)
438 msg = M(self, 'disabled_account', state)
@@ -35,12 +35,12 b''
35 <%block name="above_login_button" />
35 <%block name="above_login_button" />
36 <!-- login -->
36 <!-- login -->
37 <div class="sign-in-title">
37 <div class="sign-in-title">
38 <h1>${_('Sign In using username/password')}</h1>
38 <h1>${_('Sign In using credentials')}</h1>
39 </div>
39 </div>
40 <div class="inner form">
40 <div class="inner form">
41 ${h.form(request.route_path('login', _query={'came_from': c.came_from}), needs_csrf_token=False)}
41 ${h.form(request.route_path('login', _query={'came_from': c.came_from}), needs_csrf_token=False)}
42
42
43 <label for="username">${_('Username')}:</label>
43 <label for="username">${_('Username or email address')}:</label>
44 ${h.text('username', class_='focus', value=defaults.get('username'))}
44 ${h.text('username', class_='focus', value=defaults.get('username'))}
45 %if 'username' in errors:
45 %if 'username' in errors:
46 <span class="error-message">${errors.get('username')}</span>
46 <span class="error-message">${errors.get('username')}</span>
General Comments 0
You need to be logged in to leave comments. Login now