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
3 changes: 2 additions & 1 deletion keepercommander/command_categories.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
# Service Mode REST API
'Service Mode REST API': {
'service-create', 'service-add-config', 'service-start', 'service-stop', 'service-status',
'service-config-add', 'service-docker-setup', 'slack-app-setup', 'teams-app-setup'
'service-config-add', 'service-docker-setup', 'slack-app-setup', 'teams-app-setup',
'sailpoint-app-setup'
},

# Email Configuration Commands
Expand Down
4 changes: 1 addition & 3 deletions keepercommander/commands/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1910,7 +1910,6 @@ def _tree_json_dumps(obj, level=0, indent=2):

_NSF_ROLE_ABBREV = {
'viewer': 'VW',
'contributor': 'CT',
'share-manager': 'SM',
'content-manager': 'CM',
'content-share-manager': 'CSM',
Expand Down Expand Up @@ -2513,7 +2512,6 @@ def print_share_permissions_key():
lines.extend([
'OW = NSF Owner',
'VW = NSF Viewer',
'CT = NSF Contributor',
'SM = NSF Share Manager',
'CM = NSF Content Manager',
'CSM = NSF Content + Share Manager',
Expand Down Expand Up @@ -2705,7 +2703,7 @@ def tree_node(node, parent_path=''):
}
if nsf_shares:
key['nsf'] = {
'OW': 'Owner', 'VW': 'Viewer', 'CT': 'Contributor', 'SM': 'Share Manager',
'OW': 'Owner', 'VW': 'Viewer', 'SM': 'Share Manager',
'CM': 'Content Manager', 'CSM': 'Content + Share Manager', 'FM': 'Full Manager',
}
payload['share_permissions_key'] = key
Expand Down
10 changes: 4 additions & 6 deletions keepercommander/commands/nested_share_folder/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ def infer_role(access):
Follows the official permission matrix::

full-manager > content-share-manager > share-manager >
content-manager > viewer > contributor > requestor > navigator
content-manager > viewer > requestor > navigator

The distinguishing trait between ``share-manager`` and
``content-share-manager`` is the ability to *edit* records: both roles
Expand All @@ -485,9 +485,7 @@ def infer_role(access):
return 'content-manager'
if get('can_view') and get('can_list_access'):
return 'viewer'
if get('can_view'):
return 'contributor'
if get('can_view_title'):
if get('can_view') or get('can_view_title'):
return 'requestor'
return 'navigator'

Expand All @@ -507,8 +505,8 @@ def role_label(access_role_type):
# Map backend AccessRoleType enum names to Nested Share Folder display labels.
# Source of truth: folder_pb2.AccessRoleType (NAVIGATOR=0 ... MANAGER=6).
_ACCESS_ROLE_DISPLAY_LABELS = {
'NAVIGATOR': 'contributor',
'REQUESTOR': 'contributor',
'NAVIGATOR': 'navigator',
'REQUESTOR': 'requestor',
'VIEWER': 'viewer',
'SHARED_MANAGER': 'share-manager',
'CONTENT_MANAGER': 'content-manager',
Expand Down
8 changes: 6 additions & 2 deletions keepercommander/commands/start_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
from ..service.commands.config_operation import AddConfigService
from ..service.commands.handle_service import StartService, StopService, ServiceStatus
from ..service.commands.service_docker_setup import ServiceDockerSetupCommand
from ..service.commands.integrations import SlackAppSetupCommand, TeamsAppSetupCommand
from ..service.commands.integrations import (
SlackAppSetupCommand, TeamsAppSetupCommand, SailPointAppSetupCommand,
)

def register_commands(commands):
commands['service-create'] = CreateService()
Expand All @@ -24,6 +26,7 @@ def register_commands(commands):
commands['service-docker-setup'] = ServiceDockerSetupCommand()
commands['slack-app-setup'] = SlackAppSetupCommand()
commands['teams-app-setup'] = TeamsAppSetupCommand()
commands['sailpoint-app-setup'] = SailPointAppSetupCommand()

def register_command_info(aliases, command_info):
service_classes = [
Expand All @@ -34,7 +37,8 @@ def register_command_info(aliases, command_info):
ServiceStatus,
ServiceDockerSetupCommand,
SlackAppSetupCommand,
TeamsAppSetupCommand
TeamsAppSetupCommand,
SailPointAppSetupCommand,
]

for service_class in service_classes:
Expand Down
5 changes: 5 additions & 0 deletions keepercommander/service/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from flask import Flask, jsonify
import logging
import os
from werkzeug.middleware.proxy_fix import ProxyFix
from flask_limiter.errors import RateLimitExceeded
from .decorators.security import limiter, is_behind_proxy
Expand Down Expand Up @@ -46,6 +47,10 @@ def handle_rate_limit_exceeded(e):
logger.debug("Initializing API routes")
init_routes(app)

if (os.environ.get('SAILPOINT_RECORD') or '').strip():
from .commands.integrations.sailpoint.service import SailPointService
SailPointService.start_background_services()

print("Keeper Commander Service initialization complete")
return app

Expand Down
13 changes: 12 additions & 1 deletion keepercommander/service/commands/create_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#

import argparse
import os
from typing import Any, Dict, Optional
from ..config.service_config import ServiceConfig
from ..config.config_validation import ValidationError
Expand Down Expand Up @@ -92,9 +93,19 @@ def execute(self, params: KeeperParams, **kwargs) -> None:
from ..core.globals import init_globals
init_globals(params)

filtered_kwargs = {k: v for k, v in kwargs.items() if k in ['port', 'allowedip', 'deniedip', 'commands', 'ngrok', 'ngrok_custom_domain', 'cloudflare', 'cloudflare_custom_domain', 'certfile', 'certpassword', 'fileformat', 'run_mode', 'queue_enabled', 'update_vault_record', 'ratelimit', 'encryption', 'encryption_key', 'token_expiration']}
filtered_kwargs = {k: v for k, v in kwargs.items() if k in [
'port', 'allowedip', 'deniedip', 'commands', 'ngrok', 'ngrok_custom_domain',
'cloudflare', 'cloudflare_custom_domain', 'certfile', 'certpassword', 'fileformat',
'run_mode', 'queue_enabled', 'update_vault_record', 'ratelimit', 'encryption',
'encryption_key', 'token_expiration',
]}
args = StreamlineArgs(**filtered_kwargs)

# Optional SailPoint: enable when SAILPOINT_RECORD points at a marked config record
if (os.environ.get('SAILPOINT_RECORD') or '').strip():
from .integrations.sailpoint.service import SailPointService
SailPointService.maybe_enable(params, args)

from .integrations.vault_metadata import get_existing_api_key, write_service_metadata
existing_api_key = (
get_existing_api_key(params, args.update_vault_record)
Expand Down
2 changes: 2 additions & 0 deletions keepercommander/service/commands/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
from .integration_setup_base import IntegrationSetupCommand
from .slack_app_setup import SlackAppSetupCommand
from .teams_app_setup import TeamsAppSetupCommand
from .sailpoint_app_setup import SailPointAppSetupCommand

__all__ = [
'IntegrationSetupCommand',
'SlackAppSetupCommand',
'TeamsAppSetupCommand',
'SailPointAppSetupCommand',
]
Original file line number Diff line number Diff line change
Expand Up @@ -498,8 +498,16 @@ def _print_integration_resources(self, record_uid: str, config) -> None:
name = self.get_integration_name()
print(f" • {name} Config Record: {bcolors.OKBLUE}{record_uid}{bcolors.ENDC}")
self.print_integration_specific_resources(config)
print(f" • EPM Integration: {bcolors.OKBLUE}{'true' if config.pedm_enabled else 'false'}{bcolors.ENDC}")
print(f" • Device Approval: {bcolors.OKBLUE}{'true' if config.device_approval_enabled else 'false'}{bcolors.ENDC}")
if hasattr(config, 'pedm_enabled'):
print(
f" • EPM Integration: "
f"{bcolors.OKBLUE}{'true' if config.pedm_enabled else 'false'}{bcolors.ENDC}"
)
if hasattr(config, 'device_approval_enabled'):
print(
f" • Device Approval: "
f"{bcolors.OKBLUE}{'true' if config.device_approval_enabled else 'false'}{bcolors.ENDC}"
)

# -- Optional feature collectors -----------------------------------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# _ __
# | |/ /___ ___ _ __ ___ _ _ ®
# | ' </ -_) -_) '_ \/ -_) '_|
# |_|\_\___\___| .__/\___|_|
# |_|
#
# Keeper Commander
# Copyright 2026 Keeper Security Inc.
# Contact: commander@keepersecurity.com
#

"""SailPoint Service Mode integration package."""

from .command_policy import SailPointCommandPolicy
from .constants import DOCKER_RECORD_ENV, PARAMS_ATTR, SAILPOINT_ALLOWED_COMMANDS, SAILPOINT_RECORD_ENV


def __getattr__(name):
if name == 'SailPointService':
from .service import SailPointService
return SailPointService
raise AttributeError(f'module {__name__!r} has no attribute {name!r}')


__all__ = [
'DOCKER_RECORD_ENV',
'SAILPOINT_RECORD_ENV',
'PARAMS_ATTR',
'SAILPOINT_ALLOWED_COMMANDS',
'SailPointCommandPolicy',
'SailPointService',
]
Loading