-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1745 from 18F/margolis-translate-voice-otp
Set language in Voice OTP TwiML
- Loading branch information
Showing
17 changed files
with
278 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
module Voice | ||
class OtpController < ApplicationController | ||
NUMBER_OF_TIMES_USER_CAN_REPEAT_CODE = 5 | ||
|
||
skip_before_action :verify_authenticity_token | ||
|
||
def show | ||
if code.blank? | ||
render nothing: true, status: :bad_request | ||
return | ||
end | ||
|
||
@message = message | ||
@action_url = action_url | ||
end | ||
|
||
protected | ||
|
||
def encrypted_code | ||
params[:encrypted_code].to_s | ||
end | ||
|
||
def code | ||
return unless encrypted_code.present? | ||
|
||
cipher.decrypt(encrypted_code) | ||
end | ||
|
||
def message | ||
t('voice.otp.message', code: code_with_pauses) | ||
end | ||
|
||
def code_with_pauses | ||
code.scan(/\d/).join(', ') | ||
end | ||
|
||
def repeat_count | ||
(params[:repeat_count].presence || NUMBER_OF_TIMES_USER_CAN_REPEAT_CODE).to_i | ||
end | ||
|
||
def action_url | ||
return if repeat_count <= 1 | ||
|
||
BasicAuthUrl.build( | ||
voice_otp_url( | ||
encrypted_code: encrypted_code, | ||
repeat_count: repeat_count - 1 | ||
) | ||
) | ||
end | ||
|
||
def cipher | ||
Gibberish::AES.new(Figaro.env.attribute_encryption_key) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module BasicAuthUrl | ||
module_function | ||
|
||
def build(url, user: Figaro.env.basic_auth_user_name, password: Figaro.env.basic_auth_password) | ||
URI.parse(url).tap do |uri| | ||
uri.user = user | ||
uri.password = password | ||
end.to_s | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
doctype xml | ||
Response | ||
Say language="#{I18n.locale}" | ||
= @message | ||
- if @action_url | ||
Gather numDigits="1" action=@action_url | ||
Say = t('voice.otp.repeat_instructions') | ||
Hangup / |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
en: | ||
voice: | ||
otp: | ||
message: Hello! Your login.gov one time passcode is, %{code}, again, your passcode | ||
is, %{code}. | ||
repeat_instructions: Press 1 to repeat this message. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
es: | ||
voice: | ||
otp: | ||
message: "¡Hola! Su código de acceso de login.gov es, %{code}, nuevamente, su | ||
código de acceso es %{code}." | ||
repeat_instructions: Presione 1 para repetir este mensaje. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
fr: | ||
voice: | ||
otp: | ||
message: Bonjour! Votre code de sécurité à utilisation unique de login.gov est, | ||
%{code}, de nouveau, votre code de sécurité est, %{code}, au revoir! | ||
repeat_instructions: Appuyez sur 1 pour répéter votre code. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
require 'rails_helper' | ||
|
||
RSpec.describe Voice::OtpController do | ||
describe '#show' do | ||
subject(:action) do | ||
get :show, | ||
params: { encrypted_code: encrypted_code, repeat_count: repeat_count, locale: locale }, | ||
format: :xml | ||
end | ||
let(:locale) { nil } | ||
let(:repeat_count) { nil } | ||
let(:cipher) { Gibberish::AES.new(Figaro.env.attribute_encryption_key) } | ||
|
||
context 'with a blank encrypted_code in the URL' do | ||
let(:encrypted_code) { '' } | ||
|
||
it 'renders a blank 400' do | ||
action | ||
|
||
expect(response).to be_bad_request | ||
expect(response.body).to be_empty | ||
end | ||
end | ||
|
||
context 'with an encrypted_code in the URL' do | ||
render_views | ||
|
||
let(:code) { '1234' } | ||
let(:encrypted_code) { cipher.encrypt(code) } | ||
|
||
it 'tells Twilio to <Say> the code with pauses in between' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
say = doc.css('Say').first | ||
expect(say.text).to include('1, 2, 3, 4,') | ||
end | ||
|
||
it 'sets the lang attribute to english' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
say = doc.css('Say').first | ||
|
||
expect(say[:language]).to eq('en') | ||
end | ||
|
||
context 'when the locale is in spanish' do | ||
let(:locale) { :es } | ||
|
||
it 'sets the lang attribute to english' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
say = doc.css('Say').first | ||
|
||
expect(say[:language]).to eq('es') | ||
end | ||
|
||
it 'passes locale into the <Gather> action URL' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
gather = doc.css('Gather').first | ||
|
||
params = URIService.params(gather[:action]) | ||
expect(params[:locale]).to eq('es') | ||
end | ||
end | ||
|
||
context 'when the locale is in french' do | ||
let(:locale) { :fr } | ||
|
||
it 'sets the lang attribute to english' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
say = doc.css('Say').first | ||
|
||
expect(say[:language]).to eq('fr') | ||
end | ||
|
||
it 'passes locale into the <Gather> action URL' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
gather = doc.css('Gather').first | ||
|
||
params = URIService.params(gather[:action]) | ||
expect(params[:locale]).to eq('fr') | ||
end | ||
end | ||
|
||
it 'has a <Gather> with instructions to repeat with a repeat_count' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
gather = doc.css('Gather').first | ||
|
||
expect(gather[:action]).to include('repeat_count=4') | ||
end | ||
|
||
it 'puts the encrypted code in the <Gather> action' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
gather = doc.css('Gather').first | ||
params = URIService.params(gather[:action]) | ||
|
||
expect(cipher.decrypt(params[:encrypted_code])).to eq(code) | ||
end | ||
|
||
context 'when repeat_count counts down to 1' do | ||
let(:repeat_count) { 1 } | ||
|
||
it 'does not have a <Gather> in the response' do | ||
action | ||
|
||
doc = Nokogiri::XML(response.body) | ||
expect(doc.css('Gather')).to be_empty | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.