Show More
@@ -80,6 +80,18 b' class TestLoginController(object):' | |||
|
80 | 80 | assert username == 'test_regular' |
|
81 | 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 | 95 | def test_login_regular_forbidden_when_super_admin_restriction(self): |
|
84 | 96 | from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin |
|
85 | 97 | with fixture.auth_restriction(self.app._pyramid_registry, |
@@ -54,8 +54,8 b' CaptchaData = collections.namedtuple(' | |||
|
54 | 54 | 'CaptchaData', 'active, private_key, public_key') |
|
55 | 55 | |
|
56 | 56 | |
|
57 |
def store_user_in_session(session, user |
|
|
58 |
user = User.get_by_username(user |
|
|
57 | def store_user_in_session(session, user_identifier, remember=False): | |
|
58 | user = User.get_by_username_or_primary_email(user_identifier) | |
|
59 | 59 | auth_user = AuthUser(user.user_id) |
|
60 | 60 | auth_user.set_authenticated() |
|
61 | 61 | cs = auth_user.get_cookie_store() |
@@ -74,7 +74,7 b' def store_user_in_session(session, usern' | |||
|
74 | 74 | safe_cs = cs.copy() |
|
75 | 75 | safe_cs['password'] = '****' |
|
76 | 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 | 79 | # dumps session attrs back to cookie |
|
80 | 80 | session._update_cookie_out() |
@@ -181,7 +181,7 b' class LoginView(BaseAppView):' | |||
|
181 | 181 | # form checks for username/password, now we're authenticated |
|
182 | 182 | headers = store_user_in_session( |
|
183 | 183 | self.session, |
|
184 |
user |
|
|
184 | user_identifier=form_result['username'], | |
|
185 | 185 | remember=form_result['remember']) |
|
186 | 186 | log.debug('Redirecting to "%s" after login.', c.came_from) |
|
187 | 187 |
@@ -389,11 +389,7 b' class RhodeCodeAuthPluginBase(object):' | |||
|
389 | 389 | log.debug( |
|
390 | 390 | 'Trying to fetch user `%s` from RhodeCode database', username) |
|
391 | 391 | if username: |
|
392 | user = User.get_by_username(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) | |
|
392 | user = User.get_by_username_or_primary_email(username) | |
|
397 | 393 | else: |
|
398 | 394 | log.debug('provided username:`%s` is empty skipping...', username) |
|
399 | 395 | if not user: |
@@ -169,7 +169,7 b' class RhodeCodeAuthPlugin(RhodeCodeAuthP' | |||
|
169 | 169 | extra={"action": "user_auth_ok", "auth_module": "auth_rhodecode_anon", "username": userobj.username}) |
|
170 | 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 | 173 | log.info('user `%s` authenticated correctly', userobj.username, |
|
174 | 174 | extra={"action": "user_auth_ok", "auth_module": "auth_rhodecode", "username": userobj.username}) |
|
175 | 175 | return user_attrs |
@@ -35,7 +35,7 b' import collections' | |||
|
35 | 35 | |
|
36 | 36 | from sqlalchemy import ( |
|
37 | 37 | or_, and_, not_, func, cast, TypeDecorator, event, select, |
|
38 | true, false, null, | |
|
38 | true, false, null, union_all, | |
|
39 | 39 | Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column, |
|
40 | 40 | Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary, |
|
41 | 41 | Text, Float, PickleType, BigInteger) |
@@ -954,6 +954,12 b' class User(Base, BaseModel):' | |||
|
954 | 954 | return cls.execute(q).scalar_one_or_none() |
|
955 | 955 | |
|
956 | 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 | 963 | def get_by_auth_token(cls, auth_token, cache=False): |
|
958 | 964 | |
|
959 | 965 | q = cls.select(User)\ |
@@ -432,7 +432,7 b' def ValidAuth(localizer):' | |||
|
432 | 432 | |
|
433 | 433 | if not authenticate(username, password, '', HTTP_TYPE, |
|
434 | 434 | skip_missing=True): |
|
435 | user = User.get_by_username(username) | |
|
435 | user = User.get_by_username_or_primary_email(username) | |
|
436 | 436 | if user and not user.active: |
|
437 | 437 | log.warning('user %s is disabled', username) |
|
438 | 438 | msg = M(self, 'disabled_account', state) |
@@ -35,12 +35,12 b'' | |||
|
35 | 35 | <%block name="above_login_button" /> |
|
36 | 36 | <!-- login --> |
|
37 | 37 | <div class="sign-in-title"> |
|
38 |
<h1>${_('Sign In using |
|
|
38 | <h1>${_('Sign In using credentials')}</h1> | |
|
39 | 39 | </div> |
|
40 | 40 | <div class="inner form"> |
|
41 | 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 | 44 | ${h.text('username', class_='focus', value=defaults.get('username'))} |
|
45 | 45 | %if 'username' in errors: |
|
46 | 46 | <span class="error-message">${errors.get('username')}</span> |
General Comments 0
You need to be logged in to leave comments.
Login now