Skip to content

Commit

Permalink
Merge pull request #125 from sennetconsortium/libpitt/116-board-eps
Browse files Browse the repository at this point in the history
Libpitt/116 board eps
  • Loading branch information
maxsibilla authored Aug 17, 2023
2 parents 25cfbdf + f07a702 commit 2df94f2
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 59 deletions.
3 changes: 2 additions & 1 deletion src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

# Local Modules
from lib.file_upload_helper import UploadFileHelper
from lib.neo4j_helper import Neo4jHelper

# Set logging format and level (default is warning)
# All the API logging is forwarded to the uWSGI server and gets written into the log file `uwsgi-ingest-api.log`
Expand Down Expand Up @@ -95,7 +96,7 @@
app.config['NEO4J_USERNAME'],
app.config['NEO4J_PASSWORD'])

app.neo4j_driver_instance = neo4j_driver_instance
Neo4jHelper.set_instance(neo4j_driver_instance)

logger.info("Initialized neo4j_driver module successfully :)")
except Exception:
Expand Down
11 changes: 10 additions & 1 deletion src/instance/app.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ GLOBUS_CLIENT_APP_NAME = 'SenNet Data Portal'

# Point to remote URL for testing and production deployment
GLOBUS_CLIENT_APP_URI = 'https://data.dev.sennetconsortium.org/'
DATA_INGEST_BOARD_APP_URI = 'http://localhost:3001/'
DATA_INGEST_BOARD_NAME = ‘Data Ingest Board - DEV’

GLOBUS_BASE_FILE_USER_NAME = 'hive'
GLOBUS_ADMIN_FILE_USER_NAME = 'shirey'
Expand All @@ -35,6 +37,14 @@ UUID_WEBSERVICE_URL = 'http://uuid-api:8080/uuid'
#Search-api
SEARCH_WEBSERVICE_URL = 'https://search-api.dev.sennetconsortium.org'

# The base url for the HuBMAP portal website
PORTAL_URL = 'https://data.dev.sennetconsortium.org/'

# The base url for the HuBMAP ingest website
INGEST_URL = 'https://ingest-api.dev.sennetconsortium.org/'

GLOBUS_APP_BASE_URL = 'https://app.globus.org'

# Directory where file uploads will be placed temporarily
# until they are committed
# Remember to set the proper file system user and group permission
Expand Down Expand Up @@ -86,4 +96,3 @@ DATACITE_SENNET_PREFIX = ''
UBKG_SERVER = 'https://ontology.api.hubmapconsortium.org/'
UBKG_ENDPOINT_VALUESET = 'valueset?parent_sab=SENNET&parent_code={code}&child_sabs=SENNET'
UBKG_CODES = '{"specimen_categories":"C020076", "organ_types":{"code": "C000008", "key": "organs", "endpoint": "organs?application_context=SENNET"}, "entities": "C000012", "source_types":"C050020", "assay_types":{"code": "C004000", "key": "datasets", "endpoint": "datasets?application_context=SENNET"}}'

36 changes: 36 additions & 0 deletions src/lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from flask import Blueprint, jsonify, request, Response, current_app, abort, json
import urllib.request
from hubmap_commons import file_helper as commons_file_helper

def get_globus_url(data_access_level, group_name, uuid):
globus_server_uuid = None
dir_path = " "
# public access
if data_access_level == "public":
globus_server_uuid = current_app.config['GLOBUS_PUBLIC_ENDPOINT_UUID']
access_dir = commons_file_helper.ensureTrailingSlashURL(current_app.config['ACCESS_LEVEL_PUBLIC'])
dir_path = dir_path + access_dir + "/"
# consortium access
elif data_access_level == 'consortium':
globus_server_uuid = current_app.config['GLOBUS_CONSORTIUM_ENDPOINT_UUID']
access_dir = commons_file_helper.ensureTrailingSlashURL(current_app.config['ACCESS_LEVEL_CONSORTIUM'])
dir_path = dir_path + access_dir + group_name + "/"
# protected access
elif data_access_level == 'protected':
globus_server_uuid = current_app.config['GLOBUS_PROTECTED_ENDPOINT_UUID']
access_dir = commons_file_helper.ensureTrailingSlashURL(current_app.config['ACCESS_LEVEL_PROTECTED'])
dir_path = dir_path + access_dir + group_name + "/"

