From 5804a59be93bcaf8e969a01bf2acf89e56b24cfc Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 04:51:22 +0000 Subject: [PATCH 01/11] fix(docs): OIDC callback URL, and add new options --- docs/integrations.md | 12 +- docs/oidc-setup.md | 261 ++++++++++++++++++++++++++++++++++++++--- docs/security-guide.md | 2 +- 3 files changed, 249 insertions(+), 26 deletions(-) diff --git a/docs/integrations.md b/docs/integrations.md index 6694cbf..dbc7e78 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -214,7 +214,7 @@ OIDC_ENABLED: true OIDC_ISSUER_URL: https://keycloak.example.com/auth/realms/readur OIDC_CLIENT_ID: readur-client OIDC_CLIENT_SECRET: your-client-secret -OIDC_REDIRECT_URI: https://readur.example.com/auth/oidc/callback +OIDC_REDIRECT_URI: https://readur.example.com/api/auth/oidc/callback OIDC_SCOPES: openid profile email ``` @@ -230,7 +230,7 @@ Keycloak client configuration: "frontchannelLogout": true, "protocol": "openid-connect", "redirectUris": [ - "https://readur.example.com/auth/oidc/callback" + "https://readur.example.com/api/auth/oidc/callback" ], "webOrigins": [ "https://readur.example.com" @@ -246,7 +246,7 @@ OIDC_ENABLED: true OIDC_ISSUER_URL: https://your-tenant.auth0.com/ OIDC_CLIENT_ID: your-client-id OIDC_CLIENT_SECRET: your-client-secret -OIDC_REDIRECT_URI: https://readur.example.com/auth/oidc/callback +OIDC_REDIRECT_URI: https://readur.example.com/api/auth/oidc/callback OIDC_SCOPES: openid profile email ``` @@ -258,7 +258,7 @@ OIDC_ENABLED: true OIDC_ISSUER_URL: https://your-org.okta.com/oauth2/default OIDC_CLIENT_ID: your-client-id OIDC_CLIENT_SECRET: your-client-secret -OIDC_REDIRECT_URI: https://readur.example.com/auth/oidc/callback +OIDC_REDIRECT_URI: https://readur.example.com/api/auth/oidc/callback ``` #### Azure AD @@ -269,7 +269,7 @@ OIDC_ENABLED: true OIDC_ISSUER_URL: https://login.microsoftonline.com/{tenant-id}/v2.0 OIDC_CLIENT_ID: your-application-id OIDC_CLIENT_SECRET: your-client-secret -OIDC_REDIRECT_URI: https://readur.example.com/auth/oidc/callback +OIDC_REDIRECT_URI: https://readur.example.com/api/auth/oidc/callback OIDC_SCOPES: openid profile email User.Read ``` @@ -281,7 +281,7 @@ OIDC_ENABLED: true OIDC_ISSUER_URL: https://accounts.google.com OIDC_CLIENT_ID: your-client-id.apps.googleusercontent.com OIDC_CLIENT_SECRET: your-client-secret -OIDC_REDIRECT_URI: https://readur.example.com/auth/oidc/callback +OIDC_REDIRECT_URI: https://readur.example.com/api/auth/oidc/callback OIDC_SCOPES: openid profile email ``` diff --git a/docs/oidc-setup.md b/docs/oidc-setup.md index c5a5da4..12e862b 100644 --- a/docs/oidc-setup.md +++ b/docs/oidc-setup.md @@ -14,6 +14,7 @@ This guide explains how to configure OpenID Connect (OIDC) authentication for Re - [Microsoft Azure AD](#microsoft-azure-ad) - [Keycloak](#keycloak) - [Auth0](#auth0) + - [Authentik](#authentik) - [Generic OIDC Provider](#generic-oidc-provider) - [Testing the Setup](#testing-the-setup) - [User Experience](#user-experience) @@ -25,6 +26,8 @@ This guide explains how to configure OpenID Connect (OIDC) authentication for Re OIDC authentication in Readur provides: - **Single Sign-On (SSO)**: Users can sign in with existing corporate accounts +- **Email-Based User Syncing**: Automatically link existing local users to OIDC by email +- **Flexible Auto-Registration**: Control whether new users can self-register via OIDC - **Centralized User Management**: User provisioning handled by your identity provider - **Enhanced Security**: No need to manage passwords in Readur - **Seamless Integration**: Works alongside existing local authentication @@ -52,7 +55,9 @@ Configure OIDC by setting these environment variables: | `OIDC_CLIENT_ID` | ✅ | OAuth2 client ID from your provider | `readur-app-client-id` | | `OIDC_CLIENT_SECRET` | ✅ | OAuth2 client secret from your provider | `very-secret-key` | | `OIDC_ISSUER_URL` | ✅ | OIDC provider's issuer URL | `https://accounts.google.com` | -| `OIDC_REDIRECT_URI` | ✅ | Callback URL for your Readur instance | `https://readur.company.com/auth/oidc/callback` | +| `OIDC_REDIRECT_URI` | ✅ | Callback URL for your Readur instance | `https://readur.company.com/api/auth/oidc/callback` | +| `OIDC_AUTO_REGISTER` | ❌ | Allow new users to self-register (default: `true`) | `true` or `false` | +| `ALLOW_LOCAL_AUTH` | ❌ | Allow username/password authentication (default: `true`) | `true` or `false` | ### Example Configurations @@ -66,7 +71,9 @@ OIDC_ENABLED=true OIDC_CLIENT_ID=123456789-abcdefgh.apps.googleusercontent.com OIDC_CLIENT_SECRET=GOCSPX-your-secret-key OIDC_ISSUER_URL=https://accounts.google.com -OIDC_REDIRECT_URI=https://readur.company.com/auth/oidc/callback +OIDC_REDIRECT_URI=https://readur.company.com/api/auth/oidc/callback +OIDC_AUTO_REGISTER=true # Allow new users to register via OIDC +ALLOW_LOCAL_AUTH=true # Set to false to disable username/password login ``` #### Development Setup @@ -79,7 +86,9 @@ OIDC_ENABLED=true OIDC_CLIENT_ID=dev-client-id OIDC_CLIENT_SECRET=dev-client-secret OIDC_ISSUER_URL=https://your-keycloak.company.com/auth/realms/readur -OIDC_REDIRECT_URI=http://localhost:8000/auth/oidc/callback +OIDC_REDIRECT_URI=http://localhost:8000/api/auth/oidc/callback +OIDC_AUTO_REGISTER=false # Only allow existing users to login +ALLOW_LOCAL_AUTH=true # Keep local auth for development ``` #### Docker Compose Setup @@ -98,7 +107,8 @@ services: OIDC_CLIENT_ID: "${OIDC_CLIENT_ID}" OIDC_CLIENT_SECRET: "${OIDC_CLIENT_SECRET}" OIDC_ISSUER_URL: "${OIDC_ISSUER_URL}" - OIDC_REDIRECT_URI: "https://readur.company.com/auth/oidc/callback" + OIDC_REDIRECT_URI: "https://readur.company.com/api/auth/oidc/callback" + OIDC_AUTO_REGISTER: "true" ports: - "8000:8000" ``` @@ -122,8 +132,8 @@ services: 4. **Configure Redirect URIs**: ``` Authorized redirect URIs: - https://your-readur-domain.com/auth/oidc/callback - http://localhost:8000/auth/oidc/callback (for development) + https://your-readur-domain.com/api/auth/oidc/callback + http://localhost:8000/api/auth/oidc/callback (for development) ``` 5. **Environment Variables**: @@ -132,7 +142,8 @@ services: OIDC_CLIENT_ID=123456789-abcdefgh.apps.googleusercontent.com OIDC_CLIENT_SECRET=GOCSPX-your-secret-key OIDC_ISSUER_URL=https://accounts.google.com - OIDC_REDIRECT_URI=https://your-readur-domain.com/auth/oidc/callback + OIDC_REDIRECT_URI=https://your-readur-domain.com/api/auth/oidc/callback + OIDC_AUTO_REGISTER=true ``` ### Microsoft Azure AD @@ -142,7 +153,7 @@ services: - Click "New registration" - Name: "Readur Document Management" - Supported account types: Choose based on your needs - - Redirect URI: `https://your-readur-domain.com/auth/oidc/callback` + - Redirect URI: `https://your-readur-domain.com/api/auth/oidc/callback` 2. **Configure Authentication**: - In your app registration, go to "Authentication" @@ -166,7 +177,8 @@ services: OIDC_CLIENT_ID=12345678-1234-1234-1234-123456789012 OIDC_CLIENT_SECRET=your-client-secret OIDC_ISSUER_URL=https://login.microsoftonline.com/your-tenant-id/v2.0 - OIDC_REDIRECT_URI=https://your-readur-domain.com/auth/oidc/callback + OIDC_REDIRECT_URI=https://your-readur-domain.com/api/auth/oidc/callback + OIDC_AUTO_REGISTER=true ``` ### Keycloak @@ -184,7 +196,7 @@ services: 3. **Configure Client Settings**: - Access Type: `confidential` - Standard Flow Enabled: `ON` - - Valid Redirect URIs: `https://your-readur-domain.com/auth/oidc/callback*` + - Valid Redirect URIs: `https://your-readur-domain.com/api/auth/oidc/callback*` - Web Origins: `https://your-readur-domain.com` 4. **Get Client Secret**: @@ -197,7 +209,8 @@ services: OIDC_CLIENT_ID=readur OIDC_CLIENT_SECRET=your-keycloak-client-secret OIDC_ISSUER_URL=https://keycloak.company.com/auth/realms/your-realm - OIDC_REDIRECT_URI=https://your-readur-domain.com/auth/oidc/callback + OIDC_REDIRECT_URI=https://your-readur-domain.com/api/auth/oidc/callback + OIDC_AUTO_REGISTER=true ``` ### Auth0 @@ -208,7 +221,7 @@ services: - Application Type: "Regular Web Applications" 2. **Configure Settings**: - - Allowed Callback URLs: `https://your-readur-domain.com/auth/oidc/callback` + - Allowed Callback URLs: `https://your-readur-domain.com/api/auth/oidc/callback` - Allowed Web Origins: `https://your-readur-domain.com` - Allowed Logout URLs: `https://your-readur-domain.com/login` @@ -222,15 +235,154 @@ services: OIDC_CLIENT_ID=your-auth0-client-id OIDC_CLIENT_SECRET=your-auth0-client-secret OIDC_ISSUER_URL=https://your-app.auth0.com - OIDC_REDIRECT_URI=https://your-readur-domain.com/auth/oidc/callback + OIDC_REDIRECT_URI=https://your-readur-domain.com/api/auth/oidc/callback + OIDC_AUTO_REGISTER=true ``` +### Authentik + +[Authentik](https://goauthentik.io/) is a self-hosted, open-source identity provider that's perfect for organizations wanting full control over their authentication. + +1. **Create an Application** in Authentik Admin Interface: + - Navigate to "Applications" → "Applications" + - Click "Create" and choose "Create with Wizard" + - Name: "Readur Document Management" + - Slug: `readur` (or your preferred slug) + +2. **Configure Provider**: + - Provider type: Choose "OAuth2/OpenID Provider" + - Authorization flow: "default-provider-authorization-implicit-consent" + - Client type: "Confidential" + - Redirect URIs: Add `https://your-readur-domain.com/api/auth/oidc/callback` + - Scopes: Ensure `openid`, `email`, and `profile` are included + +3. **Get Application Credentials**: + - After creation, go to your application's "Provider" settings + - Copy the Client ID (shown in the overview) + - Copy the Client Secret (click "Copy" button) + +4. **Configure Scopes and Claims** (Optional but recommended): + - Go to "Customization" → "Property Mappings" + - Ensure the following scope mappings exist and are enabled: + - `openid` → `sub` claim + - `email` → `email` claim + - `profile` → `preferred_username` and `name` claims + +5. **Get Issuer URL**: + - The issuer URL format is: `https://your-authentik-domain.com/application/o/readur/` + - Replace `readur` with your application's slug + - Alternatively, use: `https://your-authentik-domain.com/application/o//` + +6. **Environment Variables**: + ```env + OIDC_ENABLED=true + OIDC_CLIENT_ID= + OIDC_CLIENT_SECRET= + OIDC_ISSUER_URL=https://your-authentik-domain.com/application/o/readur/ + OIDC_REDIRECT_URI=https://your-readur-domain.com/api/auth/oidc/callback + OIDC_AUTO_REGISTER=true + ``` + +7. **Testing Authentik Integration**: + - Navigate to your Readur instance + - Click "Sign in with OIDC" + - You should be redirected to Authentik's login page + - After authentication, you'll be redirected back to Readur + +**Authentik-Specific Tips**: + +- **User Attributes**: Authentik automatically provides `email`, `preferred_username`, and `name` in the OIDC claims +- **Group Mapping**: You can map Authentik groups to user attributes (future Readur feature will support role mapping) +- **Self-Service Portal**: Users can manage their Authentik profile at `https://your-authentik-domain.com/if/user/` +- **Email Verification**: If email verification is required in Authentik, ensure users verify their email before using Readur +- **Custom Branding**: Authentik allows you to customize the login page to match your organization's branding + +**Docker Compose Example with Authentik**: + +If you're running both Readur and Authentik with Docker Compose: + +```yaml +version: '3.8' + +services: + authentik-server: + image: ghcr.io/goauthentik/server:latest + restart: unless-stopped + command: server + environment: + AUTHENTIK_SECRET_KEY: your-secret-key-here + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: authentik + volumes: + - ./media:/media + - ./custom-templates:/templates + ports: + - "9000:9000" + - "9443:9443" + depends_on: + - postgresql + - redis + + readur: + image: readur:latest + environment: + DATABASE_URL: postgresql://readur:readur@postgres:5432/readur + + # Authentik OIDC Configuration + OIDC_ENABLED: "true" + OIDC_CLIENT_ID: "" + OIDC_CLIENT_SECRET: "" + OIDC_ISSUER_URL: "https://authentik.company.com/application/o/readur/" + OIDC_REDIRECT_URI: "https://readur.company.com/api/auth/oidc/callback" + OIDC_AUTO_REGISTER: "true" + ports: + - "8000:8000" + depends_on: + - postgres + + postgresql: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + volumes: + - database:/var/lib/postgresql/data + # Create both databases + command: > + bash -c " + docker-entrypoint.sh postgres & + sleep 5 + psql -U postgres -c 'CREATE DATABASE authentik;' + psql -U postgres -c 'CREATE DATABASE readur;' + wait + " + + redis: + image: redis:alpine + restart: unless-stopped + +volumes: + database: + media: +``` + +**Troubleshooting Authentik**: + +- **"Discovery failed"**: Verify the issuer URL includes the full path with application slug +- **"Invalid client"**: Double-check the Client ID matches exactly (no extra spaces) +- **"Redirect URI mismatch"**: Ensure the redirect URI in Authentik matches `OIDC_REDIRECT_URI` exactly +- **Email not syncing**: Check that the `email` scope is enabled in your Authentik application +- **Users not linking**: Verify emails match exactly between local users and Authentik user emails + ### Generic OIDC Provider For any OIDC-compliant provider: 1. **Register Your Application** with the provider -2. **Configure Redirect URI**: `https://your-readur-domain.com/auth/oidc/callback` +2. **Configure Redirect URI**: `https://your-readur-domain.com/api/auth/oidc/callback` 3. **Get Credentials**: Client ID, Client Secret, and Issuer URL 4. **Set Environment Variables**: ```env @@ -238,7 +390,8 @@ For any OIDC-compliant provider: OIDC_CLIENT_ID=your-client-id OIDC_CLIENT_SECRET=your-client-secret OIDC_ISSUER_URL=https://your-provider.com - OIDC_REDIRECT_URI=https://your-readur-domain.com/auth/oidc/callback + OIDC_REDIRECT_URI=https://your-readur-domain.com/api/auth/oidc/callback + OIDC_AUTO_REGISTER=true ``` ## Testing the Setup @@ -252,7 +405,7 @@ When starting Readur, check the logs for OIDC configuration: ✅ OIDC_CLIENT_ID: your-client-id (loaded from env) ✅ OIDC_CLIENT_SECRET: ***hidden*** (loaded from env, 32 chars) ✅ OIDC_ISSUER_URL: https://accounts.google.com (loaded from env) -✅ OIDC_REDIRECT_URI: https://your-domain.com/auth/oidc/callback (loaded from env) +✅ OIDC_REDIRECT_URI: https://your-domain.com/api/auth/oidc/callback (loaded from env) ``` ### 2. Test Discovery Endpoint @@ -306,12 +459,82 @@ For returning users: 2. Readur matches the user by `oidc_subject` and `oidc_issuer` 3. User is automatically signed in without creating a duplicate account +### Email-Based User Syncing + +Readur intelligently handles existing local users when they first log in via OIDC: + +**Existing Local User with Matching Email**: +- When an OIDC user logs in with an email that matches an existing local user +- The OIDC identity is automatically linked to that existing account +- User retains all their documents, settings, and permissions +- The `auth_provider` field is updated to `oidc` +- Future logins can use OIDC (password still works if set) + +**Example**: If you have a local user `john.doe@company.com`, and they log in via OIDC with the same email, their account is seamlessly upgraded to support OIDC authentication without creating a duplicate account. + +### Auto-Registration Control + +The `OIDC_AUTO_REGISTER` setting controls whether new users can self-register: + +**When `OIDC_AUTO_REGISTER=true` (default)**: +- New OIDC users are automatically created when they first log in +- Perfect for open environments where any company employee should get access +- Username is derived from OIDC claims (preferred_username or email) +- Users get the default "user" role + +**When `OIDC_AUTO_REGISTER=false`**: +- Only existing users (pre-created by admin or linked by email) can log in +- OIDC login attempts by unregistered users are rejected with HTTP 403 +- Ideal for production environments requiring controlled access +- Admin must pre-create users before they can use OIDC + +**Migration Strategy**: Set to `false` initially, have existing users log in to link accounts, then enable for new users. + +### Disabling Local Authentication + +For OIDC-only deployments, you can disable local username/password authentication: + +**When `ALLOW_LOCAL_AUTH=false`**: +- Local registration endpoint returns HTTP 403 Forbidden +- Local login endpoint returns HTTP 403 Forbidden +- Only OIDC authentication is available +- Perfect for enforcing SSO-only access +- Existing local users can still be linked via email when they use OIDC + +**Security Benefits**: +- Single authentication method reduces attack surface +- Centralized password management through identity provider +- No need to manage password resets or policies in Readur +- Corporate password policies automatically apply + +**Important Notes**: +- Ensure OIDC is working before disabling local auth +- At least one admin should test OIDC login first +- Cannot disable both OIDC and local auth (server will refuse to start) +- Recommended configuration for production SSO environments + +**Example OIDC-Only Configuration**: +```env +# Enable OIDC +OIDC_ENABLED=true +OIDC_CLIENT_ID=your-client-id +OIDC_CLIENT_SECRET=your-client-secret +OIDC_ISSUER_URL=https://your-provider.com +OIDC_REDIRECT_URI=https://readur.company.com/api/auth/oidc/callback +OIDC_AUTO_REGISTER=true + +# Disable local authentication +ALLOW_LOCAL_AUTH=false +``` + ### Mixed Authentication +When both authentication methods are enabled (`ALLOW_LOCAL_AUTH=true`): - Local users can continue using username/password -- OIDC users are created as separate accounts +- OIDC users can have both OIDC and password authentication - Administrators can manage both types of users -- No automatic account linking between local and OIDC accounts +- Email-based automatic account linking prevents duplicate accounts +- Users can choose their preferred login method ## Troubleshooting @@ -398,7 +621,7 @@ curl -X GET "https://your-readur-domain.com/api/auth/oidc/callback?code=AUTH_COD 1. **Use HTTPS**: Always use HTTPS in production ```env - OIDC_REDIRECT_URI=https://readur.company.com/auth/oidc/callback + OIDC_REDIRECT_URI=https://readur.company.com/api/auth/oidc/callback ``` 2. **Secure Client Secret**: Store client secrets securely diff --git a/docs/security-guide.md b/docs/security-guide.md index 6c537f7..c60f7b8 100644 --- a/docs/security-guide.md +++ b/docs/security-guide.md @@ -34,7 +34,7 @@ OIDC_ENABLED: "true" OIDC_CLIENT_ID: "readur-client" OIDC_CLIENT_SECRET: "your-client-secret" OIDC_ISSUER_URL: "https://auth.example.com/realms/readur" -OIDC_REDIRECT_URI: "https://readur.example.com/auth/oidc/callback" +OIDC_REDIRECT_URI: "https://readur.example.com/api/auth/oidc/callback" OIDC_SCOPES: "openid profile email" ``` From 0032a30bb1dace95537a233cbdd791a1a5aad86a Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 04:52:09 +0000 Subject: [PATCH 02/11] feat(oidc): add option for auto-register, local login, and user matching by email --- src/config.rs | 58 ++++++++++++++- src/db/users.rs | 72 +++++++++++++++++- src/routes/auth.rs | 177 ++++++++++++++++++++++++++++++++++++--------- 3 files changed, 271 insertions(+), 36 deletions(-) diff --git a/src/config.rs b/src/config.rs index 6ac59e0..3689f6b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -33,6 +33,10 @@ pub struct Config { pub oidc_client_secret: Option, pub oidc_issuer_url: Option, pub oidc_redirect_uri: Option, + pub oidc_auto_register: bool, + + // Authentication Configuration + pub allow_local_auth: bool, // S3 Configuration pub s3_enabled: bool, @@ -409,7 +413,45 @@ impl Config { None } }, - + oidc_auto_register: match env::var("OIDC_AUTO_REGISTER") { + Ok(val) => match val.to_lowercase().as_str() { + "true" | "1" | "yes" | "on" => { + println!("✅ OIDC_AUTO_REGISTER: true (loaded from env)"); + true + } + _ => { + println!("✅ OIDC_AUTO_REGISTER: false (loaded from env)"); + false + } + }, + Err(_) => { + println!("⚠️ OIDC_AUTO_REGISTER: true (using default - env var not set)"); + true // Default to true for convenience + } + }, + + // Authentication Configuration + allow_local_auth: match env::var("ALLOW_LOCAL_AUTH") { + Ok(val) => match val.to_lowercase().as_str() { + "true" | "1" | "yes" | "on" => { + println!("✅ ALLOW_LOCAL_AUTH: true (loaded from env)"); + true + } + "false" | "0" | "no" | "off" => { + println!("✅ ALLOW_LOCAL_AUTH: false (loaded from env)"); + false + } + _ => { + println!("⚠️ ALLOW_LOCAL_AUTH: Invalid value '{}', defaulting to true", val); + true + } + }, + Err(_) => { + println!("⚠️ ALLOW_LOCAL_AUTH: true (using default - env var not set)"); + true // Default to true for backward compatibility + } + }, + // S3 Configuration s3_enabled: match env::var("S3_ENABLED") { Ok(val) => { @@ -523,6 +565,7 @@ impl Config { // OIDC validation if config.oidc_enabled { println!("🔐 OIDC is enabled"); + println!("🔓 OIDC auto-registration: {}", config.oidc_auto_register); if config.oidc_client_id.is_none() { println!("❌ OIDC_CLIENT_ID is required when OIDC is enabled"); } @@ -538,6 +581,19 @@ impl Config { } else { println!("🔐 OIDC is disabled"); } + + // Authentication method validation + println!("🔑 Local authentication (username/password): {}", + if config.allow_local_auth { "enabled" } else { "disabled" }); + + if !config.oidc_enabled && !config.allow_local_auth { + println!("❌ WARNING: Both OIDC and local authentication are disabled!"); + println!(" You will not be able to log in. Enable at least one authentication method."); + return Err(anyhow::anyhow!( + "Invalid authentication configuration: Both OIDC and local auth are disabled. \ + Enable at least one authentication method (OIDC_ENABLED=true or ALLOW_LOCAL_AUTH=true)" + )); + } println!("✅ Configuration validation completed successfully!\n"); diff --git a/src/db/users.rs b/src/db/users.rs index 979a57b..aea5520 100644 --- a/src/db/users.rs +++ b/src/db/users.rs @@ -216,7 +216,7 @@ impl Database { let row = sqlx::query( r#" - INSERT INTO users (username, email, role, created_at, updated_at, + INSERT INTO users (username, email, role, created_at, updated_at, oidc_subject, oidc_issuer, oidc_email, auth_provider) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, username, email, password_hash, role, created_at, updated_at, @@ -249,4 +249,74 @@ impl Database { auth_provider: row.get::("auth_provider").try_into().unwrap_or(AuthProvider::Oidc), }) } + + pub async fn get_user_by_email(&self, email: &str) -> Result> { + let row = sqlx::query( + "SELECT id, username, email, password_hash, role, created_at, updated_at, + oidc_subject, oidc_issuer, oidc_email, auth_provider FROM users WHERE email = $1" + ) + .bind(email) + .fetch_optional(&self.pool) + .await?; + + match row { + Some(row) => Ok(Some(User { + id: row.get("id"), + username: row.get("username"), + email: row.get("email"), + password_hash: row.get("password_hash"), + role: row.get::("role").try_into().unwrap_or(crate::models::UserRole::User), + created_at: row.get("created_at"), + updated_at: row.get("updated_at"), + oidc_subject: row.get("oidc_subject"), + oidc_issuer: row.get("oidc_issuer"), + oidc_email: row.get("oidc_email"), + auth_provider: row.get::("auth_provider").try_into().unwrap_or(AuthProvider::Local), + })), + None => Ok(None), + } + } + + pub async fn link_user_to_oidc( + &self, + user_id: Uuid, + oidc_subject: &str, + oidc_issuer: &str, + oidc_email: &str, + ) -> Result { + let row = sqlx::query( + r#" + UPDATE users + SET oidc_subject = $2, + oidc_issuer = $3, + oidc_email = $4, + auth_provider = $5, + updated_at = NOW() + WHERE id = $1 + RETURNING id, username, email, password_hash, role, created_at, updated_at, + oidc_subject, oidc_issuer, oidc_email, auth_provider + "# + ) + .bind(user_id) + .bind(oidc_subject) + .bind(oidc_issuer) + .bind(oidc_email) + .bind(AuthProvider::Oidc.to_string()) + .fetch_one(&self.pool) + .await?; + + Ok(User { + id: row.get("id"), + username: row.get("username"), + email: row.get("email"), + password_hash: row.get("password_hash"), + role: row.get::("role").try_into().unwrap_or(crate::models::UserRole::User), + created_at: row.get("created_at"), + updated_at: row.get("updated_at"), + oidc_subject: row.get("oidc_subject"), + oidc_issuer: row.get("oidc_issuer"), + oidc_email: row.get("oidc_email"), + auth_provider: row.get::("auth_provider").try_into().unwrap_or(AuthProvider::Oidc), + }) + } } \ No newline at end of file diff --git a/src/routes/auth.rs b/src/routes/auth.rs index 5091c3f..f050c63 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -10,7 +10,8 @@ use std::sync::Arc; use crate::{ auth::{create_jwt, AuthUser}, - models::{CreateUser, LoginRequest, LoginResponse, UserResponse, UserRole}, + models::{CreateUser, LoginRequest, LoginResponse, User, UserResponse, UserRole}, + oidc::OidcUserInfo, AppState, }; @@ -39,6 +40,18 @@ async fn register( State(state): State>, Json(user_data): Json, ) -> Response { + // Check if local authentication is enabled + if !state.config.allow_local_auth { + tracing::warn!("Local registration attempt rejected - local auth is disabled"); + return ( + StatusCode::FORBIDDEN, + Json(serde_json::json!({ + "error": "Local registration is disabled", + "details": "This instance only allows OIDC authentication. Please contact your administrator." + })) + ).into_response(); + } + match state.db.create_user(user_data).await { Ok(user) => { let user_response: UserResponse = user.into(); @@ -84,6 +97,12 @@ async fn login( State(state): State>, Json(login_data): Json, ) -> Result, StatusCode> { + // Check if local authentication is enabled + if !state.config.allow_local_auth { + tracing::warn!("Local authentication attempt rejected - local auth is disabled"); + return Err(StatusCode::FORBIDDEN); + } + let user = state .db .get_user_by_username(&login_data.username) @@ -204,47 +223,91 @@ async fn oidc_callback( StatusCode::UNAUTHORIZED })?; - // Find or create user in database + // Find or create user in database with email-based syncing let issuer_url = state.config.oidc_issuer_url.as_ref().unwrap(); tracing::debug!("Looking up user by OIDC subject: {} and issuer: {}", user_info.sub, issuer_url); + let user = match state.db.get_user_by_oidc_subject(&user_info.sub, issuer_url).await { Ok(Some(existing_user)) => { tracing::debug!("Found existing OIDC user: {}", existing_user.username); existing_user }, Ok(None) => { - tracing::debug!("Creating new OIDC user"); - // Create new user - let username = user_info.preferred_username - .or_else(|| user_info.email.clone()) - .unwrap_or_else(|| format!("oidc_user_{}", &user_info.sub[..8])); - - let email = user_info.email.unwrap_or_else(|| format!("{}@oidc.local", username)); - - tracing::debug!("New user details - username: {}, email: {}", username, email); - - let create_user = CreateUser { - username, - email: email.clone(), - password: "".to_string(), // Not used for OIDC users - role: Some(UserRole::User), - }; - - let result = state.db.create_oidc_user( - create_user, - &user_info.sub, - issuer_url, - &email, - ).await; - - match result { - Ok(user) => { - tracing::info!("Successfully created OIDC user: {}", user.username); - user - }, - Err(e) => { - tracing::error!("Failed to create OIDC user: {} (full error: {:#})", e, e); - return Err(StatusCode::INTERNAL_SERVER_ERROR); + // No OIDC user found, check if there's an existing local user with this email + let email = user_info.email.clone(); + + if let Some(email_addr) = &email { + tracing::debug!("Checking for existing local user with email: {}", email_addr); + match state.db.get_user_by_email(email_addr).await { + Ok(Some(existing_local_user)) => { + // Found existing local user with matching email - link to OIDC + tracing::info!( + "Found existing local user '{}' with email '{}', linking to OIDC identity", + existing_local_user.username, + email_addr + ); + + match state.db.link_user_to_oidc( + existing_local_user.id, + &user_info.sub, + issuer_url, + email_addr, + ).await { + Ok(linked_user) => { + tracing::info!( + "Successfully linked user '{}' to OIDC identity", + linked_user.username + ); + linked_user + }, + Err(e) => { + tracing::error!("Failed to link existing user to OIDC: {}", e); + return Err(StatusCode::INTERNAL_SERVER_ERROR); + } + } + }, + Ok(None) => { + // No existing user with this email + if state.config.oidc_auto_register { + // Auto-registration is enabled, create new OIDC user + tracing::debug!("No existing user with this email, creating new OIDC user (auto-registration enabled)"); + create_new_oidc_user( + &state, + &user_info, + issuer_url, + email.as_deref(), + ).await? + } else { + // Auto-registration is disabled, reject login + tracing::warn!( + "OIDC login attempted for unregistered email '{}', but auto-registration is disabled", + email_addr + ); + return Err(StatusCode::FORBIDDEN); + } + }, + Err(e) => { + tracing::error!("Database error during email lookup: {}", e); + return Err(StatusCode::INTERNAL_SERVER_ERROR); + } + } + } else { + // No email provided by OIDC provider + if state.config.oidc_auto_register { + // Auto-registration is enabled, create new user without email sync + tracing::debug!("No email provided by OIDC, creating new user (auto-registration enabled)"); + create_new_oidc_user( + &state, + &user_info, + issuer_url, + None, + ).await? + } else { + // Auto-registration is disabled and no email to sync + tracing::warn!( + "OIDC login attempted without email claim, but auto-registration is disabled" + ); + return Err(StatusCode::FORBIDDEN); } } } @@ -265,4 +328,50 @@ async fn oidc_callback( token, user: user.into(), })) +} + +// Helper function to create a new OIDC user +async fn create_new_oidc_user( + state: &Arc, + user_info: &OidcUserInfo, + issuer_url: &str, + email: Option<&str>, +) -> Result { + tracing::debug!("Creating new OIDC user"); + + let username = user_info.preferred_username + .clone() + .or_else(|| email.map(|e| e.to_string())) + .unwrap_or_else(|| format!("oidc_user_{}", &user_info.sub[..8])); + + let user_email = email + .map(|e| e.to_string()) + .unwrap_or_else(|| format!("{}@oidc.local", username)); + + tracing::debug!("New user details - username: {}, email: {}", username, user_email); + + let create_user = CreateUser { + username, + email: user_email.clone(), + password: "".to_string(), // Not used for OIDC users + role: Some(UserRole::User), + }; + + let result = state.db.create_oidc_user( + create_user, + &user_info.sub, + issuer_url, + &user_email, + ).await; + + match result { + Ok(user) => { + tracing::info!("Successfully created OIDC user: {}", user.username); + Ok(user) + }, + Err(e) => { + tracing::error!("Failed to create OIDC user: {} (full error: {:#})", e, e); + Err(StatusCode::INTERNAL_SERVER_ERROR) + } + } } \ No newline at end of file From f7d6eeac6fe5d0d9c4a7c9c86543f6add403106c Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 16:21:26 +0000 Subject: [PATCH 03/11] fix(unit-tests): add missing fields to unit tests --- src/test_helpers.rs | 6 +++++- src/test_utils.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/test_helpers.rs b/src/test_helpers.rs index 4da27b2..3b96c69 100644 --- a/src/test_helpers.rs +++ b/src/test_helpers.rs @@ -204,7 +204,11 @@ pub fn create_test_config() -> Config { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, - + oidc_auto_register: true, + + // Authentication Configuration + allow_local_auth: true, + // S3 Configuration (disabled for tests by default) s3_enabled: false, s3_config: None, diff --git a/src/test_utils.rs b/src/test_utils.rs index 5935b63..b377b30 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -835,7 +835,11 @@ impl TestConfigBuilder { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, - + oidc_auto_register: true, + + // Authentication Configuration + allow_local_auth: true, + // S3 Configuration s3_enabled: false, s3_config: None, From bb3291155da3da63fdeba860695e7459c9d47182 Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 17:45:49 +0000 Subject: [PATCH 04/11] fix(unit-tests): add missing fields to OIDC unit tests --- tests/unit_oidc_unit_tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit_oidc_unit_tests.rs b/tests/unit_oidc_unit_tests.rs index b8c81b6..62e566e 100644 --- a/tests/unit_oidc_unit_tests.rs +++ b/tests/unit_oidc_unit_tests.rs @@ -26,6 +26,8 @@ fn create_test_config_with_oidc(issuer_url: &str) -> Config { oidc_client_secret: Some("test-client-secret".to_string()), oidc_issuer_url: Some(issuer_url.to_string()), oidc_redirect_uri: Some("http://localhost:8000/auth/oidc/callback".to_string()), + oidc_auto_register: true, + allow_local_auth: true, s3_enabled: false, s3_config: None, } From e367f4908adfcdd98b46d231fbdd843b1239b961 Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 17:51:21 +0000 Subject: [PATCH 05/11] fix(unit-tests): add missing fields to OTHER oidc unit test --- tests/integration_oidc_tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/integration_oidc_tests.rs b/tests/integration_oidc_tests.rs index 9578794..84622a6 100644 --- a/tests/integration_oidc_tests.rs +++ b/tests/integration_oidc_tests.rs @@ -130,6 +130,8 @@ mod tests { oidc_client_secret: Some("test-client-secret".to_string()), oidc_issuer_url: Some(mock_server.uri()), oidc_redirect_uri: Some("http://localhost:8000/auth/oidc/callback".to_string()), + oidc_auto_register: true, + allow_local_auth: true, s3_enabled: false, s3_config: None, }; @@ -447,4 +449,4 @@ mod tests { assert_eq!(response.status(), StatusCode::UNAUTHORIZED); } -} \ No newline at end of file +} From 1a7e6d7fa91f61aa673c8974bc92cb8bf459d8ad Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 19:07:18 +0000 Subject: [PATCH 06/11] refactor(config): make oidc_auto_register and allow_local_auth optional --- src/config.rs | 37 +++++++++++++++++++++---------------- src/routes/auth.rs | 8 ++++---- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/config.rs b/src/config.rs index 3689f6b..266fdcc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -33,10 +33,10 @@ pub struct Config { pub oidc_client_secret: Option, pub oidc_issuer_url: Option, pub oidc_redirect_uri: Option, - pub oidc_auto_register: bool, + pub oidc_auto_register: Option, // Authentication Configuration - pub allow_local_auth: bool, + pub allow_local_auth: Option, // S3 Configuration pub s3_enabled: bool, @@ -417,16 +417,20 @@ impl Config { Ok(val) => match val.to_lowercase().as_str() { "true" | "1" | "yes" | "on" => { println!("✅ OIDC_AUTO_REGISTER: true (loaded from env)"); - true + Some(true) + } + "false" | "0" | "no" | "off" => { + println!("✅ OIDC_AUTO_REGISTER: false (loaded from env)"); + Some(false) } _ => { - println!("✅ OIDC_AUTO_REGISTER: false (loaded from env)"); - false + println!("⚠️ OIDC_AUTO_REGISTER: Invalid value '{}', using default (false)", val); + None } }, Err(_) => { - println!("⚠️ OIDC_AUTO_REGISTER: true (using default - env var not set)"); - true // Default to true for convenience + println!("⚠️ OIDC_AUTO_REGISTER: Not set, will use default (false)"); + None } }, @@ -435,20 +439,20 @@ impl Config { Ok(val) => match val.to_lowercase().as_str() { "true" | "1" | "yes" | "on" => { println!("✅ ALLOW_LOCAL_AUTH: true (loaded from env)"); - true + Some(true) } "false" | "0" | "no" | "off" => { println!("✅ ALLOW_LOCAL_AUTH: false (loaded from env)"); - false + Some(false) } _ => { - println!("⚠️ ALLOW_LOCAL_AUTH: Invalid value '{}', defaulting to true", val); - true + println!("⚠️ ALLOW_LOCAL_AUTH: Invalid value '{}', using default (true)", val); + None } }, Err(_) => { - println!("⚠️ ALLOW_LOCAL_AUTH: true (using default - env var not set)"); - true // Default to true for backward compatibility + println!("⚠️ ALLOW_LOCAL_AUTH: Not set, will use default (true)"); + None } }, @@ -565,7 +569,7 @@ impl Config { // OIDC validation if config.oidc_enabled { println!("🔐 OIDC is enabled"); - println!("🔓 OIDC auto-registration: {}", config.oidc_auto_register); + println!("🔓 OIDC auto-registration: {}", config.oidc_auto_register.unwrap_or(false)); if config.oidc_client_id.is_none() { println!("❌ OIDC_CLIENT_ID is required when OIDC is enabled"); } @@ -583,10 +587,11 @@ impl Config { } // Authentication method validation + let allow_local_auth = config.allow_local_auth.unwrap_or(true); println!("🔑 Local authentication (username/password): {}", - if config.allow_local_auth { "enabled" } else { "disabled" }); + if allow_local_auth { "enabled" } else { "disabled" }); - if !config.oidc_enabled && !config.allow_local_auth { + if !config.oidc_enabled && !allow_local_auth { println!("❌ WARNING: Both OIDC and local authentication are disabled!"); println!(" You will not be able to log in. Enable at least one authentication method."); return Err(anyhow::anyhow!( diff --git a/src/routes/auth.rs b/src/routes/auth.rs index f050c63..9f41e84 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -41,7 +41,7 @@ async fn register( Json(user_data): Json, ) -> Response { // Check if local authentication is enabled - if !state.config.allow_local_auth { + if !state.config.allow_local_auth.unwrap_or(true) { tracing::warn!("Local registration attempt rejected - local auth is disabled"); return ( StatusCode::FORBIDDEN, @@ -98,7 +98,7 @@ async fn login( Json(login_data): Json, ) -> Result, StatusCode> { // Check if local authentication is enabled - if !state.config.allow_local_auth { + if !state.config.allow_local_auth.unwrap_or(true) { tracing::warn!("Local authentication attempt rejected - local auth is disabled"); return Err(StatusCode::FORBIDDEN); } @@ -268,7 +268,7 @@ async fn oidc_callback( }, Ok(None) => { // No existing user with this email - if state.config.oidc_auto_register { + if state.config.oidc_auto_register.unwrap_or(false) { // Auto-registration is enabled, create new OIDC user tracing::debug!("No existing user with this email, creating new OIDC user (auto-registration enabled)"); create_new_oidc_user( @@ -293,7 +293,7 @@ async fn oidc_callback( } } else { // No email provided by OIDC provider - if state.config.oidc_auto_register { + if state.config.oidc_auto_register.unwrap_or(false) { // Auto-registration is enabled, create new user without email sync tracing::debug!("No email provided by OIDC, creating new user (auto-registration enabled)"); create_new_oidc_user( From 64a66b987fb93b588a3c64a451feae38c7dd81cd Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 19:30:17 +0000 Subject: [PATCH 07/11] fix(tests): make those optional too --- src/test_helpers.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test_helpers.rs b/src/test_helpers.rs index 3b96c69..d642b59 100644 --- a/src/test_helpers.rs +++ b/src/test_helpers.rs @@ -204,10 +204,8 @@ pub fn create_test_config() -> Config { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, - oidc_auto_register: true, - - // Authentication Configuration - allow_local_auth: true, + oidc_auto_register: None, + allow_local_auth: None, // S3 Configuration (disabled for tests by default) s3_enabled: false, From de785f77457e82e3db1616b9edec6657498499dd Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 20:31:02 +0000 Subject: [PATCH 08/11] fix(tests): so... many... --- src/test_utils.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test_utils.rs b/src/test_utils.rs index b377b30..ef4b7a1 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -835,10 +835,8 @@ impl TestConfigBuilder { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, - oidc_auto_register: true, - - // Authentication Configuration - allow_local_auth: true, + oidc_auto_register: None, + allow_local_auth: None, // S3 Configuration s3_enabled: false, From 7b2f6c174dda883d7112395bda3eb4adc8737f40 Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 21:02:49 +0000 Subject: [PATCH 09/11] fix(tests): add new values to all unit tests that need it --- tests/integration_config_oidc_tests.rs | 2 ++ tests/integration_document_upload_hash_duplicate_tests.rs | 2 ++ tests/integration_ignored_files_integration_tests.rs | 2 ++ tests/integration_oidc_tests.rs | 2 ++ tests/integration_source_scheduler_simple_tests.rs | 2 ++ tests/integration_source_scheduler_tests.rs | 2 ++ tests/integration_source_sync_cancellation_workflow_tests.rs | 2 ++ tests/integration_source_sync_hash_duplicate_tests.rs | 2 ++ tests/integration_stop_sync_functionality_tests.rs | 2 ++ tests/integration_universal_source_sync_tests.rs | 2 ++ tests/integration_webdav_comprehensive_tests.rs | 2 ++ tests/integration_webdav_hash_duplicate_tests.rs | 2 ++ tests/integration_webdav_integration_tests.rs | 2 ++ 13 files changed, 26 insertions(+) diff --git a/tests/integration_config_oidc_tests.rs b/tests/integration_config_oidc_tests.rs index c85ce27..42cee1b 100644 --- a/tests/integration_config_oidc_tests.rs +++ b/tests/integration_config_oidc_tests.rs @@ -70,6 +70,8 @@ mod tests { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, } diff --git a/tests/integration_document_upload_hash_duplicate_tests.rs b/tests/integration_document_upload_hash_duplicate_tests.rs index 9e7971c..fa892b9 100644 --- a/tests/integration_document_upload_hash_duplicate_tests.rs +++ b/tests/integration_document_upload_hash_duplicate_tests.rs @@ -96,6 +96,8 @@ async fn create_test_app_state() -> Result> { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, } diff --git a/tests/integration_ignored_files_integration_tests.rs b/tests/integration_ignored_files_integration_tests.rs index 9b59607..cb5efaa 100644 --- a/tests/integration_ignored_files_integration_tests.rs +++ b/tests/integration_ignored_files_integration_tests.rs @@ -38,6 +38,8 @@ async fn create_test_app_state() -> Result> { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, } diff --git a/tests/integration_oidc_tests.rs b/tests/integration_oidc_tests.rs index 84622a6..05553ba 100644 --- a/tests/integration_oidc_tests.rs +++ b/tests/integration_oidc_tests.rs @@ -37,6 +37,8 @@ mod tests { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; diff --git a/tests/integration_source_scheduler_simple_tests.rs b/tests/integration_source_scheduler_simple_tests.rs index 676dea7..9e7d835 100644 --- a/tests/integration_source_scheduler_simple_tests.rs +++ b/tests/integration_source_scheduler_simple_tests.rs @@ -46,6 +46,8 @@ async fn create_test_app_state() -> Arc { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; diff --git a/tests/integration_source_scheduler_tests.rs b/tests/integration_source_scheduler_tests.rs index 1585ca7..5770783 100644 --- a/tests/integration_source_scheduler_tests.rs +++ b/tests/integration_source_scheduler_tests.rs @@ -190,6 +190,8 @@ async fn create_test_app_state() -> Arc { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; diff --git a/tests/integration_source_sync_cancellation_workflow_tests.rs b/tests/integration_source_sync_cancellation_workflow_tests.rs index f17638d..694127f 100644 --- a/tests/integration_source_sync_cancellation_workflow_tests.rs +++ b/tests/integration_source_sync_cancellation_workflow_tests.rs @@ -60,6 +60,8 @@ async fn create_test_app_state() -> Arc { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; diff --git a/tests/integration_source_sync_hash_duplicate_tests.rs b/tests/integration_source_sync_hash_duplicate_tests.rs index d75e33d..2bd610e 100644 --- a/tests/integration_source_sync_hash_duplicate_tests.rs +++ b/tests/integration_source_sync_hash_duplicate_tests.rs @@ -143,6 +143,8 @@ async fn create_test_app_state() -> Result> { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, } diff --git a/tests/integration_stop_sync_functionality_tests.rs b/tests/integration_stop_sync_functionality_tests.rs index 2661616..181538f 100644 --- a/tests/integration_stop_sync_functionality_tests.rs +++ b/tests/integration_stop_sync_functionality_tests.rs @@ -53,6 +53,8 @@ async fn create_test_app_state() -> Arc { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; diff --git a/tests/integration_universal_source_sync_tests.rs b/tests/integration_universal_source_sync_tests.rs index 8b41ac8..f681dbb 100644 --- a/tests/integration_universal_source_sync_tests.rs +++ b/tests/integration_universal_source_sync_tests.rs @@ -154,6 +154,8 @@ async fn create_test_app_state() -> Arc { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; diff --git a/tests/integration_webdav_comprehensive_tests.rs b/tests/integration_webdav_comprehensive_tests.rs index ddea9af..0d80efe 100644 --- a/tests/integration_webdav_comprehensive_tests.rs +++ b/tests/integration_webdav_comprehensive_tests.rs @@ -348,6 +348,8 @@ fn test_webdav_scheduler_creation() { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; diff --git a/tests/integration_webdav_hash_duplicate_tests.rs b/tests/integration_webdav_hash_duplicate_tests.rs index e091543..918ad5e 100644 --- a/tests/integration_webdav_hash_duplicate_tests.rs +++ b/tests/integration_webdav_hash_duplicate_tests.rs @@ -143,6 +143,8 @@ async fn create_test_app_state() -> Result> { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, } diff --git a/tests/integration_webdav_integration_tests.rs b/tests/integration_webdav_integration_tests.rs index afc8149..693bca7 100644 --- a/tests/integration_webdav_integration_tests.rs +++ b/tests/integration_webdav_integration_tests.rs @@ -104,6 +104,8 @@ async fn setup_test_app() -> (Router, Arc) { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, }; From 7a07bdc38b62c0e7610f082d293e1d9708c92907 Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 21:47:30 +0000 Subject: [PATCH 10/11] fix(tests): make sure those are used as optional --- tests/integration_oidc_tests.rs | 4 ++-- tests/unit_oidc_unit_tests.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration_oidc_tests.rs b/tests/integration_oidc_tests.rs index 05553ba..e26d323 100644 --- a/tests/integration_oidc_tests.rs +++ b/tests/integration_oidc_tests.rs @@ -132,8 +132,8 @@ mod tests { oidc_client_secret: Some("test-client-secret".to_string()), oidc_issuer_url: Some(mock_server.uri()), oidc_redirect_uri: Some("http://localhost:8000/auth/oidc/callback".to_string()), - oidc_auto_register: true, - allow_local_auth: true, + oidc_auto_register: Some(true), + allow_local_auth: Some(true), s3_enabled: false, s3_config: None, }; diff --git a/tests/unit_oidc_unit_tests.rs b/tests/unit_oidc_unit_tests.rs index 62e566e..0d5d25b 100644 --- a/tests/unit_oidc_unit_tests.rs +++ b/tests/unit_oidc_unit_tests.rs @@ -26,8 +26,8 @@ fn create_test_config_with_oidc(issuer_url: &str) -> Config { oidc_client_secret: Some("test-client-secret".to_string()), oidc_issuer_url: Some(issuer_url.to_string()), oidc_redirect_uri: Some("http://localhost:8000/auth/oidc/callback".to_string()), - oidc_auto_register: true, - allow_local_auth: true, + oidc_auto_register: Some(true), + allow_local_auth: Some(true), s3_enabled: false, s3_config: None, } From a92c028b7457b7ba1ddc824b32d743270449db6a Mon Sep 17 00:00:00 2001 From: aaldebs99 Date: Sat, 11 Oct 2025 22:19:54 +0000 Subject: [PATCH 11/11] fix(tests): SURELY this is the last of it --- tests/integration_cancellation_tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration_cancellation_tests.rs b/tests/integration_cancellation_tests.rs index 83dc540..860086c 100644 --- a/tests/integration_cancellation_tests.rs +++ b/tests/integration_cancellation_tests.rs @@ -52,6 +52,8 @@ async fn create_test_app_state() -> Arc { oidc_client_secret: None, oidc_issuer_url: None, oidc_redirect_uri: None, + oidc_auto_register: None, + allow_local_auth: None, s3_enabled: false, s3_config: None, };