Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ gem 'autoprefixer-rails', '~> 10.0'
gem 'aws-sdk-kms', '~> 1.4'
gem 'aws-sdk-lambda'
gem 'aws-sdk-ses', '~> 1.6'
gem 'aws-sdk-sqs'
gem 'base32-crockford'
gem 'device_detector'
gem 'devise', '~> 4.7.2'
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ GEM
aws-sdk-ses (1.36.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sdk-sqs (1.35.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sdk-ssm (1.103.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
Expand Down Expand Up @@ -738,6 +741,7 @@ DEPENDENCIES
aws-sdk-kms (~> 1.4)
aws-sdk-lambda
aws-sdk-ses (~> 1.6)
aws-sdk-sqs
axe-matchers (~> 2.6.0)
base32-crockford
better_errors (>= 2.5.1)
Expand Down
31 changes: 31 additions & 0 deletions app/services/push_notification/http_push.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ def url_options
attr_reader :now

def deliver_one(service_provider)
if AppConfig.env.risc_notifications_sqs_enabled == 'true'
deliver_sqs(service_provider)
else
deliver_direct(service_provider)
end
end

def deliver_sqs(service_provider)
sqs_client.send_message(
queue_url: sqs_queue_url,
message_body: {
push_notification_url: service_provider.push_notification_url,
jwt: jwt(service_provider),
}.to_json,
)
end

def deliver_direct(service_provider)
response = faraday.post(
service_provider.push_notification_url,
jwt(service_provider),
Expand Down Expand Up @@ -83,5 +101,18 @@ def agency_uuid(service_provider)
service_provider: service_provider.issuer,
)&.uuid
end

def sqs_client
@sqs_client ||= Aws::SQS::Client.new(
region: Identity::Hostdata::EC2.load.region,
)
end

def sqs_queue_url
@sqs_queue_url ||=
sqs_client.get_queue_url(
queue_name: "#{Identity::Hostdata.env}-risc-notifications",
).queue_url
end
end
end
1 change: 1 addition & 0 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ remember_device_expiration_hours_aal_1: '720'
remember_device_expiration_hours_aal_2: '12'
report_timeout:
requests_per_ip_track_only_mode: 'false'
risc_notifications_sqs_enabled: 'false'
saml_secret_rotation_enabled:
service_provider_request_ttl_hours: '24'
session_check_delay: '30'
Expand Down
125 changes: 109 additions & 16 deletions spec/services/push_notification/http_push_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require 'spec_helper'
require 'rails_helper'

RSpec.describe PushNotification::HttpPush do
include Rails.application.routes.url_helpers
Expand All @@ -22,20 +22,73 @@
)
end
let(:now) { Time.zone.now }
let(:risc_notifications_sqs_enabled) { 'false' }
let(:sqs_client) { Aws::SQS::Client.new(stub_responses: true) }
let(:sqs_queue_url) { 'https://some-queue-url.example.com/example' }

subject(:http_push) { PushNotification::HttpPush.new(event, now: now) }

before do
allow(AppConfig.env).to receive(:risc_notifications_sqs_enabled).
and_return(risc_notifications_sqs_enabled)
allow(Identity::Hostdata).to receive(:env).and_return('dev')

allow(http_push).to receive(:sqs_client).and_return(sqs_client)

sqs_client.stub_responses(
:get_queue_url,
{ queue_url: sqs_queue_url },
)
end

describe '#deliver' do
subject(:deliver) { http_push.deliver }

it 'makes an HTTP post to service providers with a push_notification_url' do
stub_request(:post, sp_with_push_url.push_notification_url).
with do |request|
expect(request.headers['Content-Type']).to eq('application/secevent+jwt')
expect(request.headers['Accept']).to eq('application/json')
context 'when the SQS queue is disabled' do
let(:risc_notifications_sqs_enabled) { 'false' }

it 'makes an HTTP post to service providers with a push_notification_url' do
stub_request(:post, sp_with_push_url.push_notification_url).
with do |request|
expect(request.headers['Content-Type']).to eq('application/secevent+jwt')
expect(request.headers['Accept']).to eq('application/json')

payload, headers = JWT.decode(
request.body,
AppArtifacts.store.oidc_public_key,
true,
algorithm: 'RS256',
)

expect(headers['typ']).to eq('secevent+jwt')

expect(payload['iss']).to eq(root_url)
expect(payload['iat']).to eq(now.to_i)
expect(payload['exp']).to eq((now + 12.hours).to_i)
expect(payload['aud']).to eq(sp_with_push_url.push_notification_url)
expect(payload['events']).to eq(event.event_type => event.payload.as_json)
end

deliver
end
end

context 'when the SQS queue is enabled' do
let(:risc_notifications_sqs_enabled) { 'true' }

it 'posts to the SQS queue' do
expect(sqs_client).to receive(:get_queue_url).
with(queue_name: 'dev-risc-notifications').
and_call_original

expect(sqs_client).to receive(:send_message) do |queue_url:, message_body:|
expect(queue_url).to eq(sqs_queue_url)

message = JSON.parse(message_body, symbolize_names: true)
expect(message[:push_notification_url]).to eq(sp_with_push_url.push_notification_url)

payload, headers = JWT.decode(
request.body,
message[:jwt],
AppArtifacts.store.oidc_public_key,
true,
algorithm: 'RS256',
Expand All @@ -50,19 +103,44 @@
expect(payload['events']).to eq(event.event_type => event.payload.as_json)
end

deliver
deliver
end
end

context 'with an event that sends agency-specific iss_sub' do
let(:event) { PushNotification::AccountPurgedEvent.new(user: user) }

let(:agency_uuid) { AgencyIdentityLinker.new(sp_with_push_url_identity).link_identity.uuid }

it 'sends the agency-specific uuid' do
stub_request(:post, sp_with_push_url.push_notification_url).
with do |request|
context 'when the SQS queue is disabled' do
let(:risc_notifications_sqs_enabled) { 'false' }

it 'sends the agency-specific uuid' do
stub_request(:post, sp_with_push_url.push_notification_url).
with do |request|
payload, _headers = JWT.decode(
request.body,
AppArtifacts.store.oidc_public_key,
true,
algorithm: 'RS256',
)

expect(payload['events'][event.event_type]['subject']['sub']).to eq(agency_uuid)
end

deliver
end
end

context 'when the SQS queue is enabled' do
let(:risc_notifications_sqs_enabled) { 'true' }

it 'sends the agency-specific uuid' do
expect(sqs_client).to receive(:send_message) do |queue_url:, message_body:|
message = JSON.parse(message_body, symbolize_names: true)

payload, _headers = JWT.decode(
request.body,
message[:jwt],
AppArtifacts.store.oidc_public_key,
true,
algorithm: 'RS256',
Expand All @@ -71,7 +149,8 @@
expect(payload['events'][event.event_type]['subject']['sub']).to eq(agency_uuid)
end

deliver
deliver
end
end
end

Expand Down Expand Up @@ -119,10 +198,24 @@
RevokeServiceProviderConsent.new(identity).call
end

it 'does not notify that SP' do
deliver
context 'when the SQS queue is disabled' do
let(:risc_notifications_sqs_enabled) { 'false' }

it 'does not notify that SP' do
deliver

expect(WebMock).not_to have_requested(:get, sp_with_push_url.push_notification_url)
expect(WebMock).not_to have_requested(:get, sp_with_push_url.push_notification_url)
end
end

context 'when the SQS queue is enabled' do
let(:risc_notifications_sqs_enabled) { 'true' }

it 'does not notify that SP' do
expect(sqs_client).to_not receive(:send_message)

deliver
end
end
end
end
Expand Down