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
26 changes: 26 additions & 0 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ The CLI requires three credentials for SCM API authentication:
| `client_secret` | `SCM_CLIENT_SECRET` | Service account client secret |
| `tsg_id` | `SCM_TSG_ID` | Tenant Service Group ID |

### API Endpoint Overrides

By default the SDK talks to the production SCM endpoints. Both can be
overridden per context or via environment variables (env wins over context;
when unset, the SDK defaults apply):

| Setting | Environment Variable | Description |
| --- | --- | --- |
| `api_base_url` | `SCM_API_BASE_URL` | Override the SCM API base URL |
| `token_url` | `SCM_TOKEN_URL` | Override the OAuth2 token URL |

```bash
# Persist per context
$ scm context create fedramp \
--client-id "fed@333333333.iam.panserviceaccount.com" \
--client-secret "fed-secret" \
--tsg-id "333333333" \
--api-base-url "https://api.example.paloaltonetworks.com" \
--token-url "https://auth.example.paloaltonetworks.com/am/oauth2/access_token"

# Or override for a one-off command
SCM_API_BASE_URL="https://api.example.paloaltonetworks.com" scm context test
```

### Authentication Fallback

If no credentials are configured, the CLI automatically enters mock mode:
Expand Down Expand Up @@ -292,6 +316,8 @@ production:
| `client_id` | None | `SCM_CLIENT_ID` | SCM API authentication |
| `client_secret` | None | `SCM_CLIENT_SECRET` | SCM API authentication |
| `tsg_id` | None | `SCM_TSG_ID` | SCM API tenant identification |
| `api_base_url` | SDK default | `SCM_API_BASE_URL` | Override the SCM API base URL |
| `token_url` | SDK default | `SCM_TOKEN_URL` | Override the OAuth2 token URL |

### Reserved Settings

Expand Down
32 changes: 26 additions & 6 deletions src/scm_cli/commands/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ def show_command(
console.print(" Client Secret: [red]Not set[/red]")

console.print(f" Log Level: {config.get('log_level', 'INFO')}")
console.print(f" API Base URL: {config.get('api_base_url') or '[dim]SDK default[/dim]'}")
console.print(f" Token URL: {config.get('token_url') or '[dim]SDK default[/dim]'}")

except ValueError as e:
console.print(f"[red]Error: {e}[/red]")
Expand Down Expand Up @@ -156,6 +158,16 @@ def create_command(
"-r",
help="SCM API region (default: americas)",
),
api_base_url: str = typer.Option(
None,
"--api-base-url",
help="Override the SCM API base URL (default: SDK default)",
),
token_url: str = typer.Option(
None,
"--token-url",
help="Override the OAuth2 token URL (default: SDK default)",
),
):
"""Create a new authentication context.

Expand Down Expand Up @@ -193,6 +205,8 @@ def create_command(
log_level=log_level,
access_token=access_token,
region=region,
api_base_url=api_base_url,
token_url=token_url,
)

console.print(f"[green]✓ Context '{context_name}' created successfully[/green]")
Expand Down Expand Up @@ -369,12 +383,18 @@ def test_command(
raise typer.Exit(1)

# Initialize the SCM client with context credentials
client = Scm(
client_id=config.get("client_id"),
client_secret=config.get("client_secret"),
tsg_id=config.get("tsg_id"),
log_level=config.get("log_level", "INFO"),
)
scm_kwargs = {
"client_id": config.get("client_id"),
"client_secret": config.get("client_secret"),
"tsg_id": config.get("tsg_id"),
"log_level": config.get("log_level", "INFO"),
}
# Endpoint overrides — omit when unset so SDK defaults apply
if config.get("api_base_url"):
scm_kwargs["api_base_url"] = config["api_base_url"]
if config.get("token_url"):
scm_kwargs["token_url"] = config["token_url"]
client = Scm(**scm_kwargs)

console.print("[green]✓ Authentication successful![/green]")
console.print(f" Client ID: {config.get('client_id')}")
Expand Down
8 changes: 8 additions & 0 deletions src/scm_cli/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ def get_auth_config() -> dict[str, str]:
# Add region (not required — defaults to americas)
auth["region"] = settings.get("region", "americas")

# Endpoint overrides (not required — omitted entirely so SDK defaults apply)
api_base_url = settings.get("api_base_url", None)
if api_base_url:
auth["api_base_url"] = api_base_url
token_url = settings.get("token_url", None)
if token_url:
auth["token_url"] = token_url

return auth


Expand Down
9 changes: 9 additions & 0 deletions src/scm_cli/utils/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ def create_context(
log_level: str = "INFO",
access_token: str | None = None,
region: str = "americas",
api_base_url: str | None = None,
token_url: str | None = None,
) -> None:
"""Create or update a context configuration.

Expand All @@ -107,6 +109,8 @@ def create_context(
log_level: Logging level (default: INFO).
access_token: Bearer token for direct auth (alternative to OAuth2).
region: SCM API region (default: americas).
api_base_url: Override for the SCM API base URL (omitted when unset; SDK default applies).
token_url: Override for the OAuth2 token URL (omitted when unset; SDK default applies).

"""
ensure_context_dir()
Expand All @@ -118,6 +122,11 @@ def create_context(
"region": region,
}

if api_base_url:
config["api_base_url"] = api_base_url
if token_url:
config["token_url"] = token_url

if access_token:
config["access_token"] = access_token
else:
Expand Down
20 changes: 18 additions & 2 deletions src/scm_cli/utils/sdk_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,16 @@ def __init__(self):
"access_token": access_token,
"log_level": settings.get("log_level", "INFO"),
}
if "region" in inspect.signature(Scm.__init__).parameters:
scm_params = inspect.signature(Scm.__init__).parameters
if "region" in scm_params:
scm_kwargs["region"] = resolved_region
# Endpoint overrides (env > context) — omitted when unset so SDK defaults apply
api_base_url = settings.get("api_base_url", None)
if api_base_url and "api_base_url" in scm_params:
scm_kwargs["api_base_url"] = api_base_url
token_url = settings.get("token_url", None)
if token_url and "token_url" in scm_params:
scm_kwargs["token_url"] = token_url
self.client = Scm(**scm_kwargs)
self.logger.info("Successfully initialized SDK client with bearer token")
else:
Expand All @@ -138,8 +146,16 @@ def __init__(self):
"tsg_id": self.tsg_id,
"log_level": settings.get("log_level", "INFO"),
}
if "region" in inspect.signature(Scm.__init__).parameters:
scm_params = inspect.signature(Scm.__init__).parameters
if "region" in scm_params:
scm_kwargs["region"] = resolved_region
# Endpoint overrides (env > context) — omitted when unset so SDK defaults apply
api_base_url = credentials.get("api_base_url") or settings.get("api_base_url", None)
if api_base_url and "api_base_url" in scm_params:
scm_kwargs["api_base_url"] = api_base_url
token_url = credentials.get("token_url") or settings.get("token_url", None)
if token_url and "token_url" in scm_params:
scm_kwargs["token_url"] = token_url
self.client = Scm(**scm_kwargs)
self.logger.info(f"Successfully initialized SDK client for TSG ID: {self.tsg_id}")
except (ValueError, AuthenticationError) as e:
Expand Down
Loading
Loading