Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
elixir
beacon
Commits
c0e45aba
Commit
c0e45aba
authored
Feb 16, 2018
by
Jacek Lebioda
Browse files
Restricted datasets work
parent
725bda0d
Changes
5
Hide whitespace changes
Inline
Side-by-side
beacon/data_types.py
View file @
c0e45aba
...
...
@@ -12,6 +12,72 @@ import re
from
beacon
import
helpers
class
APIArgumentError
(
ValueError
):
"""
Exception thrown if there's something wrong with the argument
provided to API query
"""
pass
def
create_beacon_dataset
(
**
kwargs
):
"""
Creates an object which describes a dataset
Needs the following keys in `**kwargs`:
'id', 'name', 'assemblyId', 'createDateTime', 'updateDateTime'
Optional keys in `**kwargs`:
'description', 'version', 'variantCount', 'callCount', 'sampleCount'
'externalUrl', 'info'
"""
fields_required
=
[
'id'
,
'name'
,
'assemblyId'
,
'createDateTime'
,
'updateDateTime'
]
fields_optional
=
[
'description'
,
'version'
,
'variantCount'
,
'callCount'
,
'sampleCount'
,
'externalUrl'
,
'info'
]
return
populate_fields
({},
fields_required
,
fields_optional
,
**
kwargs
)
def
validate_input
(
**
kwargs
):
"""
Validates query parameters. If there's a violation,
an `APIArgumentError` exception is raised
"""
def
wrap
(
name
,
message
,
regexp
):
re_regexp
=
re
.
compile
(
regexp
)
if
not
helpers
.
check_input
(
kwargs
,
name
,
re_regexp
):
arg
=
kwargs
.
get
(
name
)
if
kwargs
.
get
(
name
)
is
not
None
else
'<<None>>'
raise
APIArgumentError
(
"Invalid {0} ({1}) - {2}"
.
format
(
name
,
arg
,
message
))
wrap
(
'referenceName'
,
'accepted ones are: 1-22, X, Y, M, MT'
,
r
'^([1-9]|1\d|2[012]|[Xx]|[Yy]|[Mm][Tt]?)$'
)
wrap
(
'start'
,
'should be a positive number'
,
r
'^\d+$'
)
wrap
(
'assemblyId'
,
'accepted ones are: GRCh38, GRCh37 and NCBI36'
,
r
'^([Gg][Rr][Cc][Hh](38|37)|[Nn][Cc][Bb][Ii]36)$'
)
wrap
(
'referenceBases'
,
'should contain only A, C, T, G, D'
,
r
'^[AaCcTtGgDd]*$'
)
wrap
(
'alternateBases'
,
'should contain only A, C, T, G, D'
,
r
'^[AaCcTtGgDd]*$'
)
whitelist
=
[
True
,
False
,
'true'
,
'false'
,
'True'
,
'False'
,
'1'
]
if
'includeDatasetResponses'
in
kwargs
and
kwargs
[
'includeDatasetResponses'
]
not
in
whitelist
:
raise
APIArgumentError
(
"Invalid {0} ({1}) - {2}"
.
format
(
"includeDatasetResponses"
,
kwargs
[
'includeDatasetResponses'
],
'a boolean was expected'
))
return
True
def
inject_required
(
what_to_inject
,
from_what
,
to_what
):
"""
Extends the object `to_what` with `what_to_inject`.
...
...
@@ -213,69 +279,3 @@ def create_beacon_error(**kwargs):
fields_optional
=
[
'message'
,
'errorCode'
]
return
populate_fields
(
error
,
fields_required
,
fields_optional
,
**
kwargs
)
def
create_beacon_dataset
(
**
kwargs
):
"""
Creates an object which describes a dataset
Needs the following keys in `**kwargs`:
'id', 'name', 'assemblyId', 'createDateTime', 'updateDateTime'
Optional keys in `**kwargs`:
'description', 'version', 'variantCount', 'callCount', 'sampleCount'
'externalUrl', 'info'
"""
fields_required
=
[
'id'
,
'name'
,
'assemblyId'
,
'createDateTime'
,
'updateDateTime'
]
fields_optional
=
[
'description'
,
'version'
,
'variantCount'
,
'callCount'
,
'sampleCount'
,
'externalUrl'
,
'info'
]
return
populate_fields
({},
fields_required
,
fields_optional
,
**
kwargs
)
def
validate_input
(
**
kwargs
):
"""
Validates query parameters. If there's a violation,
an `APIArgumentError` exception is raised
"""
def
wrap
(
name
,
message
,
regexp
):
re_regexp
=
re
.
compile
(
regexp
)
if
not
helpers
.
check_input
(
kwargs
,
name
,
re_regexp
):
arg
=
kwargs
.
get
(
name
)
if
kwargs
.
get
(
name
)
is
not
None
else
'<<None>>'
raise
APIArgumentError
(
"Invalid {0} ({1}) - {2}"
.
format
(
name
,
arg
,
message
))
wrap
(
'referenceName'
,
'accepted ones are: 1-22, X, Y, M, MT'
,
r
'^([1-9]|1\d|2[012]|[Xx]|[Yy]|[Mm][Tt]?)$'
)
wrap
(
'start'
,
'should be a positive number'
,
r
'^\d+$'
)
wrap
(
'assemblyId'
,
'accepted ones are: GRCh38, GRCh37 and NCBI36'
,
r
'^([Gg][Rr][Cc][Hh](38|37)|[Nn][Cc][Bb][Ii]36)$'
)
wrap
(
'referenceBases'
,
'should contain only A, C, T, G, D'
,
r
'^[AaCcTtGgDd]*$'
)
wrap
(
'alternateBases'
,
'should contain only A, C, T, G, D'
,
r
'^[AaCcTtGgDd]*$'
)
whitelist
=
[
True
,
False
,
'true'
,
'false'
,
'True'
,
'False'
,
'1'
]
if
'includeDatasetResponses'
in
kwargs
and
kwargs
[
'includeDatasetResponses'
]
not
in
whitelist
:
raise
APIArgumentError
(
"Invalid {0} ({1}) - {2}"
.
format
(
"includeDatasetResponses"
,
kwargs
[
'includeDatasetResponses'
],
'a boolean was expected'
))
return
True
class
APIArgumentError
(
ValueError
):
"""
Exception thrown if there's something wrong with the argument
provided to API query
"""
pass
beacon/helpers.py
View file @
c0e45aba
...
...
@@ -9,9 +9,6 @@ import json
import
os
import
urllib.request
from
beacon.data_types
import
APIArgumentError
from
beacon.middleware
import
build_query
def
make_http_request
(
uri
):
"""
...
...
@@ -95,20 +92,3 @@ def load_json(path_to_file):
return
the_object
except
ValueError
as
e
:
raise
ValueError
(
"It is not a correct JSON! {0}"
.
format
(
str
(
e
.
args
)))
def
build_urls
(
instances
,
**
kwargs
):
"""
Returns list of all URLs to query.
"""
the_query
=
build_query
(
**
kwargs
)
if
not
len
(
instances
):
message
=
"The beacon have no datasets or the specified datasets do not exist!"
raise
APIArgumentError
(
message
)
urls
=
[
uri
[
'endpoint'
]
+
the_query
for
uri
in
instances
]
return
urls
beacon/middleware.py
View file @
c0e45aba
...
...
@@ -10,9 +10,9 @@ import re
from
flask
import
jsonify
from
beacon
import
data_types
,
helpers
,
liftover
from
beacon.data_types
import
validate_input
,
APIArgumentError
from
beacon.
helper
s
import
build_urls
from
beacon.settings
import
restrict_access
,
raise_error_if_not_sufficient_permission
s
from
beacon.data_types
import
APIArgumentError
,
validate_input
from
beacon.
setting
s
import
raise_error_if_not_sufficient_permissions
,
\
restrict_acces
s
def
query
(
configuration
,
request
,
user_session_data
):
...
...
@@ -29,9 +29,11 @@ def query(configuration, request, user_session_data):
validate_input
(
**
request
)
data_sets
=
request
.
get
(
'datasetIds'
,
[])
instances
=
configuration
.
get_databases
(
data_sets
)
instances
,
filtered_instances
=
restrict_access
(
instances
,
user_session_data
)
raise_error_if_not_sufficient_permissions
(
filtered_instances
,
**
request
)
all_instances
=
configuration
.
get_databases
(
data_sets
)
instances
,
filtered_instances
=
restrict_access
(
all_instances
,
user_session_data
)
raise_error_if_not_sufficient_permissions
(
filtered_instances
,
**
request
)
urls
=
build_urls
(
instances
,
**
request
)
variantdb_responses
=
helpers
.
make_http_requests
(
urls
)
...
...
@@ -103,3 +105,18 @@ def include_dataset_responses(where_to_inject, responses_to_inject, databases):
# This is a place where obtaining more information would happen
def
build_urls
(
instances
,
**
kwargs
):
"""
Returns list of all URLs to query.
"""
the_query
=
build_query
(
**
kwargs
)
if
not
len
(
instances
):
message
=
"The beacon have no datasets or the specified datasets do not exist!"
raise
APIArgumentError
(
message
)
urls
=
[
uri
[
'endpoint'
]
+
the_query
for
uri
in
instances
]
return
urls
beacon/settings.py
View file @
c0e45aba
...
...
@@ -10,6 +10,7 @@ the settings file or load it from `local_settings.json` from project's root.
import
os
import
sys
from
copy
import
deepcopy
from
beacon
import
settings_loader
from
beacon.data_types
import
APIArgumentError
from
beacon.settings_loader
import
DATASET_PUBLIC
,
DATASET_CONTROLLED
...
...
@@ -58,6 +59,28 @@ class Settings:
else
:
return
self
.
DatasetConnections
def
get_beacon_information_for_user
(
self
,
user_data
):
"""Returns information about the configuration with respect to the user's privileges"""
beacon_information
=
deepcopy
(
self
.
Beacon
)
all_datasets
=
beacon_information
.
get
(
'datasets'
,
[])
for
dataset
in
all_datasets
:
dataset_metadata
=
self
.
get_metadata_for_dataset
(
dataset
)
if
should_be_accessible_by
(
dataset
,
dataset_metadata
,
user_data
):
dataset
[
'info'
][
'authorized'
]
=
"true"
else
:
dataset
[
'info'
][
'authorized'
]
=
"false"
beacon_information
[
'datasets'
]
=
all_datasets
return
beacon_information
def
get_metadata_for_dataset
(
self
,
dataset
):
dataset_id
=
dataset
.
get
(
'id'
)
for
metadata
in
self
.
DatasetConnections
:
if
metadata
[
'id'
]
==
dataset_id
:
return
metadata
raise
Exception
(
'No metadata found for given dataset ID! id={}'
.
format
(
dataset_id
))
def
load_configuration
(
self
):
"""
Tries to load the configuration file gracefully from two sources.
...
...
@@ -109,24 +132,11 @@ class Settings:
self
.
_try_to_load_from_source
(
filename
,
"config file"
)
def
get_configuration_for_user
(
configuration
,
user_data
):
"""Returns information about the configuration with respect to the user's privileges"""
all_datasets
=
configuration
[
'datasets'
]
configuration
[
'datasets'
]
=
[]
for
dataset
in
all_datasets
:
if
should_be_accessible_by
(
dataset
,
user_data
):
dataset
[
'info'
][
'authorized'
]
=
"true"
else
:
dataset
[
'info'
][
'authorized'
]
=
"false"
configuration
[
'datasets'
].
append
(
dataset
)
return
configuration
def
should_be_accessible_by
(
dataset
,
user_data
):
def
should_be_accessible_by
(
dataset
,
dataset_metadata
,
user_data
):
"""Checks, if the dataset provided in the argument should be accessible by the user"""
if
is_dataset_public
(
dataset
):
return
True
if
dataset
[
'n
ame'
]
in
user_data
:
if
dataset
_metadata
[
'aaiResourceN
ame'
]
in
user_data
:
return
True
return
False
...
...
beacon/web/views.py
View file @
c0e45aba
...
...
@@ -41,19 +41,22 @@ def register_views(app, configuration):
def
view_login
():
group_names
=
oidc
.
user_getfield
(
"groupNames"
)
or
[]
session
[
'permissions'
]
=
group_names
return
render_template
(
'index.html'
,
root
=
settings
.
API_PREFIX
,
sub
=
oidc
.
user_getfield
(
"sub"
),
name
=
oidc
.
user_getfield
(
"name"
),
preferred_username
=
oidc
.
user_getfield
(
"preferred_username"
),
email
=
oidc
.
user_getfield
(
"email"
),
bona_fide_status
=
oidc
.
user_getfield
(
"bona_fide_status"
),
groupNames
=
oidc
.
user_getfield
(
"groupNames"
),
)
# In case anyone needs it
user_information
=
{
'sub'
:
oidc
.
user_getfield
(
"sub"
),
'name'
:
oidc
.
user_getfield
(
"name"
),
'preferred_username'
:
oidc
.
user_getfield
(
"preferred_username"
),
'email'
:
oidc
.
user_getfield
(
"email"
),
'bona_fide_status'
:
oidc
.
user_getfield
(
"bona_fide_status"
),
'groupNames'
:
oidc
.
user_getfield
(
"groupNames"
)
}
return
render_template
(
'index.html'
,
root
=
settings
.
API_PREFIX
)
@
app
.
route
(
'/logout'
,
methods
=
[
'GET'
,
'POST'
])
def
view_logout
():
session
[
'permissions'
]
=
''
session
[
'permissions'
]
=
[]
oidc
.
logout
()
return
redirect
(
'https://perun.elixir-czech.cz/oidc/endsession'
)
...
...
@@ -63,7 +66,7 @@ def register_views(app, configuration):
def
api_beacon
():
"""`/beacon/` API endpoint"""
user_session_data
=
session
.
get
(
'permissions'
,
[])
beacon_information
=
settings
.
get_configuration_for_user
(
configuration
.
Beacon
,
user_session_data
)
beacon_information
=
configuration
.
get_beacon_information_for_user
(
user_session_data
)
return
jsonify
(
beacon_information
)
@
app
.
route
(
settings
.
prefix
(
'/beacon/query'
),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment