Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy RC 407 to Production #11117

Merged
merged 22 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9541594
Adding labels to kubernetes resources for easier tracing (#11081)
stephencshelton Aug 15, 2024
cf00082
Refactor backup code verification to follow conventional form pattern…
aduth Aug 15, 2024
9ffd3a3
Do not write config file by default on boot (#11100)
mitchellhenke Aug 16, 2024
3674e15
Use defer for non-critical scripts (#11096)
aduth Aug 16, 2024
c44f12a
LG-14078: Rate-limit backup code attempts based on IP+user ID (#11094)
aduth Aug 16, 2024
12da96c
LG-14175: Expire EIPP enrollments in job (#11085)
eileen-nava Aug 16, 2024
b5b9e5f
Document analytics methods properties in primary IdV flow (#11099)
aduth Aug 16, 2024
e92a99f
Update Propshaft to 0.9.x (#11103)
aduth Aug 16, 2024
2c81950
Try: Fix second MFA reminder flakey spec (#11109)
aduth Aug 19, 2024
0c0e643
Remove reference to frontend interest group team in contributing guid…
aduth Aug 19, 2024
08a2287
Renamed variable (#11102)
AShukla-GSA Aug 19, 2024
0923f95
Enforce YAML normalization for application.yml.default (#11106)
aduth Aug 19, 2024
e101d33
Jmax/lg 13875 rename documentstep in doc auth (#11092)
jmax-gsa Aug 19, 2024
c3225b4
Update configuration link in SDK upgrade documentation (#11111)
aduth Aug 19, 2024
8632b33
Updated name and references (#11098)
AShukla-GSA Aug 19, 2024
f963d71
LG-13934 Add socure webhook (#11101)
jmax-gsa Aug 19, 2024
9dac9eb
LG-14231: Users can't access accounts after enrollment expires (#11105)
eileen-nava Aug 19, 2024
2f77eb1
LG-12532 Add 50/50 tests for IPP state id page (#11090)
shanechesnutt-ft Aug 19, 2024
e26251b
add regression spec for earlier bug fix [skip changelog] (#11112)
eileen-nava Aug 19, 2024
daab1f8
LG-14219: Arrange email as first item in IdV consent screen (#11113)
aduth Aug 19, 2024
43762a1
LG-14022 | Merge selected DIVR stats into MKMR (#11072)
n1zyy Aug 19, 2024
a897789
changelog: upcoming feature, doc auth, create feature flag for future…
AShukla-GSA Aug 20, 2024
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
4 changes: 4 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ trigger_devops:
- kubectl config get-contexts
- export CONTEXT=$(kubectl config get-contexts | grep reviewapp | awk '{print $1}' | head -1)
- kubectl config use-context "$CONTEXT"
- export SANITIZED_BRANCH_NAME=$(echo "$CI_COMMIT_REF_NAME" | tr '/' '-' | tr -c '[:alnum:]-_' '-' | sed 's/-*$//')
- echo "${CI_COMMIT_REF_NAME}"
- echo "${SANITIZED_BRANCH_NAME}"
- |-
export IDP_CONFIG=$(cat <<EOF
{
Expand Down Expand Up @@ -532,6 +535,7 @@ trigger_devops:
- >-
helm upgrade --install --namespace review-apps
--debug
--set global.labels.branch="${SANITIZED_BRANCH_NAME}"
--set env="reviewapps-$CI_ENVIRONMENT_SLUG"
--set idp.image.repository="${ECR_REGISTRY}/identity-idp/review"
--set idp.image.tag="${CI_COMMIT_SHA}"
Expand Down
7 changes: 1 addition & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,7 @@ commits together, merges it, then deletes the branch.

Everyone is encouraged to participate in code review. To solicit feedback from specific people,
consider adding individuals or groups as requested reviewers on your pull request. Most internal
product teams have a team handle which can be used to notify everyone on that team, or you can
request reviews from one of the available interest group teams:

- `18f/identity-frontend` for developers interested in frontend development

To request to join any of these teams, you can contact any existing member and ask to be added.
product teams have a team handle which can be used to notify everyone on that team.

## Public domain

Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ GEM
profanity_filter (0.1.1)
prometheus_exporter (2.1.0)
webrick
propshaft (0.7.0)
propshaft (0.9.1)
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ lint_erb: ## Lints ERB files
bundle exec erblint app/views app/components

lint_yaml: normalize_yaml ## Lints YAML files
(! git diff --name-only | grep "^config/.*\.yml$$") || (echo "Error: Run 'make normalize_yaml' to normalize YAML"; exit 1)
(! git diff --name-only | grep "^config/.*\.yml") || (echo "Error: Run 'make normalize_yaml' to normalize YAML"; exit 1)

lint_font_glyphs: ## Lints to validate content glyphs match expectations from fonts
scripts/yaml_characters \
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/socure_webhook_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class SocureWebhookController < ApplicationController
skip_before_action :verify_authenticity_token

def create
render json: { message: 'Got here.' }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ def show
service_provider: current_sp,
remember_device_default: remember_device_default,
)
@backup_code_form = BackupCodeVerificationForm.new(current_user)
@backup_code_form = BackupCodeVerificationForm.new(user: current_user, request:)
end

def create
@backup_code_form = BackupCodeVerificationForm.new(current_user)
@backup_code_form = BackupCodeVerificationForm.new(user: current_user, request:)
result = @backup_code_form.submit(backup_code_params)
handle_result(result)
end
Expand All @@ -46,10 +46,10 @@ def presenter_for_two_factor_authentication_method
)
end

def handle_invalid_backup_code
def handle_invalid_backup_code(result)
update_invalid_user

flash.now[:error] = t('two_factor_authentication.invalid_backup_code')
flash.now[:error] = result.first_error_message

if current_user.locked_out?
handle_second_factor_locked_user(type: 'backup_code')
Expand All @@ -69,7 +69,7 @@ def handle_result(result)
return handle_last_code if all_codes_used?
handle_valid_backup_code
else
handle_invalid_backup_code
handle_invalid_backup_code(result)
end
end

Expand Down
55 changes: 47 additions & 8 deletions app/forms/backup_code_verification_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,74 @@

class BackupCodeVerificationForm
include ActiveModel::Model
include ActionView::Helpers::TranslationHelper
include DOTIW::Methods

def initialize(user)
validate :validate_rate_limited
validate :validate_and_consume_backup_code!

attr_reader :user, :backup_code, :request

def initialize(user:, request:)
@user = user
@backup_code = ''
@request = request
end

def submit(params)
@backup_code = params[:backup_code]

rate_limiter.increment!

FormResponse.new(
success: valid_backup_code?,
success: valid?,
errors:,
extra: extra_analytics_attributes,
serialize_error_details_only: true,
)
end

private

def validate_rate_limited
return if !rate_limiter.limited?
errors.add(
:backup_code,
:rate_limited,
message: t(
'errors.messages.phone_confirmation_limited',
timeout: distance_of_time_in_words(Time.zone.now, rate_limiter.expires_at),
),
)
end

attr_reader :user, :backup_code
def validate_and_consume_backup_code!
return if rate_limiter.limited? || valid_backup_code?
errors.add(:backup_code, :invalid, message: t('two_factor_authentication.invalid_backup_code'))
end

def valid_backup_code?
valid_backup_code_config_created_at.present?
end

def valid_backup_code_config_created_at
return @valid_backup_code_config_created_at if defined?(@valid_backup_code_config_created_at)
@valid_backup_code_config_created_at = BackupCodeGenerator.new(@user).
@valid_backup_code_config_created_at = BackupCodeGenerator.new(user).
if_valid_consume_code_return_config_created_at(backup_code)
end

def rate_limiter
@rate_limiter ||= RateLimiter.new(
rate_limit_type: :backup_code_user_id_per_ip,
target: [user.id, request.ip].join('-'),
)
end

def extra_analytics_attributes
{
multi_factor_auth_method_created_at: valid_backup_code_config_created_at&.strftime('%s%L'),
}
{ multi_factor_auth_method_created_at: }
end

def multi_factor_auth_method_created_at
return nil if !valid?
valid_backup_code_config_created_at.strftime('%s%L')
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { FormStepsButton } from '@18f/identity-form-steps';
import { Cancel } from '@18f/identity-verify-flow';
import { useI18n, HtmlTextWithStrongNoWrap } from '@18f/identity-react-i18n';
import type { FormStepComponentProps } from '@18f/identity-form-steps';
import UnknownError from './unknown-error';
import GeneralError from './general-error';
import TipList from './tip-list';
import { SelfieCaptureContext } from '../context';
import {
DocumentCaptureSubheaderOne,
SelfieCaptureWithHeader,
DocumentFrontAndBackCapture,
} from './documents-step';
} from './documents-and-selfie-step';
import type { ReviewIssuesStepValue } from './review-issues-step';

interface DocumentCaptureReviewIssuesProps extends FormStepComponentProps<ReviewIssuesStepValue> {
Expand Down Expand Up @@ -53,7 +53,7 @@ function DocumentCaptureReviewIssues({
{isSelfieCaptureEnabled && (
<DocumentCaptureSubheaderOne isSelfieCaptureEnabled={isSelfieCaptureEnabled} />
)}
<UnknownError
<GeneralError
unknownFieldErrors={unknownFieldErrors}
isFailedDocType={isFailedDocType}
isFailedSelfie={isFailedSelfie}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FormStepError } from '@18f/identity-form-steps';
import type { I18n } from '@18f/identity-i18n';
import Warning from './warning';
import DocumentCaptureTroubleshootingOptions from './document-capture-troubleshooting-options';
import UnknownError from './unknown-error';
import GeneralError from './general-error';
import { InPersonContext } from '../context';
import AnalyticsContext from '../context/analytics';
import SelfieCaptureContext from '../context/selfie-capture';
Expand Down Expand Up @@ -124,7 +124,7 @@ function DocumentCaptureWarning({
>
<div ref={subheadingRef}>{!!subheading && subheading}</div>
<div ref={errorMessageDisplayedRef}>
<UnknownError
<GeneralError
unknownFieldErrors={unknownFieldErrors}
isFailedDocType={isFailedDocType}
isFailedSelfie={isFailedSelfie}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useDidUpdateEffect } from '@18f/identity-react-hooks';
import type { FormStep } from '@18f/identity-form-steps';
import { getConfigValue } from '@18f/identity-config';
import { UploadFormEntriesError } from '../services/upload';
import DocumentsStep from './documents-step';
import DocumentsAndSelfieStep from './documents-and-selfie-step';
import InPersonPrepareStep from './in-person-prepare-step';
import InPersonLocationPostOfficeSearchStep from './in-person-location-post-office-search-step';
import InPersonLocationFullAddressEntryPostOfficeSearchStep from './in-person-location-full-address-entry-post-office-search-step';
Expand Down Expand Up @@ -53,7 +53,7 @@ function DocumentCapture({ onStepChange = () => {} }: DocumentCaptureProps) {
// Define different states to be used in human readable array declaration
const documentFormStep: FormStep = {
name: 'documents',
form: DocumentsStep,
form: DocumentsAndSelfieStep,
title: t('doc_auth.headings.document_capture'),
};
const reviewFormStep: FormStep = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function DocumentFrontAndBackCapture({

type ImageValue = Blob | string | null | undefined;

interface DocumentsStepValue {
interface DocumentsAndSelfieStepValue {
front: ImageValue;
back: ImageValue;
selfie: ImageValue;
Expand All @@ -93,17 +93,17 @@ interface DocumentsStepValue {
}

type DefaultSideProps = Pick<
FormStepComponentProps<DocumentsStepValue>,
FormStepComponentProps<DocumentsAndSelfieStepValue>,
'registerField' | 'onChange' | 'errors' | 'onError'
>;

function DocumentsStep({
export default function DocumentsAndSelfieStep({
value = {},
onChange = () => {},
errors = [],
onError = () => {},
registerField = () => undefined,
}: FormStepComponentProps<DocumentsStepValue>) {
}: FormStepComponentProps<DocumentsAndSelfieStepValue>) {
const { t } = useI18n();
const { isMobile } = useContext(DeviceContext);
const { isLastStep } = useContext(FormStepsContext);
Expand Down Expand Up @@ -145,5 +145,3 @@ function DocumentsStep({
</>
);
}

export default DocumentsStep;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Link } from '@18f/identity-components';
import formatHTML from '@18f/identity-react-i18n/format-html';
import MarketingSiteContext from '../context/marketing-site';

interface UnknownErrorProps extends ComponentProps<'p'> {
interface GeneralErrorProps extends ComponentProps<'p'> {
unknownFieldErrors: FormStepError<{ front: string; back: string }>[];
isFailedDocType: boolean;
isFailedSelfie: boolean;
Expand Down Expand Up @@ -40,15 +40,15 @@ function getError({ unknownFieldErrors }: GetErrorArguments) {
return err;
}

function UnknownError({
function GeneralError({
unknownFieldErrors = [],
isFailedDocType = false,
isFailedSelfie = false,
isFailedSelfieLivenessOrQuality = false,
altFailedDocTypeMsg = null,
altIsFailedSelfieDontIncludeAttempts = false,
hasDismissed,
}: UnknownErrorProps) {
}: GeneralErrorProps) {
const { t } = useI18n();
const { getHelpCenterURL } = useContext(MarketingSiteContext);
const helpCenterLink = getHelpCenterURL({
Expand Down Expand Up @@ -107,4 +107,4 @@ function UnknownError({
return <p />;
}

export default UnknownError;
export default GeneralError;
1 change: 1 addition & 0 deletions app/jobs/get_usps_proofing_results_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def handle_expired_status_update(enrollment, response, response_message)
status: :expired,
status_check_completed_at: Time.zone.now,
)
enrollment.profile.deactivate_due_to_ipp_expiration

if fraud_result_pending?(enrollment)
analytics(user: enrollment.user).idv_ipp_deactivated_for_never_visiting_post_office(
Expand Down
6 changes: 6 additions & 0 deletions app/jobs/reports/monthly_key_metrics_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'csv'
require 'reporting/proofing_rate_report'
require 'reporting/monthly_idv_report'

module Reports
class MonthlyKeyMetricsReport < BaseReport
Expand Down Expand Up @@ -68,6 +69,7 @@ def reports
@reports ||= [
active_users_count_report.active_users_count_emailable_report,
total_user_count_report.total_user_count_emailable_report,
monthly_idv_report.monthly_idv_report_emailable_report,
proofing_rate_report.proofing_rate_emailable_report,
account_deletion_rate_report.account_deletion_emailable_report,
account_reuse_report.account_reuse_emailable_report,
Expand Down Expand Up @@ -113,6 +115,10 @@ def agency_and_sp_report
@agency_and_sp_report ||= Reporting::AgencyAndSpReport.new(report_date)
end

def monthly_idv_report
@monthly_idv_report ||= Reporting::MonthlyIdvReport.new(end_date: report_date)
end

def upload_to_s3(report_body, report_name: nil)
_latest, path = generate_s3_paths(REPORT_NAME, 'csv', subname: report_name, now: report_date)

Expand Down
2 changes: 1 addition & 1 deletion app/mailers/report_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def tables_report(
@message = message

@reports = reports.map(&:dup).each_with_index do |report, index|
report.title ||= "Table #{index + 1}"
report.title ||= report.subtitle || "Table #{index + 1}"
end

case attachment_format
Expand Down
8 changes: 8 additions & 0 deletions app/models/profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ def deactivate_due_to_gpo_expiration
)
end

def deactivate_due_to_ipp_expiration
update!(
active: false,
deactivation_reason: :verification_cancelled,
in_person_verification_pending_at: nil,
)
end

def deactivate_for_in_person_verification
update!(active: false, in_person_verification_pending_at: Time.zone.now)
end
Expand Down
4 changes: 2 additions & 2 deletions app/presenters/completions_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ class CompletionsPresenter
attr_reader :current_user, :current_sp, :decrypted_pii, :requested_attributes, :completion_context

SORTED_IAL2_ATTRIBUTE_MAPPING = [
[[:email], :email],
[[:all_emails], :all_emails],
[%i[given_name family_name], :full_name],
[[:address], :address],
[[:phone], :phone],
[[:email], :email],
[[:all_emails], :all_emails],
[[:birthdate], :birthdate],
[[:social_security_number], :social_security_number],
[[:x509_subject], :x509_subject],
Expand Down
Loading