Refactor email delivery and background jobs system
- Switch from SolidQueue to async job processor for simpler background job handling - Remove SolidQueue gem and related configuration files - Add letter_opener gem for development email preview - Fix invitation email template issues (invitation_login_token method and route helper) - Configure SMTP settings via environment variables in application.rb - Add email delivery configuration banner on admin users page - Improve admin users page with inline action buttons and SMTP configuration warnings - Update development and production environments to use async processor - Add helper methods to detect SMTP configuration and filter out localhost settings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
275
test/controllers/api/forward_auth_controller_test.rb
Normal file
275
test/controllers/api/forward_auth_controller_test.rb
Normal file
@@ -0,0 +1,275 @@
|
||||
require "test_helper"
|
||||
|
||||
module Api
|
||||
class ForwardAuthControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user = users(:one)
|
||||
@admin_user = users(:two)
|
||||
@inactive_user = users(:three)
|
||||
@group = groups(:one)
|
||||
@rule = ForwardAuthRule.create!(domain_pattern: "test.example.com", active: true)
|
||||
@inactive_rule = ForwardAuthRule.create!(domain_pattern: "inactive.example.com", active: false)
|
||||
end
|
||||
|
||||
# Authentication Tests
|
||||
test "should redirect to login when no session cookie" do
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 302
|
||||
assert_match %r{/signin}, response.location
|
||||
assert_equal "No session cookie", response.headers["X-Auth-Reason"]
|
||||
end
|
||||
|
||||
test "should redirect when session cookie is invalid" do
|
||||
get "/api/verify", headers: {
|
||||
"X-Forwarded-Host" => "test.example.com",
|
||||
"Cookie" => "_clinch_session_id=invalid_session_id"
|
||||
}
|
||||
|
||||
assert_response 302
|
||||
assert_match %r{/signin}, response.location
|
||||
assert_equal "Invalid session", response.headers["X-Auth-Reason"]
|
||||
end
|
||||
|
||||
test "should redirect when session is expired" do
|
||||
expired_session = @user.sessions.create!(created_at: 1.year.ago)
|
||||
|
||||
get "/api/verify", headers: {
|
||||
"X-Forwarded-Host" => "test.example.com",
|
||||
"Cookie" => "_clinch_session_id=#{expired_session.id}"
|
||||
}
|
||||
|
||||
assert_response 302
|
||||
assert_match %r{/signin}, response.location
|
||||
assert_equal "Session expired", response.headers["X-Auth-Reason"]
|
||||
end
|
||||
|
||||
test "should redirect when user is inactive" do
|
||||
sign_in_as(@inactive_user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 302
|
||||
assert_equal "User account is not active", response.headers["X-Auth-Reason"]
|
||||
end
|
||||
|
||||
test "should return 200 when user is authenticated" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
end
|
||||
|
||||
# Rule Matching Tests
|
||||
test "should return 200 when matching rule exists" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
end
|
||||
|
||||
test "should return 200 with default headers when no rule matches" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "unknown.example.com" }
|
||||
|
||||
assert_response 200
|
||||
assert_equal "X-Remote-User", response.headers["X-Remote-User"]
|
||||
assert_equal @user.email_address, response.headers["X-Remote-User"]
|
||||
end
|
||||
|
||||
test "should return 403 when rule exists but is inactive" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "inactive.example.com" }
|
||||
|
||||
assert_response 403
|
||||
assert_equal "No authentication rule configured for this domain", response.headers["X-Auth-Reason"]
|
||||
end
|
||||
|
||||
test "should return 403 when rule exists but user not in allowed groups" do
|
||||
@rule.allowed_groups << @group
|
||||
sign_in_as(@user) # User not in group
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 403
|
||||
assert_match %r{permission to access this domain}, response.headers["X-Auth-Reason"]
|
||||
end
|
||||
|
||||
test "should return 200 when user is in allowed groups" do
|
||||
@rule.allowed_groups << @group
|
||||
@user.groups << @group
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
end
|
||||
|
||||
# Domain Pattern Tests
|
||||
test "should match wildcard domains correctly" do
|
||||
wildcard_rule = ForwardAuthRule.create!(domain_pattern: "*.example.com", active: true)
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "app.example.com" }
|
||||
assert_response 200
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "api.example.com" }
|
||||
assert_response 200
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "other.com" }
|
||||
assert_response 200 # Falls back to default behavior
|
||||
end
|
||||
|
||||
test "should match exact domains correctly" do
|
||||
exact_rule = ForwardAuthRule.create!(domain_pattern: "api.example.com", active: true)
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "api.example.com" }
|
||||
assert_response 200
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "app.api.example.com" }
|
||||
assert_response 200 # Falls back to default behavior
|
||||
end
|
||||
|
||||
# Header Configuration Tests
|
||||
test "should return default headers when rule has no custom config" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
assert_equal "X-Remote-User", response.headers.keys.find { |k| k.include?("User") }
|
||||
assert_equal "X-Remote-Email", response.headers.keys.find { |k| k.include?("Email") }
|
||||
assert_equal "X-Remote-Name", response.headers.keys.find { |k| k.include?("Name") }
|
||||
assert_equal @user.email_address, response.headers["X-Remote-User"]
|
||||
end
|
||||
|
||||
test "should return custom headers when configured" do
|
||||
custom_rule = ForwardAuthRule.create!(
|
||||
domain_pattern: "custom.example.com",
|
||||
active: true,
|
||||
headers_config: {
|
||||
user: "X-WEBAUTH-USER",
|
||||
email: "X-WEBAUTH-EMAIL",
|
||||
groups: "X-WEBAUTH-ROLES"
|
||||
}
|
||||
)
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "custom.example.com" }
|
||||
|
||||
assert_response 200
|
||||
assert_equal "X-WEBAUTH-USER", response.headers.keys.find { |k| k.include?("USER") }
|
||||
assert_equal "X-WEBAUTH-EMAIL", response.headers.keys.find { |k| k.include?("EMAIL") }
|
||||
assert_equal @user.email_address, response.headers["X-WEBAUTH-USER"]
|
||||
end
|
||||
|
||||
test "should return no headers when all headers disabled" do
|
||||
no_headers_rule = ForwardAuthRule.create!(
|
||||
domain_pattern: "noheaders.example.com",
|
||||
active: true,
|
||||
headers_config: { user: "", email: "", name: "", groups: "", admin: "" }
|
||||
)
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "noheaders.example.com" }
|
||||
|
||||
assert_response 200
|
||||
auth_headers = response.headers.select { |k, v| k.match?(/^(X-|Remote-)/i) }
|
||||
assert_empty auth_headers
|
||||
end
|
||||
|
||||
test "should include groups header when user has groups" do
|
||||
@user.groups << @group
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
assert_equal @group.name, response.headers["X-Remote-Groups"]
|
||||
end
|
||||
|
||||
test "should not include groups header when user has no groups" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
assert_nil response.headers["X-Remote-Groups"]
|
||||
end
|
||||
|
||||
test "should include admin header correctly" do
|
||||
sign_in_as(@admin_user) # Assuming users(:two) is admin
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
assert_equal "true", response.headers["X-Remote-Admin"]
|
||||
end
|
||||
|
||||
test "should include multiple groups when user has multiple groups" do
|
||||
group2 = groups(:two)
|
||||
@user.groups << @group
|
||||
@user.groups << group2
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
groups_header = response.headers["X-Remote-Groups"]
|
||||
assert_includes groups_header, @group.name
|
||||
assert_includes groups_header, group2.name
|
||||
end
|
||||
|
||||
# Header Fallback Tests
|
||||
test "should fall back to Host header when X-Forwarded-Host is missing" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "Host" => "test.example.com" }
|
||||
|
||||
assert_response 200
|
||||
end
|
||||
|
||||
test "should handle requests without any host headers" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify"
|
||||
|
||||
assert_response 200
|
||||
assert_equal "User #{@user.email_address} authenticated (no domain specified)",
|
||||
request.env["action_dispatch.instance"].instance_variable_get(:@logged_messages)&.last
|
||||
end
|
||||
|
||||
# Security Tests
|
||||
test "should handle malformed session IDs gracefully" do
|
||||
get "/api/verify", headers: {
|
||||
"X-Forwarded-Host" => "test.example.com",
|
||||
"Cookie" => "_clinch_session_id=malformed_session_id_with_special_chars!@#$%"
|
||||
}
|
||||
|
||||
assert_response 302
|
||||
assert_equal "Invalid session", response.headers["X-Auth-Reason"]
|
||||
end
|
||||
|
||||
test "should handle very long domain names" do
|
||||
long_domain = "a" * 250 + ".example.com"
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => long_domain }
|
||||
|
||||
assert_response 200 # Should fall back to default behavior
|
||||
end
|
||||
|
||||
test "should handle case insensitive domain matching" do
|
||||
sign_in_as(@user)
|
||||
|
||||
get "/api/verify", headers: { "X-Forwarded-Host" => "TEST.Example.COM" }
|
||||
|
||||
assert_response 200
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user