56 lines
1.5 KiB
Ruby
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
|