def get_registered_service_account_from_email(service_account_email): """ Parse email to get google project id """ session = get_db_session() return (session.query(UserServiceAccount).filter_by( email=service_account_email).first())
def patch_user_service_account( google_project_id, service_account_email, project_access, db=None ): """ Update user service account which includes - Add and remove project access and bucket groups to/from fence db - Add and remove access members to/from google access group Args: google_project_id (str): google project id service_account_email (str): service account email project_access (List(str)): list of projects db(str): db connection string Returns: None """ session = get_db_session(db) service_account = ( session.query(UserServiceAccount).filter_by(email=service_account_email).first() ) if not service_account: raise fence.errors.NotFound( "{} does not exist in DB".format(service_account_email) ) accessed_project_ids = { ap.project_id for ap in ( session.query(ServiceAccountAccessPrivilege) .filter_by(service_account_id=service_account.id) .all() ) } granting_project_ids = get_project_ids_from_project_auth_ids( session, project_access ) to_add = set.difference(granting_project_ids, accessed_project_ids) to_delete = set.difference(accessed_project_ids, granting_project_ids) _revoke_user_service_account_from_google( session, to_delete, google_project_id, service_account ) # Use granting_project_ids here, not to_add, bc the google-delete-expired-service-account # job doesn't clean out the entries in the ServiceAccountAccessPrivilege table. # So the set diff (=to_add) won't include the proj if the SA was previously registered for that proj, # even if the SA later expired and was removed from the relevant GBAG. add_user_service_account_to_google( session, granting_project_ids, google_project_id, service_account ) _revoke_user_service_account_from_db(session, to_delete, service_account) # On the other hand, use to_add here and not granting_project_ids, # otherwise this will add duplicates to ServiceAccountAccessPrivilege. # Because at time of writing, aforementioned tbl has no compound unique constraint. add_user_service_account_to_db(session, to_add, service_account)
def is_user_member_of_google_project( user_id, google_cloud_manager, db=None, membership=None ): """ Return whether or not the given user is a member of the provided Google project ID. This will verify that either the user's email or their linked Google account email exists as a member in the project. Args: user_id (int): User identifier google_cloud_manager (GoogleCloudManager): cloud manager instance db(str): db connection string membership (List(GooglePolicyMember) : pre-calculated list of members, Will make call to Google API if membership is None Returns: bool: whether or not the given user is a member of ALL of the provided Google project IDs """ session = get_db_session(db) user = session.query(User).filter_by(id=user_id).first() if not user: logger.error( "Could not determine if user (id: {} is from project:" " {} due to error. User does not exist...".format( user_id, google_cloud_manager.project_id ) ) return False linked_google_account = ( session.query(UserGoogleAccount) .filter(UserGoogleAccount.user_id == user_id) .first() ) try: members = membership or google_cloud_manager.get_project_membership() member_emails = [member.email_id.lower() for member in members] # first check if user.email is in project, then linked account if not (user.email and user.email.lower() in member_emails): if not ( linked_google_account and linked_google_account.email.lower() in member_emails ): # no user email is in project return False except Exception as exc: logger.error( "Could not determine if user (id: {}) is from project:" " {} due to error. Details: {}".format( user.id, getattr(google_cloud_manager, "project_id", "unknown"), exc ) ) return False return True
def extend_service_account_access(service_account_email, db=None): """ Extend the Google service accounts access to data by extending the expiration time for each of the Google Bucket Access Groups it's in. WARNING: This does NOT do any AuthZ, do before this. Args: service_account_email (str): service account email db(str): db connection string """ session = get_db_session(db) service_account = ( session.query(UserServiceAccount).filter_by(email=service_account_email).first() ) if service_account: bucket_access_groups = get_google_access_groups_for_service_account( service_account ) # timestamp at which the SA will lose bucket access # by default: use configured time or 7 days expiration_time = int(time.time()) + config.get( "GOOGLE_USER_SERVICE_ACCOUNT_ACCESS_EXPIRES_IN", 604800 ) requested_expires_in = get_valid_expiration_from_request() if requested_expires_in: requested_expiration = int(time.time()) + requested_expires_in expiration_time = min(expiration_time, requested_expiration) logger.debug( "Service Account ({}) access extended to {}.".format( service_account.email, expiration_time ) ) for access_group in bucket_access_groups: bucket_access = ( session.query(ServiceAccountToGoogleBucketAccessGroup) .filter_by( service_account_id=service_account.id, access_group_id=access_group.id, ) .first() ) if not bucket_access: bucket_access = ServiceAccountToGoogleBucketAccessGroup( service_account_id=service_account.id, access_group_id=access_group.id, expires=expiration_time, ) session.add(bucket_access) bucket_access.expires = expiration_time session.commit()
def force_delete_service_account(service_account_email, db=None): """ Delete from our db the given user service account by email. Args: service_account_email (str): user service account email db(str): db connection string """ session = get_db_session(db) sa = (session.query(UserServiceAccount).filter_by( email=service_account_email).first()) if sa: session.delete(sa) session.commit()
def patch_user_service_account( google_project_id, service_account_email, project_access, db=None ): """ Update user service account which includes - Add and remove project access and bucket groups to/from fence db - Add and remove access members to/from google access group Args: google_project_id (str): google project id service_account_email (str): service account email project_access (List(str)): list of projects db(str): db connection string Returns: None """ session = get_db_session(db) service_account = ( session.query(UserServiceAccount).filter_by(email=service_account_email).first() ) if not service_account: raise fence.errors.NotFound( "{} does not exist in DB".format(service_account_email) ) accessed_project_ids = { ap.project_id for ap in ( session.query(ServiceAccountAccessPrivilege) .filter_by(service_account_id=service_account.id) .all() ) } granting_project_ids = get_project_ids_from_project_auth_ids( session, project_access ) to_add = set.difference(granting_project_ids, accessed_project_ids) to_delete = set.difference(accessed_project_ids, granting_project_ids) _revoke_user_service_account_from_google( session, to_delete, google_project_id, service_account ) add_user_service_account_to_google( session, to_add, google_project_id, service_account ) _revoke_user_service_account_from_db(session, to_delete, service_account) add_user_service_account_to_db(session, to_add, service_account)
def get_project_from_auth_id(project_auth_id, db=None): """ Return a Project given a Project.auth_id (or None if it doesnt exist.) Args: project_auth_id (str): Project.auth_id Returns: int: Project """ session = get_db_session(db) project = session.query(Project).filter_by(auth_id=project_auth_id).first() return project
def get_user_by_email(user_email, db=None): """ Return user from fence DB Args: user_id (str): user's fence email id Returns: bool: user in fence DB with user_email """ session = get_db_session(db) user = (session.query(User).filter(User.email == user_email)).first() return user
def do_all_users_have_access_to_project(users, project_id, db=None): session = get_db_session(db) # users will be list of fence.model.User's # check if all users has access to a project with project_id for user in users: access_privilege = ( session.query(AccessPrivilege) .filter(AccessPrivilege.user_id == user.id) .filter(AccessPrivilege.project_id == project_id) ).first() if not access_privilege: return False return True
def extend_service_account_access(service_account_email, db=None): """ Extend the Google service accounts access to data by extending the expiration time for each of the Google Bucket Access Groups it's in. WARNING: This does NOT do any AuthZ, do before this. Args: service_account_email (str): service account email db(str): db connection string """ session = get_db_session(db) service_account = ( session.query(UserServiceAccount).filter_by(email=service_account_email).first() ) if service_account: bucket_access_groups = get_google_access_groups_for_service_account( service_account ) # use configured time or 7 days expiration_time = int(time.time()) + flask.current_app.config.get( "GOOGLE_USER_SERVICE_ACCOUNT_ACCESS_EXPIRES_IN", 604800 ) for access_group in bucket_access_groups: bucket_access = ( session.query(ServiceAccountToGoogleBucketAccessGroup) .filter_by( service_account_id=service_account.id, access_group_id=access_group.id, ) .first() ) if not bucket_access: bucket_access = ServiceAccountToGoogleBucketAccessGroup( service_account_id=service_account.id, access_group_id=access_group.id, expires=expiration_time, ) session.add(bucket_access) bucket_access.expires = expiration_time session.commit()
def force_remove_service_account_from_access( service_account_email, google_project_id, db=None ): """ loop through ServiceAccountToBucket remove from google group delete entries from db Args: service_account_email (str): service account email google_project_id (str): google project id db (None, str): Optional db connection string Returns: None """ session = get_db_session(db) service_account = ( session.query(UserServiceAccount).filter_by(email=service_account_email).first() ) if not service_account: raise fence.errors.NotFound( "{} does not exist in DB".format(service_account_email) ) access_groups = get_google_access_groups_for_service_account(service_account) for bucket_access_group in access_groups: try: with GoogleCloudManager(google_project_id, use_default=False) as g_manager: g_manager.remove_member_from_group( member_email=service_account.email, group_id=bucket_access_group.email, ) except Exception as exc: raise GoogleAPIError( "Can not remove member {} from access group {}. Detail {}".format( service_account.email, bucket_access_group.email, exc ) ) _force_remove_service_account_from_access_db(service_account, access_groups, db)
def _force_remove_service_account_from_access_db( service_account, access_groups, db=None ): """ Remove the access of service account from db. Args: service_account (str): service account email db(str): db connection string Returns: None Restrictions: service account has to exist in db """ session = get_db_session(db) for bucket_access_group in access_groups: sa_to_group = ( session.query(ServiceAccountToGoogleBucketAccessGroup) .filter_by( service_account_id=service_account.id, access_group_id=bucket_access_group.id, ) .first() ) if sa_to_group: session.delete(sa_to_group) session.commit() # delete all access privileges access_privileges = ( session.query(ServiceAccountAccessPrivilege) .filter_by(service_account_id=service_account.id) .all() ) for access in access_privileges: session.delete(access) session.commit()
def do_all_users_have_access_to_project(users, project_id, db=None): session = get_db_session(db) # users will be list of fence.model.User's # check if all users has access to a project with project_id for user in users: access_privilege = (session.query(AccessPrivilege).filter( AccessPrivilege.user_id == user.id).filter( AccessPrivilege.project_id == project_id)).first() if not access_privilege: project = (session.query(Project).filter( Project.id == project_id)).first() project_rep = project.auth_id if project else project_id logger.info( "User ({}) does not have access to project ({}). There may be other " "users that do not have access to this project.".format( user.username.lower(), project_rep)) return False return True
def user_has_access_to_project(user, project_id, db=None): """ Return True IFF user has access to provided project auth_id Args: user (fence.model.User): user to check access project_id (string): project auth_id db (str): database connection string Returns: bool: True IFF user has access to provided project auth_id """ session = get_db_session(db) access_privilege = (session.query(AccessPrivilege).filter( AccessPrivilege.user_id == user.id).filter( AccessPrivilege.project_id == project_id)).first() return bool(access_privilege)
def get_user_by_linked_email(linked_email, db=None): """" Return user identified by linked_email address Args: linked_email (str): email address linked to user Returns: (User): User db object """ session = get_db_session(db) linked_account = (session.query(UserGoogleAccount).filter( UserGoogleAccount.email == linked_email).first()) if linked_account: user = session.query(User).filter( User.id == linked_account.user_id).first() return user else: return None
def force_add_service_accounts_to_access( service_account_emails, google_project_id, project_access, db=None ): """ service_account_emails(list(str)): list of account emails google_project_id(str): google project id project_access(list(str)): list of projects db(str): db connection string """ session = get_db_session(db) with GoogleCloudManager(google_project_id) as google_project: for service_account_email in service_account_emails: g_service_account = google_project.get_service_account( service_account_email ) sa = ( session.query(UserServiceAccount) .filter_by(email=service_account_email) .first() ) if not sa: sa = UserServiceAccount( google_unique_id=g_service_account.get("uniqueId"), email=service_account_email, google_project_id=google_project_id, ) session.add(sa) session.commit() project_ids = set() for project in project_access: project_db = session.query(Project).filter_by(auth_id=project).first() if project_db: project_ids.add(project_db.id) add_user_service_account_to_db(session, project_ids, sa) add_user_service_account_to_google( session, project_ids, google_project_id, sa )
def force_remove_service_account_from_db(service_account_email, db=None): """ remove service account from user_service_account table Args: service_account_email(str): service account to be removed from db db(None, str): Optional db connection string """ session = get_db_session(db) service_account = (session.query(UserServiceAccount).filter_by( email=service_account_email).first()) if not service_account: logger.info( "Service account ({}) requested for removal from database " "was not found in the database.".format(service_account_email)) else: session.delete(service_account) session.commit() return