First commit!

This commit is contained in:
Dan Milne
2025-11-03 17:37:28 +11:00
commit 429d41eead
141 changed files with 5890 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
# frozen_string_literal: true
class DsnAuthenticationService
class AuthenticationError < StandardError; end
def self.authenticate(request, project_id)
# Try multiple authentication methods in order of preference
# Method 1: Query parameter authentication
public_key = extract_key_from_query_params(request)
return find_project(public_key, project_id) if public_key
# Method 2: X-Baffle-Auth header (similar to X-Sentry-Auth)
public_key = extract_key_from_baffle_auth_header(request)
return find_project(public_key, project_id) if public_key
# Method 3: Authorization Bearer token
public_key = extract_key_from_authorization_header(request)
return find_project(public_key, project_id) if public_key
# Method 4: Basic auth (username is the public_key)
public_key = extract_key_from_basic_auth(request)
return find_project(public_key, project_id) if public_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=public_key, baffle_version=1
# Or: Sentry sentry_key=public_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 public_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_project(public_key, project_id)
return nil unless public_key.present? && project_id.present?
# Find project by public_key first
project = Project.find_by(public_key: public_key)
raise AuthenticationError, "Invalid public_key" unless project
# Verify project_id matches (supports both slug and ID)
project_matches = Project.find_by_project_id(project_id)
raise AuthenticationError, "Invalid project_id" unless project_matches == project
# Ensure project is enabled
raise AuthenticationError, "Project is disabled" unless project.enabled?
project
end
end