Fix add validation to webpush subscription keys (#30542)
parent
5f15a892fa
commit
4655be0da6
|
@ -25,6 +25,8 @@ class Web::PushSubscription < ApplicationRecord
|
||||||
validates :key_p256dh, presence: true
|
validates :key_p256dh, presence: true
|
||||||
validates :key_auth, presence: true
|
validates :key_auth, presence: true
|
||||||
|
|
||||||
|
validates_with WebPushKeyValidator
|
||||||
|
|
||||||
delegate :locale, to: :associated_user
|
delegate :locale, to: :associated_user
|
||||||
|
|
||||||
def encrypt(payload)
|
def encrypt(payload)
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class WebPushKeyValidator < ActiveModel::Validator
|
||||||
|
def validate(subscription)
|
||||||
|
begin
|
||||||
|
Webpush::Encryption.encrypt('validation_test', subscription.key_p256dh, subscription.key_auth)
|
||||||
|
rescue ArgumentError, OpenSSL::PKey::EC::Point::Error
|
||||||
|
subscription.errors.add(:base, I18n.t('crypto.errors.invalid_key'))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
Fabricator(:web_push_subscription, from: Web::PushSubscription) do
|
Fabricator(:web_push_subscription, from: Web::PushSubscription) do
|
||||||
endpoint Faker::Internet.url
|
endpoint Faker::Internet.url
|
||||||
key_p256dh Faker::Internet.password
|
key_p256dh do
|
||||||
key_auth Faker::Internet.password
|
curve = OpenSSL::PKey::EC.generate('prime256v1')
|
||||||
|
ecdh_key = curve.public_key.to_bn.to_s(2)
|
||||||
|
Base64.urlsafe_encode64(ecdh_key)
|
||||||
|
end
|
||||||
|
key_auth { Base64.urlsafe_encode64(Random.new.bytes(16)) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,14 +5,17 @@ require 'rails_helper'
|
||||||
describe 'API V1 Push Subscriptions' do
|
describe 'API V1 Push Subscriptions' do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:endpoint) { 'https://fcm.googleapis.com/fcm/send/fiuH06a27qE:APA91bHnSiGcLwdaxdyqVXNDR9w1NlztsHb6lyt5WDKOC_Z_Q8BlFxQoR8tWFSXUIDdkyw0EdvxTu63iqamSaqVSevW5LfoFwojws8XYDXv_NRRLH6vo2CdgiN4jgHv5VLt2A8ah6lUX' }
|
let(:endpoint) { 'https://fcm.googleapis.com/fcm/send/fiuH06a27qE:APA91bHnSiGcLwdaxdyqVXNDR9w1NlztsHb6lyt5WDKOC_Z_Q8BlFxQoR8tWFSXUIDdkyw0EdvxTu63iqamSaqVSevW5LfoFwojws8XYDXv_NRRLH6vo2CdgiN4jgHv5VLt2A8ah6lUX' }
|
||||||
|
let(:keys) do
|
||||||
|
{
|
||||||
|
p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
|
||||||
|
auth: 'eH_C8rq2raXqlcBVDa1gLg==',
|
||||||
|
}
|
||||||
|
end
|
||||||
let(:create_payload) do
|
let(:create_payload) do
|
||||||
{
|
{
|
||||||
subscription: {
|
subscription: {
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
keys: {
|
keys: keys,
|
||||||
p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
|
|
||||||
auth: 'eH_C8rq2raXqlcBVDa1gLg==',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}.with_indifferent_access
|
}.with_indifferent_access
|
||||||
end
|
end
|
||||||
|
@ -37,6 +40,16 @@ describe 'API V1 Push Subscriptions' do
|
||||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||||
|
|
||||||
|
shared_examples 'validation error' do
|
||||||
|
it 'returns a validation error' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status(422)
|
||||||
|
expect(endpoint_push_subscriptions.count).to eq(0)
|
||||||
|
expect(endpoint_push_subscription).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'POST /api/v1/push/subscription' do
|
describe 'POST /api/v1/push/subscription' do
|
||||||
subject { post '/api/v1/push/subscription', params: create_payload, headers: headers }
|
subject { post '/api/v1/push/subscription', params: create_payload, headers: headers }
|
||||||
|
|
||||||
|
@ -68,13 +81,29 @@ describe 'API V1 Push Subscriptions' do
|
||||||
context 'with invalid endpoint URL' do
|
context 'with invalid endpoint URL' do
|
||||||
let(:endpoint) { 'app://example.foo' }
|
let(:endpoint) { 'app://example.foo' }
|
||||||
|
|
||||||
it 'returns a validation error' do
|
it_behaves_like 'validation error'
|
||||||
subject
|
end
|
||||||
|
|
||||||
expect(response).to have_http_status(422)
|
context 'with invalid p256dh key' do
|
||||||
expect(endpoint_push_subscriptions.count).to eq(0)
|
let(:keys) do
|
||||||
expect(endpoint_push_subscription).to be_nil
|
{
|
||||||
|
p256dh: 'BEm_invalidf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
|
||||||
|
auth: 'eH_C8rq2raXqlcBVDa1gLg==',
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'validation error'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid base64 p256dh key' do
|
||||||
|
let(:keys) do
|
||||||
|
{
|
||||||
|
p256dh: 'not base64',
|
||||||
|
auth: 'eH_C8rq2raXqlcBVDa1gLg==',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'validation error'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue