Example #1
0
 def test_user_exists(self):
     """
     Verify that user_exists function returns correct response.
     """
     # Create users from factory
     UserFactory(username='******', email='*****@*****.**')
     assert user_exists({'username': '******', 'email': '*****@*****.**'})
     assert user_exists({'username': '******'})
     assert user_exists({'email': '*****@*****.**'})
     assert not user_exists({'username': '******'})
     assert user_exists({'username': '******'})
Example #2
0
def ensure_user_information(
        strategy,
        auth_entry,
        backend=None,
        user=None,
        social=None,
        current_partial=None,  # lint-amnesty, pylint: disable=keyword-arg-before-vararg
        allow_inactive_user=False,
        details=None,
        *args,
        **kwargs):
    """
    Ensure that we have the necessary information about a user (either an
    existing account or registration data) to proceed with the pipeline.
    """

    # We're deliberately verbose here to make it clear what the intended
    # dispatch behavior is for the various pipeline entry points, given the
    # current state of the pipeline. Keep in mind the pipeline is re-entrant
    # and values will change on repeated invocations (for example, the first
    # time through the login flow the user will be None so we dispatch to the
    # login form; the second time it will have a value so we continue to the
    # next pipeline step directly).
    #
    # It is important that we always execute the entire pipeline. Even if
    # behavior appears correct without executing a step, it means important
    # invariants have been violated and future misbehavior is likely.
    def dispatch_to_login():
        """Redirects to the login page."""
        return redirect(AUTH_DISPATCH_URLS[AUTH_ENTRY_LOGIN])

    def dispatch_to_register():
        """Redirects to the registration page."""
        return redirect(AUTH_DISPATCH_URLS[AUTH_ENTRY_REGISTER])

    def should_force_account_creation():
        """ For some third party providers, we auto-create user accounts """
        current_provider = provider.Registry.get_from_pipeline({
            'backend':
            current_partial.backend,
            'kwargs':
            kwargs
        })
        return (current_provider
                and (current_provider.skip_email_verification
                     or current_provider.send_to_registration_first))

    def is_provider_saml():
        """ Verify that the third party provider uses SAML """
        current_provider = provider.Registry.get_from_pipeline({
            'backend':
            current_partial.backend,
            'kwargs':
            kwargs
        })
        saml_providers_list = list(
            provider.Registry.get_enabled_by_backend_name('tpa-saml'))
        return (current_provider and current_provider.slug in [
            saml_provider.slug for saml_provider in saml_providers_list
        ])

    if current_partial:
        strategy.session_set('partial_pipeline_token_', current_partial.token)
        strategy.storage.partial.store(current_partial)

    if not user:
        # Use only email for user existence check in case of saml provider
        if is_provider_saml():
            user_details = {'email': details.get('email')} if details else None
        else:
            user_details = details
        if user_exists(user_details or {}):
            # User has not already authenticated and the details sent over from
            # identity provider belong to an existing user.
            return dispatch_to_login()

        if is_api(auth_entry):
            return HttpResponseBadRequest()
        elif auth_entry == AUTH_ENTRY_LOGIN:
            # User has authenticated with the third party provider but we don't know which edX
            # account corresponds to them yet, if any.
            if should_force_account_creation():
                return dispatch_to_register()
            return dispatch_to_login()
        elif auth_entry == AUTH_ENTRY_REGISTER:
            # User has authenticated with the third party provider and now wants to finish
            # creating their edX account.
            return dispatch_to_register()
        elif auth_entry == AUTH_ENTRY_ACCOUNT_SETTINGS:
            raise AuthEntryError(
                backend, 'auth_entry is wrong. Settings requires a user.')
        elif auth_entry in AUTH_ENTRY_CUSTOM:
            # Pass the username, email, etc. via query params to the custom entry page:
            return redirect_to_custom_form(strategy.request, auth_entry,
                                           details or {}, kwargs)
        else:
            raise AuthEntryError(backend, 'auth_entry invalid')

    if not user.is_active:
        # The user account has not been verified yet.
        if allow_inactive_user:
            # This parameter is used by the auth_exchange app, which always allows users to
            # login, whether or not their account is validated.
            pass
        elif social is None:
            # The user has just registered a new account as part of this pipeline. Their account
            # is inactive but we allow the login to continue, because if we pause again to force
            # the user to activate their account via email, the pipeline may get lost (e.g.
            # email takes too long to arrive, user opens the activation email on a different
            # device, etc.). This is consistent with first party auth and ensures that the
            # pipeline completes fully, which is critical.
            pass
        else:
            # This is an existing account, linked to a third party provider but not activated.
            # Double-check these criteria:
            assert user is not None
            assert social is not None
            # We now also allow them to login again, because if they had entered their email
            # incorrectly then there would be no way for them to recover the account, nor
            # register anew via SSO. See SOL-1324 in JIRA.
            # However, we will log a warning for this case:
            logger.warning(
                '[THIRD_PARTY_AUTH] User is using third_party_auth to login but has not yet activated their account. '
                'Username: {username}'.format(username=user.username))
Example #3
0
def get_username(strategy, details, backend, user=None, *args, **kwargs):  # lint-amnesty, pylint: disable=keyword-arg-before-vararg
    """
    Copy of social_core.pipeline.user.get_username to achieve
    1. additional logging
    2. case insensitive username checks
    3. enforce same maximum and minimum length restrictions we have in `user_api/accounts`
    """
    if 'username' not in backend.setting('USER_FIELDS', USER_FIELDS):
        return
    storage = strategy.storage

    if not user:
        email_as_username = strategy.setting('USERNAME_IS_FULL_EMAIL', False)
        uuid_length = strategy.setting('UUID_LENGTH', 16)
        min_length = strategy.setting('USERNAME_MIN_LENGTH',
                                      accounts.USERNAME_MIN_LENGTH)
        max_length = strategy.setting('USERNAME_MAX_LENGTH',
                                      accounts.USERNAME_MAX_LENGTH)
        do_slugify = strategy.setting('SLUGIFY_USERNAMES', False)
        do_clean = strategy.setting('CLEAN_USERNAMES', True)

        if do_clean:
            override_clean = strategy.setting('CLEAN_USERNAME_FUNCTION')
            if override_clean:
                clean_func = module_member(override_clean)
            else:
                clean_func = storage.user.clean_username
        else:
            clean_func = lambda val: val

        if do_slugify:
            override_slug = strategy.setting('SLUGIFY_FUNCTION')
            if override_slug:
                slug_func = module_member(override_slug)
            else:
                slug_func = slugify
        else:
            slug_func = lambda val: val

        if email_as_username and details.get('email'):
            username = details['email']
        elif details.get('username'):
            username = details['username']
        else:
            username = uuid4().hex

        short_username = (username[:max_length - uuid_length]
                          if max_length is not None else username)
        final_username = slug_func(clean_func(username[:max_length]))

        # Generate a unique username for current user using username
        # as base but adding a unique hash at the end. Original
        # username is cut to avoid any field max_length.
        # The final_username may be empty and will skip the loop.
        # We are using our own version of user_exists to avoid possible case sensitivity issues.
        while not final_username or len(
                final_username) < min_length or user_exists(
                    {'username': final_username}):
            # adding a dash between user-supplied and system-generated values to avoid weird combinations
            username = short_username + '-' + username_suffix_generator(
                uuid_length)
            final_username = slug_func(clean_func(username[:max_length]))
            logger.info(
                '[THIRD_PARTY_AUTH] New username generated. Username: {username}'
                .format(username=final_username))
    else:
        final_username = storage.user.get_username(user)
    return {'username': final_username}