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

Commit 564aace

Browse files
authored
Merge pull request #133 from CSCfi/bugfix/review-code
Sanity check and review code for small improvements
2 parents c70bd0a + 1b4cf69 commit 564aace

17 files changed

Lines changed: 253 additions & 80 deletions

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ omit =
1414
[report]
1515
# Regexes for lines to exclude from consideration
1616
exclude_lines =
17+
pragma: no cover
1718
# Don't complain about missing debug-only code:
1819
def __repr__
1920
if self\.debug

Pipfile

Lines changed: 0 additions & 18 deletions
This file was deleted.

beacon_api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
__license__ = CONFIG_INFO.license
1414
__copyright__ = CONFIG_INFO.copyright
1515
__docs_url__ = CONFIG_INFO.docs_url
16-
__handover_drs__ = CONFIG_INFO.handover_drs
16+
__handover_drs__ = CONFIG_INFO.handover_drs.rstrip("/")
1717
__handover_datasets__ = CONFIG_INFO.handover_datasets
1818
__handover_beacon__ = CONFIG_INFO.handover_beacon
1919
__handover_base__ = CONFIG_INFO.handover_base

beacon_api/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ async def initialize(app):
8282

8383
async def destroy(app):
8484
"""Upon server close, close the DB connection pool."""
85-
await app['pool'].close()
85+
# will defer this to asyncpg
86+
await app['pool'].close() # pragma: no cover
8687

8788

8889
def set_cors(server):

beacon_api/extensions/handover.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def make_handover(paths, datasetIds, chr='', start=0, end=0, ref='', alt='', var
2222
for dataset in set(datasetIds):
2323
handovers.append({"handoverType": {"id": "CUSTOM", "label": label},
2424
"description": desc,
25-
"url": __handover_drs__ + path.format(dataset=dataset, chr=chr, start=start,
26-
end=end, ref=ref, alt=alt)})
25+
"url": __handover_drs__ + "/" + path.format(dataset=dataset, chr=chr, start=start,
26+
end=end, ref=ref, alt=alt)})
2727

2828
return handovers

beacon_api/utils/db_load.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,11 @@ async def load_metadata(self, vcf, metafile, datafile):
222222
LOG.info(f'Calculate number of samples from {datafile}')
223223
len_samples = len(vcf.samples)
224224
LOG.info(f'Parse metadata from {metafile}')
225-
with open(metafile, 'r') as metafile:
225+
with open(metafile, 'r') as meta_file:
226226
# read metadata from given JSON file
227227
# TO DO: parse metadata directly from datafile if possible
228-
LOG.info(metafile)
229-
metadata = json.load(metafile)
228+
LOG.info(meta_file)
229+
metadata = json.load(meta_file)
230230
LOG.info(metadata)
231231
LOG.info('Metadata has been parsed')
232232
try:
@@ -255,7 +255,8 @@ async def load_metadata(self, vcf, metafile, datafile):
255255
LOG.error(f'AN ERROR OCCURRED WHILE ATTEMPTING TO INSERT METADATA -> {e}')
256256
except Exception as e:
257257
LOG.error(f'AN ERROR OCCURRED WHILE ATTEMPTING TO PARSE METADATA -> {e}')
258-
return metadata['datasetId']
258+
else:
259+
return metadata['datasetId']
259260

260261
def _chunks(self, iterable, size):
261262
"""Chunk records.

beacon_api/utils/validate.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def set_defaults(validator, properties, instance, schema):
5454
for error in validate_properties(
5555
validator, properties, instance, schema,
5656
):
57-
yield error
57+
# Difficult to unit test
58+
yield error # pragma: no cover
5859

5960
return validators.extend(
6061
validator_class, {"properties": set_defaults},
@@ -76,8 +77,6 @@ def wrapper(func):
7677
@wraps(func)
7778
async def wrapped(*args):
7879
request = args[-1]
79-
if not isinstance(request, web.Request):
80-
raise BeaconBadRequest(request, request.host, "invalid request", "This does not seem a valid HTTP Request.")
8180
try:
8281
_, obj = await parse_request_object(request)
8382
except Exception:
@@ -121,7 +120,21 @@ def token_scheme_check(token, scheme, obj, host):
121120
raise BeaconUnauthorised(obj, host, "invalid_token", 'Invalid token scheme, Bearer required.')
122121

123122
if token is None:
124-
raise BeaconUnauthorised(obj, host, "invalid_token", 'Token cannot be empty.')
123+
# Might never happen
124+
raise BeaconUnauthorised(obj, host, "invalid_token", 'Token cannot be empty.') # pragma: no cover
125+
126+
127+
def verify_aud_claim():
128+
"""Verify audience claim."""
129+
aud = []
130+
verify_aud = OAUTH2_CONFIG.verify_aud # Option to skip verification of `aud` claim
131+
if verify_aud:
132+
aud = os.environ.get('JWT_AUD', OAUTH2_CONFIG.audience) # List of intended audiences of token
133+
# if verify_aud is set to True, we expect that a desired aud is then supplied.
134+
# However, if verify_aud=True and no aud is supplied, we use aud=[None] which will fail for
135+
# all tokens as a security measure. If aud=[], all tokens will pass (as is the default value).
136+
aud = aud.split(',') if aud is not None else [None]
137+
return verify_aud, aud
125138

126139

127140
def token_auth():
@@ -132,8 +145,6 @@ def token_auth():
132145
"""
133146
@web.middleware
134147
async def token_middleware(request, handler):
135-
if not isinstance(request, web.Request):
136-
raise BeaconBadRequest(request, request.host, "invalid request", "This does not seem a valid HTTP Request.")
137148
if request.path in ['/query'] and 'Authorization' in request.headers:
138149
_, obj = await parse_request_object(request)
139150
try:
@@ -147,14 +158,7 @@ async def token_middleware(request, handler):
147158

