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'])
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'] )