mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
Integrate config-server policies with startup and authentication (#57175)
## What changed - Use the managed configuration loader in CLI, exec, TUI, and app-server configuration flows so `requirements.toml` can select a config server. - Sign in to the config server when needed during interactive TUI startup and CLI login, reload configuration after CLI sign-in, and clear config-server credentials on CLI logout. - Preserve the app-server's config-server settings and refresh worker across OpenAI login and logout. - Require config-server settings to be applied when loading CLI and doctor configuration. ## Testing Add app-server integration tests using a local HTTPS config server to verify managed provider and instruction settings at startup, preservation across API-key login and logout, and cache reuse after restart and thread resume. GitOrigin-RevId: 27ee81f7943d2290a5c3fcc0ac556579ec63f8b4
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use codex_app_server::in_process::EmbeddedNetworkPolicy;
|
||||
use codex_cloud_config::chatgpt_config_bundle_loader_for_storage;
|
||||
use codex_cloud_config::managed_config_bundle_loader_for_storage;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_core::config::Config;
|
||||
@@ -27,6 +27,7 @@ pub async fn load_config(
|
||||
.build()
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
config.config_layer_stack.ensure_config_server_applied()?;
|
||||
EmbeddedNetworkPolicy::default().activate(&mut config);
|
||||
Ok(config)
|
||||
}
|
||||
@@ -58,11 +59,12 @@ pub async fn config_builder(
|
||||
)
|
||||
.await
|
||||
.context("failed to load bootstrap configuration")?;
|
||||
let cloud_config_bundle = chatgpt_config_bundle_loader_for_storage(
|
||||
let cloud_config_bundle = managed_config_bundle_loader_for_storage(
|
||||
network_policy.bind_bootstrap_auth(
|
||||
bootstrap_auth_config(codex_home.as_path(), &bootstrap_config)
|
||||
.context("failed to resolve cloud configuration authentication")?,
|
||||
),
|
||||
bootstrap_config.config_layer_stack.requirements_toml(),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -628,7 +628,7 @@ async fn load_config(
|
||||
..config_overrides_from_interactive(interactive, arg0_paths)
|
||||
};
|
||||
|
||||
crate::cloud_config::config_builder(
|
||||
let config = crate::cloud_config::config_builder(
|
||||
&root_config_overrides,
|
||||
LoaderOverrides::default(),
|
||||
overrides,
|
||||
@@ -636,7 +636,9 @@ async fn load_config(
|
||||
.await?
|
||||
.build()
|
||||
.await
|
||||
.context("failed to load Codex config")
|
||||
.context("failed to load Codex config")?;
|
||||
config.config_layer_stack.ensure_config_server_applied()?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn config_overrides_from_interactive(
|
||||
|
||||
@@ -18,7 +18,7 @@ pub use login::read_api_key_from_stdin;
|
||||
pub use login::run_login_status;
|
||||
pub use login::run_login_with_access_token;
|
||||
pub use login::run_login_with_api_key;
|
||||
pub use login::run_login_with_chatgpt;
|
||||
pub use login::run_login_with_browser;
|
||||
pub use login::run_login_with_device_code;
|
||||
pub use login::run_login_with_device_code_fallback_to_browser;
|
||||
pub use login::run_logout;
|
||||
|
||||
@@ -172,6 +172,27 @@ pub async fn login_with_chatgpt(
|
||||
server.block_until_done().await
|
||||
}
|
||||
|
||||
/// Signs into the config server if needed, then reloads configuration.
|
||||
async fn sign_in_to_config_server_and_reload_config(
|
||||
config: &mut Config,
|
||||
cli_config_overrides: &CliConfigOverrides,
|
||||
) -> std::io::Result<()> {
|
||||
let requirements = config.config_layer_stack.requirements_toml();
|
||||
let network = codex_app_server::in_process::EmbeddedNetworkPolicy::load(
|
||||
&codex_config::LoaderOverrides::default(),
|
||||
)
|
||||
.await;
|
||||
let bootstrap_auth = network.bind_bootstrap_auth(config.auth_config());
|
||||
let auth_route_config = bootstrap_auth.auth_route_config.clone();
|
||||
codex_cloud_config::ensure_config_server_login(bootstrap_auth, requirements).await?;
|
||||
*config = crate::cloud_config::load_config(cli_config_overrides, Default::default())
|
||||
.await
|
||||
.map_err(|error| std::io::Error::other(format!("{error:#}")))?;
|
||||
network.activate(config);
|
||||
config.application_auth_route_config = Some(auth_route_config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finish_login(result: std::io::Result<()>) -> ! {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
@@ -187,17 +208,28 @@ fn finish_login(result: std::io::Result<()>) -> ! {
|
||||
|
||||
async fn run_interactive_login<F>(
|
||||
cli_config_overrides: CliConfigOverrides,
|
||||
login: impl FnOnce(Config) -> F,
|
||||
chatgpt_login: impl FnOnce(Config) -> F,
|
||||
) -> !
|
||||
where
|
||||
F: std::future::Future<Output = std::io::Result<()>>,
|
||||
{
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let mut config = load_config_or_exit(cli_config_overrides.clone()).await;
|
||||
if !config
|
||||
.config_layer_stack
|
||||
.requirements_toml()
|
||||
.requires_remote_config()
|
||||
{
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
finish_login(chatgpt_login(config).await);
|
||||
}
|
||||
|
||||
let result =
|
||||
sign_in_to_config_server_and_reload_config(&mut config, &cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
finish_login(login(config).await)
|
||||
finish_login(result)
|
||||
}
|
||||
|
||||
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
pub async fn run_login_with_browser(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
run_interactive_login(cli_config_overrides, |config| async move {
|
||||
tracing::info!("starting browser login flow");
|
||||
|
||||
@@ -226,7 +258,7 @@ pub async fn run_login_with_api_key(
|
||||
cli_config_overrides: CliConfigOverrides,
|
||||
api_key: String,
|
||||
) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let config = load_managed_login_config_or_exit(cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
tracing::info!("starting api key login flow");
|
||||
|
||||
@@ -259,7 +291,7 @@ pub async fn run_login_with_access_token(
|
||||
cli_config_overrides: CliConfigOverrides,
|
||||
access_token: String,
|
||||
) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let config = load_managed_login_config_or_exit(cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
tracing::info!("starting access token login flow");
|
||||
|
||||
@@ -521,9 +553,28 @@ pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
async fn logout_config_server(config: &Config) -> std::io::Result<bool> {
|
||||
match codex_cloud_config::managed_config_auth_manager(
|
||||
config.auth_config(),
|
||||
config.config_layer_stack.requirements_toml(),
|
||||
)? {
|
||||
Some(manager) => manager.logout().await,
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let auth_route_config = config.auth_route_config();
|
||||
let config_server_logout = if config
|
||||
.config_layer_stack
|
||||
.requirements_toml()
|
||||
.requires_remote_config()
|
||||
{
|
||||
logout_config_server(&config).await
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
|
||||
// Cleanup is independent of the current feature gate. Retain the grant lock
|
||||
// through account removal so another process cannot commit a late login.
|
||||
@@ -590,7 +641,14 @@ pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
false
|
||||
};
|
||||
|
||||
if logged_out || cleared_bedrock_config {
|
||||
let config_server_logged_out = match config_server_logout {
|
||||
Ok(removed) => removed,
|
||||
Err(error) => {
|
||||
eprintln!("Error logging out: {error}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
if logged_out || config_server_logged_out || cleared_bedrock_config {
|
||||
eprintln!("Successfully logged out");
|
||||
} else {
|
||||
eprintln!("Not logged in");
|
||||
@@ -598,6 +656,20 @@ pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
async fn load_managed_login_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config {
|
||||
let mut config = load_config_or_exit(cli_config_overrides.clone()).await;
|
||||
if config
|
||||
.config_layer_stack
|
||||
.requirements_toml()
|
||||
.requires_remote_config()
|
||||
&& let Err(error) =
|
||||
sign_in_to_config_server_and_reload_config(&mut config, &cli_config_overrides).await
|
||||
{
|
||||
finish_login(Err(error));
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
async fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config {
|
||||
let cli_overrides = match cli_config_overrides.parse_overrides() {
|
||||
Ok(v) => v,
|
||||
|
||||
@@ -16,7 +16,7 @@ use codex_cli::read_api_key_from_stdin;
|
||||
use codex_cli::run_login_status;
|
||||
use codex_cli::run_login_with_access_token;
|
||||
use codex_cli::run_login_with_api_key;
|
||||
use codex_cli::run_login_with_chatgpt;
|
||||
use codex_cli::run_login_with_browser;
|
||||
use codex_cli::run_login_with_device_code;
|
||||
use codex_cli::run_logout;
|
||||
use codex_cloud_tasks::Cli as CloudTasksCli;
|
||||
@@ -1699,7 +1699,7 @@ async fn cli_main(
|
||||
let access_token = read_access_token_from_stdin();
|
||||
run_login_with_access_token(login_cli.config_overrides, access_token).await;
|
||||
} else {
|
||||
run_login_with_chatgpt(login_cli.config_overrides).await;
|
||||
run_login_with_browser(login_cli.config_overrides).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user