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, user |
|
57 | def store_user_in_session(session, user_identifier, remember=False): | |
58 |
user = User.get_by_username(user |
|
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', user |
|
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 |
user |
|
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 |
|
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