Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion keepercommander/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
# Contact: commander@keepersecurity.com
#

__version__ = '18.1.3'
__version__ = '18.1.4'
36 changes: 36 additions & 0 deletions keepercommander/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,42 @@ def search_shared_folders(params, searchstring, use_regex=False):
return search_results


def search_nested_share_folders(params, searchstring, use_regex=False):
"""Search Nested Share Folders (v3 folder tree).

Args:
params: KeeperParams
searchstring: Search string (tokens or regex depending on use_regex)
use_regex: If True, treat as regex. If False (default), token-based search.
If searchstring is empty, returns all Nested Share Folders.

Returns:
List of (folder_uid, name) tuples.
"""
nsf_folders = getattr(params, 'nested_share_folders', {}) or {}

if not searchstring:
match_func = lambda target: True
elif use_regex:
p = re.compile(searchstring.lower())
match_func = lambda target: p.search(target)
else:
tokens = [t.lower() for t in searchstring.split() if t.strip()]
if not tokens:
match_func = lambda target: True
else:
match_func = lambda target: all(token in target for token in tokens)

search_results = []
for folder_uid, folder in nsf_folders.items():
name = folder.get('name', '')
target = (folder_uid + ' ' + name).lower()
if match_func(target):
search_results.append((folder_uid, name))

return search_results


def search_teams(params, searchstring, use_regex=False):
"""Search teams.

Expand Down
11 changes: 3 additions & 8 deletions keepercommander/commands/discoveryrotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
get_vault_record_title_type, find_pam_records_by_search,
resolve_pam_config_folder_info, pam_folder_json_payload, place_record_in_folder,
create_pam_configuration_in_folder, update_pam_record, records_in_folder,
reload_pam_record_if_nsf_updated,
)
from .pam.config_helper import configuration_controller_get, \
pam_configurations_get_all, pam_configuration_remove, \
Expand Down Expand Up @@ -75,13 +76,11 @@
from .discover.rule_list import PAMGatewayActionDiscoverRuleListCommand
from .discover.rule_remove import PAMGatewayActionDiscoverRuleRemoveCommand
from .discover.rule_update import PAMGatewayActionDiscoverRuleUpdateCommand
from .pam_debug.acl import PAMDebugACLCommand
from .pam_debug.dump import PAMDebugDumpCommand
from .pam_debug.gateway import PAMDebugGatewayCommand
from .pam_debug.graph import PAMDebugGraphCommand
from .pam_debug.info import PAMDebugInfoCommand
from .pam_debug.krouter import PAMDebugKRouterCommand
from .pam_debug.link import PAMDebugLinkCommand
from .pam_debug.rotation_setting import PAMDebugRotationSettingsCommand
from .pam_debug.vertex import PAMDebugVertexCommand
from .pam.cnapp_commands import PAMCnappCommand
Expand Down Expand Up @@ -463,11 +462,6 @@ def __init__(self):
self.register_command('gateway', PAMDebugGatewayCommand(), 'Debug a gateway', 'g')
self.register_command('krouter', PAMDebugKRouterCommand(), 'Show connected krouter version', 'k')
self.register_command('graph', PAMDebugGraphCommand(), 'Render graphs', 'r')

# Disable for now. Needs more work.
# self.register_command('verify', PAMDebugVerifyCommand(), 'Verify graphs')
self.register_command('acl', PAMDebugACLCommand(), 'Control ACL of PAM Users', 'c')
self.register_command('link', PAMDebugLinkCommand(), 'Link resource to configuration', 'l')
self.register_command('rs-reset', PAMDebugRotationSettingsCommand(),
'Create/reset rotation settings', 'rs')
self.register_command('vertex', PAMDebugVertexCommand(),
Expand Down Expand Up @@ -3128,7 +3122,8 @@ def execute(self, params, **kwargs):
self.parse_properties(params, configuration, config_edit=True, **kwargs)
self.verify_required(configuration, command='pam-config-edit')

update_pam_record(params, configuration, command='pam-config-edit')
was_nsf = update_pam_record(params, configuration, command='pam-config-edit')
configuration = reload_pam_record_if_nsf_updated(params, configuration, configuration.record_uid, was_nsf)

admin_cred_ref = ''
value = field.get_default_value(dict)
Expand Down
2 changes: 1 addition & 1 deletion keepercommander/commands/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def _load_record_for_ls(params, uid):
rec = vault.TypedRecord(version=3)
rec.record_uid = uid
rec.title = dj.get('title', uid)
rec.record_type = dj.get('type', '')
rec.type_name = dj.get('type', '')
rec.load_record_data(dj, None)
return rec
return None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ def _apply(cls, params, action, folder_uid, recipient, role, expiration,
logging.info("%s share '%s' %s", kind, recipient, verb)
else:
logging.warning("%s share '%s' failed", kind, recipient)
except ValueError as e:
except _nsf.ShareInviteSentError as e:
logging.warning("nsf-share-folder: %s", e)
except Exception as e:
raise CommandError('nsf-share-folder', str(e))
Expand Down
10 changes: 7 additions & 3 deletions keepercommander/commands/nested_share_folder/sharing_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,13 @@ def execute(self, params, **kwargs):
raise_if_record_share_target_is_owner(
params, record_uid, email, 'nsf-share-record',
is_ownership_transfer=(action == 'owner'))
result, effective_action = self._dispatch(
params, action, record_uid, email, access_role_type, expiration,
rotate_on_expiration)
try:
result, effective_action = self._dispatch(
params, action, record_uid, email, access_role_type, expiration,
rotate_on_expiration)
except _nsf.ShareInviteSentError as e:
logging.warning('nsf-share-record: %s', e)
continue
self._log_results(result, effective_action, email)

# Strategy dispatch — returns (result, effective_action)
Expand Down
27 changes: 26 additions & 1 deletion keepercommander/commands/pam/vault_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,31 @@ def is_pam_nsf_record(params, record_uid):
return False


def update_pam_record(params, record, command='pam', force_nsf=False):
def reload_pam_record_if_nsf_updated(params, record, record_uid, was_nsf_updated):
"""Reload a PAM record from cache if it was updated via NSF.

