modules/two_factor_authentication/lib/open_project/two_factor_authentication/token_strategy/sns.rb
require "net/http"
require "aws-sdk-sns"
module OpenProject::TwoFactorAuthentication
module TokenStrategy
class Sns < Base
cattr_accessor :service_params
def self.validate!
super
validate_params
end
def self.identifier
:sns
end
def self.mobile_token?
true
end
def self.supported_channels
[:sms]
end
def self.validate_params
%w[access_key_id secret_access_key region].each do |key|
unless configuration_params[key]
raise ArgumentError, "Amazon SNS delivery settings is missing mandatory key :#{key}"
end
end
end
private
def send_sms
Rails.logger.info { "[2FA] SNS delivery sending SMS request for #{user.login}" }
submit
end
##
# Prepares the request for the given user and token
def build_sms_params
{
phone_number: build_user_phone,
message: build_token_text(token)
}
end
def build_token_text(token)
I18n.t("two_factor_authentication.text_otp_delivery_message_sms", app_title: Setting.app_title, token:)
end
##
# Prepares the user's phone number for commcit.
# Required format: +xxaaabbbccc
# Stored format: +xx yyy yyy yyyy (optional whitespacing)
def build_user_phone
phone = device.phone_number
phone.gsub!(/\s/, "")
phone
end
# rubocop:disable Metrics/AbcSize
def submit
aws_params = configuration_params.slice "region", "access_key_id", "secret_access_key"
sns = ::Aws::SNS::Client.new aws_params
sns.set_sms_attributes(
attributes: {
# Use transactional message type to ensure timely delivery.
# Amazon SNS optimizes the message delivery to achieve the highest reliability.
"DefaultSMSType" => "Transactional",
# Set sender ID name (may not be supported in all countries)
"DefaultSenderID" => configuration_params.fetch("sender_id", "OpenProject")
}
)
result = sns.publish(build_sms_params)
# If successful, SNS returns an object with a message id
message_id = result.try :message_id
if message_id.present?
Rails.logger.info { "[2FA] SNS delivery succeeded for user #{user.login}: #{message_id}" }
return
end
raise result
rescue StandardError => e
Rails.logger.error do
"[2FA] SNS delivery failed for user #{user.login} (Error: #{e})"
end
raise I18n.t("two_factor_authentication.sns.delivery_failed")
end
# rubocop:enable Metrics/AbcSize
end
end
end