Skip to content

WX integration feedack - Add namespacing handling to builtins to minimize the problems of collisions in config file and environment variable usage #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 6, 2025
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
6 changes: 6 additions & 0 deletions docs/api-planet-auth-config-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# ::: planet_auth_config_injection
options:
show_root_full_path: true
inherited_members: true
show_submodules: true
show_if_no_docstring: false
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ nav:
- API Reference:
- Planet Auth: 'api-planet-auth.md'
- Planet Auth Utils: 'api-planet-auth-utils.md'
- Planet Auth Config Injection: 'api-planet-auth-config-injection.md'
- Examples:
- Installation: 'examples-installation.md'
- CLI: 'examples-cli.md'
Expand Down
61 changes: 61 additions & 0 deletions src/planet_auth_config_injection/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright 2025 Planet Labs PBC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
# The Planet Authentication Library Configration Injection Package: `planet_auth_config_injection`

This package provides interfaces and utilities for higher-level applications
to inject configuration into the Planet Authentication Library.

The Planet Auth Library provides configuration injection to improve the
end-user experience of tools built on top of the Planet Auth Library.
This allows built-in default client configurations to be provided.
Namespacing may also be configured to avoid collisions in the Auth Library's
use of environment variables. Injected configration is primarily consumed
by initialization functionality in planet_auth_utils.PlanetAuthFactory
and the various planet_auth_utils provided `click` commands.

These concerns belong more to the final end-user application than to a
library that sits between the Planet Auth library and the end-user
application. Such libraries themselves may be used by a variety of
applications in any number of deployment environments, making the
decision of what configuration to inject a difficult one.

Library writers may provide configuration injection to their developers,
but should be conscious of the fact that multiple libraries within an
application may depend on Planet Auth libraries. Library writers are
advised to provide configuration injection as an option for their users,
and not silently force it into the loaded.

In order to inject configuration, the application writer must do two things:

1. They must write a class that implements the
[planet_auth_config_injection.BuiltinConfigurationProviderInterface][]
interface.
2. They must set the environment variable `PL_AUTH_BUILTIN_CONFIG_PROVIDER` to the
fully qualified package, module, and class name of their implementation
_before_ any import of the `planet_auth` or `planet_auth_utils` packages.
"""

from .builtins_provider import (
AUTH_BUILTIN_PROVIDER,
BuiltinConfigurationProviderInterface,
EmptyBuiltinProfileConstants,
)

__all__ = [
"AUTH_BUILTIN_PROVIDER",
"BuiltinConfigurationProviderInterface",
"EmptyBuiltinProfileConstants",
]
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,45 @@
from typing import Dict, List, Optional


# Unlike other environment variables, AUTH_BUILTIN_PROVIDER is not name-spaced.
# It is intended for libraries and applications to inject configuration by
# being set within the program. It's not expected to be set by end-users.
AUTH_BUILTIN_PROVIDER = "PL_AUTH_BUILTIN_CONFIG_PROVIDER"
"""
Environment variable to specify a python module and class that implement the
BuiltinConfigurationProviderInterface abstract interface to provide the library
and utility commands with some built-in configurations.
"""

_NOOP_AUTH_CLIENT_CONFIG = {
"client_type": "none",
}


class BuiltinConfigurationProviderInterface(ABC):
"""
Interface to define what profiles are built-in.

What auth configuration profiles are built-in is
completely pluggable for users of the planet_auth and
planet_auth_utils packages. This is to support reuse
in different deployments, or even support reuse by a
different software stack all together.

To inject built-in that override the coded in defaults,
set the environment variable PL_AUTH_BUILTIN_CONFIG_PROVIDER
to the module.classname of a class that implements this interface.
Interface to define built-in application configuration.
This includes providing built-in auth client configuration
profiles, pre-defined trust environments for server use,
and namespacing for environment and global configuration
variables.

Built-in profile names are expected to be all lowercase.

Built-in trust environments are expected to be all uppercase.
"""

def namespace(self) -> str:
"""
Application namespace. This will be used as a prefix in various
contexts so that multiple applications may use the Planet auth
libraries in the same environment without collisions. Presently,
this namespace is used as a prefix for environment variables, and
as a prefix for config settings store to the user's `~/.planet.json`
file.
"""
return ""

@abstractmethod
def builtin_client_authclient_config_dicts(self) -> Dict[str, dict]:
"""
Expand Down
11 changes: 4 additions & 7 deletions src/planet_auth_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,10 @@
opt_username,
opt_yes_no,
)
from .commands.cli.util import recast_exceptions_to_click
from .commands.cli.util import recast_exceptions_to_click, monkeypatch_hide_click_cmd_options
from planet_auth_utils.constants import EnvironmentVariables
from planet_auth_utils.plauth_factory import PlanetAuthFactory
from planet_auth_utils.builtins import (
Builtins,
# Easily causes circular dependencies. Intentionally not part of the main package interface for now.
# BuiltinConfigurationProviderInterface,
)
from planet_auth_utils.builtins import Builtins
from planet_auth_utils.profile import Profile
from planet_auth_utils.plauth_user_config import PlanetAuthUserConfig