148159
# Token decoding parameters
149160
key = await get_key() # JWK used to decode token with
150-
aud = []
151-
verify_aud = OAUTH2_CONFIG.verify_aud # Option to skip verification of `aud` claim
152-
if verify_aud:
153-
aud = os.environ.get('JWT_AUD', OAUTH2_CONFIG.audience) # List of intended audiences of token
154-
# if verify_aud is set to True, we expect that a desired aud is then supplied.
155-
# However, if verify_aud=True and no aud is supplied, we use aud=[None] which will fail for
156-
# all tokens as a security measure. If aud=[], all tokens will pass (as is the default value).
157-
aud = aud.split(',') if aud is not None else [None]
161+
verify_aud, aud = verify_aud_claim()
158162
# Prepare JWTClaims validation
159163
# can be populated with claims that are required to be present in the payload of the token
160164
claims_options = {
@@ -195,14 +199,15 @@ async def token_middleware(request, handler):
195199
# currently if a token is valid that means request is authenticated
196200
"authenticated": True}
197201
return await handler(request)
202+
# Testing the exceptions is done in integration tests
198203
except MissingClaimError as e:
199-
raise BeaconUnauthorised(obj, request.host, "invalid_token", f'Missing claim(s): {e}')
204+
raise BeaconUnauthorised(obj, request.host, "invalid_token", f'Missing claim(s): {e}') # pragma: no cover
200205
except ExpiredTokenError as e:
201-
raise BeaconUnauthorised(obj, request.host, "invalid_token", f'Expired signature: {e}')
206+
raise BeaconUnauthorised(obj, request.host, "invalid_token", f'Expired signature: {e}') # pragma: no cover
202207
except InvalidClaimError as e:
203-
raise BeaconForbidden(obj, request.host, f'Token info not corresponding with claim: {e}')
208+
raise BeaconForbidden(obj, request.host, f'Token info not corresponding with claim: {e}') # pragma: no cover
204209
except InvalidTokenError as e:
205-
raise BeaconUnauthorised(obj, request.host, "invalid_token", f'Invalid authorization token: {e}')
210+
raise BeaconUnauthorised(obj, request.host, "invalid_token", f'Invalid authorization token: {e}') # pragma: no cover
206211
else:
207212
request["token"] = {"bona_fide_status": False,
208213
"permissions": None,

docs/deploy.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ For use with Kubernetes we provide ``YAML`` configuration.
7676
role: beacon
7777
spec:
7878
containers:
79-
- image: cscfi/beacon
79+
- image: cscfi/beacon-python
8080
imagePullPolicy: Always
8181
name: beacon
8282
ports:
@@ -88,8 +88,9 @@ For use with Kubernetes we provide ``YAML`` configuration.
8888
name: data
8989
volumes:
9090
- name: data
91-
persistentVolumeClaim:
92-
claimName: beaconpy
91+
# change below with preferred volume class
92+
hostPath:
93+
path: /local/disk/path
9394
---
9495
apiVersion: v1
9596
kind: Service

docs/permissions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ The permissions are then passed in :meth:`beacon_api.utils.validate` as illustra
6161
.. literalinclude:: /../beacon_api/utils/validate.py
6262
:language: python
6363
:dedent: 16
64-
:lines: 175-188
64+
:lines: 179-192
6565

6666
If there is no claim for GA4GH permissions as illustrated above, they will not be added to
6767
``controlled_datasets``.

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
aiohttp
22
aiohttp_cors
33
asyncpg
4-
jsonschema==3.0.2
4+
jsonschema
55
Cython
66
numpy
77
cyvcf2==0.10.1; python_version < '3.7'

0 commit comments

Comments
 (0)