if globus_server_uuid is not None:
dir_path = dir_path + uuid + "/"
dir_path = urllib.parse.quote(dir_path, safe='')

# https://current_app.globus.org/file-manager?origin_id=28bb03c-a87d-4dd7-a661-7ea2fb6ea631&origin_path=2%FIEC%20Testing%20Group%20F03584b3d0f8b46de1b29f04be1568779%2F
globus_url = commons_file_helper.ensureTrailingSlash(current_app.config[
'GLOBUS_APP_BASE_URL']) + "file-manager?origin_id=" + globus_server_uuid + "&origin_path=" + dir_path

else:
globus_url = ""
if uuid is None:
globus_url = ""
return globus_url
23 changes: 21 additions & 2 deletions src/lib/file.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import csv
import os
import logging
from hubmap_commons import file_helper as commons_file_helper
from flask import current_app, request
from atlas_consortia_commons.rest import *
from werkzeug import utils
from collections import OrderedDict

from lib.file_upload_helper import UploadFileHelper

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -80,4 +80,23 @@ def ln_err(error: str, row: int = None, column: str = None):
'column': column,
'error': error,
'row': row
}
}


def files_exist(uuid, data_access_level):
if not uuid or not data_access_level:
return False
if data_access_level == "public":
absolute_path = commons_file_helper.ensureTrailingSlashURL(current_app.config['GLOBUS_PUBLIC_ENDPOINT_FILEPATH'])
# consortium access
elif data_access_level == 'consortium':
absolute_path = commons_file_helper.ensureTrailingSlashURL(current_app.config['GLOBUS_CONSORTIUM_ENDPOINT_FILEPATH'])
# protected access
elif data_access_level == 'protected':
absolute_path = commons_file_helper.ensureTrailingSlashURL(current_app.config['GLOBUS_PROTECTED_ENDPOINT_FILEPATH'])

file_path = absolute_path + uuid
if os.path.exists(file_path) and os.path.isdir(file_path) and os.listdir(file_path):
return True
else:
return False
26 changes: 26 additions & 0 deletions src/lib/neo4j_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from neo4j import Driver

neo4j_driver_instance = None


class Neo4jHelper:
@staticmethod
def set_instance(neo4j):
global neo4j_driver_instance
neo4j_driver_instance = neo4j

@staticmethod
def get_instance():
global neo4j_driver_instance
return neo4j_driver_instance

@staticmethod
def close():
# Specify as module-scope variable
global neo4j_driver_instance

if isinstance(neo4j_driver_instance, Driver):
neo4j_driver_instance.close()
neo4j_driver_instance = None
else:
raise TypeError("The private module variable '_driver' is not a neo4j.Driver object")
4 changes: 2 additions & 2 deletions src/lib/ontology.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from atlas_consortia_commons.ubkg.ubkg_sdk import UbkgSDK
from flask import current_app


def get_organ_types_ep():
return UbkgSDK.get_endpoint(current_app.ubkg.organ_types)

Expand All @@ -15,4 +14,5 @@ class Ontology(UbkgSDK):
def assay_types_ext():
Ontology.Ops.key = 'data_type'
Ontology.Ops.url_params = '&dataset_provider=external'
return Ontology.transform_ontology(current_app.ubkg.assay_types, 'AssayTypesExt')
return Ontology.transform_ontology(current_app.ubkg.assay_types, 'AssayTypesExt')

84 changes: 50 additions & 34 deletions src/routes/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,48 @@

# Endpoints for UI Login and Logout


# Redirect users from react app login page to Globus auth login widget then redirect back
@auth_blueprint.route('/login')
def login():
return _login(current_app.config['GLOBUS_CLIENT_APP_URI'])

@auth_blueprint.route('/data-ingest-board-login')
def data_ingest_login():
return _login(redirect_uri=current_app.config['DATA_INGEST_BOARD_APP_URI'], key='ingest_board_tokens')


@auth_blueprint.route('/logout')
def logout():
return _logout(redirect_uri=current_app.config['GLOBUS_CLIENT_APP_URI'], app_name=current_app.config['GLOBUS_CLIENT_APP_NAME'])


