Skip to content

Commit

Permalink
Merge pull request #253 from EGA-archive/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
costero-e authored Nov 29, 2023
2 parents 6b00b5f + 868704e commit 2cf0345
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 89 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/push_docker_image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Create and publish a Docker image

on:
push:
branches: ['develop', 'master']
tags:
- 'v*.*.*'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
strategy:
matrix:
platform:
- linux/amd64
- linux/arm64
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
# Indicates that the Docker build context is the current directory.
# Otherwise, it would be a Git URL (doing a separate clean checkout).
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}x
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,19 @@ This repository is an implementation of the [Beacon v2.0 Model](https://github.c
### Managing AAI-LSAAI permissions

To give the right permissions for AAI you will need to etierh set the permissions of the users inside permissions folder, within the [public_datasets.yml](permissions/public_datasets.yml), [registered_datasets.yml](permissions/registered_datasets.yml), [controlled_datasets.yml](permissions/controlled_datasets.yml) files, or run the beacon admin page that allows you to manage all the permissions in a friendly way and no need to open .yml files. Just start the UI, that will run in http://localhost:8010, by executing this command from the deploy folder after the containers are up and running:
To give the right permissions for AAI you will need to set the permissions of the users inside permissions folder, within the [public_datasets.yml](permissions/public_datasets.yml), [registered_datasets.yml](permissions/registered_datasets.yml), [controlled_datasets.yml](permissions/controlled_datasets.yml) files, or run the beacon admin page that allows you to manage all the permissions in a friendly way and no need to open .yml files. Just start the UI, that will run in http://localhost:8010, by executing this command from the deploy folder after the containers are up and running:
```bash
docker exec beacon-permissions bash permissions/permissions-ui/start.sh
```
Please, bear in mind that the name of the user has to be the same that you used when creating the user in LS or in IDP, whatever the AAI method you are working with.

Also, you will need to edit the file [conf.py](beacon/conf.py) and introduce the domain where your keycloak is being hosted inside **ldp_user_info** and the issuers you trust for your token inside **trusted_issuers**. In case you want to run your local container, use this configuration:
```bash
idp_user_info = 'http://idp:8080/auth/realms/Beacon/protocol/openid-connect/userinfo'
lsaai_user_info = 'https://login.elixir-czech.org/oidc/userinfo'
trusted_issuers = ['http://idp:8080/auth/realms/Beacon', 'https://login.elixir-czech.org/oidc/']
```

When you have your access token, pass it in a header with **Authorization: Bearer** in your POST request to get your answers. This token works coming from either from LS AAI or from keycloak (idp container).

### Beacon security system
Expand Down
49 changes: 9 additions & 40 deletions beacon/request/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)
from beacon.utils.stream import json_stream
from beacon.db.datasets import get_datasets
from beacon.utils.auth import resolve_token, check_user
from beacon.utils.auth import resolve_token

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -58,7 +58,7 @@ async def wrapper(request: Request):
access_token = request.headers.get('Authorization')


visa_datasets=[]

LOG.debug(access_token)
if access_token is not None:
pass
Expand All @@ -70,37 +70,13 @@ async def wrapper(request: Request):
specific_datasets = []
access_token = access_token[7:] # cut out 7 characters: len('Bearer ')
LOG.debug(access_token)
if access_token != 'public':
decoded = jwt.decode(access_token, options={"verify_signature": False})
LOG.debug(decoded)
user = await check_user(access_token)
LOG.debug(user)
try:
token_username = user['preferred_username']
except Exception:
token_username = None
LOG.debug(token_username)
issuer = decoded['iss']
try:
visa_datasets = user['ga4gh_passport_v1']
except Exception:
pass



authorized_datasets, authenticated = await resolve_token(access_token, search_datasets)

authorized_datasets, authenticated, username = await resolve_token(access_token, search_datasets)

LOG.debug(authorized_datasets)
if visa_datasets:
for visa_dataset in visa_datasets:
try:
visa = jwt.decode(visa_dataset, options={"verify_signature": False}, algorithms=["RS256"])
LOG.debug(visa)
LOG.debug(visa["ga4gh_visa_v1"]["value"])
dataset_url = visa["ga4gh_visa_v1"]["value"]
dataset_url_splitted = dataset_url.split('/')
visa_dataset = dataset_url_splitted[-1]
authorized_datasets.append(visa_dataset)
except Exception:
visa_dataset = None


#LOG.debug('all datasets: %s', all_datasets)
LOG.info('resolved datasets: %s', authorized_datasets)
Expand Down Expand Up @@ -197,16 +173,13 @@ async def wrapper(request: Request):
if access_token != 'public':


if issuer in conf.trusted_issuers:
pass
else:
raise web.HTTPUnauthorized('invalid token')


with open("/beacon/beacon/request/response_type.yml", 'r') as response_type_file:
response_type_dict = yaml.safe_load(response_type_file)

try:
response_type = response_type_dict[token_username]
response_type = response_type_dict[username]
except Exception:
LOG.debug(Exception)
response_type = ['boolean']
Expand Down Expand Up @@ -286,10 +259,6 @@ async def wrapper(request: Request):
specific_datasets = []
access_token = access_token[7:] # cut out 7 characters: len('Bearer ')


decoded = jwt.decode(access_token, options={"verify_signature": False})
LOG.debug(decoded)
token_username = decoded['preferred_username']

