Example #1
0
class TestEsiSecurity(unittest.TestCase):
    CALLBACK_URI = "https://foo.bar/baz/callback"
    CLIENT_ID = 'foo'
    SECRET_KEY = 'bar'
    BASIC_TOKEN = six.u('Zm9vOmJhcg==')
    SECURITY_NAME = 'evesso'
    TOKEN_IDENTIFIER = 'ESIPY_TEST_TOKEN'
    CODE_VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
    CODE_CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

    RSC_SSO_ENDPOINTS = "test/resources/oauth-authorization-server.json"
    RSC_JWKS = "test/resources/jwks.json"

    def setUp(self):
        warnings.simplefilter('ignore')
        self.custom_refresh_token_signal = Signal()

        with httmock.HTTMock(*_all_auth_mock_):
            self.security = EsiSecurity(
                redirect_uri=TestEsiSecurity.CALLBACK_URI,
                client_id=TestEsiSecurity.CLIENT_ID,
                secret_key=TestEsiSecurity.SECRET_KEY,
                signal_token_updated=self.custom_refresh_token_signal,
                token_identifier=TestEsiSecurity.TOKEN_IDENTIFIER)
            self.security_pkce = EsiSecurity(
                redirect_uri=TestEsiSecurity.CALLBACK_URI,
                client_id=TestEsiSecurity.CLIENT_ID,
                code_verifier=TestEsiSecurity.CODE_VERIFIER,
            )

        with open(TestEsiSecurity.RSC_SSO_ENDPOINTS, 'r') as sso_endpoints:
            self.sso_endpoints = json.load(sso_endpoints)

    def test_esisecurity_init(self):
        with httmock.HTTMock(*_all_auth_mock_):
            with self.assertRaises(AttributeError):
                EsiSecurity(redirect_uri=TestEsiSecurity.CALLBACK_URI,
                            client_id=TestEsiSecurity.CLIENT_ID,
                            secret_key=TestEsiSecurity.SECRET_KEY,
                            sso_endpoints_url="")

            with self.assertRaises(AttributeError):
                EsiSecurity(redirect_uri=TestEsiSecurity.CALLBACK_URI,
                            client_id=TestEsiSecurity.CLIENT_ID)

            with open(TestEsiSecurity.RSC_JWKS, 'r') as jwks:
                jwks = json.load(jwks)
                EsiSecurity(redirect_uri=TestEsiSecurity.CALLBACK_URI,
                            client_id=TestEsiSecurity.CLIENT_ID,
                            secret_key=TestEsiSecurity.SECRET_KEY,
                            jwks_key=jwks['keys'][0])

        self.assertEqual(self.security.security_name,
                         TestEsiSecurity.SECURITY_NAME)
        self.assertEqual(self.security.redirect_uri,
                         TestEsiSecurity.CALLBACK_URI)
        self.assertEqual(self.security.client_id, TestEsiSecurity.CLIENT_ID)
        self.assertEqual(self.security.secret_key, TestEsiSecurity.SECRET_KEY)
        self.assertEqual(self.security.token_identifier,
                         TestEsiSecurity.TOKEN_IDENTIFIER)
        self.assertEqual(self.security.oauth_issuer,
                         self.sso_endpoints['issuer'])
        self.assertEqual(self.security.oauth_authorize,
                         self.sso_endpoints['authorization_endpoint'])
        self.assertEqual(self.security.oauth_token,
                         self.sso_endpoints['token_endpoint'])
        self.assertEqual(self.security.oauth_revoke,
                         self.sso_endpoints['revocation_endpoint'])

    def test_esisecurity_update_token(self):
        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })
        self.assertEqual(self.security.access_token, 'access_token')
        self.assertEqual(self.security.refresh_token, 'refresh_token')
        self.assertEqual(self.security.token_expiry, int(time.time() + 60))

    def test_esisecurity_get_auth_uri(self):
        with self.assertRaises(AttributeError):
            self.security.get_auth_uri(state="")

        self.assertEqual(self.security.get_auth_uri(state='teststate'),
                         ("%s?response_type=code"
                          "&redirect_uri=%s&client_id=%s&state=teststate") %
                         (self.sso_endpoints['authorization_endpoint'],
                          quote(TestEsiSecurity.CALLBACK_URI,
                                safe=''), TestEsiSecurity.CLIENT_ID))

        self.assertEqual(
            self.security.get_auth_uri(implicit=True, state='teststate'),
            ("%s?response_type=token"
             "&redirect_uri=%s&client_id=%s&state=teststate") %
            (self.sso_endpoints['authorization_endpoint'],
             quote(TestEsiSecurity.CALLBACK_URI,
                   safe=''), TestEsiSecurity.CLIENT_ID))

        scopes = ["Scope1", "Scope2"]
        self.assertEqual(
            self.security.get_auth_uri(scopes=scopes, state='teststate'),
            ("%s?response_type=code&redirect_uri=%s"
             "&client_id=%s&scope=Scope1+Scope2&state=teststate") %
            (self.sso_endpoints['authorization_endpoint'],
             quote(TestEsiSecurity.CALLBACK_URI,
                   safe=''), TestEsiSecurity.CLIENT_ID))

    def test_esisecurity_get_access_token_request_params(self):
        params = self.security.get_access_token_params('foo')
        self.assertEqual(
            params['headers'],
            {'Authorization': 'Basic %s' % TestEsiSecurity.BASIC_TOKEN})
        self.assertEqual(params['url'], self.sso_endpoints['token_endpoint'])
        self.assertEqual(params['data'], {
            'grant_type': 'authorization_code',
            'code': 'foo',
        })

    def test_esisecurity_get_refresh_token_request_params(self):
        with self.assertRaises(AttributeError):
            self.security.get_refresh_token_params()

        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })

        # refresh all scopes
        params = self.security.get_refresh_token_params()
        self.assertEqual(
            params['headers'],
            {'Authorization': 'Basic %s' % TestEsiSecurity.BASIC_TOKEN})
        self.assertEqual(params['url'], self.sso_endpoints['token_endpoint'])
        self.assertEqual(params['data'], {
            'grant_type': 'refresh_token',
            'refresh_token': 'refresh_token',
        })

        # refresh specific scopes
        params = self.security.get_refresh_token_params(scope_list=['a', 'b'])
        self.assertEqual(
            params['data'], {
                'grant_type': 'refresh_token',
                'refresh_token': 'refresh_token',
                'scope': 'a+b'
            })

        # refresh specific scopes exception
        with self.assertRaises(AttributeError):
            self.security.get_refresh_token_params(scope_list='notalist')

    def test_esisecurity_token_expiry(self):
        self.security.token_expiry = None
        self.assertTrue(self.security.is_token_expired())

        self.security.token_expiry = time.time() - 10
        self.assertTrue(self.security.is_token_expired())

        self.security.token_expiry = time.time() + 60
        self.assertFalse(self.security.is_token_expired())
        self.assertTrue(self.security.is_token_expired(offset=70))

    def test_esisecurity_auth(self):
        with httmock.HTTMock(oauth_token):
            ret = self.security.auth('let it bee')
            self.assertEqual(ret['access_token'], 'access_token')
            self.assertEqual(ret['refresh_token'], 'refresh_token')
            self.assertEqual(ret['expires_in'], 1200)

            ret = self.security.auth('no_refresh')
            self.assertEqual(ret['access_token'], 'access_token')
            self.assertNotIn('refresh_token', ret)
            self.assertEqual(ret['expires_in'], 1200)

            with self.assertRaises(APIException):
                self.security.auth('fail_test')

    def test_esisecurity_refresh(self):
        with httmock.HTTMock(oauth_token):
            self.security.refresh_token = 'refresh_token'
            ret = self.security.refresh()
            self.assertEqual(ret['access_token'], 'access_token')
            self.assertEqual(ret['refresh_token'], 'refresh_token')
            self.assertEqual(ret['expires_in'], 1200)

            with self.assertRaises(APIException):
                self.security.refresh_token = 'fail_test_token'
                self.security.refresh()

    def test_esisecurity_revoke(self):
        with httmock.HTTMock(oauth_revoke):
            self.security.refresh_token = 'refresh_token'
            self.security.revoke()

            self.security.access_token = 'access_token'
            self.security.revoke()

            with self.assertRaises(AttributeError):
                self.security.revoke()

    def test_esisecurity_verify(self):
        # this is just for coverage purpose. This doesn't work without valid
        # jwt token
        with self.assertRaises(AttributeError):
            self.security.verify()

        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })
        with self.assertRaises(JWTError):
            self.security.verify()
        with httmock.HTTMock(*_all_auth_mock_):
            with open(TestEsiSecurity.RSC_JWKS, 'r') as jwks:
                jwks = json.load(jwks)
                security_nojwks = EsiSecurity(
                    redirect_uri=TestEsiSecurity.CALLBACK_URI,
                    client_id=TestEsiSecurity.CLIENT_ID,
                    secret_key=TestEsiSecurity.SECRET_KEY,
                    jwks_key=jwks['keys'][0])

        security_nojwks.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })
        with self.assertRaises(JWTError):
            security_nojwks.verify()

    def test_esisecurity_call(self):
        class RequestTest(object):
            def __init__(self):
                self._security = []
                self._p = {'header': {}}

        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })

        req = RequestTest()
        self.security(req)
        self.assertNotIn('Authorization', req._p['header'])

        req._security.append({
            'unknown_security_name': {},
        })
        self.security(req)
        self.assertNotIn('Authorization', req._p['header'])

        req._security.append({
            'evesso': {},
        })
        self.security(req)
        self.assertIn('Authorization', req._p['header'])
        self.assertEqual('Bearer access_token',
                         req._p['header']['Authorization'])

    def test_esisecurity_callback_refresh(self):
        class RequestTest(object):
            """ pyswagger Request object over simplified for test purpose"""
            def __init__(self):
                self._security = ['evesso']
                self._p = {'header': {}}

        def callback_function(**kwargs):
            callback_function.count += 1

        callback_function.count = 0

        self.custom_refresh_token_signal.add_receiver(callback_function)

        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': -1
        })

        # test the auto refresh callback event customized
        with httmock.HTTMock(oauth_token):
            req = RequestTest()
            self.security(req)
            self.assertEqual(callback_function.count, 1)

    def test_esisecurity_non_json_response(self):
        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': -1
        })
        with httmock.HTTMock(non_json_error):
            try:
                self.security.auth('somecode')
            except APIException as exc:
                self.assertEqual(exc.status_code, 502)
                self.assertEqual(
                    exc.response,
                    six.b('<html><body>Some HTML Errors</body></html>'))

            try:
                self.security.refresh()
            except APIException as exc:
                self.assertEqual(exc.status_code, 502)
                self.assertEqual(
                    exc.response,
                    six.b('<html><body>Some HTML Errors</body></html>'))

    def test_esisecurity_pkce(self):
        uri = self.security_pkce.get_auth_uri('test')
        self.assertIn('code_challenge=%s' % TestEsiSecurity.CODE_CHALLENGE,
                      uri)

        params = self.security_pkce.get_access_token_params('test')
        self.assertEqual(params['data']['code_verifier'],
                         TestEsiSecurity.CODE_VERIFIER)
        self.assertEqual(params['data']['client_id'],
                         TestEsiSecurity.CLIENT_ID)
        self.assertNotIn('Authorization', params['headers'])