@auth_blueprint.route('/data-ingest-board-logout')
def data_ingest_logout():
return _logout(redirect_uri=current_app.config['DATA_INGEST_BOARD_APP_URI'], app_name=current_app.config['DATA_INGEST_BOARD_NAME'], key='ingest_board_tokens')


def get_user_info(token):
auth_client = AuthClient(authorizer=AccessTokenAuthorizer(token))
return auth_client.oauth2_userinfo()


def get_auth_header_dict(token) -> dict:
return {'Authorization': 'Bearer ' + token, 'X-SenNet-Application': 'ingest-api'}


def get_auth_header() -> dict:
auth_helper_instance = AuthHelper.instance()
token = auth_helper_instance.getAuthorizationTokens(request.headers)
return get_auth_header_dict(token)

def _login(redirect_uri, key = 'tokens'):
#redirect_uri = url_for('login', _external=True)
redirect_uri = current_app.config['FLASK_APP_BASE_URI'] + 'login'
_redirect_uri = current_app.config['FLASK_APP_BASE_URI'] + request.path.replace('/', '')

confidential_app_auth_client =\
confidential_app_auth_client = \
ConfidentialAppAuthClient(current_app.config['APP_CLIENT_ID'],
current_app.config['APP_CLIENT_SECRET'])
confidential_app_auth_client.oauth2_start_flow(redirect_uri, refresh_tokens=True)
confidential_app_auth_client.oauth2_start_flow(_redirect_uri, refresh_tokens=True)

# If there's no "code" query string parameter, we're in this route
# starting a Globus Auth login flow.
Expand Down Expand Up @@ -57,57 +88,42 @@ def login():
json_str = json.dumps(info)

# Store the resulting tokens in server session
session.update(
tokens=token_response.by_resource_server
)
# session.update(
# tokens=token_response.by_resource_server
# )
session[key] = token_response.by_resource_server

logger.info(f"Logged in User: {user_info['name']}")
# Finally redirect back to the client
return redirect(current_app.config['GLOBUS_CLIENT_APP_URI'] + '?info=' + str(json_str))
return redirect(redirect_uri + '?info=' + str(json_str))


@auth_blueprint.route('/logout')
def logout():
def _logout(redirect_uri, app_name, key='tokens'):
"""
- Revoke the tokens with Globus Auth.
- Destroy the session state.
- Redirect the user to the Globus Auth logout page.
"""
confidential_app_auth_client =\
confidential_app_auth_client = \
ConfidentialAppAuthClient(current_app.config['APP_CLIENT_ID'],
current_app.config['APP_CLIENT_SECRET'])

# Revoke the tokens with Globus Auth
if 'tokens' in session:
if key in session:
for token in (token_info['access_token']
for token_info in session['tokens'].values()):
confidential_app_auth_client.oauth2_revoke_token(token)
for token_info in session[key].values()):
confidential_app_auth_client.oauth2_revoke_token(token)

# Destroy the session state
session.clear()

# build the logout URI with query params
# there is no tool to help build this (yet!)
globus_logout_url = (
'https://auth.globus.org/v2/web/logout' +
'?client={}'.format(current_app.config['APP_CLIENT_ID']) +
'&redirect_uri={}'.format(current_app.config['GLOBUS_CLIENT_APP_URI']) +
'&redirect_name={}'.format(current_app.config['GLOBUS_CLIENT_APP_NAME']))
'https://auth.globus.org/v2/web/logout' +
'?client={}'.format(current_app.config['APP_CLIENT_ID']) +
'&redirect_uri={}'.format(redirect_uri) +
'&redirect_name={}'.format(app_name))

# Redirect the user to the Globus Auth logout page
return redirect(globus_logout_url)


def get_user_info(token):
auth_client = AuthClient(authorizer=AccessTokenAuthorizer(token))
return auth_client.oauth2_userinfo()


def get_auth_header_dict(token) -> dict:
return {'Authorization': 'Bearer ' + token, 'X-SenNet-Application': 'ingest-api'}


def get_auth_header() -> dict:
auth_helper_instance = AuthHelper.instance()
token = auth_helper_instance.getAuthorizationTokens(request.headers)
return get_auth_header_dict(token)
return redirect(globus_logout_url)
Loading

0 comments on commit 2df94f2

Please sign in to comment.