feat: autogenerate vapid keys for push notifications (#3128)
* feat: Autogenerate push notification keys * add vapid service class and remove pushkey model * add spec for vapid service * unset vapid env keys * Unset VAPID_PRIVATE_KEY env variable Co-authored-by: Sojan Jose <sojan@chatwoot.com> Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>
This commit is contained in:
parent
0c9b682329
commit
6cfd7d3836
7 changed files with 86 additions and 8 deletions
|
@ -28,6 +28,7 @@ class DashboardController < ActionController::Base
|
|||
'ANALYTICS_HOST'
|
||||
).merge(
|
||||
APP_VERSION: Chatwoot.config[:version],
|
||||
VAPID_PUBLIC_KEY: VapidService.public_key,
|
||||
ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false')
|
||||
)
|
||||
end
|
||||
|
|
|
@ -34,5 +34,4 @@ describe('campaignMixin', () => {
|
|||
const wrapper = shallowMount(Component);
|
||||
expect(wrapper.vm.isOnOffType).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import events from 'widget/api/events';
|
|||
|
||||
const state = {
|
||||
isOpen: false,
|
||||
}
|
||||
};
|
||||
|
||||
const actions = {
|
||||
create: async (_, { name }) => {
|
||||
|
@ -17,7 +17,7 @@ const actions = {
|
|||
const mutations = {
|
||||
toggleOpen($state) {
|
||||
$state.isOpen = !$state.isOpen;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -43,7 +43,7 @@ class Notification::PushNotificationService
|
|||
end
|
||||
|
||||
def send_browser_push?(subscription)
|
||||
ENV['VAPID_PUBLIC_KEY'] && subscription.browser_push?
|
||||
VapidService.public_key && subscription.browser_push?
|
||||
end
|
||||
|
||||
def send_browser_push(subscription)
|
||||
|
@ -56,8 +56,8 @@ class Notification::PushNotificationService
|
|||
auth: subscription.subscription_attributes['auth'],
|
||||
vapid: {
|
||||
subject: push_url,
|
||||
public_key: ENV['VAPID_PUBLIC_KEY'],
|
||||
private_key: ENV['VAPID_PRIVATE_KEY']
|
||||
public_key: VapidService.public_key,
|
||||
private_key: VapidService.private_key
|
||||
},
|
||||
ssl_timeout: 5,
|
||||
open_timeout: 5,
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
hostURL: '<%= ENV.fetch('FRONTEND_URL', '') %>',
|
||||
fbAppId: '<%= ENV.fetch('FB_APP_ID', nil) %>',
|
||||
signupEnabled: '<%= @global_config['ENABLE_ACCOUNT_SIGNUP'] %>',
|
||||
<% if ENV['VAPID_PUBLIC_KEY'] %>
|
||||
vapidPublicKey: new Uint8Array(<%= Base64.urlsafe_decode64(ENV['VAPID_PUBLIC_KEY']).bytes %>),
|
||||
<% if @global_config['VAPID_PUBLIC_KEY'] %>
|
||||
vapidPublicKey: new Uint8Array(<%= Base64.urlsafe_decode64(@global_config['VAPID_PUBLIC_KEY']).bytes %>),
|
||||
<% end %>
|
||||
enabledLanguages: <%= available_locales_with_name.to_json.html_safe %>,
|
||||
selectedLocale: '<%= I18n.locale %>'
|
||||
|
|
25
lib/vapid_service.rb
Normal file
25
lib/vapid_service.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
class VapidService
|
||||
def self.public_key
|
||||
vapid_keys['public_key']
|
||||
end
|
||||
|
||||
def self.private_key
|
||||
vapid_keys['private_key']
|
||||
end
|
||||
|
||||
def self.vapid_keys
|
||||
config = GlobalConfig.get('VAPID_KEYS')
|
||||
return config['VAPID_KEYS'] if config['VAPID_KEYS'].present?
|
||||
|
||||
# keys don't exist in the database. so let's generate and save them
|
||||
keys = Webpush.generate_key
|
||||
# TODO: remove the logic on environment variables when we completely deprecate
|
||||
public_key = ENV['VAPID_PUBLIC_KEY'] || keys.public_key
|
||||
private_key = ENV['VAPID_PRIVATE_KEY'] || keys.private_key
|
||||
|
||||
i = InstallationConfig.where(name: 'VAPID_KEYS').first_or_create(value: { public_key: public_key, private_key: private_key })
|
||||
i.value
|
||||
end
|
||||
|
||||
private_class_method :vapid_keys
|
||||
end
|
53
spec/lib/vapid_service_spec.rb
Normal file
53
spec/lib/vapid_service_spec.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe VapidService do
|
||||
subject(:trigger) { described_class }
|
||||
|
||||
describe 'execute' do
|
||||
context 'when called with default options' do
|
||||
before do
|
||||
GlobalConfig.clear_cache
|
||||
end
|
||||
|
||||
it 'hit DB for the first call' do
|
||||
expect(InstallationConfig).to receive(:find_by)
|
||||
GlobalConfig.get('VAPID_KEYS')
|
||||
end
|
||||
|
||||
it 'get public key from env' do
|
||||
# this gets public key
|
||||
ENV['VAPID_PUBLIC_KEY'] = 'test'
|
||||
described_class.public_key
|
||||
|
||||
# subsequent calls should not hit DB
|
||||
expect(InstallationConfig).not_to receive(:find_by)
|
||||
described_class.public_key
|
||||
ENV['VAPID_PUBLIC_KEY'] = nil
|
||||
end
|
||||
|
||||
it 'get private key from env' do
|
||||
# this gets private key
|
||||
ENV['VAPID_PRIVATE_KEY'] = 'test'
|
||||
described_class.private_key
|
||||
|
||||
# subsequent calls should not hit DB
|
||||
expect(InstallationConfig).not_to receive(:find_by)
|
||||
described_class.private_key
|
||||
ENV['VAPID_PRIVATE_KEY'] = nil
|
||||
ENV['VAPID_PRIVATE_KEY'] = nil
|
||||
end
|
||||
|
||||
it 'clears cache and fetch from DB next time, when clear_cache is called' do
|
||||
# this loads from DB and is cached
|
||||
GlobalConfig.get('VAPID_KEYS')
|
||||
|
||||
# clears the cache
|
||||
GlobalConfig.clear_cache
|
||||
|
||||
# should be loaded from DB
|
||||
expect(InstallationConfig).to receive(:find_by).with({ name: 'VAPID_KEYS' }).and_return(nil)
|
||||
GlobalConfig.get('VAPID_KEYS')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue