Skip to content

Commit 6ef37b9

Browse files
WX integration feedack - Add namespacing handling to builtins to minimize the problems of collisions in config file and environment variable usage (#28)
* Move the constants for config injection into the built-in config interface module. * Move BuiltinConfigurationProviderInterface to a separate top level package that does not have any dependencies. Having it be part of the package makes it very difficult to avoid circular dependencies. * add the concept of a namespace for the config injector. * add namespacing to environment variables and ~/.planet.json variables. * Elevate monkey patch of click commands to a exposed lib function. * Doc updates
1 parent b8c40d3 commit 6ef37b9

File tree

12 files changed

+272
-112
lines changed

12 files changed

+272
-112
lines changed
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# ::: planet_auth_config_injection
2+
options:
3+
show_root_full_path: true
4+
inherited_members: true
5+
show_submodules: true
6+
show_if_no_docstring: false

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ nav:
8080
- API Reference:
8181
- Planet Auth: 'api-planet-auth.md'
8282
- Planet Auth Utils: 'api-planet-auth-utils.md'
83+
- Planet Auth Config Injection: 'api-planet-auth-config-injection.md'
8384
- Examples:
8485
- Installation: 'examples-installation.md'
8586
- CLI: 'examples-cli.md'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2025 Planet Labs PBC.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
# The Planet Authentication Library Configration Injection Package: `planet_auth_config_injection`
17+
18+
This package provides interfaces and utilities for higher-level applications
19+
to inject configuration into the Planet Authentication Library.
20+
21+
The Planet Auth Library provides configuration injection to improve the
22+
end-user experience of tools built on top of the Planet Auth Library.
23+
This allows built-in default client configurations to be provided.
24+
Namespacing may also be configured to avoid collisions in the Auth Library's
25+
use of environment variables. Injected configration is primarily consumed
26+
by initialization functionality in planet_auth_utils.PlanetAuthFactory
27+
and the various planet_auth_utils provided `click` commands.
28+
29+
These concerns belong more to the final end-user application than to a
30+
library that sits between the Planet Auth library and the end-user
31+
application. Such libraries themselves may be used by a variety of
32+
applications in any number of deployment environments, making the
33+
decision of what configuration to inject a difficult one.
34+
35+
Library writers may provide configuration injection to their developers,
36+
but should be conscious of the fact that multiple libraries within an
37+
application may depend on Planet Auth libraries. Library writers are
38+
advised to provide configuration injection as an option for their users,
39+
and not silently force it into the loaded.
40+
41+
In order to inject configuration, the application writer must do two things:
42+
43+
1. They must write a class that implements the
44+
[planet_auth_config_injection.BuiltinConfigurationProviderInterface][]
45+
interface.
46+
2. They must set the environment variable `PL_AUTH_BUILTIN_CONFIG_PROVIDER` to the
47+
fully qualified package, module, and class name of their implementation
48+
_before_ any import of the `planet_auth` or `planet_auth_utils` packages.
49+
"""
50+
51+
from .builtins_provider import (
52+
AUTH_BUILTIN_PROVIDER,
53+
BuiltinConfigurationProviderInterface,
54+
EmptyBuiltinProfileConstants,
55+
)
56+
57+
__all__ = [
58+
"AUTH_BUILTIN_PROVIDER",
59+
"BuiltinConfigurationProviderInterface",
60+
"EmptyBuiltinProfileConstants",
61+
]

src/planet_auth_utils/builtins_provider.py renamed to src/planet_auth_config_injection/builtins_provider.py

+26-11
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,45 @@
1616
from typing import Dict, List, Optional
1717

1818

19+
# Unlike other environment variables, AUTH_BUILTIN_PROVIDER is not name-spaced.
20+
# It is intended for libraries and applications to inject configuration by
21+
# being set within the program. It's not expected to be set by end-users.
22+
AUTH_BUILTIN_PROVIDER = "PL_AUTH_BUILTIN_CONFIG_PROVIDER"
23+
"""
24+
Environment variable to specify a python module and class that implement the
25+
BuiltinConfigurationProviderInterface abstract interface to provide the library
26+
and utility commands with some built-in configurations.
27+
"""
28+
1929
_NOOP_AUTH_CLIENT_CONFIG = {
2030
"client_type": "none",
2131
}
2232

2333

2434
class BuiltinConfigurationProviderInterface(ABC):
2535
"""
26-
Interface to define what profiles are built-in.
27-
28-
What auth configuration profiles are built-in is
29-
completely pluggable for users of the planet_auth and
30-
planet_auth_utils packages. This is to support reuse
31-
in different deployments, or even support reuse by a
32-
different software stack all together.
33-
34-
To inject built-in that override the coded in defaults,
35-
set the environment variable PL_AUTH_BUILTIN_CONFIG_PROVIDER
36-
to the module.classname of a class that implements this interface.
36+
Interface to define built-in application configuration.
37+
This includes providing built-in auth client configuration
38+
profiles, pre-defined trust environments for server use,
39+
and namespacing for environment and global configuration
40+
variables.
3741
3842
Built-in profile names are expected to be all lowercase.
3943
4044
Built-in trust environments are expected to be all uppercase.
4145
"""
4246

47+
def namespace(self) -> str:
48+
"""
49+
Application namespace. This will be used as a prefix in various
50+
contexts so that multiple applications may use the Planet auth
51+
libraries in the same environment without collisions. Presently,
52+
this namespace is used as a prefix for environment variables, and
53+
as a prefix for config settings store to the user's `~/.planet.json`
54+
file.
55+
"""
56+
return ""
57+
4358
@abstractmethod
4459
def builtin_client_authclient_config_dicts(self) -> Dict[str, dict]:
4560
"""

src/planet_auth_utils/__init__.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,10 @@
9696
opt_username,
9797
opt_yes_no,
9898
)
99-
from .commands.cli.util import recast_exceptions_to_click
99+
from .commands.cli.util import recast_exceptions_to_click, monkeypatch_hide_click_cmd_options
100100
from planet_auth_utils.constants import EnvironmentVariables
101101
from planet_auth_utils.plauth_factory import PlanetAuthFactory
102-
from planet_auth_utils.builtins import (
103-
Builtins,
104-
# Easily causes circular dependencies. Intentionally not part of the main package interface for now.
105-
# BuiltinConfigurationProviderInterface,
106-
)
102+
from planet_auth_utils.builtins import Builtins
107103
from planet_auth_utils.profile import Profile
108104
from planet_auth_utils.plauth_user_config import PlanetAuthUserConfig
109105