After NSF updates, the in-memory record object becomes stale. This helper
reloads it from the refreshed cache. For classic updates, returns the record
unchanged since classic updates use deferred sync (no immediate cache refresh).

Raises CommandError if NSF update occurred but reload fails, rather than
proceeding with known-stale data (which reintroduces the field-reversion bug).
"""
if was_nsf_updated:
from ..pam_import.record_loader import load_pam_record
reloaded = load_pam_record(params, record_uid)
if not reloaded:
raise CommandError('pam', f'Failed to reload NSF-updated record {record_uid} from cache after sync. '
'Record data may be stale; aborting edit to prevent field reversion.')
return reloaded
return record


def update_pam_record(params, record, command='pam', force_nsf=False) -> bool:
"""Update a PAM record via NSF v3 API or classic record_management.

Returns True if the record was updated via NSF and the in-memory object is now stale
and must be reloaded from cache. Returns False for classic updates using deferred sync.
"""
from ..nested_share_folder.helpers import normalize_nsf_user_message
from ...nested_share_folder.record_api import update_record_v3
Expand All @@ -587,11 +610,13 @@ def update_pam_record(params, record, command='pam', force_nsf=False):
'Failed to update record in Nested Share Folder')
from ..pam_import.nsf_helpers import sync_down_preserving_nsf_keys
sync_down_preserving_nsf_keys(params)
return True
else:
record_management.update_record(params, record)
# Defer vault refresh so a second classic edit in the same session does
# not send a stale record_cache revision (no immediate sync_down here).
params.sync_data = True
return False


def execute_record_add_in_folder(params, args, folder_uid, command='pam'):
Expand Down
156 changes: 0 additions & 156 deletions keepercommander/commands/pam_debug/acl.py

This file was deleted.

Loading
Loading