authorized_datasets, authenticated = await resolve_token(access_token, search_datasets)
LOG.debug(authorized_datasets)
Expand Down
47 changes: 11 additions & 36 deletions beacon/utils/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,48 +40,23 @@ async def resolve_token(token, requested_datasets_ids):
raise web.HTTPUnauthorized(body=error)
'''
content = await resp.content.read()
authorized_datasets = content.decode('utf-8')
content = content.decode('utf-8')
content_splitted= content.split(':')
authorized_datasets = content_splitted[-1]
authorized_datasets_list = authorized_datasets.split('"')
try:
username_ = content_splitted[1]
username_list = username_.split('"')
username = username_list[1]
except Exception:
username = ''
LOG.debug(username)
auth_datasets = []
for auth_dataset in authorized_datasets_list:
if ',' not in auth_dataset:
if '[' not in auth_dataset:
if ']' not in auth_dataset:
auth_datasets.append(auth_dataset)
LOG.debug(auth_datasets)
return auth_datasets, True
return auth_datasets, True, username

async def check_user(access_token):
user = None
idp_user_info = idpu
lsaai_user_info = lsu
async with ClientSession(trust_env=True) as session:
headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ' + access_token }
LOG.debug('Contacting %s', idp_user_info)
async with session.get(idp_user_info, headers=headers) as resp:
LOG.debug('Response %s', resp)
if resp.status == 200:
user = await resp.json()
LOG.error(user)
return user
else:
content = await resp.text()
LOG.error('Not a Keycloak token')
#LOG.error('Content: %s', content)
user = 'public'

if user == 'public':
async with ClientSession(trust_env=True) as session:
headers = { 'Accept': 'application/json', 'Authorization': 'Bearer ' + access_token }
LOG.debug('Contacting %s', lsaai_user_info)
async with session.get(lsaai_user_info, headers=headers) as resp:
LOG.debug('Response %s', resp)
if resp.status == 200:
user = await resp.json()
return user
else:
content = await resp.text()
LOG.error('Not a LS AAI token')
LOG.error('Content: %s', content)
user = 'public'
return user
2 changes: 1 addition & 1 deletion deploy/data/individuals.json

Large diffs are not rendered by default.

132 changes: 132 additions & 0 deletions deploy/data/individuals_av.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
[
{
"ethnicity": {
"id": "NCIT:C42331",
"label": "African"
},
"id": "AV0101",
"interventionsOrProcedures": [
{
"procedureCode": {
"id": "OPCS4:L46.3",
"label": "OPCS(v4-0.0):Ligation of visceral branch of abdominal aorta NEC"
}
}
],
"measures": [
{
"assayCode": {
"id": "LOINC:35925-4",
"label": "BMI"
},
"date": "2021-09-24",
"measurementValue": {
"unit": {
"id": "NCIT:C49671",
"label": "Kilogram per Square Meter"
},
"value": "26.63838307"
}
},
{
"assayCode": {
"id": "LOINC:3141-9",
"label": "Weight"
},
"date": "2021-09-24",
"measurementValue": {
"unit": {
"id": "NCIT:C28252",
"label": "Kilogram"
},
"value": "85.6358"
}
},
{
"assayCode": {
"id": "LOINC:8308-9",
"label": "Height-standing"
},
"date": "2021-09-24",
"measurementValue": {
"unit": {
"id": "NCIT:C49668",
"label": "Centimeter"
},
"value": "179.2973"
}
}
],
"sex": {
"id": "NCIT:C20197",
"label": "male"
}
},
{
"ethnicity": {
"id": "NCIT:C41261",
"label": "British"
},
"geographicOrigin": {
"id": "GAZ:00002641",
"label": "England"
},
"id": "HG00097",
"interventionsOrProcedures": [
{
"procedureCode": {
"id": "OPCS4:K06.1",
"label": "OPCS(v4-0.0):Repositioning of transposed great arteries"
}
}
],
"measures": [
{
"assayCode": {
"id": "LOINC:35925-4",
"label": "BMI"
},
"date": "2021-09-24",
"measurementValue": {
"unit": {
"id": "NCIT:C49671",
"label": "Kilogram per Square Meter"
},
"value": "25.62278441"
}
},
{
"assayCode": {
"id": "LOINC:3141-9",
"label": "Weight"
},
"date": "2021-09-24",
"measurementValue": {
"unit": {
"id": "NCIT:C28252",
"label": "Kilogram"
},
"value": "64.5722"
}
},
{
"assayCode": {
"id": "LOINC:8308-9",
"label": "Height-standing"
},
"date": "2021-09-24",
"measurementValue": {
"unit": {
"id": "NCIT:C49668",
"label": "Centimeter"
},
"value": "158.7485"
}
}
],
"sex": {
"id": "NCIT:C16576",
"label": "female"
}
}
]
14 changes: 11 additions & 3 deletions permissions/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
LOG = logging.getLogger(__name__)

@bearer_required
async def permission(request: Request, username: Optional[str]):
async def permission(request: Request, username: Optional[str], list_visa_datasets: Optional [list]):

if request.headers.get('Content-Type') == 'application/json':
post_data = await request.json()
Expand All @@ -43,8 +43,16 @@ async def permission(request: Request, username: Optional[str]):
LOG.debug('requested datasets: %s', requested_datasets)
datasets = await request.app['permissions'].get(username, requested_datasets=requested_datasets)
LOG.debug('selected datasets: %s', datasets)

return web.json_response(list(datasets or [])) # cuz python-json doesn't like sets
dict_returned={}
dict_returned['username']=username
datasets=list(datasets)
LOG.error('visa_datasets: {}'.format(list_visa_datasets))
for visa_dataset in list_visa_datasets:
datasets.append(visa_dataset)
dict_returned['datasets']=list(datasets)
LOG.error(dict_returned['datasets'])

return web.json_response(dict_returned) # cuz python-json doesn't like sets

async def initialize(app):
"""Initialize server."""
Expand Down
Loading

0 comments on commit 2cf0345

Please sign in to comment.