import unittest
from future.moves.urllib.parse import urlparse, urljoin, parse_qs

import pytest
from addons.twofactor.tests.utils import _valid_code
from nose.tools import (assert_equal, assert_false, assert_is_none,
                        assert_is_not_none, assert_true)
from osf_tests.factories import UserFactory

pytestmark = pytest.mark.django_db


class TestCallbacks(unittest.TestCase):
    def setUp(self):
        super(TestCallbacks, self).setUp()

        self.user = UserFactory()
        self.user.add_addon('twofactor')
        self.user_settings = self.user.get_addon('twofactor')

    def test_add_to_user(self):
        assert_equal(self.user_settings.totp_drift, 0)
        assert_is_not_none(self.user_settings.totp_secret)
        assert_false(self.user_settings.is_confirmed)

    def test_remove_from_unconfirmed_user(self):
        # drift defaults to 0. Change it so we can test it was changed back.
        self.user_settings.totp_drift = 1
        self.user_settings.save()

        self.user.delete_addon('twofactor')
        self.user_settings.reload()

        assert_equal(self.user_settings.totp_drift, 0)
        assert_is_none(self.user_settings.totp_secret)
        assert_false(self.user_settings.is_confirmed)

    def test_remove_from_confirmed_user(self):
        # drift defaults to 0. Change it so we can test it was changed back.
        self.user_settings.totp_drift = 1
        self.user_settings.is_confirmed = True
        self.user_settings.save()

        self.user.delete_addon('twofactor')
        self.user_settings.reload()

        assert_equal(self.user_settings.totp_drift, 0)
        assert_is_none(self.user_settings.totp_secret)
        assert_false(self.user_settings.is_confirmed)


class TestUserSettingsModel(unittest.TestCase):
    TOTP_SECRET = 'b8f85986068f8079aa9d'
    TOTP_SECRET_B32 = 'XD4FTBQGR6AHTKU5'

    def setUp(self):
        super(TestUserSettingsModel, self).setUp()

        self.user = UserFactory()
        self.user.add_addon('twofactor')
        self.user_settings = self.user.get_addon('twofactor')

        self.user_settings.totp_secret = self.TOTP_SECRET
        self.user_settings.save()

    def tearDown(self):
        super(TestUserSettingsModel, self).tearDown()
        self.user.__class__.delete(self.user)

    def test_b32(self):
        assert_equal(self.user_settings.totp_secret_b32, self.TOTP_SECRET_B32)

    def test_otpauth_url(self):
        url = urlparse(self.user_settings.otpauth_url)

        assert_equal(url.scheme, 'otpauth')
        assert_equal(url.netloc, 'totp')
        assert_equal(url.path, '/RDM:{}'.format(self.user.username))
        assert_equal(
            parse_qs(url.query),
            {'secret': [self.TOTP_SECRET_B32]}
        )

    def test_json(self):
        # url = 'otpauth://totp/RDM:{}?secret=' + self.TOTP_SECRET_B32

        settings = self.user_settings.to_json(user=None)
        assert_equal(
            settings,
            {
                'is_enabled': True,
                'addon_full_name': 'Two-factor Authentication',
                'addon_short_name': 'twofactor',
                'drift': 0,
                'is_confirmed': False,
                'nodes': [],
                'secret': self.TOTP_SECRET_B32,
                'has_auth': False,
            }
        )

    def test_verify_valid_code(self):
        assert_true(
            self.user_settings.verify_code(_valid_code(self.TOTP_SECRET))
        )

    def test_verify_valid_core_drift(self):
        # use a code from 30 seconds in the future
        assert_true(
            self.user_settings.verify_code(
                _valid_code(self.TOTP_SECRET, drift=1)
            )
        )

        # make sure drift is updated.
        assert_equal(self.user_settings.totp_drift, 1)

        # use a code from 60 seconds in the future
        assert_true(
            self.user_settings.verify_code(
                _valid_code(self.TOTP_SECRET, drift=2)
            )
        )

        # make sure drift is updated.
        assert_equal(self.user_settings.totp_drift, 2)

        # use the current code (which is now 2 periods away from the drift)
        assert_false(
            self.user_settings.verify_code(_valid_code(self.TOTP_SECRET))
        )

