Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.

Commit 1f2533a

Browse files
authored
Merge pull request #424 from CSCfi/feature/visa_issuer_check
[#227] implement jku checking feature
2 parents 53821cb + 65afd59 commit 1f2533a

5 files changed

Lines changed: 70 additions & 1 deletion

File tree

beacon_api/conf/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,14 @@ def parse_oauth2_config_file(path: str) -> Any:
6363
"""Parse configuration file."""
6464
config = ConfigParser()
6565
config.read(path)
66-
config_vars: Dict[str, Union[str, bool, None]] = {
66+
config_vars: Dict[str, Union[str, bool, None, List[str]]] = {
6767
"server": config.get("oauth2", "server"),
6868
"issuers": config.get("oauth2", "issuers"),
6969
"userinfo": config.get("oauth2", "userinfo"),
7070
"audience": config.get("oauth2", "audience") or None,
7171
"verify_aud": bool(strtobool(config.get("oauth2", "verify_aud"))),
7272
"bona_fide_value": config.get("oauth2", "bona_fide_value"),
73+
"trusted_jkus": config.get("oauth2", "trusted_jkus", fallback="").split(","),
7374
}
7475
return convert(config_vars)
7576

beacon_api/conf/config.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,8 @@ audience=
122122
# If your service is not part of any network or AAI, but you still want to use tokens
123123
# produced by other AAI parties, set this value to False to skip the audience validation step
124124
verify_aud=False
125+
126+
# Comma separated list of trusted JKUs for checking passports
127+
# Passport with an untrusted JKU will be denied access
128+
# Leave empty to disable JKU checking
129+
trusted_jkus=

beacon_api/permissions/ga4gh.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ async def get_ga4gh_permissions(token: str) -> Tuple[set, bool]:
163163
for encoded_passport in encoded_passports:
164164
# Decode passport
165165
header, payload = await decode_passport(encoded_passport)
166+
# If trusted_jkus variable is set, only allow passports with a trusted JKU
167+
if not OAUTH2_CONFIG.trusted_jkus == [""]:
168+
# Skip passports with untrusted JKUs
169+
passport_jku = header.get("jku")
170+
if passport_jku not in OAUTH2_CONFIG.trusted_jkus:
171+
LOG.debug("Untrusted JKU.")
172+
continue
166173
# Sort passports that carry dataset permissions
167174
pass_type = payload.get("ga4gh_visa_v1", {}).get("type")
168175
if pass_type == "ControlledAccessGrants": # nosec

tests/test.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,8 @@ audience=
116116
# If your service is not part of any network or AAI, but you still want to use tokens
117117
# produced by other AAI parties, set this value to False to skip the audience validation step
118118
verify_aud=False
119+
120+
# Comma separated list of trusted JKUs for checking passports
121+
# Passport with an untrusted JKU will be denied access
122+
# Leave empty to disable JKU checking
123+
trusted_jkus=http://test.csc.fi/jwk

tests/test_basic.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .test_app import PARAMS, generate_token
1010
from testfixtures import TempDirectory
1111
from test.support.os_helper import EnvironmentVarGuard
12+
from beacon_api.conf import OAUTH2_CONFIG
1213

1314

1415
def mock_token(bona_fide, permissions, auth):
@@ -70,6 +71,11 @@ async def load_datafile(self, vcf, datafile, datasetId, n=1000, min_ac=1):
7071
return ["datasetId", "variants"]
7172

7273

74+
async def mock_get_ga4gh_controlled(input):
75+
"""Mock retrieve dataset permissions."""
76+
return input
77+
78+
7379
class TestBasicFunctions(unittest.IsolatedAsyncioTestCase):
7480
"""Test supporting functions."""
7581

@@ -482,5 +488,50 @@ async def test_get_ga4gh_permissions(self, m_userinfo, m_decode, m_controlled, m
482488
self.assertEqual(bona_fide_status, True)
483489

484490

491+
class TestCaseCheckJku(unittest.IsolatedAsyncioTestCase):
492+
"""Test case."""
493+
494+
@unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_bona_fide")
495+
@unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_controlled", side_effect=mock_get_ga4gh_controlled)
496+
@unittest.mock.patch("beacon_api.permissions.ga4gh.decode_passport")
497+
@unittest.mock.patch("beacon_api.permissions.ga4gh.retrieve_user_data")
498+
async def test_jku_check(self, m_userinfo, m_decode, m_controller, m_bonafide):
499+
"""Test trusted and untrusted jku."""
500+
# Test: trusted jku
501+
m_userinfo.return_value = [""]
502+
header = {"jku": "http://test.csc.fi/jwk"}
503+
payload = {"ga4gh_visa_v1": {"type": "ControlledAccessGrants"}}
504+
m_decode.return_value = header, payload
505+
m_bonafide.return_value = False
506+
dataset_permissions, bona_fide_status = await get_ga4gh_permissions({})
507+
self.assertEqual(dataset_permissions, [("", header)])
508+
self.assertEqual(bona_fide_status, False)
509+
# Test: untrusted jku
510+
m_userinfo.return_value = [""]
511+
header = {"jku": "untrusted_jku"}
512+
payload = {"ga4gh_visa_v1": {"type": "ControlledAccessGrants"}}
513+
m_decode.return_value = header, payload
514+
m_bonafide.return_value = False
515+
dataset_permissions, bona_fide_status = await get_ga4gh_permissions({})
516+
self.assertEqual(dataset_permissions, [])
517+
self.assertEqual(bona_fide_status, False)
518+
519+
@unittest.mock.patch("beacon_api.permissions.ga4gh.OAUTH2_CONFIG", new=OAUTH2_CONFIG._replace(trusted_jkus=[""]))
520+
@unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_bona_fide")
521+
@unittest.mock.patch("beacon_api.permissions.ga4gh.get_ga4gh_controlled", side_effect=mock_get_ga4gh_controlled)
522+
@unittest.mock.patch("beacon_api.permissions.ga4gh.decode_passport")
523+
@unittest.mock.patch("beacon_api.permissions.ga4gh.retrieve_user_data")
524+
async def test_jku_check_not_active(self, m_userinfo, m_decode, m_controller, m_bonafide):
525+
"""Test if jku check is skipped when trusted_jkus config var is not set."""
526+
m_userinfo.return_value = [""]
527+
header = {"jku": "untrusted_jku"}
528+
payload = {"ga4gh_visa_v1": {"type": "ControlledAccessGrants"}}
529+
m_decode.return_value = header, payload
530+
m_bonafide.return_value = False
531+
dataset_permissions, bona_fide_status = await get_ga4gh_permissions({})
532+
self.assertEqual(dataset_permissions, [("", header)])
533+
self.assertEqual(bona_fide_status, False)
534+
535+
485536
if __name__ == "__main__":
486537
unittest.main()

0 commit comments

Comments
 (0)