@@ -162,10 +158,11 @@
162158
"opt_token_file",
163159
"opt_username",
164160
"opt_yes_no",
161+
#
165162
"recast_exceptions_to_click",
163+
"monkeypatch_hide_click_cmd_options",
166164
#
167165
"Builtins",
168-
# "BuiltinConfigurationProviderInterface",
169166
"EnvironmentVariables",
170167
"PlanetAuthFactory",
171168
"Profile",

src/planet_auth_utils/builtins.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717

1818
from planet_auth import AuthClientConfig
1919
from planet_auth_utils.profile import ProfileException
20-
from planet_auth_utils.constants import EnvironmentVariables
2120
from planet_auth.logging.auth_logger import getAuthLogger
22-
from .builtins_provider import BuiltinConfigurationProviderInterface, EmptyBuiltinProfileConstants
21+
from planet_auth_config_injection import (
22+
BuiltinConfigurationProviderInterface,
23+
EmptyBuiltinProfileConstants,
24+
AUTH_BUILTIN_PROVIDER,
25+
)
2326

2427
auth_logger = getAuthLogger()
2528

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

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

@@ -86,6 +90,12 @@ class Builtins:
8690
def _load_builtin_jit():
8791
if not Builtins._builtin:
8892
Builtins._builtin = _load_builtins()
93+
auth_logger.debug(msg=f"Successfully loaded built-in provider: {Builtins._builtin.__class__.__name__}")
94+
95+
@staticmethod
96+
def namespace() -> str:
97+
Builtins._load_builtin_jit()
98+
return Builtins._builtin.namespace()
8999

90100
@staticmethod
91101
def is_builtin_profile(profile: str) -> bool:

src/planet_auth_utils/commands/cli/main.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ def cmd_plauth_embedded(ctx):
8484
Embeddable version of the Planet Auth Client root command.
8585
The embedded command differs from the stand-alone command in that it
8686
expects the context to be instantiated and options to be handled by
87-
the parent command. See [planet_auth_utils.PlanetAuthFactory.initialize_auth_client_context][]
87+
the parent command. The [planet_auth.Auth][] library context _must_
88+
be saved to the object field `AUTH` in the click context object.
89+
90+
See [planet_auth_utils.PlanetAuthFactory.initialize_auth_client_context][]
8891
for user-friendly auth client context initialization.
8992
9093
See [examples](/examples/#embedding-the-click-auth-command).

src/planet_auth_utils/commands/cli/util.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import click
1616
import functools
1717
import json
18-
from typing import Optional
18+
from typing import List, Optional
1919

2020
import planet_auth
2121
from planet_auth.constants import AUTH_CONFIG_FILE_SOPS, AUTH_CONFIG_FILE_PLAIN
@@ -26,7 +26,25 @@
2626
from .prompts import prompt_and_change_user_default_profile_if_different
2727

2828

29+
def monkeypatch_hide_click_cmd_options(cmd, hide_options: List[str]):
30+
"""
31+
Monkey patch a click command to hide the specified command options.
32+
Useful when reusing click commands in contexts where you do not
33+
wish to expose all the options.
34+
"""
35+
for hide_option in hide_options:
36+
for param in cmd.params:
37+
if param.name == hide_option:
38+
param.hidden = True
39+
break
40+
41+
2942
def recast_exceptions_to_click(*exceptions, **params): # pylint: disable=W0613
43+
"""
44+
Decorator to catch exceptions and raise them as ClickExceptions.
45+
Useful to apply to `click` commands to supress stack traces that
46+
might be otherwise exposed to the end-user.
47+
"""
3048
if not exceptions:
3149
exceptions = (Exception,)
3250
# params.get('some_arg', 'default')

0 commit comments

Comments
 (0)