You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
7.2 KiB
147 lines
7.2 KiB
# Copyright (C) 2005-2025 Splunk Inc. All Rights Reserved.
|
|
|
|
import json
|
|
import sys
|
|
from splunk.rest import simpleRequest
|
|
from splunk.clilib.bundle_paths import make_splunkhome_path
|
|
from ITOA import itoa_common as utils
|
|
from .base_migration_interface import BaseMigrationInterface
|
|
|
|
sys.path.append(make_splunkhome_path(['etc', 'apps', 'SA-UserAccess', 'lib']))
|
|
from user_access_utils import UserAccessStore, UserAccess
|
|
|
|
|
|
class UserAccessMigrationInterface(BaseMigrationInterface):
|
|
"""
|
|
Interface to access UserAccess Objects
|
|
"""
|
|
|
|
def _iterator_from_kvstore(self, object_type):
|
|
"""
|
|
Helper method to obtain content from kvstore.
|
|
This method is specific to ITOA objects.
|
|
@type object_type: basestring
|
|
@param object_type: type of the object
|
|
@type limit: int
|
|
@param limit: batch limit to pull from kvstore
|
|
"""
|
|
results = []
|
|
location = '/servicesNS/nobody/SA-UserAccess/storage/collections/data/{0}'.format(object_type)
|
|
rsp, content = simpleRequest(location, sessionKey=self.session_key)
|
|
if rsp.status != 200:
|
|
self.logger.error("Failed to get object content, response=%s, content=%s", rsp, content)
|
|
raise Exception("Failed to get object content.")
|
|
try:
|
|
results = json.loads(content)
|
|
except Exception:
|
|
message = "Failed to convert object contents to JSON format."
|
|
self.logger.error(message)
|
|
raise Exception(message)
|
|
|
|
for result in results:
|
|
yield result
|
|
|
|
def migration_get(self, object_type, limit=100):
|
|
"""
|
|
Method to retrieve object content either from local storage or kvstore
|
|
If this is the first version migration, there is no content from the local
|
|
storage, an attempt will be made to retrieve content from kvstore.
|
|
Any subsequent GET will be from the local storage.
|
|
@type object_type: basestring
|
|
@param object_type: object_type
|
|
@type limit: int
|
|
@param limit: get bulk batch size, default to 100
|
|
@return: iterator, an iterator contains retrieved json objects
|
|
"""
|
|
target_file_list = []
|
|
self.logger.info("Migration helper directory: %s, processing object_type: %s." %
|
|
(self.migration_helper_directory, object_type))
|
|
|
|
if utils.FileManager.is_exists(self.migration_helper_directory):
|
|
target_file_list = self._get_object_file_list(object_type)
|
|
self.logger.info("Retrieving content from local storage: %s." % target_file_list)
|
|
|
|
if target_file_list:
|
|
self.logger.info("Trying to obtain data from the local file system...")
|
|
data = self._iterator_from_filesystem(target_file_list)
|
|
else:
|
|
self.logger.info("Trying to obtain data from KV store...")
|
|
data = self._iterator_from_kvstore(object_type)
|
|
|
|
return data
|
|
|
|
def migration_save_single_object_to_kvstore(self, object_type, validation=True, dupname_tag=None, skip_local_failure=False, transaction_id=None):
|
|
"""
|
|
Actual method to save content to the kvstore for a single object.
|
|
The coming data are coming from the local storage.
|
|
@type object_type: basestring
|
|
@param object_type: ITSI object types
|
|
@type validation: boolean
|
|
@param validation: require validation when saving to kvstore
|
|
@type dupname_tag: basestring
|
|
@param dupname_tag: a special tag to the duplicated titles.
|
|
@return: boolean
|
|
"""
|
|
self.logger.info("Saving single object: {} with transaction_id:{}".format(object_type, transaction_id))
|
|
target_file_list = self._get_object_file_list(object_type)
|
|
for target_file in target_file_list:
|
|
self.logger.info("Retrieving info from: {} with transaction_id:{}".format(target_file, transaction_id))
|
|
data = utils.FileManager.read_data(target_file)
|
|
if object_type == 'app_acl':
|
|
for single_record in data:
|
|
object_id = single_record.get('obj_id')
|
|
object_app = single_record.get('obj_app')
|
|
object_storename = single_record.get('obj_storename')
|
|
obj_type = single_record.get('obj_type')
|
|
object_acl = single_record.get('obj_acl')
|
|
if object_acl:
|
|
object_owner = object_acl.get('obj_owner')
|
|
if 'obj_owner' in object_acl:
|
|
object_acl.pop('obj_owner')
|
|
try:
|
|
success, content = UserAccess.update_perms(
|
|
object_id, object_acl, object_app, obj_type, object_storename,
|
|
self.session_key, self.logger, object_owner, merge=True
|
|
)
|
|
except Exception as e:
|
|
self.logger.exception(e)
|
|
self.logger.error('Failed to update permissions for object_id: {}, object_acl: {}, object_app: {}, obb_type: {}, object_storename: {}, object_owner: {}, transaction_id: {}'.format(
|
|
object_id, object_acl, object_app, obj_type, object_storename, object_owner, transaction_id
|
|
))
|
|
content = ('Failed to update permissions for object_id: {}, object_acl: {}, object_app: {}, obb_type: {}, object_storename: {}, object_owner: {}, transaction_id: {}'.format(
|
|
object_id, object_acl, object_app, obj_type, object_storename, object_owner, transaction_id
|
|
))
|
|
raise
|
|
else:
|
|
success = False
|
|
content = "Could not find `object_acl` in the ACL blob for transaction_id:{}".format(transaction_id)
|
|
if not success:
|
|
# If any of the single record update is failed, bail out the restoring
|
|
self.logger.error("Failed to update permissions for {} with transaction_id: {}".format(object_type, transaction_id))
|
|
raise Exception(content)
|
|
|
|
elif object_type == 'app_capabilities':
|
|
store = UserAccessStore.getInstance(app_name='SA-UserAccess', ns='nobody')
|
|
if data:
|
|
store.bulk_update(object_type, data, self.session_key, self.logger)
|
|
|
|
def migration_delete_kvstore(self, object_type):
|
|
"""
|
|
Actual method to delete content from the kvstore for the object.
|
|
This method applies to all ITOA objects
|
|
@type object_type: basestring
|
|
@param object_type: ITSI object types
|
|
@return: boolean
|
|
"""
|
|
location = '/servicesNS/nobody/SA-UserAccess/storage/collections/data/{0}'.format(object_type)
|
|
self.logger.info('Deleting existing object_type: %s URI: %s.', object_type, location)
|
|
filter_data = {}
|
|
response, content = simpleRequest(
|
|
location,
|
|
method="DELETE",
|
|
sessionKey=self.session_key,
|
|
getargs=filter_data,
|
|
raiseAllErrors=False
|
|
)
|
|
return
|