78 lines
2.4 KiB
Ruby
78 lines
2.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class DsnAuthenticationService
|
|
class AuthenticationError < StandardError; end
|
|
|
|
def self.authenticate(request)
|
|
# Try multiple authentication methods in order of preference
|
|
|
|
# Method 1: Query parameter authentication
|
|
dsn_key = extract_key_from_query_params(request)
|
|
return find_dsn(dsn_key) if dsn_key
|
|
|
|
# Method 2: X-Baffle-Auth header (similar to X-Sentry-Auth)
|
|
dsn_key = extract_key_from_baffle_auth_header(request)
|
|
return find_dsn(dsn_key) if dsn_key
|
|
|
|
# Method 3: Authorization Bearer token
|
|
dsn_key = extract_key_from_authorization_header(request)
|
|
return find_dsn(dsn_key) if dsn_key
|
|
|
|
# Method 4: Basic auth (username is the dsn_key)
|
|
dsn_key = extract_key_from_basic_auth(request)
|
|
return find_dsn(dsn_key) if dsn_key
|
|
|
|
raise AuthenticationError, "No valid authentication method found"
|
|
end
|
|
|
|
private
|
|
|
|
def self.extract_key_from_query_params(request)
|
|
# Support both baffle_key and sentry_key for compatibility
|
|
request.GET['baffle_key'] || request.GET['sentry_key'] || request.GET['glitchtip_key']
|
|
end
|
|
|
|
def self.extract_key_from_baffle_auth_header(request)
|
|
auth_header = request.headers['X-Baffle-Auth'] || request.headers['X-Sentry-Auth']
|
|
return nil unless auth_header
|
|
|
|
# Parse: Baffle baffle_key=dsn_key, baffle_version=1
|
|
# Or: Sentry sentry_key=dsn_key, sentry_version=7
|
|
match = auth_header.match(/(?:baffle_key|sentry_key)=([^,\s]+)/)
|
|
match&.[](1)
|
|
end
|
|
|
|
def self.extract_key_from_authorization_header(request)
|
|
authorization_header = request.headers['Authorization']
|
|
return nil unless authorization_header
|
|
|
|
# Parse: Bearer dsn_key
|
|
if authorization_header.start_with?('Bearer ')
|
|
authorization_header[7..-1].strip
|
|
end
|
|
end
|
|
|
|
def self.extract_key_from_basic_auth(request)
|
|
authorization_header = request.headers['Authorization']
|
|
return nil unless authorization_header&.start_with?('Basic ')
|
|
|
|
# Decode basic auth: username:password (password is ignored)
|
|
credentials = Base64.decode64(authorization_header[6..-1])
|
|
username = credentials.split(':').first
|
|
username
|
|
end
|
|
|
|
def self.find_dsn(dsn_key)
|
|
return nil unless dsn_key.present?
|
|
|
|
# Find DSN by key
|
|
dsn = Dsn.authenticate(dsn_key)
|
|
raise AuthenticationError, "Invalid DSN key" unless dsn
|
|
|
|
# Ensure DSN is enabled
|
|
raise AuthenticationError, "DSN is disabled" unless dsn.enabled?
|
|
|
|
dsn
|
|
end
|
|
end
|