Example #2
0
class TestEsiSecurity(unittest.TestCase):
    CALLBACK_URI = "https://foo.bar/baz/callback"
    LOGIN_EVE = "https://login.eveonline.com"
    OAUTH_VERIFY = "https://esi.tech.ccp.is/verify/"
    OAUTH_TOKEN = "%s/oauth/token" % LOGIN_EVE
    OAUTH_AUTHORIZE = "%s/oauth/authorize" % LOGIN_EVE
    CLIENT_ID = 'foo'
    SECRET_KEY = 'bar'
    BASIC_TOKEN = six.u('Zm9vOmJhcg==')
    SECURITY_NAME = 'evesso'

    @mock.patch('six.moves.urllib.request.urlopen')
    def setUp(self, urlopen_mock):
        # I hate those mock... thx urlopen instead of requests...
        urlopen_mock.return_value = open('test/resources/swagger.json')

        self.app = App.create(
            'https://esi.tech.ccp.is/latest/swagger.json'
        )

        self.security = EsiSecurity(
            app=self.app,
            redirect_uri=TestEsiSecurity.CALLBACK_URI,
            client_id=TestEsiSecurity.CLIENT_ID,
            secret_key=TestEsiSecurity.SECRET_KEY,
        )

    def test_esisecurity_init_with_app(self):
        """ test security init with app and URL"""
        with self.assertRaises(NameError):
            EsiSecurity(
                app=self.app,
                redirect_uri=TestEsiSecurity.CALLBACK_URI,
                client_id=TestEsiSecurity.CLIENT_ID,
                secret_key=TestEsiSecurity.SECRET_KEY,
                security_name="security_name_that_does_not_exist"
            )

        with self.assertRaises(AttributeError):
            EsiSecurity(
                app=self.app,
                redirect_uri=TestEsiSecurity.CALLBACK_URI,
                client_id=TestEsiSecurity.CLIENT_ID,
                secret_key=TestEsiSecurity.SECRET_KEY,
                esi_url=""
            )

        self.assertEqual(
            self.security.security_name,
            TestEsiSecurity.SECURITY_NAME
        )
        self.assertEqual(
            self.security.redirect_uri,
            TestEsiSecurity.CALLBACK_URI
        )
        self.assertEqual(
            self.security.client_id,
            TestEsiSecurity.CLIENT_ID
        )
        self.assertEqual(
            self.security.secret_key,
            TestEsiSecurity.SECRET_KEY
        )
        self.assertEqual(
            self.security.oauth_verify,
            TestEsiSecurity.OAUTH_VERIFY
        )
        self.assertEqual(
            self.security.oauth_token,
            TestEsiSecurity.OAUTH_TOKEN
        )
        self.assertEqual(
            self.security.oauth_authorize,
            TestEsiSecurity.OAUTH_AUTHORIZE
        )

    def test_esisecurity_other_init(self):
        """ test security init without app and with urls """
        with self.assertRaises(AttributeError):
            EsiSecurity(
                redirect_uri=TestEsiSecurity.CALLBACK_URI,
                client_id=TestEsiSecurity.CLIENT_ID,
                secret_key=TestEsiSecurity.SECRET_KEY,
                sso_url=""
            )

        security = EsiSecurity(
            redirect_uri=TestEsiSecurity.CALLBACK_URI,
            client_id=TestEsiSecurity.CLIENT_ID,
            secret_key=TestEsiSecurity.SECRET_KEY,
            sso_url='foo.com',
            esi_url='bar.baz'
        )

        self.assertEqual(
            security.oauth_verify,
            "bar.baz/verify/"
        )
        self.assertEqual(
            security.oauth_token,
            "foo.com/oauth/token"
        )
        self.assertEqual(
            security.oauth_authorize,
            "foo.com/oauth/authorize"
        )

    def test_esisecurity_update_token(self):
        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })
        self.assertEqual(self.security.access_token, 'access_token')
        self.assertEqual(self.security.refresh_token, 'refresh_token')
        self.assertEqual(self.security.token_expiry, int(time.time() + 60))

    def test_esisecurity_get_auth_uri(self):
        self.assertEqual(
            self.security.get_auth_uri(),
            ("%s/oauth/authorize?response_type=code"
             "&redirect_uri=%s&client_id=%s") % (
                TestEsiSecurity.LOGIN_EVE,
                quote(TestEsiSecurity.CALLBACK_URI, safe=''),
                TestEsiSecurity.CLIENT_ID
            )
        )

        self.assertEqual(
            self.security.get_auth_uri(implicit=True),
            ("%s/oauth/authorize?response_type=token"
             "&redirect_uri=%s&client_id=%s") % (
                TestEsiSecurity.LOGIN_EVE,
                quote(TestEsiSecurity.CALLBACK_URI, safe=''),
                TestEsiSecurity.CLIENT_ID
            )
        )

        scopes = ["Scope1", "Scope2"]
        state = "foo"
        self.assertEqual(
            self.security.get_auth_uri(scopes, state),
            ("%s/oauth/authorize?response_type=code&redirect_uri=%s"
             "&client_id=%s&scope=Scope1+Scope2&state=foo") % (
                TestEsiSecurity.LOGIN_EVE,
                quote(TestEsiSecurity.CALLBACK_URI, safe=''),
                TestEsiSecurity.CLIENT_ID
            )
        )

    def test_esisecurity_get_access_token_request_params(self):
        params = self.security.get_access_token_params('foo')
        self.assertEqual(
            params['headers'],
            {'Authorization': 'Basic %s' % TestEsiSecurity.BASIC_TOKEN}
        )
        self.assertEqual(
            params['url'],
            TestEsiSecurity.OAUTH_TOKEN
        )
        self.assertEqual(
            params['data'],
            {
                'grant_type': 'authorization_code',
                'code': 'foo',
            }
        )

    def test_esisecurity_get_refresh_token_request_params(self):
        with self.assertRaises(AttributeError):
            self.security.get_refresh_token_params()

        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })

        params = self.security.get_refresh_token_params()

        self.assertEqual(
            params['headers'],
            {'Authorization': 'Basic %s' % TestEsiSecurity.BASIC_TOKEN}
        )
        self.assertEqual(
            params['url'],
            TestEsiSecurity.OAUTH_TOKEN
        )
        self.assertEqual(
            params['data'],
            {
                'grant_type': 'refresh_token',
                'refresh_token': 'refresh_token',
            }
        )

    def test_esisecurity_token_expiry(self):
        self.security.token_expiry = None
        self.assertTrue(self.security.is_token_expired())

        self.security.token_expiry = time.time() - 10
        self.assertTrue(self.security.is_token_expired())

        self.security.token_expiry = time.time() + 60
        self.assertFalse(self.security.is_token_expired())
        self.assertTrue(self.security.is_token_expired(offset=70))

    def test_esisecurity_auth(self):
        with httmock.HTTMock(oauth_token):
            ret = self.security.auth('let it bee')
            self.assertEqual(ret['access_token'], 'access_token')
            self.assertEqual(ret['refresh_token'], 'refresh_token')
            self.assertEqual(ret['expires_in'], 1200)

            ret = self.security.auth('no_refresh')
            self.assertEqual(ret['access_token'], 'access_token')
            self.assertNotIn('refresh_token', ret)
            self.assertEqual(ret['expires_in'], 1200)

            with self.assertRaises(APIException):
                self.security.auth('fail_test')

    def test_esisecurity_refresh(self):
        with httmock.HTTMock(oauth_token):
            self.security.refresh_token = 'refresh_token'
            ret = self.security.refresh()
            self.assertEqual(ret['access_token'], 'access_token')
            self.assertEqual(ret['refresh_token'], 'refresh_token')
            self.assertEqual(ret['expires_in'], 1200)

            with self.assertRaises(APIException):
                self.security.refresh_token = 'fail_test_token'
                self.security.refresh()

    def test_esisecurity_verify(self):
        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })

        with httmock.HTTMock(oauth_verify):
            char_data = self.security.verify()
            self.assertEqual(char_data['CharacterID'], 123456789)
            self.assertEqual(char_data['CharacterName'], 'EsiPy Tester')
            self.assertEqual(char_data['CharacterOwnerHash'], 'YetAnotherHash')

        with httmock.HTTMock(oauth_verify_fail):
            with self.assertRaises(APIException):
                self.security.verify()

    def test_esisecurity_call(self):
        class RequestTest(object):

            def __init__(self):
                self._security = []
                self._p = {'header': {}}

        self.security.update_token({
            'access_token': 'access_token',
            'refresh_token': 'refresh_token',
            'expires_in': 60
        })

        req = RequestTest()
        self.security(req)
        self.assertNotIn('Authorization', req._p['header'])

        req._security.append({
            'unknown_security_name': {},
        })
        self.security(req)
        self.assertNotIn('Authorization', req._p['header'])

        req._security.append({
            'evesso': {},
        })
        self.security(req)
        self.assertIn('Authorization', req._p['header'])
        self.assertEqual(
            'Bearer access_token',
            req._p['header']['Authorization']
        )