test(login/register): via password or magic-link
Password login ┌─────┬────────────────────────────────────────────────────┬────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────┐ │ # │ Case │ Where │ Assertion │ ├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ L1 │ Login by username │ auth_login.hurl Case 1 │ 200 + access_token, user.email match │ ├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ L2 │ Login by email (dispatch on @) │ auth_login.hurl Case 2 │ 200, same session shape as L1 │ ├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ L3 │ Bad password on username path │ auth_login.hurl Case 3 │ 403 anti-enum │ ├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ L4 │ Bad password on email path │ auth_login.hurl Case 4 │ 403 anti-enum (same shape as L3) │ ├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ L5 │ Unknown username │ auth_login.hurl Case 5 │ 403 anti-enum (same shape as L3) │ ├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ L6 │ Unknown email │ auth_login.hurl Case 6 │ 403 anti-enum (same shape as L3) │ ├─────┼────────────────────────────────────────────────────┼────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────┤ │ L7 │ /api/auth/oidc/providers reports methods correctly │ auth_login.hurl Case 7 │ password_login_enabled: true, magic_link_login_enabled: true, require_verified_email: false │ └─────┴────────────────────────────────────────────────────┴────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘ Password registration ┌─────┬───────────────────────────────────────────────────┬──────────────────────────────┬─────────────────────────────────────────────────────────┐ │ # │ Case │ Where │ Assertion │ ├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼─────────────────────────────────────────────────────────┤ │ R1 │ Classic username + email + password → uniform 200 │ registration.hurl Step 2 │ anti-enum message contains "request received" │ ├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼─────────────────────────────────────────────────────────┤ │ R2 │ Login after register works │ registration.hurl Step 2b │ 200 + session for the new user │ ├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼─────────────────────────────────────────────────────────┤ │ R3 │ Email collision → uniform 200 (no rewrite) │ registration.hurl Steps 8-10 │ attacker password doesn't work; original account intact │ ├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────┤ │ R4 │ Username collision → uniform 200 │ registration.hurl Step 11 │ same anti-enum shape │ ├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────┤ │ R5 │ Off-domain rejection │ registration.hurl Step 12 │ 403 RegistrationDomainNotAllowed │ ├─────┼───────────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────┤ │ R6 │ Case-insensitive domain match │ registration.hurl Step 12b │ uniform 200 on charlie@EXAMPLE.COM │ └─────┴───────────────────────────────────────────────────┴──────────────────────────────┴────────────────────────────┘ Magic-link registration (email-only signup) ┌─────┬──────────────────────────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────┐ │ # │ Case │ Where │ Assertion │ ├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤ │ MR1 │ Email-only signup → welcome mail queued │ registration.hurl Step 3 │ uniform 200 + browser-binding cookie set │ ├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤ │ MR2 │ Welcome mail contains magic-link URL │ registration.hurl Step 4 │ captured from mock SMTP │ ├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤ │ MR3 │ PR 22 cross-browser confirmation page │ registration.hurl Step 5a │ 200 HTML "different browser" │ ├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤ │ MR4 │ Cookie-bound redemption lands on SPA │ registration.hurl Step 5b │ 302 → /files (SvelteKit route, post-migration) │ ├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤ │ MR5 │ email_verified_at stamped after redemption │ registration.hurl Step 6 │ field present on /api/auth/me │ ├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤ │ MR6 │ Second magic-link post-signup │ registration.hurl Step 7 │ uniform 200 │ ├─────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────┤ │ MR7 │ Profile PATCH — no-op, name set, empty-string rejected, username-taken 409, claim-once 409, etc. │ registration.hurl Steps 6a–6i │ full profile lifecycle │ └─────┴──────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────┘ Magic-link login (existing account) ┌─────┬──────────────────────────────────────────────────────────┬──────────────────────────────────────┬───────────────────────────────────────┐ │ # │ Case │ Where │ Assertion │ ├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤ │ ML1 │ Baseline password login still works │ auth_magic_link_login.hurl Steps 1-2 │ 200 │ ├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤ │ ML2 │ magic-link/send with email identifier │ auth_magic_link_login.hurl Step 3 │ uniform 200 + cookie │ ├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤ │ ML3 │ magic-link/send with username identifier (dispatch on @) │ auth_magic_link_login.hurl Step 4 │ uniform 200 │ ├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤ │ ML4 │ Password-user policy: mail actually sent │ auth_magic_link_login.hurl Step 5 │ SMTP capture proves permit_magic_link_for_password_users in effect │ ├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤ │ ML5 │ Redemption creates a session │ auth_magic_link_login.hurl Steps 6-7 │ 302 → /files, /api/auth/me returns the same user │ ├─────┼──────────────────────────────────────────────────────────┼──────────────────────────────────────┼───────────────────────────────────────┤ │ ML6 │ Anti-enum on unknown identifier │ auth_magic_link_login.hurl Step 8 │ same uniform 200 shape as ML3 │ └─────┴──────────────────────────────────────────────────────────┴──────────────────────────────────────┴───────────────────────────────────────┘ OIDC ┌─────┬────────────────────────────────────────────────────────────────────────┬───────────────────┬────────────────────────────────────────────────────────────────────────────────────────────┐ │ # │ Case │ Where │ Assertion │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O1 │ Setup local admin (bootstrap) │ oidc.hurl Step 1 │ 201 │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O2 │ Providers endpoint — OIDC visible │ oidc.hurl Step 2 │ enabled: true, provider_name: MockSSO, password_login_enabled: true, magic_link_login_enabled: false (OIDC-master rule) │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O2b │ Magic-link/send refused (endpoint layer) │ oidc.hurl Step 2b │ 403 MagicLinkLoginDisabled — proves the policy gate fires, not a 503 SMTP-unwired │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O3 │ Authorize redirect includes PKCE + state │ oidc.hurl Step 3 │ 307 to fake IdP │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O4 │ IdP round-trip + JIT provisioning │ oidc.hurl Step 4 │ Callback lands on /login?oidc_code=… │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O5 │ Code exchange → session cookies │ oidc.hurl Step 5 │ 200 + all three cookies │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O6 │ JIT profile mapping (name, given/family, picture, groups → admin role) │ oidc.hurl Step 6 │ every claim reflected on /api/auth/me │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O7 │ Refresh rotation on OIDC session │ oidc.hurl Step 7 │ new access/refresh/CSRF cookies │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O8 │ Refreshed cookies authenticate │ oidc.hurl Step 8 │ 200 on /api/auth/me │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O9 │ Repeat login = same local user (no dup) │ oidc.hurl Step 9 │ user_id stable │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O10 │ Anti-takeover: unverified email → refused │ oidc.hurl Step 10 │ 401/403 │ ├─────┼────────────────────────────────────────────────────────────────────────┼───────────────────┼────────────────────────────────────────────────────────────────────────────────────────────┤ │ O11 │ One-time code replay refused │ oidc.hurl Step 11 │ second /exchange → 401 │ └─────┴────────────────────────────────────────────────────────────────────────┴───────────────────┴────────────────────────────────────────────────────────────────────────────────────────────┘ test
This commit is contained in:
+245
-1
@@ -497,6 +497,120 @@ pub struct AuthConfig {
|
||||
/// Env: `OXICLOUD_REGISTRATION_ALLOWED_EMAIL_DOMAINS` (comma-
|
||||
/// separated).
|
||||
pub registration_allowed_email_domains: Vec<String>,
|
||||
/// Additive auth-policy toggles the operator has opted into.
|
||||
/// Distinct from `allowed_auth_methods` (which enables/disables a
|
||||
/// method wholesale) — this vector composes policy switches that
|
||||
/// tweak the default auth behaviour. Empty = pure defaults in
|
||||
/// effect, matching legacy behaviour.
|
||||
///
|
||||
/// Vector shape (rather than one boolean per policy) so future
|
||||
/// switches can be added by appending a variant instead of
|
||||
/// growing the env-var surface — `OXICLOUD_AUTH_POLICIES=policy_a,policy_b`.
|
||||
/// Each variant's name carries its own polarity (`Permit...`,
|
||||
/// future `Require...` / `Deny...`); the field name stays neutral
|
||||
/// so a future deny-style policy reads correctly at the call site.
|
||||
///
|
||||
/// Env: `OXICLOUD_AUTH_POLICIES` (comma-separated).
|
||||
///
|
||||
/// Deprecated legacy alias: `OXICLOUD_MAGIC_LINK_OPEN_TO_PASSWORD_USERS=true`
|
||||
/// still adds `PermitMagicLinkForPasswordUsers` to the vector for
|
||||
/// backwards compatibility; emits a startup warning encouraging
|
||||
/// migration to the vector form.
|
||||
pub auth_policies: Vec<AuthPolicy>,
|
||||
/// Allowlist of self-service auth methods offered on the login
|
||||
/// page and accepted by their respective endpoints. Empty (the
|
||||
/// default) = both methods allowed, matching legacy behaviour.
|
||||
/// OIDC is orthogonal — controlled via `OxidcConfig::enabled`.
|
||||
///
|
||||
/// Semantics:
|
||||
/// * `AuthMethod::Password` allowed → `POST /api/auth/login`
|
||||
/// accepts credentials; password-based `register` works.
|
||||
/// * `AuthMethod::MagicLink` allowed → `POST /api/auth/magic-
|
||||
/// link/send` mints tokens; email-only `register` works.
|
||||
///
|
||||
/// A method NOT in the list returns 403 with a specific
|
||||
/// `error_type` (`PasswordLoginDisabled`,
|
||||
/// `MagicLinkLoginDisabled`) so frontends can render a
|
||||
/// contextual message rather than a generic auth error.
|
||||
///
|
||||
/// Startup guard: when `MagicLink` is in the list but
|
||||
/// `SmtpConfig::is_enabled()` is false, the server refuses to
|
||||
/// start. A magic-link policy without a mail sender is a
|
||||
/// misconfiguration that silently locks users out.
|
||||
///
|
||||
/// Env: `OXICLOUD_AUTH_METHODS` (comma-separated:
|
||||
/// `password,magic_link`). Alias: the older
|
||||
/// `OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN=true` still removes
|
||||
/// Password from this list when set (backwards-compat).
|
||||
pub allowed_auth_methods: Vec<AuthMethod>,
|
||||
/// Require the user's email to be verified before login is
|
||||
/// permitted. When `true`, `POST /api/auth/login` returns 403
|
||||
/// `EmailNotVerified` for any account whose `email_verified_at`
|
||||
/// is NULL. Users can prove control by clicking a magic-link
|
||||
/// (which stamps `email_verified_at`) — so this composes with
|
||||
/// `AuthMethod::MagicLink` in the allowlist above to provide a
|
||||
/// verification path.
|
||||
///
|
||||
/// Admin-created users (`POST /api/admin/users`) and the
|
||||
/// first-run setup admin (`POST /api/setup`) get
|
||||
/// `email_verified_at = NOW()` at creation — admin fiat counts
|
||||
/// as verification, matching the OIDC-JIT convention.
|
||||
///
|
||||
/// Env: `OXICLOUD_REQUIRE_VERIFIED_EMAIL` (default `false`).
|
||||
pub require_verified_email: bool,
|
||||
}
|
||||
|
||||
/// Self-service auth method. Exposed as `AuthConfig::allowed_auth_methods`
|
||||
/// and parsed from `OXICLOUD_AUTH_METHODS` (comma-separated). OIDC is
|
||||
/// deliberately excluded — it lives in `OidcConfig` with its own gate.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AuthMethod {
|
||||
Password,
|
||||
MagicLink,
|
||||
}
|
||||
|
||||
impl AuthMethod {
|
||||
/// Case-insensitive parse: accepts `password`, `magic_link`, and the
|
||||
/// dash form `magic-link` (some operators habitually use dashes).
|
||||
/// Unknown token returns `None` so the caller can log-and-skip.
|
||||
pub fn parse(s: &str) -> Option<Self> {
|
||||
match s.trim().to_ascii_lowercase().as_str() {
|
||||
"password" => Some(Self::Password),
|
||||
"magic_link" | "magic-link" | "magiclink" => Some(Self::MagicLink),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additive auth-policy switches. Exposed as `AuthConfig::auth_policies`
|
||||
/// and parsed from `OXICLOUD_AUTH_POLICIES` (comma-separated). Each
|
||||
/// variant's name states its own polarity — `Permit...` grants an
|
||||
/// exception, future `Require...` / `Deny...` variants restrict.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AuthPolicy {
|
||||
/// Allow magic-link login for accounts that ALSO have a password
|
||||
/// configured. Off by default — magic-link is otherwise gated by
|
||||
/// `magic_link_eligibility()` to users without a password
|
||||
/// (mailbox-strength should not shadow a stronger credential).
|
||||
/// Enabling this weakens the password to mailbox-strength for
|
||||
/// affected accounts; opt-in only.
|
||||
///
|
||||
/// Deprecated legacy alias: `OXICLOUD_MAGIC_LINK_OPEN_TO_PASSWORD_USERS=true`
|
||||
/// adds this variant to the vector with a startup warning.
|
||||
PermitMagicLinkForPasswordUsers,
|
||||
}
|
||||
|
||||
impl AuthPolicy {
|
||||
/// Case-insensitive parse: accepts `permit_magic_link_for_password_users`
|
||||
/// (canonical) and the dash form. Unknown token returns `None` so
|
||||
/// the caller can log-and-skip.
|
||||
pub fn parse(s: &str) -> Option<Self> {
|
||||
match s.trim().to_ascii_lowercase().as_str() {
|
||||
"permit_magic_link_for_password_users"
|
||||
| "permit-magic-link-for-password-users" => Some(Self::PermitMagicLinkForPasswordUsers),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rate limiting and brute-force protection configuration.
|
||||
@@ -549,10 +663,29 @@ impl Default for AuthConfig {
|
||||
hash_parallelism: 2,
|
||||
rate_limit: RateLimitConfig::default(),
|
||||
registration_allowed_email_domains: Vec::new(),
|
||||
auth_policies: Vec::new(),
|
||||
allowed_auth_methods: vec![AuthMethod::Password, AuthMethod::MagicLink],
|
||||
require_verified_email: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthConfig {
|
||||
/// True iff `method` is enabled (or the allowlist is empty — meaning
|
||||
/// "all methods allowed", matching pre-`OXICLOUD_AUTH_METHODS`
|
||||
/// behaviour when the operator hasn't opted in yet).
|
||||
pub fn is_method_allowed(&self, method: AuthMethod) -> bool {
|
||||
self.allowed_auth_methods.is_empty() || self.allowed_auth_methods.contains(&method)
|
||||
}
|
||||
|
||||
/// True iff `policy` has been opted into via `OXICLOUD_AUTH_POLICIES`
|
||||
/// (or its legacy alias). Default policies are OFF — the vector is
|
||||
/// additive only, no invert / defaults.
|
||||
pub fn has_policy(&self, policy: AuthPolicy) -> bool {
|
||||
self.auth_policies.contains(&policy)
|
||||
}
|
||||
}
|
||||
|
||||
/// OpenID Connect (OIDC) configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OidcConfig {
|
||||
@@ -1550,6 +1683,96 @@ impl AppConfig {
|
||||
.collect();
|
||||
}
|
||||
|
||||
// Self-service auth-method allowlist. Empty (unset) = both methods
|
||||
// allowed. Unknown tokens are logged-and-skipped; a completely
|
||||
// unparseable value falls back to the default rather than locking
|
||||
// the operator out. If the resulting list is empty (e.g. the
|
||||
// operator wrote `OXICLOUD_AUTH_METHODS=nope`), we restore the
|
||||
// default — a zero-method allowlist would refuse every login.
|
||||
if let Ok(v) = env::var("OXICLOUD_AUTH_METHODS") {
|
||||
let methods: Vec<AuthMethod> = v
|
||||
.split(',')
|
||||
.filter_map(|s| {
|
||||
let parsed = AuthMethod::parse(s);
|
||||
if parsed.is_none() && !s.trim().is_empty() {
|
||||
eprintln!(
|
||||
"⚠️ OXICLOUD_AUTH_METHODS: ignoring unknown token '{}' \
|
||||
(expected: password, magic_link)",
|
||||
s.trim()
|
||||
);
|
||||
}
|
||||
parsed
|
||||
})
|
||||
.collect();
|
||||
if methods.is_empty() {
|
||||
eprintln!(
|
||||
"⚠️ OXICLOUD_AUTH_METHODS parsed to an empty allowlist; \
|
||||
falling back to default (password, magic_link)"
|
||||
);
|
||||
} else {
|
||||
config.auth.allowed_auth_methods = methods;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy alias: OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN=true still
|
||||
// removes Password from the allowlist. Its main handling in the
|
||||
// OIDC config block below is preserved for the `login_options`
|
||||
// response; this line makes the effect apply uniformly through
|
||||
// `is_method_allowed(Password)` so services don't need to check
|
||||
// both flags.
|
||||
if let Ok(v) = env::var("OXICLOUD_OIDC_DISABLE_PASSWORD_LOGIN")
|
||||
&& v.parse::<bool>().unwrap_or(false)
|
||||
{
|
||||
config
|
||||
.auth
|
||||
.allowed_auth_methods
|
||||
.retain(|m| *m != AuthMethod::Password);
|
||||
}
|
||||
|
||||
if let Ok(v) = env::var("OXICLOUD_REQUIRE_VERIFIED_EMAIL") {
|
||||
config.auth.require_verified_email = v.parse::<bool>().unwrap_or(false);
|
||||
}
|
||||
|
||||
// Auth-policy vector. Additive — each recognised token adds a
|
||||
// variant; unknown tokens are logged-and-skipped so a typo
|
||||
// doesn't silently zero the whole vector (an operator wanting
|
||||
// "no policies" simply doesn't set the env var).
|
||||
//
|
||||
// The legacy alias
|
||||
// `OXICLOUD_MAGIC_LINK_OPEN_TO_PASSWORD_USERS=true` is applied
|
||||
// AFTER this block (see the MagicLinkConfig section below) so a
|
||||
// deployment setting BOTH env vars ends up with a single copy
|
||||
// of `PermitMagicLinkForPasswordUsers` regardless of order.
|
||||
if let Ok(v) = env::var("OXICLOUD_AUTH_POLICIES") {
|
||||
for token in v.split(',') {
|
||||
match AuthPolicy::parse(token) {
|
||||
Some(policy) => {
|
||||
if !config.auth.auth_policies.contains(&policy) {
|
||||
config.auth.auth_policies.push(policy);
|
||||
}
|
||||
}
|
||||
None if !token.trim().is_empty() => {
|
||||
eprintln!(
|
||||
"⚠️ OXICLOUD_AUTH_POLICIES: ignoring unknown token '{}' \
|
||||
(known: permit_magic_link_for_password_users)",
|
||||
token.trim()
|
||||
);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
// Reflect the vector into the legacy magic_link config field
|
||||
// so `magic_link_eligibility()` (the site that reads the
|
||||
// boolean today) doesn't need to know about the new form.
|
||||
if config
|
||||
.auth
|
||||
.auth_policies
|
||||
.contains(&AuthPolicy::PermitMagicLinkForPasswordUsers)
|
||||
{
|
||||
config.magic_link.open_to_password_users = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Feature flags
|
||||
if let Ok(enable_auth) = env::var("OXICLOUD_ENABLE_AUTH").map(|v| v.parse::<bool>())
|
||||
&& let Ok(val) = enable_auth
|
||||
@@ -2114,8 +2337,29 @@ impl AppConfig {
|
||||
{
|
||||
config.magic_link.send_per_ip_per_hour = n;
|
||||
}
|
||||
// Legacy alias — writes the same effect as
|
||||
// `OXICLOUD_AUTH_POLICIES=permit_magic_link_for_password_users`.
|
||||
// Warn once at boot so operators know to migrate before we drop
|
||||
// the old var. Kept indefinitely for compat, but the encouraged
|
||||
// form is the vector.
|
||||
if let Ok(v) = env::var("OXICLOUD_MAGIC_LINK_OPEN_TO_PASSWORD_USERS") {
|
||||
config.magic_link.open_to_password_users = v == "true" || v == "1";
|
||||
let enabled = v == "true" || v == "1";
|
||||
config.magic_link.open_to_password_users = enabled;
|
||||
if enabled
|
||||
&& !config
|
||||
.auth
|
||||
.auth_policies
|
||||
.contains(&AuthPolicy::PermitMagicLinkForPasswordUsers)
|
||||
{
|
||||
config
|
||||
.auth
|
||||
.auth_policies
|
||||
.push(AuthPolicy::PermitMagicLinkForPasswordUsers);
|
||||
}
|
||||
eprintln!(
|
||||
"⚠️ OXICLOUD_MAGIC_LINK_OPEN_TO_PASSWORD_USERS is deprecated. \
|
||||
Use `OXICLOUD_AUTH_POLICIES=permit_magic_link_for_password_users` instead."
|
||||
);
|
||||
}
|
||||
if let Ok(v) = env::var("OXICLOUD_NOTIFY_INTERNAL_USERS_ON_SHARE") {
|
||||
config.magic_link.notify_internal_users_on_share = v == "true" || v == "1";
|
||||
|
||||
Reference in New Issue
Block a user