Expand Down Expand Up @@ -162,10 +158,11 @@
"opt_token_file",
"opt_username",
"opt_yes_no",
#
"recast_exceptions_to_click",
"monkeypatch_hide_click_cmd_options",
#
"Builtins",
# "BuiltinConfigurationProviderInterface",
"EnvironmentVariables",
"PlanetAuthFactory",
"Profile",
Expand Down
16 changes: 13 additions & 3 deletions src/planet_auth_utils/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@

from planet_auth import AuthClientConfig
from planet_auth_utils.profile import ProfileException
from planet_auth_utils.constants import EnvironmentVariables
from planet_auth.logging.auth_logger import getAuthLogger
from .builtins_provider import BuiltinConfigurationProviderInterface, EmptyBuiltinProfileConstants
from planet_auth_config_injection import (
BuiltinConfigurationProviderInterface,
EmptyBuiltinProfileConstants,
AUTH_BUILTIN_PROVIDER,
)

auth_logger = getAuthLogger()

Expand All @@ -33,6 +36,7 @@ def _load_builtins_worker(builtin_provider_fq_class_name, log_warning=False):
return

module_name, _, class_name = builtin_provider_fq_class_name.rpartition(".")
auth_logger.debug(msg=f'Loading built-in provider:"{builtin_provider_fq_class_name}".')
if module_name and class_name:
try:
builtin_provider_module = importlib.import_module(module_name) # nosemgrep - WARNING - See below
Expand Down Expand Up @@ -61,7 +65,7 @@ def _load_builtins() -> BuiltinConfigurationProviderInterface:
# Undermining it can undermine client or service security.
# It is a convenience for seamless developer experience, but maybe
# we should not be so eager to please.
builtin_provider = _load_builtins_worker(os.getenv(EnvironmentVariables.AUTH_BUILTIN_PROVIDER))
builtin_provider = _load_builtins_worker(os.getenv(AUTH_BUILTIN_PROVIDER))
if builtin_provider:
return builtin_provider

Expand All @@ -86,6 +90,12 @@ class Builtins:
def _load_builtin_jit():
if not Builtins._builtin:
Builtins._builtin = _load_builtins()
auth_logger.debug(msg=f"Successfully loaded built-in provider: {Builtins._builtin.__class__.__name__}")

@staticmethod
def namespace() -> str:
Builtins._load_builtin_jit()
return Builtins._builtin.namespace()

@staticmethod
def is_builtin_profile(profile: str) -> bool:
Expand Down
5 changes: 4 additions & 1 deletion src/planet_auth_utils/commands/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ def cmd_plauth_embedded(ctx):
Embeddable version of the Planet Auth Client root command.
The embedded command differs from the stand-alone command in that it
expects the context to be instantiated and options to be handled by
the parent command. See [planet_auth_utils.PlanetAuthFactory.initialize_auth_client_context][]
the parent command. The [planet_auth.Auth][] library context _must_
be saved to the object field `AUTH` in the click context object.

See [planet_auth_utils.PlanetAuthFactory.initialize_auth_client_context][]
for user-friendly auth client context initialization.

See [examples](/examples/#embedding-the-click-auth-command).
Expand Down
20 changes: 19 additions & 1 deletion src/planet_auth_utils/commands/cli/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import click
import functools
import json
from typing import Optional
from typing import List, Optional

import planet_auth
from planet_auth.constants import AUTH_CONFIG_FILE_SOPS, AUTH_CONFIG_FILE_PLAIN
Expand All @@ -26,7 +26,25 @@
from .prompts import prompt_and_change_user_default_profile_if_different


def monkeypatch_hide_click_cmd_options(cmd, hide_options: List[str]):
"""
Monkey patch a click command to hide the specified command options.
Useful when reusing click commands in contexts where you do not
wish to expose all the options.
"""
for hide_option in hide_options:
for param in cmd.params:
if param.name == hide_option:
param.hidden = True
break


def recast_exceptions_to_click(*exceptions, **params): # pylint: disable=W0613
"""
Decorator to catch exceptions and raise them as ClickExceptions.
Useful to apply to `click` commands to supress stack traces that
might be otherwise exposed to the end-user.
"""
if not exceptions:
exceptions = (Exception,)
# params.get('some_arg', 'default')
Expand Down
Loading