Skip to content

Commit

Permalink
Merge 67d1df2 into 6e55fcd
Browse files Browse the repository at this point in the history
  • Loading branch information
BinamB committed Feb 19, 2021
2 parents 6e55fcd + 67d1df2 commit 1c6735b
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 5 deletions.
22 changes: 22 additions & 0 deletions fence/blueprints/login/ras.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import flask
import jwt
import os
from flask_sqlalchemy_session import current_session

from fence.models import GA4GHVisaV1, IdentityProvider

from fence.blueprints.login.base import DefaultOAuth2Login, DefaultOAuth2Callback

from fence.config import config
from fence.scripting.fence_create import init_syncer


class RASLogin(DefaultOAuth2Login):
Expand Down Expand Up @@ -69,3 +71,23 @@ def post_login(self, user, token_result):
flask.current_app.ras_client.store_refresh_token(
user=user, refresh_token=refresh_token, expires=expires
)

if not user.project_access:
current_session.close()
DB = os.environ.get("FENCE_DB") or config.get("DB")
if DB is None:
try:
from fence.settings import DB
except ImportError:
pass
dbGaP = os.environ.get("dbGaP") or config.get("dbGaP")
if not isinstance(dbGaP, list):
dbGaP = [dbGaP]

sync = init_syncer(
dbGaP,
None,
DB,
)
sync.single_visa_sync = True
sync.sync_single_user_visas(user, current_session)
146 changes: 141 additions & 5 deletions fence/sync/sync_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ def __init__(
folder=None,
sync_from_visas=False,
fallback_to_dbgap_sftp=False,
single_visa_sync=False,
):
"""
Syncs ACL files from dbGap to auth database and storage backends
Expand Down Expand Up @@ -332,6 +333,7 @@ def __init__(
self.auth_source = defaultdict(set)
# auth_source used for logging. username : [source1, source2]
self.visa_types = config.get("USERSYNC", {}).get("visa_types", {})
self.single_visa_sync = single_visa_sync

if storage_credentials:
self.storage_manager = StorageManager(
Expand Down Expand Up @@ -762,10 +764,11 @@ def sync_to_db_and_storage_backend(self, user_project, user_info, sess):
# pass the original, non-lowered user_info dict
self._upsert_userinfo(sess, user_info)

self._revoke_from_storage(
to_delete, sess, google_bulk_mapping=google_bulk_mapping
)
self._revoke_from_db(sess, to_delete)
if not self.single_visa_sync:
self._revoke_from_storage(
to_delete, sess, google_bulk_mapping=google_bulk_mapping
)
self._revoke_from_db(sess, to_delete)

self._grant_from_storage(
to_add,
Expand All @@ -791,7 +794,8 @@ def sync_to_db_and_storage_backend(self, user_project, user_info, sess):
)
self._update_from_db(sess, to_update, user_project_lowercase)

self._validate_and_update_user_admin(sess, user_info_lowercase)
if not self.single_visa_sync:
self._validate_and_update_user_admin(sess, user_info_lowercase)

if config["GOOGLE_BULK_UPDATES"]:
self.logger.info("Doing bulk Google update...")
Expand Down Expand Up @@ -2041,3 +2045,135 @@ def sync_visas(self):
with self.driver.session as s:
self._sync_visas(s)
# if returns with some failure use telemetry file

def sync_single_user_visas(self, user, sess=None):
"""
Sync a single user's visa during login
"""

# sess = self.session

self.ras_sync_client = RASVisa(logger=self.logger)
self.single_visa_sync = True
dbgap_config = self.dbGaP[0]
enable_common_exchange_area_access = dbgap_config.get(
"enable_common_exchange_area_access", False
)
study_common_exchange_areas = dbgap_config.get(
"study_common_exchange_areas", {}
)

try:
user_yaml = UserYAML.from_file(
self.sync_from_local_yaml_file, encrypted=False, logger=self.logger
)
except (EnvironmentError, AssertionError) as e:
self.logger.error(str(e))
self.logger.error("aborting early")
return

user_projects = dict()
user_info = dict()
projects = {}
info = {}

for visa in user.ga4gh_visas_v1:
project = {}
visa_type = self._pick_sync_type(visa)
encoded_visa = visa.ga4gh_visa
project, info = visa_type._parse_single_visa(
user,
encoded_visa,
visa.expires,
self.parse_consent_code,
sess,
)
projects = {**projects, **project}
user_projects[user.username] = projects
user_info[user.username] = info

user_projects = self.parse_projects(user_projects)

if self.parse_consent_code and enable_common_exchange_area_access:
self.logger.info(
f"using study to common exchange area mapping: {study_common_exchange_areas}"
)

for username in user_projects.keys():
for project in user_projects[username].keys():
phsid = project.split(".")
dbgap_project = phsid[0]
privileges = user_projects[username][project]
if len(phsid) > 1 and self.parse_consent_code:
consent_code = phsid[-1]

# c999 indicates full access to all consents and access
# to a study-specific exchange area
# access to at least one study-specific exchange area implies access
# to the parent study's common exchange area
#
# NOTE: Handling giving access to all consents is done at
# a later time, when we have full information about possible
# consents
self.logger.debug(
f"got consent code {consent_code} from dbGaP project "
f"{dbgap_project}"
)
if (
consent_code == "c999"
and enable_common_exchange_area_access
and dbgap_project in study_common_exchange_areas
):
self.logger.info(
"found study with consent c999 and Fence "
"is configured to parse exchange area data. Giving user "
f"{username} {privileges} privileges in project: "
f"{study_common_exchange_areas[dbgap_project]}."
)
self._add_dbgap_project_for_user(
study_common_exchange_areas[dbgap_project],
privileges,
username,
sess,
user_projects,
dbgap_config,
)

dbgap_project += "." + consent_code

if dbgap_project not in self.project_mapping:
self._add_dbgap_project_for_user(
dbgap_project,
privileges,
username,
sess,
user_projects,
dbgap_config,
)

for element_dict in self.project_mapping.get(dbgap_project, []):
try:
phsid_privileges = {element_dict["auth_id"]: set(privileges)}

# need to add dbgap project to arborist
if self.arborist_client:
self._add_dbgap_study_to_arborist(
element_dict["auth_id"], dbgap_config
)

if username not in user_projects:
user_projects[username] = {}
user_projects[username].update(phsid_privileges)

except ValueError as e:
self.logger.info(e)
if self.parse_consent_code:
self._grant_all_consents_to_c999_users(
user_projects, user_yaml.project_to_resource
)
# update fence db
if user_projects:
self.logger.info("Sync to db and storage backend")
self.sync_to_db_and_storage_backend(user_projects, user_info, sess)
else:
self.logger.info("No users for syncing")
20 changes: 20 additions & 0 deletions tests/dbgap_sync/test_user_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,3 +725,23 @@ def test_user_sync_with_visas(
"phs000298": ["read", "read-storage"],
},
)


@pytest.mark.parametrize("syncer", ["google"], indirect=True)
def test_sync_in_login(
syncer,
db_session,
storage_client,
rsa_private_key,
kid,
monkeypatch,
):
monkeypatch.setattr(syncer, "single_visa_sync", True)
user = models.query_for_user(
session=db_session, username="TESTUSERB"
) # contains only visa information
syncer.sync_single_user_visas(user, db_session)
user = models.query_for_user(
session=db_session, username="TESTUSERB"
) # contains only visa information
assert len(user.project_access) == 6

0 comments on commit 1c6735b

Please sign in to comment.