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
121 changes: 73 additions & 48 deletions keepercommander/commands/discover/result_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import json
import sys
import os.path
import re
from keeper_secrets_manager_core.utils import url_safe_str_to_bytes
from . import PAMGatewayActionDiscoverCommandBase, GatewayContext
from ..pam.router_helper import (router_get_connected_gateways, router_set_record_rotation_information,
Expand Down Expand Up @@ -84,7 +83,10 @@ class PAMGatewayActionDiscoverResultProcessCommand(PAMGatewayActionDiscoverComma
"database",
"privatePEMKey",
"connectDatabase",
"operatingSystem"
"operatingSystem",

# This is a custom field
"Alternative Login"
]

def get_parser(self):
Expand Down Expand Up @@ -284,7 +286,7 @@ def _edit_record(self, content: DiscoveryObject, pad: str, editable: List[str])
new_values = map(str.strip, new_value.split(','))
new_value = "\n".join(new_values)
elif type_hint == "multiline":
print(_b(f"{pad}Enter multilines of text or a path, on the first line, "
print(_b(f"{pad}Enter multiline of text or a path, on the first line, "
"to a file that contains the value."))
print(_b(f"{pad}To end, type 'END' at the start of a new line. You can paste text."))
new_value = ""
Expand Down Expand Up @@ -331,9 +333,13 @@ def _edit_record(self, content: DiscoveryObject, pad: str, editable: List[str])
except (Exception,):
pass

for edit_field in content.fields:
if edit_field.label == edit_label:
edit_field.value = [new_value]
for section in ["fields", "custom"]:
if not hasattr(content, section):
continue
for edit_field in getattr(content, section):
if edit_field.label == edit_label:
edit_field.value = [new_value]
break

# Else, the label they entered cannot be edited.
else:
Expand Down Expand Up @@ -370,50 +376,53 @@ def _add_all_preprocess(vertex: DAGVertex, content: DiscoveryObject, parent_vert
def _prompt_display_fields(self, content: DiscoveryObject, pad: str) -> List[str]:

editable = []
for field in content.fields:
has_editable = False
if field.label in PAMGatewayActionDiscoverResultProcessCommand.EDITABLE:
editable.append(field.label)
has_editable = True
value = field.value

# If there is a value, and it's not just [], also make sure the
if len(value) > 0 and value[0] is not None:
# PAM records will have only 1 item in the value array.
value = value[0]
if field.label in self.FIELD_MAPPING:
type_hint = self.FIELD_MAPPING[field.label].get("type")
formatted_value = []
if type_hint == "dict":
field_input_format = self.FIELD_MAPPING[field.label].get("field_format")
for format_field in field_input_format:
formatted_value.append(f"{format_field.get('label')}: "
f"{value.get(format_field.get('key'))}")
elif type_hint == "csv":
formatted_value.append(", ".join(value.split("\n")))
elif type_hint == "multiline":
formatted_value.append(value)
elif type_hint == "choice":
formatted_value.append(value)
value = ", ".join(formatted_value)
else:
if has_editable:
value = f"{bcolors.FAIL}MISSING{bcolors.ENDC}"
for section in ["fields", "custom"]:
if not hasattr(content, section):
continue
for field in getattr(content, section):
has_editable = False
if field.label in PAMGatewayActionDiscoverResultProcessCommand.EDITABLE:
editable.append(field.label)
has_editable = True
value = field.value

# If there is a value, and it's not just [], also make sure the
if len(value) > 0 and value[0] is not None:
# PAM records will have only 1 item in the value array.
value = value[0]
if field.label in self.FIELD_MAPPING:
type_hint = self.FIELD_MAPPING[field.label].get("type")
formatted_value = []
if type_hint == "dict":
field_input_format = self.FIELD_MAPPING[field.label].get("field_format")
for format_field in field_input_format:
formatted_value.append(f"{format_field.get('label')}: "
f"{value.get(format_field.get('key'))}")
elif type_hint == "csv":
formatted_value.append(", ".join(value.split("\n")))
elif type_hint == "multiline":
formatted_value.append(value)
elif type_hint == "choice":
formatted_value.append(value)
value = ", ".join(formatted_value)
else:
value = f"{bcolors.OKBLUE}None{bcolors.ENDC}"
if has_editable:
value = f"{bcolors.FAIL}MISSING{bcolors.ENDC}"
else:
value = f"{bcolors.OKBLUE}None{bcolors.ENDC}"

color = bcolors.HEADER
if has_editable:
color = bcolors.OKGREEN
color = bcolors.HEADER
if has_editable:
color = bcolors.OKGREEN

rows = str(value).split("\n")
if len(rows) > 1:
value = rows[0] + _b(f"... {len(rows)} rows.")
rows = str(value).split("\n")
if len(rows) > 1:
value = rows[0] + _b(f"... {len(rows)} rows.")

print(f"{pad} "
f"{color}Label:{bcolors.ENDC} {field.label}, "
f"{_h('Type:')} {field.type}, "
f"{_h('Value:')} {value}")
print(f"{pad} "
f"{color}Label:{bcolors.ENDC} {field.label}, "
f"{_h('Type:')} {field.type}, "
f"{_h('Value:')} {value}")

if len(content.notes) > 0:
print("")
Expand Down Expand Up @@ -1012,12 +1021,27 @@ def _prepare_record(content: DiscoveryObject, context: Optional[Any] = None) ->
"field_type": field.type,
"field_value": field.value
}
if field.type != field.label:
if field.label is not None and field.type != field.label:
field_args["field_label"] = field.label
record_field = vault.TypedField.new_field(**field_args)
record_field.required = field.required
record.fields.append(record_field)

# If the content has custom fields, add them.
# Make sure the record has a list for custom fields.
if hasattr(content, "custom"):
if record.custom is None:
record.custom = []
for field in content.custom:
field_args = {
"field_type": field.type,
"field_value": field.value,
"field_label": field.label
}
record_field = vault.TypedField.new_field(**field_args)
record_field.required = field.required
record.custom.append(record_field)

folder = params.folder_cache.get(content.shared_folder_uid)
folder_key = None # type: Optional[bytes]
if isinstance(folder, subfolder.SharedFolderFolderNode):
Expand Down Expand Up @@ -1328,7 +1352,8 @@ def preview(self, job_item: JobItem, params: KeeperParams, gateway_context: Gate
infra = Infrastructure(record=gateway_context.configuration,
params=params,
logger=logging,
debug_level=debug_level, use_per_graph_endpoints=True)
debug_level=debug_level,
use_per_graph_endpoints=True)
infra.load(sync_point)

configuration = None
Expand Down
2 changes: 1 addition & 1 deletion keepercommander/discovery_common/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.1.13'
__version__ = '1.1.14'
5 changes: 3 additions & 2 deletions keepercommander/discovery_common/infrastructure.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ..keeper_dag.exceptions import DAGVertexException
from ..keeper_dag.crypto import urlsafe_str_to_bytes
from ..keeper_dag.types import PamEndpoints, PamGraphId
from .types import DiscoveryObject
from ..discovery_common.types import DiscoveryObject
import os
import importlib
import time
Expand Down Expand Up @@ -58,7 +58,8 @@ def __init__(self, record: Any, logger: Optional[Any] = None, history_level: int
self.debug_level = debug_level
self.fail_on_corrupt = fail_on_corrupt
self.save_batch_count = save_batch_count
self.use_per_graph_endpoints = use_per_graph_endpoints
# self.use_per_graph_endpoints = use_per_graph_endpoints
self.use_per_graph_endpoints = False

self.auto_save = False
self.delta_graph = True
Expand Down
3 changes: 2 additions & 1 deletion keepercommander/discovery_common/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ def __init__(self, record: Any, logger: Optional[Any] = None, debug_level: int =
self.debug_level = debug_level
self.fail_on_corrupt = fail_on_corrupt
self.save_batch_count = save_batch_count
self.use_per_graph_endpoints = use_per_graph_endpoints
# self.use_per_graph_endpoints = use_per_graph_endpoints
self.use_per_graph_endpoints = False

self.agent = make_agent("jobs")
if agent is not None:
Expand Down
8 changes: 4 additions & 4 deletions keepercommander/discovery_common/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,16 @@ def close(self):
Releases all DAG instances and connections to prevent memory leaks.
"""

if self.jobs:
if hasattr(self, "jobs") and self.jobs:
self.jobs.close()
self.jobs = None
if self.infra:
if hasattr(self, "infra") and self.infra:
self.infra.close()
self.infra = None
if self.record_link:
if hasattr(self, "record_link") and self.record_link:
self.record_link.close()
self.record_link = None
if self.user_service:
if hasattr(self, "user_service") and self.user_service:
self.user_service.close()
self.user_service = None

Expand Down
5 changes: 3 additions & 2 deletions keepercommander/discovery_common/record_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self,
log_prefix: str = "GS Record Linking",
save_batch_count: int = 200,
agent: Optional[str] = None,
use_read_protobuf: bool = True,
use_read_protobuf: bool = False,
use_write_protobuf: bool = True,
use_per_graph_endpoints: bool = False,
**kwargs):
Expand Down Expand Up @@ -50,7 +50,8 @@ def __init__(self,
self.log_prefix = log_prefix
self.debug_level = debug_level
self.save_batch_count = save_batch_count
self.use_per_graph_endpoints = use_per_graph_endpoints
# self.use_per_graph_endpoints = use_per_graph_endpoints
self.use_per_graph_endpoints = False

# Based on the connection type, use_write_protobuf might be set to False is True was passed.
# Use self.conn.use_write_protobuf; don't use passed in use_write_protobuf.
Expand Down
3 changes: 2 additions & 1 deletion keepercommander/discovery_common/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ def __init__(self, record: Any, logger: Optional[Any] = None, debug_level: int
self.logger = logger
self.debug_level = debug_level
self.fail_on_corrupt = fail_on_corrupt
self.use_per_graph_endpoints = use_per_graph_endpoints
# self.use_per_graph_endpoints = use_per_graph_endpoints
self.use_per_graph_endpoints = False

self.agent = make_agent("rules")
if agent is not None:
Expand Down
27 changes: 26 additions & 1 deletion keepercommander/discovery_common/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations
from enum import Enum
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
import time
import datetime
import base64
Expand Down Expand Up @@ -300,6 +300,9 @@ class RecordField(BaseModel):


class UserAclRotationSettings(BaseModel):
# Vault team may add attributes without telling others :(
model_config = ConfigDict(extra='allow')

# Base64 JSON schedule
schedule: Optional[str] = ""

Expand Down Expand Up @@ -344,6 +347,9 @@ def get_schedule(self) -> Optional[dict]:


class UserAcl(BaseModel):
# Vault team may add attributes without telling others :(
model_config = ConfigDict(extra='allow')

# Is this user's password/private key managed by this resource?
# This should be unique for all the ACL edges of this user vertex; only one ACL edge should have a True value.
belongs_to: bool = False
Expand All @@ -356,6 +362,9 @@ class UserAcl(BaseModel):
# This will only be True if the ACL of the PAM User connects to a configuration vertex.
is_iam_user: Optional[bool] = False

# No clue what this is. Vault team adding stuff without telling others.
is_launch_credential: bool = False

rotation_settings: Optional[UserAclRotationSettings] = None

@staticmethod
Expand Down Expand Up @@ -388,6 +397,7 @@ class DiscoveryConfiguration(DiscoveryItem):

class DiscoveryUser(DiscoveryItem):
user: Optional[str] = None
alt_user: Optional[str] = None
dn: Optional[str] = None
database: Optional[str] = None
managed: bool = False
Expand Down Expand Up @@ -501,6 +511,7 @@ class DiscoveryObject(BaseModel):
parent_record_uid: Optional[str] = None
record_type: str
fields: List[RecordField]
custom: List[RecordField] = []
ignore_object: bool = False
action_rules_result: Optional[str] = None
admin_uid: Optional[str] = None
Expand Down Expand Up @@ -612,6 +623,8 @@ def has_directories(self) -> bool:
class NormalizedRecord(BaseModel):
"""
This class attempts to normalize KeeperRecord, TypedRecord, KSM Record into a normalized record.

`fields` contains both standard and custom fields.
"""
record_uid: str
record_type: str
Expand All @@ -631,6 +644,7 @@ def _field(self,
return field
if label is not None and label == field.label:
return field

return None

def find_field(self,
Expand Down Expand Up @@ -659,6 +673,17 @@ def get_user(self) -> Optional[str]:
value = value[0]
return value

def get_alt_user(self) -> Optional[str]:
field = self._field(label="Alternative Login")
if field is None:
return None
value = field.value
if isinstance(value, list):
if len(value) == 0:
return None
value = value[0]
return value

def get_dn(self) -> Optional[str]:
field = self._field(label="distinguishedName")
if field is None:
Expand Down
Loading
Loading