feat(token): add stored token support with resolve_token and token_source

Introduce a centralized token resolution system that supports both
environment variables and config-file-stored tokens with clear priority
(env var wins). This enables cron-based sync which runs in minimal
shell environments without env vars.

Core changes:
- GitLabConfig gains optional `token` field and `resolve_token()` method
  that checks env var first, then config file, returning trimmed values
- `token_source()` returns human-readable provenance ("environment variable"
  or "config file") for diagnostics
- `ensure_config_permissions()` enforces 0600 on config files containing
  tokens (Unix only, no-op on other platforms)

New CLI commands:
- `lore token set [--token VALUE]` — validates against GitLab API, stores
  in config, enforces file permissions. Supports flag, stdin pipe, or
  interactive entry.
- `lore token show [--unmask]` — displays masked token with source label

Consumers updated to use resolve_token():
- auth_test: removes manual env var lookup
- doctor: shows token source in health check output
- ingest: uses centralized resolution

Includes 10 unit tests for resolve/source logic and 2 for mask_token.
This commit is contained in:
teernisse
2026-02-18 16:27:35 -05:00
parent a4df8e5444
commit 30ed02c694
7 changed files with 303 additions and 23 deletions

View File

@@ -1,5 +1,5 @@
use crate::core::config::Config;
use crate::core::error::{LoreError, Result};
use crate::core::error::Result;
use crate::gitlab::GitLabClient;
pub struct AuthTestResult {
@@ -11,17 +11,7 @@ pub struct AuthTestResult {
pub async fn run_auth_test(config_path: Option<&str>) -> Result<AuthTestResult> {
let config = Config::load(config_path)?;
let token = std::env::var(&config.gitlab.token_env_var)
.map(|t| t.trim().to_string())
.map_err(|_| LoreError::TokenNotSet {
env_var: config.gitlab.token_env_var.clone(),
})?;
if token.is_empty() {
return Err(LoreError::TokenNotSet {
env_var: config.gitlab.token_env_var.clone(),
});
}
let token = config.gitlab.resolve_token()?;
let client = GitLabClient::new(&config.gitlab.base_url, &token, None);