コード例 #1
0
    def validate_user(self, username, password, client, request, *args,
                      **kwargs):
        """
        Overrides the OAuth2Validator validate method to implement multi factor
        authentication.

        If MFA is disabled, authentication requires just a username and
        password.

        If MFA is enabled, authentication requires a username, password,
        and either a MFA code or a backup code. If the request only provides
        the username and password, the server will generate an appropriate
        challenge and respond with `mfa_required = True`.

        Upon using a backup code to authenticate, MFA will be disabled.

        :param attrs: Dictionary of data inputted by the user.
        :raises deux.oauth2.exceptions.InvalidLoginError: If invalid MFA
            code or backup code are submitted. Also if both types of code are
            submitted simultaneously.
        :raises deux.oauth2.exceptions.ChallengeRequiredMessage: If the user
            has MFA enabled but only supplies the correct username and
            password. This exception will prompt the OAuth2 system to send a
            response asking the user to supply an MFA code.
        """

        user = authenticate(username=username, password=password)
        if not (user and user.is_active):
            raise InvalidLoginError(
                force_text(strings.INVALID_CREDENTIALS_ERROR))

        mfa = None
        if hasattr(user, "multi_factor_auth"):
            mfa = user.multi_factor_auth

        if mfa and mfa.enabled:
            mfa_code = request.extra_credentials.get("mfa_code")
            backup_code = request.extra_credentials.get("backup_code")

            if mfa_code and backup_code:
                raise InvalidLoginError(force_text(strings.BOTH_CODES_ERROR))
            elif mfa_code:
                bin_key = mfa.get_bin_key(mfa.challenge_type)
                if not verify_mfa_code(bin_key, mfa_code):
                    raise InvalidLoginError(
                        force_text(strings.INVALID_MFA_CODE_ERROR))
            elif backup_code:
                if not mfa.check_and_use_backup_code(backup_code):
                    raise InvalidLoginError(
                        force_text(strings.INVALID_BACKUP_CODE_ERROR))
            else:
                challenge = MultiFactorChallenge(mfa, mfa.challenge_type)
                challenge.generate_challenge()
                raise ChallengeRequiredMessage(mfa.challenge_type)
        request.user = user
        return True
コード例 #2
0
 def test_verify_mfa_code_fail(self):
     int_mfa_code = int(generate_mfa_code(self.bin_key, 0))
     mfa_code_tests = (None, "", generate_mfa_code(self.bin_key, -3),
                       generate_mfa_code(self.bin_key, -2),
                       generate_mfa_code(self.bin_key, 2),
                       generate_mfa_code(self.bin_key, 3),
                       six.text_type(int_mfa_code + 1).zfill(
                           mfa_settings.MFA_CODE_NUM_DIGITS), "abcdef")
     for mfa_code in mfa_code_tests:
         self.assertFalse(verify_mfa_code(self.bin_key, mfa_code))
コード例 #3
0
    def validate(self, attrs):
        """
        Extends the AuthTokenSerializer validate method to implement multi
        factor authentication.

        If MFA is disabled, authentication requires just a username and
        password.

        If MFA is enabled, authentication requires a username, password,
        and either a MFA code or a backup code. If the request only provides
        the username and password, the server will generate an appropriate
        challenge and respond with `mfa_required = True`.

        Upon using a backup code to authenticate, MFA will be disabled.

        :param attrs: Dictionary of data inputted by the user.
        :raises serializers.ValidationError: If invalid MFA code or backup code
            are submitted. Also if both types of code are submitted
            simultaneously.
        """
        attrs = super(MFAAuthTokenSerializer, self).validate(attrs)
        # User must exist if super method didn't throw error.
        user = attrs["user"]
        assert user is not None, "User should exist after super call."

        multi_factor_model_name = mfa_settings.MFA_MODEL._meta.model_name
        mfa = getattr(user,
                      "{}_multi_factor_auth".format(multi_factor_model_name),
                      None)

        if mfa and mfa.enabled:
            mfa_code = attrs.get("mfa_code")
            backup_code = attrs.get("backup_code")

            if mfa_code and backup_code:
                raise serializers.ValidationError(
                    force_text(strings.BOTH_CODES_ERROR))
            elif mfa_code:
                bin_key = mfa.get_bin_key(mfa.challenge_type)
                if not verify_mfa_code(bin_key, mfa_code):
                    raise serializers.ValidationError(
                        force_text(strings.INVALID_MFA_CODE_ERROR))
            elif backup_code:
                if not mfa.check_and_use_backup_code(backup_code):
                    raise serializers.ValidationError(
                        force_text(strings.INVALID_BACKUP_CODE_ERROR))
            else:
                challenge = MultiFactorChallenge(mfa, mfa.challenge_type)
                challenge.generate_challenge()
                attrs["mfa_required"] = True
                attrs["mfa_type"] = mfa.challenge_type
        return attrs
コード例 #4
0
ファイル: serializers.py プロジェクト: techscientist/deux
    def validate(self, internal_data):
        """
        Validates the request to verify the MFA code. It first ensures that
        MFA is not already enabled and then verifies that the MFA code is the
        correct code.

        :param internal_data: Dictionary of the request data.
        :raises serializers.ValidationError: If MFA is already enabled or if
            the inputted MFA code is not valid.
        """
        if self.instance.enabled:
            raise serializers.ValidationError(
                {"detail": strings.ENABLED_ERROR})

        mfa_code = internal_data.get("mfa_code")
        bin_key = self.instance.get_bin_key(self.challenge_type)
        if not verify_mfa_code(bin_key, mfa_code):
            raise serializers.ValidationError(
                {"mfa_code": strings.INVALID_MFA_CODE_ERROR})
        return {"mfa_code": mfa_code}
コード例 #5
0
 def test_verify_mfa_code_success(self):
     mfa_code_tests = (generate_mfa_code(self.bin_key, -1),
                       generate_mfa_code(self.bin_key, 0),
                       generate_mfa_code(self.bin_key, 1))
     for mfa_code in mfa_code_tests:
         self.assertTrue(verify_mfa_code(self.bin_key, mfa_code))
コード例 #6
0
 def verify_challenge_code(self, mfa_code):
     return verify_mfa_code(self.bin_key, mfa_code)