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())
Exemple #2
0
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)
Exemple #3
0
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
Exemple #4
0
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()
Exemple #6
0
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
Exemple #9
0
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
Exemple #10
0
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()
Exemple #11
0
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)
Exemple #12
0
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
Exemple #16
0
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