Skip to content
Open
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
821 changes: 819 additions & 2 deletions keepercli-package/src/keepercli/commands/pam/keeper_pam.py

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions keepercli-package/src/keepercli/commands/secrets_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser):
'-f', '--force', dest='force', action='store_true', help='Force add or remove app'
)
parser.add_argument(
'--email', action='store', type=str, dest='email', help='Email of user to grant / remove application access to / from'
'--email', '-e', action='store', type=str, dest='email', help='Email of user to grant / remove application access to / from'
)
parser.add_argument(
'--admin', action='store_true', help='Allow share recipient to manage application'
Expand Down Expand Up @@ -366,7 +366,7 @@ def add_client(
current_time_ms + first_access_expire_duration * MILLISECONDS_PER_MINUTE
)
access_expire_in_ms = (
access_expire_in_min * MILLISECONDS_PER_MINUTE
(current_time_ms + access_expire_in_min * MILLISECONDS_PER_MINUTE)
if access_expire_in_min else None
)

Expand All @@ -385,7 +385,8 @@ def add_client(
first_access_expire_duration_ms=first_access_expire_duration_ms,
access_expire_in_ms=access_expire_in_ms,
master_key=master_key,
server=server
server=server,
client_type=GENERAL
)

tokens.append(token_data['token_info'])
Expand Down
44 changes: 41 additions & 3 deletions keepercli-package/src/keepercli/commands/shares.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ def execute(self, context: KeeperParams, **kwargs) -> None:
share_expiration = share_management_utils.get_share_expiration(
kwargs.get('expire_at'), kwargs.get('expire_in')
)

dry_run = kwargs.get('dry_run', False)

request = RecordShares.prep_request(
vault=vault,
Expand All @@ -162,12 +164,12 @@ def execute(self, context: KeeperParams, **kwargs) -> None:
emails=emails,
share_expiration=share_expiration,
action=action,
dry_run=kwargs.get('dry_run', False),
dry_run=dry_run,
can_edit=kwargs.get('can_edit'),
can_share=kwargs.get('can_share'),
recursive=kwargs.get('recursive')
)
if request:
if request and not dry_run:
success_responses, failed_responses = RecordShares.send_requests(vault, [request])
if success_responses:
logger.info(f'{len(success_responses)} share requests were successfully processed')
Expand All @@ -176,6 +178,10 @@ def execute(self, context: KeeperParams, **kwargs) -> None:
for failed_response in failed_responses:
logger.error(f'Failed to process share request: {failed_response}')
vault.sync_down()
elif request and dry_run:
ShareRecordCommand._print_share_table(request, vault)
else:
logger.info('Nothing to do')

def _validate_and_replace_contacts(self, vault, emails: list, force: bool) -> list:
"""Validate emails against known contacts and optionally replace with matches."""
Expand Down Expand Up @@ -222,7 +228,39 @@ def get_contact(user, contacts):
return contact

return None


@staticmethod
def _print_share_table(request: record_pb2.RecordShareUpdateRequest, vault: vault_online.VaultOnline) -> None:
headers = ['Username', 'Record UID', 'Title', 'Share Action', 'Expiration']
table = []
for attr in ['addSharedRecord', 'updateSharedRecord', 'removeSharedRecord']:
if hasattr(request, attr):
for obj in getattr(request, attr):
record_uid = utils.base64_url_encode(obj.recordUid)
username = obj.toUsername
record = vault.vault_data.get_record(record_uid=record_uid)
row = [username, record_uid, record.title]
if attr in ['addSharedRecord', 'updateSharedRecord']:
if obj.transfer:
row.append('Transfer Ownership')
elif obj.editable or obj.shareable:
if obj.editable and obj.shareable:
row.append('Can Edit & Share')
elif obj.editable:
row.append('Can Edit')
else:
row.append('Can Share')
else:
row.append('Read Only')
else:
row.append('Remove Share')
if obj.expiration > 0:
dt = datetime.datetime.fromtimestamp(obj.expiration//1000)
row.append(str(dt))
else:
row.append(None)
table.append(row)
report_utils.dump_report_data(table, headers, row_number=True, group_by=0)

class ShareFolderCommand(base.ArgparseCommand):
def __init__(self):
Expand Down
5 changes: 3 additions & 2 deletions keepercli-package/src/keepercli/helpers/gateway_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List

from keepersdk.vault import vault_online
from keepersdk.proto import pam_pb2
from keepersdk.proto import pam_pb2, enterprise_pb2
from keepersdk.vault import ksm_management


Expand Down Expand Up @@ -69,7 +69,8 @@ def create_gateway(
first_access_expire_duration_ms=first_access_expire_duration_ms,
access_expire_in_ms=None,
master_key=master_key,
server=vault.keeper_auth.keeper_endpoint.server
server=vault.keeper_auth.keeper_endpoint.server,
client_type=enterprise_pb2.DISCOVERY_AND_ROTATION_CONTROLLER
)

return _extract_one_time_token(one_time_token_dict)
Expand Down
1 change: 1 addition & 0 deletions keepercli-package/src/keepercli/register_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,4 @@ def register_commands(commands: base.CliCommands, scopes: Optional[base.CommandS
commands.register_command('team-approve', enterprise_team.TeamApproveCommand(), base.CommandScope.Enterprise)
commands.register_command('user-report', user_report.UserReportCommand(), base.CommandScope.Enterprise, 'ur')
commands.register_command('security-audit-report', security_audit_report.SecurityAuditReportCommand(), base.CommandScope.Enterprise, 'sar')
commands.register_command('pam', keeper_pam.PAMControllerCommand(), base.CommandScope.Enterprise)
1 change: 1 addition & 0 deletions keepersdk-package/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ protobuf>=5.28.3
websockets>=13.1
fido2>=2.0.0; python_version>='3.10'
email-validator>=2.0.0
pydantic>=2.6.4; python_version>='3.8'
20 changes: 20 additions & 0 deletions keepersdk-package/src/keepersdk/helpers/config_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from ..proto import pam_pb2
from ..vault import vault_extensions, vault_online, vault_record
from .. import utils, crypto

def pam_configuration_create_record_v6(vault: vault_online.VaultOnline, record: vault_record.TypedRecord, folder_uid: str):
if not record.record_uid:
record.record_uid = utils.generate_uid()

if not record.record_key:
record.record_key = utils.generate_aes_key()

record_data = vault_extensions.extract_typed_record_data(record)
json_data = record.load_record_data(record_data)

car = pam_pb2.ConfigurationAddRequest()
car.configurationUid = utils.base64_url_decode(record.record_uid)
car.recordKey = crypto.encrypt_aes_v2(record.record_key, vault.keeper_auth.auth_context.data_key)
car.data = crypto.encrypt_aes_v2(json_data, record.record_key)

vault.keeper_auth.execute_auth_rest('pam/add_configuration_record', car)
Loading