mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 13:50:31 +00:00
feat: add anonymous read mode (NORA_AUTH_ANONYMOUS_READ)
When auth is enabled with anonymous_read=true, GET/HEAD requests are allowed without credentials (pull/download), while write operations (PUT/POST/DELETE/PATCH) still require authentication. Use case: public demo registries, read-only mirrors. Config: NORA_AUTH_ANONYMOUS_READ=true or auth.anonymous_read=true
This commit is contained in:
@@ -94,6 +94,16 @@ pub async fn auth_middleware(
|
|||||||
return next.run(request).await;
|
return next.run(request).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow anonymous read if configured
|
||||||
|
let is_read_method = matches!(
|
||||||
|
*request.method(),
|
||||||
|
axum::http::Method::GET | axum::http::Method::HEAD
|
||||||
|
);
|
||||||
|
if state.config.auth.anonymous_read && is_read_method {
|
||||||
|
// Read requests allowed without auth
|
||||||
|
return next.run(request).await;
|
||||||
|
}
|
||||||
|
|
||||||
// Extract Authorization header
|
// Extract Authorization header
|
||||||
let auth_header = request
|
let auth_header = request
|
||||||
.headers()
|
.headers()
|
||||||
|
|||||||
@@ -200,6 +200,9 @@ fn default_max_file_size() -> u64 {
|
|||||||
pub struct AuthConfig {
|
pub struct AuthConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
/// Allow anonymous read access (pull/download without auth, push requires auth)
|
||||||
|
#[serde(default)]
|
||||||
|
pub anonymous_read: bool,
|
||||||
#[serde(default = "default_htpasswd_file")]
|
#[serde(default = "default_htpasswd_file")]
|
||||||
pub htpasswd_file: String,
|
pub htpasswd_file: String,
|
||||||
#[serde(default = "default_token_storage")]
|
#[serde(default = "default_token_storage")]
|
||||||
@@ -279,6 +282,7 @@ impl Default for AuthConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
anonymous_read: false,
|
||||||
htpasswd_file: "users.htpasswd".to_string(),
|
htpasswd_file: "users.htpasswd".to_string(),
|
||||||
token_storage: "data/tokens".to_string(),
|
token_storage: "data/tokens".to_string(),
|
||||||
}
|
}
|
||||||
@@ -457,6 +461,9 @@ impl Config {
|
|||||||
if let Ok(val) = env::var("NORA_AUTH_ENABLED") {
|
if let Ok(val) = env::var("NORA_AUTH_ENABLED") {
|
||||||
self.auth.enabled = val.to_lowercase() == "true" || val == "1";
|
self.auth.enabled = val.to_lowercase() == "true" || val == "1";
|
||||||
}
|
}
|
||||||
|
if let Ok(val) = env::var("NORA_AUTH_ANONYMOUS_READ") {
|
||||||
|
self.auth.anonymous_read = val.to_lowercase() == "true" || val == "1";
|
||||||
|
}
|
||||||
if let Ok(val) = env::var("NORA_AUTH_HTPASSWD_FILE") {
|
if let Ok(val) = env::var("NORA_AUTH_HTPASSWD_FILE") {
|
||||||
self.auth.htpasswd_file = val;
|
self.auth.htpasswd_file = val;
|
||||||
}
|
}
|
||||||
@@ -741,10 +748,40 @@ mod tests {
|
|||||||
fn test_auth_config_default() {
|
fn test_auth_config_default() {
|
||||||
let a = AuthConfig::default();
|
let a = AuthConfig::default();
|
||||||
assert!(!a.enabled);
|
assert!(!a.enabled);
|
||||||
|
assert!(!a.anonymous_read);
|
||||||
assert_eq!(a.htpasswd_file, "users.htpasswd");
|
assert_eq!(a.htpasswd_file, "users.htpasswd");
|
||||||
assert_eq!(a.token_storage, "data/tokens");
|
assert_eq!(a.token_storage, "data/tokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_auth_anonymous_read_from_toml() {
|
||||||
|
let toml = r#"
|
||||||
|
[server]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 4000
|
||||||
|
|
||||||
|
[storage]
|
||||||
|
mode = "local"
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
enabled = true
|
||||||
|
anonymous_read = true
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let config: Config = toml::from_str(toml).unwrap();
|
||||||
|
assert!(config.auth.enabled);
|
||||||
|
assert!(config.auth.anonymous_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_env_override_anonymous_read() {
|
||||||
|
let mut config = Config::default();
|
||||||
|
std::env::set_var("NORA_AUTH_ANONYMOUS_READ", "true");
|
||||||
|
config.apply_env_overrides();
|
||||||
|
assert!(config.auth.anonymous_read);
|
||||||
|
std::env::remove_var("NORA_AUTH_ANONYMOUS_READ");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_maven_proxy_entry_simple() {
|
fn test_maven_proxy_entry_simple() {
|
||||||
let entry = MavenProxyEntry::Simple("https://repo.example.com".to_string());
|
let entry = MavenProxyEntry::Simple("https://repo.example.com".to_string());
|
||||||
|
|||||||
Reference in New Issue
Block a user