[((17, 20, 17, 33), 'osf_tests.factories.UserFactory', 'UserFactory', ({}, {}), '()', False, 'from osf_tests.factories import UserFactory\n'), ((22, 8, 22, 54), 'nose.tools.assert_equal', 'assert_equal', ({(22, 21, 22, 50): 'self.user_settings.totp_drift', (22, 52, 22, 53): '(0)'}, {}), '(self.user_settings.totp_drift, 0)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((23, 8, 23, 58), 'nose.tools.assert_is_not_none', 'assert_is_not_none', ({(23, 27, 23, 57): 'self.user_settings.totp_secret'}, {}), '(self.user_settings.totp_secret)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((24, 8, 24, 53), 'nose.tools.assert_false', 'assert_false', ({(24, 21, 24, 52): 'self.user_settings.is_confirmed'}, {}), '(self.user_settings.is_confirmed)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((34, 8, 34, 54), 'nose.tools.assert_equal', 'assert_equal', ({(34, 21, 34, 50): 'self.user_settings.totp_drift', (34, 52, 34, 53): '(0)'}, {}), '(self.user_settings.totp_drift, 0)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((35, 8, 35, 54), 'nose.tools.assert_is_none', 'assert_is_none', ({(35, 23, 35, 53): 'self.user_settings.totp_secret'}, {}), '(self.user_settings.totp_secret)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((36, 8, 36, 53), 'nose.tools.assert_false', 'assert_false', ({(36, 21, 36, 52): 'self.user_settings.is_confirmed'}, {}), '(self.user_settings.is_confirmed)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((47, 8, 47, 54), 'nose.tools.assert_equal', 'assert_equal', ({(47, 21, 47, 50): 'self.user_settings.totp_drift', (47, 52, 47, 53): '(0)'}, {}), '(self.user_settings.totp_drift, 0)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((48, 8, 48, 54), 'nose.tools.assert_is_none', 'assert_is_none', ({(48, 23, 48, 53): 'self.user_settings.totp_secret'}, {}), '(self.user_settings.totp_secret)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((49, 8, 49, 53), 'nose.tools.assert_false', 'assert_false', ({(49, 21, 49, 52): 'self.user_settings.is_confirmed'}, {}), '(self.user_settings.is_confirmed)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((59, 20, 59, 33), 'osf_tests.factories.UserFactory', 'UserFactory', ({}, {}), '()', False, 'from osf_tests.factories import UserFactory\n'), ((71, 8, 71, 78), 'nose.tools.assert_equal', 'assert_equal', ({(71, 21, 71, 55): 'self.user_settings.totp_secret_b32', (71, 57, 71, 77): 'self.TOTP_SECRET_B32'}, {}), '(self.user_settings.totp_secret_b32, self.TOTP_SECRET_B32)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((74, 14, 74, 54), 'future.moves.urllib.parse.urlparse', 'urlparse', ({(74, 23, 74, 53): 'self.user_settings.otpauth_url'}, {}), '(self.user_settings.otpauth_url)', False, 'from future.moves.urllib.parse import urlparse, urljoin, parse_qs\n'), ((76, 8, 76, 43), 'nose.tools.assert_equal', 'assert_equal', ({(76, 21, 76, 31): 'url.scheme', (76, 33, 76, 42): '"""otpauth"""'}, {}), "(url.scheme, 'otpauth')", False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((77, 8, 77, 40), 'nose.tools.assert_equal', 'assert_equal', ({(77, 21, 77, 31): 'url.netloc', (77, 33, 77, 39): '"""totp"""'}, {}), "(url.netloc, 'totp')", False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((88, 8, 100, 9), 'nose.tools.assert_equal', 'assert_equal', ({(89, 12, 89, 20): 'settings', (90, 12, 99, 13): "{'is_enabled': True, 'addon_full_name': 'Two-factor Authentication',\n    'addon_short_name': 'twofactor', 'drift': 0, 'is_confirmed': False,\n    'nodes': [], 'secret': self.TOTP_SECRET_B32, 'has_auth': False}"}, {}), "(settings, {'is_enabled': True, 'addon_full_name':\n    'Two-factor Authentication', 'addon_short_name': 'twofactor', 'drift': \n    0, 'is_confirmed': False, 'nodes': [], 'secret': self.TOTP_SECRET_B32,\n    'has_auth': False})", False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((116, 8, 116, 54), 'nose.tools.assert_equal', 'assert_equal', ({(116, 21, 116, 50): 'self.user_settings.totp_drift', (116, 52, 116, 53): '(1)'}, {}), '(self.user_settings.totp_drift, 1)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((126, 8, 126, 54), 'nose.tools.assert_equal', 'assert_equal', ({(126, 21, 126, 50): 'self.user_settings.totp_drift', (126, 52, 126, 53): '(2)'}, {}), '(self.user_settings.totp_drift, 2)', False, 'from nose.tools import assert_equal, assert_false, assert_is_none, assert_is_not_none, assert_true\n'), ((80, 12, 80, 31), 'future.moves.urllib.parse.parse_qs', 'parse_qs', ({(80, 21, 80, 30): 'url.query'}, {}), '(url.query)', False, 'from future.moves.urllib.parse import urlparse, urljoin, parse_qs\n'), ((104, 43, 104, 72), 'addons.twofactor.tests.utils._valid_code', '_valid_code', ({(104, 55, 104, 71): 'self.TOTP_SECRET'}, {}), '(self.TOTP_SECRET)', False, 'from addons.twofactor.tests.utils import _valid_code\n'), ((111, 16, 111, 54), 'addons.twofactor.tests.utils._valid_code', '_valid_code', (), '', False, 'from addons.twofactor.tests.utils import _valid_code\n'), ((121, 16, 121, 54), 'addons.twofactor.tests.utils._valid_code', '_valid_code', (), '', False, 'from addons.twofactor.tests.utils import _valid_code\n'), ((130, 43, 130, 72), 'addons.twofactor.tests.utils._valid_code', '_valid_code', ({(130, 55, 130, 71): 'self.TOTP_SECRET'}, {}), '(self.TOTP_SECRET)', False, 'from addons.twofactor.tests.utils import _valid_code\n')]