def get(self): google_projects = flask.request.args.get("google_project_ids") if not google_projects: return ( "Getting service accounts is only supported with the " "google_project_ids query param at the moment.", 400, ) # if not monitor, we should assume google project ids and parse google_project_ids = [ project_id.strip() for project_id in unquote(google_projects).split(",") ] # check if user has permission to get service accounts # for these projects user_id = current_token["sub"] authorized = is_user_member_of_all_google_projects( user_id, google_project_ids) if not authorized: return ( "Could not determine if user is a member on all the " "provided Google project IDs.", 403, ) service_accounts = self._get_project_service_accounts( google_project_ids=google_project_ids) response = {"service_accounts": service_accounts} return response, 200
def _get_patched_service_account_error_status(id_, sa): """ Get error status for attempting to patch given service account with access. Args: id_ (str): Google service account identifier to update sa ( fence.resources.google.service_account.GoogleServiceAccountRegistration ): the service account object with its email, project_access, a google project, and optionally a user who is attempting to modify/add """ # check if user has permission to update the service account authorized = is_user_member_of_all_google_projects( sa.user_id, [sa.google_project_id] ) if not authorized: msg = ( 'User "{}" does not have permission to update the provided ' 'service account "{}".'.format(sa.user_id, id_) ) raise Unauthorized(msg) error_response = _get_service_account_error_status(sa) return error_response
def delete(self, id_): """ Delete a service account Args: id_ (str): Google service account email to delete """ user_id = current_token["sub"] service_account_email = get_service_account_email(id_) registered_service_account = get_registered_service_account_from_email( service_account_email) if not registered_service_account: raise NotFound( "Could not find a registered service account from given email {}" .format(service_account_email)) google_project_id = registered_service_account.google_project_id # check if user has permission to delete the service account authorized = is_user_member_of_all_google_projects( user_id, [google_project_id]) if not authorized: return ( 'User "{}" does not have permission to delete the provided ' 'service account "{}".'.format(user_id, id_), 403, ) return self._delete(id_)
def check_validity(self, early_return=True, db=None, config=None): """ Determine whether or not project is valid for registration. If early_return is False, this object will store information about the failure. Args: early_return (bool, optional): Description """ google_project_number = get_google_project_number( self.google_project_id) has_access = bool(google_project_number) self.set("monitor_has_access", has_access) # always early return if we can't access the project if not has_access: return membership = get_google_project_membership(self.google_project_id) if self.user_id is not None: user_has_access = is_user_member_of_all_google_projects( self.user_id, [self.google_project_id], membership=membership, db=db) self.set("user_has_access", user_has_access) if not user_has_access: # always early return if user isn't a member on the project return parent_org = get_google_project_parent_org(self.google_project_id) valid_parent_org = True # if there is an org, let's remove whitelisted orgs and then check validity # again white_listed_google_parent_orgs = ( config.get("WHITE_LISTED_GOOGLE_PARENT_ORGS") if config else None) if parent_org: valid_parent_org = is_org_whitelisted( parent_org, white_listed_google_parent_orgs=white_listed_google_parent_orgs, ) self.set("valid_parent_org", valid_parent_org) if not valid_parent_org and early_return: return user_members = None service_account_members = [] try: user_members, service_account_members = get_google_project_valid_users_and_service_accounts( self.google_project_id, membership=membership) self.set("valid_member_types", True) except Exception: self.set("valid_member_types", False) if early_return: return # if we have valid members, we can check if they exist in fence users_in_project = None if user_members is not None: try: users_in_project = get_users_from_google_members(user_members, db=db) self.set("members_exist_in_fence", True) except Exception: self.set("members_exist_in_fence", False) if early_return: return # use a generic validityinfo object to hold all the service accounts # validity. then check all the service accounts. Top level will be # invalid if any service accounts are invalid new_service_account_validity = ValidityInfo() if self.new_service_account: service_account_validity_info = GoogleServiceAccountValidity( self.new_service_account, self.google_project_id, google_project_number) service_account_id = str(self.new_service_account) google_sa_domains = ( config.get("GOOGLE_MANAGED_SERVICE_ACCOUNT_DOMAINS") if config else None) # we do NOT need to check the service account type and external access # for google-managed accounts. if is_google_managed_service_account( service_account_id, google_managed_service_account_domains=google_sa_domains, ): service_account_validity_info.check_validity( early_return=early_return, check_type_and_access=False, config=config, ) else: service_account_validity_info.check_validity( early_return=early_return, check_type_and_access=True, config=config) # update project with error info from the service accounts new_service_account_validity.set(service_account_id, service_account_validity_info) if not service_account_validity_info and early_return: # if we need to return early for invalid SA, make sure to include # error details and invalidate the overall validity self.set("new_service_account", new_service_account_validity) return self.set("new_service_account", new_service_account_validity) service_accounts = get_service_account_ids_from_google_members( service_account_members) white_listed_service_accounts = ( config.get("WHITE_LISTED_SERVICE_ACCOUNT_EMAILS") if config else None) app_creds_file = (config.get("GOOGLE_APPLICATION_CREDENTIALS") if config else None) remove_white_listed_service_account_ids( service_accounts, app_creds_file=app_creds_file, white_listed_sa_emails=white_listed_service_accounts, ) # use a generic validityinfo object to hold all the service accounts # validity. then check all the service accounts. Top level will be # invalid if any service accounts are invalid service_accounts_validity = ValidityInfo() for service_account in service_accounts: service_account_id = str(service_account) service_account_validity_info = GoogleServiceAccountValidity( service_account, self.google_project_id, google_project_number) google_sa_domains = ( config.get("GOOGLE_MANAGED_SERVICE_ACCOUNT_DOMAINS") if config else None) # we do NOT need to check the service account type and external access # for google-managed accounts. if is_google_managed_service_account( service_account_id, google_managed_service_account_domains=google_sa_domains, ): service_account_validity_info.check_validity( early_return=early_return, check_type_and_access=False, config=config, ) else: service_account_validity_info.check_validity( early_return=early_return, check_type_and_access=True, config=config) # update project with error info from the service accounts service_accounts_validity.set(service_account_id, service_account_validity_info) if not service_account_validity_info and early_return: # if we need to return early for invalid SA, make sure to include # error details and invalidate the overall validity self.set("service_accounts", service_accounts_validity) return self.set("service_accounts", service_accounts_validity) # get the service accounts for the project to determine all the data # the project can access through the service accounts service_accounts = get_registered_service_accounts( self.google_project_id, db=db) service_account_project_access = get_project_access_from_service_accounts( service_accounts, db=db) # use a generic validityinfo object to hold all the projects validity project_access_validities = ValidityInfo() # extend list with any provided access to test for provided_access in self.new_service_account_access: project = get_project_from_auth_id(provided_access) # if provided access doesn't exist, set error in project_validity if not project: project_validity = ValidityInfo() project_validity.set("exists", False) project_validity.set("all_users_have_access", None) project_access_validities.set(str(provided_access), project_validity) else: service_account_project_access.append(project) # make sure all the users of the project actually have access to all # the data the service accounts have access to for project in service_account_project_access: project_validity = ValidityInfo() project_validity.set("exists", True) # if all the users exist in our db, we can check if they have valid # access valid_access = None if users_in_project: valid_access = do_all_users_have_access_to_project( users_in_project, project.id, db=db) project_validity.set("all_users_have_access", valid_access) project_access_validities.set(str(project.auth_id), project_validity) self.set("access", project_access_validities) return