diff --git a/Makefile b/Makefile index ee1c06d68c3..c3e18730b32 100644 --- a/Makefile +++ b/Makefile @@ -177,7 +177,7 @@ brakeman: ## Runs brakeman code security check (bundle exec brakeman) || (echo "Error: update code as needed to remove security issues. For known exceptions already in brakeman.ignore, use brakeman to interactively update exceptions."; exit 1) public/packs/manifest.json: yarn.lock $(shell find app/javascript -type f) ## Builds JavaScript assets - yarn build + yarn build:js browsers.json: yarn.lock .browserslistrc ## Generates browsers.json browser support file yarn generate-browsers-json diff --git a/app/components/block_link_component.rb b/app/components/block_link_component.rb index 9878ee58641..34f25d68654 100644 --- a/app/components/block_link_component.rb +++ b/app/components/block_link_component.rb @@ -1,11 +1,11 @@ class BlockLinkComponent < BaseComponent - attr_reader :url, :action, :new_tab, :tag_options + attr_reader :url, :action, :new_tab, :tag_options, :component alias_method :new_tab?, :new_tab - def initialize(url:, action: tag.method(:a), new_tab: false, **tag_options) - @action = action + def initialize(url: '#', component: nil, new_tab: false, **tag_options) @url = url + @component = component @new_tab = new_tab @tag_options = tag_options end @@ -21,11 +21,11 @@ def target end def wrapper(&block) - wrapper = action.call(**tag_options, href: url, class: css_class, target:, &block) - if wrapper.respond_to?(:render_in) - render wrapper, &block + if component + render component.new(href: url, class: css_class), &block else - wrapper + action = tag.method(:a) + action.call(**tag_options, href: url, class: css_class, target:, &block) end end end diff --git a/app/components/form_link_component.html.erb b/app/components/form_link_component.html.erb new file mode 100644 index 00000000000..3e72713c616 --- /dev/null +++ b/app/components/form_link_component.html.erb @@ -0,0 +1,3 @@ + + <%= link_to('#', **tag_options) { content } %> + diff --git a/app/components/form_link_component.rb b/app/components/form_link_component.rb new file mode 100644 index 00000000000..a3ba4823fd3 --- /dev/null +++ b/app/components/form_link_component.rb @@ -0,0 +1,7 @@ +class FormLinkComponent < BaseComponent + attr_reader :tag_options + + def initialize(**tag_options) + @tag_options = tag_options + end +end diff --git a/app/components/form_link_component.ts b/app/components/form_link_component.ts new file mode 100644 index 00000000000..5c47ccb4c63 --- /dev/null +++ b/app/components/form_link_component.ts @@ -0,0 +1 @@ +import '@18f/identity-form-link/form-link-element'; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 23e80e0b8b6..43c77f0d1d8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -105,11 +105,17 @@ def sign_out(*args) end def resolved_authn_context_result - @resolved_authn_context_result ||= AuthnContextResolver.new( - service_provider: current_sp, - vtr: sp_session[:vtr], - acr_values: sp_session[:acr_values], - ).resolve + return @resolved_authn_context_result if defined?(@resolved_authn_context_result) + + if current_sp.nil? + @resolved_authn_context_result = Vot::Parser::Result.no_sp_result + else + @resolved_authn_context_result = AuthnContextResolver.new( + service_provider: current_sp, + vtr: sp_session[:vtr], + acr_values: sp_session[:acr_values], + ).resolve + end end def context diff --git a/app/controllers/concerns/idv/document_capture_concern.rb b/app/controllers/concerns/idv/document_capture_concern.rb index 7c45dc786e9..c09714d7eba 100644 --- a/app/controllers/concerns/idv/document_capture_concern.rb +++ b/app/controllers/concerns/idv/document_capture_concern.rb @@ -29,18 +29,16 @@ def failure(message, extra = nil) FormResponse.new(**form_response_params) end - # @param [DocAuth::Response, - # DocumentCaptureSessionResult] response - def extract_pii_from_doc(user, response, store_in_session: false) + def extract_pii_from_doc(user, store_in_session: false) if defined?(idv_session) # hybrid mobile does not have idv_session - idv_session.had_barcode_read_failure = response.attention_with_barcode? + idv_session.had_barcode_read_failure = stored_result.attention_with_barcode? if store_in_session - idv_session.pii_from_doc = response.pii_from_doc - idv_session.selfie_check_performed = response.selfie_check_performed? + idv_session.pii_from_doc = stored_result.pii_from_doc + idv_session.selfie_check_performed = stored_result.selfie_check_performed? end end - track_document_issuing_state(user, response.pii_from_doc[:state]) + track_document_issuing_state(user, stored_result.pii_from_doc[:state]) end def stored_result diff --git a/app/controllers/frontend_log_controller.rb b/app/controllers/frontend_log_controller.rb index 66434ae7212..1517a09d20e 100644 --- a/app/controllers/frontend_log_controller.rb +++ b/app/controllers/frontend_log_controller.rb @@ -56,13 +56,12 @@ class FrontendLogController < ApplicationController EVENT_MAP = ALLOWED_EVENTS.index_by(&:to_s).merge(LEGACY_EVENT_MAP).freeze def create - result = frontend_logger.track_event(log_params[:event], log_params[:payload].to_h) + success = frontend_logger.track_event(log_params[:event], log_params[:payload].to_h) - if result - render json: { success: true }, status: :ok + if success + render json: { success: }, status: :ok else - render json: { success: false, error_message: 'invalid event' }, - status: :bad_request + render json: { success:, error_message: 'invalid event' }, status: :bad_request end end diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 0611a352902..e29ff969f11 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -91,7 +91,7 @@ def analytics_arguments def handle_stored_result if stored_result&.success? && selfie_requirement_met? save_proofing_components(current_user) - extract_pii_from_doc(current_user, stored_result, store_in_session: true) + extract_pii_from_doc(current_user, store_in_session: true) flash[:success] = t('doc_auth.headings.capture_complete') successful_response else diff --git a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb index d31d675c30f..b554e376e0e 100644 --- a/app/controllers/idv/hybrid_mobile/document_capture_controller.rb +++ b/app/controllers/idv/hybrid_mobile/document_capture_controller.rb @@ -63,7 +63,7 @@ def analytics_arguments def handle_stored_result if stored_result&.success? && selfie_requirement_met? save_proofing_components(document_capture_user) - extract_pii_from_doc(document_capture_user, stored_result) + extract_pii_from_doc(document_capture_user) successful_response else extra = { stored_result_present: stored_result.present? } diff --git a/app/controllers/idv/link_sent_controller.rb b/app/controllers/idv/link_sent_controller.rb index 282fdfff6fc..c99e007ac44 100644 --- a/app/controllers/idv/link_sent_controller.rb +++ b/app/controllers/idv/link_sent_controller.rb @@ -26,7 +26,7 @@ def update # The doc capture flow will have fetched the results already. We need # to fetch them again here to add the PII to this session - handle_document_verification_success(document_capture_session_result) + handle_document_verification_success idv_session.redo_document_capture = nil redirect_to idv_ssn_url @@ -63,9 +63,9 @@ def analytics_arguments }.merge(ab_test_analytics_buckets) end - def handle_document_verification_success(get_results_response) + def handle_document_verification_success save_proofing_components(current_user) - extract_pii_from_doc(current_user, get_results_response, store_in_session: true) + extract_pii_from_doc(current_user, store_in_session: true) idv_session.flow_path = 'hybrid' end @@ -80,12 +80,7 @@ def render_step_incomplete_error end def take_photo_with_phone_successful? - document_capture_session_result.present? && document_capture_session_result.success? && - selfie_requirement_met? - end - - def document_capture_session_result - @document_capture_session_result ||= document_capture_session&.load_result + stored_result&.success? && selfie_requirement_met? end end end diff --git a/app/controllers/users/delete_controller.rb b/app/controllers/users/delete_controller.rb index f3d9152c951..ddb024c8ead 100644 --- a/app/controllers/users/delete_controller.rb +++ b/app/controllers/users/delete_controller.rb @@ -14,6 +14,7 @@ def delete irs_attempts_api_tracker.logged_in_account_purged(success: true) send_push_notifications notify_user_via_email_of_deletion + notify_user_via_sms_of_deletion delete_user sign_out flash[:success] = t('devise.registrations.destroyed') @@ -60,5 +61,16 @@ def notify_user_via_email_of_deletion end end # rubocop:enable IdentityIdp/MailLaterLinter + + def notify_user_via_sms_of_deletion + phone_configurations = current_user.phone_configurations + phone_configurations.each do |configuration| + next unless configuration.capabilities.supports_sms? + Telephony.send_account_deleted_notice( + to: configuration.phone, + country_code: Phonelib.parse(configuration.phone).country, + ) + end + end end end diff --git a/app/forms/openid_connect_logout_form.rb b/app/forms/openid_connect_logout_form.rb index fc71d2ee6d3..0995ec9efb2 100644 --- a/app/forms/openid_connect_logout_form.rb +++ b/app/forms/openid_connect_logout_form.rb @@ -17,12 +17,12 @@ class OpenidConnectLogoutForm validates :client_id, presence: { - message: I18n.t('openid_connect.logout.errors.client_id_missing'), + message: ->(_, _) { I18n.t('openid_connect.logout.errors.client_id_missing') }, }, if: :reject_id_token_hint? validates :id_token_hint, absence: { - message: I18n.t('openid_connect.logout.errors.id_token_hint_present'), + message: ->(_, _) { I18n.t('openid_connect.logout.errors.id_token_hint_present') }, }, if: :reject_id_token_hint? validates :post_logout_redirect_uri, presence: true diff --git a/app/javascript/packages/analytics/is-trackable-error-event.spec.ts b/app/javascript/packages/analytics/is-trackable-error-event.spec.ts index 7b7b53ec5cb..3612b4e39cc 100644 --- a/app/javascript/packages/analytics/is-trackable-error-event.spec.ts +++ b/app/javascript/packages/analytics/is-trackable-error-event.spec.ts @@ -25,6 +25,16 @@ describe('isTrackableErrorEvent', () => { }); }); + context('with non-javascript filename', () => { + const event = new ErrorEvent('error', { + filename: new URL('foo', window.location.origin).toString(), + }); + + it('returns false', () => { + expect(isTrackableErrorEvent(event)).to.be.false(); + }); + }); + context('with filename from the same host', () => { const event = new ErrorEvent('error', { filename: new URL('foo.js', window.location.origin).toString(), diff --git a/app/javascript/packages/analytics/is-trackable-error-event.ts b/app/javascript/packages/analytics/is-trackable-error-event.ts index b5e10cb519e..859f766b870 100644 --- a/app/javascript/packages/analytics/is-trackable-error-event.ts +++ b/app/javascript/packages/analytics/is-trackable-error-event.ts @@ -1,6 +1,7 @@ function isTrackableErrorEvent(event: ErrorEvent): boolean { try { - return new URL(event.filename).host === window.location.host; + const { host, pathname } = new URL(event.filename); + return host === window.location.host && pathname.endsWith('.js'); } catch { return false; } diff --git a/app/javascript/packages/form-link/README.md b/app/javascript/packages/form-link/README.md new file mode 100644 index 00000000000..9bd81495941 --- /dev/null +++ b/app/javascript/packages/form-link/README.md @@ -0,0 +1,22 @@ +# `@18f/identity-form-link` + +Custom element for links which submit as a form, supporting non-GET navigation. + +## Usage + +### Custom Element + +Importing the element will register the `` custom element: + +```ts +import '@18f/identity-form-link/form-link-element'; +``` + +The custom element will implement the link submission behavior, but all markup must already exist. + +```html + + Submit + + +``` diff --git a/app/javascript/packages/form-link/form-link-element.spec.ts b/app/javascript/packages/form-link/form-link-element.spec.ts new file mode 100644 index 00000000000..d3b8bcbf350 --- /dev/null +++ b/app/javascript/packages/form-link/form-link-element.spec.ts @@ -0,0 +1,28 @@ +import sinon from 'sinon'; +import { getByRole, fireEvent } from '@testing-library/dom'; +import './form-link-element'; + +describe('FormLinkElement', () => { + function createElement() { + document.body.innerHTML = ` + + Submit + + + `; + + return document.body.querySelector('lg-form-link')!; + } + + it('submits form on link click', () => { + const element = createElement(); + const link = getByRole(element, 'link'); + + const onSubmit = sinon.stub(); + element.form.submit = onSubmit; + const didPreventDefault = !fireEvent.click(link); + + expect(onSubmit).to.have.been.called(); + expect(didPreventDefault).to.be.true(); + }); +}); diff --git a/app/javascript/packages/form-link/form-link-element.ts b/app/javascript/packages/form-link/form-link-element.ts new file mode 100644 index 00000000000..0e59ab55697 --- /dev/null +++ b/app/javascript/packages/form-link/form-link-element.ts @@ -0,0 +1,30 @@ +class FormLinkElement extends HTMLElement { + connectedCallback() { + this.link.addEventListener('click', this.submit); + } + + get form(): HTMLFormElement { + return this.querySelector('form')!; + } + + get link(): HTMLAnchorElement { + return this.querySelector('a')!; + } + + submit = (event: MouseEvent) => { + event.preventDefault(); + this.form.submit(); + }; +} + +declare global { + interface HTMLElementTagNameMap { + 'lg-form-link': FormLinkElement; + } +} + +if (!customElements.get('lg-form-link')) { + customElements.define('lg-form-link', FormLinkElement); +} + +export default FormLinkElement; diff --git a/app/javascript/packages/form-link/package.json b/app/javascript/packages/form-link/package.json new file mode 100644 index 00000000000..fe327bd7db0 --- /dev/null +++ b/app/javascript/packages/form-link/package.json @@ -0,0 +1,8 @@ +{ + "name": "@18f/identity-form-link", + "version": "1.0.0", + "private": true, + "sideEffects": [ + "./form-link-element.ts" + ] +} diff --git a/app/javascript/packages/phone-input/package.json b/app/javascript/packages/phone-input/package.json index 0ee0ba81331..893a9b9fe43 100644 --- a/app/javascript/packages/phone-input/package.json +++ b/app/javascript/packages/phone-input/package.json @@ -4,7 +4,7 @@ "version": "1.0.0", "dependencies": { "intl-tel-input": "^17.0.19", - "libphonenumber-js": "^1.10.55" + "libphonenumber-js": "^1.10.56" }, "sideEffects": [ "./index.ts" diff --git a/app/models/monthly_auth_count.rb b/app/models/monthly_auth_count.rb deleted file mode 100644 index 0790a0111fe..00000000000 --- a/app/models/monthly_auth_count.rb +++ /dev/null @@ -1,2 +0,0 @@ -class MonthlyAuthCount < ApplicationRecord -end diff --git a/app/models/phone_configuration.rb b/app/models/phone_configuration.rb index 43c2e5ccb22..7aec13dd78e 100644 --- a/app/models/phone_configuration.rb +++ b/app/models/phone_configuration.rb @@ -19,8 +19,6 @@ def masked_phone def selection_presenters options = [] - capabilities = PhoneNumberCapabilities.new(phone, phone_confirmed: !!confirmed_at?) - if capabilities.supports_sms? options << TwoFactorAuthentication::SignInPhoneSelectionPresenter. new(user:, configuration: self, delivery_method: :sms) @@ -34,6 +32,10 @@ def selection_presenters options end + def capabilities + PhoneNumberCapabilities.new(phone, phone_confirmed: !!confirmed_at?) + end + def friendly_name :phone end diff --git a/app/presenters/image_upload_response_presenter.rb b/app/presenters/image_upload_response_presenter.rb index e6476312323..b524a1f43fe 100644 --- a/app/presenters/image_upload_response_presenter.rb +++ b/app/presenters/image_upload_response_presenter.rb @@ -78,6 +78,7 @@ def attention_with_barcode? end def ocr_pii + return unless success? return unless attention_with_barcode? && @form_response.respond_to?(:pii_from_doc) @form_response.pii_from_doc&.slice(:first_name, :last_name, :dob) end diff --git a/app/services/frontend_logger.rb b/app/services/frontend_logger.rb index 9ec2d53af4d..3b0ec281906 100644 --- a/app/services/frontend_logger.rb +++ b/app/services/frontend_logger.rb @@ -25,8 +25,6 @@ def track_event(name, attributes) analytics_method.call(**hash_from_kwargs(attributes, analytics_method)) true else - # 2023-10-31 - Temporary - analytics.track_event("Frontend (warning): #{name}", attributes) false end end diff --git a/app/services/reporting/total_user_count_report.rb b/app/services/reporting/total_user_count_report.rb index c88c75e7af5..d7a875ad60c 100644 --- a/app/services/reporting/total_user_count_report.rb +++ b/app/services/reporting/total_user_count_report.rb @@ -12,6 +12,7 @@ def total_user_count_report [ ['Metric', 'All Users', 'Verified users', 'Time Range Start', 'Time Range End'], ['All-time count', total_user_count, verified_user_count, '-', report_date.to_date], + ['All-time fully registered', total_fully_registered, '-', '-', report_date.to_date], [ 'New users count', new_user_count, @@ -45,6 +46,12 @@ def new_user_count end end + def total_fully_registered + Reports::BaseReport.transaction_with_timeout do + RegistrationLog.where('registered_at <= ?', end_date).where.not(registered_at: nil).count + end + end + def total_user_count Reports::BaseReport.transaction_with_timeout do User.where('created_at <= ?', end_date).count diff --git a/app/services/vot/parser.rb b/app/services/vot/parser.rb index 1ac2ed9e8b0..5f9eeffde03 100644 --- a/app/services/vot/parser.rb +++ b/app/services/vot/parser.rb @@ -9,7 +9,19 @@ class ParseException < StandardError; end :identity_proofing?, :biometric_comparison?, :ialmax?, - ) + ) do + def self.no_sp_result + self.new( + component_values: [], + aal2?: false, + phishing_resistant?: false, + hspd12?: false, + identity_proofing?: false, + biometric_comparison?: false, + ialmax?: false, + ) + end + end attr_reader :vector_of_trust, :acr_values diff --git a/app/validators/idv/form_address_validator.rb b/app/validators/idv/form_address_validator.rb index 2754def1921..16158a64889 100644 --- a/app/validators/idv/form_address_validator.rb +++ b/app/validators/idv/form_address_validator.rb @@ -7,7 +7,7 @@ module FormAddressValidator validates_format_of :zipcode, with: /\A\d{5}(-?\d{4})?\z/, - message: I18n.t('idv.errors.pattern_mismatch.zipcode'), + message: ->(_, _) { I18n.t('idv.errors.pattern_mismatch.zipcode') }, allow_blank: true validates :city, presence: true, length: { maximum: 255 } diff --git a/app/validators/idv/form_ssn_format_validator.rb b/app/validators/idv/form_ssn_format_validator.rb index 75349cfd840..4ec8935d2d8 100644 --- a/app/validators/idv/form_ssn_format_validator.rb +++ b/app/validators/idv/form_ssn_format_validator.rb @@ -6,7 +6,7 @@ module FormSsnFormatValidator validates :ssn, presence: true validates_format_of :ssn, with: /\A\d{3}-?\d{2}-?\d{4}\z/, - message: I18n.t('idv.errors.pattern_mismatch.ssn'), + message: ->(_, _) { I18n.t('idv.errors.pattern_mismatch.ssn') }, allow_blank: false end end diff --git a/app/validators/idv/form_state_id_validator.rb b/app/validators/idv/form_state_id_validator.rb index 62dbe02e3e1..389eb10f61e 100644 --- a/app/validators/idv/form_state_id_validator.rb +++ b/app/validators/idv/form_state_id_validator.rb @@ -37,10 +37,12 @@ module FormStateIdValidator attributes: [:dob], less_than_or_equal_to: ->(_rec) { Time.zone.today - IdentityConfig.store.idv_min_age_years.years }, - message: I18n.t( - 'in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.range_min_age', - app_name: APP_NAME, - ) + message: ->(_, _) do + I18n.t( + 'in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.range_min_age', + app_name: APP_NAME, + ) + end # rubocop:enable Layout/LineLength end # rubocop:enable Metrics/BlockLength diff --git a/app/views/sign_up/emails/show.html.erb b/app/views/sign_up/emails/show.html.erb index 4948ff96636..be934f3b8d7 100644 --- a/app/views/sign_up/emails/show.html.erb +++ b/app/views/sign_up/emails/show.html.erb @@ -15,31 +15,29 @@ <%= t('notices.signed_up_but_unconfirmed.first_paragraph_end') %>

