Conversation
Running this: `sf <folder> -e <user>` yields this error: ` Folder.SharedFolderUpdateUser.manageRecords: Expected an int, got a boolean.` This is because the `SharedFolderUpdateUser` proto accepts `SetBooleanValue` as `manageRecords` and `manageUsers`, but that's not what we're setting conditionally. This isn't a problem for `SharedFolderUpdateTeam` because these accept `bool`. This isn't a problem for `SharedFolderUpdateRecord` because we make sure to only set `SetBooleanValue`
* PAM workflow: enforce allowedTimes window in launch gate
WorkflowAccessValidator now blocks launch when the current time is
outside config.parameters.allowedTimes (allowedDays / timeRanges in the
configured timeZone), matching the web vault behavior in
use-pam-workflow-preventing-tunnel-launch.ts. Handles overnight ranges
that cross midnight. Falls back to local time if zoneinfo is
unavailable (Python <3.9) or the IANA tz name is unknown.
* PAM workflow: hard-disconnect pam launch and pam tunnel at lease expiry
Mirrors web vault behavior: when the workflow lease expiresOn is
reached during an active session, the connection is torn down
immediately — no grace period, no warning, no reconnect attempt
(matches ConnectionManager.scheduleWorkflowAccessExpiry in vault).
Adds WorkflowGate (NamedTuple) and check_workflow_for_launch(),
threading flow_uid and expires_on_ms (millis since epoch) from the
active workflow back to the launch path. Old check_workflow_and_prompt_2fa
is kept as a thin shim for backward compatibility.
pam launch: a daemon threading.Timer fires _on_lease_expired() at
expiresOn, flipping shutdown_requested and lease_expired. The "Access
expired — session terminated" line is printed AFTER
reset_local_terminal_after_pam_session() so the message survives the
terminal reset.
pam tunnel start: a daemon threading.Timer calls
tube_registry.close_tube(tube_id, reason=Normal) at expiresOn.
* PAM workflow: prompt for reason/ticket inline at launch instead of bailing
When pam launch / pam tunnel start hits a workflow in WS_NEEDS_ACTION
with AC_REASON or AC_TICKET pending, Commander now collects the missing
fields inline (prompt_toolkit; multi-line for reason, single-line for
ticket) and submits the request directly, then re-validates so the user
sees the resulting state (waiting / ready_to_start / started). Previously
the user was told to run pam workflow request and re-launch.
New flags --reason / -r and --ticket / -tk on both pam launch and pam
tunnel start drive the same flow non-interactively. Pure-approval
workflows (AC_APPROVAL only) are also auto-submitted.
Refactor: extracted submit_access_request() and prompt_for_reason_ticket()
into workflow/helpers.py; WorkflowRequestAccessCommand now reuses
submit_access_request. WorkflowAccessValidator returns block_reason and
pending_conditions so the orchestrator can decide between printing the
"Run: pam workflow request..." hint and prompting inline.
* PAM workflow: prompt to check out when launch hits WS_READY_TO_START
When a workflow is approved but not yet checked out, pam launch and
pam tunnel start now offer to perform the start_workflow call inline
("Check out 'RECORD' now? [Y/n]") and re-validate, instead of bailing
with a "Run: pam workflow start..." hint. New --auto-checkout / -aco
flag confirms automatically for non-interactive runs.
The orchestrator runs validate() in a small transition loop (max 3:
needs_action -> ready_to_start -> started) so a workflow that has no
approval requirement and lands in ready_to_start right after submitting
a reason/ticket flows through without the user having to re-launch.
WorkflowGate now carries started_by_launch=True when the orchestrator
itself triggered the checkout. This is the input Phase 1.2 (auto check-in
on session end) needs to know whether the launch owns the lease.
Refactor: validate()'s auto_prompt_actionable parameter renamed to
silent_actionable, now suppresses both WS_NEEDS_ACTION and
WS_READY_TO_START prints. start_workflow_for_record() extracted to
workflow/helpers.py.
* PAM workflow: auto check-in pam launch session when launch owns the checkout
When pam launch itself triggered start_workflow (gate.started_by_launch=
True from Phase 2.2 auto-checkout), the lease is now released in the
session-end finally via end_workflow(flow_uid). Skipped when the lease
already expired (server-side release) or when the user pre-checked-out
manually via pam workflow start.
pam tunnel start does not yet auto check-in on stop — the tunnel session
is fire-and-forget so the gate info would need to be persisted on
TunnelSession and consumed by pam tunnel stop. Lease expiry already
tears down the tunnel via Phase 1.3's daemon timer, so the only gap is
manual "pam tunnel stop" without an explicit "pam workflow end" — left
as a follow-up.
* PAM workflow: --wait polls for approval before launching
When pam launch / pam tunnel start hits a workflow in WS_WAITING (request
submitted, awaiting approver action), the new --wait / -w flag polls
get_user_access_state every 8 seconds until the workflow transitions out
of waiting (approved -> ready_to_start or started, or denied -> back to
needs_action) or until --wait-timeout elapses (default 600 seconds).
Without --wait the existing immediate-exit behavior is preserved.
Ctrl+C cancels the wait cleanly.
The orchestrator's transition loop now allows the
needs_action -> waiting -> ready_to_start -> started chain end-to-end.
silent_actionable=True now also suppresses the WS_WAITING print so
polling iterations stay quiet; one-shot use prints via _print_waiting.
* PAM workflow: skip MFA prompt when gateway is offline
Match web vault LaunchButton.tsx:151-164 — when the controllerStatus is
not Online, the workflow MFA prompt is skipped (the launch proceeds
without two_factor_value and surfaces its own gateway-offline error
when it tries to talk to the router).
is_gateway_online_for_record(params, record_uid) is best-effort: it uses
launch_cache.get to avoid an expensive PAM_LINK DAG rebuild on the first
launch. On cache miss or any lookup failure it returns None, in which
case the orchestrator keeps the existing behavior (prompt for MFA).
Subsequent launches with a warm cache get the offline-skip behavior.
* PAM action rotate: enforce the same two gates as the web vault
Web vault rotation does not apply workflow gates (approval / checkout /
justification / MFA / time-window) — see
PasswordRotation.tsx:137-209 + dag-pam-link.ts:334. Rotation is treated
as a privileged maintenance operation. The only two checks WV applies
on the Rotate Now button are:
1. getAllowRotation() — enterprise enforcement
(allow_rotate_credentials, with a
legacy allow_pam_rotation fallback)
2. getConfigAllowedSettings() — per-PAM-config allowedSettings.rotation
This commit adds the analogs for pam action rotate:
- _is_rotation_allowed_by_enforcement(params): checked once at the
top of execute() so both single-record and folder-pattern paths are
gated. Intentionally more permissive than WV's default-deny: only
returns False when the enforcement explicitly sets
allow_rotate_credentials=False (or legacy allow_pam_rotation=False).
Personal / non-enterprise accounts where params.enforcements is None,
empty, missing 'booleans', or carries unrelated grants are not
blocked. The check is wrapped in try/except and tolerates any
unexpected payload shape, so a malformed enforcements blob can't
break rotation for non-enterprise users.
- PAM config rotation switch: re-uses the existing
PAMConfigurationListCommand._pam_config_allowed_settings_json
helper to read allowedSettings from the PAM config DAG; when
rotation is explicitly False on the config the rotation is skipped
with a clear "disabled by PAM Configuration" message. The helper
already swallows all DAG-load exceptions and returns rotation=None
in that case; the call site adds a second try/except and uses
`is False` so None values (no DAG entry, personal users, lookup
failures) fall through to allow.
This replaces the earlier (incorrect) workflow gating attempt: the
--reason / --ticket / --auto-checkout / --wait / --wait-timeout flags
added previously have been removed since pam launch / pam tunnel start
remain the only places where workflow gates apply, matching WV.
* PAM tunnel: document that stop does not release the workflow lease
Adds an explanatory comment on PAMTunnelStopCommand and a symmetry note
at the workflow gate in PAMTunnelStartCommand. The behavior matches the
web vault (ConnectionManager.ts:268-303 stops the connection without
calling end_workflow); the workflow lease and the tunnel are decoupled
so a single approval window can host many sequential/concurrent tunnels.
The lease ends via expiresOn server-side or an explicit
`pam workflow end`. Releasing automatically on tunnel stop would clobber
leases the user established manually via `pam workflow start`, so the
non-release is intentional, not a gap.
* PAM launch / tunnel: gate on PAM config allowedSettings before lease auto-checkout
Mirrors web vault behavior — the Launch button checks
getConfigAllowedSettings(recordUid).connections
(GuacConnectBanner.tsx:37-45, message "launching_restricted_config")
and the Start Port-Forward button checks
getConfigAllowedSettings(recordUid).portForwards
(StartPortForwardButton.tsx:160-163, message "tunnel_disabled_config")
BEFORE any workflow gate runs. Order is:
enforcement → PAM config → workflow
Adds is_pam_config_action_allowed_for_record(params, record_uid, action_key)
in workflow/helpers.py. Reuses
PAMConfigurationListCommand._pam_config_allowed_settings_json (which
already wraps DAG load in try/except). Fast path: read config_uid from
launch_cache when available; otherwise fall back to the full
get_config_uid_from_record DAG resolution.
Returns True (allow) on any lookup failure (personal accounts, no PAM
context, missing DAG, non-PAM record types) so non-enterprise usage is
not blocked. Returns False only when the flag is explicitly False on
the PAM config DAG.
pam launch passes action_key='connections'; pam tunnel start passes
action_key='tunneling' (the JSON helper renames the DAG key
portForwards → tunneling).
Closes a previously identified gap: auto-checkout would otherwise take
a workflow lease for a record where the PAM config disables the action,
wasting the lease on a launch/tunnel that would fail downstream.
* PAM launch / tunnel: per-user enforcement gate before PAM-config gate
Closes the last WV-parity gap. Web vault gates Connect / Start-Tunnel
buttons on a per-user enterprise enforcement boolean before any other
check (pam-enforcement-selectors.ts:38-49):
getAllowConnections → allow_launch_pam_on_cloud_connection
getAllowPortForwards → allow_launch_pam_tunnels
These are already the rolled-up sum of the user's role permissions —
verified against WV that no additional role / team / record-ACL /
deny-list checks gate the buttons. Order in WV is:
enforcement → PAM config → workflow
Adds is_pam_action_allowed_by_enforcement(params, key) in
workflow/helpers.py and wires it into pam launch (key
'allow_launch_pam_on_cloud_connection') and pam tunnel start (key
'allow_launch_pam_tunnels'), called BEFORE the PAM-config gate.
Same defensive semantics as the rotation enforcement check
(_is_rotation_allowed_by_enforcement): allow by default, deny only on
explicit False; tolerates None / empty / malformed enforcement payloads
so personal / non-enterprise accounts are never blocked.
License gate (mkPam's isPamEnabled) intentionally skipped — Commander
has historically treated license defensively (let the gateway / server
fail with a specific error rather than pre-flighting client-side). Same
approach as the rotation enforcement check. Documented in detail in the
helper's docstring so future maintainers see the deviation and the
trade-off (granted enforcement + no license means the user passes this
gate and fails later at the gateway with a more specific error).
* PAM workflow: encode TimeOfDayRange as HHMM (server format), not minutes-since-midnight
* PAM workflow create: skip auto-add of creator as approver when approvalsNeeded=0
* PAM workflow create: drop creator auto-add; require --approver when approvalsNeeded > 0
* PAM workflow create: pre-check existing config and fail with an actionable message
* PAM launch: fall back to pam/get_configuration_controller when get_controllers misses (matches web vault)
* PAM tunnel: dedup lease-expiry timer per record, document soft-close limitation
* PAM workflow delete: pre-check existing config and bail with clear message when nothing to delete
* PAM workflow: handle no_workflow / needs_start with inline prompt + initial request submission (matches web vault first-time flow)
* Strict-deny PAM enforcement helpers when key absent in enterprise context
* Rotation enforcement: drop legacy allow_pam_rotation fallback to honor allow_rotate_credentials:false
* PAM launch: drop redundant catch-all error after workflow gate, mirror tunnel
* Workflow gate: allow on transport error so prod routers without workflow API don't hard-block legacy launch/tunnel
Comment on lines
+181
to
+182
| print(f"Run: pam workflow add-approver {record_uid} " | ||
| f"{' '.join(f'--user {u}' for u in approvers)}") |
|
|
||
| def _print_waiting(self, conditions, checked_out_by: str = ''): | ||
| cond_str = WorkflowFormatter.format_conditions(conditions) if conditions else 'approval' | ||
| print(f"\n{bcolors.WARNING}Workflow access is pending: waiting for {cond_str}.{bcolors.ENDC}") |
| f"to request approval.") | ||
| else: | ||
| cond_str = WorkflowFormatter.format_conditions(conditions) | ||
| print(f"Pending conditions: {cond_str}") |
Comment on lines
+327
to
+328
| print(f"Run: {bcolors.OKBLUE}pam workflow state --flow-uid {flow_uid_str}{bcolors.ENDC} " | ||
| f"to see details.") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.