diff --git a/auth/auth.py b/auth/auth.py index 0e5457a36..715b5a0dd 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -70,7 +70,7 @@ def process_basic_auth(auth): logger.debug('Invalid basic auth format.') return - credentials = b64decode(normalized[1]).split(':', 1) + credentials = [part.decode('utf-8') for part in b64decode(normalized[1]).split(':', 1)] if len(credentials) != 2: logger.debug('Invalid basic auth credential format.') diff --git a/data/users.py b/data/users.py index e5064b6fd..7cb83a619 100644 --- a/data/users.py +++ b/data/users.py @@ -12,6 +12,9 @@ class DatabaseUsers(object): """ Simply delegate to the model implementation. """ return model.verify_user(username_or_email, password) + def user_exists(self, username): + return model.get_user(username) is not None + class LDAPConnection(object): def __init__(self, ldap_uri, user_dn, user_pw): @@ -40,15 +43,9 @@ class LDAPUsers(object): self._email_attr = email_attr self._passwd_attr = passwd_attr - def verify_user(self, username_or_email, password): - """ Verify the credentials with LDAP and if they are valid, create or update the user - in our database. """ - - # Make sure that even if the server supports anonymous binds, we don't allow it - if not password: - return None - + def _ldap_user_search(self, username_or_email): with self._ldap_conn as conn: + logger.debug('Incoming username or email param: %s', username_or_email.__repr__()) user_search_dn = ','.join(self._user_rdn + self._base_dn) query = u'(|({0}={2})({1}={2}))'.format(self._uid_attr, self._email_attr, username_or_email) @@ -57,41 +54,60 @@ class LDAPUsers(object): if len(user) != 1: return None - found_dn, found_response = user[0] + return user[0] - # First validate the password by binding as the user - try: - with LDAPConnection(self._ldap_uri, found_dn, password.encode('utf-8')): - pass - except ldap.INVALID_CREDENTIALS: + def verify_user(self, username_or_email, password): + """ Verify the credentials with LDAP and if they are valid, create or update the user + in our database. """ + + # Make sure that even if the server supports anonymous binds, we don't allow it + if not password: + return None + + found_user = self._ldap_user_search(username_or_email) + + if found_user is None: + return None + + found_dn, found_response = found_user + + # First validate the password by binding as the user + try: + with LDAPConnection(self._ldap_uri, found_dn, password.encode('utf-8')): + pass + except ldap.INVALID_CREDENTIALS: + return None + + # Now check if we have a federated login for this user + username = found_response[self._uid_attr][0].decode('utf-8') + email = found_response[self._email_attr][0] + db_user = model.verify_federated_login('ldap', username) + + if not db_user: + # We must create the user in our db + valid_username = None + for valid_username in generate_valid_usernames(username): + if model.is_username_unique(valid_username): + break + + if not valid_username: + logger.error('Unable to pick a username for user: %s', username) return None - # Now check if we have a federated login for this user - username = unicode(found_response[self._uid_attr][0].decode('utf-8')) - email = found_response[self._email_attr][0] - db_user = model.verify_federated_login('ldap', username) + db_user = model.create_user(valid_username, None, email, add_change_pw_notification=False) + db_user.verified = True + model.attach_federated_login(db_user, 'ldap', username) + else: + # Update the db attributes from ldap + db_user.email = email - if not db_user: - # We must create the user in our db - valid_username = None - for valid_username in generate_valid_usernames(username): - if model.is_username_unique(valid_username): - break + db_user.save() - if not valid_username: - logger.error('Unable to pick a username for user: %s', username) - return None + return db_user - db_user = model.create_user(valid_username, None, email, add_change_pw_notification=False) - db_user.verified = True - model.attach_federated_login(db_user, 'ldap', username) - else: - # Update the db attributes from ldap - db_user.email = email - - db_user.save() - - return db_user + def user_exists(self, username): + found_user = self._ldap_user_search(username) + return found_user is not None class UserAuthentication(object): diff --git a/endpoints/index.py b/endpoints/index.py index f0d233414..ea60ff56e 100644 --- a/endpoints/index.py +++ b/endpoints/index.py @@ -95,8 +95,7 @@ def create_user(): abort(400, 'Invalid robot account or password.', issue='robot-login-failure') - existing_user = model.get_user(username) - if existing_user: + if authentication.user_exists(username): verified = authentication.verify_user(username, password) if verified: # Mark that the user was logged in. diff --git a/requirements.txt b/requirements.txt index 7951f5dd8..264879298 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ PyGithub==1.24.1 PyMySQL==0.6.2 PyPDF2==1.21 SQLAlchemy==0.9.4 +Unidecode==0.04.16 Werkzeug==0.9.4 alembic==0.6.4 aniso8601==0.82 @@ -40,6 +41,7 @@ pycrypto==2.6.1 python-daemon==1.6 python-dateutil==2.2 python-digitalocean==0.7 +python-ldap==2.4.15 python-magic==0.4.6 pytz==2014.2 raven==4.2.1