diff options
author | BonfaceKilz | 2020-09-17 16:00:08 +0300 |
---|---|---|
committer | GitHub | 2020-09-17 16:00:08 +0300 |
commit | 8da6a70916d2cf18e476ab0adf47f802c481205d (patch) | |
tree | 8b9a50fd96d3ab1ee3e812e9be01edba1edeb042 | |
parent | 0ba82e21e4ec8a0364ee22351e130106dd0b53ea (diff) | |
parent | 225c360d0a5c57957fe2bc3299108e9b39f12929 (diff) | |
download | genenetwork2-8da6a70916d2cf18e476ab0adf47f802c481205d.tar.gz |
Merge pull request #443 from BonfaceKilz/tests/test-auth-and-utf8
Tests/test auth and utf8
-rw-r--r-- | wqflask/tests/base/test_data_set.py | 10 | ||||
-rw-r--r-- | wqflask/tests/base/test_trait.py | 101 | ||||
-rw-r--r-- | wqflask/tests/utility/test_authentication_tools.py | 193 | ||||
-rw-r--r-- | wqflask/tests/utility/test_hmac.py | 35 | ||||
-rw-r--r-- | wqflask/utility/authentication_tools.py | 41 | ||||
-rw-r--r-- | wqflask/utility/hmac.py | 9 |
6 files changed, 363 insertions, 26 deletions
diff --git a/wqflask/tests/base/test_data_set.py b/wqflask/tests/base/test_data_set.py index 94780a5d..dd7f5051 100644 --- a/wqflask/tests/base/test_data_set.py +++ b/wqflask/tests/base/test_data_set.py @@ -66,7 +66,7 @@ class TestDataSetTypes(unittest.TestCase): @mock.patch('base.data_set.g') def test_set_dataset_key_mrna(self, db_mock): with app.app_context(): - db_mock.db.execute.return_value = [1, 2, 3] + db_mock.db.execute.return_value.fetchone.return_value = [1, 2, 3] redis_mock = mock.Mock() redis_mock.get.return_value = self.test_dataset data_set = DatasetType(redis_mock) @@ -84,7 +84,7 @@ class TestDataSetTypes(unittest.TestCase): @mock.patch('base.data_set.g') def test_set_dataset_key_pheno(self, db_mock): with app.app_context(): - db_mock.db.execute.return_value = [1, 2, 3] + db_mock.db.execute.return_value.fetchone.return_value = [1, 2, 3] redis_mock = mock.Mock() redis_mock.get.return_value = self.test_dataset data_set = DatasetType(redis_mock) @@ -93,7 +93,6 @@ class TestDataSetTypes(unittest.TestCase): redis_mock.set.assert_called_once_with( "dataset_structure", '{"Aging-Brain-UCIPublish": "Publish", "AKXDGeno": "Geno", "B139_K_1206_M": "ProbeSet", "AD-cases-controls-MyersGeno": "Geno", "AD-cases-controls-MyersPublish": "Publish", "All Phenotypes": "Publish", "Test": "Publish", "AXBXAPublish": "Publish", "B139_K_1206_R": "ProbeSet", "AXBXAGeno": "Geno"}') - expected_db_call = """""" db_mock.db.execute.assert_called_with( ("SELECT InfoFiles.GN_AccesionId " + "FROM InfoFiles, PublishFreeze, InbredSet " + @@ -105,7 +104,7 @@ class TestDataSetTypes(unittest.TestCase): @mock.patch('base.data_set.g') def test_set_dataset_other_pheno(self, db_mock): with app.app_context(): - db_mock.db.execute.return_value = [1, 2, 3] + db_mock.db.execute.return_value.fetchone.return_value = [1, 2, 3] redis_mock = mock.Mock() redis_mock.get.return_value = self.test_dataset data_set = DatasetType(redis_mock) @@ -114,7 +113,6 @@ class TestDataSetTypes(unittest.TestCase): redis_mock.set.assert_called_once_with( "dataset_structure", '{"Aging-Brain-UCIPublish": "Publish", "AKXDGeno": "Geno", "B139_K_1206_M": "ProbeSet", "AD-cases-controls-MyersGeno": "Geno", "AD-cases-controls-MyersPublish": "Publish", "All Phenotypes": "Publish", "Test": "Publish", "AXBXAPublish": "Publish", "B139_K_1206_R": "ProbeSet", "AXBXAGeno": "Geno"}') - expected_db_call = """""" db_mock.db.execute.assert_called_with( ("SELECT PublishFreeze.Name " + "FROM PublishFreeze, InbredSet " + @@ -125,7 +123,7 @@ class TestDataSetTypes(unittest.TestCase): @mock.patch('base.data_set.g') def test_set_dataset_geno(self, db_mock): with app.app_context(): - db_mock.db.execute.return_value = [1, 2, 3] + db_mock.db.execute.return_value.fetchone.return_value = [1, 2, 3] redis_mock = mock.Mock() redis_mock.get.return_value = self.test_dataset data_set = DatasetType(redis_mock) diff --git a/wqflask/tests/base/test_trait.py b/wqflask/tests/base/test_trait.py new file mode 100644 index 00000000..53b0d440 --- /dev/null +++ b/wqflask/tests/base/test_trait.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +"""Tests wqflask/base/trait.py""" +import unittest +import mock + +from base.trait import GeneralTrait +from base.trait import retrieve_trait_info + + +class TestResponse: + """Mock Test Response after a request""" + @property + def content(self): + """Mock the content from Requests.get(params).content""" + return "[1, 2, 3, 4]" + + +class TestNilResponse: + """Mock Test Response after a request""" + @property + def content(self): + """Mock the content from Requests.get(params).content""" + return "{}" + + +class MockTrait(GeneralTrait): + @property + def wikidata_alias_fmt(self): + return "Mock alias" + + +class TestRetrieveTraitInfo(unittest.TestCase): + """Tests for 'retrieve_trait_info'""" + def test_retrieve_trait_info_with_empty_dataset(self): + """Test that an exception is raised when dataset is empty""" + with self.assertRaises(AssertionError): + retrieve_trait_info(trait=mock.MagicMock(), + dataset={}) + + @mock.patch('base.trait.requests.get') + @mock.patch('base.trait.g') + def test_retrieve_trait_info_with_empty_trait_info(self, + g_mock, + requests_mock): + """Empty trait info""" + requests_mock.return_value = TestNilResponse() + with self.assertRaises(KeyError): + retrieve_trait_info(trait=mock.MagicMock(), + dataset=mock.MagicMock()) + + @mock.patch('base.trait.requests.get') + @mock.patch('base.trait.g') + def test_retrieve_trait_info_with_non_empty_trait_info(self, + g_mock, + requests_mock): + """Test that attributes are set""" + mock_dataset = mock.MagicMock() + requests_mock.return_value = TestResponse() + type(mock_dataset).display_fields = mock.PropertyMock( + return_value=["a", "b", "c", "d"]) + test_trait = retrieve_trait_info(trait=MockTrait(dataset=mock_dataset), + dataset=mock_dataset) + self.assertEqual(test_trait.a, 1) + self.assertEqual(test_trait.b, 2) + self.assertEqual(test_trait.c, 3) + self.assertEqual(test_trait.d, 4) + + @mock.patch('base.trait.requests.get') + @mock.patch('base.trait.g') + def test_retrieve_trait_info_utf8_parsing(self, + g_mock, + requests_mock): + """Test that utf-8 strings are parsed correctly""" + utf_8_string = "test_string" + mock_dataset = mock.MagicMock() + requests_mock.return_value = TestResponse() + type(mock_dataset).display_fields = mock.PropertyMock( + return_value=["a", "b", "c", "d"]) + type(mock_dataset).type = 'Publish' + + mock_trait = MockTrait( + dataset=mock_dataset, + pre_publication_description=utf_8_string + ) + trait_attrs = { + "group_code": "test_code", + "pre_publication_description": "test_pre_pub", + "pre_publication_abbreviation": "ファイルを画面毎に見て行くには、次のコマンドを使います。", + "post_publication_description": None, + "pubmed_id": None, + 'year': "2020", + "authors": "Jane Doe かいと", + } + for key, val in list(trait_attrs.items()): + setattr(mock_trait, key, val) + test_trait = retrieve_trait_info(trait=mock_trait, + dataset=mock_dataset) + self.assertEqual(test_trait.abbreviation, + "ファイルを画面毎に見て行くには、次のコマンドを使います。") + self.assertEqual(test_trait.authors, + "Jane Doe かいと") diff --git a/wqflask/tests/utility/test_authentication_tools.py b/wqflask/tests/utility/test_authentication_tools.py new file mode 100644 index 00000000..99c74245 --- /dev/null +++ b/wqflask/tests/utility/test_authentication_tools.py @@ -0,0 +1,193 @@ +"""Tests for authentication tools""" +import unittest +import mock + +from utility.authentication_tools import check_resource_availability +from utility.authentication_tools import add_new_resource + + +class TestResponse: + """Mock Test Response after a request""" + @property + def content(self): + """Mock the content from Requests.get(params).content""" + return '["foo"]' + + +class TestUser: + """Mock user""" + @property + def user_id(self): + """Mockes user id. Used in Flask.g.user_session.user_id""" + return "Jane" + + +class TestUserSession: + """Mock user session""" + @property + def user_session(self): + """Mock user session. Mocks Flask.g.user_session object""" + return TestUser() + + +def mock_add_resource(resource_ob, update=False): + return resource_ob + + +class TestCheckResourceAvailability(unittest.TestCase): + """Test methods related to checking the resource availability""" + @mock.patch('utility.authentication_tools.add_new_resource') + @mock.patch('utility.authentication_tools.Redis') + @mock.patch('utility.authentication_tools.g') + @mock.patch('utility.authentication_tools.get_resource_id') + def test_check_resource_availability_default_mask( + self, + resource_id_mock, + g_mock, + redis_mock, + add_new_resource_mock): + """Test the resource availability with default mask""" + resource_id_mock.return_value = 1 + g_mock.return_value = mock.Mock() + redis_mock.smembers.return_value = [] + test_dataset = mock.MagicMock() + type(test_dataset).type = mock.PropertyMock(return_value="Test") + add_new_resource_mock.return_value = {"default_mask": 2} + self.assertEqual(check_resource_availability(test_dataset), 2) + + @mock.patch('utility.authentication_tools.requests.get') + @mock.patch('utility.authentication_tools.add_new_resource') + @mock.patch('utility.authentication_tools.Redis') + @mock.patch('utility.authentication_tools.g') + @mock.patch('utility.authentication_tools.get_resource_id') + def test_check_resource_availability_non_default_mask( + self, + resource_id_mock, + g_mock, + redis_mock, + add_new_resource_mock, + requests_mock): + """Test the resource availability with default mask""" + resource_id_mock.return_value = 1 + g_mock.return_value = mock.Mock() + redis_mock.smembers.return_value = [] + add_new_resource_mock.return_value = {"default_mask": 2} + requests_mock.return_value = TestResponse() + test_dataset = mock.MagicMock() + type(test_dataset).type = mock.PropertyMock(return_value="Test") + self.assertEqual(check_resource_availability(test_dataset), + ['foo']) + + @mock.patch('utility.authentication_tools.webqtlConfig.SUPER_PRIVILEGES', + "SUPERUSER") + @mock.patch('utility.authentication_tools.requests.get') + @mock.patch('utility.authentication_tools.add_new_resource') + @mock.patch('utility.authentication_tools.Redis') + @mock.patch('utility.authentication_tools.g', TestUserSession()) + @mock.patch('utility.authentication_tools.get_resource_id') + def test_check_resource_availability_of_super_user( + self, + resource_id_mock, + redis_mock, + add_new_resource_mock, + requests_mock): + """Test the resource availability if the user is the super user""" + resource_id_mock.return_value = 1 + redis_mock.smembers.return_value = ["Jane"] + add_new_resource_mock.return_value = {"default_mask": 2} + requests_mock.return_value = TestResponse() + test_dataset = mock.MagicMock() + type(test_dataset).type = mock.PropertyMock(return_value="Test") + self.assertEqual(check_resource_availability(test_dataset), + "SUPERUSER") + + @mock.patch('utility.authentication_tools.webqtlConfig.DEFAULT_PRIVILEGES', + "John Doe") + def test_check_resource_availability_string_dataset(self): + """Test the resource availability if the dataset is a string""" + self.assertEqual(check_resource_availability("Test"), + "John Doe") + + @mock.patch('utility.authentication_tools.webqtlConfig.DEFAULT_PRIVILEGES', + "John Doe") + def test_check_resource_availability_temp(self): + """Test the resource availability if the dataset is a string""" + test_dataset = mock.MagicMock() + type(test_dataset).type = mock.PropertyMock(return_value="Temp") + self.assertEqual(check_resource_availability(test_dataset), + "John Doe") + + +class TestAddNewResource(unittest.TestCase): + """Test cases for add_new_resource method""" + @mock.patch('utility.authentication_tools.webqtlConfig.DEFAULT_PRIVILEGES', + "John Doe") + @mock.patch('utility.authentication_tools.add_resource', mock_add_resource) + @mock.patch('utility.authentication_tools.get_group_code') + def test_add_new_resource_if_publish_datatype(self, group_code_mock): + """Test add_new_resource if dataset type is 'publish'""" + group_code_mock.return_value = "Test" + test_dataset = mock.MagicMock() + type(test_dataset).type = mock.PropertyMock(return_value="Publish") + type(test_dataset).id = mock.PropertyMock(return_value=10) + expected_value = { + "owner_id": "none", + "default_mask": "John Doe", + "group_masks": {}, + "name": "Test_None", + "data": { + "dataset": 10, + "trait": None + }, + "type": "dataset-publish" + } + self.assertEqual(add_new_resource(test_dataset), + expected_value) + + @mock.patch('utility.authentication_tools.webqtlConfig.DEFAULT_PRIVILEGES', + "John Doe") + @mock.patch('utility.authentication_tools.add_resource', mock_add_resource) + @mock.patch('utility.authentication_tools.get_group_code') + def test_add_new_resource_if_geno_datatype(self, group_code_mock): + """Test add_new_resource if dataset type is 'geno'""" + group_code_mock.return_value = "Test" + test_dataset = mock.MagicMock() + type(test_dataset).name = mock.PropertyMock(return_value="Geno") + type(test_dataset).type = mock.PropertyMock(return_value="Geno") + type(test_dataset).id = mock.PropertyMock(return_value=20) + expected_value = { + "owner_id": "none", + "default_mask": "John Doe", + "group_masks": {}, + "name": "Geno", + "data": { + "dataset": 20, + }, + "type": "dataset-geno" + } + self.assertEqual(add_new_resource(test_dataset), + expected_value) + + @mock.patch('utility.authentication_tools.webqtlConfig.DEFAULT_PRIVILEGES', + "John Doe") + @mock.patch('utility.authentication_tools.add_resource', mock_add_resource) + @mock.patch('utility.authentication_tools.get_group_code') + def test_add_new_resource_if_other_datatype(self, group_code_mock): + """Test add_new_resource if dataset type is not 'geno' or 'publish'""" + group_code_mock.return_value = "Test" + test_dataset = mock.MagicMock() + type(test_dataset).name = mock.PropertyMock(return_value="Geno") + type(test_dataset).type = mock.PropertyMock(return_value="other") + type(test_dataset).id = mock.PropertyMock(return_value=20) + expected_value = { + "owner_id": "none", + "default_mask": "John Doe", + "group_masks": {}, + "name": "Geno", + "data": { + "dataset": 20, + }, + "type": "dataset-probeset" + } + self.assertEqual(add_new_resource(test_dataset), + expected_value) diff --git a/wqflask/tests/utility/test_hmac.py b/wqflask/tests/utility/test_hmac.py new file mode 100644 index 00000000..c7927685 --- /dev/null +++ b/wqflask/tests/utility/test_hmac.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +"""Test hmac utility functions""" + +import unittest +import mock + +from utility.hmac import data_hmac +from utility.hmac import url_for_hmac +from utility.hmac import hmac_creation + + +class TestHmacUtil(unittest.TestCase): + """Test Utility method for hmac creation""" + + def test_hmac_creation(self): + """Test hmac creation with a utf-8 string""" + self.assertEqual(hmac_creation("ファイ"), "21fa1d935bbbb07a7875") + + def test_data_hmac(self): + """Test data_hmac fn with a utf-8 string""" + self.assertEqual(data_hmac("ファイ"), "ファイ:21fa1d935bbbb07a7875") + + @mock.patch("utility.hmac.url_for") + def test_url_for_hmac_with_plain_url(self, mock_url): + """Test url_for_hmac without params""" + mock_url.return_value = "https://mock_url.com/ファイ/" + self.assertEqual(url_for_hmac("ファイ"), + "https://mock_url.com/ファイ/?hm=a62896a50d9ffcff7deb") + + @mock.patch("utility.hmac.url_for") + def test_url_for_hmac_with_param_in_url(self, mock_url): + """Test url_for_hmac with params""" + mock_url.return_value = "https://mock_url.com/?ファイ=1" + self.assertEqual(url_for_hmac("ファイ"), + "https://mock_url.com/?ファイ=1&hm=b2128fb28bc32da3b5b7") diff --git a/wqflask/utility/authentication_tools.py b/wqflask/utility/authentication_tools.py index 3553b92b..239b08e3 100644 --- a/wqflask/utility/authentication_tools.py +++ b/wqflask/utility/authentication_tools.py @@ -1,4 +1,6 @@ from __future__ import absolute_import, print_function, division +import logging +from flask import Flask, g, redirect, url_for import json import requests @@ -9,33 +11,31 @@ from utility import hmac from utility.redis_tools import get_redis_conn, get_resource_info, get_resource_id, add_resource Redis = get_redis_conn() -from flask import Flask, g, redirect, url_for -import logging -logger = logging.getLogger(__name__ ) +logger = logging.getLogger(__name__) + def check_resource_availability(dataset, trait_id=None): - #At least for now assume temporary entered traits are accessible - if type(dataset) == str: - return webqtlConfig.DEFAULT_PRIVILEGES - if dataset.type == "Temp": + # At least for now assume temporary entered traits are accessible + if type(dataset) == str or dataset.type == "Temp": return webqtlConfig.DEFAULT_PRIVILEGES resource_id = get_resource_id(dataset, trait_id) - if resource_id: #ZS: This should never be false, but it's technically possible if a non-Temp dataset somehow had a type other than Publish/ProbeSet/Geno + if resource_id: # ZS: This should never be false, but it's technically possible if a non-Temp dataset somehow had a type other than Publish/ProbeSet/Geno resource_info = get_resource_info(resource_id) - if not resource_info: #ZS: If resource isn't already in redis, add it with default privileges + if not resource_info: # ZS: If resource isn't already in redis, add it with default privileges resource_info = add_new_resource(dataset, trait_id) - #ZS: Check if super-user - we should probably come up with some way to integrate this into the proxy + # ZS: Check if super-user - we should probably come up with some way to integrate this into the proxy if g.user_session.user_id in Redis.smembers("super_users"): - return webqtlConfig.SUPER_PRIVILEGES + return webqtlConfig.SUPER_PRIVILEGES response = None - the_url = "http://localhost:8080/available?resource={}&user={}".format(resource_id, g.user_session.user_id) + the_url = "http://localhost:8080/available?resource={}&user={}".format( + resource_id, g.user_session.user_id) try: response = json.loads(requests.get(the_url).content) except: @@ -43,18 +43,19 @@ def check_resource_availability(dataset, trait_id=None): return response + def add_new_resource(dataset, trait_id=None): resource_ob = { - 'owner_id' : "none", # webqtlConfig.DEFAULT_OWNER_ID, + 'owner_id': "none", # webqtlConfig.DEFAULT_OWNER_ID, 'default_mask': webqtlConfig.DEFAULT_PRIVILEGES, - 'group_masks' : {} + 'group_masks': {} } if dataset.type == "Publish": resource_ob['name'] = get_group_code(dataset) + "_" + str(trait_id) resource_ob['data'] = { 'dataset': dataset.id, - 'trait' : trait_id + 'trait': trait_id } resource_ob['type'] = 'dataset-publish' elif dataset.type == "Geno": @@ -74,15 +75,19 @@ def add_new_resource(dataset, trait_id=None): return resource_info + def get_group_code(dataset): - results = g.db.execute("SELECT InbredSetCode from InbredSet where Name='{}'".format(dataset.group.name)).fetchone() + results = g.db.execute("SELECT InbredSetCode from InbredSet where Name='{}'".format( + dataset.group.name)).fetchone() if results[0]: return results[0] else: return "" + def check_admin(resource_id=None): - the_url = "http://localhost:8080/available?resource={}&user={}".format(resource_id, g.user_session.user_id) + the_url = "http://localhost:8080/available?resource={}&user={}".format( + resource_id, g.user_session.user_id) try: response = json.loads(requests.get(the_url).content)['admin'] except: @@ -96,6 +101,7 @@ def check_admin(resource_id=None): else: return "not-admin" + def check_owner(dataset=None, trait_id=None, resource_id=None): if resource_id: resource_info = get_resource_info(resource_id) @@ -110,6 +116,7 @@ def check_owner(dataset=None, trait_id=None, resource_id=None): return False + def check_owner_or_admin(dataset=None, trait_id=None, resource_id=None): if not resource_id: if dataset.type == "Temp": diff --git a/wqflask/utility/hmac.py b/wqflask/utility/hmac.py index b08be97e..fd75803e 100644 --- a/wqflask/utility/hmac.py +++ b/wqflask/utility/hmac.py @@ -7,11 +7,11 @@ from flask import url_for from wqflask import app + def hmac_creation(stringy): """Helper function to create the actual hmac""" secret = app.config['SECRET_HMAC_CODE'] - hmaced = hmac.new(secret, stringy, hashlib.sha1) hm = hmaced.hexdigest() # ZS: Leaving the below comment here to ask Pjotr about @@ -20,10 +20,12 @@ def hmac_creation(stringy): hm = hm[:20] return hm + def data_hmac(stringy): - """Takes arbitray data string and appends :hmac so we know data hasn't been tampered with""" + """Takes arbitrary data string and appends :hmac so we know data hasn't been tampered with""" return stringy + ":" + hmac_creation(stringy) + def url_for_hmac(endpoint, **values): """Like url_for but adds an hmac at the end to insure the url hasn't been tampered with""" @@ -36,5 +38,6 @@ def url_for_hmac(endpoint, **values): combiner = "?" return url + combiner + "hm=" + hm + app.jinja_env.globals.update(url_for_hmac=url_for_hmac, - data_hmac=data_hmac)
\ No newline at end of file + data_hmac=data_hmac) |