-
-
-
+

<%= t('devise.registrations.close_window') %>

-<%= simple_form_for @resend_email_confirmation_form, - html: { class: 'margin-bottom-2' }, - url: sign_up_register_path do |f| %> - <%= f.input :email, as: :hidden %> - <%= f.input :resend, as: :hidden %> -

<%= t('notices.signed_up_but_unconfirmed.no_email_sent_explanation_start') %> - <%= f.button :button, t('links.resend'), class: 'usa-button--unstyled margin-left-05' %>

- -

- <%= t( - 'notices.use_diff_email.text_html', - link_html: link_to(t('notices.use_diff_email.link'), sign_up_email_path), - ) %> -

-

<%= t('devise.registrations.close_window') %>

+<%= render TroubleshootingOptionsComponent.new do |c| %> + <% c.with_header { t('components.troubleshooting_options.default_heading') } %> + <% c.with_option(component: FormLinkComponent) do %> + <%= t('notices.signed_up_but_unconfirmed.resend_confirmation_email') %> + <%= simple_form_for @resend_email_confirmation_form, + html: { class: 'display-none' }, + url: sign_up_register_path do |f| %> + <%= f.input :email, as: :hidden %> + <%= f.input :resend, as: :hidden %> + <%= f.button :button, t('notices.signed_up_but_unconfirmed.resend_confirmation_email') %> + <% end %> + <% end %> + <% c.with_option( + url: sign_up_email_path, + ).with_content(t('notices.use_diff_email.link').upcase_first) %> +<% end %> - <% if FeatureManagement.enable_load_testing_mode? && EmailAddress.find_with_email(email) %> - <%= link_to( - 'CONFIRM NOW', - sign_up_create_email_confirmation_url(confirmation_token: EmailAddress.find_with_email(email).confirmation_token), - id: 'confirm-now', - ) %> - <% end %> +<% if FeatureManagement.enable_load_testing_mode? && EmailAddress.find_with_email(email) %> + <%= link_to( + 'CONFIRM NOW', + sign_up_create_email_confirmation_url(confirmation_token: EmailAddress.find_with_email(email).confirmation_token), + id: 'confirm-now', + ) %> <% end %> diff --git a/config/locales/notices/en.yml b/config/locales/notices/en.yml index c621d34246d..a617bb02aa9 100644 --- a/config/locales/notices/en.yml +++ b/config/locales/notices/en.yml @@ -35,7 +35,7 @@ en: first_paragraph_end: with a link to confirm your email address. Follow the link to continue creating your account. first_paragraph_start: We sent an email to - no_email_sent_explanation_start: Didn’t receive an email? + resend_confirmation_email: Resend the confirmation email timeout_warning: partially_signed_in: continue: Continue sign in diff --git a/config/locales/notices/es.yml b/config/locales/notices/es.yml index 180544da64e..3fae3ca377b 100644 --- a/config/locales/notices/es.yml +++ b/config/locales/notices/es.yml @@ -37,7 +37,7 @@ es: first_paragraph_end: con un enlace para confirmar su email. Siga el enlace para continuar creando su cuenta. first_paragraph_start: Enviamos un email a - no_email_sent_explanation_start: '¿No recibió un email?' + resend_confirmation_email: Reenviar el correo electrónico de confirmación timeout_warning: partially_signed_in: continue: Continuar el inicio de sesión diff --git a/config/locales/notices/fr.yml b/config/locales/notices/fr.yml index 0e366e5db32..9ed9eb1a3cc 100644 --- a/config/locales/notices/fr.yml +++ b/config/locales/notices/fr.yml @@ -37,7 +37,7 @@ fr: first_paragraph_end: avec un lien pour confirmer votre adresse courriel. Suivez le lien pour continuer à créer votre compte. first_paragraph_start: Nous avons envoyé un courriel à - no_email_sent_explanation_start: Vous n’avez pas reçu d’e-mail? + resend_confirmation_email: Renvoyer le courriel de confirmation timeout_warning: partially_signed_in: continue: Continuer la connexion diff --git a/config/locales/telephony/en.yml b/config/locales/telephony/en.yml index c8a6b95d05c..b6a9a6e0808 100644 --- a/config/locales/telephony/en.yml +++ b/config/locales/telephony/en.yml @@ -1,6 +1,7 @@ --- en: telephony: + account_deleted_notice: This text message confirms you have deleted your %{app_name} account. account_reset_cancellation_notice: Your request to delete your %{app_name} account has been cancelled. account_reset_notice: As requested, your %{app_name} account will be deleted in %{interval}. Don't want to delete your account? Sign in to your diff --git a/config/locales/telephony/es.yml b/config/locales/telephony/es.yml index 37a022203a2..eb830e07212 100644 --- a/config/locales/telephony/es.yml +++ b/config/locales/telephony/es.yml @@ -1,6 +1,7 @@ --- es: telephony: + account_deleted_notice: Este SMS confirma que ha eliminado su cuenta de %{app_name}. account_reset_cancellation_notice: Su solicitud para eliminar su cuenta de %{app_name} ha sido cancelada. account_reset_notice: Según lo solicitado, su cuenta %{app_name} se eliminará en %{interval}. ¿No quieres eliminar tu cuenta? Inicie sesión en su cuenta diff --git a/config/locales/telephony/fr.yml b/config/locales/telephony/fr.yml index 013d8e60358..e29117bc63c 100644 --- a/config/locales/telephony/fr.yml +++ b/config/locales/telephony/fr.yml @@ -1,6 +1,7 @@ --- fr: telephony: + account_deleted_notice: Cet SMS confirme que vous avez supprimé votre compte %{app_name}. account_reset_cancellation_notice: Votre demande de suppression de votre compte %{app_name} a été annulée. account_reset_notice: Comme demandé, votre compte %{app_name} sera supprimé dans les %{interval}. Vous ne voulez pas supprimer votre compte? Connectez-vous diff --git a/db/primary_migrate/20240215212318_add_vtr_and_acrvalues_to_identities.rb b/db/primary_migrate/20240215212318_add_vtr_and_acrvalues_to_identities.rb new file mode 100644 index 00000000000..3cdf60f9ec1 --- /dev/null +++ b/db/primary_migrate/20240215212318_add_vtr_and_acrvalues_to_identities.rb @@ -0,0 +1,6 @@ +class AddVtrAndAcrvaluesToIdentities < ActiveRecord::Migration[7.1] + def change + add_column :identities, :vtr, :string + add_column :identities, :acr_values, :string + end +end diff --git a/db/primary_migrate/20240216184124_drop_monthly_auth_counts.rb b/db/primary_migrate/20240216184124_drop_monthly_auth_counts.rb new file mode 100644 index 00000000000..7c47e069de5 --- /dev/null +++ b/db/primary_migrate/20240216184124_drop_monthly_auth_counts.rb @@ -0,0 +1,17 @@ +class DropMonthlyAuthCounts < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def up + drop_table :monthly_auth_counts + end + + def down + create_table :monthly_auth_counts do |t| + t.string :issuer, null: false + t.string :year_month, null: false + t.integer :user_id, null: false + t.integer :auth_count, default: 1, null: false + end + add_index :monthly_auth_counts, %i[issuer year_month user_id], algorithm: :concurrently, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 8cdcaf3f109..4c3a718d4d5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_01_10_142935) do +ActiveRecord::Schema[7.1].define(version: 2024_02_16_184124) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_stat_statements" @@ -288,6 +288,8 @@ t.datetime "deleted_at", precision: nil t.integer "aal" t.text "requested_aal_value" + t.string "vtr" + t.string "acr_values" t.index ["access_token"], name: "index_identities_on_access_token", unique: true t.index ["session_uuid"], name: "index_identities_on_session_uuid", unique: true t.index ["user_id", "service_provider"], name: "index_identities_on_user_id_and_service_provider", unique: true @@ -362,14 +364,6 @@ t.index ["ftp_at"], name: "index_letter_requests_to_usps_ftp_logs_on_ftp_at" end - create_table "monthly_auth_counts", force: :cascade do |t| - t.string "issuer", null: false - t.string "year_month", null: false - t.integer "user_id", null: false - t.integer "auth_count", default: 1, null: false - t.index ["issuer", "year_month", "user_id"], name: "index_monthly_auth_counts_on_issuer_and_year_month_and_user_id", unique: true - end - create_table "notification_phone_configurations", force: :cascade do |t| t.bigint "in_person_enrollment_id", null: false t.text "encrypted_phone", null: false, comment: "Encrypted phone number to send notifications to" diff --git a/docs/local-development.md b/docs/local-development.md index bc560e8533f..344db74541b 100644 --- a/docs/local-development.md +++ b/docs/local-development.md @@ -65,7 +65,7 @@ asked to consent to share their information with the partner before being sent b To simulate a true end-to-end user experience, you can either... - Use the built-in test controller for SAML logins at http://localhost:3000/test/saml/login or OIDC logins at http://localhost:3000/test/oidc/login - + Note: to update service provider configurations, run the command `rake db:seed` or `make setup`. - Or, run a sample partner application, which is configured by default to run with your local IdP instance: - OIDC: https://github.com/18F/identity-oidc-sinatra @@ -125,8 +125,8 @@ $ SKIP_BUILD=true bundle exec rspec spec/features Since the automatic build is meant to act as a safeguard to prevent stale assets from being used, disabling it will mean you're responsible for running the build any time JavaScript or Sass source -files are changed. You can do this by running `yarn build` for JavaScript, or `yarn build:css` for -stylesheets. +files are changed. You can do this by running `yarn build:js` for JavaScript, or `yarn build:css` +for stylesheets. ### Viewing email messages diff --git a/lib/cleanup/destroyable_records.rb b/lib/cleanup/destroyable_records.rb index 08cf370a4f4..47746e7ba6b 100644 --- a/lib/cleanup/destroyable_records.rb +++ b/lib/cleanup/destroyable_records.rb @@ -26,7 +26,7 @@ def print_data stdout.puts '********' stdout.puts 'Integration:' - if integration.nil? + if integration.blank? stdout.puts 'No associated integration' else stdout.puts integration.attributes.to_yaml @@ -46,7 +46,7 @@ def print_data stdout.puts '*******' stdout.puts 'These are the IAA orders that will be affected: \n' - if iaa_orders.nil? + if iaa_orders.blank? stdout.puts 'No IAA orders will be affected' else stdout.puts 'These are the IAA orders that will be affected: \n' @@ -79,11 +79,11 @@ def destroy_records private def integration_usages - integration&.integration_usages + integration&.integration_usages || [] end def iaa_orders - integration&.iaa_orders + integration&.iaa_orders || [] end def in_person_enrollments diff --git a/lib/telephony.rb b/lib/telephony.rb index d8d5eb7a2c6..ac43d2f3698 100644 --- a/lib/telephony.rb +++ b/lib/telephony.rb @@ -80,6 +80,7 @@ def self.alert_sender :send_doc_auth_link, :send_personal_key_regeneration_notice, :send_personal_key_sign_in_notice, + :send_account_deleted_notice, :send_account_reset_notice, :send_account_reset_cancellation_notice, :send_notification diff --git a/lib/telephony/alert_sender.rb b/lib/telephony/alert_sender.rb index b202e5bbfc1..adc2a149af7 100644 --- a/lib/telephony/alert_sender.rb +++ b/lib/telephony/alert_sender.rb @@ -2,6 +2,13 @@ module Telephony class AlertSender SMS_MAX_LENGTH = 160 + def send_account_deleted_notice(to:, country_code:) + message = I18n.t('telephony.account_deleted_notice', app_name: APP_NAME) + response = adapter.deliver(message: message, to: to, country_code: country_code) + log_response(response, context: __method__.to_s.gsub(/^send_/, '')) + response + end + def send_account_reset_notice(to:, country_code:, interval:) message = I18n.t( 'telephony.account_reset_notice', app_name: APP_NAME, diff --git a/package.json b/package.json index 13abe4113f6..2fba5d67906 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "generate-browsers-json": "./scripts/generate-browsers-json.js", "clean": "rm -rf public/packs/*", "prebuild": "yarn run clean", - "build": "webpack && yarn generate-browsers-json", + "build": "yarn build:js", + "build:js": "concurrently 'yarn:webpack' 'make browsers.json'", "build:css": "build-sass app/assets/stylesheets/*.css.scss app/components/*.scss --load-path=app/assets/stylesheets --out-dir=app/assets/builds" }, "dependencies": { @@ -31,6 +32,7 @@ "babel-plugin-polyfill-corejs3": "^0.5.2", "browserslist": "^4.22.3", "cleave.js": "^1.6.0", + "concurrently": "^8.2.2", "core-js": "^3.21.1", "fast-glob": "^3.2.7", "foundation-emails": "^2.3.1", diff --git a/spec/components/block_link_component_spec.rb b/spec/components/block_link_component_spec.rb index f3b9ece14a2..4ead0f748c6 100644 --- a/spec/components/block_link_component_spec.rb +++ b/spec/components/block_link_component_spec.rb @@ -29,26 +29,22 @@ end end - context 'with custom renderer' do - # rubocop:disable RSpec/LeakyConstantDeclaration - class ExampleBlockLinkCustomRendererComponent < BaseComponent - def initialize(href:, **) - @href = href - end - - def call - content_tag(:button, "Example #{content.strip}", data: { href: @href }) - end + context 'with a component' do + before do + stub_const( + 'TestComponent', Class.new(BaseComponent) do + def call + content_tag(:div, 'from test component', class: 'style') + end + end + ) end - # rubocop:enable RSpec/LeakyConstantDeclaration - it 'renders using the custom renderer' do - rendered = render_inline BlockLinkComponent.new( - url: '/', - action: ExampleBlockLinkCustomRendererComponent.method(:new), - ).with_content('Link Text') + it 'renders using the specified component' do + rendered = render_inline(BlockLinkComponent.new(component: TestComponent)) - expect(rendered).to have_css('button[data-href="/"]', text: 'Example Link Text') + expect(rendered).to have_css('.style') + expect(rendered).to have_text('from test component') end end end diff --git a/spec/components/form_link_component_spec.rb b/spec/components/form_link_component_spec.rb new file mode 100644 index 00000000000..f6d11807824 --- /dev/null +++ b/spec/components/form_link_component_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe FormLinkComponent, type: :component do + let(:options) { { href: '/', method: :post } } + let(:content) { 'Title' } + + subject(:rendered) do + render_inline(described_class.new(**options).with_content(content)) + end + + it 'renders custom element with link' do + expect(rendered).to have_css('lg-form-link a[href="/"]', text: content) + end +end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 11d7a7cc266..72a9aa0a6db 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -473,6 +473,19 @@ def index expect(result.aal2?).to eq(true) expect(result.identity_proofing?).to eq(true) end + + context 'without an SP' do + it 'returns a no-SP result' do + sp = nil + sp_session = {} + allow(controller).to receive(:current_sp).and_return(sp) + allow(controller).to receive(:sp_session).and_return(sp_session) + + result = subject.resolved_authn_context_result + + expect(result).to eq(Vot::Parser::Result.no_sp_result) + end + end end describe '#sp_session_request_url_with_updated_params' do diff --git a/spec/controllers/frontend_log_controller_spec.rb b/spec/controllers/frontend_log_controller_spec.rb index 6a3ce372296..9ca398cedbb 100644 --- a/spec/controllers/frontend_log_controller_spec.rb +++ b/spec/controllers/frontend_log_controller_spec.rb @@ -31,10 +31,9 @@ end context 'with invalid event name' do - it 'logs with warning' do + it 'responds as unsuccessful' do action - expect(fake_analytics).to have_logged_event('Frontend (warning): Custom Event') expect(response).to have_http_status(:bad_request) expect(json[:success]).to eq(false) expect(json[:error_message]).to eq('invalid event') @@ -239,10 +238,9 @@ end context 'with invalid event name' do - it 'logs with warning' do + it 'responds as unsuccessful' do action - expect(fake_analytics).to have_logged_event('Frontend (warning): Custom Event') expect(response).to have_http_status(:bad_request) expect(json[:success]).to eq(false) expect(json[:error_message]).to eq('invalid event') diff --git a/spec/controllers/users/delete_controller_spec.rb b/spec/controllers/users/delete_controller_spec.rb index 4f3007cb5e9..e61186248db 100644 --- a/spec/controllers/users/delete_controller_spec.rb +++ b/spec/controllers/users/delete_controller_spec.rb @@ -74,7 +74,14 @@ allow(UserMailer).to receive(:account_delete_submitted).and_call_original stub_signed_in_user delete - expect(UserMailer).not_to have_received(:account_delete_submitted) + expect(ActionMailer::Base.deliveries.count).to eq 1 + end + + it 'text user of account deletion' do + allow(Telephony).to receive(:send_account_deleted_notice).and_call_original + stub_signed_in_user + delete + expect(Telephony).to have_received(:send_account_deleted_notice) end it 'logs a succesful submit' do @@ -114,6 +121,7 @@ def stub_signed_in_user user = create( :user, :fully_registered, + :with_phone, email: 'old_email@example.com', password: ControllerHelper::VALID_PASSWORD, ) diff --git a/spec/factories/auth_app_configurations.rb b/spec/factories/auth_app_configurations.rb index 473d945bd32..8413405d69d 100644 --- a/spec/factories/auth_app_configurations.rb +++ b/spec/factories/auth_app_configurations.rb @@ -2,7 +2,7 @@ Faker::Config.locale = :en factory :auth_app_configuration do - name { Faker::Lorem.word } + name { Faker::Lorem.unique.words.join(' ') } otp_secret_key { SecureRandom.hex(16) } user end diff --git a/spec/factories/piv_cac_configurations.rb b/spec/factories/piv_cac_configurations.rb index f2b5c9bb0de..68c73f0b22b 100644 --- a/spec/factories/piv_cac_configurations.rb +++ b/spec/factories/piv_cac_configurations.rb @@ -2,7 +2,7 @@ Faker::Config.locale = :en factory :piv_cac_configuration do - name { Faker::Lorem.word } + name { Faker::Lorem.unique.words.join(' ') } x509_dn_uuid { 'helloworld' } user end diff --git a/spec/features/idv/doc_auth/document_capture_spec.rb b/spec/features/idv/doc_auth/document_capture_spec.rb index 9141c0d592d..525886c37ba 100644 --- a/spec/features/idv/doc_auth/document_capture_spec.rb +++ b/spec/features/idv/doc_auth/document_capture_spec.rb @@ -58,16 +58,15 @@ attach_images( Rails.root.join( 'spec', 'fixtures', - 'ial2_test_credential_barcode_attention_no_dob.yml' + 'ial2_test_credential_barcode_attention_no_address.yml' ), ) submit_images - expect(page).to have_content(t('doc_auth.errors.barcode_attention.heading')) - click_idv_continue - - # should show try again + expect(page).to have_content(t('doc_auth.errors.alerts.address_check')) expect(page).to have_current_path(idv_document_capture_path) + + click_try_again attach_images submit_images expect(page).to have_current_path(idv_ssn_path) diff --git a/spec/features/visitors/email_confirmation_spec.rb b/spec/features/visitors/email_confirmation_spec.rb index 9e86c14bc27..2a9ac6680ac 100644 --- a/spec/features/visitors/email_confirmation_spec.rb +++ b/spec/features/visitors/email_confirmation_spec.rb @@ -46,7 +46,7 @@ it 'sends the confirmation email again' do sign_up_with('test@example.com') - expect { click_on t('links.resend') }. + expect { click_on t('notices.signed_up_but_unconfirmed.resend_confirmation_email') }. to change { ActionMailer::Base.deliveries.count }.by(1) expect(last_email.html_part.body).to have_content( diff --git a/spec/fixtures/ial2_test_credential_barcode_attention_no_address.yml b/spec/fixtures/ial2_test_credential_barcode_attention_no_address.yml new file mode 100644 index 00000000000..c32bbceb669 --- /dev/null +++ b/spec/fixtures/ial2_test_credential_barcode_attention_no_address.yml @@ -0,0 +1,14 @@ +document: + first_name: Jane + last_name: Doe + middle_name: Q + city: Bayside + state: NY + zipcode: '11364' + dob: 10/06/1938 + phone: +1 314-555-1212 + state_id_jurisdiction: 'NY' + state_id_number: 'S59397998' +failed_alerts: + - name: 2D Barcode Read + result: Attention \ No newline at end of file diff --git a/spec/lib/cleanup/destroyable_records_spec.rb b/spec/lib/cleanup/destroyable_records_spec.rb index d425b780ebf..269379bf495 100644 --- a/spec/lib/cleanup/destroyable_records_spec.rb +++ b/spec/lib/cleanup/destroyable_records_spec.rb @@ -106,6 +106,16 @@ expect(iaa_order.integrations.include? integration).to be false end + describe 'integration without usages or iaa_orders' do + let!(:empty_integration) { create(:integration) } + let!(:service_provider) { empty_integration.service_provider } + + it 'destroys the integration' do + deleted_int = Agreements::Integration.find_by(id: empty_integration.id) + expect(deleted_int).to be nil + end + end + it 'does not delete unrelated objects' do iu2.reload iaa_order.reload diff --git a/spec/lib/telephony/alert_sender_spec.rb b/spec/lib/telephony/alert_sender_spec.rb index 01616c6e797..7430cdc73ad 100644 --- a/spec/lib/telephony/alert_sender_spec.rb +++ b/spec/lib/telephony/alert_sender_spec.rb @@ -10,6 +10,18 @@ Telephony::Test::Message.clear_messages end + describe 'send_account_deleted_notice' do + it 'sends the correct message' do + subject.send_account_deleted_notice(to: recipient, country_code: 'US') + + last_message = Telephony::Test::Message.messages.last + expect(last_message.to).to eq(recipient) + expect(last_message.body).to eq( + I18n.t('telephony.account_deleted_notice', app_name: APP_NAME), + ) + end + end + describe 'send_account_reset_notice' do it 'sends the correct message' do subject.send_account_reset_notice(to: recipient, country_code: 'US', interval: interval) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 3d64d596206..04b0c431cdd 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -79,8 +79,7 @@ class Analytics # rubocop:enable Style/GlobalVars # rubocop:disable Rails/Output print ' Bundling JavaScript and stylesheets... ' - system 'WEBPACK_PORT= yarn build > /dev/null 2>&1' - system 'yarn build:css > /dev/null 2>&1' + system 'WEBPACK_PORT= yarn concurrently "yarn:build:*" > /dev/null 2>&1' puts '✨ Done!' # rubocop:enable Rails/Output end diff --git a/spec/services/frontend_logger_spec.rb b/spec/services/frontend_logger_spec.rb index 2d146bf1d85..545ea9f4a4f 100644 --- a/spec/services/frontend_logger_spec.rb +++ b/spec/services/frontend_logger_spec.rb @@ -37,16 +37,14 @@ def example_method_handler(ok:, **rest) context 'with unknown event' do let(:name) { :test_event } - it 'logs unknown event with warning' do - call - - expect(analytics).to have_logged_event('Frontend (warning): test_event') - end + it { expect(call).to eq(false) } end context 'with method handler' do let(:name) { 'method' } + it { expect(call).to eq(true) } + it 'calls method with attributes based on signature' do call @@ -57,6 +55,8 @@ def example_method_handler(ok:, **rest) context 'with proc handler' do let(:name) { 'proc' } + it { expect(call).to eq(true) } + it 'calls the method and passes analytics and attributes' do call diff --git a/spec/services/reporting/total_user_count_report_spec.rb b/spec/services/reporting/total_user_count_report_spec.rb index bf3f2bb503d..b67f7111bb6 100644 --- a/spec/services/reporting/total_user_count_report_spec.rb +++ b/spec/services/reporting/total_user_count_report_spec.rb @@ -10,6 +10,13 @@ [ ['Metric', 'All Users', 'Verified users', 'Time Range Start', 'Time Range End'], ['All-time count', expected_total_count, expected_verified_count, '-', Date.new(2021, 3, 1)], + [ + 'All-time fully registered', + expected_total_fully_registered, + '-', + '-', + Date.new(2021, 3, 1), + ], [ 'New users count', expected_new_count, @@ -44,6 +51,7 @@ before { create(:user) } let(:expected_total_count) { 1 } let(:expected_verified_count) { 0 } + let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 1 } let(:expected_new_verified_count) { 0 } let(:expected_annual_count) { expected_total_count } @@ -58,6 +66,7 @@ let(:expected_total_count) { 2 } let(:expected_verified_count) { 0 } + let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 1 } let(:expected_new_verified_count) { 0 } let(:expected_annual_count) { 1 } @@ -75,6 +84,7 @@ end let(:expected_total_count) { 2 } let(:expected_verified_count) { 1 } + let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 2 } let(:expected_new_verified_count) { 1 } let(:expected_annual_count) { expected_total_count } @@ -92,6 +102,7 @@ # A suspended user is still a total user: let(:expected_total_count) { 1 } let(:expected_verified_count) { 0 } + let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 1 } let(:expected_new_verified_count) { 0 } let(:expected_annual_count) { 1 } @@ -106,6 +117,7 @@ # A user with a fraud rejection is still a total user let(:expected_total_count) { 1 } let(:expected_verified_count) { 0 } + let(:expected_total_fully_registered) { 1 } let(:expected_new_count) { 1 } let(:expected_new_verified_count) { 0 } let(:expected_annual_count) { 1 } @@ -113,5 +125,23 @@ it_behaves_like 'a report with the specified counts' end + + context 'with fully registered user' do + before do + create(:user) + create_list(:user, 2).each do |user| + RegistrationLog.create(user: user, registered_at: user.created_at) + end + end + let(:expected_total_count) { 3 } + let(:expected_verified_count) { 0 } + let(:expected_total_fully_registered) { 2 } + let(:expected_new_count) { 3 } + let(:expected_new_verified_count) { 0 } + let(:expected_annual_count) { 3 } + let(:expected_annual_verified_count) { 0 } + + it_behaves_like 'a report with the specified counts' + end end end diff --git a/spec/support/features/session_helper.rb b/spec/support/features/session_helper.rb index b5754b5dc38..70f3ba3a267 100644 --- a/spec/support/features/session_helper.rb +++ b/spec/support/features/session_helper.rb @@ -490,7 +490,7 @@ def submit_form_with_valid_but_wrong_email end def click_link_to_use_a_different_email - click_link t('notices.use_diff_email.link') + click_link t('notices.use_diff_email.link').upcase_first end def submit_form_with_valid_email(email = 'test@test.com') diff --git a/spec/views/sign_up/emails/show.html.erb_spec.rb b/spec/views/sign_up/emails/show.html.erb_spec.rb index b51b5daf051..b8f2e9f22d7 100644 --- a/spec/views/sign_up/emails/show.html.erb_spec.rb +++ b/spec/views/sign_up/emails/show.html.erb_spec.rb @@ -19,10 +19,14 @@ expect(rendered).to have_selector('h1', text: t('headings.verify_email')) end - it 'contains link to resend confirmation page' do + it 'contains a form link to resend confirmation page' do render - expect(rendered).to have_button(t('links.resend')) + expect(rendered).to have_selector('lg-form-link') + expect(rendered).to have_link(href: '#', class: ['usa-link', 'block-link']) + expect(rendered). + to have_button(t('notices.signed_up_but_unconfirmed.resend_confirmation_email')) + expect(rendered).to have_css("form[action='#{sign_up_register_path}']") end context 'when enable_load_testing_mode? is true and email address found' do diff --git a/yarn.lock b/yarn.lock index 6f4fc7cd765..16b74664e85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -985,12 +985,12 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.9", "@babel/runtime@^7.8.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" - integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== +"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.8.4": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== dependencies: - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.14.0" "@babel/template@^7.15.4", "@babel/template@^7.20.7", "@babel/template@^7.22.15": version "7.22.15" @@ -2483,7 +2483,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: +chalk@^4.0, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -2673,6 +2673,21 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concurrently@^8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.2.tgz#353141985c198cfa5e4a3ef90082c336b5851784" + integrity sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg== + dependencies: + chalk "^4.1.2" + date-fns "^2.30.0" + lodash "^4.17.21" + rxjs "^7.8.1" + shell-quote "^1.8.1" + spawn-command "0.0.2" + supports-color "^8.1.1" + tree-kill "^1.2.2" + yargs "^17.7.2" + confusing-browser-globals@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" @@ -2836,6 +2851,13 @@ data-urls@^4.0.0: whatwg-mimetype "^3.0.0" whatwg-url "^12.0.0" +date-fns@^2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== + dependencies: + "@babel/runtime" "^7.21.0" + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -4655,10 +4677,10 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libphonenumber-js@^1.10.55: - version "1.10.55" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.55.tgz#ec864e369bf7babde02021d06b5f2433d7e9c78e" - integrity sha512-MrTg2JFLscgmTY6/oT9vopYETlgUls/FU6OaeeamGwk4LFxjIgOUML/ZSZICgR0LPYXaonVJo40lzMvaaTJlQA== +libphonenumber-js@^1.10.56: + version "1.10.56" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.56.tgz#96d9d64c31888ec363ed99fbf77cf4ad12117aa4" + integrity sha512-d0GdKshNnyfl5gM7kZ9rXjGiAbxT/zCXp0k+EAzh8H4zrb2R7GXtMCrULrX7UQxtfx6CLy/vz/lomvW79FAFdA== lightningcss-darwin-arm64@1.23.0: version "1.23.0" @@ -5811,6 +5833,11 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + regenerator-transform@^0.14.2: version "0.14.5" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" @@ -5954,10 +5981,10 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.4.0, rxjs@^7.5.5: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== +rxjs@^7.4.0, rxjs@^7.5.5, rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -6347,6 +6374,11 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spawn-command@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" + integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ== + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -6779,6 +6811,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +tree-kill@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + trim-newlines@^4.0.2: version "4.1.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-4.1.1.tgz#28c88deb50ed10c7ba6dc2474421904a00139125" @@ -7351,7 +7388,7 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1: +yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==