Files
fizzy/app/models/push/subscription.rb
T
2025-12-09 20:24:09 -05:00

56 lines
1.5 KiB
Ruby

class Push::Subscription < ApplicationRecord
PERMITTED_ENDPOINT_HOSTS = %w[
jmt17.google.com
fcm.googleapis.com
updates.push.services.mozilla.com
web.push.apple.com
notify.windows.com
].freeze
belongs_to :account, default: -> { user.account }
belongs_to :user
validates :endpoint, presence: true
validate :validate_endpoint_url
def notification(**params)
WebPush::Notification.new(
**params,
badge: user.notifications.unread.count,
endpoint: endpoint,
endpoint_ip: resolved_endpoint_ip,
p256dh_key: p256dh_key,
auth_key: auth_key
)
end
def resolved_endpoint_ip
return @resolved_endpoint_ip if defined?(@resolved_endpoint_ip)
@resolved_endpoint_ip = SsrfProtection.resolve_public_ip(endpoint_uri&.host)
end
private
def endpoint_uri
@endpoint_uri ||= URI.parse(endpoint) if endpoint.present?
rescue URI::InvalidURIError
nil
end
def validate_endpoint_url
if endpoint_uri.nil?
errors.add(:endpoint, "is not a valid URL")
elsif endpoint_uri.scheme != "https"
errors.add(:endpoint, "must use HTTPS")
elsif !permitted_endpoint_host?
errors.add(:endpoint, "is not a permitted push service")
elsif resolved_endpoint_ip.nil?
errors.add(:endpoint, "resolves to a private or invalid IP address")
end
end
def permitted_endpoint_host?
host = endpoint_uri&.host&.downcase
PERMITTED_ENDPOINT_HOSTS.any? { |permitted| host&.end_with?(permitted) }
end
end