From 49c7e79ac8a875396577adc610d67b4d27b0667c Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Fri, 26 Dec 2025 09:47:12 -0800 Subject: [PATCH 01/48] sort of working --- dpc-portal/Gemfile | 3 - dpc-portal/Gemfile.lock | 85 ++--- .../app/controllers/application_controller.rb | 12 + .../controllers/login_dot_gov_controller.rb | 2 +- .../controllers/users/sessions_controller.rb | 20 +- dpc-portal/app/models/invitation.rb | 2 +- dpc-portal/app/models/user.rb | 19 +- .../app/views/layouts/application.html.erb | 2 +- dpc-portal/config/initializers/devise.rb | 342 ------------------ dpc-portal/config/routes.rb | 17 +- dpc-portal/spec/rails_helper.rb | 6 +- dpc-portal/spec/requests/application_spec.rb | 4 +- .../spec/requests/client_tokens_spec.rb | 7 +- .../credential_delegate_invitations_spec.rb | 3 +- dpc-portal/spec/requests/invitations_spec.rb | 1 + dpc-portal/spec/requests/ip_addresses_spec.rb | 7 +- .../spec/requests/organizations_spec.rb | 5 +- dpc-portal/spec/requests/public_keys_spec.rb | 7 +- .../spec/requests/users/sessions_spec.rb | 2 +- dpc-portal/spec/system/accessibility_spec.rb | 1 - 20 files changed, 104 insertions(+), 443 deletions(-) delete mode 100644 dpc-portal/config/initializers/devise.rb diff --git a/dpc-portal/Gemfile b/dpc-portal/Gemfile index 4ec92c1d20..8c176c10b4 100644 --- a/dpc-portal/Gemfile +++ b/dpc-portal/Gemfile @@ -23,9 +23,6 @@ gem 'auto-session-timeout' gem 'aws-sdk-cloudwatch' gem 'bootsnap', '>= 1.4.2', require: false gem 'bundler', '>= 1.15.0' -gem 'devise', '>= 4.9.3' -gem 'devise-async' -gem 'devise-security' gem 'dotenv-rails', groups: %i[development test] gem 'fhir_models' gem 'health_check' diff --git a/dpc-portal/Gemfile.lock b/dpc-portal/Gemfile.lock index bbe86bfaf8..2696e57fa9 100644 --- a/dpc-portal/Gemfile.lock +++ b/dpc-portal/Gemfile.lock @@ -128,13 +128,12 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) - base64 (0.2.0) + base64 (0.3.0) bcp47 (0.3.3) i18n - bcrypt (3.1.20) - benchmark (0.4.1) - bigdecimal (3.1.8) - bindata (2.5.0) + benchmark (0.5.0) + bigdecimal (3.3.1) + bindata (2.5.1) bootsnap (1.18.4) msgpack (~> 1.2) builder (3.3.0) @@ -158,29 +157,18 @@ GEM coderay (1.1.3) coercible (1.0.0) descendants_tracker (~> 0.0.1) - concurrent-ruby (1.3.4) - connection_pool (2.4.1) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) crack (1.0.0) bigdecimal rexml crass (1.0.6) css_parser (1.17.1) addressable - date (3.4.1) + date (3.5.1) date_time_precision (0.8.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devise (4.9.4) - bcrypt (~> 3.0) - orm_adapter (~> 0.1) - railties (>= 4.1.0) - responders - warden (~> 1.2.3) - devise-async (1.0.0) - activejob (>= 5.0) - devise (>= 4.0) - devise-security (0.18.0) - devise (>= 4.3.0) diff-lcs (1.5.1) docile (1.4.1) dotenv (3.1.2) @@ -202,13 +190,14 @@ GEM factory_bot (~> 6.4) railties (>= 5.0.0) fakefs (2.5.0) - faraday (2.10.1) - faraday-net_http (>= 2.0, < 3.2) + faraday (2.14.0) + faraday-net_http (>= 2.0, < 3.5) + json logger - faraday-follow_redirects (0.3.0) + faraday-follow_redirects (0.4.0) faraday (>= 1, < 3) - faraday-net_http (3.1.1) - net-http + faraday-net_http (3.4.2) + net-http (~> 0.5) ffi (1.17.0) fhir_models (4.3.0) bcp47 (>= 0.3) @@ -221,12 +210,13 @@ GEM globalid (1.2.1) activesupport (>= 6.1) hashdiff (1.1.1) - hashie (5.0.0) + hashie (5.1.0) + logger health_check (3.1.0) railties (>= 5.0) htmlbeautifier (1.4.3) htmlentities (4.3.4) - i18n (1.14.6) + i18n (1.14.8) concurrent-ruby (~> 1.0) ice_nine (0.11.2) io-console (0.8.1) @@ -239,7 +229,7 @@ GEM activesupport (>= 5.0.0) jmespath (1.6.2) json (2.9.0) - json-jwt (1.16.6) + json-jwt (1.17.0) activesupport (>= 4.2) aes_key_wrap base64 @@ -265,7 +255,7 @@ GEM listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.0) + logger (1.7.0) lograge (0.14.0) actionpack (>= 4) activesupport (>= 4) @@ -291,7 +281,8 @@ GEM multi_json (~> 1.10) rbnacl (~> 5.0) rbnacl-libsodium (~> 1.0) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop @@ -304,23 +295,24 @@ GEM mime-types-data (3.2024.0820) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.25.5) + minitest (6.0.0) + prism (~> 1.5) msgpack (1.7.2) multi_json (1.15.0) multi_xml (0.7.1) bigdecimal (~> 3.1) mustermann (3.0.4) ruby2_keywords (~> 0.0.1) - net-http (0.4.1) - uri - net-imap (0.5.8) + net-http (0.9.1) + uri (>= 0.11.1) + net-imap (0.6.2) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol newrelic_rpm (8.16.0) nio4r (2.7.3) @@ -334,8 +326,9 @@ GEM rack (>= 1.2, < 4) snaky_hash (~> 2.0) version_gem (~> 1.1) - omniauth (2.1.2) + omniauth (2.1.4) hashie (>= 3.4.6) + logger rack (>= 2.2.3) rack-protection omniauth-rails_csrf_protection (1.0.2) @@ -344,7 +337,7 @@ GEM omniauth_openid_connect (0.8.0) omniauth (>= 1.9, < 3) openid_connect (~> 2.2) - openid_connect (2.3.0) + openid_connect (2.3.1) activemodel attr_required (>= 1.0.0) email_validator @@ -357,7 +350,6 @@ GEM tzinfo validate_url webfinger (~> 2.0) - orm_adapter (0.5.0) ostruct (0.6.0) parallel (1.26.3) parser (3.3.6.0) @@ -370,6 +362,7 @@ GEM pp (0.6.2) prettyprint prettyprint (0.2.0) + prism (1.7.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -378,13 +371,13 @@ GEM psych (5.2.6) date stringio - public_suffix (6.0.1) + public_suffix (6.0.2) puma (6.4.3) nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) - rack (3.2.3) - rack-oauth2 (2.2.1) + rack (3.2.4) + rack-oauth2 (2.3.0) activesupport attr_required faraday (~> 2.0) @@ -439,9 +432,6 @@ GEM io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) - responders (3.1.1) - actionpack (>= 5.2) - railties (>= 5.2) rexml (3.4.4) rouge (4.3.0) rspec-core (3.13.0) @@ -536,7 +526,7 @@ GEM thread_safe (0.3.6) tilt (2.4.0) timecop (0.9.10) - timeout (0.4.3) + timeout (0.6.0) truemail (3.3.1) simpleidn (~> 0.2.1) tzinfo (2.0.6) @@ -546,7 +536,7 @@ GEM unicode-display_width (3.1.2) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) - uri (1.0.4) + uri (1.1.1) useragent (0.16.11) validate_url (1.0.15) activemodel (>= 3.0.0) @@ -560,8 +550,6 @@ GEM axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) - warden (1.2.9) - rack (>= 2.0.9) webdrivers (5.3.1) nokogiri (~> 1.6) rubyzip (>= 1.3.0) @@ -609,9 +597,6 @@ DEPENDENCIES byebug capybara climate_control - devise (>= 4.9.3) - devise-async - devise-security dotenv-rails factory_bot_rails fakefs diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index e47488dc32..b2b86f4d2b 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -2,6 +2,7 @@ # Parent class of all controllers class ApplicationController < ActionController::Base + attr_accessor :current_user IDP_HOST = ENV.fetch('IDP_HOST') IDP_CLIENT_ID = "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV.fetch('ENV')}".freeze @@ -11,6 +12,17 @@ class ApplicationController < ActionController::Base auto_session_timeout User.timeout_in + def active_url + '/active' + end + + def current_user + @current_user = User.where(id: session['user']).first + end + + def authenticate_user! + redirect_to new_user_session_path unless current_user + end private def check_user_verification diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index 56a88d20ca..b7bdc55a09 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Handles interactions with login.gov -class LoginDotGovController < Devise::OmniauthCallbacksController +class LoginDotGovController skip_before_action :verify_authenticity_token, only: :openid_connect def openid_connect diff --git a/dpc-portal/app/controllers/users/sessions_controller.rb b/dpc-portal/app/controllers/users/sessions_controller.rb index b79ab1c66c..f87349d2fd 100644 --- a/dpc-portal/app/controllers/users/sessions_controller.rb +++ b/dpc-portal/app/controllers/users/sessions_controller.rb @@ -1,15 +1,27 @@ # frozen_string_literal: true module Users - # Adds functionality to devise session controller - class SessionsController < Devise::SessionsController - auto_session_timeout_actions + class SessionsController < ApplicationController + def active + render_session_status + end + def timeout + render_session_timeout + end + def create + user_info = request.env['omniauth.auth'] + if user_info.provider == 'developer' + user = User.where(email: user_info.info.email).first + session['user'] = user.id if user + end + render plain: :foo + end def destroy Rails.logger.info(['User logged out', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoggedOut }]) - sign_out(current_user) + session.delete('user') redirect_to url_for_login_dot_gov_logout, allow_other_host: true end end diff --git a/dpc-portal/app/models/invitation.rb b/dpc-portal/app/models/invitation.rb index 792ecbbdf4..af38ee1170 100644 --- a/dpc-portal/app/models/invitation.rb +++ b/dpc-portal/app/models/invitation.rb @@ -4,7 +4,7 @@ class Invitation < ApplicationRecord validates :invited_by, :invited_given_name, :invited_family_name, presence: true, if: :needs_validation? validates :invited_email, :invited_email_confirmation, presence: true, if: :new_record? - validates :invited_email, format: Devise.email_regexp, confirmation: true, if: :new_record? + validates :invited_email, format: URI::MailTo::EMAIL_REGEXP, confirmation: true, if: :new_record? validates :invitation_type, presence: true validate :cannot_cancel_accepted validate :check_if_duplicate, if: :new_record? diff --git a/dpc-portal/app/models/user.rb b/dpc-portal/app/models/user.rb index 8d1edcc031..d6cef778ba 100644 --- a/dpc-portal/app/models/user.rb +++ b/dpc-portal/app/models/user.rb @@ -2,12 +2,6 @@ # Base user class class User < ApplicationRecord - # Include default devise modules. Others available are: - # :confirmable, :lockable, and :trackable - devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :validatable, - :timeoutable, :omniauthable, omniauth_providers: [:openid_connect] - audited only: %i[verification_reason verification_status], on: :update validates :verification_reason, allow_nil: true, allow_blank: true, @@ -21,12 +15,17 @@ class User < ApplicationRecord enum :verification_reason, %i[ao_med_sanction_waived ao_med_sanctions] enum :verification_status, %i[approved rejected] - before_validation(on: :create) do - # Assign random, acceptable password to keep Devise happy. - # User should log in only through IdP - self.password = Devise.friendly_token[0, 20] unless password.present? + def self.remember_for + 12.hours + end + + def self.timeout_in + 30.minutes end + def timeout_in + self.class.timeout_in + end def provider_links ao_org_links.includes(:provider_organization) + cd_org_links.where(disabled_at: nil).includes(:provider_organization) diff --git a/dpc-portal/app/views/layouts/application.html.erb b/dpc-portal/app/views/layouts/application.html.erb index edaa440f11..abd97bcccb 100644 --- a/dpc-portal/app/views/layouts/application.html.erb +++ b/dpc-portal/app/views/layouts/application.html.erb @@ -26,7 +26,7 @@ - <% if user_signed_in? %> + <% if !!current_user %> <%= auto_session_timeout_js %> <% end %> diff --git a/dpc-portal/config/initializers/devise.rb b/dpc-portal/config/initializers/devise.rb deleted file mode 100644 index 2660592a7f..0000000000 --- a/dpc-portal/config/initializers/devise.rb +++ /dev/null @@ -1,342 +0,0 @@ -# frozen_string_literal: true - -# Assuming you have not yet modified this file, each configuration option below -# is set to its default value. Note that some are commented out while others -# are not: uncommented lines are intended to protect your configuration from -# breaking changes in upgrades (i.e., in the event that future versions of -# Devise change the default values for those options). -# -# Use this hook to configure devise mailer, warden hooks and so forth. -# Many of these configuration options can be set straight in your model. - -require "dpc_portal_utils" - -Devise.setup do |config| - include DpcPortalUtils - begin - private_key = OpenSSL::PKey::RSA.new(ENV['LOGIN_GOV_PRIVATE_KEY']) - rescue TypeError, OpenSSL::PKey::RSAError => e - Rails.logger.error("Unable to create private key for omniauth: #{e}") - private_key = OpenSSL::PKey::RSA.new(1024) - end - idp_host = ENV.fetch('IDP_HOST', 'idp.int.identitysandbox.gov') - config.omniauth :openid_connect, { - name: :openid_connect, - issuer: "https://#{idp_host}/", - discovery: true, - scope: %i[openid email all_emails], - response_type: :code, - acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', - client_auth_method: :jwt_bearer, - client_options: { - port: 443, - scheme: 'https', - host: idp_host, - identifier: "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV['ENV']}", - private_key: private_key, - redirect_uri: "#{my_protocol_host}/portal/users/auth/openid_connect/callback" - } - } - # The secret key used by Devise. Devise uses this key to generate - # random tokens. Changing this key will render invalid all existing - # confirmation, reset password and unlock tokens in the database. - # Devise will use the `secret_key_base` as its `secret_key` - # by default. You can change it below and use your own secret key. - # config.secret_key = '7c6c088e32429add964776c6270e102769c71e4be2817e0e198857372f78142dfde431b4c94fdda416eaf8b5b958710f5f7fbb9cec18944159d76ec1b3b23d80' - config.secret_key = Rails.application.secret_key_base - - # ==> Controller configuration - # Configure the parent class to the devise controllers. - # config.parent_controller = 'DeviseController' - - # ==> Mailer Configuration - # Configure the e-mail address which will be shown in Devise::Mailer, - # note that it will be overwritten if you use your own mailer class - # with default "from" parameter. - config.mailer_sender = 'dpcinfo@cms.hhs.gov' - - # Configure the class responsible to send e-mails. - # config.mailer = 'Devise::Mailer' - - # Configure the parent class responsible to send e-mails. - # config.parent_mailer = 'ActionMailer::Base' - - # ==> ORM configuration - # Load and configure the ORM. Supports :active_record (default) and - # :mongoid (bson_ext recommended) by default. Other ORMs may be - # available as additional gems. - require 'devise/orm/active_record' - - # ==> Configuration for any authentication mechanism - # Configure which keys are used when authenticating a user. The default is - # just :email. You can configure it to use [:username, :subdomain], so for - # authenticating a user, both parameters are required. Remember that those - # parameters are used only when authenticating and not when retrieving from - # session. If you need permissions, you should implement that in a before filter. - # You can also supply a hash where the value is a boolean determining whether - # or not authentication should be aborted when the value is not present. - # config.authentication_keys = [:email] - - # Configure parameters from the request object used for authentication. Each entry - # given should be a request method and it will automatically be passed to the - # find_for_authentication method and considered in your model lookup. For instance, - # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. - # The same considerations mentioned for authentication_keys also apply to request_keys. - # config.request_keys = [] - - # Configure which authentication keys should be case-insensitive. - # These keys will be downcased upon creating or modifying a user and when used - # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [:email] - - # Configure which authentication keys should have whitespace stripped. - # These keys will have whitespace before and after removed upon creating or - # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [:email] - - # Tell if authentication through request.params is enabled. True by default. - # It can be set to an array that will enable params authentication only for the - # given strategies, for example, `config.params_authenticatable = [:database]` will - # enable it only for database (email + password) authentication. - # config.params_authenticatable = true - - # Tell if authentication through HTTP Auth is enabled. False by default. - # It can be set to an array that will enable http authentication only for the - # given strategies, for example, `config.http_authenticatable = [:database]` will - # enable it only for database authentication. - # For API-only applications to support authentication "out-of-the-box", you will likely want to - # enable this with :database unless you are using a custom strategy. - # The supported strategies are: - # :database = Support basic authentication with authentication key + password - # config.http_authenticatable = false - - # If 401 status code should be returned for AJAX requests. True by default. - # config.http_authenticatable_on_xhr = true - - # The realm used in Http Basic Authentication. 'Application' by default. - # config.http_authentication_realm = 'Application' - - # It will change confirmation, password recovery and other workflows - # to behave the same regardless if the e-mail provided was right or wrong. - # Does not affect registerable. - # config.paranoid = true - - # By default Devise will store the user in session. You can skip storage for - # particular strategies by setting this option. - # Notice that if you are skipping storage for all authentication paths, you - # may want to disable generating routes to Devise's sessions controller by - # passing skip: :sessions to `devise_for` in your config/routes.rb - config.skip_session_storage = [:http_auth] - - # By default, Devise cleans up the CSRF token on authentication to - # avoid CSRF token fixation attacks. This means that, when using AJAX - # requests for sign in and sign up, you need to get a new CSRF token - # from the server. You can disable this option at your own risk. - # config.clean_up_csrf_token_on_authentication = true - - # When false, Devise will not attempt to reload routes on eager load. - # This can reduce the time taken to boot the app but if your application - # requires the Devise mappings to be loaded during boot time the application - # won't boot properly. - # config.reload_routes = true - - # ==> Configuration for :database_authenticatable - # For bcrypt, this is the cost for hashing the password and defaults to 12. If - # using other algorithms, it sets how many times you want the password to be hashed. - # The number of stretches used for generating the hashed password are stored - # with the hashed password. This allows you to change the stretches without - # invalidating existing passwords. - # - # Limiting the stretches to just one in testing will increase the performance of - # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use - # a value less than 10 in other environments. Note that, for bcrypt (the default - # algorithm), the cost increases exponentially with the number of stretches (e.g. - # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). - config.stretches = Rails.env.test? ? 1 : 12 - - # Set up a pepper to generate the hashed password. - # config.pepper = '7ce8080788d624d2ae1b2a8a5487f6cf9231aea379e6c8bd8c83c8e25f7cd268f72429a8170edcd324fab1af5aa3a5215faaa7f407e1db155408e6e7fe88dc0e' - - # Send a notification to the original email when the user's email is changed. - # config.send_email_changed_notification = false - - # Send a notification email when the user's password is changed. - # config.send_password_change_notification = false - - # ==> Configuration for :confirmable - # A period that the user is allowed to access the website even without - # confirming their account. For instance, if set to 2.days, the user will be - # able to access the website for two days without confirming their account, - # access will be blocked just in the third day. - # You can also set it to nil, which will allow the user to access the website - # without confirming their account. - # Default is 0.days, meaning the user cannot access the website without - # confirming their account. - # config.allow_unconfirmed_access_for = 2.days - - # A period that the user is allowed to confirm their account before their - # token becomes invalid. For example, if set to 3.days, the user can confirm - # their account within 3 days after the mail was sent, but on the fourth day - # their account can't be confirmed with the token any more. - # Default is nil, meaning there is no restriction on how long a user can take - # before confirming their account. - # config.confirm_within = 3.days - - # If true, requires any email changes to be confirmed (exactly the same way as - # initial account confirmation) to be applied. Requires additional unconfirmed_email - # db field (see migrations). Until confirmed, new email is stored in - # unconfirmed_email column, and copied to email column on successful confirmation. - config.reconfirmable = true - - # Defines which key will be used when confirming an account - # config.confirmation_keys = [:email] - - # ==> Configuration for :rememberable - # The time the user will be remembered without asking for credentials again. - config.remember_for = 12.hours - - # Invalidates all the remember me tokens when the user signs out. - config.expire_all_remember_me_on_sign_out = true - - # If true, extends the user's remember period when remembered via cookie. - # config.extend_remember_period = false - - # Options to be passed to the created cookie. For instance, you can set - # secure: true in order to force SSL only cookies. - # config.rememberable_options = {} - - # ==> Configuration for :validatable - # Range for password length. - config.password_length = 6..128 - - # Email regex used to validate email formats. It simply asserts that - # one (and only one) @ exists in the given string. This is mainly - # to give user feedback and not to assert the e-mail validity. - config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ - - # ==> Configuration for :timeoutable - # The time you want to timeout the user session without activity. After this - # time the user will be asked for credentials again. Default is 30 minutes. - config.timeout_in = 30.minutes - - # ==> Configuration for :lockable - # Defines which strategy will be used to lock an account. - # :failed_attempts = Locks an account after a number of failed attempts to sign in. - # :none = No lock strategy. You should handle locking by yourself. - # config.lock_strategy = :failed_attempts - - # Defines which key will be used when locking and unlocking an account - # config.unlock_keys = [:email] - - # Defines which strategy will be used to unlock an account. - # :email = Sends an unlock link to the user email - # :time = Re-enables login after a certain amount of time (see :unlock_in below) - # :both = Enables both strategies - # :none = No unlock strategy. You should handle unlocking by yourself. - # config.unlock_strategy = :both - - # Number of authentication tries before locking an account if lock_strategy - # is failed attempts. - # config.maximum_attempts = 20 - - # Time interval to unlock the account if :time is enabled as unlock_strategy. - # config.unlock_in = 1.hour - - # Warn on the last attempt before the account is locked. - # config.last_attempt_warning = true - - # ==> Configuration for :recoverable - # - # Defines which key will be used when recovering the password for an account - # config.reset_password_keys = [:email] - - # Time interval you can reset your password with a reset password key. - # Don't put a too small interval or your users won't have the time to - # change their passwords. - config.reset_password_within = 6.hours - - # When set to false, does not sign a user in automatically after their password is - # reset. Defaults to true, so a user is signed in automatically after a reset. - # config.sign_in_after_reset_password = true - - # ==> Configuration for :encryptable - # Allow you to use another hashing or encryption algorithm besides bcrypt (default). - # You can use :sha1, :sha512 or algorithms from others authentication tools as - # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 - # for default behavior) and :restful_authentication_sha1 (then you should set - # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). - # - # Require the `devise-encryptable` gem when using anything other than bcrypt - # config.encryptor = :sha512 - - # ==> Scopes configuration - # Turn scoped views on. Before rendering "sessions/new", it will first check for - # "users/sessions/new". It's turned off by default because it's slower if you - # are using only default views. - # config.scoped_views = false - - # Configure the default scope given to Warden. By default it's the first - # devise role declared in your routes (usually :user). - # config.default_scope = :user - - # Set this configuration to false if you want /users/sign_out to sign out - # only the current scope. By default, Devise signs out all scopes. - # config.sign_out_all_scopes = true - - # ==> Navigation configuration - # Lists the formats that should be treated as navigational. Formats like - # :html should redirect to the sign in page when the user does not have - # access, but formats like :xml or :json, should return 401. - # - # If you have any extra navigational formats, like :iphone or :mobile, you - # should add them to the navigational formats lists. - # - # The "*/*" below is required to match Internet Explorer requests. - config.navigational_formats = ['*/*', :html, :turbo_stream] - - # The default HTTP method used to sign out a resource. Default is :delete. - config.sign_out_via = :delete - - # ==> OmniAuth - # Add a new OmniAuth provider. Check the wiki for more information on setting - # up on your models and hooks. - # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' - - # ==> Warden configuration - # If you want to use other strategies, that are not supported by Devise, or - # change the failure app, you can configure them inside the config.warden block. - # - # config.warden do |manager| - # manager.intercept_401 = false - # manager.default_strategies(scope: :user).unshift :some_external_strategy - # end - - # ==> Mountable engine configurations - # When using Devise inside an engine, let's call it `MyEngine`, and this engine - # is mountable, there are some extra configurations to be taken into account. - # The following options are available, assuming the engine is mounted as: - # - # mount MyEngine, at: '/my_engine' - # - # The router that invoked `devise_for`, in the example above, would be: - # config.router_name = :my_engine - # - # When using OmniAuth, Devise cannot automatically set OmniAuth path, - # so you need to do it manually. For the users scope, it would be: - # config.omniauth_path_prefix = '/my_engine/users/auth' - - # ==> Hotwire/Turbo configuration - # When using Devise with Hotwire/Turbo, the http status for error responses - # and some redirects must match the following. The default in Devise for existing - # apps is `200 OK` and `302 Found respectively`, but new apps are generated with - # these new defaults that match Hotwire/Turbo behavior. - # Note: These might become the new default in future versions of Devise. - config.responder.error_status = :unprocessable_entity - config.responder.redirect_status = :see_other - - # ==> Configuration for :registerable - - # When set to false, does not sign a user in automatically after their password is - # changed. Defaults to true, so a user is signed in automatically after changing a password. - # config.sign_in_after_change_password = true -end diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index 9cd3af92b6..3b7a56d9f7 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -5,16 +5,6 @@ # and config.ru via config.relative_url_root. # Rails.application.routes.draw do - devise_for :users, controllers: { sessions: 'users/sessions', omniauth_callbacks: 'login_dot_gov' } - devise_scope :user do - get '/users/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' - get '/users/auth/logged_out', to: 'login_dot_gov#logged_out' - get '/users/auth/no_account', to: 'login_dot_gov#no_account', as: 'no_account' - delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' - get 'active', to: 'users/sessions#active' - get 'timeout', to: 'users/sessions#timeout' - end - # Defines the root path route ("/") root 'organizations#index' @@ -23,7 +13,12 @@ # However, to complete the mimicing, it uses the Rails.application.routes.recognize_path # method, which does not work correctly for applications served on a subpath. match '/portal', to: 'organizations#index', via: :get - + delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' + get '/auth/:provider/callback', to: 'users/sessions#create' + get '/users/sign_in', to: 'users/sessions#new', as: 'new_user_session' + delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' + get 'active', to: 'users/sessions#active', as: 'active' + get 'timeout', to: 'users/sessions#timeout', as: 'timeout' resources :organizations, only: [:index, :show, :new, :create] do resources :client_tokens, only: [:new, :create, :destroy] resources :public_keys, only: [:new, :create, :destroy] diff --git a/dpc-portal/spec/rails_helper.rb b/dpc-portal/spec/rails_helper.rb index 0237ab7e98..29999c198a 100644 --- a/dpc-portal/spec/rails_helper.rb +++ b/dpc-portal/spec/rails_helper.rb @@ -10,6 +10,7 @@ require 'capybara/rails' require 'support/component_support' require 'support/dpc_client_support' +require 'support/login_support' require 'support/match_html_fragment' # Add additional requires below this line. Rails is not loaded until this point! require 'view_component/test_helpers' @@ -37,10 +38,7 @@ end RSpec.configure do |config| config.include FactoryBot::Syntax::Methods - - # Devise test helpers - config.include Devise::Test::IntegrationHelpers, type: :request - + config.include Rails.application.routes.url_helpers # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_paths = ["#{Rails.root}/spec/fixtures"] diff --git a/dpc-portal/spec/requests/application_spec.rb b/dpc-portal/spec/requests/application_spec.rb index 29a05cb231..6fd1d5666a 100644 --- a/dpc-portal/spec/requests/application_spec.rb +++ b/dpc-portal/spec/requests/application_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' RSpec.describe 'Application', type: :request do + include LoginSupport before(:all) do Rails.application.routes.disable_clear_and_finalize = true Rails.application.routes.draw do @@ -57,8 +58,7 @@ end class TestController < ApplicationController - before_action :authenticate_user! def index - render plain: 'foo' + render plain: :foo end end diff --git a/dpc-portal/spec/requests/client_tokens_spec.rb b/dpc-portal/spec/requests/client_tokens_spec.rb index 23f61aa3db..a5d8560528 100644 --- a/dpc-portal/spec/requests/client_tokens_spec.rb +++ b/dpc-portal/spec/requests/client_tokens_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'ClientTokens', type: :request do include DpcClientSupport + include LoginSupport let(:terms_of_service_accepted_by) { create(:user) } @@ -17,7 +18,7 @@ context 'not logged in' do it 'redirects to login' do get '/organizations/no-such-id/client_tokens/new' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -157,7 +158,7 @@ context 'not logged in' do it 'redirects to login' do post '/organizations/no-such-id/client_tokens' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -222,7 +223,7 @@ context 'not logged in' do it 'redirects to login' do delete '/organizations/no-such-id/client_tokens/no-such-id' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end diff --git a/dpc-portal/spec/requests/credential_delegate_invitations_spec.rb b/dpc-portal/spec/requests/credential_delegate_invitations_spec.rb index e6297b55af..f052ccde1c 100644 --- a/dpc-portal/spec/requests/credential_delegate_invitations_spec.rb +++ b/dpc-portal/spec/requests/credential_delegate_invitations_spec.rb @@ -4,12 +4,13 @@ RSpec.describe 'CredentialDelegateInvitations', type: :request do include DpcClientSupport + include LoginSupport describe 'GET /new' do context 'not logged in' do it 'redirects to login' do get '/organizations/no-such-id/credential_delegate_invitations/new' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index d261a66e01..b5dc697d3f 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -3,6 +3,7 @@ require 'rails_helper' RSpec.describe 'Invitations', type: :request do + include LoginSupport RSpec.shared_examples 'an invitation endpoint' do |method, path_suffix| let(:org) { invitation.provider_organization } let(:bad_org) { create(:provider_organization) } diff --git a/dpc-portal/spec/requests/ip_addresses_spec.rb b/dpc-portal/spec/requests/ip_addresses_spec.rb index 1de08c1dd7..28236fd1e7 100644 --- a/dpc-portal/spec/requests/ip_addresses_spec.rb +++ b/dpc-portal/spec/requests/ip_addresses_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'IpAddresses', type: :request do include DpcClientSupport + include LoginSupport let(:terms_of_service_accepted_by) { create(:user) } @@ -17,7 +18,7 @@ context 'not logged in' do it 'redirects to login' do get '/organizations/no-such-id/ip_addresses/new' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -157,7 +158,7 @@ context 'not logged in' do it 'redirects to login' do post '/organizations/no-such-id/ip_addresses' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -242,7 +243,7 @@ context 'not logged in' do it 'redirects to login' do delete '/organizations/no-such-id/ip_addresses/no-such-id' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end diff --git a/dpc-portal/spec/requests/organizations_spec.rb b/dpc-portal/spec/requests/organizations_spec.rb index aca1b72341..d7a1671d5a 100644 --- a/dpc-portal/spec/requests/organizations_spec.rb +++ b/dpc-portal/spec/requests/organizations_spec.rb @@ -5,12 +5,13 @@ RSpec.describe 'Organizations', type: :request do include DpcClientSupport include ComponentSupport + include LoginSupport describe 'GET /index' do context 'not logged in' do it 'redirects to login' do get '/organizations' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -56,7 +57,7 @@ it 'redirects to login' do org = create(:provider_organization) get "/organizations/#{org.id}" - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end diff --git a/dpc-portal/spec/requests/public_keys_spec.rb b/dpc-portal/spec/requests/public_keys_spec.rb index 49aed675eb..c58ec7e94a 100644 --- a/dpc-portal/spec/requests/public_keys_spec.rb +++ b/dpc-portal/spec/requests/public_keys_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'PublicKeys', type: :request do include DpcClientSupport + include LoginSupport let(:terms_of_service_accepted_by) { create(:user) } @@ -21,7 +22,7 @@ context 'not logged in' do it 'redirects to login' do get '/organizations/no-such-id/public_keys/new' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -161,7 +162,7 @@ context 'not logged in' do it 'redirects to login' do post '/organizations/no-such-id/public_keys' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -263,7 +264,7 @@ context 'not logged in' do it 'redirects to login' do delete '/organizations/no-such-id/public_keys/no-such-id' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end diff --git a/dpc-portal/spec/requests/users/sessions_spec.rb b/dpc-portal/spec/requests/users/sessions_spec.rb index 66b358ec27..04a974d751 100644 --- a/dpc-portal/spec/requests/users/sessions_spec.rb +++ b/dpc-portal/spec/requests/users/sessions_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe 'Users::Sessions', type: :request do +RSpec.describe 'Sessions', type: :request do describe 'logout' do context 'logged in' do let!(:user) { create(:user) } diff --git a/dpc-portal/spec/system/accessibility_spec.rb b/dpc-portal/spec/system/accessibility_spec.rb index 3e2ed447ad..4aa21cddcf 100644 --- a/dpc-portal/spec/system/accessibility_spec.rb +++ b/dpc-portal/spec/system/accessibility_spec.rb @@ -3,7 +3,6 @@ require 'rails_helper' RSpec.describe 'Accessibility', type: :system do - include Devise::Test::IntegrationHelpers include DpcClientSupport before do driven_by(:selenium_headless) From b38beaa774196536f4901d25cb690da9d56f5e04 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Fri, 26 Dec 2025 12:59:58 -0800 Subject: [PATCH 02/48] working except in tests --- .../app/controllers/application_controller.rb | 2 +- .../controllers/users/sessions_controller.rb | 6 ++- dpc-portal/app/helpers/application_helper.rb | 9 ++++ .../{devise => users}/sessions/new.html.erb | 2 +- dpc-portal/config/initializers/omniauth.rb | 54 +++++++++++++++++++ dpc-portal/config/routes.rb | 1 + .../spec/helpers/application_helper_spec.rb | 15 ++++++ dpc-portal/spec/rails_helper.rb | 3 +- dpc-portal/spec/requests/application_spec.rb | 1 + .../spec/requests/client_tokens_spec.rb | 5 +- dpc-portal/spec/support/login_support.rb | 7 +++ 11 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 dpc-portal/app/helpers/application_helper.rb rename dpc-portal/app/views/{devise => users}/sessions/new.html.erb (65%) create mode 100644 dpc-portal/config/initializers/omniauth.rb create mode 100644 dpc-portal/spec/helpers/application_helper_spec.rb create mode 100644 dpc-portal/spec/support/login_support.rb diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index b2b86f4d2b..8ed0db2bb8 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -49,7 +49,7 @@ def url_for_login_dot_gov_logout URI::HTTPS.build(host: IDP_HOST, path: '/openid_connect/logout', query: { client_id: IDP_CLIENT_ID, - post_logout_redirect_uri: "#{root_url}users/auth/logged_out", + post_logout_redirect_uri: "#{root_url}auth/logged_out", state: }.to_query) end diff --git a/dpc-portal/app/controllers/users/sessions_controller.rb b/dpc-portal/app/controllers/users/sessions_controller.rb index f87349d2fd..c393aec230 100644 --- a/dpc-portal/app/controllers/users/sessions_controller.rb +++ b/dpc-portal/app/controllers/users/sessions_controller.rb @@ -11,11 +11,13 @@ def timeout end def create user_info = request.env['omniauth.auth'] - if user_info.provider == 'developer' + if user_info.provider == 'developer' && !Rails.env.test? + Rails.logger.warn('Trying to log in via developer provider outside test') + else user = User.where(email: user_info.info.email).first session['user'] = user.id if user end - render plain: :foo + redirect_to(session.delete(:user_return_to) || organizations_path) end def destroy Rails.logger.info(['User logged out', diff --git a/dpc-portal/app/helpers/application_helper.rb b/dpc-portal/app/helpers/application_helper.rb new file mode 100644 index 0000000000..e2910af266 --- /dev/null +++ b/dpc-portal/app/helpers/application_helper.rb @@ -0,0 +1,9 @@ +module ApplicationHelper + def current_user + @current_user + end + + def omniauth_authorize_path(service) + return "/portal/auth/#{service}" + end +end diff --git a/dpc-portal/app/views/devise/sessions/new.html.erb b/dpc-portal/app/views/users/sessions/new.html.erb similarity index 65% rename from dpc-portal/app/views/devise/sessions/new.html.erb rename to dpc-portal/app/views/users/sessions/new.html.erb index 85a247e2b0..39c6b28440 100644 --- a/dpc-portal/app/views/devise/sessions/new.html.erb +++ b/dpc-portal/app/views/users/sessions/new.html.erb @@ -1 +1 @@ -<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:user, :openid_connect))) %> +<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:ldg_ial1))) %> diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb new file mode 100644 index 0000000000..8029e77047 --- /dev/null +++ b/dpc-portal/config/initializers/omniauth.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Configure omniauth providers + +require "dpc_portal_utils" + +include DpcPortalUtils + +Rails.application.config.middleware.use OmniAuth::Builder do + OmniAuth.config.logger = Rails.logger + provider :developer if Rails.env.test? + + begin + private_key = OpenSSL::PKey::RSA.new(ENV['LOGIN_GOV_PRIVATE_KEY']) + rescue TypeError, OpenSSL::PKey::RSAError => e + Rails.logger.error("Unable to create private key for omniauth: #{e}") + private_key = OpenSSL::PKey::RSA.new(1024) + end + idp_host = ENV.fetch('IDP_HOST', 'idp.int.identitysandbox.gov') + provider :openid_connect, { + name: :ldg_ial1, + issuer: "https://#{idp_host}/", + discovery: true, + scope: %i[openid email all_emails], + response_type: :code, + acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', + client_auth_method: :jwt_bearer, + client_options: { + port: 443, + scheme: 'https', + host: idp_host, + identifier: "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV['ENV']}", + private_key: private_key, + redirect_uri: "#{my_protocol_host}/portal/auth/ldg_ial1/callback" + } + } + provider :openid_connect, { + name: :ldg_ial2, + issuer: "https://#{idp_host}/", + discovery: true, + scope: %i[openid email all_emails profile social_security_number], + response_type: :code, + acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', + client_auth_method: :jwt_bearer, + client_options: { + port: 443, + scheme: 'https', + host: idp_host, + identifier: "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV['ENV']}", + private_key: private_key, + redirect_uri: "#{my_protocol_host}/portal/auth/ldg_ial2/callback" + } + } +end diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index 3b7a56d9f7..76aa4e0240 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -18,6 +18,7 @@ get '/users/sign_in', to: 'users/sessions#new', as: 'new_user_session' delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' get 'active', to: 'users/sessions#active', as: 'active' + get '/auth/logged_out', to: redirect('/portal/users/sign_in') get 'timeout', to: 'users/sessions#timeout', as: 'timeout' resources :organizations, only: [:index, :show, :new, :create] do resources :client_tokens, only: [:new, :create, :destroy] diff --git a/dpc-portal/spec/helpers/application_helper_spec.rb b/dpc-portal/spec/helpers/application_helper_spec.rb new file mode 100644 index 0000000000..391b114ab2 --- /dev/null +++ b/dpc-portal/spec/helpers/application_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ApplicationHelper. For example: +# +# describe ApplicationHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe ApplicationHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/dpc-portal/spec/rails_helper.rb b/dpc-portal/spec/rails_helper.rb index 29999c198a..e326678988 100644 --- a/dpc-portal/spec/rails_helper.rb +++ b/dpc-portal/spec/rails_helper.rb @@ -10,7 +10,6 @@ require 'capybara/rails' require 'support/component_support' require 'support/dpc_client_support' -require 'support/login_support' require 'support/match_html_fragment' # Add additional requires below this line. Rails is not loaded until this point! require 'view_component/test_helpers' @@ -38,7 +37,7 @@ end RSpec.configure do |config| config.include FactoryBot::Syntax::Methods - config.include Rails.application.routes.url_helpers + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_paths = ["#{Rails.root}/spec/fixtures"] diff --git a/dpc-portal/spec/requests/application_spec.rb b/dpc-portal/spec/requests/application_spec.rb index 6fd1d5666a..eff36d8bce 100644 --- a/dpc-portal/spec/requests/application_spec.rb +++ b/dpc-portal/spec/requests/application_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' RSpec.describe 'Application', type: :request do include LoginSupport diff --git a/dpc-portal/spec/requests/client_tokens_spec.rb b/dpc-portal/spec/requests/client_tokens_spec.rb index a5d8560528..f538d603d3 100644 --- a/dpc-portal/spec/requests/client_tokens_spec.rb +++ b/dpc-portal/spec/requests/client_tokens_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' require 'support/credential_resource_shared_examples' RSpec.describe 'ClientTokens', type: :request do @@ -158,7 +159,7 @@ context 'not logged in' do it 'redirects to login' do post '/organizations/no-such-id/client_tokens' - expect(response).to redirect_to('/users/sign_in') + expect(response).to redirect_to('/portal/users/sign_in') end end @@ -223,7 +224,7 @@ context 'not logged in' do it 'redirects to login' do delete '/organizations/no-such-id/client_tokens/no-such-id' - expect(response).to redirect_to('/users/sign_in') + expect(response).to redirect_to('/portal/users/sign_in') end end diff --git a/dpc-portal/spec/support/login_support.rb b/dpc-portal/spec/support/login_support.rb new file mode 100644 index 0000000000..e15c498d2f --- /dev/null +++ b/dpc-portal/spec/support/login_support.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module LoginSupport + def sign_in(user) + + end +end From a48e722a3e8e0e508d6169b13ec646cf4ce0f5e4 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:32:03 -0800 Subject: [PATCH 03/48] working without devise --- .../page/utility/error_component.html.erb | 2 +- .../app/controllers/application_controller.rb | 16 +++-- .../app/controllers/invitations_controller.rb | 4 +- .../controllers/login_dot_gov_controller.rb | 9 +-- .../controllers/users/sessions_controller.rb | 22 ++---- dpc-portal/app/helpers/application_helper.rb | 5 +- dpc-portal/app/models/user.rb | 1 + .../app/views/login_dot_gov/failure.html.erb | 2 +- .../login_dot_gov/openid_connect.html.erb | 2 +- .../app/views/users/sessions/new.html.erb | 2 +- dpc-portal/config/environments/test.rb | 1 + dpc-portal/config/initializers/omniauth.rb | 23 +----- dpc-portal/config/routes.rb | 14 ++-- dpc-portal/spec/factories/users.rb | 1 + .../spec/helpers/application_helper_spec.rb | 28 +++++--- dpc-portal/spec/requests/application_spec.rb | 38 ++++------ .../spec/requests/client_tokens_spec.rb | 4 +- dpc-portal/spec/requests/invitations_spec.rb | 11 +-- .../spec/requests/login_dot_gov_spec.rb | 70 ++++++++----------- .../spec/requests/organizations_spec.rb | 1 + dpc-portal/spec/requests/public_keys_spec.rb | 1 + .../spec/requests/users/sessions_spec.rb | 18 ++++- .../auto_session_logout_service_spec.rb | 4 +- dpc-portal/spec/support/login_support.rb | 9 ++- 24 files changed, 141 insertions(+), 147 deletions(-) diff --git a/dpc-portal/app/components/page/utility/error_component.html.erb b/dpc-portal/app/components/page/utility/error_component.html.erb index 804019b951..d6255f4c3a 100644 --- a/dpc-portal/app/components/page/utility/error_component.html.erb +++ b/dpc-portal/app/components/page/utility/error_component.html.erb @@ -13,7 +13,7 @@ destination: renew_organization_invitation_path(@invitation.provider_organization, @invitation), method: :post) %> <% when :ao_accepted %> - <%= button_to new_user_session_path, class: 'usa-button', method: :get, data: { turbo: false } do %> + <%= button_to sign_in_path, class: 'usa-button', method: :get, data: { turbo: false } do %> Sign in with <% end %> <% when :cd_accepted %> diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index 8ed0db2bb8..69f2003f66 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -2,7 +2,6 @@ # Parent class of all controllers class ApplicationController < ActionController::Base - attr_accessor :current_user IDP_HOST = ENV.fetch('IDP_HOST') IDP_CLIENT_ID = "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV.fetch('ENV')}".freeze @@ -15,14 +14,23 @@ class ApplicationController < ActionController::Base def active_url '/active' end - + def current_user - @current_user = User.where(id: session['user']).first + @current_user ||= User.where(id: session['user']).first end def authenticate_user! - redirect_to new_user_session_path unless current_user + return if current_user + + flash[:alert] = t('devise.failure.unauthenticated') + session[:user_return_to] = request.path + redirect_to sign_in_path end + + def sign_in(user) + session['user'] = user.id + end + private def check_user_verification diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index 1ef10895eb..725234a38e 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -62,7 +62,7 @@ def register return unless create_link session.delete("invitation_status_#{@invitation.id}") - sign_in(:user, @user) + sign_in(@user) Rails.logger.info(['User logged in', { actionContext: LoggingConstants::ActionContext::Registration, actionType: LoggingConstants::ActionType::UserLoggedIn, @@ -80,7 +80,7 @@ def login path: '/openid_connect/authorize', query: { acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', client_id: IDP_CLIENT_ID, - redirect_uri: "#{my_protocol_host}/portal/users/auth/openid_connect/callback", + redirect_uri: "#{my_protocol_host}/portal/users/auth/login_dot_gov/callback", response_type: 'code', scope: 'openid email all_emails profile social_security_number', nonce: @nonce, diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index b7bdc55a09..489fb8446b 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Handles interactions with login.gov -class LoginDotGovController +class LoginDotGovController < ApplicationController skip_before_action :verify_authenticity_token, only: :openid_connect def openid_connect @@ -9,7 +9,7 @@ def openid_connect user = User.find_by(provider: auth.provider, uid: auth.uid) if user - sign_in(:user, user) + sign_in(user) session[:logged_in_at] = Time.now Rails.logger.info(['User logged in', { actionContext: LoggingConstants::ActionContext::Authentication, @@ -48,11 +48,6 @@ def logout redirect_to url_for_login_dot_gov_logout, allow_other_host: true end - # Return from login.gov - def logged_out - redirect_to session.delete(:user_return_to) || new_user_session_path - end - private def handle_invitation_flow_failure(invitation_id) diff --git a/dpc-portal/app/controllers/users/sessions_controller.rb b/dpc-portal/app/controllers/users/sessions_controller.rb index c393aec230..c82b22b26f 100644 --- a/dpc-portal/app/controllers/users/sessions_controller.rb +++ b/dpc-portal/app/controllers/users/sessions_controller.rb @@ -1,24 +1,10 @@ # frozen_string_literal: true module Users + # Handles session destruction class SessionsController < ApplicationController - def active - render_session_status - end + auto_session_timeout_actions - def timeout - render_session_timeout - end - def create - user_info = request.env['omniauth.auth'] - if user_info.provider == 'developer' && !Rails.env.test? - Rails.logger.warn('Trying to log in via developer provider outside test') - else - user = User.where(email: user_info.info.email).first - session['user'] = user.id if user - end - redirect_to(session.delete(:user_return_to) || organizations_path) - end def destroy Rails.logger.info(['User logged out', { actionContext: LoggingConstants::ActionContext::Authentication, @@ -26,5 +12,9 @@ def destroy session.delete('user') redirect_to url_for_login_dot_gov_logout, allow_other_host: true end + + def logged_out + redirect_to session.delete(:user_return_to) || sign_in_path + end end end diff --git a/dpc-portal/app/helpers/application_helper.rb b/dpc-portal/app/helpers/application_helper.rb index e2910af266..d1813bb99b 100644 --- a/dpc-portal/app/helpers/application_helper.rb +++ b/dpc-portal/app/helpers/application_helper.rb @@ -1,9 +1,12 @@ +# frozen_string_literal: true + +# Utility methods for views module ApplicationHelper def current_user @current_user end def omniauth_authorize_path(service) - return "/portal/auth/#{service}" + "/portal/auth/#{service}" end end diff --git a/dpc-portal/app/models/user.rb b/dpc-portal/app/models/user.rb index d6cef778ba..ced291c76b 100644 --- a/dpc-portal/app/models/user.rb +++ b/dpc-portal/app/models/user.rb @@ -26,6 +26,7 @@ def self.timeout_in def timeout_in self.class.timeout_in end + def provider_links ao_org_links.includes(:provider_organization) + cd_org_links.where(disabled_at: nil).includes(:provider_organization) diff --git a/dpc-portal/app/views/login_dot_gov/failure.html.erb b/dpc-portal/app/views/login_dot_gov/failure.html.erb index d32a17cbc9..e93aed0ef5 100644 --- a/dpc-portal/app/views/login_dot_gov/failure.html.erb +++ b/dpc-portal/app/views/login_dot_gov/failure.html.erb @@ -1 +1 @@ -

<%= @message %> <%= link_to 'Try again?', new_user_session_path %>

+

<%= @message %> <%= link_to 'Try again?', sign_in_path %>

diff --git a/dpc-portal/app/views/login_dot_gov/openid_connect.html.erb b/dpc-portal/app/views/login_dot_gov/openid_connect.html.erb index 60742beaaa..5cf6cb6485 100644 --- a/dpc-portal/app/views/login_dot_gov/openid_connect.html.erb +++ b/dpc-portal/app/views/login_dot_gov/openid_connect.html.erb @@ -1,2 +1,2 @@

You logged in, <%= current_user.given_name %> <%= current_user.family_name %>!

-

<%= link_to 'Try again?', new_user_session_path %>

+

<%= link_to 'Try again?', sign_in_path %>

diff --git a/dpc-portal/app/views/users/sessions/new.html.erb b/dpc-portal/app/views/users/sessions/new.html.erb index 39c6b28440..3949df9114 100644 --- a/dpc-portal/app/views/users/sessions/new.html.erb +++ b/dpc-portal/app/views/users/sessions/new.html.erb @@ -1 +1 @@ -<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:ldg_ial1))) %> +<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:login_dot_gov))) %> diff --git a/dpc-portal/config/environments/test.rb b/dpc-portal/config/environments/test.rb index 0d31929fb0..fa685743a9 100644 --- a/dpc-portal/config/environments/test.rb +++ b/dpc-portal/config/environments/test.rb @@ -8,6 +8,7 @@ # and recreated between test runs. Don't rely on the data there! Rails.application.configure do + config.colorize_logging = false # Settings specified here will take precedence over those in config/application.rb. # Turn false under Spring and add config.action_view.cache_template_loading = true. diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index 8029e77047..51b0beb5aa 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -8,8 +8,6 @@ Rails.application.config.middleware.use OmniAuth::Builder do OmniAuth.config.logger = Rails.logger - provider :developer if Rails.env.test? - begin private_key = OpenSSL::PKey::RSA.new(ENV['LOGIN_GOV_PRIVATE_KEY']) rescue TypeError, OpenSSL::PKey::RSAError => e @@ -18,7 +16,7 @@ end idp_host = ENV.fetch('IDP_HOST', 'idp.int.identitysandbox.gov') provider :openid_connect, { - name: :ldg_ial1, + name: :login_dot_gov, issuer: "https://#{idp_host}/", discovery: true, scope: %i[openid email all_emails], @@ -31,24 +29,7 @@ host: idp_host, identifier: "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV['ENV']}", private_key: private_key, - redirect_uri: "#{my_protocol_host}/portal/auth/ldg_ial1/callback" - } - } - provider :openid_connect, { - name: :ldg_ial2, - issuer: "https://#{idp_host}/", - discovery: true, - scope: %i[openid email all_emails profile social_security_number], - response_type: :code, - acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', - client_auth_method: :jwt_bearer, - client_options: { - port: 443, - scheme: 'https', - host: idp_host, - identifier: "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV['ENV']}", - private_key: private_key, - redirect_uri: "#{my_protocol_host}/portal/auth/ldg_ial2/callback" + redirect_uri: "#{my_protocol_host}/portal/auth/login_dot_gov/callback" } } end diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index 76aa4e0240..b7d5073b62 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -13,13 +13,19 @@ # However, to complete the mimicing, it uses the Rails.application.routes.recognize_path # method, which does not work correctly for applications served on a subpath. match '/portal', to: 'organizations#index', via: :get + + get '/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' + get '/auth/logged_out', to: 'users/sessions#logged_out' + get '/auth/no_account', to: 'login_dot_gov#no_account', as: 'no_account' delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' - get '/auth/:provider/callback', to: 'users/sessions#create' - get '/users/sign_in', to: 'users/sessions#new', as: 'new_user_session' - delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' get 'active', to: 'users/sessions#active', as: 'active' - get '/auth/logged_out', to: redirect('/portal/users/sign_in') get 'timeout', to: 'users/sessions#timeout', as: 'timeout' + + get '/users/sign_in', to: 'users/sessions#new', as: 'sign_in' + delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' + get '/auth/login_dot_gov/callback', to: 'login_dot_gov#openid_connect' + get '/auth/:provider/callback', to: 'users/sessions#create' + resources :organizations, only: [:index, :show, :new, :create] do resources :client_tokens, only: [:new, :create, :destroy] resources :public_keys, only: [:new, :create, :destroy] diff --git a/dpc-portal/spec/factories/users.rb b/dpc-portal/spec/factories/users.rb index 50d36cd18e..cc09470447 100644 --- a/dpc-portal/spec/factories/users.rb +++ b/dpc-portal/spec/factories/users.rb @@ -3,6 +3,7 @@ FactoryBot.define do factory :user, aliases: %i[invited_by] do sequence(:uid) { |n| n } + provider { :login_dot_gov } email { "user#{rand(0..100_000)}@example.com" } end end diff --git a/dpc-portal/spec/helpers/application_helper_spec.rb b/dpc-portal/spec/helpers/application_helper_spec.rb index 391b114ab2..a9cb10be3c 100644 --- a/dpc-portal/spec/helpers/application_helper_spec.rb +++ b/dpc-portal/spec/helpers/application_helper_spec.rb @@ -1,15 +1,21 @@ +# frozen_string_literal: true + require 'rails_helper' -# Specs in this file have access to a helper object that includes -# the ApplicationHelper. For example: -# -# describe ApplicationHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end RSpec.describe ApplicationHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" + describe 'current_user' do + let(:user) { create(:user) } + it 'should return nil if not set' do + expect(helper.current_user).to be_nil + end + it 'should return if set' do + assign(:current_user, user) + expect(helper.current_user).to eq user + end + end + describe 'omniauth_authorize_path' do + it 'should return path to service' do + expect(omniauth_authorize_path(:foo)).to eq '/portal/auth/foo' + end + end end diff --git a/dpc-portal/spec/requests/application_spec.rb b/dpc-portal/spec/requests/application_spec.rb index eff36d8bce..e5a83c3537 100644 --- a/dpc-portal/spec/requests/application_spec.rb +++ b/dpc-portal/spec/requests/application_spec.rb @@ -5,36 +5,30 @@ RSpec.describe 'Application', type: :request do include LoginSupport - before(:all) do - Rails.application.routes.disable_clear_and_finalize = true - Rails.application.routes.draw do - match '/test', to: 'test#index', via: :get - end - end let!(:user) { create(:user) } before { sign_in user } it 'sets cache control to no-store' do - get '/test' - expect(response.body).to eq('foo') + get '/' + expect(response).to be_ok expect(response.headers['cache-control']).to eq 'no-store' end it 'logs user_id to new relic' do expect(NewRelic::Agent).to receive(:add_custom_attributes).with({ user_id: user.id }) - get '/test' - expect(response.body).to eq('foo') + get '/' + expect(response).to be_ok end describe 'timed out' do after { Timecop.return } it 'redirects to login after inactivity' do - get '/test' - expect(response.body).to eq('foo') + get '/' + expect(response).to be_ok Timecop.travel(31.minutes.from_now) - get '/test' - expect(response).to redirect_to('/portal/users/sign_in') + get '/' + expect(response).to redirect_to('/users/sign_in') expect(flash[:notice] = 'Your session expired. Please sign in again to continue.') end @@ -44,22 +38,16 @@ { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::SessionTimedOut }]) logged_in_at = Time.now - get '/test' - expect(response.body).to eq('foo') + get '/' + expect(response).to be_ok until Time.now > logged_in_at + 12.hours - get '/test' - expect(response.body).to eq('foo') + get '/' + expect(response).to be_ok Timecop.travel(29.minutes.from_now) end - get '/test' + get '/' expect(response).to redirect_to('/users/sign_in') expect(flash[:notice] = 'You have exceeded the maximum session length. Please sign in again to continue.') end end end - -class TestController < ApplicationController - def index - render plain: :foo - end -end diff --git a/dpc-portal/spec/requests/client_tokens_spec.rb b/dpc-portal/spec/requests/client_tokens_spec.rb index f538d603d3..eaf2c5a434 100644 --- a/dpc-portal/spec/requests/client_tokens_spec.rb +++ b/dpc-portal/spec/requests/client_tokens_spec.rb @@ -159,7 +159,7 @@ context 'not logged in' do it 'redirects to login' do post '/organizations/no-such-id/client_tokens' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end @@ -224,7 +224,7 @@ context 'not logged in' do it 'redirects to login' do delete '/organizations/no-such-id/client_tokens/no-such-id' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') end end diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index b5dc697d3f..6d992b234b 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' RSpec.describe 'Invitations', type: :request do include LoginSupport @@ -156,7 +157,7 @@ it 'should show error page if fail to proof' do org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" - get '/users/auth/failure' + get '/auth/failure' expect(response).to be_forbidden expect(response.body).to include(I18n.t('verification.fail_to_proof_text')) end @@ -178,7 +179,7 @@ it 'should not show step navigation' do org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" - get '/users/auth/failure' + get '/auth/failure' expect(response).to be_forbidden expect(response.body).to_not include('') end @@ -198,7 +199,7 @@ it 'should show step 2' do org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" - get '/users/auth/failure' + get '/auth/failure' expect(response).to be_forbidden expect(response.body).to include('2') end @@ -810,7 +811,7 @@ def log_in OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:openid_connect, + OmniAuth.config.add_mock(:login_dot_gov, { uid: '12345', credentials: { expires_in: 899, token: 'bearer-token' }, @@ -818,7 +819,7 @@ def log_in extra: { raw_info: { given_name: 'Bob', family_name: 'Hoskins', ial: 'http://idmanagement.gov/ns/assurance/ial/2' } } }) - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! end diff --git a/dpc-portal/spec/requests/login_dot_gov_spec.rb b/dpc-portal/spec/requests/login_dot_gov_spec.rb index 8147572066..8d86ff9c7e 100644 --- a/dpc-portal/spec/requests/login_dot_gov_spec.rb +++ b/dpc-portal/spec/requests/login_dot_gov_spec.rb @@ -3,12 +3,12 @@ require 'rails_helper' RSpec.describe 'LoginDotGov', type: :request do - describe 'POST /users/auth/openid_connect' do + describe 'POST /auth/login_dot_gov' do RSpec.shared_examples 'an openid client' do context 'user exists' do - before { create(:user, uid: '12345', provider: 'openid_connect', email: 'bob@example.com') } + before { create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com') } it 'should sign in a user' do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(response.location).to eq organizations_url expect(response).to be_redirect @@ -20,13 +20,13 @@ expect(Rails.logger).to receive(:info).with(['User logged in', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoggedIn }]) - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! end it 'should not add another user' do - expect(User.where(uid: '12345', provider: 'openid_connect').count).to eq 1 + expect(User.where(uid: '12345', provider: 'login_dot_gov').count).to eq 1 expect do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! end.to change { User.count }.by(0) end @@ -35,7 +35,7 @@ context 'user does not exist' do it 'should not persist user' do expect do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! end.to change { User.count }.by(0) end @@ -46,7 +46,7 @@ context 'IAL/2' do before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:openid_connect, + OmniAuth.config.add_mock(:login_dot_gov, { uid: '12345', credentials: { expires_in: 899, token: }, @@ -61,20 +61,20 @@ it_behaves_like 'an openid client' context :user_exists do - before { create(:user, uid: '12345', provider: 'openid_connect', email: 'bob@example.com') } + before { create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com') } it 'updates user names' do expect do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! end.to change { - User.where(uid: '12345', provider: 'openid_connect', email: 'bob@example.com', given_name: 'Bob', + User.where(uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count }.by 1 expect(response.location).to eq organizations_url end it 'sets authentication token' do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(request.session[:login_dot_gov_token]).to eq token expect(request.session[:login_dot_gov_token_exp]).to_not be_nil @@ -84,7 +84,7 @@ context :user_does_not_exist do it 'does not sign in user' do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(response.location).to eq organizations_url expect(response).to be_redirect @@ -93,7 +93,7 @@ end it 'sets authentication token' do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(request.session[:login_dot_gov_token]).to eq token expect(request.session[:login_dot_gov_token_exp]).to_not be_nil @@ -105,7 +105,7 @@ context 'IAL/1' do before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:openid_connect, + OmniAuth.config.add_mock(:login_dot_gov, { uid: '12345', info: { email: 'bob@example.com' }, extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], @@ -116,21 +116,21 @@ context :user_exists do before do - create(:user, uid: '12345', provider: 'openid_connect', email: 'bob@example.com', given_name: 'Bob', + create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins') end it 'does not update user names' do - expect(User.where(uid: '12345', provider: 'openid_connect', email: 'bob@example.com', given_name: 'Bob', + expect(User.where(uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(response.location).to eq organizations_url - expect(User.where(uid: '12345', provider: 'openid_connect', email: 'bob@example.com', given_name: 'Bob', + expect(User.where(uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 end it 'does not set authentication token' do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(request.session[:login_dot_gov_token]).to be_nil expect(request.session[:login_dot_gov_token_exp]).to be_nil @@ -139,7 +139,7 @@ context 'user does not exist' do it 'does not sign in user' do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(response.location).to eq no_account_url expect(response).to be_redirect @@ -152,12 +152,12 @@ { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoginWithoutAccount }] ) - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! end it 'does not set authentication token' do - post '/users/auth/openid_connect' + post '/auth/login_dot_gov' follow_redirect! expect(request.session[:login_dot_gov_token]).to be_nil expect(request.session[:login_dot_gov_token_exp]).to be_nil @@ -166,9 +166,9 @@ end end - describe 'Get /users/auth/failure' do + describe 'Get /auth/failure' do it 'should succeed' do - get '/users/auth/failure' + get '/auth/failure' expect(response).to be_ok end @@ -177,7 +177,7 @@ expect(Rails.logger).to receive(:info).with(['User cancelled login', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserCancelledLogin }]) - get '/users/auth/failure' + get '/auth/failure' end end @@ -195,23 +195,9 @@ end end - describe 'Get /users/auth/logged_out' do - it 'should redirect to user_return_to' do - get '/organizations' - expect(request.session[:user_return_to]).to eq organizations_path - get '/users/auth/logged_out' - expect(response).to redirect_to(organizations_path) - end - - it 'should redirect to new session if no user_return_to set' do - get '/users/auth/logged_out' - expect(response).to redirect_to(new_user_session_path) - end - end - - describe 'Get /users/auth/no_account' do + describe 'Get /auth/no_account' do it 'should show logout button' do - get '/users/auth/no_account' + get '/auth/no_account' expect(response.body).to include 'Sign out of Login.gov' end end diff --git a/dpc-portal/spec/requests/organizations_spec.rb b/dpc-portal/spec/requests/organizations_spec.rb index d7a1671d5a..c22e8fed26 100644 --- a/dpc-portal/spec/requests/organizations_spec.rb +++ b/dpc-portal/spec/requests/organizations_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' RSpec.describe 'Organizations', type: :request do include DpcClientSupport diff --git a/dpc-portal/spec/requests/public_keys_spec.rb b/dpc-portal/spec/requests/public_keys_spec.rb index c58ec7e94a..ddf676b219 100644 --- a/dpc-portal/spec/requests/public_keys_spec.rb +++ b/dpc-portal/spec/requests/public_keys_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' require 'support/credential_resource_shared_examples' +require 'support/login_support' RSpec.describe 'PublicKeys', type: :request do include DpcClientSupport diff --git a/dpc-portal/spec/requests/users/sessions_spec.rb b/dpc-portal/spec/requests/users/sessions_spec.rb index 04a974d751..2de93e35bb 100644 --- a/dpc-portal/spec/requests/users/sessions_spec.rb +++ b/dpc-portal/spec/requests/users/sessions_spec.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' RSpec.describe 'Sessions', type: :request do + include LoginSupport describe 'logout' do context 'logged in' do let!(:user) { create(:user) } @@ -12,7 +14,7 @@ it 'should prevent access' do delete '/users/sign_out' get '/organizations' - expect(response).to redirect_to('/portal/users/sign_in') + expect(response).to redirect_to('/users/sign_in') expect(flash[:alert]).to be_present end @@ -31,5 +33,19 @@ expect(response.location).to include(ENV.fetch('IDP_HOST')) end end + + describe 'Get /auth/logged_out' do + it 'should redirect to user_return_to' do + get '/organizations' + expect(request.session[:user_return_to]).to eq organizations_path + get '/auth/logged_out' + expect(response).to redirect_to(organizations_path) + end + + it 'should redirect to new session if no user_return_to set' do + get '/auth/logged_out' + expect(response).to redirect_to(sign_in_path) + end + end end end diff --git a/dpc-portal/spec/services/auto_session_logout_service_spec.rb b/dpc-portal/spec/services/auto_session_logout_service_spec.rb index a2430acc42..3cb3206099 100644 --- a/dpc-portal/spec/services/auto_session_logout_service_spec.rb +++ b/dpc-portal/spec/services/auto_session_logout_service_spec.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' RSpec.describe 'AutoSessionLogoutService', type: :request do + include LoginSupport let(:user) { create(:user) } before { sign_in user } @@ -13,6 +15,6 @@ it 'is timed out' do get '/timeout' - expect(response).to redirect_to(new_user_session_path) + expect(response).to redirect_to(sign_in_path) end end diff --git a/dpc-portal/spec/support/login_support.rb b/dpc-portal/spec/support/login_support.rb index e15c498d2f..76300a4ba9 100644 --- a/dpc-portal/spec/support/login_support.rb +++ b/dpc-portal/spec/support/login_support.rb @@ -2,6 +2,13 @@ module LoginSupport def sign_in(user) - + OmniAuth.config.test_mode = true + OmniAuth.config.add_mock(:login_dot_gov, + { uid: user.uid, + info: { email: user.email }, + extra: { raw_info: { all_emails: [user.email], + ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) + post '/auth/login_dot_gov' + follow_redirect! end end From cc1342f8c0a9ffe0161a217d598cb355d3fc583c Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:39:18 -0800 Subject: [PATCH 04/48] better for eval --- dpc-portal/config/routes.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index b7d5073b62..d3b3ba2c65 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -5,6 +5,14 @@ # and config.ru via config.relative_url_root. # Rails.application.routes.draw do + # Former devise routes + get '/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' + get '/auth/logged_out', to: 'users/sessions#logged_out' + get '/auth/no_account', to: 'login_dot_gov#no_account', as: 'no_account' + delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' + get 'active', to: 'users/sessions#active', as: 'active' + get 'timeout', to: 'users/sessions#timeout', as: 'timeout' + # Defines the root path route ("/") root 'organizations#index' @@ -14,13 +22,6 @@ # method, which does not work correctly for applications served on a subpath. match '/portal', to: 'organizations#index', via: :get - get '/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' - get '/auth/logged_out', to: 'users/sessions#logged_out' - get '/auth/no_account', to: 'login_dot_gov#no_account', as: 'no_account' - delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' - get 'active', to: 'users/sessions#active', as: 'active' - get 'timeout', to: 'users/sessions#timeout', as: 'timeout' - get '/users/sign_in', to: 'users/sessions#new', as: 'sign_in' delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' get '/auth/login_dot_gov/callback', to: 'login_dot_gov#openid_connect' From f32edc0a7367b3050c82058e10887e226b168341 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:41:55 -0800 Subject: [PATCH 05/48] cleanup --- dpc-portal/config/routes.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index d3b3ba2c65..fc886fbedb 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -12,6 +12,9 @@ delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' get 'active', to: 'users/sessions#active', as: 'active' get 'timeout', to: 'users/sessions#timeout', as: 'timeout' + get '/users/sign_in', to: 'users/sessions#new', as: 'sign_in' + delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' + get '/auth/login_dot_gov/callback', to: 'login_dot_gov#openid_connect' # Defines the root path route ("/") root 'organizations#index' @@ -22,10 +25,6 @@ # method, which does not work correctly for applications served on a subpath. match '/portal', to: 'organizations#index', via: :get - get '/users/sign_in', to: 'users/sessions#new', as: 'sign_in' - delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' - get '/auth/login_dot_gov/callback', to: 'login_dot_gov#openid_connect' - get '/auth/:provider/callback', to: 'users/sessions#create' resources :organizations, only: [:index, :show, :new, :create] do resources :client_tokens, only: [:new, :create, :destroy] From 892b051440d089cc2566f07ee66a222d72b62af9 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:42:51 -0800 Subject: [PATCH 06/48] cleanup --- dpc-portal/config/routes.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index fc886fbedb..1b50a4f8b8 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -25,7 +25,6 @@ # method, which does not work correctly for applications served on a subpath. match '/portal', to: 'organizations#index', via: :get - resources :organizations, only: [:index, :show, :new, :create] do resources :client_tokens, only: [:new, :create, :destroy] resources :public_keys, only: [:new, :create, :destroy] From 0099dc461dec4c75e1187829da36f645a0ab9bdd Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Mon, 29 Dec 2025 16:05:56 -0800 Subject: [PATCH 07/48] test invitation flow --- dpc-portal/app/controllers/invitations_controller.rb | 2 +- dpc-portal/config/routes.rb | 2 +- dpc-portal/spec/requests/invitations_spec.rb | 6 +++--- dpc-portal/spec/requests/login_dot_gov_spec.rb | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index 725234a38e..d174cb2a89 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -80,7 +80,7 @@ def login path: '/openid_connect/authorize', query: { acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', client_id: IDP_CLIENT_ID, - redirect_uri: "#{my_protocol_host}/portal/users/auth/login_dot_gov/callback", + redirect_uri: "#{my_protocol_host}/portal/auth/login_dot_gov/callback", response_type: 'code', scope: 'openid email all_emails profile social_security_number', nonce: @nonce, diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index 1b50a4f8b8..057d93b5ba 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -6,7 +6,7 @@ # Rails.application.routes.draw do # Former devise routes - get '/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' + get '/users/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' get '/auth/logged_out', to: 'users/sessions#logged_out' get '/auth/no_account', to: 'login_dot_gov#no_account', as: 'no_account' delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index 6d992b234b..bb8ee8d354 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -157,7 +157,7 @@ it 'should show error page if fail to proof' do org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" - get '/auth/failure' + get '/users/auth/failure' expect(response).to be_forbidden expect(response.body).to include(I18n.t('verification.fail_to_proof_text')) end @@ -179,7 +179,7 @@ it 'should not show step navigation' do org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" - get '/auth/failure' + get '/users/auth/failure' expect(response).to be_forbidden expect(response.body).to_not include('') end @@ -199,7 +199,7 @@ it 'should show step 2' do org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" - get '/auth/failure' + get '/users/auth/failure' expect(response).to be_forbidden expect(response.body).to include('2') end diff --git a/dpc-portal/spec/requests/login_dot_gov_spec.rb b/dpc-portal/spec/requests/login_dot_gov_spec.rb index 8d86ff9c7e..086cc0c4c5 100644 --- a/dpc-portal/spec/requests/login_dot_gov_spec.rb +++ b/dpc-portal/spec/requests/login_dot_gov_spec.rb @@ -168,7 +168,7 @@ describe 'Get /auth/failure' do it 'should succeed' do - get '/auth/failure' + get '/users/auth/failure' expect(response).to be_ok end @@ -177,7 +177,7 @@ expect(Rails.logger).to receive(:info).with(['User cancelled login', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserCancelledLogin }]) - get '/auth/failure' + get '/users/auth/failure' end end From 8c705e48a732cc670edaef2570804a02140a721a Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:55:48 -0500 Subject: [PATCH 08/48] fix user create, accessibility tests --- .../app/controllers/invitations_controller.rb | 2 +- dpc-portal/spec/requests/invitations_spec.rb | 10 ++--- dpc-portal/spec/system/accessibility_spec.rb | 45 ++++++++++++------- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index d174cb2a89..166e4eac91 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -200,7 +200,7 @@ def create_ao_org_link def user user_info = UserInfoService.new.user_info(session) - @user = User.find_or_create_by!(provider: :openid_connect, uid: user_info['sub']) do |user_to_create| + @user = User.find_or_create_by!(provider: :login_dot_gov, uid: user_info['sub']) do |user_to_create| assign_user_attributes(user_to_create, user_info) log_create_user end diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index bb8ee8d354..4224fd2b5f 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -631,13 +631,13 @@ post "/organizations/#{org.id}/invitations/#{invitation.id}/register" end it 'should not create user if exists' do - create(:user, provider: :openid_connect, uid: user_info_template['sub']) + create(:user, provider: :login_dot_gov, uid: user_info_template['sub']) expect do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" end.to change { User.count }.by 0 end it 'should update name of user if changed' do - user = create(:user, provider: :openid_connect, uid: user_info_template['sub'], given_name: :foo, + user = create(:user, provider: :login_dot_gov, uid: user_info_template['sub'], given_name: :foo, family_name: :bar) expect do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" @@ -647,7 +647,7 @@ expect(user.family_name).to eq user_info_template['family_name'] end it 'should not override pac_id on existing user' do - create(:user, provider: :openid_connect, uid: user_info_template['sub'], pac_id: :foo) + create(:user, provider: :login_dot_gov, uid: user_info_template['sub'], pac_id: :foo) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) # We have the fake CPI API Gateway return the ssn as pac_id @@ -693,7 +693,7 @@ get "/organizations/#{org.id}/invitations/#{invitation.id}/confirm_cd" end it 'should not save verification_status on user and org' do - create(:user, provider: :openid_connect, uid: user_info_template['sub'], pac_id: :foo) + create(:user, provider: :login_dot_gov, uid: user_info_template['sub'], pac_id: :foo) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) expect(user.verification_status).to be_nil @@ -725,7 +725,7 @@ expect(request.session[:user_pac_id]).to be_nil end it 'should set pac_id on existing user' do - create(:user, provider: :openid_connect, uid: user_info_template['sub']) + create(:user, provider: :login_dot_gov, uid: user_info_template['sub']) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) # We have the fake CPI API Gateway return the ssn as pac_id diff --git a/dpc-portal/spec/system/accessibility_spec.rb b/dpc-portal/spec/system/accessibility_spec.rb index 4aa21cddcf..61654f4d55 100644 --- a/dpc-portal/spec/system/accessibility_spec.rb +++ b/dpc-portal/spec/system/accessibility_spec.rb @@ -9,6 +9,19 @@ end let(:dpc_api_organization_id) { 'some-gnarly-guid' } let(:axe_standard) { %w[best-practice wcag21aa] } + let(:uid) { '12345' } + + before do + OmniAuth.config.test_mode = true + OmniAuth.config.add_mock(:login_dot_gov, + { uid:, + info: { email: 'bob@example.com' }, + extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], + ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) + end + def sign_in + visit '/auth/login_dot_gov/callback' + end context 'login' do it 'shows login page ok' do visit '/users/sign_in' @@ -23,31 +36,33 @@ end context 'bad user tries to log in' do - before do - OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:openid_connect, - { uid: '12345', - info: { email: 'bob@example.com' }, - extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], - ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) - end it 'shows no such user page' do - visit '/users/auth/openid_connect/callback' - expect(page).to have_text('You must have an account to sign in.') + visit '/auth/login_dot_gov/callback' + expect(page).to have_text('The email you used is not associated with a DPC account.') expect(page).to be_axe_clean.according_to axe_standard end it 'shows sanctioned ao page' do - create(:user, provider: :openid_connect, uid: '12345', + create(:user, provider: :login_dot_gov, uid: '12345', verification_status: 'rejected', verification_reason: 'ao_med_sanctions') - visit '/users/auth/openid_connect/callback' + visit '/auth/login_dot_gov/callback' expect(page).to have_text(I18n.t('verification.ao_med_sanctions_status')) expect(page).to be_axe_clean.according_to axe_standard end end + + context 'valid user tries to log in' do + it 'shows success page' do + create(:user, provider: :login_dot_gov, uid: '12345', + verification_status: 'approved') + visit '/auth/login_dot_gov/callback' + expect(page).to have_text("You don't have any organizations to show.") + expect(page).to be_axe_clean.according_to axe_standard + end + end end context 'organizations' do - let!(:user) { create(:user) } + let!(:user) { create(:user, uid:, provider: :login_dot_gov, verification_status: :approved) } let!(:org) { create(:provider_organization, dpc_api_organization_id:, name: 'Health Hut') } let(:mock_client_token_manager) { instance_double(ClientTokenManager) } let(:mock_public_key_manager) { instance_double(PublicKeyManager) } @@ -63,7 +78,7 @@ allow(mock_client_token_manager).to receive(:client_tokens).and_return(tokens) allow(mock_public_key_manager).to receive(:public_keys).and_return(keys) allow(mock_ip_address_manager).to receive(:ip_addresses).and_return(ip_addresses) - sign_in user + sign_in end context 'list' do it 'empty' do @@ -294,7 +309,6 @@ page.fill_in 'invited_email', with: 'john@beatles.com' page.fill_in 'invited_email_confirmation', with: 'john@beatles.com' page.find_button(value: 'Send invite').click - page.find_button(value: 'Yes, I acknowledge').click expect(page).to_not have_text("can't be blank") expect(page).to have_text('Credential Delegate invited successfully') expect(page).to be_axe_clean.according_to axe_standard @@ -307,7 +321,6 @@ page.fill_in 'invited_email', with: invitation.invited_email page.fill_in 'invited_email_confirmation', with: invitation.invited_email page.find_button(value: 'Send invite').click - page.find_button(value: 'Yes, I acknowledge').click expect(page).to_not have_text("can't be blank") expect(page).to have_text(I18n.t('errors.attributes.base.duplicate_cd.status')) expect(page).to be_axe_clean.according_to axe_standard From 901048108f181cffad9ed55b5477dc51056ba95a Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:27:46 -0400 Subject: [PATCH 09/48] force merge dpc-admin and dpc-web --- dpc-admin/.rubocop_todo.yml | 197 ++++++++---------- dpc-admin/Gemfile | 2 +- dpc-admin/Gemfile.lock | 52 +++-- dpc-admin/app/models/concerns/taggable.rb | 1 + .../user_management/updating_users_spec.rb | 1 + .../spec/services/public_key_manager_spec.rb | 1 + dpc-web/.rubocop_todo.yml | 158 ++++++-------- dpc-web/Gemfile | 2 +- dpc-web/Gemfile.lock | 52 +++-- dpc-web/app/models/concerns/taggable.rb | 1 + .../user_resets_password_spec.rb | 1 + .../user_confirmation_email_spec.rb | 1 + .../portal/managing_api_credentials_spec.rb | 1 + dpc-web/spec/models/user_spec.rb | 1 + dpc-web/spec/system/accessibility_spec.rb | 1 + 15 files changed, 221 insertions(+), 251 deletions(-) diff --git a/dpc-admin/.rubocop_todo.yml b/dpc-admin/.rubocop_todo.yml index f0ba814cac..96414e9ad0 100644 --- a/dpc-admin/.rubocop_todo.yml +++ b/dpc-admin/.rubocop_todo.yml @@ -1,28 +1,28 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2021-04-12 18:10:20 UTC using RuboCop version 1.12.0. +# on 2026-03-17 22:32:50 UTC using RuboCop version 1.85.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 3 -# Cop supports --auto-correct. +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: - 'spec/features/organization_management/creating_and_updating_organizations_spec.rb' - 'spec/features/organization_management/searching_and_filtering_organizations_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, no_empty_lines Layout/EmptyLinesAroundBlockBody: Exclude: - 'spec/controllers/registered_organizations_controller_spec.rb' -# Offense count: 17 -# Cop supports --auto-correct. +# Offense count: 16 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table # SupportedColonStyles: key, separator, table @@ -30,24 +30,9 @@ Layout/EmptyLinesAroundBlockBody: Layout/HashAlignment: Exclude: - 'spec/models/user_spec.rb' - - 'spec/services/api_client_spec.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment. -Layout/LeadingCommentSpace: - Exclude: - - 'node_modules/node-sass/src/libsass/extconf.rb' - -# Offense count: 21 -# Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. -# URISchemes: http, https -Layout/LineLength: - Max: 251 # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space Layout/SpaceAroundEqualsInParameterDefault: @@ -55,15 +40,7 @@ Layout/SpaceAroundEqualsInParameterDefault: - 'spec/features/organization_management/creating_and_updating_organizations_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator. -# SupportedStylesForExponentOperator: space, no_space -Layout/SpaceAroundOperators: - Exclude: - - 'spec/services/api_client_spec.rb' - -# Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBraces: space, no_space @@ -72,52 +49,64 @@ Layout/SpaceInsideHashLiteralBraces: - 'spec/controllers/organizations_controller_spec.rb' # Offense count: 4 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Exclude: - 'spec/features/user_management/updating_users_spec.rb' - 'spec/models/user_spec.rb' -# Offense count: 16 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: strict, consistent -Lint/SymbolConversion: +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +Lint/LiteralAsCondition: Exclude: - - 'app/serializers/organization_submit_serializer.rb' - - 'app/services/api_client.rb' - - 'app/services/fhir_resource_builder.rb' - - 'spec/services/api_client_spec.rb' + - 'lib/tasks/webpacker.rake' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'spec/factories/users.rb' -# Offense count: 5 +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. +Lint/UselessAccessModifier: + Exclude: + - 'app/models/organization.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). Lint/UselessAssignment: Exclude: - - 'spec/features/organization_management/creating_and_updating_organizations_spec.rb' - 'spec/models/user_spec.rb' - 'spec/services/base_search_spec.rb' -# Offense count: 2 +# Offense count: 1 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: Max: 133 -# Offense count: 4 -# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. +# Offense count: 10 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: - Max: 25 + Max: 20 + +# Offense count: 3 +# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. +# AllowedMethods: call +# WaywardPredicates: infinite?, nonzero? +Naming/PredicateMethod: + Exclude: + - 'app/helpers/nav_helper.rb' + - 'app/services/client_token_manager.rb' + - 'lib/luhnacy_lib/luhnacy_lib.rb' # Offense count: 14 -# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers. +# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. # SupportedStyles: snake_case, normalcase, non_integer -# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339 +# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 Naming/VariableNumber: Exclude: - 'app/controllers/organizations_controller.rb' @@ -127,74 +116,70 @@ Naming/VariableNumber: - 'spec/models/user_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Performance/StringReplacement: Exclude: - 'app/services/public_key_manager.rb' # Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch -# IgnoredMethods: lambda, proc, it +# AllowedMethods: lambda, proc, it Style/BlockDelimiters: Exclude: - 'spec/factories/organizations.rb' -# Offense count: 6 -# Cop supports --auto-correct. -Style/CaseLikeIf: +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/CollectionQuerying: Exclude: - - 'app/controllers/organizations_controller.rb' - - 'app/controllers/taggings_controller.rb' - - 'app/services/base_search.rb' + - 'app/models/user.rb' # Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: Keywords. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Keywords, RequireColon. # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE Style/CommentAnnotation: Exclude: - 'spec/shared_examples/internal_user_authenticable_controller.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/ExpandPathArguments: Exclude: - 'Rakefile' -# Offense count: 3 -# Cop supports --auto-correct. +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Exclude: + - '**/*.arb' - 'Gemfile' - - 'node_modules/node-sass/src/libsass/extconf.rb' - 'spec/factories/registered_organizations.rb' -# Offense count: 2 -# Configuration parameters: AllowedVariables. -Style/GlobalVars: - Exclude: - - 'node_modules/node-sass/src/libsass/extconf.rb' - # Offense count: 1 -# Configuration parameters: MinBodyLength. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: Exclude: - 'app/models/organization_user_assignment.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/KeywordParametersOrder: +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: braces, no_braces +Style/HashAsLastArrayItem: Exclude: - - 'spec/support/api_client_support.rb' + - 'spec/services/public_key_manager_spec.rb' + - 'spec/support/dpc_client_support.rb' # Offense count: 3 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedMethods. # AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with Style/NestedParenthesizedCalls: @@ -202,54 +187,53 @@ Style/NestedParenthesizedCalls: - 'spec/controllers/registered_organizations_controller_spec.rb' # Offense count: 8 -# Cop supports --auto-correct. -# Configuration parameters: Strict. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns. Style/NumericLiterals: MinDigits: 6 # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/RedundantAssignment: Exclude: - 'app/services/base_search.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/RedundantBegin: Exclude: - 'lib/tasks/webpacker.rake' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpEscape: Exclude: - 'app/models/address.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Exclude: - 'lib/luhnacy_lib/luhnacy_lib.rb' -# Offense count: 14 -# Cop supports --auto-correct. +# Offense count: 10 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Mode. Style/StringConcatenation: Exclude: - 'app/helpers/nav_helper.rb' - 'app/models/organization.rb' - - 'app/services/api_client.rb' - 'app/services/base_search.rb' - 'spec/features/organization_management/searching_and_filtering_organizations_spec.rb' - 'spec/features/user_management/searching_and_filtering_users_spec.rb' - 'spec/lib/luhnacy_lib_spec.rb' -# Offense count: 30 -# Cop supports --auto-correct. +# Offense count: 29 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Exclude: - - 'node_modules/node-sass/src/libsass/extconf.rb' - 'spec/controllers/registered_organizations_controller_spec.rb' - 'spec/factories/addresses.rb' - 'spec/factories/organizations.rb' @@ -257,34 +241,17 @@ Style/StringLiterals: - 'spec/helpers/organizations_helper_spec.rb' - 'spec/models/user_spec.rb' -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: single_quotes, double_quotes -Style/StringLiteralsInInterpolation: - Exclude: - - 'node_modules/node-sass/src/libsass/extconf.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowMethodsWithArguments, IgnoredMethods. -# IgnoredMethods: respond_to, define_method -Style/SymbolProc: - Exclude: - - 'spec/factories/registered_organizations.rb' - # Offense count: 7 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, no_comma +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma Style/TrailingCommaInHashLiteral: Exclude: - 'spec/services/base_search_spec.rb' -# Offense count: 43 -# Cop supports --auto-correct. -# Configuration parameters: WordRegex. -# SupportedStyles: percent, brackets -Style/WordArray: - EnforcedStyle: percent - MinSize: 3 +# Offense count: 13 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Max: 194 diff --git a/dpc-admin/Gemfile b/dpc-admin/Gemfile index fc837eb333..c7ba51eeea 100644 --- a/dpc-admin/Gemfile +++ b/dpc-admin/Gemfile @@ -30,7 +30,7 @@ gem 'sassc-rails', '>= 2.1.2' gem 'uglifier', '>= 1.3.0' gem 'pg', '>= 0.18', '< 2.0' gem 'csv' -gem 'devise' +gem 'devise', '~> 5.0.3' gem 'devise-async' gem 'devise-security' gem 'health_check' diff --git a/dpc-admin/Gemfile.lock b/dpc-admin/Gemfile.lock index 3c01b93304..701195ed16 100644 --- a/dpc-admin/Gemfile.lock +++ b/dpc-admin/Gemfile.lock @@ -82,7 +82,7 @@ GEM uri (>= 0.13.1) addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) - ast (2.4.2) + ast (2.4.3) aws-eventstream (1.4.0) aws-partitions (1.1148.0) aws-sdk-core (3.229.0) @@ -101,7 +101,7 @@ GEM base64 (0.3.0) bcp47 (0.3.3) i18n - bcrypt (3.1.20) + bcrypt (3.1.22) benchmark (0.4.1) bigdecimal (3.1.7) bindex (0.8.1) @@ -139,10 +139,10 @@ GEM database_cleaner-core (2.0.1) date (3.4.1) date_time_precision (0.8.1) - devise (4.9.4) + devise (5.0.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0) + railties (>= 7.0) responders warden (~> 1.2.3) devise-async (1.0.0) @@ -197,7 +197,10 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.18.1) + json (2.19.1) + json-schema (6.2.0) + addressable (~> 2.8) + bigdecimal (>= 3.1, < 5) jsonapi-renderer (0.2.2) jwt (3.1.2) base64 @@ -213,12 +216,13 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) - language_server-protocol (3.17.0.3) + language_server-protocol (3.17.0.5) launchy (3.0.0) addressable (~> 2.8) childprocess (~> 5.0) letter_opener (1.10.0) launchy (>= 2.2, < 4) + lint_roller (1.1.0) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -243,6 +247,8 @@ GEM net-smtp marcel (1.0.4) matrix (0.4.2) + mcp (0.8.0) + json-schema (>= 4.1) method_source (1.1.0) mime-types (3.5.2) mime-types-data (~> 3.2015) @@ -296,8 +302,8 @@ GEM actionpack (>= 4.2) omniauth (~> 2.0) orm_adapter (0.5.0) - parallel (1.26.3) - parser (3.3.6.0) + parallel (1.27.0) + parser (3.3.10.2) ast (~> 2.4.1) racc pg (1.5.6) @@ -307,6 +313,7 @@ GEM pp (0.6.2) prettyprint prettyprint (0.2.0) + prism (1.9.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -363,14 +370,14 @@ GEM rdoc (6.14.2) erb psych (>= 4.0.0) - regexp_parser (2.9.3) + regexp_parser (2.11.3) reline (0.6.2) io-console (~> 0.5) request_store (1.6.0) rack (>= 1.4) - responders (3.1.1) - actionpack (>= 5.2) - railties (>= 5.2) + responders (3.2.0) + actionpack (>= 7.0) + railties (>= 7.0) rexml (3.4.4) rspec-core (3.13.0) rspec-support (~> 3.13.0) @@ -389,18 +396,21 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rubocop (1.69.1) + rubocop (1.85.1) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + mcp (~> 0.6) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.36.2, < 2.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.36.2) - parser (>= 3.3.1.0) + rubocop-ast (1.49.1) + parser (>= 3.3.7.2) + prism (~> 1.7) rubocop-performance (1.23.0) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) @@ -466,9 +476,9 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.9.1) - unicode-display_width (3.1.2) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) uri (1.1.1) useragent (0.16.11) version_gem (1.1.9) @@ -516,7 +526,7 @@ DEPENDENCIES climate_control csv database_cleaner-active_record (~> 2.2) - devise + devise (~> 5.0.3) devise-async devise-security dotenv-rails diff --git a/dpc-admin/app/models/concerns/taggable.rb b/dpc-admin/app/models/concerns/taggable.rb index 4e17140e8a..056273d885 100644 --- a/dpc-admin/app/models/concerns/taggable.rb +++ b/dpc-admin/app/models/concerns/taggable.rb @@ -2,6 +2,7 @@ module Taggable extend ActiveSupport::Concern + included do has_many :taggings, as: :taggable has_many :tags, through: :taggings diff --git a/dpc-admin/spec/features/user_management/updating_users_spec.rb b/dpc-admin/spec/features/user_management/updating_users_spec.rb index 1f26a7bcaf..9587548ee7 100644 --- a/dpc-admin/spec/features/user_management/updating_users_spec.rb +++ b/dpc-admin/spec/features/user_management/updating_users_spec.rb @@ -4,6 +4,7 @@ RSpec.feature 'updating users' do include DpcClientSupport + let!(:internal_user) { create :internal_user } before(:each) do diff --git a/dpc-admin/spec/services/public_key_manager_spec.rb b/dpc-admin/spec/services/public_key_manager_spec.rb index 1b9d83c26d..265a57c585 100644 --- a/dpc-admin/spec/services/public_key_manager_spec.rb +++ b/dpc-admin/spec/services/public_key_manager_spec.rb @@ -4,6 +4,7 @@ RSpec.describe PublicKeyManager do include DpcClientSupport + describe '#create_public_key' do before(:each) do @public_key_params = { label: 'Test Key 1', public_key: file_fixture('stubbed_key.pem').read, snippet_signature: 'stubbed_sign_txt_signature' } diff --git a/dpc-web/.rubocop_todo.yml b/dpc-web/.rubocop_todo.yml index e9c3ee34ce..97034cad4c 100644 --- a/dpc-web/.rubocop_todo.yml +++ b/dpc-web/.rubocop_todo.yml @@ -1,35 +1,35 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2021-06-15 18:19:16 UTC using RuboCop version 1.17.0. +# on 2026-03-17 22:28:10 UTC using RuboCop version 1.85.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 3 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Exclude: - 'spec/features/authentication/user_password_expire_spec.rb' - 'spec/features/portal/updating_organization_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, no_empty_lines Layout/EmptyLinesAroundBlockBody: Exclude: - 'spec/features/authentication/user_password_expire_spec.rb' -# Offense count: 8 -# Cop supports --auto-correct. +# Offense count: 12 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces Layout/FirstHashElementIndentation: EnforcedStyle: consistent -# Offense count: 17 -# Cop supports --auto-correct. +# Offense count: 16 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table # SupportedColonStyles: key, separator, table @@ -37,10 +37,9 @@ Layout/FirstHashElementIndentation: Layout/HashAlignment: Exclude: - 'spec/models/user_spec.rb' - - 'spec/services/api_client_spec.rb' # Offense count: 6 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space Layout/SpaceAroundEqualsInParameterDefault: @@ -48,21 +47,13 @@ Layout/SpaceAroundEqualsInParameterDefault: - 'spec/features/portal/managing_api_credentials_spec.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Layout/SpaceAroundMethodCallOperator: Exclude: - 'spec/helpers/application_helper_spec.rb' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator. -# SupportedStylesForExponentOperator: space, no_space -Layout/SpaceAroundOperators: - Exclude: - - 'spec/services/api_client_spec.rb' - # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: final_newline, final_blank_line Layout/TrailingEmptyLines: @@ -70,58 +61,62 @@ Layout/TrailingEmptyLines: - 'spec/features/authentication/user_deletes_account_spec.rb' - 'spec/helpers/devise_helper_spec.rb' -# Offense count: 3 -# Cop supports --auto-correct. +# Offense count: 5 +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Exclude: - 'spec/models/user_spec.rb' -# Offense count: 16 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle. -# SupportedStyles: strict, consistent -Lint/SymbolConversion: - Exclude: - - 'app/serializers/organization_submit_serializer.rb' - - 'app/services/api_client.rb' - - 'app/services/fhir_resource_builder.rb' - - 'spec/services/api_client_spec.rb' - # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'spec/factories/users.rb' # Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. +# NotImplementedExceptions: NotImplementedError Lint/UnusedMethodArgument: Exclude: - 'spec/features/portal/managing_api_credentials_spec.rb' +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods. +Lint/UselessAccessModifier: + Exclude: + - 'app/models/organization.rb' + # Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). Lint/UselessAssignment: Exclude: - 'spec/features/portal/managing_api_credentials_spec.rb' - 'spec/models/user_spec.rb' -# Offense count: 2 -# Configuration parameters: CountComments, CountAsOne. -Metrics/ClassLength: - Max: 141 - -# Offense count: 10 -# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. +# Offense count: 7 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: - Max: 25 + Max: 20 + +# Offense count: 5 +# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. +# AllowedMethods: call +# WaywardPredicates: infinite?, nonzero? +Naming/PredicateMethod: + Exclude: + - 'app/controllers/public_keys_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/services/client_token_manager.rb' + - 'lib/luhnacy_lib/luhnacy_lib.rb' # Offense count: 25 -# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers. +# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. # SupportedStyles: snake_case, normalcase, non_integer -# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339 +# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 Naming/VariableNumber: Exclude: - 'app/controllers/application_controller.rb' @@ -132,59 +127,55 @@ Naming/VariableNumber: - 'spec/models/user_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Performance/StringReplacement: Exclude: - 'app/services/public_key_manager.rb' # Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch -# IgnoredMethods: lambda, proc, it +# AllowedMethods: lambda, proc, it Style/BlockDelimiters: Exclude: - 'spec/factories/organizations.rb' # Offense count: 1 -# Cop supports --auto-correct. -Style/CaseLikeIf: +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/CollectionQuerying: Exclude: - - 'app/controllers/application_controller.rb' + - 'app/models/user.rb' # Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: Keywords. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Keywords, RequireColon. # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE Style/CommentAnnotation: Exclude: - 'spec/features/portal/managing_api_credentials_spec.rb' # Offense count: 2 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Exclude: + - '**/*.arb' - 'spec/factories/registered_organizations.rb' - 'spec/features/authentication/user_deletes_account_spec.rb' # Offense count: 1 -# Configuration parameters: MinBodyLength. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: Exclude: - 'app/models/organization_user_assignment.rb' -# Offense count: 1 -# Cop supports --auto-correct. -Style/KeywordParametersOrder: - Exclude: - - 'spec/support/api_client_support.rb' - # Offense count: 3 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedMethods. # AllowedMethods: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with Style/NestedParenthesizedCalls: @@ -192,13 +183,13 @@ Style/NestedParenthesizedCalls: - 'spec/controllers/public_keys_controller_spec.rb' # Offense count: 7 -# Cop supports --auto-correct. -# Configuration parameters: Strict. +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns. Style/NumericLiterals: MinDigits: 6 # Offense count: 13 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). Style/RedundantRegexpEscape: Exclude: - 'app/models/address.rb' @@ -209,23 +200,22 @@ Style/RedundantRegexpEscape: - 'spec/features/confirmation/user_confirmation_email_spec.rb' # Offense count: 1 -# Cop supports --auto-correct. +# This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: Exclude: - 'lib/luhnacy_lib/luhnacy_lib.rb' -# Offense count: 16 -# Cop supports --auto-correct. +# Offense count: 9 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: Mode. Style/StringConcatenation: Exclude: - - 'app/helpers/user_helper.rb' - 'app/models/organization.rb' - - 'app/services/api_client.rb' - 'spec/lib/dpc_middleware/ig_fix_spec.rb' - 'spec/lib/luhnacy_lib_spec.rb' # Offense count: 10 -# Cop supports --auto-correct. +# This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: @@ -235,25 +225,9 @@ Style/StringLiterals: - 'spec/features/authentication/user_resets_password_spec.rb' - 'spec/models/user_spec.rb' -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowMethodsWithArguments, IgnoredMethods. -# IgnoredMethods: respond_to, define_method -Style/SymbolProc: - Exclude: - - 'spec/factories/registered_organizations.rb' - -# Offense count: 42 -# Cop supports --auto-correct. -# Configuration parameters: WordRegex. -# SupportedStyles: percent, brackets -Style/WordArray: - EnforcedStyle: percent - MinSize: 3 - -# Offense count: 8 -# Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings. # URISchemes: http, https Layout/LineLength: - Max: 251 + Max: 183 diff --git a/dpc-web/Gemfile b/dpc-web/Gemfile index c5eb6dc318..aea7490aef 100644 --- a/dpc-web/Gemfile +++ b/dpc-web/Gemfile @@ -32,7 +32,7 @@ gem 'bootsnap', '>= 1.1.0', require: false gem 'sassc-rails', '>= 2.1.2' gem 'uglifier', '>= 1.3.0' gem 'pg', '>= 0.18', '< 2.0' -gem 'devise', '>= 4.9.3' +gem 'devise', '>= 5.0.3' gem 'devise-async', '>= 1.0.0' gem 'devise-security', '>= 0.17.0' gem 'truemail' diff --git a/dpc-web/Gemfile.lock b/dpc-web/Gemfile.lock index 7c57a37c2e..e212dfeb3f 100644 --- a/dpc-web/Gemfile.lock +++ b/dpc-web/Gemfile.lock @@ -82,7 +82,7 @@ GEM uri (>= 0.13.1) addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) - ast (2.4.2) + ast (2.4.3) aws-eventstream (1.4.0) aws-partitions (1.1148.0) aws-sdk-core (3.229.0) @@ -117,7 +117,7 @@ GEM base64 (0.3.0) bcp47 (0.3.3) i18n - bcrypt (3.1.20) + bcrypt (3.1.22) benchmark (0.4.1) bigdecimal (3.1.7) bindex (0.8.1) @@ -165,10 +165,10 @@ GEM date_time_precision (0.8.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devise (4.9.4) + devise (5.0.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0) + railties (>= 7.0) responders warden (~> 1.2.3) devise-async (1.0.0) @@ -227,7 +227,10 @@ GEM rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.18.1) + json (2.19.1) + json-schema (6.2.0) + addressable (~> 2.8) + bigdecimal (>= 3.1, < 5) jsonapi-renderer (0.2.2) jwt (3.1.2) base64 @@ -245,7 +248,7 @@ GEM kaminari-core (1.2.2) kramdown (2.4.0) rexml - language_server-protocol (3.17.0.3) + language_server-protocol (3.17.0.5) launchy (3.0.0) addressable (~> 2.8) childprocess (~> 5.0) @@ -256,6 +259,7 @@ GEM letter_opener (~> 1.7) railties (>= 5.2) rexml + lint_roller (1.1.0) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -280,6 +284,8 @@ GEM net-smtp marcel (1.0.4) matrix (0.4.2) + mcp (0.8.0) + json-schema (>= 4.1) method_source (1.1.0) mime-types (3.5.2) mime-types-data (~> 3.2015) @@ -317,8 +323,8 @@ GEM version_gem (~> 1.1, >= 1.1.8) orm_adapter (0.5.0) ostruct (0.6.2) - parallel (1.26.3) - parser (3.3.6.0) + parallel (1.27.0) + parser (3.3.10.2) ast (~> 2.4.1) racc pg (1.5.6) @@ -328,6 +334,7 @@ GEM pp (0.6.2) prettyprint prettyprint (0.2.0) + prism (1.9.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -380,14 +387,14 @@ GEM rdoc (6.14.2) erb psych (>= 4.0.0) - regexp_parser (2.9.3) + regexp_parser (2.11.3) reline (0.6.2) io-console (~> 0.5) request_store (1.6.0) rack (>= 1.4) - responders (3.1.1) - actionpack (>= 5.2) - railties (>= 5.2) + responders (3.2.0) + actionpack (>= 7.0) + railties (>= 7.0) rexml (3.4.4) rspec-core (3.13.0) rspec-support (~> 3.13.0) @@ -406,18 +413,21 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rubocop (1.69.1) + rubocop (1.85.1) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + mcp (~> 0.6) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.36.2, < 2.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.36.2) - parser (>= 3.3.1.0) + rubocop-ast (1.49.1) + parser (>= 3.3.7.2) + prism (~> 1.7) rubocop-performance (1.23.0) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) @@ -475,9 +485,9 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.9.1) - unicode-display_width (3.1.2) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) uri (1.1.1) useragent (0.16.11) vcr (6.2.0) @@ -530,7 +540,7 @@ DEPENDENCIES climate_control coffee-rails (~> 5.0, >= 5.0.0) csv - devise (>= 4.9.3) + devise (>= 5.0.3) devise-async (>= 1.0.0) devise-security (>= 0.17.0) dotenv-rails (>= 2.8.1) diff --git a/dpc-web/app/models/concerns/taggable.rb b/dpc-web/app/models/concerns/taggable.rb index 4e17140e8a..056273d885 100644 --- a/dpc-web/app/models/concerns/taggable.rb +++ b/dpc-web/app/models/concerns/taggable.rb @@ -2,6 +2,7 @@ module Taggable extend ActiveSupport::Concern + included do has_many :taggings, as: :taggable has_many :tags, through: :taggings diff --git a/dpc-web/spec/features/authentication/user_resets_password_spec.rb b/dpc-web/spec/features/authentication/user_resets_password_spec.rb index 58a8f3551d..b6f415eb5d 100644 --- a/dpc-web/spec/features/authentication/user_resets_password_spec.rb +++ b/dpc-web/spec/features/authentication/user_resets_password_spec.rb @@ -4,6 +4,7 @@ RSpec.feature 'user resets password' do include ActiveJob::TestHelper + let(:user) { create :user } context 'when successful' do diff --git a/dpc-web/spec/features/confirmation/user_confirmation_email_spec.rb b/dpc-web/spec/features/confirmation/user_confirmation_email_spec.rb index d848bfc920..30b57cf6c8 100644 --- a/dpc-web/spec/features/confirmation/user_confirmation_email_spec.rb +++ b/dpc-web/spec/features/confirmation/user_confirmation_email_spec.rb @@ -4,6 +4,7 @@ RSpec.feature 'user resends confirmation instructions' do include ActiveJob::TestHelper + let(:user) { create :user, confirmed_at: nil } context 'when successful' do diff --git a/dpc-web/spec/features/portal/managing_api_credentials_spec.rb b/dpc-web/spec/features/portal/managing_api_credentials_spec.rb index 37b58cf3cc..ee5236c70a 100644 --- a/dpc-web/spec/features/portal/managing_api_credentials_spec.rb +++ b/dpc-web/spec/features/portal/managing_api_credentials_spec.rb @@ -4,6 +4,7 @@ RSpec.feature 'managing api credentials' do include DpcClientSupport + context 'as an unassigned user' do let!(:user) { create :user } diff --git a/dpc-web/spec/models/user_spec.rb b/dpc-web/spec/models/user_spec.rb index 3368a7e760..81132c4620 100644 --- a/dpc-web/spec/models/user_spec.rb +++ b/dpc-web/spec/models/user_spec.rb @@ -5,6 +5,7 @@ RSpec.describe User, type: :model do include ActiveJob::TestHelper + subject { create :user } describe 'factory' do diff --git a/dpc-web/spec/system/accessibility_spec.rb b/dpc-web/spec/system/accessibility_spec.rb index 7a44d680de..2d1d73161b 100644 --- a/dpc-web/spec/system/accessibility_spec.rb +++ b/dpc-web/spec/system/accessibility_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'Accessibility', type: :system do include Devise::Test::IntegrationHelpers include DpcClientSupport + before do driven_by(:selenium_headless) end From 4397aa28b1f24fc5d0a353460fe52a089617e179 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Mon, 30 Mar 2026 07:54:28 -0700 Subject: [PATCH 10/48] merge main --- .github/workflows/check_healthy.yml | 5 +- .github/workflows/docker-build.yml | 27 +- .github/workflows/ecs-deploy.yml | 13 +- .github/workflows/ecs-release.yml | 6 +- .github/workflows/smoke-test.yml | 1 + docker-compose.portals.yml | 4 + dpc-admin/Gemfile | 22 +- dpc-admin/Gemfile.lock | 145 +++++------ dpc-admin/package-lock.json | 12 +- dpc-load-testing/yarn.lock | 6 +- dpc-portal/.rubocop.yml | 2 + dpc-portal/.rubocop_todo.yml | 42 ++++ dpc-portal/Gemfile | 22 +- dpc-portal/Gemfile.lock | 232 +++++++++--------- .../organization_list_row_component.rb | 1 + .../components/core/table/header_component.rb | 3 +- dpc-portal/app/concerns/credential_manager.rb | 1 + .../controllers/organizations_controller.rb | 3 +- dpc-portal/package-lock.json | 12 +- .../core/modal/modal_component_spec.rb | 1 + .../client_token/new_token_component_spec.rb | 1 + .../client_token/show_token_component_spec.rb | 1 + .../list_component_spec.rb | 1 + .../new_invitation_component_spec.rb | 1 + .../accept_invitation_component_spec.rb | 1 + .../invitation_login_component_spec.rb | 1 + .../ip_address/new_address_component_spec.rb | 1 + .../compound_show_component_spec.rb | 1 + .../credentials_componenent_spec.rb | 1 + .../new_organization_component_spec.rb | 1 + ...new_organization_success_component_spec.rb | 1 + .../organization/tos_form_component_spec.rb | 1 + .../page/public_key/new_key_component_spec.rb | 1 + .../page/session/login_component_spec.rb | 1 + dpc-portal/spec/requests/invitations_spec.rb | 1 + .../spec/requests/users/sessions_spec.rb | 1 + .../auto_session_logout_service_spec.rb | 1 + .../services/client_token_manager_spec.rb | 1 + dpc-portal/spec/system/accessibility_spec.rb | 1 + dpc-test.sh | 5 +- dpc-web/Gemfile | 22 +- dpc-web/Gemfile.lock | 156 ++++++------ dpc-web/package-lock.json | 6 +- 43 files changed, 448 insertions(+), 320 deletions(-) create mode 100644 dpc-portal/.rubocop_todo.yml diff --git a/.github/workflows/check_healthy.yml b/.github/workflows/check_healthy.yml index cc8f90764a..472f93eed5 100644 --- a/.github/workflows/check_healthy.yml +++ b/.github/workflows/check_healthy.yml @@ -12,7 +12,10 @@ on: jobs: wait-for-services: name: Wait for services to be healthy - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + runs-on: >- + codebuild-dpc-app${{ + contains(fromJSON('["prod", "sandbox"]'), inputs.env) && '-prod' || '-non-prod' + }}-${{ github.run_id }}-${{ github.run_attempt }} strategy: matrix: include: diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index e79f4450e8..0acd4133cf 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -4,6 +4,11 @@ on: workflow_dispatch: workflow_call: inputs: + env: + description: AWS environment to deploy to + required: true + type: 'string' + default: 'dev' block_release_tag: description: Whether should block marking build as release type: boolean @@ -32,7 +37,10 @@ jobs: permissions: contents: read id-token: write - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + runs-on: >- + codebuild-dpc-app${{ + contains(fromJSON('["prod", "sandbox"]'), inputs.env) && '-prod' || '-non-prod' + }}-${{ github.run_id }}-${{ github.run_attempt }} outputs: version_tag: ${{ steps.get-version-tag.outputs.version_tag }} docker_tag: ${{ steps.output_docker_tag.outputs.docker_tag }} @@ -64,7 +72,10 @@ jobs: permissions: contents: read id-token: write - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + runs-on: >- + codebuild-dpc-app${{ + contains(fromJSON('["prod", "sandbox"]'), inputs.env) && '-prod' || '-non-prod' + }}-${{ github.run_id }}-${{ github.run_attempt }} strategy: matrix: app_dir: [ dpc-portal, dpc-admin, dpc-web ] @@ -106,7 +117,8 @@ jobs: permissions: contents: read id-token: write - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + # Will be updated as part of DPC-5307 + runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} steps: - name: "Set up Ansible" run: pip install ansible @@ -122,6 +134,10 @@ jobs: java-version: "17" distribution: "corretto" cache: maven + - name: Debug Environment + run: | + uname -m # Should output: aarch64 + docker info | grep -i arch - name: Clean maven run: mvn -ntp -U clean @@ -174,7 +190,10 @@ jobs: permissions: contents: read id-token: write - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + runs-on: >- + codebuild-dpc-app${{ + contains(fromJSON('["prod", "sandbox"]'), inputs.env) && '-prod' || '-non-prod' + }}-${{ github.run_id }}-${{ github.run_attempt }} strategy: matrix: ecr_repository: [ web-portal, web-admin, web, api, attribution, aggregation ] diff --git a/.github/workflows/ecs-deploy.yml b/.github/workflows/ecs-deploy.yml index 2a87e55ab2..2caea6aebb 100644 --- a/.github/workflows/ecs-deploy.yml +++ b/.github/workflows/ecs-deploy.yml @@ -73,7 +73,10 @@ jobs: permissions: contents: read id-token: write - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + runs-on: >- + codebuild-dpc-app${{ + contains(fromJSON('["prod", "sandbox"]'), inputs.env) && '-prod' || '-non-prod' + }}-${{ github.run_id }}-${{ github.run_attempt }} outputs: image_tag: ${{ steps.image-tag.outputs.image_tag }} steps: @@ -145,7 +148,7 @@ jobs: uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2 - name: Install Tofu - uses: cmsgov/cdap/actions/setup-tenv@8343fb96563ce4b74c4dececee9b268f42bd4a40 + uses: cmsgov/cdap/actions/setup-tenv@f4c14d47cc20e7f6de9112d7155af1213c9bca5a - name: Verify persistent plan run: | @@ -199,6 +202,7 @@ jobs: permissions: contents: read id-token: write + # Will be updated as part of DPC-5315 runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} needs: deploy steps: @@ -269,7 +273,10 @@ jobs: permissions: contents: read id-token: write - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + runs-on: >- + codebuild-dpc-app${{ + contains(fromJSON('["prod", "sandbox"]'), inputs.env) && '-prod' || '-non-prod' + }}-${{ github.run_id }}-${{ github.run_attempt }} steps: - uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0 name: Slack Success diff --git a/.github/workflows/ecs-release.yml b/.github/workflows/ecs-release.yml index 66c2162f23..14406b8f44 100644 --- a/.github/workflows/ecs-release.yml +++ b/.github/workflows/ecs-release.yml @@ -58,7 +58,10 @@ permissions: jobs: validate-parameters: name: "Validate Parameters" - runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} + runs-on: >- + codebuild-dpc-app${{ + contains(fromJSON('["prod", "sandbox"]'), inputs.env) && '-prod' || '-non-prod' + }}-${{ github.run_id }}-${{ github.run_attempt }} outputs: version_tag: ${{ steps.get-version-tag.outputs.version_tag }} steps: @@ -74,6 +77,7 @@ jobs: - validate-parameters uses: ./.github/workflows/docker-build.yml with: + env: ${{ inputs.env || 'dev' }} block_release_tag: ${{ github.event_name != 'workflow_dispatch' }} secrets: inherit diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml index bbe9816b21..05d5268611 100644 --- a/.github/workflows/smoke-test.yml +++ b/.github/workflows/smoke-test.yml @@ -30,6 +30,7 @@ jobs: run: working-directory: ./dpc-load-testing name: Smoke Test + # Will be updated as part of DPC-5315 runs-on: codebuild-dpc-app-${{github.run_id}}-${{github.run_attempt}} steps: - uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0 diff --git a/docker-compose.portals.yml b/docker-compose.portals.yml index 8b4a6bd04a..831f23b5a0 100644 --- a/docker-compose.portals.yml +++ b/docker-compose.portals.yml @@ -6,6 +6,7 @@ services: redis: image: artifactory.cloud.cms.gov/docker/redis:latest + platform: linux/arm64 healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s @@ -18,6 +19,7 @@ services: context: . dockerfile: dpc-web/Dockerfile image: dpc-web:latest + platform: linux/arm64 volumes: - "./dpc-web/certs:/dpc-web/certs" - "./dpc-web/coverage:/dpc-web/coverage" @@ -60,6 +62,7 @@ services: context: . dockerfile: dpc-admin/Dockerfile image: dpc-web-admin:latest + platform: linux/arm64 volumes: - "./dpc-admin/coverage:/dpc-admin/coverage" - ./tmp/letter_opener/admin:/dpc-admin/tmp/letter_opener @@ -98,6 +101,7 @@ services: context: . dockerfile: dpc-portal/Dockerfile image: dpc-web-portal:latest + platform: linux/arm64 volumes: # Mount specific directories to avoid overwriting # precompiled assets (public/assets/) and node_modules diff --git a/dpc-admin/Gemfile b/dpc-admin/Gemfile index c7ba51eeea..b6cd603173 100644 --- a/dpc-admin/Gemfile +++ b/dpc-admin/Gemfile @@ -5,17 +5,17 @@ ruby '~>3.3' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' # Specifying rails components to be able to omit unused actioncable -gem 'actionmailbox', '~> 8.0.2.1' -gem 'actionmailer', '~> 8.0.2.1' -gem 'actionpack', '~> 8.0.2.1' -gem 'actiontext', '~> 8.0.2.1' -gem 'actionview', '~> 8.0.2.1' -gem 'activejob', '~> 8.0.2.1' -gem 'activemodel', '~> 8.0.2.1' -gem 'activerecord', '~> 8.0.2.1' -gem 'activestorage', '~> 8.0.2.1' -gem 'activesupport', '~> 8.0.2.1' -gem 'railties', '~> 8.0.2.1' +gem 'actionmailbox', '~> 8.0.4.1' +gem 'actionmailer', '~> 8.0.4.1' +gem 'actionpack', '~> 8.0.4.1' +gem 'actiontext', '~> 8.0.4.1' +gem 'actionview', '~> 8.0.4.1' +gem 'activejob', '~> 8.0.4.1' +gem 'activemodel', '~> 8.0.4.1' +gem 'activerecord', '~> 8.0.4.1' +gem 'activestorage', '~> 8.0.4.1' +gem 'activesupport', '~> 8.0.4.1' +gem 'railties', '~> 8.0.4.1' gem 'bundler', '>= 1.15.0' gem 'dotenv-rails', groups: [:development, :test] gem 'puma', '~> 6.4.3' diff --git a/dpc-admin/Gemfile.lock b/dpc-admin/Gemfile.lock index 701195ed16..5744807c0d 100644 --- a/dpc-admin/Gemfile.lock +++ b/dpc-admin/Gemfile.lock @@ -10,23 +10,23 @@ PATH GEM remote: https://rubygems.org/ specs: - actionmailbox (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailbox (8.0.4.1) + actionpack (= 8.0.4.1) + activejob (= 8.0.4.1) + activerecord (= 8.0.4.1) + activestorage (= 8.0.4.1) + activesupport (= 8.0.4.1) mail (>= 2.8.0) - actionmailer (8.0.2.1) - actionpack (= 8.0.2.1) - actionview (= 8.0.2.1) - activejob (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailer (8.0.4.1) + actionpack (= 8.0.4.1) + actionview (= 8.0.4.1) + activejob (= 8.0.4.1) + activesupport (= 8.0.4.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.2.1) - actionview (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionpack (8.0.4.1) + actionview (= 8.0.4.1) + activesupport (= 8.0.4.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -34,15 +34,15 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.2.1) - actionpack (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actiontext (8.0.4.1) + actionpack (= 8.0.4.1) + activerecord (= 8.0.4.1) + activestorage (= 8.0.4.1) + activesupport (= 8.0.4.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.2.1) - activesupport (= 8.0.2.1) + actionview (8.0.4.1) + activesupport (= 8.0.4.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -52,22 +52,22 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (8.0.2.1) - activesupport (= 8.0.2.1) + activejob (8.0.4.1) + activesupport (= 8.0.4.1) globalid (>= 0.3.6) - activemodel (8.0.2.1) - activesupport (= 8.0.2.1) - activerecord (8.0.2.1) - activemodel (= 8.0.2.1) - activesupport (= 8.0.2.1) + activemodel (8.0.4.1) + activesupport (= 8.0.4.1) + activerecord (8.0.4.1) + activemodel (= 8.0.4.1) + activesupport (= 8.0.4.1) timeout (>= 0.4.0) - activestorage (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activesupport (= 8.0.2.1) + activestorage (8.0.4.1) + actionpack (= 8.0.4.1) + activejob (= 8.0.4.1) + activerecord (= 8.0.4.1) + activesupport (= 8.0.4.1) marcel (~> 1.0) - activesupport (8.0.2.1) + activesupport (8.0.4.1) base64 benchmark (>= 0.3) bigdecimal @@ -76,7 +76,7 @@ GEM drb i18n (>= 1.6, < 2) logger (>= 1.4.2) - minitest (>= 5.1) + minitest (>= 5.1, < 6) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) @@ -102,7 +102,7 @@ GEM bcp47 (0.3.3) i18n bcrypt (3.1.22) - benchmark (0.4.1) + benchmark (0.5.0) bigdecimal (3.1.7) bindex (0.8.1) bootsnap (1.18.3) @@ -127,7 +127,7 @@ GEM climate_control (1.2.0) coderay (1.1.3) concurrent-ruby (1.3.4) - connection_pool (2.4.1) + connection_pool (3.0.2) crack (1.0.0) bigdecimal rexml @@ -157,7 +157,7 @@ GEM dotenv (= 3.1.0) railties (>= 6.1) drb (2.2.3) - erb (5.0.2) + erb (6.0.2) erubi (1.13.1) et-orbi (1.3.0) tzinfo @@ -183,7 +183,7 @@ GEM fugit (1.11.2) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) hashdiff (1.1.0) hashie (5.0.0) @@ -191,13 +191,14 @@ GEM railties (>= 5.0) i18n (1.14.4) concurrent-ruby (~> 1.0) - io-console (0.8.1) - irb (1.15.2) + io-console (0.8.2) + irb (1.17.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.19.1) + json (2.19.3) json-schema (6.2.0) addressable (~> 2.8) bigdecimal (>= 3.1, < 5) @@ -232,7 +233,7 @@ GEM activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - loofah (2.24.1) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) luhnacy (0.2.2) @@ -240,12 +241,13 @@ GEM multi_json (~> 1.10) rbnacl (~> 5.0) rbnacl-libsodium (~> 1.0) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.4) + marcel (1.1.0) matrix (0.4.2) mcp (0.8.0) json-schema (>= 4.1) @@ -255,7 +257,7 @@ GEM mime-types-data (3.2024.0507) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.25.5) + minitest (5.27.0) msgpack (1.7.2) multi_json (1.17.0) multi_xml (0.7.2) @@ -310,7 +312,7 @@ GEM pg-aws_rds_iam (0.7.0) aws-sdk-rds (~> 1.0) pg (~> 1.1) - pp (0.6.2) + pp (0.6.3) prettyprint prettyprint (0.2.0) prism (1.9.0) @@ -319,7 +321,7 @@ GEM method_source (~> 1.0) pry-nav (1.0.0) pry (>= 0.9.10, < 0.15) - psych (5.2.6) + psych (5.3.1) date stringio public_suffix (5.0.5) @@ -337,7 +339,7 @@ GEM rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -347,19 +349,20 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.0.2.1) - actionpack (= 8.0.2.1) - activesupport (= 8.0.2.1) + railties (8.0.4.1) + actionpack (= 8.0.4.1) + activesupport (= 8.0.4.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.0) + rake (13.3.1) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) @@ -367,11 +370,12 @@ GEM ffi rbnacl-libsodium (1.0.16) rbnacl (>= 3.0.1) - rdoc (6.14.2) + rdoc (7.2.0) erb psych (>= 4.0.0) + tsort regexp_parser (2.11.3) - reline (0.6.2) + reline (0.6.3) io-console (~> 0.5) request_store (1.6.0) rack (>= 1.4) @@ -460,12 +464,13 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - stringio (3.1.7) + stringio (3.2.0) thor (1.4.0) tilt (2.3.0) timeout (0.4.3) truemail (3.3.1) simpleidn (~> 0.2.1) + tsort (0.2.0) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) @@ -500,23 +505,23 @@ GEM websocket (1.2.10) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.3) + zeitwerk (2.7.5) PLATFORMS ruby DEPENDENCIES - actionmailbox (~> 8.0.2.1) - actionmailer (~> 8.0.2.1) - actionpack (~> 8.0.2.1) - actiontext (~> 8.0.2.1) - actionview (~> 8.0.2.1) + actionmailbox (~> 8.0.4.1) + actionmailer (~> 8.0.4.1) + actionpack (~> 8.0.4.1) + actiontext (~> 8.0.4.1) + actionview (~> 8.0.4.1) active_model_serializers - activejob (~> 8.0.2.1) - activemodel (~> 8.0.2.1) - activerecord (~> 8.0.2.1) - activestorage (~> 8.0.2.1) - activesupport (~> 8.0.2.1) + activejob (~> 8.0.4.1) + activemodel (~> 8.0.4.1) + activerecord (~> 8.0.4.1) + activestorage (~> 8.0.4.1) + activesupport (~> 8.0.4.1) api_client! bootsnap (>= 1.4.2) bundler (>= 1.15.0) @@ -554,7 +559,7 @@ DEPENDENCIES rack (>= 3.2.3) rack-session (>= 2.1.1) rails-controller-testing - railties (~> 8.0.2.1) + railties (~> 8.0.4.1) rbnacl rbnacl-libsodium rexml (>= 3.4.2) diff --git a/dpc-admin/package-lock.json b/dpc-admin/package-lock.json index bbe9d2dde0..443e5cee26 100644 --- a/dpc-admin/package-lock.json +++ b/dpc-admin/package-lock.json @@ -3142,9 +3142,9 @@ } }, "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -3732,9 +3732,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { diff --git a/dpc-load-testing/yarn.lock b/dpc-load-testing/yarn.lock index a9a46e7b6f..b5f39f6bdf 100644 --- a/dpc-load-testing/yarn.lock +++ b/dpc-load-testing/yarn.lock @@ -2126,9 +2126,9 @@ picocolors@^1.1.1: integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== pirates@^4.0.4: version "4.0.7" diff --git a/dpc-portal/.rubocop.yml b/dpc-portal/.rubocop.yml index 03b9ce4a91..0358b56192 100644 --- a/dpc-portal/.rubocop.yml +++ b/dpc-portal/.rubocop.yml @@ -1,3 +1,5 @@ +inherit_from: .rubocop_todo.yml + AllCops: SuggestExtensions: false NewCops: enable diff --git a/dpc-portal/.rubocop_todo.yml b/dpc-portal/.rubocop_todo.yml new file mode 100644 index 0000000000..e1777e9b84 --- /dev/null +++ b/dpc-portal/.rubocop_todo.yml @@ -0,0 +1,42 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2026-03-18 23:10:44 UTC using RuboCop version 1.85.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 6 +# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. +# AllowedMethods: call +# WaywardPredicates: infinite?, nonzero? +Naming/PredicateMethod: + Exclude: + - 'app/controllers/public_keys_controller.rb' + - 'app/services/client_token_manager.rb' + - 'app/services/ip_address_manager.rb' + - 'app/services/public_key_manager.rb' + - 'spec/support/match_html_fragment.rb' + +# Offense count: 9 +# Configuration parameters: AllowedClasses. +Style/OneClassPerFile: + Exclude: + - 'app/jobs/sync_organization_job.rb' + - 'app/models/invitation.rb' + - 'app/services/ao_invitation_service.rb' + - 'app/services/ao_verification_service.rb' + - 'app/services/user_info_service.rb' + - 'spec/jobs/sync_organization_job_spec.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/PredicateWithKind: + Exclude: + - 'app/controllers/organizations_controller.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/RedundantStructKeywordInit: + Exclude: + - 'app/components/core/table/header_component.rb' diff --git a/dpc-portal/Gemfile b/dpc-portal/Gemfile index 8c176c10b4..d5805356ce 100644 --- a/dpc-portal/Gemfile +++ b/dpc-portal/Gemfile @@ -6,18 +6,18 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '~>3.3' # Specifying rails components to be able to omit unused actioncable -gem 'actionmailbox', '~> 8.0.2.1' -gem 'actionmailer', '~> 8.0.2.1' -gem 'actionpack', '~> 8.0.2.1' -gem 'actiontext', '~> 8.0.2.1' -gem 'actionview', '~> 8.0.2.1' -gem 'activejob', '~> 8.0.2.1' -gem 'activemodel', '~> 8.0.2.1' +gem 'actionmailbox', '~> 8.0.4.1' +gem 'actionmailer', '~> 8.0.4.1' +gem 'actionpack', '~> 8.0.4.1' +gem 'actiontext', '~> 8.0.4.1' +gem 'actionview', '~> 8.0.4.1' +gem 'activejob', '~> 8.0.4.1' +gem 'activemodel', '~> 8.0.4.1' gem 'active_model_serializers' -gem 'activerecord', '~> 8.0.2.1' +gem 'activerecord', '~> 8.0.4.1' gem 'activerecord-session_store' -gem 'activestorage', '~> 8.0.2.1' -gem 'activesupport', '~> 8.0.2.1' +gem 'activestorage', '~> 8.0.4.1' +gem 'activesupport', '~> 8.0.4.1' gem 'audited' gem 'auto-session-timeout' gem 'aws-sdk-cloudwatch' @@ -42,7 +42,7 @@ gem 'pg', '>= 0.18', '< 2.0' gem 'puma', '~> 6.4.3' gem 'rack', '>= 3.2.3' gem 'rack-session', '>= 2.1.1' -gem 'railties', '~> 8.0.2.1' +gem 'railties', '~> 8.0.4.1' gem 'rexml', '>= 3.4.2' gem 'sassc-rails', '>= 2.1.2' gem 'sinatra', '>= 4.2.0' diff --git a/dpc-portal/Gemfile.lock b/dpc-portal/Gemfile.lock index a315b5499a..7341c2b1e1 100644 --- a/dpc-portal/Gemfile.lock +++ b/dpc-portal/Gemfile.lock @@ -10,23 +10,23 @@ PATH GEM remote: https://rubygems.org/ specs: - actionmailbox (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailbox (8.0.4.1) + actionpack (= 8.0.4.1) + activejob (= 8.0.4.1) + activerecord (= 8.0.4.1) + activestorage (= 8.0.4.1) + activesupport (= 8.0.4.1) mail (>= 2.8.0) - actionmailer (8.0.2.1) - actionpack (= 8.0.2.1) - actionview (= 8.0.2.1) - activejob (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailer (8.0.4.1) + actionpack (= 8.0.4.1) + actionview (= 8.0.4.1) + activejob (= 8.0.4.1) + activesupport (= 8.0.4.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.2.1) - actionview (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionpack (8.0.4.1) + actionview (= 8.0.4.1) + activesupport (= 8.0.4.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -34,15 +34,15 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.2.1) - actionpack (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actiontext (8.0.4.1) + actionpack (= 8.0.4.1) + activerecord (= 8.0.4.1) + activestorage (= 8.0.4.1) + activesupport (= 8.0.4.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.2.1) - activesupport (= 8.0.2.1) + actionview (8.0.4.1) + activesupport (= 8.0.4.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -52,14 +52,14 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (8.0.2.1) - activesupport (= 8.0.2.1) + activejob (8.0.4.1) + activesupport (= 8.0.4.1) globalid (>= 0.3.6) - activemodel (8.0.2.1) - activesupport (= 8.0.2.1) - activerecord (8.0.2.1) - activemodel (= 8.0.2.1) - activesupport (= 8.0.2.1) + activemodel (8.0.4.1) + activesupport (= 8.0.4.1) + activerecord (8.0.4.1) + activemodel (= 8.0.4.1) + activesupport (= 8.0.4.1) timeout (>= 0.4.0) activerecord-session_store (2.1.0) actionpack (>= 6.1) @@ -68,13 +68,13 @@ GEM multi_json (~> 1.11, >= 1.11.2) rack (>= 2.0.8, < 4) railties (>= 6.1) - activestorage (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activesupport (= 8.0.2.1) + activestorage (8.0.4.1) + actionpack (= 8.0.4.1) + activejob (= 8.0.4.1) + activerecord (= 8.0.4.1) + activesupport (= 8.0.4.1) marcel (~> 1.0) - activesupport (8.0.2.1) + activesupport (8.0.4.1) base64 benchmark (>= 0.3) bigdecimal @@ -83,14 +83,14 @@ GEM drb i18n (>= 1.6, < 2) logger (>= 1.4.2) - minitest (>= 5.1) + minitest (>= 5.1, < 6) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) aes_key_wrap (1.1.0) - ast (2.4.2) + ast (2.4.3) attr_required (1.0.2) audited (5.8.0) activerecord (>= 5.2, < 8.2) @@ -128,12 +128,12 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) - base64 (0.3.0) + base64 (0.2.0) bcp47 (0.3.3) i18n benchmark (0.5.0) - bigdecimal (3.3.1) - bindata (2.5.1) + bigdecimal (3.1.8) + bindata (2.5.0) bootsnap (1.18.4) msgpack (~> 1.2) builder (3.3.0) @@ -157,7 +157,7 @@ GEM coderay (1.1.3) coercible (1.0.0) descendants_tracker (~> 0.0.1) - concurrent-ruby (1.3.6) + concurrent-ruby (1.3.4) connection_pool (3.0.2) crack (1.0.0) bigdecimal @@ -165,7 +165,7 @@ GEM crass (1.0.6) css_parser (1.17.1) addressable - date (3.5.1) + date (3.4.1) date_time_precision (0.8.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) @@ -179,7 +179,7 @@ GEM dumb_delegator (1.0.0) email_validator (2.2.4) activemodel - erb (5.0.2) + erb (6.0.2) erubi (1.13.1) et-orbi (1.2.11) tzinfo @@ -194,7 +194,7 @@ GEM faraday-net_http (>= 2.0, < 3.5) json logger - faraday-follow_redirects (0.4.0) + faraday-follow_redirects (0.3.0) faraday (>= 1, < 3) faraday-net_http (3.4.2) net-http (~> 0.5) @@ -207,37 +207,40 @@ GEM fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) hashdiff (1.1.1) - hashie (5.1.0) - logger + hashie (5.0.0) health_check (3.1.0) railties (>= 5.0) htmlbeautifier (1.4.3) htmlentities (4.3.4) - i18n (1.14.8) + i18n (1.14.6) concurrent-ruby (~> 1.0) ice_nine (0.11.2) - io-console (0.8.1) - irb (1.15.2) + io-console (0.8.2) + irb (1.17.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jbuilder (2.12.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) - json (2.9.0) - json-jwt (1.17.0) + json (2.19.2) + json-jwt (1.16.6) activesupport (>= 4.2) aes_key_wrap base64 bindata faraday (~> 2.0) faraday-follow_redirects + json-schema (6.2.0) + addressable (~> 2.8) + bigdecimal (>= 3.1, < 5) jsonapi-renderer (0.2.2) - jwt (2.8.2) + jwt (3.1.2) base64 kaminari (1.2.2) activesupport (>= 4.1.0) @@ -251,7 +254,8 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) - language_server-protocol (3.17.0.3) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -261,7 +265,7 @@ GEM activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - loofah (2.24.1) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) lookbook (2.3.2) @@ -281,26 +285,26 @@ GEM multi_json (~> 1.10) rbnacl (~> 5.0) rbnacl-libsodium (~> 1.0) - mail (2.9.0) - logger + mail (2.8.1) mini_mime (>= 0.1.1) net-imap net-pop net-smtp marcel (1.0.4) matrix (0.4.2) + mcp (0.8.0) + json-schema (>= 4.1) method_source (1.1.0) mime-types (3.5.2) mime-types-data (~> 3.2015) mime-types-data (3.2024.0820) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (6.0.0) - prism (~> 1.5) + minitest (5.27.0) msgpack (1.7.2) multi_json (1.15.0) - multi_xml (0.7.1) - bigdecimal (~> 3.1) + multi_xml (0.8.1) + bigdecimal (>= 3.1, < 5) mustermann (3.0.4) ruby2_keywords (~> 0.0.1) net-http (0.9.1) @@ -312,23 +316,23 @@ GEM net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.1) + net-smtp (0.5.0) net-protocol newrelic_rpm (8.16.0) nio4r (2.7.3) nokogiri (1.19.1) mini_portile2 (~> 2.8.2) racc (~> 1.4) - oauth2 (2.0.9) - faraday (>= 0.17.3, < 3.0) - jwt (>= 1.0, < 3.0) + oauth2 (2.0.18) + faraday (>= 0.17.3, < 4.0) + jwt (>= 1.0, < 4.0) + logger (~> 1.2) multi_xml (~> 0.5) rack (>= 1.2, < 4) - snaky_hash (~> 2.0) - version_gem (~> 1.1) - omniauth (2.1.4) + snaky_hash (~> 2.0, >= 2.0.3) + version_gem (~> 1.1, >= 1.1.9) + omniauth (2.1.2) hashie (>= 3.4.6) - logger rack (>= 2.2.3) rack-protection omniauth-rails_csrf_protection (1.0.2) @@ -337,7 +341,7 @@ GEM omniauth_openid_connect (0.8.0) omniauth (>= 1.9, < 3) openid_connect (~> 2.2) - openid_connect (2.3.1) + openid_connect (2.3.0) activemodel attr_required (>= 1.0.0) email_validator @@ -351,33 +355,33 @@ GEM validate_url webfinger (~> 2.0) ostruct (0.6.0) - parallel (1.26.3) - parser (3.3.6.0) + parallel (1.27.0) + parser (3.3.10.2) ast (~> 2.4.1) racc pg (1.5.7) pg-aws_rds_iam (0.7.0) aws-sdk-rds (~> 1.0) pg (~> 1.1) - pp (0.6.2) + pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.7.0) + prism (1.9.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) pry-nav (1.0.0) pry (>= 0.9.10, < 0.15) - psych (5.2.6) + psych (5.3.1) date stringio - public_suffix (6.0.2) + public_suffix (6.0.1) puma (6.4.3) nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) rack (3.2.5) - rack-oauth2 (2.3.0) + rack-oauth2 (2.2.1) activesupport attr_required faraday (~> 2.0) @@ -393,7 +397,7 @@ GEM rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -403,19 +407,20 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.0.2.1) - actionpack (= 8.0.2.1) - activesupport (= 8.0.2.1) + railties (8.0.4.1) + actionpack (= 8.0.4.1) + activesupport (= 8.0.4.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.0) + rake (13.3.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) @@ -423,12 +428,13 @@ GEM ffi rbnacl-libsodium (1.0.16) rbnacl (>= 3.0.1) - rdoc (6.14.2) + rdoc (7.2.0) erb psych (>= 4.0.0) + tsort redcarpet (3.6.0) - regexp_parser (2.9.3) - reline (0.6.2) + regexp_parser (2.11.3) + reline (0.6.3) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) @@ -451,18 +457,21 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rubocop (1.69.1) + rubocop (1.85.1) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + mcp (~> 0.6) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.36.2, < 2.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.36.2) - parser (>= 3.3.1.0) + rubocop-ast (1.49.1) + parser (>= 3.3.7.2) + prism (~> 1.7) rubocop-performance (1.23.0) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) @@ -495,9 +504,9 @@ GEM rack-protection (= 4.2.1) rack-session (>= 2.0.0, < 3) tilt (~> 2.0) - snaky_hash (2.0.1) - hashie - version_gem (~> 1.1, >= 1.1.1) + snaky_hash (2.0.3) + hashie (>= 0.1.0, < 6) + version_gem (>= 1.1.8, < 3) solid_queue (1.2.1) activejob (>= 7.1) activerecord (>= 7.1) @@ -516,7 +525,7 @@ GEM actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) - stringio (3.1.7) + stringio (3.2.0) swd (2.0.3) activesupport (>= 3) attr_required (>= 0.0.5) @@ -526,22 +535,23 @@ GEM thread_safe (0.3.6) tilt (2.4.0) timecop (0.9.10) - timeout (0.6.0) + timeout (0.4.3) truemail (3.3.1) simpleidn (~> 0.2.1) + tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uglifier (4.2.0) execjs (>= 0.3.0, < 3) - unicode-display_width (3.1.2) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) uri (1.1.1) useragent (0.16.11) validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix - version_gem (1.1.4) + version_gem (1.1.9) view_component (3.19.0) activesupport (>= 5.2.0, < 8.1) concurrent-ruby (~> 1.0) @@ -573,18 +583,18 @@ PLATFORMS ruby DEPENDENCIES - actionmailbox (~> 8.0.2.1) - actionmailer (~> 8.0.2.1) - actionpack (~> 8.0.2.1) - actiontext (~> 8.0.2.1) - actionview (~> 8.0.2.1) + actionmailbox (~> 8.0.4.1) + actionmailer (~> 8.0.4.1) + actionpack (~> 8.0.4.1) + actiontext (~> 8.0.4.1) + actionview (~> 8.0.4.1) active_model_serializers - activejob (~> 8.0.2.1) - activemodel (~> 8.0.2.1) - activerecord (~> 8.0.2.1) + activejob (~> 8.0.4.1) + activemodel (~> 8.0.4.1) + activerecord (~> 8.0.4.1) activerecord-session_store - activestorage (~> 8.0.2.1) - activesupport (~> 8.0.2.1) + activestorage (~> 8.0.4.1) + activesupport (~> 8.0.4.1) api_client! audited auto-session-timeout @@ -622,7 +632,7 @@ DEPENDENCIES rack (>= 3.2.3) rack-session (>= 2.1.1) rails-controller-testing - railties (~> 8.0.2.1) + railties (~> 8.0.4.1) rbnacl rbnacl-libsodium rexml (>= 3.4.2) diff --git a/dpc-portal/app/components/core/organization_list_row/organization_list_row_component.rb b/dpc-portal/app/components/core/organization_list_row/organization_list_row_component.rb index ec05311ecb..a6d2906544 100644 --- a/dpc-portal/app/components/core/organization_list_row/organization_list_row_component.rb +++ b/dpc-portal/app/components/core/organization_list_row/organization_list_row_component.rb @@ -5,6 +5,7 @@ module OrganizationListRow # Render a USWDS-styled card for an organization. class OrganizationListRowComponent < ViewComponent::Base include OrganizationUtils + with_collection_parameter :link def initialize(link:) diff --git a/dpc-portal/app/components/core/table/header_component.rb b/dpc-portal/app/components/core/table/header_component.rb index 6d5460c977..705dd1c73c 100644 --- a/dpc-portal/app/components/core/table/header_component.rb +++ b/dpc-portal/app/components/core/table/header_component.rb @@ -6,8 +6,7 @@ module Table class HeaderComponent < ViewComponent::Base Column = Struct.new( :label, - :sortable, - keyword_init: true + :sortable ) attr_reader :caption, :columns diff --git a/dpc-portal/app/concerns/credential_manager.rb b/dpc-portal/app/concerns/credential_manager.rb index 54d765fa19..bfb7fd2073 100644 --- a/dpc-portal/app/concerns/credential_manager.rb +++ b/dpc-portal/app/concerns/credential_manager.rb @@ -3,6 +3,7 @@ # Shared functions of credential managers module CredentialManager extend ActiveSupport::Concern + attr_reader :api_id, :errors SERVER_ERROR_MSG = "We're sorry but we can't complete your request. Please try again tomorrow." diff --git a/dpc-portal/app/controllers/organizations_controller.rb b/dpc-portal/app/controllers/organizations_controller.rb index c081f3c8cf..fb2d94f477 100644 --- a/dpc-portal/app/controllers/organizations_controller.rb +++ b/dpc-portal/app/controllers/organizations_controller.rb @@ -3,6 +3,7 @@ # Shows Credential Delegates info about the organizations they manage the credentials for class OrganizationsController < ApplicationController include OrganizationUtils + before_action :authenticate_user! before_action :check_user_verification before_action :load_organization, only: %i[show tos_form sign_tos success] @@ -13,7 +14,7 @@ class OrganizationsController < ApplicationController def index @links = current_user.provider_links - ao_or_cd = @links.any? { |link| link.is_a?(AoOrgLink) } + ao_or_cd = @links.any?(AoOrgLink) render(Page::Organization::OrganizationListComponent.new(ao_or_cd:, links: @links)) end diff --git a/dpc-portal/package-lock.json b/dpc-portal/package-lock.json index 988f73b280..deafa40413 100644 --- a/dpc-portal/package-lock.json +++ b/dpc-portal/package-lock.json @@ -2061,9 +2061,9 @@ "optional": true }, "node_modules/node-forge": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", - "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { @@ -2194,9 +2194,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { diff --git a/dpc-portal/spec/components/core/modal/modal_component_spec.rb b/dpc-portal/spec/components/core/modal/modal_component_spec.rb index e7ed95bebf..5630e92ca5 100644 --- a/dpc-portal/spec/components/core/modal/modal_component_spec.rb +++ b/dpc-portal/spec/components/core/modal/modal_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Core::Modal::ModalComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/client_token/new_token_component_spec.rb b/dpc-portal/spec/components/page/client_token/new_token_component_spec.rb index c8f7d45563..8b4b5f6fc3 100644 --- a/dpc-portal/spec/components/page/client_token/new_token_component_spec.rb +++ b/dpc-portal/spec/components/page/client_token/new_token_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::ClientToken::NewTokenComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/client_token/show_token_component_spec.rb b/dpc-portal/spec/components/page/client_token/show_token_component_spec.rb index ccc396f3b4..1e697d1184 100644 --- a/dpc-portal/spec/components/page/client_token/show_token_component_spec.rb +++ b/dpc-portal/spec/components/page/client_token/show_token_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::ClientToken::ShowTokenComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/credential_delegate/list_component_spec.rb b/dpc-portal/spec/components/page/credential_delegate/list_component_spec.rb index 185449520d..277e7d1a63 100644 --- a/dpc-portal/spec/components/page/credential_delegate/list_component_spec.rb +++ b/dpc-portal/spec/components/page/credential_delegate/list_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::CredentialDelegate::ListComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/credential_delegate/new_invitation_component_spec.rb b/dpc-portal/spec/components/page/credential_delegate/new_invitation_component_spec.rb index 4b58f43df5..db15a08d15 100644 --- a/dpc-portal/spec/components/page/credential_delegate/new_invitation_component_spec.rb +++ b/dpc-portal/spec/components/page/credential_delegate/new_invitation_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::CredentialDelegate::NewInvitationComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/invitations/accept_invitation_component_spec.rb b/dpc-portal/spec/components/page/invitations/accept_invitation_component_spec.rb index 2e087b06bc..d3eb85f496 100644 --- a/dpc-portal/spec/components/page/invitations/accept_invitation_component_spec.rb +++ b/dpc-portal/spec/components/page/invitations/accept_invitation_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Invitations::AcceptInvitationComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/invitations/invitation_login_component_spec.rb b/dpc-portal/spec/components/page/invitations/invitation_login_component_spec.rb index 82f21914b1..2235aaa590 100644 --- a/dpc-portal/spec/components/page/invitations/invitation_login_component_spec.rb +++ b/dpc-portal/spec/components/page/invitations/invitation_login_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Invitations::InvitationLoginComponent, type: :component do include ComponentSupport + describe 'login component' do let(:provider_organization) { build(:provider_organization, dpc_api_organization_id: 'foo') } let(:invitation) { create(:invitation, :cd, provider_organization:) } diff --git a/dpc-portal/spec/components/page/ip_address/new_address_component_spec.rb b/dpc-portal/spec/components/page/ip_address/new_address_component_spec.rb index ef45628a22..d99de162d8 100644 --- a/dpc-portal/spec/components/page/ip_address/new_address_component_spec.rb +++ b/dpc-portal/spec/components/page/ip_address/new_address_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::IpAddress::NewAddressComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/organization/compound_show_component_spec.rb b/dpc-portal/spec/components/page/organization/compound_show_component_spec.rb index 3941eb2107..690d94cdcf 100644 --- a/dpc-portal/spec/components/page/organization/compound_show_component_spec.rb +++ b/dpc-portal/spec/components/page/organization/compound_show_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Organization::CompoundShowComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/organization/credentials_componenent_spec.rb b/dpc-portal/spec/components/page/organization/credentials_componenent_spec.rb index f53ec01e4c..2d4cde8b87 100644 --- a/dpc-portal/spec/components/page/organization/credentials_componenent_spec.rb +++ b/dpc-portal/spec/components/page/organization/credentials_componenent_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Organization::CredentialsComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/organization/new_organization_component_spec.rb b/dpc-portal/spec/components/page/organization/new_organization_component_spec.rb index 9c74b96f6a..97d2ae7074 100644 --- a/dpc-portal/spec/components/page/organization/new_organization_component_spec.rb +++ b/dpc-portal/spec/components/page/organization/new_organization_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Organization::NewOrganizationComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/organization/new_organization_success_component_spec.rb b/dpc-portal/spec/components/page/organization/new_organization_success_component_spec.rb index 4813343a3a..9168d38293 100644 --- a/dpc-portal/spec/components/page/organization/new_organization_success_component_spec.rb +++ b/dpc-portal/spec/components/page/organization/new_organization_success_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Organization::NewOrganizationSuccessComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/organization/tos_form_component_spec.rb b/dpc-portal/spec/components/page/organization/tos_form_component_spec.rb index 40f733de28..21efdc3edd 100644 --- a/dpc-portal/spec/components/page/organization/tos_form_component_spec.rb +++ b/dpc-portal/spec/components/page/organization/tos_form_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Organization::TosFormComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/public_key/new_key_component_spec.rb b/dpc-portal/spec/components/page/public_key/new_key_component_spec.rb index 828f8e9fe7..a8a679a72a 100644 --- a/dpc-portal/spec/components/page/public_key/new_key_component_spec.rb +++ b/dpc-portal/spec/components/page/public_key/new_key_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::PublicKey::NewKeyComponent, type: :component do include ComponentSupport + describe 'html' do subject(:html) do render_inline(component) diff --git a/dpc-portal/spec/components/page/session/login_component_spec.rb b/dpc-portal/spec/components/page/session/login_component_spec.rb index f4e6b01484..859c31c087 100644 --- a/dpc-portal/spec/components/page/session/login_component_spec.rb +++ b/dpc-portal/spec/components/page/session/login_component_spec.rb @@ -4,6 +4,7 @@ RSpec.describe Page::Session::LoginComponent, type: :component do include ComponentSupport + describe 'login component' do let(:url) { '/' } let(:sandbox_url) { 'https://sandbox.dpc.cms.gov/' } diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index 194c8663aa..26a243224a 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'Invitations', type: :request do include LoginSupport + RSpec.shared_examples 'an invitation endpoint' do |method, path_suffix, type| let(:org) { invitation.provider_organization } let(:bad_org) { create(:provider_organization) } diff --git a/dpc-portal/spec/requests/users/sessions_spec.rb b/dpc-portal/spec/requests/users/sessions_spec.rb index 2de93e35bb..09c65ec833 100644 --- a/dpc-portal/spec/requests/users/sessions_spec.rb +++ b/dpc-portal/spec/requests/users/sessions_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'Sessions', type: :request do include LoginSupport + describe 'logout' do context 'logged in' do let!(:user) { create(:user) } diff --git a/dpc-portal/spec/services/auto_session_logout_service_spec.rb b/dpc-portal/spec/services/auto_session_logout_service_spec.rb index 3cb3206099..5408bb0e32 100644 --- a/dpc-portal/spec/services/auto_session_logout_service_spec.rb +++ b/dpc-portal/spec/services/auto_session_logout_service_spec.rb @@ -5,6 +5,7 @@ RSpec.describe 'AutoSessionLogoutService', type: :request do include LoginSupport + let(:user) { create(:user) } before { sign_in user } diff --git a/dpc-portal/spec/services/client_token_manager_spec.rb b/dpc-portal/spec/services/client_token_manager_spec.rb index eae115726b..2e88ae1604 100644 --- a/dpc-portal/spec/services/client_token_manager_spec.rb +++ b/dpc-portal/spec/services/client_token_manager_spec.rb @@ -4,6 +4,7 @@ RSpec.describe ClientTokenManager do include DpcClientSupport + let(:api_id) { SecureRandom.uuid } let(:manager) { ClientTokenManager.new(api_id) } describe '#create_client_token' do diff --git a/dpc-portal/spec/system/accessibility_spec.rb b/dpc-portal/spec/system/accessibility_spec.rb index d85b43e1fb..1366a9859e 100644 --- a/dpc-portal/spec/system/accessibility_spec.rb +++ b/dpc-portal/spec/system/accessibility_spec.rb @@ -4,6 +4,7 @@ RSpec.describe 'Accessibility', type: :system do include DpcClientSupport + before do driven_by(:selenium_headless) end diff --git a/dpc-test.sh b/dpc-test.sh index 0cf19ea465..99bc343d9b 100755 --- a/dpc-test.sh +++ b/dpc-test.sh @@ -63,7 +63,10 @@ fi docker compose -p start-v1-app down -USE_BFD_MOCK=true docker compose -p start-v1-app up db attribution aggregation --wait +# Temporary fix while DPC-5265 is in progress. Remove once we have a better solution. +#USE_BFD_MOCK=true docker compose -p start-v1-app up db attribution aggregation --wait +USE_BFD_MOCK=true docker compose -p start-v1-app up db attribution --wait +USE_BFD_MOCK=true docker compose -p start-v1-app up aggregation --wait # Run the integration tests USE_BFD_MOCK=true docker compose -p start-v1-app up --exit-code-from tests tests diff --git a/dpc-web/Gemfile b/dpc-web/Gemfile index aea7490aef..29dc8377d3 100644 --- a/dpc-web/Gemfile +++ b/dpc-web/Gemfile @@ -7,17 +7,17 @@ ruby '~>3.3' # Anchored versions, do not bump without testing # Specifying rails components to be able to omit unused actioncable -gem 'actionmailbox', '~> 8.0.2.1' -gem 'actionmailer', '~> 8.0.2.1' -gem 'actionpack', '~> 8.0.2.1' -gem 'actiontext', '~> 8.0.2.1' -gem 'actionview', '~> 8.0.2.1' -gem 'activejob', '~> 8.0.2.1' -gem 'activemodel', '~> 8.0.2.1' -gem 'activerecord', '~> 8.0.2.1' -gem 'activestorage', '~> 8.0.2.1' -gem 'activesupport', '~> 8.0.2.1' -gem 'railties', '~> 8.0.2.1' +gem 'actionmailbox', '~> 8.0.4.1' +gem 'actionmailer', '~> 8.0.4.1' +gem 'actionpack', '~> 8.0.4.1' +gem 'actiontext', '~> 8.0.4.1' +gem 'actionview', '~> 8.0.4.1' +gem 'activejob', '~> 8.0.4.1' +gem 'activemodel', '~> 8.0.4.1' +gem 'activerecord', '~> 8.0.4.1' +gem 'activestorage', '~> 8.0.4.1' +gem 'activesupport', '~> 8.0.4.1' +gem 'railties', '~> 8.0.4.1' gem 'csv' gem 'bundler', '>= 1.15.0' gem 'sprockets-rails', '>= 3.4.2' diff --git a/dpc-web/Gemfile.lock b/dpc-web/Gemfile.lock index e212dfeb3f..73e37983e1 100644 --- a/dpc-web/Gemfile.lock +++ b/dpc-web/Gemfile.lock @@ -10,23 +10,23 @@ PATH GEM remote: https://rubygems.org/ specs: - actionmailbox (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailbox (8.0.4.1) + actionpack (= 8.0.4.1) + activejob (= 8.0.4.1) + activerecord (= 8.0.4.1) + activestorage (= 8.0.4.1) + activesupport (= 8.0.4.1) mail (>= 2.8.0) - actionmailer (8.0.2.1) - actionpack (= 8.0.2.1) - actionview (= 8.0.2.1) - activejob (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailer (8.0.4.1) + actionpack (= 8.0.4.1) + actionview (= 8.0.4.1) + activejob (= 8.0.4.1) + activesupport (= 8.0.4.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.2.1) - actionview (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionpack (8.0.4.1) + actionview (= 8.0.4.1) + activesupport (= 8.0.4.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -34,15 +34,15 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.2.1) - actionpack (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actiontext (8.0.4.1) + actionpack (= 8.0.4.1) + activerecord (= 8.0.4.1) + activestorage (= 8.0.4.1) + activesupport (= 8.0.4.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.2.1) - activesupport (= 8.0.2.1) + actionview (8.0.4.1) + activesupport (= 8.0.4.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -52,22 +52,22 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (8.0.2.1) - activesupport (= 8.0.2.1) + activejob (8.0.4.1) + activesupport (= 8.0.4.1) globalid (>= 0.3.6) - activemodel (8.0.2.1) - activesupport (= 8.0.2.1) - activerecord (8.0.2.1) - activemodel (= 8.0.2.1) - activesupport (= 8.0.2.1) + activemodel (8.0.4.1) + activesupport (= 8.0.4.1) + activerecord (8.0.4.1) + activemodel (= 8.0.4.1) + activesupport (= 8.0.4.1) timeout (>= 0.4.0) - activestorage (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activesupport (= 8.0.2.1) + activestorage (8.0.4.1) + actionpack (= 8.0.4.1) + activejob (= 8.0.4.1) + activerecord (= 8.0.4.1) + activesupport (= 8.0.4.1) marcel (~> 1.0) - activesupport (8.0.2.1) + activesupport (8.0.4.1) base64 benchmark (>= 0.3) bigdecimal @@ -76,7 +76,7 @@ GEM drb i18n (>= 1.6, < 2) logger (>= 1.4.2) - minitest (>= 5.1) + minitest (>= 5.1, < 6) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) @@ -118,7 +118,7 @@ GEM bcp47 (0.3.3) i18n bcrypt (3.1.22) - benchmark (0.4.1) + benchmark (0.5.0) bigdecimal (3.1.7) bindex (0.8.1) bootsnap (1.18.3) @@ -155,7 +155,7 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.3.4) - connection_pool (2.4.1) + connection_pool (3.0.2) crack (1.0.0) bigdecimal rexml @@ -184,7 +184,7 @@ GEM railties (>= 6.1) drb (2.2.3) dumb_delegator (1.1.0) - erb (5.0.2) + erb (6.0.2) erubi (1.13.1) et-orbi (1.3.0) tzinfo @@ -212,22 +212,24 @@ GEM fugit (1.11.2) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) hashdiff (1.1.0) - hashie (5.0.0) + hashie (5.1.0) + logger health_check (3.1.0) railties (>= 5.0) i18n (1.14.4) concurrent-ruby (~> 1.0) ice_nine (0.11.2) - io-console (0.8.1) - irb (1.15.2) + io-console (0.8.2) + irb (1.17.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jmespath (1.6.2) - json (2.19.1) + json (2.19.3) json-schema (6.2.0) addressable (~> 2.8) bigdecimal (>= 3.1, < 5) @@ -269,7 +271,7 @@ GEM activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - loofah (2.24.1) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) luhnacy (0.2.2) @@ -277,12 +279,13 @@ GEM multi_json (~> 1.10) rbnacl (~> 5.0) rbnacl-libsodium (~> 1.0) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.4) + marcel (1.1.0) matrix (0.4.2) mcp (0.8.0) json-schema (>= 4.1) @@ -292,11 +295,11 @@ GEM mime-types-data (3.2024.0507) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.25.5) + minitest (5.27.0) msgpack (1.7.2) multi_json (1.17.0) - multi_xml (0.7.2) - bigdecimal (~> 3.1) + multi_xml (0.8.1) + bigdecimal (>= 3.1, < 5) net-http (0.9.1) uri (>= 0.11.1) net-imap (0.5.8) @@ -313,14 +316,14 @@ GEM nokogiri (1.19.1) mini_portile2 (~> 2.8.2) racc (~> 1.4) - oauth2 (2.0.14) + oauth2 (2.0.18) faraday (>= 0.17.3, < 4.0) jwt (>= 1.0, < 4.0) logger (~> 1.2) multi_xml (~> 0.5) rack (>= 1.2, < 4) snaky_hash (~> 2.0, >= 2.0.3) - version_gem (~> 1.1, >= 1.1.8) + version_gem (~> 1.1, >= 1.1.9) orm_adapter (0.5.0) ostruct (0.6.2) parallel (1.27.0) @@ -331,7 +334,7 @@ GEM pg-aws_rds_iam (0.7.0) aws-sdk-rds (~> 1.0) pg (~> 1.1) - pp (0.6.2) + pp (0.6.3) prettyprint prettyprint (0.2.0) prism (1.9.0) @@ -340,7 +343,7 @@ GEM method_source (~> 1.0) pry-nav (1.0.0) pry (>= 0.9.10, < 0.15) - psych (5.2.6) + psych (5.3.1) date stringio public_suffix (5.0.5) @@ -354,7 +357,7 @@ GEM rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -364,19 +367,20 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.0.2.1) - actionpack (= 8.0.2.1) - activesupport (= 8.0.2.1) + railties (8.0.4.1) + actionpack (= 8.0.4.1) + activesupport (= 8.0.4.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.0) + rake (13.3.1) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) @@ -384,11 +388,12 @@ GEM ffi rbnacl-libsodium (1.0.16) rbnacl (>= 3.0.1) - rdoc (6.14.2) + rdoc (7.2.0) erb psych (>= 4.0.0) + tsort regexp_parser (2.11.3) - reline (0.6.2) + reline (0.6.3) io-console (~> 0.5) request_store (1.6.0) rack (>= 1.4) @@ -471,13 +476,14 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - stringio (3.1.7) + stringio (3.2.0) thor (1.4.0) thread_safe (0.3.6) tilt (2.3.0) timeout (0.4.3) truemail (3.3.1) simpleidn (~> 0.2.1) + tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uglifier (4.2.0) @@ -510,23 +516,23 @@ GEM websocket (1.2.10) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.3) + zeitwerk (2.7.5) PLATFORMS ruby DEPENDENCIES - actionmailbox (~> 8.0.2.1) - actionmailer (~> 8.0.2.1) - actionpack (~> 8.0.2.1) - actiontext (~> 8.0.2.1) - actionview (~> 8.0.2.1) + actionmailbox (~> 8.0.4.1) + actionmailer (~> 8.0.4.1) + actionpack (~> 8.0.4.1) + actiontext (~> 8.0.4.1) + actionview (~> 8.0.4.1) active_model_serializers (>= 0.10.13) - activejob (~> 8.0.2.1) - activemodel (~> 8.0.2.1) - activerecord (~> 8.0.2.1) - activestorage (~> 8.0.2.1) - activesupport (~> 8.0.2.1) + activejob (~> 8.0.4.1) + activemodel (~> 8.0.4.1) + activerecord (~> 8.0.4.1) + activestorage (~> 8.0.4.1) + activesupport (~> 8.0.4.1) api_client! axe-core-capybara axe-core-rspec @@ -568,7 +574,7 @@ DEPENDENCIES rack (>= 3.2.3) rack-session (>= 2.1.1) rails-controller-testing (>= 1.0.5) - railties (~> 8.0.2.1) + railties (~> 8.0.4.1) rexml (>= 3.4.2) rspec-rails (>= 5.1.2) rubocop diff --git a/dpc-web/package-lock.json b/dpc-web/package-lock.json index b0da06085b..31a282392a 100644 --- a/dpc-web/package-lock.json +++ b/dpc-web/package-lock.json @@ -3222,9 +3222,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { From a90696d6462a73d013d294c892168630933fdfe1 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Tue, 7 Apr 2026 07:20:33 -0700 Subject: [PATCH 11/48] undevise javascript test --- dpc-portal/spec/system/new_invitation_spec.rb | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/dpc-portal/spec/system/new_invitation_spec.rb b/dpc-portal/spec/system/new_invitation_spec.rb index a594cf7ba9..60c57c0b61 100644 --- a/dpc-portal/spec/system/new_invitation_spec.rb +++ b/dpc-portal/spec/system/new_invitation_spec.rb @@ -3,21 +3,32 @@ require 'rails_helper' RSpec.describe Page::CredentialDelegate::NewInvitationComponent, type: :system, js: true do - include Devise::Test::IntegrationHelpers include DpcClientSupport before do driven_by(:selenium_headless) end + let(:uid) { '12345' } + before do + OmniAuth.config.test_mode = true + OmniAuth.config.add_mock(:login_dot_gov, + { uid:, + info: { email: 'bob@example.com' }, + extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], + ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) + end + def sign_in + visit '/auth/login_dot_gov/callback' + end context 'CD invite' do let(:dpc_api_organization_id) { 'some-gnarly-guid' } - let!(:user) { create(:user) } + let!(:user) { create(:user, provider: :login_dot_gov, uid: '12345') } let!(:org) { create(:provider_organization, dpc_api_organization_id:, name: 'Health Hut') } let!(:ao_org_link) { create(:ao_org_link, user:, provider_organization: org) } before do - sign_in user + sign_in org.update!(terms_of_service_accepted_by: user) end From 20e90ffc48469d6c9a993db2618aa2f0ca3b0533 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Wed, 8 Apr 2026 07:30:08 -0700 Subject: [PATCH 12/48] add login support to integration tests --- dpc-portal/spec/integration/client_tokens_spec.rb | 3 +++ dpc-portal/spec/integration/ip_addresses_spec.rb | 3 +++ dpc-portal/spec/integration/public_keys_spec.rb | 3 +++ 3 files changed, 9 insertions(+) diff --git a/dpc-portal/spec/integration/client_tokens_spec.rb b/dpc-portal/spec/integration/client_tokens_spec.rb index 94a40ea062..d9675b2be2 100644 --- a/dpc-portal/spec/integration/client_tokens_spec.rb +++ b/dpc-portal/spec/integration/client_tokens_spec.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' RSpec.describe 'ClientTokens', type: :request do + include LoginSupport + before(:example) { WebMock.disable_net_connect!(allow_localhost: true, allow: ['api']) } after(:example) { WebMock.disable_net_connect!(allow_localhost: true) } diff --git a/dpc-portal/spec/integration/ip_addresses_spec.rb b/dpc-portal/spec/integration/ip_addresses_spec.rb index 6b5a40fcb3..76e1fc7a8e 100644 --- a/dpc-portal/spec/integration/ip_addresses_spec.rb +++ b/dpc-portal/spec/integration/ip_addresses_spec.rb @@ -1,8 +1,11 @@ # frozen_string_literal: true require 'rails_helper' +require 'support/login_support' RSpec.describe 'IpAddresses', type: :request do + include LoginSupport + before(:example) { WebMock.disable_net_connect!(allow_localhost: true, allow: ['api']) } after(:example) { WebMock.disable_net_connect!(allow_localhost: true) } diff --git a/dpc-portal/spec/integration/public_keys_spec.rb b/dpc-portal/spec/integration/public_keys_spec.rb index 96e6c2067c..1736cd23e8 100644 --- a/dpc-portal/spec/integration/public_keys_spec.rb +++ b/dpc-portal/spec/integration/public_keys_spec.rb @@ -3,8 +3,11 @@ require 'base64' require 'openssl' require 'rails_helper' +require 'support/login_support' RSpec.describe 'PublicKeys', type: :request do + include LoginSupport + before(:example) { WebMock.disable_net_connect!(allow_localhost: true, allow: ['api']) } after(:example) { WebMock.disable_net_connect!(allow_localhost: true) } From 05b8bc19409dc0d4a66fe69c7d71bfe151b8b7e9 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Wed, 22 Apr 2026 18:03:24 -0400 Subject: [PATCH 13/48] POC: Replace Login.gov with ID.me --- docker-compose.portals.yml | 6 +++--- dpc-portal/Procfile.dev | 4 ++-- .../app/controllers/application_controller.rb | 2 +- .../app/controllers/invitations_controller.rb | 2 +- dpc-portal/config/environments/test.rb | 2 +- dpc-portal/config/initializers/devise.rb | 17 +++++------------ dpc-portal/lib/dpc_portal_utils.rb | 2 +- .../jobs/verify_resource_health_job_spec.rb | 4 ++-- dpc-portal/spec/requests/invitations_spec.rb | 2 +- .../spec/services/user_info_service_spec.rb | 2 +- 10 files changed, 18 insertions(+), 25 deletions(-) diff --git a/docker-compose.portals.yml b/docker-compose.portals.yml index 22d8d27cb0..9b9d6a21f7 100644 --- a/docker-compose.portals.yml +++ b/docker-compose.portals.yml @@ -142,7 +142,7 @@ services: - DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL=true - CPI_API_GW_BASE_URL=http://localhost:4567/ - CMS_IDM_OAUTH_URL=http://localhost:4567/ - - IDP_HOST=idp.int.identitysandbox.gov + - IDP_HOST=api.idmelabs.com - RUBY_YJIT_ENABLE=1 - ENV=local - NEW_RELIC_MONITOR_MODE=false @@ -150,12 +150,12 @@ services: - RAILS_DEVELOPMENT_HOSTS=host.docker.internal - SKIP_SIMPLE_COV=${SKIP_SIMPLE_COV:-} ports: - - "3100:3100" + - "3000:3000" depends_on: db: condition: service_healthy healthcheck: - test: curl --fail http://localhost:3100/health_check || exit 1 + test: curl --fail http://localhost:3000/health_check || exit 1 interval: 10s timeout: 5s start_period: 30s diff --git a/dpc-portal/Procfile.dev b/dpc-portal/Procfile.dev index 81515e22da..39f2fd8bb7 100644 --- a/dpc-portal/Procfile.dev +++ b/dpc-portal/Procfile.dev @@ -1,3 +1,3 @@ -web: bundle exec rails server -b 0.0.0.0 -p 3100 +web: bundle exec rails server -b 0.0.0.0 -p 3000 solidqueue: bundle exec rails solid_queue:start -cpigw: env PORT=4567 ruby spec/support/fake_cpi_gateway.rb \ No newline at end of file +cpigw: env PORT=4567 ruby spec/support/fake_cpi_gateway.rb diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index e47488dc32..e3111260d8 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -3,7 +3,7 @@ # Parent class of all controllers class ApplicationController < ActionController::Base IDP_HOST = ENV.fetch('IDP_HOST') - IDP_CLIENT_ID = "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV.fetch('ENV')}".freeze + IDP_CLIENT_ID = "925bb2985ccf623114359caa76228919" before_action :check_session_length before_action :set_current_request_attributes diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index 140b0f3c83..8e8426b34f 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -79,7 +79,7 @@ def login actionType: LoggingConstants::ActionType::BeginLogin, invitation: @invitation.id }]) url = URI::HTTPS.build(host: IDP_HOST, - path: '/openid_connect/authorize', + path: '/oauth/authorize', query: { acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', client_id: IDP_CLIENT_ID, redirect_uri: "#{my_protocol_host}/users/auth/openid_connect/callback", diff --git a/dpc-portal/config/environments/test.rb b/dpc-portal/config/environments/test.rb index 0d31929fb0..b4b1b86380 100644 --- a/dpc-portal/config/environments/test.rb +++ b/dpc-portal/config/environments/test.rb @@ -69,4 +69,4 @@ end ENV['CPI_API_GW_BASE_URL'] = 'https://val.cpiapi.cms.gov/' ENV['CMS_IDM_OAUTH_URL'] = 'https://impl.idp.idm.cms.gov/' -ENV['IDP_HOST'] = 'idp.int.identitysandbox.gov' +ENV['IDP_HOST'] = 'api.idmelabs.com' diff --git a/dpc-portal/config/initializers/devise.rb b/dpc-portal/config/initializers/devise.rb index 73227be795..3d611c54db 100644 --- a/dpc-portal/config/initializers/devise.rb +++ b/dpc-portal/config/initializers/devise.rb @@ -13,18 +13,12 @@ Devise.setup do |config| include DpcPortalUtils - begin - private_key = OpenSSL::PKey::RSA.new(ENV['LOGIN_GOV_PRIVATE_KEY']) - rescue TypeError, OpenSSL::PKey::RSAError => e - Rails.logger.error("Unable to create private key for omniauth: #{e}") - private_key = OpenSSL::PKey::RSA.new(1024) - end - idp_host = ENV.fetch('IDP_HOST', 'idp.int.identitysandbox.gov') + idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') config.omniauth :openid_connect, { name: :openid_connect, - issuer: "https://#{idp_host}/", + issuer: "https://#{idp_host}/oidc", discovery: true, - scope: %i[openid email all_emails], + scope: %i[openid http://idmanagement.gov/ns/assurance/ial/1/aal/1], response_type: :code, acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', client_auth_method: :jwt_bearer, @@ -32,9 +26,8 @@ port: 443, scheme: 'https', host: idp_host, - identifier: "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV['ENV']}", - private_key: private_key, - redirect_uri: "#{my_protocol_host}/users/auth/openid_connect/callback" + identifier: "925bb2985ccf623114359caa76228919", + redirect_uri: "#{my_protocol_host}" } } # The secret key used by Devise. Devise uses this key to generate diff --git a/dpc-portal/lib/dpc_portal_utils.rb b/dpc-portal/lib/dpc_portal_utils.rb index b4689412a3..b4327720ca 100644 --- a/dpc-portal/lib/dpc_portal_utils.rb +++ b/dpc-portal/lib/dpc_portal_utils.rb @@ -6,7 +6,7 @@ def my_protocol_host env = ENV.fetch('ENV', nil) case env when 'local' - 'http://localhost:3100' + 'http://localhost:3000' else host_name = ENV.fetch('HOST_NAME', nil) Rails.logger.error 'HOST_NAME is not set by env' if host_name.nil? diff --git a/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb b/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb index 80139da3ae..834fa6b59f 100644 --- a/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb +++ b/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb @@ -85,7 +85,7 @@ context 'not connected to AWS' do it 'should ignore connection error and move on gracefully' do - stub_request(:get, 'https://idp.int.identitysandbox.gov').to_return(status: 200) + stub_request(:get, 'https://api.idmelabs.com').to_return(status: 200) expect(mock_dpc_client).to receive(:healthcheck) expect(mock_dpc_client).to receive(:response_successful?).and_return(true).twice @@ -149,7 +149,7 @@ def expect_cpi(auth_health: true, api_health: true, metric: 1) end def expect_idp(site_status: 200, metric: 1) - stub_request(:get, 'https://idp.int.identitysandbox.gov').to_return(status: site_status) + stub_request(:get, 'https://api.idmelabs.com').to_return(status: site_status) expect_put_metric('PortalConnectedToIdp', metric) end diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index 2ccc0c7b19..ddca1ada9e 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -838,7 +838,7 @@ def log_in def user_info_template(overrides = {}) { 'sub' => '097d06f7-e9ad-4327-8db3-0ba193b7a2c2', - 'iss' => 'https://idp.int.identitysandbox.gov/', + 'iss' => 'https://api.idmelabs.com/oidc', 'email' => 'bob@testy.com', 'email_verified' => true, 'all_emails' => [ diff --git a/dpc-portal/spec/services/user_info_service_spec.rb b/dpc-portal/spec/services/user_info_service_spec.rb index 7e05896bc7..faf10bb18b 100644 --- a/dpc-portal/spec/services/user_info_service_spec.rb +++ b/dpc-portal/spec/services/user_info_service_spec.rb @@ -14,7 +14,7 @@ let(:response) do { 'sub' => '097d06f7-e9ad-4327-8db3-0ba193b7a2c2', - 'iss' => 'https://idp.int.identitysandbox.gov/', + 'iss' => 'https://api.idmelabs.com/oidc', 'email' => 'david@example.com', 'email_verified' => true, 'all_emails' => [ From ca211a9d76895f4935ad8aa57a24d216cac626d2 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Fri, 24 Apr 2026 09:36:42 -0400 Subject: [PATCH 14/48] Update devise.rb --- dpc-portal/config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/config/initializers/devise.rb b/dpc-portal/config/initializers/devise.rb index 3d611c54db..d6b8b0e78e 100644 --- a/dpc-portal/config/initializers/devise.rb +++ b/dpc-portal/config/initializers/devise.rb @@ -18,7 +18,7 @@ name: :openid_connect, issuer: "https://#{idp_host}/oidc", discovery: true, - scope: %i[openid http://idmanagement.gov/ns/assurance/ial/1/aal/1], + scope: %i[openid http://idmanagement.gov/ns/assurance/ial/2/aal/2], response_type: :code, acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', client_auth_method: :jwt_bearer, From 3d9e8a4f1ee4f0d45f08c5d2af74951ab4064a93 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Fri, 24 Apr 2026 10:07:52 -0400 Subject: [PATCH 15/48] Single quotes --- dpc-portal/app/controllers/application_controller.rb | 2 +- dpc-portal/config/initializers/devise.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index e3111260d8..de74edb29f 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -3,7 +3,7 @@ # Parent class of all controllers class ApplicationController < ActionController::Base IDP_HOST = ENV.fetch('IDP_HOST') - IDP_CLIENT_ID = "925bb2985ccf623114359caa76228919" + IDP_CLIENT_ID = '925bb2985ccf623114359caa76228919' before_action :check_session_length before_action :set_current_request_attributes diff --git a/dpc-portal/config/initializers/devise.rb b/dpc-portal/config/initializers/devise.rb index d6b8b0e78e..644fe333b5 100644 --- a/dpc-portal/config/initializers/devise.rb +++ b/dpc-portal/config/initializers/devise.rb @@ -26,7 +26,7 @@ port: 443, scheme: 'https', host: idp_host, - identifier: "925bb2985ccf623114359caa76228919", + identifier: '925bb2985ccf623114359caa76228919', redirect_uri: "#{my_protocol_host}" } } From d215f5c02d19c215ab7ecefc8e68131ce1f0421e Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Mon, 27 Apr 2026 09:03:45 -0400 Subject: [PATCH 16/48] Use port 3100 --- docker-compose.portals.yml | 4 ++-- dpc-portal/Procfile.dev | 4 ++-- dpc-portal/lib/dpc_portal_utils.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.portals.yml b/docker-compose.portals.yml index 9b9d6a21f7..90d8eed5e9 100644 --- a/docker-compose.portals.yml +++ b/docker-compose.portals.yml @@ -150,12 +150,12 @@ services: - RAILS_DEVELOPMENT_HOSTS=host.docker.internal - SKIP_SIMPLE_COV=${SKIP_SIMPLE_COV:-} ports: - - "3000:3000" + - "3100:3100" depends_on: db: condition: service_healthy healthcheck: - test: curl --fail http://localhost:3000/health_check || exit 1 + test: curl --fail http://localhost:3100/health_check || exit 1 interval: 10s timeout: 5s start_period: 30s diff --git a/dpc-portal/Procfile.dev b/dpc-portal/Procfile.dev index 39f2fd8bb7..81515e22da 100644 --- a/dpc-portal/Procfile.dev +++ b/dpc-portal/Procfile.dev @@ -1,3 +1,3 @@ -web: bundle exec rails server -b 0.0.0.0 -p 3000 +web: bundle exec rails server -b 0.0.0.0 -p 3100 solidqueue: bundle exec rails solid_queue:start -cpigw: env PORT=4567 ruby spec/support/fake_cpi_gateway.rb +cpigw: env PORT=4567 ruby spec/support/fake_cpi_gateway.rb \ No newline at end of file diff --git a/dpc-portal/lib/dpc_portal_utils.rb b/dpc-portal/lib/dpc_portal_utils.rb index b4327720ca..b4689412a3 100644 --- a/dpc-portal/lib/dpc_portal_utils.rb +++ b/dpc-portal/lib/dpc_portal_utils.rb @@ -6,7 +6,7 @@ def my_protocol_host env = ENV.fetch('ENV', nil) case env when 'local' - 'http://localhost:3000' + 'http://localhost:3100' else host_name = ENV.fetch('HOST_NAME', nil) Rails.logger.error 'HOST_NAME is not set by env' if host_name.nil? From 29292c89b7944e40a06d9e913be60eb9d36259c9 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Mon, 27 Apr 2026 11:50:46 -0400 Subject: [PATCH 17/48] Send env var for client id --- docker-compose.portals.yml | 1 + dpc-portal/app/controllers/application_controller.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.portals.yml b/docker-compose.portals.yml index 90d8eed5e9..325628835f 100644 --- a/docker-compose.portals.yml +++ b/docker-compose.portals.yml @@ -143,6 +143,7 @@ services: - CPI_API_GW_BASE_URL=http://localhost:4567/ - CMS_IDM_OAUTH_URL=http://localhost:4567/ - IDP_HOST=api.idmelabs.com + - IDP_CLIENT_ID=925bb2985ccf623114359caa76228919 - RUBY_YJIT_ENABLE=1 - ENV=local - NEW_RELIC_MONITOR_MODE=false diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index 8049cdc423..131c8ba867 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -3,7 +3,7 @@ # Parent class of all controllers class ApplicationController < ActionController::Base IDP_HOST = ENV.fetch('IDP_HOST') - IDP_CLIENT_ID = '925bb2985ccf623114359caa76228919' + IDP_CLIENT_ID = ENV.fetch('IDP_CLIENT_ID') before_action :check_session_length before_action :set_current_request_attributes From 3204fb93c31c2ef34b25adef40c3c76ade7cc889 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Mon, 27 Apr 2026 15:21:12 -0400 Subject: [PATCH 18/48] Use ID.me --- .../controllers/login_dot_gov_controller.rb | 4 +- dpc-portal/app/helpers/application_helper.rb | 2 +- dpc-portal/app/services/user_info_service.rb | 2 +- .../app/views/users/sessions/new.html.erb | 2 +- dpc-portal/config/initializers/omniauth.rb | 25 ++-- dpc-portal/config/routes.rb | 2 +- ops/config/encrypted/local.env | 127 +++--------------- 7 files changed, 37 insertions(+), 127 deletions(-) diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index 64e72f850e..b2660309e6 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -2,9 +2,9 @@ # Handles interactions with login.gov class LoginDotGovController < ApplicationController - skip_before_action :verify_authenticity_token, only: :openid_connect + skip_before_action :verify_authenticity_token, only: :id_me - def openid_connect + def id_me auth = request.env['omniauth.auth'] user = User.find_by(provider: auth.provider, uid: auth.uid) diff --git a/dpc-portal/app/helpers/application_helper.rb b/dpc-portal/app/helpers/application_helper.rb index d1813bb99b..f6166740af 100644 --- a/dpc-portal/app/helpers/application_helper.rb +++ b/dpc-portal/app/helpers/application_helper.rb @@ -7,6 +7,6 @@ def current_user end def omniauth_authorize_path(service) - "/portal/auth/#{service}" + "/auth/#{service}" end end diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index 1758f51433..a35bdd59cf 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -2,7 +2,7 @@ # A service that verifies generates an ao invitation class UserInfoService - USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/openid_connect/userinfo") + USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/public/v3/userinfo") def user_info(session) validate_session(session) diff --git a/dpc-portal/app/views/users/sessions/new.html.erb b/dpc-portal/app/views/users/sessions/new.html.erb index 3949df9114..3ce22dc3dc 100644 --- a/dpc-portal/app/views/users/sessions/new.html.erb +++ b/dpc-portal/app/views/users/sessions/new.html.erb @@ -1 +1 @@ -<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:login_dot_gov))) %> +<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:id_me))) %> diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index 51b0beb5aa..ab4e0a0a1d 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -8,28 +8,23 @@ Rails.application.config.middleware.use OmniAuth::Builder do OmniAuth.config.logger = Rails.logger - begin - private_key = OpenSSL::PKey::RSA.new(ENV['LOGIN_GOV_PRIVATE_KEY']) - rescue TypeError, OpenSSL::PKey::RSAError => e - Rails.logger.error("Unable to create private key for omniauth: #{e}") - private_key = OpenSSL::PKey::RSA.new(1024) - end - idp_host = ENV.fetch('IDP_HOST', 'idp.int.identitysandbox.gov') + idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') + client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') + client_secret = ENV['IDP_CLIENT_SECRET'] provider :openid_connect, { - name: :login_dot_gov, - issuer: "https://#{idp_host}/", + name: :id_me, + issuer: "https://#{idp_host}/oidc", discovery: true, - scope: %i[openid email all_emails], + scope: %i[openid http://idmanagement.gov/ns/assurance/ial/2/aal/2], response_type: :code, - acr_values: 'http://idmanagement.gov/ns/assurance/ial/1', - client_auth_method: :jwt_bearer, + client_auth_method: :client_secret_post, client_options: { port: 443, scheme: 'https', host: idp_host, - identifier: "urn:gov:cms:openidconnect.profiles:sp:sso:cms:dpc:#{ENV['ENV']}", - private_key: private_key, - redirect_uri: "#{my_protocol_host}/portal/auth/login_dot_gov/callback" + identifier: client_id, + secret: client_secret, + redirect_uri: "#{my_protocol_host}/auth/id_me/callback" } } end diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index 996dc19bc0..3f45ccfcdd 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -14,7 +14,7 @@ get 'timeout', to: 'users/sessions#timeout', as: 'timeout' get '/users/sign_in', to: 'users/sessions#new', as: 'sign_in' delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' - get '/auth/login_dot_gov/callback', to: 'login_dot_gov#openid_connect' + get '/auth/id_me/callback', to: 'login_dot_gov#id_me' # Defines the root path route ("/") root 'organizations#index' diff --git a/ops/config/encrypted/local.env b/ops/config/encrypted/local.env index c992102087..ef52ecaf74 100644 --- a/ops/config/encrypted/local.env +++ b/ops/config/encrypted/local.env @@ -1,107 +1,22 @@ $ANSIBLE_VAULT;1.1;AES256 -34343139303238333833313866353535373461383839386265343865396335326230626662363865 -6234653135616565666265323866616336393632666663350a363764306466373666613433313436 -62646561366630356532613138353135653739343339333632323266666133316239323362373463 -6237646366643231370a303764626363303830653932666434323362346162386630613261656334 -61303965323331333535363861626263626166666631386639616461373766356163646536396564 -64373731666366376263343738636235333334653661323130393839306637333065623936373330 -61383438356236313738396439633063303737333538316561373032346339653135626139613663 -65363866326665383637343064383831653561626135636661656162636230303631633464613635 -66323365646662376163383765396462663766313365656537313631303838316534343763323865 -61363835636437316439663561666661383764623863356636393230303336623737313866646263 -34653662316162383837613836666266623065623965346330636133363135633862303938363462 -33376430303861376631356536363061323235616633623434316637376632363834323333653665 -34306464303339316461346337633135323630653930396431333334366462643032363061353133 -61663830633331663234613134306338373635663131363038666661343231363331623430343038 -61656437346466666334303533626236353430393430666339363764613839656536346135633430 -36316266373562663863323864393835386562363932333739316366616639373836656438373337 -61626437316365363931616163656536623330636365343139333434633532656265623831303466 -35353032613166356261303230366261336535356637663261323731626366623665346236633033 -36363634303565313966323336386439626238356639653037623461396432343437626634636630 -62373664383437623461346166373066303334646263653235656665306461626663633238303130 -34666662656464623466316363373463316637323232386439376535363366313363333038383964 -31306462376431303836356166316636613636393262336334383232616630323063373464313232 -33313139306339666630303334333663643164303535373133313861323066383766653266666665 -35623637613533323933636538303439363933646338613065363639343432323938343764336435 -32383430636263366433313331613763326161613863393139613866353563653865646362333866 -65666562376435663230383638303433353539623935323337383064303234643466316364366433 -61386464633331346563666230653334653863343338366365396665376636663166623566646137 -63646131646334633034343837616231666531636336613039303764646466653735313333376665 -34323638613264316139383165383536666439353538613337383863373533633131366434303339 -36653538373063636237376564616335386436383638303638353734386237373363316238656437 -36353364346230656237663163613661363763376562376339663234626434636136363830363166 -30323164393865646335666264633663333266343538393266333733326562303732303232366364 -39376335396263353464393531333862663435376136643066353363383030373835626364333939 -64396264326434663835343735373338346137316135333731353033316565646566346363323832 -63643138316562336436363962383936663963376538616634616439623532356437303831373664 -65343562383733636166373133616532623634653136613438363536353234653938333832323136 -37386439323638316134333533343532366663303733646430626435613563393835653763376434 -61373831386136626131363864396639303239643130303762633365363039303239333437636461 -35333534396336363134373539353037643062646234343737346135376332343465303766613565 -61653961616664646462373864636331356334646562343164353032326261313265353866303162 -62313139393639653634646134393630613133663730623637336565323865623232623666343937 -64326534393533636239623262376234386136336435396236623362313732656165306665383965 -63383963313266323639303332646134376561613964313262363330316436356337326664643037 -65616664376264623661663764616131363934323162613938626265356532336531623237633661 -65313135326662643361613965313334343135653337326366396531363364636365613262323836 -36306533373739343631366666633463626538383034336566313330396533366530613861383036 -35393032626638343337373230613530383564643236353664616165326262613336306137396236 -32626431653732396662356435353462316266623636353837613036333665316338346130363935 -37383464386335353462353761386263343363616530303964316463353765333939333432383739 -38316232383630306237333337306334346664653334613365646565373433616233663261376330 -39356561636638623265353336333339346431633637306538633930396666303230386431653163 -35306234616436366138386133386135653731633266373731663864313630636663353761636461 -30303035643439353731316363393032636365323739643735393363373135643439653434316534 -63386462653232313866633961306366353937613466356662646265393863356539316134633635 -65306239343437343965623761623934393038363464313961356434316533383734316464383165 -36353034393166646435396264343939666165333837623462396238323130363063623563323038 -39383961636665396532336235643266656638346431623061653431323036616264613962626663 -33313336303933333163623663366164303237386166646565343334363330396338613936363232 -33343330396639613565363361653665346265363161633836376133323932353065303336333862 -32643935306430303835616666633661316533663534356262633631306233626234666636666666 -37316433393939396661323066633831303766313031333839343036393861353330316663316437 -36346333396634313662393030326464353336316366343930346233353235633335666336646463 -65633132303736303835346365646537376161376637333833373362653463613439376430393964 -38356534643739663763333039313034393037316434643964313066333862666330313066383638 -38356462303962333432393834663330333939663333396533613533363037643766663938386261 -35623764663061396661626465653133623163383262386136353039366332353030336335626561 -64383731393265353965323163376237633365613061643666623534643165396434616666343138 -37623635313962663061353936653062393734333861656664656138353966326638383064613130 -30663362353137353064386533303130303232306662663932613537323962363132663763643036 -37666163653262353063386365643538303730646331633463343733376663643862366632316432 -38383464376635376635626462353162356566633734633738323135323438313231336462306537 -30656532653761663164613835313062326561316562336632306530326238373735616339313763 -39353331626463316363323861303136616431363565343334323335313363376462666262323135 -32366131303431333830363635313730366339656235366530636136396163653933326234396139 -36323938346161323065383538643134393930393138643238366134643365643433616266396434 -36633535646434306134663038336338633437653933666439636335323534613764303364313165 -33373962313666373465653931326433333965666561646666353064316261323661306230653436 -39376238353637633062363865333833306130616232333736336366653436303736353762626566 -64613932633764613237353133633264363065313866313135653433653661623164376534623735 -37333438306261633334353936363838653766386165393837626139353861336637303761343639 -38396462636233383938346361343136623431653039383933353637323332626662336365653763 -36373039396430383037666364323033383966323830313562656562653137623363346234613135 -65666662303032323037336336643234623164623065653163663037326432666135643765333032 -30316237623163396430333438646461633136396638363938653263613837333031663232303562 -65383338323437383061663731663536373963336139333363626630306133356266316637383733 -32316238353061343264613464343432313932303066663732333564393433366235343864333334 -65316562626138356532316530653433373633326437303235333737346630613131646434666164 -61346238666266666531316136643737663362343266623336613536336265386138353564333239 -36386534386563393139646333623864366435353936323333363930623530643939646237656562 -33663461306136663965333237386235656433376333306662636339653564396534656166623536 -33643530643432643931313766623363356266393432373138306533356363313366656536303631 -30393436366135363864373464613834323737333736363766613865613434323735666333636631 -63613664353636636466303439396362306363626538393330656161626463653039616338316330 -35316463353431663461333661346461613561633635303262336465326564383838343839646133 -66623339356365316130316466326133366631393631393236316665356233336361313462356339 -34616266356462323065623739613233353465366636653732386161646665313166636663306463 -39333463316433383466386164633464636633613562653032616435373732626232356161393361 -31346334313162373832393765316137303834643864623163643862663732613265633162646336 -32353962643032646262346430313036323432396139646534393032336437386231383463613630 -65613232643362326131643634383535373761653965363735643331373535323339363331623235 -36613935366537306137633062303465316131366564653739656335646365356339623763623238 -34383533666135343362646330363334633837306537663339376363316237613161333633353735 -62333437306535356561613030643131376361316436383735663637373031353838656461643262 -36393437613236306566393635323762653762363165353538616262316533336563376635313632 -66353162616436303938353831313536353434646335333235333433336463303062623938366338 -6235663334336432323863623535366562616236376236646539 +38383764616664386236663635363232383035373933363830626633303734313934353634343237 +3131316235376139303537396533656165636431316239320a616162313039613035613531343233 +37356164393738643837663462373034623639366165343838666433613863383234313033633137 +3035326662306533370a646536666437383234343765643732666636306662333237306133393631 +39656162316532323939366639306434323934646230356338326437396434643433323639356136 +36386238323333623137396637303332303835326266663937366532373339646530306136323034 +64343735343635626562323437333261636532396534323735313035643334343634653630393562 +31366436656437633736333336653531633138666366636265303466643132323538333662653565 +34663935323138653839663337333062346139376266373166353563393233336532343561643639 +66346438636161396532373465653734333866376362373638333161306638323838613463363637 +30343066326562396430346162386531356163323239393265643532653338313236393532663130 +65333736663338336535323838303866346261633737386161663031306266663732613230323634 +65393463353332626239303330616538336135326361373163393332366437653333316162613965 +62663266353235393261333831633662323364336430656330376566653562633033303633303731 +37633833343662343866623336353939613230633930313236346563626432306133633637666663 +31326132366161353935333535396238383664313333303063356339396661666334353966633631 +33346634373134653366333035336661336131316633376362383639623131343363363333653730 +66343332396432336437393735346632653961356364653966343061663331633732303935343936 +63643138623035336462303739376361653930326463383366393131363964613565623063396664 +34623863633561313066653566363430383837343961343639393432326666343337613661323062 +66643636316235653365353736666432643431643235613934356439323037306531 From 365d38e25d26559cd63795a16eaca238437a642f Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Mon, 27 Apr 2026 16:48:51 -0400 Subject: [PATCH 19/48] Update omniauth.rb --- dpc-portal/config/initializers/omniauth.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index ab4e0a0a1d..34824ae928 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -10,7 +10,7 @@ OmniAuth.config.logger = Rails.logger idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') - client_secret = ENV['IDP_CLIENT_SECRET'] + client_secret = ENV.fetch('IDP_CLIENT_SECRET') provider :openid_connect, { name: :id_me, issuer: "https://#{idp_host}/oidc", From d858f34ea775d691cb566b39155bb6901cfca239 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Tue, 28 Apr 2026 10:41:34 -0400 Subject: [PATCH 20/48] Update omniauth.rb --- dpc-portal/config/initializers/omniauth.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index 34824ae928..ab4e0a0a1d 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -10,7 +10,7 @@ OmniAuth.config.logger = Rails.logger idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') - client_secret = ENV.fetch('IDP_CLIENT_SECRET') + client_secret = ENV['IDP_CLIENT_SECRET'] provider :openid_connect, { name: :id_me, issuer: "https://#{idp_host}/oidc", From c9e15b8740c8d4e5aeb51a9cdacd2eb7a5e724e4 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Tue, 28 Apr 2026 13:26:55 -0400 Subject: [PATCH 21/48] Update invitations_controller.rb --- dpc-portal/app/controllers/invitations_controller.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index d8f277c4b7..363fc48448 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -80,11 +80,10 @@ def login invitation: @invitation.id }]) url = URI::HTTPS.build(host: IDP_HOST, path: '/oauth/authorize', - query: { acr_values: 'http://idmanagement.gov/ns/assurance/ial/2', - client_id: IDP_CLIENT_ID, - redirect_uri: "#{my_protocol_host}/users/auth/login_dot_gov/callback", + query: { client_id: IDP_CLIENT_ID, + redirect_uri: "#{my_protocol_host}/auth/id_me/callback", response_type: 'code', - scope: 'openid email all_emails profile social_security_number', + scope: 'openid http://idmanagement.gov/ns/assurance/ial/2/aal/2', nonce: @nonce, state: @state }.to_query) redirect_to url, allow_other_host: true @@ -203,7 +202,7 @@ def create_ao_org_link def user user_info = UserInfoService.new.user_info(session) - @user = User.find_or_create_by!(provider: :login_dot_gov, uid: user_info['sub']) do |user_to_create| + @user = User.find_or_create_by!(provider: :id_me, uid: user_info['sub']) do |user_to_create| assign_user_attributes(user_to_create, user_info) log_create_user end From a9bf7277de8895f7c12e8857b9aa785f32679449 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Wed, 29 Apr 2026 10:11:26 -0400 Subject: [PATCH 22/48] Update Gemfile.lock --- dpc-portal/Gemfile.lock | 140 +++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 72 deletions(-) diff --git a/dpc-portal/Gemfile.lock b/dpc-portal/Gemfile.lock index a0da64d994..42af435ed4 100644 --- a/dpc-portal/Gemfile.lock +++ b/dpc-portal/Gemfile.lock @@ -1,32 +1,32 @@ PATH remote: vendor/api_client specs: - api_client (0.2.0) + api_client (0.2.1) active_model_serializers - activemodel (~> 8.0.2) + activemodel (~> 8.0.5) macaroons oauth2 GEM remote: https://rubygems.org/ specs: - actionmailbox (8.0.4.1) - actionpack (= 8.0.4.1) - activejob (= 8.0.4.1) - activerecord (= 8.0.4.1) - activestorage (= 8.0.4.1) - activesupport (= 8.0.4.1) + actionmailbox (8.0.5) + actionpack (= 8.0.5) + activejob (= 8.0.5) + activerecord (= 8.0.5) + activestorage (= 8.0.5) + activesupport (= 8.0.5) mail (>= 2.8.0) - actionmailer (8.0.4.1) - actionpack (= 8.0.4.1) - actionview (= 8.0.4.1) - activejob (= 8.0.4.1) - activesupport (= 8.0.4.1) + actionmailer (8.0.5) + actionpack (= 8.0.5) + actionview (= 8.0.5) + activejob (= 8.0.5) + activesupport (= 8.0.5) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.4.1) - actionview (= 8.0.4.1) - activesupport (= 8.0.4.1) + actionpack (8.0.5) + actionview (= 8.0.5) + activesupport (= 8.0.5) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -34,15 +34,15 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.4.1) - actionpack (= 8.0.4.1) - activerecord (= 8.0.4.1) - activestorage (= 8.0.4.1) - activesupport (= 8.0.4.1) + actiontext (8.0.5) + actionpack (= 8.0.5) + activerecord (= 8.0.5) + activestorage (= 8.0.5) + activesupport (= 8.0.5) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.4.1) - activesupport (= 8.0.4.1) + actionview (8.0.5) + activesupport (= 8.0.5) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -52,14 +52,14 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (8.0.4.1) - activesupport (= 8.0.4.1) + activejob (8.0.5) + activesupport (= 8.0.5) globalid (>= 0.3.6) - activemodel (8.0.4.1) - activesupport (= 8.0.4.1) - activerecord (8.0.4.1) - activemodel (= 8.0.4.1) - activesupport (= 8.0.4.1) + activemodel (8.0.5) + activesupport (= 8.0.5) + activerecord (8.0.5) + activemodel (= 8.0.5) + activesupport (= 8.0.5) timeout (>= 0.4.0) activerecord-session_store (2.1.0) actionpack (>= 6.1) @@ -68,13 +68,13 @@ GEM multi_json (~> 1.11, >= 1.11.2) rack (>= 2.0.8, < 4) railties (>= 6.1) - activestorage (8.0.4.1) - actionpack (= 8.0.4.1) - activejob (= 8.0.4.1) - activerecord (= 8.0.4.1) - activesupport (= 8.0.4.1) + activestorage (8.0.5) + actionpack (= 8.0.5) + activejob (= 8.0.5) + activerecord (= 8.0.5) + activesupport (= 8.0.5) marcel (~> 1.0) - activesupport (8.0.4.1) + activesupport (8.0.5) base64 benchmark (>= 0.3) bigdecimal @@ -83,7 +83,7 @@ GEM drb i18n (>= 1.6, < 2) logger (>= 1.4.2) - minitest (>= 5.1, < 6) + minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) @@ -131,7 +131,6 @@ GEM base64 (0.2.0) bcp47 (0.3.3) i18n - bcrypt (3.1.22) benchmark (0.5.0) bigdecimal (4.1.0) bindata (2.5.0) @@ -164,7 +163,7 @@ GEM bigdecimal rexml crass (1.0.6) - css_parser (1.17.1) + css_parser (2.1.0) addressable date (3.4.1) date_time_precision (0.8.1) @@ -180,7 +179,7 @@ GEM dumb_delegator (1.0.0) email_validator (2.2.4) activemodel - erb (6.0.2) + erb (6.0.4) erubi (1.13.1) et-orbi (1.2.11) tzinfo @@ -220,7 +219,7 @@ GEM concurrent-ruby (~> 1.0) ice_nine (0.11.2) io-console (0.8.2) - irb (1.17.0) + irb (1.18.0) pp (>= 0.6.0) prism (>= 1.3.0) rdoc (>= 4.0.0) @@ -269,7 +268,7 @@ GEM loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - lookbook (2.3.2) + lookbook (2.3.14) activemodel css_parser htmlbeautifier (~> 1.3) @@ -291,7 +290,7 @@ GEM net-imap net-pop net-smtp - marcel (1.0.4) + marcel (1.1.0) matrix (0.4.2) mcp (0.10.0) json-schema (>= 4.1) @@ -301,7 +300,9 @@ GEM mime-types-data (3.2024.0820) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.27.0) + minitest (6.0.5) + drb (~> 2.0) + prism (~> 1.5) msgpack (1.7.2) multi_json (1.15.0) multi_xml (0.8.1) @@ -321,7 +322,7 @@ GEM net-protocol newrelic_rpm (8.16.0) nio4r (2.7.3) - nokogiri (1.19.1) + nokogiri (1.19.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) oauth2 (2.0.18) @@ -393,7 +394,7 @@ GEM base64 (>= 0.1.0) logger (>= 1.6.0) rack (>= 3.0.0, < 4) - rack-session (2.1.1) + rack-session (2.1.2) base64 (>= 0.1.0) rack (>= 3.0.0) rack-test (2.1.0) @@ -411,9 +412,9 @@ GEM rails-html-sanitizer (1.7.0) loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.0.4.1) - actionpack (= 8.0.4.1) - activesupport (= 8.0.4.1) + railties (8.0.5) + actionpack (= 8.0.5) + activesupport (= 8.0.5) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -421,7 +422,7 @@ GEM tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.1) + rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) @@ -433,14 +434,14 @@ GEM erb psych (>= 4.0.0) tsort - redcarpet (3.6.0) + redcarpet (3.6.1) regexp_parser (2.11.3) reline (0.6.3) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) rexml (3.4.4) - rouge (4.3.0) + rouge (4.7.0) rspec-core (3.13.0) rspec-support (~> 3.13.0) rspec-expectations (3.13.2) @@ -561,10 +562,6 @@ GEM axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) - webdrivers (5.3.1) - nokogiri (~> 1.6) - rubyzip (>= 1.3.0) - selenium-webdriver (~> 4.0, < 4.11) webfinger (2.1.3) activesupport faraday (~> 2.0) @@ -578,24 +575,24 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) yard (0.9.36) - zeitwerk (2.6.17) + zeitwerk (2.7.5) PLATFORMS ruby DEPENDENCIES - actionmailbox (~> 8.0.4.1) - actionmailer (~> 8.0.4.1) - actionpack (~> 8.0.4.1) - actiontext (~> 8.0.4.1) - actionview (~> 8.0.4.1) + actionmailbox (~> 8.0.5) + actionmailer (~> 8.0.5) + actionpack (~> 8.0.5) + actiontext (~> 8.0.5) + actionview (~> 8.0.5) active_model_serializers - activejob (~> 8.0.4.1) - activemodel (~> 8.0.4.1) - activerecord (~> 8.0.4.1) + activejob (~> 8.0.5) + activemodel (~> 8.0.5) + activerecord (~> 8.0.5) activerecord-session_store - activestorage (~> 8.0.4.1) - activesupport (~> 8.0.4.1) + activestorage (~> 8.0.5) + activesupport (~> 8.0.5) api_client! audited auto-session-timeout @@ -617,12 +614,12 @@ DEPENDENCIES json-jwt (>= 1.16.6) kaminari lograge - lookbook (>= 2.2.1) + lookbook (>= 2.3.3) luhnacy (~> 0.2.1) macaroons net-imap (>= 0.5.8) newrelic_rpm (~> 8.10) - nokogiri (>= 1.18.9) + nokogiri (>= 1.19.3) omniauth-rails_csrf_protection omniauth_openid_connect pg (>= 0.18, < 2.0) @@ -631,9 +628,9 @@ DEPENDENCIES pry-nav puma (~> 6.4.3) rack (>= 3.2.3) - rack-session (>= 2.1.1) + rack-session (>= 2.1.2) rails-controller-testing - railties (~> 8.0.4.1) + railties (~> 8.0.5) rbnacl rbnacl-libsodium rexml (>= 3.4.2) @@ -652,7 +649,6 @@ DEPENDENCIES tzinfo-data uglifier (>= 1.3.0) view_component (~> 3.9) - webdrivers webmock webrick yard (>= 0.9.36) From 601d42112fc450ebc90a55023cc6a3e84abdff40 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Wed, 29 Apr 2026 10:19:46 -0400 Subject: [PATCH 23/48] Update yarn.lock --- dpc-load-testing/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dpc-load-testing/yarn.lock b/dpc-load-testing/yarn.lock index b5f39f6bdf..3d59474625 100644 --- a/dpc-load-testing/yarn.lock +++ b/dpc-load-testing/yarn.lock @@ -1258,9 +1258,9 @@ flat-cache@^4.0.0: keyv "^4.5.4" flatted@^3.2.9: - version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" - integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + version "3.4.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" + integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== fs.realpath@^1.0.0: version "1.0.0" From 3ab93eb1924eab5e932d72f295cf5881c4827656 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Wed, 29 Apr 2026 10:30:18 -0400 Subject: [PATCH 24/48] Update Gemfile.lock --- dpc-portal/Gemfile.lock | 264 ++++++++++++++++++++-------------------- 1 file changed, 134 insertions(+), 130 deletions(-) diff --git a/dpc-portal/Gemfile.lock b/dpc-portal/Gemfile.lock index 42af435ed4..56441dd0be 100644 --- a/dpc-portal/Gemfile.lock +++ b/dpc-portal/Gemfile.lock @@ -47,7 +47,7 @@ GEM erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - active_model_serializers (0.10.14) + active_model_serializers (0.10.16) actionpack (>= 4.1) activemodel (>= 4.1) case_transform (>= 0.2) @@ -61,13 +61,12 @@ GEM activemodel (= 8.0.5) activesupport (= 8.0.5) timeout (>= 0.4.0) - activerecord-session_store (2.1.0) - actionpack (>= 6.1) - activerecord (>= 6.1) + activerecord-session_store (2.2.0) + actionpack (>= 7.0) + activerecord (>= 7.0) cgi (>= 0.3.6) - multi_json (~> 1.11, >= 1.11.2) rack (>= 2.0.8, < 4) - railties (>= 6.1) + railties (>= 7.0) activestorage (8.0.5) actionpack (= 8.0.5) activejob (= 8.0.5) @@ -87,7 +86,7 @@ GEM securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) uri (>= 0.13.1) - addressable (2.8.9) + addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) aes_key_wrap (1.1.0) ast (2.4.3) @@ -95,32 +94,35 @@ GEM audited (5.8.0) activerecord (>= 5.2, < 8.2) activesupport (>= 5.2, < 8.2) - auto-session-timeout (1.3) - actionpack (>= 3.2, < 8.1) - aws-eventstream (1.3.0) - aws-partitions (1.989.0) - aws-sdk-cloudwatch (1.103.0) - aws-sdk-core (~> 3, >= 3.207.0) + auto-session-timeout (1.4) + actionpack (>= 3.2, < 9) + aws-eventstream (1.4.0) + aws-partitions (1.1241.0) + aws-sdk-cloudwatch (1.134.0) + aws-sdk-core (~> 3, >= 3.244.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.209.1) + aws-sdk-core (3.246.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) + aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) + base64 + bigdecimal jmespath (~> 1, >= 1.6.1) - aws-sdk-rds (1.253.0) - aws-sdk-core (~> 3, >= 3.207.0) + logger + aws-sdk-rds (1.311.0) + aws-sdk-core (~> 3, >= 3.244.0) aws-sigv4 (~> 1.5) - aws-sigv4 (1.10.0) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) - axe-core-api (4.10.1) + axe-core-api (4.11.2) dumb_delegator ostruct virtus - axe-core-capybara (4.10.1) - axe-core-api (= 4.10.1) + axe-core-capybara (4.11.2) + axe-core-api (= 4.11.2) dumb_delegator - axe-core-rspec (4.10.1) - axe-core-api (= 4.10.1) + axe-core-rspec (4.11.2) + axe-core-api (= 4.11.2) dumb_delegator ostruct virtus @@ -128,19 +130,20 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) - base64 (0.2.0) + base64 (0.3.0) bcp47 (0.3.3) i18n benchmark (0.5.0) - bigdecimal (4.1.0) - bindata (2.5.0) - bootsnap (1.18.4) + bigdecimal (4.1.2) + bindata (2.5.1) + bootsnap (1.24.1) msgpack (~> 1.2) builder (3.3.0) - bundler-audit (0.9.1) - bundler (>= 1.2.0, < 3) + bundler-audit (0.9.3) + bundler (>= 1.2.0) thor (~> 1.0) - byebug (11.1.3) + byebug (13.0.0) + reline (>= 0.6.0) capybara (3.40.0) addressable matrix @@ -152,70 +155,71 @@ GEM xpath (~> 3.2) case_transform (0.2) activesupport - cgi (0.4.2) + cgi (0.5.1) climate_control (1.2.0) coderay (1.1.3) coercible (1.0.0) descendants_tracker (~> 0.0.1) - concurrent-ruby (1.3.4) + concurrent-ruby (1.3.6) connection_pool (3.0.2) - crack (1.0.0) + crack (1.0.1) bigdecimal rexml crass (1.0.6) css_parser (2.1.0) addressable - date (3.4.1) + date (3.5.1) date_time_precision (0.8.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - diff-lcs (1.5.1) + diff-lcs (1.6.2) docile (1.4.1) - dotenv (3.1.2) - dotenv-rails (3.1.2) - dotenv (= 3.1.2) + dotenv (3.2.0) + dotenv-rails (3.2.0) + dotenv (= 3.2.0) railties (>= 6.1) drb (2.2.3) - dumb_delegator (1.0.0) + dumb_delegator (1.1.0) email_validator (2.2.4) activemodel erb (6.0.4) erubi (1.13.1) - et-orbi (1.2.11) + et-orbi (1.4.0) tzinfo - execjs (2.9.1) - factory_bot (6.4.6) - activesupport (>= 5.0.0) - factory_bot_rails (6.4.3) - factory_bot (~> 6.4) - railties (>= 5.0.0) - fakefs (2.5.0) + execjs (2.10.1) + factory_bot (6.5.6) + activesupport (>= 6.1.0) + factory_bot_rails (6.5.1) + factory_bot (~> 6.5) + railties (>= 6.1.0) + fakefs (3.2.1) faraday (2.14.1) faraday-net_http (>= 2.0, < 3.5) json logger - faraday-follow_redirects (0.3.0) + faraday-follow_redirects (0.5.0) faraday (>= 1, < 3) faraday-net_http (3.4.2) net-http (~> 0.5) - ffi (1.17.0) - fhir_models (4.3.0) + ffi (1.17.4) + fhir_models (5.0.0) bcp47 (>= 0.3) date_time_precision (>= 0.8) mime-types (>= 3.0) nokogiri (>= 1.11.4) - fugit (1.11.1) - et-orbi (~> 1, >= 1.2.11) + fugit (1.12.1) + et-orbi (~> 1.4) raabro (~> 1.4) globalid (1.3.0) activesupport (>= 6.1) - hashdiff (1.1.1) - hashie (5.0.0) + hashdiff (1.2.1) + hashie (5.1.0) + logger health_check (3.1.0) railties (>= 5.0) htmlbeautifier (1.4.3) htmlentities (4.3.4) - i18n (1.14.6) + i18n (1.14.8) concurrent-ruby (~> 1.0) ice_nine (0.11.2) io-console (0.8.2) @@ -224,21 +228,18 @@ GEM prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - jbuilder (2.12.0) - actionview (>= 5.0.0) - activesupport (>= 5.0.0) + jbuilder (2.14.1) + actionview (>= 7.0.0) + activesupport (>= 7.0.0) jmespath (1.6.2) - json (2.19.2) - json-jwt (1.16.6) + json (2.19.4) + json-jwt (1.17.0) activesupport (>= 4.2) aes_key_wrap base64 bindata faraday (~> 2.0) faraday-follow_redirects - json-schema (6.2.0) - addressable (~> 2.8) - bigdecimal (>= 3.1, < 5) jsonapi-renderer (0.2.2) jwt (3.1.2) base64 @@ -256,7 +257,8 @@ GEM kaminari-core (1.2.2) language_server-protocol (3.17.0.5) lint_roller (1.1.0) - listen (3.9.0) + listen (3.10.0) + logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) @@ -285,43 +287,42 @@ GEM multi_json (~> 1.10) rbnacl (~> 5.0) rbnacl-libsodium (~> 1.0) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp marcel (1.1.0) - matrix (0.4.2) - mcp (0.10.0) - json-schema (>= 4.1) + matrix (0.4.3) method_source (1.1.0) - mime-types (3.5.2) - mime-types-data (~> 3.2015) - mime-types-data (3.2024.0820) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2026.0414) mini_mime (1.1.5) mini_portile2 (2.8.9) minitest (6.0.5) drb (~> 2.0) prism (~> 1.5) - msgpack (1.7.2) - multi_json (1.15.0) + msgpack (1.8.0) + multi_json (1.20.1) multi_xml (0.8.1) bigdecimal (>= 3.1, < 5) - mustermann (3.0.4) - ruby2_keywords (~> 0.0.1) + mustermann (3.1.1) net-http (0.9.1) uri (>= 0.11.1) - net-imap (0.5.8) + net-imap (0.6.4) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol newrelic_rpm (8.16.0) - nio4r (2.7.3) + nio4r (2.7.5) nokogiri (1.19.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) @@ -333,17 +334,18 @@ GEM rack (>= 1.2, < 4) snaky_hash (~> 2.0, >= 2.0.3) version_gem (~> 1.1, >= 1.1.9) - omniauth (2.1.2) + omniauth (2.1.4) hashie (>= 3.4.6) + logger rack (>= 2.2.3) rack-protection - omniauth-rails_csrf_protection (1.0.2) + omniauth-rails_csrf_protection (2.0.1) actionpack (>= 4.2) omniauth (~> 2.0) omniauth_openid_connect (0.8.0) omniauth (>= 1.9, < 3) openid_connect (~> 2.2) - openid_connect (2.3.0) + openid_connect (2.3.1) activemodel attr_required (>= 1.0.0) email_validator @@ -356,15 +358,15 @@ GEM tzinfo validate_url webfinger (~> 2.0) - ostruct (0.6.0) - parallel (1.27.0) - parser (3.3.10.2) + ostruct (0.6.3) + parallel (2.1.0) + parser (3.3.11.1) ast (~> 2.4.1) racc - pg (1.5.7) - pg-aws_rds_iam (0.7.0) + pg (1.6.3) + pg-aws_rds_iam (0.8.0) aws-sdk-rds (~> 1.0) - pg (~> 1.1) + pg (~> 1.3) pp (0.6.3) prettyprint prettyprint (0.2.0) @@ -382,8 +384,8 @@ GEM nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) - rack (3.2.5) - rack-oauth2 (2.2.1) + rack (3.2.6) + rack-oauth2 (2.3.0) activesupport attr_required faraday (~> 2.0) @@ -397,7 +399,7 @@ GEM rack-session (2.1.2) base64 (>= 0.1.0) rack (>= 3.0.0) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) rackup (2.3.1) rack (>= 3) @@ -435,36 +437,35 @@ GEM psych (>= 4.0.0) tsort redcarpet (3.6.1) - regexp_parser (2.11.3) + regexp_parser (2.12.0) reline (0.6.3) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) rexml (3.4.4) rouge (4.7.0) - rspec-core (3.13.0) + rspec-core (3.13.6) rspec-support (~> 3.13.0) - rspec-expectations (3.13.2) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.1) + rspec-mocks (3.13.8) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (6.1.4) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) - rspec-core (~> 3.13) - rspec-expectations (~> 3.13) - rspec-mocks (~> 3.13) - rspec-support (~> 3.13) - rspec-support (3.13.1) - rubocop (1.85.1) + rspec-rails (8.0.4) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) + rspec-core (>= 3.13.0, < 5.0.0) + rspec-expectations (>= 3.13.0, < 5.0.0) + rspec-mocks (>= 3.13.0, < 5.0.0) + rspec-support (>= 3.13.0, < 5.0.0) + rspec-support (3.13.7) + rubocop (1.86.1) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - mcp (~> 0.6) - parallel (~> 1.10) + parallel (>= 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) @@ -474,12 +475,12 @@ GEM rubocop-ast (1.49.1) parser (>= 3.3.7.2) prism (~> 1.7) - rubocop-performance (1.23.0) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (1.13.0) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) + rubyzip (3.2.2) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -489,9 +490,11 @@ GEM sprockets-rails tilt securerandom (0.4.1) - selenium-webdriver (4.10.0) + selenium-webdriver (4.43.0) + base64 (~> 0.2) + logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2, < 3.0) + rubyzip (>= 1.2.2, < 4.0) websocket (~> 1.0) simplecov (0.17.0) docile (~> 1.1) @@ -509,19 +512,20 @@ GEM snaky_hash (2.0.3) hashie (>= 0.1.0, < 6) version_gem (>= 1.1.8, < 3) - solid_queue (1.2.1) + solid_queue (1.4.0) activejob (>= 7.1) activerecord (>= 7.1) concurrent-ruby (>= 1.3.1) - fugit (~> 1.11.0) + fugit (~> 1.11) railties (>= 7.1) thor (>= 1.3.1) - spring (4.2.1) + spring (4.4.2) spring-watcher-listen (2.1.0) listen (>= 2.7, < 4.0) spring (>= 4) - sprockets (4.2.1) + sprockets (4.2.2) concurrent-ruby (~> 1.0) + logger rack (>= 2.2.4, < 4) sprockets-rails (3.5.2) actionpack (>= 6.1) @@ -533,17 +537,17 @@ GEM attr_required (>= 0.0.5) faraday (~> 2.0) faraday-follow_redirects - thor (1.4.0) + thor (1.5.0) thread_safe (0.3.6) - tilt (2.4.0) - timecop (0.9.10) - timeout (0.4.3) + tilt (2.7.0) + timecop (0.9.11) + timeout (0.6.1) truemail (3.3.1) simpleidn (~> 0.2.1) tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uglifier (4.2.0) + uglifier (4.2.1) execjs (>= 0.3.0, < 3) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) @@ -554,9 +558,9 @@ GEM activemodel (>= 3.0.0) public_suffix version_gem (1.1.9) - view_component (3.19.0) - activesupport (>= 5.2.0, < 8.1) - concurrent-ruby (~> 1.0) + view_component (3.24.0) + activesupport (>= 5.2.0, < 8.2) + concurrent-ruby (~> 1) method_source (~> 1.0) virtus (2.0.0) axiom-types (~> 0.1) @@ -566,15 +570,15 @@ GEM activesupport faraday (~> 2.0) faraday-follow_redirects - webmock (3.23.1) + webmock (3.26.2) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.8.2) + webrick (1.9.2) websocket (1.2.11) xpath (3.2.0) nokogiri (~> 1.8) - yard (0.9.36) + yard (0.9.43) zeitwerk (2.7.5) PLATFORMS @@ -654,7 +658,7 @@ DEPENDENCIES yard (>= 0.9.36) RUBY VERSION - ruby 3.3.1p55 + ruby 3.3.11 BUNDLED WITH - 2.5.9 + 4.0.10 From eea3d43e80af54c63c664a256ceea8697a68760e Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Wed, 29 Apr 2026 10:54:26 -0400 Subject: [PATCH 25/48] Update tests --- dpc-portal/spec/factories/users.rb | 2 +- .../spec/helpers/application_helper_spec.rb | 2 +- dpc-portal/spec/requests/invitations_spec.rb | 17 ++++--- .../spec/requests/login_dot_gov_spec.rb | 46 +++++++++---------- dpc-portal/spec/support/login_support.rb | 4 +- dpc-portal/spec/system/accessibility_spec.rb | 14 +++--- dpc-portal/spec/system/new_invitation_spec.rb | 6 +-- 7 files changed, 45 insertions(+), 46 deletions(-) diff --git a/dpc-portal/spec/factories/users.rb b/dpc-portal/spec/factories/users.rb index cc09470447..bac0df9dcb 100644 --- a/dpc-portal/spec/factories/users.rb +++ b/dpc-portal/spec/factories/users.rb @@ -3,7 +3,7 @@ FactoryBot.define do factory :user, aliases: %i[invited_by] do sequence(:uid) { |n| n } - provider { :login_dot_gov } + provider { :id_me } email { "user#{rand(0..100_000)}@example.com" } end end diff --git a/dpc-portal/spec/helpers/application_helper_spec.rb b/dpc-portal/spec/helpers/application_helper_spec.rb index a9cb10be3c..d8d5af3c59 100644 --- a/dpc-portal/spec/helpers/application_helper_spec.rb +++ b/dpc-portal/spec/helpers/application_helper_spec.rb @@ -15,7 +15,7 @@ end describe 'omniauth_authorize_path' do it 'should return path to service' do - expect(omniauth_authorize_path(:foo)).to eq '/portal/auth/foo' + expect(omniauth_authorize_path(:foo)).to eq '/auth/foo' end end end diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index cb631b06e1..828b3afcd3 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -141,8 +141,7 @@ org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" redirect_params = Rack::Utils.parse_query(URI.parse(response.location).query) - expect(redirect_params['acr_values']).to eq('http://idmanagement.gov/ns/assurance/ial/2') - expect(redirect_params['redirect_uri']).to start_with('http://localhost:3100/users/') + expect(redirect_params['redirect_uri']).to start_with('http://localhost:3100/') expect(request.session[:user_return_to]).to eq expected_redirect end @@ -633,13 +632,13 @@ post "/organizations/#{org.id}/invitations/#{invitation.id}/register" end it 'should not create user if exists' do - create(:user, provider: :login_dot_gov, uid: user_info_template['sub']) + create(:user, provider: :id_me, uid: user_info_template['sub']) expect do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" end.to change { User.count }.by 0 end it 'should update name of user if changed' do - user = create(:user, provider: :login_dot_gov, uid: user_info_template['sub'], given_name: :foo, + user = create(:user, provider: :id_me, uid: user_info_template['sub'], given_name: :foo, family_name: :bar) expect do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" @@ -649,7 +648,7 @@ expect(user.family_name).to eq user_info_template['family_name'] end it 'should not override pac_id on existing user' do - create(:user, provider: :login_dot_gov, uid: user_info_template['sub'], pac_id: :foo) + create(:user, provider: :id_me, uid: user_info_template['sub'], pac_id: :foo) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) # We have the fake CPI API Gateway return the ssn as pac_id @@ -708,7 +707,7 @@ get "/organizations/#{org.id}/invitations/#{invitation.id}/confirm_cd" end it 'should not save verification_status on user and org' do - create(:user, provider: :login_dot_gov, uid: user_info_template['sub'], pac_id: :foo) + create(:user, provider: :id_me, uid: user_info_template['sub'], pac_id: :foo) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) expect(user.verification_status).to be_nil @@ -740,7 +739,7 @@ expect(request.session[:user_pac_id]).to be_nil end it 'should set pac_id on existing user' do - create(:user, provider: :login_dot_gov, uid: user_info_template['sub']) + create(:user, provider: :id_me, uid: user_info_template['sub']) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) # We have the fake CPI API Gateway return the ssn as pac_id @@ -826,7 +825,7 @@ def log_in OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:login_dot_gov, + OmniAuth.config.add_mock(:id_me, { uid: '12345', credentials: { expires_in: 899, token: 'bearer-token' }, @@ -834,7 +833,7 @@ def log_in extra: { raw_info: { given_name: 'Bob', family_name: 'Hoskins', ial: 'http://idmanagement.gov/ns/assurance/ial/2' } } }) - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! end diff --git a/dpc-portal/spec/requests/login_dot_gov_spec.rb b/dpc-portal/spec/requests/login_dot_gov_spec.rb index 086cc0c4c5..891a503b50 100644 --- a/dpc-portal/spec/requests/login_dot_gov_spec.rb +++ b/dpc-portal/spec/requests/login_dot_gov_spec.rb @@ -3,12 +3,12 @@ require 'rails_helper' RSpec.describe 'LoginDotGov', type: :request do - describe 'POST /auth/login_dot_gov' do + describe 'POST /auth/id_me' do RSpec.shared_examples 'an openid client' do context 'user exists' do - before { create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com') } + before { create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com') } it 'should sign in a user' do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(response.location).to eq organizations_url expect(response).to be_redirect @@ -20,13 +20,13 @@ expect(Rails.logger).to receive(:info).with(['User logged in', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoggedIn }]) - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! end it 'should not add another user' do - expect(User.where(uid: '12345', provider: 'login_dot_gov').count).to eq 1 + expect(User.where(uid: '12345', provider: 'id_me').count).to eq 1 expect do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! end.to change { User.count }.by(0) end @@ -35,7 +35,7 @@ context 'user does not exist' do it 'should not persist user' do expect do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! end.to change { User.count }.by(0) end @@ -46,7 +46,7 @@ context 'IAL/2' do before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:login_dot_gov, + OmniAuth.config.add_mock(:id_me, { uid: '12345', credentials: { expires_in: 899, token: }, @@ -61,20 +61,20 @@ it_behaves_like 'an openid client' context :user_exists do - before { create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com') } + before { create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com') } it 'updates user names' do expect do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! end.to change { - User.where(uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', + User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count }.by 1 expect(response.location).to eq organizations_url end it 'sets authentication token' do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(request.session[:login_dot_gov_token]).to eq token expect(request.session[:login_dot_gov_token_exp]).to_not be_nil @@ -84,7 +84,7 @@ context :user_does_not_exist do it 'does not sign in user' do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(response.location).to eq organizations_url expect(response).to be_redirect @@ -93,7 +93,7 @@ end it 'sets authentication token' do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(request.session[:login_dot_gov_token]).to eq token expect(request.session[:login_dot_gov_token_exp]).to_not be_nil @@ -105,7 +105,7 @@ context 'IAL/1' do before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:login_dot_gov, + OmniAuth.config.add_mock(:id_me, { uid: '12345', info: { email: 'bob@example.com' }, extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], @@ -116,21 +116,21 @@ context :user_exists do before do - create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', + create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins') end it 'does not update user names' do - expect(User.where(uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', + expect(User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(response.location).to eq organizations_url - expect(User.where(uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', + expect(User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 end it 'does not set authentication token' do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(request.session[:login_dot_gov_token]).to be_nil expect(request.session[:login_dot_gov_token_exp]).to be_nil @@ -139,7 +139,7 @@ context 'user does not exist' do it 'does not sign in user' do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(response.location).to eq no_account_url expect(response).to be_redirect @@ -152,12 +152,12 @@ { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoginWithoutAccount }] ) - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! end it 'does not set authentication token' do - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! expect(request.session[:login_dot_gov_token]).to be_nil expect(request.session[:login_dot_gov_token_exp]).to be_nil diff --git a/dpc-portal/spec/support/login_support.rb b/dpc-portal/spec/support/login_support.rb index 76300a4ba9..088c6b0775 100644 --- a/dpc-portal/spec/support/login_support.rb +++ b/dpc-portal/spec/support/login_support.rb @@ -3,12 +3,12 @@ module LoginSupport def sign_in(user) OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:login_dot_gov, + OmniAuth.config.add_mock(:id_me, { uid: user.uid, info: { email: user.email }, extra: { raw_info: { all_emails: [user.email], ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) - post '/auth/login_dot_gov' + post '/auth/id_me' follow_redirect! end end diff --git a/dpc-portal/spec/system/accessibility_spec.rb b/dpc-portal/spec/system/accessibility_spec.rb index 1366a9859e..e28f8b4069 100644 --- a/dpc-portal/spec/system/accessibility_spec.rb +++ b/dpc-portal/spec/system/accessibility_spec.rb @@ -14,14 +14,14 @@ before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:login_dot_gov, + OmniAuth.config.add_mock(:id_me, { uid:, info: { email: 'bob@example.com' }, extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) end def sign_in - visit '/auth/login_dot_gov/callback' + visit '/auth/id_me/callback' end context 'login' do it 'shows login page ok' do @@ -38,14 +38,14 @@ def sign_in context 'bad user tries to log in' do it 'shows no such user page' do - visit '/auth/login_dot_gov/callback' + visit '/auth/id_me/callback' expect(page).to have_text('The email you used is not associated with a DPC account.') expect(page).to be_axe_clean.according_to axe_standard end it 'shows sanctioned ao page' do - create(:user, provider: :login_dot_gov, uid: '12345', + create(:user, provider: :id_me, uid: '12345', verification_status: 'rejected', verification_reason: 'ao_med_sanctions') - visit '/auth/login_dot_gov/callback' + visit '/auth/id_me/callback' expect(page).to have_text(I18n.t('verification.ao_med_sanctions_status')) expect(page).to be_axe_clean.according_to axe_standard end @@ -55,7 +55,7 @@ def sign_in it 'shows success page' do create(:user, provider: :login_dot_gov, uid: '12345', verification_status: 'approved') - visit '/auth/login_dot_gov/callback' + visit '/auth/id_me/callback' expect(page).to have_text("You don't have any organizations to show.") expect(page).to be_axe_clean.according_to axe_standard end @@ -63,7 +63,7 @@ def sign_in end context 'organizations' do - let!(:user) { create(:user, uid:, provider: :login_dot_gov, verification_status: :approved) } + let!(:user) { create(:user, uid:, provider: :id_me, verification_status: :approved) } let!(:org) { create(:provider_organization, dpc_api_organization_id:, name: 'Health Hut') } let(:mock_client_token_manager) { instance_double(ClientTokenManager) } let(:mock_public_key_manager) { instance_double(PublicKeyManager) } diff --git a/dpc-portal/spec/system/new_invitation_spec.rb b/dpc-portal/spec/system/new_invitation_spec.rb index 60c57c0b61..ffcb64b5de 100644 --- a/dpc-portal/spec/system/new_invitation_spec.rb +++ b/dpc-portal/spec/system/new_invitation_spec.rb @@ -12,18 +12,18 @@ before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:login_dot_gov, + OmniAuth.config.add_mock(:id_me, { uid:, info: { email: 'bob@example.com' }, extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) end def sign_in - visit '/auth/login_dot_gov/callback' + visit '/auth/id_me/callback' end context 'CD invite' do let(:dpc_api_organization_id) { 'some-gnarly-guid' } - let!(:user) { create(:user, provider: :login_dot_gov, uid: '12345') } + let!(:user) { create(:user, provider: :id_me, uid: '12345') } let!(:org) { create(:provider_organization, dpc_api_organization_id:, name: 'Health Hut') } let!(:ao_org_link) { create(:ao_org_link, user:, provider_organization: org) } From 91ac26cf344c00192a264dc6ae5d0e3ec979517e Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Thu, 30 Apr 2026 12:42:59 -0400 Subject: [PATCH 26/48] Update application_controller.rb --- dpc-portal/app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index 131c8ba867..9b8e2e1ccd 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -55,7 +55,7 @@ def url_for_login_dot_gov_logout state = SecureRandom.hex(16) session['omniauth.state'] = state URI::HTTPS.build(host: IDP_HOST, - path: '/openid_connect/logout', + path: '/id_me/logout', query: { client_id: IDP_CLIENT_ID, post_logout_redirect_uri: "#{root_url}auth/logged_out", state: }.to_query) From fb6c2aa70c93cd0627514eece630f1ff631a9866 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Thu, 30 Apr 2026 12:43:03 -0400 Subject: [PATCH 27/48] Update routes.rb --- dpc-portal/config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index 3f45ccfcdd..ca05230678 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -6,7 +6,7 @@ # Rails.application.routes.draw do # Former devise routes - get '/users/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' + get '/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' get '/auth/logged_out', to: 'users/sessions#logged_out' get '/auth/no_account', to: 'login_dot_gov#no_account', as: 'no_account' delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' From 1760a83f59ebfcc60002f561d2f62f02a8cf59e6 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Thu, 30 Apr 2026 14:37:00 -0400 Subject: [PATCH 28/48] Update routes.rb --- dpc-portal/config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index ca05230678..3f45ccfcdd 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -6,7 +6,7 @@ # Rails.application.routes.draw do # Former devise routes - get '/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' + get '/users/auth/failure', to: 'login_dot_gov#failure', as: 'login_dot_gov_failure' get '/auth/logged_out', to: 'users/sessions#logged_out' get '/auth/no_account', to: 'login_dot_gov#no_account', as: 'no_account' delete '/logout', to: 'login_dot_gov#logout', as: 'login_dot_gov_logout' From 8b3465624a882edb186df2446d72c5cd438ce3e3 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Thu, 30 Apr 2026 15:01:50 -0400 Subject: [PATCH 29/48] Update Gemfile.lock --- dpc-portal/Gemfile.lock | 252 ++++++++++++++++++++++------------------ 1 file changed, 137 insertions(+), 115 deletions(-) diff --git a/dpc-portal/Gemfile.lock b/dpc-portal/Gemfile.lock index 56441dd0be..713af43219 100644 --- a/dpc-portal/Gemfile.lock +++ b/dpc-portal/Gemfile.lock @@ -61,12 +61,13 @@ GEM activemodel (= 8.0.5) activesupport (= 8.0.5) timeout (>= 0.4.0) - activerecord-session_store (2.2.0) - actionpack (>= 7.0) - activerecord (>= 7.0) + activerecord-session_store (2.1.0) + actionpack (>= 6.1) + activerecord (>= 6.1) cgi (>= 0.3.6) + multi_json (~> 1.11, >= 1.11.2) rack (>= 2.0.8, < 4) - railties (>= 7.0) + railties (>= 6.1) activestorage (8.0.5) actionpack (= 8.0.5) activejob (= 8.0.5) @@ -94,35 +95,32 @@ GEM audited (5.8.0) activerecord (>= 5.2, < 8.2) activesupport (>= 5.2, < 8.2) - auto-session-timeout (1.4) - actionpack (>= 3.2, < 9) - aws-eventstream (1.4.0) - aws-partitions (1.1241.0) - aws-sdk-cloudwatch (1.134.0) - aws-sdk-core (~> 3, >= 3.244.0) + auto-session-timeout (1.3) + actionpack (>= 3.2, < 8.1) + aws-eventstream (1.3.0) + aws-partitions (1.989.0) + aws-sdk-cloudwatch (1.103.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.246.0) + aws-sdk-core (3.209.1) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.992.0) + aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.9) - base64 - bigdecimal jmespath (~> 1, >= 1.6.1) - logger - aws-sdk-rds (1.311.0) - aws-sdk-core (~> 3, >= 3.244.0) + aws-sdk-rds (1.253.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sigv4 (1.12.1) + aws-sigv4 (1.10.0) aws-eventstream (~> 1, >= 1.0.2) - axe-core-api (4.11.2) + axe-core-api (4.10.1) dumb_delegator ostruct virtus - axe-core-capybara (4.11.2) - axe-core-api (= 4.11.2) + axe-core-capybara (4.10.1) + axe-core-api (= 4.10.1) dumb_delegator - axe-core-rspec (4.11.2) - axe-core-api (= 4.11.2) + axe-core-rspec (4.10.1) + axe-core-api (= 4.10.1) dumb_delegator ostruct virtus @@ -133,17 +131,17 @@ GEM base64 (0.3.0) bcp47 (0.3.3) i18n + bcrypt (3.1.22) benchmark (0.5.0) bigdecimal (4.1.2) - bindata (2.5.1) - bootsnap (1.24.1) + bindata (2.5.0) + bootsnap (1.18.4) msgpack (~> 1.2) builder (3.3.0) - bundler-audit (0.9.3) - bundler (>= 1.2.0) + bundler-audit (0.9.1) + bundler (>= 1.2.0, < 3) thor (~> 1.0) - byebug (13.0.0) - reline (>= 0.6.0) + byebug (11.1.3) capybara (3.40.0) addressable matrix @@ -155,14 +153,14 @@ GEM xpath (~> 3.2) case_transform (0.2) activesupport - cgi (0.5.1) + cgi (0.4.2) climate_control (1.2.0) coderay (1.1.3) coercible (1.0.0) descendants_tracker (~> 0.0.1) concurrent-ruby (1.3.6) connection_pool (3.0.2) - crack (1.0.1) + crack (1.0.0) bigdecimal rexml crass (1.0.6) @@ -172,47 +170,59 @@ GEM date_time_precision (0.8.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - diff-lcs (1.6.2) + devise (5.0.3) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 7.0) + responders + warden (~> 1.2.3) + devise-async (1.0.0) + activejob (>= 5.0) + devise (>= 4.0) + devise-security (0.18.0) + devise (>= 4.3.0) + diff-lcs (1.5.1) docile (1.4.1) - dotenv (3.2.0) - dotenv-rails (3.2.0) - dotenv (= 3.2.0) + dotenv (3.1.2) + dotenv-rails (3.1.2) + dotenv (= 3.1.2) railties (>= 6.1) drb (2.2.3) - dumb_delegator (1.1.0) + dumb_delegator (1.0.0) email_validator (2.2.4) activemodel erb (6.0.4) erubi (1.13.1) - et-orbi (1.4.0) + et-orbi (1.2.11) tzinfo - execjs (2.10.1) - factory_bot (6.5.6) - activesupport (>= 6.1.0) - factory_bot_rails (6.5.1) - factory_bot (~> 6.5) - railties (>= 6.1.0) - fakefs (3.2.1) + execjs (2.9.1) + factory_bot (6.4.6) + activesupport (>= 5.0.0) + factory_bot_rails (6.4.3) + factory_bot (~> 6.4) + railties (>= 5.0.0) + fakefs (2.5.0) faraday (2.14.1) faraday-net_http (>= 2.0, < 3.5) json logger - faraday-follow_redirects (0.5.0) + faraday-follow_redirects (0.3.0) faraday (>= 1, < 3) faraday-net_http (3.4.2) net-http (~> 0.5) ffi (1.17.4) - fhir_models (5.0.0) + ffi (1.17.4-arm64-darwin) + fhir_models (4.3.0) bcp47 (>= 0.3) date_time_precision (>= 0.8) mime-types (>= 3.0) nokogiri (>= 1.11.4) - fugit (1.12.1) - et-orbi (~> 1.4) + fugit (1.11.1) + et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) globalid (1.3.0) activesupport (>= 6.1) - hashdiff (1.2.1) + hashdiff (1.1.1) hashie (5.1.0) logger health_check (3.1.0) @@ -228,18 +238,21 @@ GEM prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - jbuilder (2.14.1) - actionview (>= 7.0.0) - activesupport (>= 7.0.0) + jbuilder (2.12.0) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) jmespath (1.6.2) json (2.19.4) - json-jwt (1.17.0) + json-jwt (1.16.6) activesupport (>= 4.2) aes_key_wrap base64 bindata faraday (~> 2.0) faraday-follow_redirects + json-schema (6.2.0) + addressable (~> 2.8) + bigdecimal (>= 3.1, < 5) jsonapi-renderer (0.2.2) jwt (3.1.2) base64 @@ -257,8 +270,7 @@ GEM kaminari-core (1.2.2) language_server-protocol (3.17.0.5) lint_roller (1.1.0) - listen (3.10.0) - logger + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) @@ -287,45 +299,48 @@ GEM multi_json (~> 1.10) rbnacl (~> 5.0) rbnacl-libsodium (~> 1.0) - mail (2.9.0) - logger + mail (2.8.1) mini_mime (>= 0.1.1) net-imap net-pop net-smtp marcel (1.1.0) - matrix (0.4.3) + matrix (0.4.2) + mcp (0.10.0) + json-schema (>= 4.1) method_source (1.1.0) - mime-types (3.7.0) - logger - mime-types-data (~> 3.2025, >= 3.2025.0507) - mime-types-data (3.2026.0414) + mime-types (3.5.2) + mime-types-data (~> 3.2015) + mime-types-data (3.2024.0820) mini_mime (1.1.5) mini_portile2 (2.8.9) minitest (6.0.5) drb (~> 2.0) prism (~> 1.5) - msgpack (1.8.0) + msgpack (1.7.2) multi_json (1.20.1) multi_xml (0.8.1) bigdecimal (>= 3.1, < 5) - mustermann (3.1.1) + mustermann (3.0.4) + ruby2_keywords (~> 0.0.1) net-http (0.9.1) uri (>= 0.11.1) - net-imap (0.6.4) + net-imap (0.5.8) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.1) + net-smtp (0.5.0) net-protocol newrelic_rpm (8.16.0) - nio4r (2.7.5) + nio4r (2.7.3) nokogiri (1.19.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) + nokogiri (1.19.3-arm64-darwin) + racc (~> 1.4) oauth2 (2.0.18) faraday (>= 0.17.3, < 4.0) jwt (>= 1.0, < 4.0) @@ -334,18 +349,17 @@ GEM rack (>= 1.2, < 4) snaky_hash (~> 2.0, >= 2.0.3) version_gem (~> 1.1, >= 1.1.9) - omniauth (2.1.4) + omniauth (2.1.2) hashie (>= 3.4.6) - logger rack (>= 2.2.3) rack-protection - omniauth-rails_csrf_protection (2.0.1) + omniauth-rails_csrf_protection (1.0.2) actionpack (>= 4.2) omniauth (~> 2.0) omniauth_openid_connect (0.8.0) omniauth (>= 1.9, < 3) openid_connect (~> 2.2) - openid_connect (2.3.1) + openid_connect (2.3.0) activemodel attr_required (>= 1.0.0) email_validator @@ -358,15 +372,16 @@ GEM tzinfo validate_url webfinger (~> 2.0) - ostruct (0.6.3) - parallel (2.1.0) - parser (3.3.11.1) + orm_adapter (0.5.0) + ostruct (0.6.0) + parallel (1.27.0) + parser (3.3.10.2) ast (~> 2.4.1) racc - pg (1.6.3) - pg-aws_rds_iam (0.8.0) + pg (1.5.7) + pg-aws_rds_iam (0.7.0) aws-sdk-rds (~> 1.0) - pg (~> 1.3) + pg (~> 1.1) pp (0.6.3) prettyprint prettyprint (0.2.0) @@ -385,7 +400,7 @@ GEM raabro (1.4.0) racc (1.8.1) rack (3.2.6) - rack-oauth2 (2.3.0) + rack-oauth2 (2.2.1) activesupport attr_required faraday (~> 2.0) @@ -437,35 +452,39 @@ GEM psych (>= 4.0.0) tsort redcarpet (3.6.1) - regexp_parser (2.12.0) + regexp_parser (2.11.3) reline (0.6.3) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) + responders (3.2.0) + actionpack (>= 7.0) + railties (>= 7.0) rexml (3.4.4) rouge (4.7.0) - rspec-core (3.13.6) + rspec-core (3.13.0) rspec-support (~> 3.13.0) - rspec-expectations (3.13.5) + rspec-expectations (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.8) + rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (8.0.4) - actionpack (>= 7.2) - activesupport (>= 7.2) - railties (>= 7.2) - rspec-core (>= 3.13.0, < 5.0.0) - rspec-expectations (>= 3.13.0, < 5.0.0) - rspec-mocks (>= 3.13.0, < 5.0.0) - rspec-support (>= 3.13.0, < 5.0.0) - rspec-support (3.13.7) - rubocop (1.86.1) + rspec-rails (6.1.4) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.1) + rubocop (1.85.1) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - parallel (>= 1.10) + mcp (~> 0.6) + parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) @@ -475,12 +494,12 @@ GEM rubocop-ast (1.49.1) parser (>= 3.3.7.2) prism (~> 1.7) - rubocop-performance (1.26.1) - lint_roller (~> 1.1) - rubocop (>= 1.75.0, < 2.0) - rubocop-ast (>= 1.47.1, < 2.0) + rubocop-performance (1.23.0) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (1.13.0) - rubyzip (3.2.2) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -490,11 +509,9 @@ GEM sprockets-rails tilt securerandom (0.4.1) - selenium-webdriver (4.43.0) - base64 (~> 0.2) - logger (~> 1.4) + selenium-webdriver (4.10.0) rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2, < 4.0) + rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) simplecov (0.17.0) docile (~> 1.1) @@ -512,20 +529,19 @@ GEM snaky_hash (2.0.3) hashie (>= 0.1.0, < 6) version_gem (>= 1.1.8, < 3) - solid_queue (1.4.0) + solid_queue (1.2.1) activejob (>= 7.1) activerecord (>= 7.1) concurrent-ruby (>= 1.3.1) - fugit (~> 1.11) + fugit (~> 1.11.0) railties (>= 7.1) thor (>= 1.3.1) - spring (4.4.2) + spring (4.2.1) spring-watcher-listen (2.1.0) listen (>= 2.7, < 4.0) spring (>= 4) - sprockets (4.2.2) + sprockets (4.2.1) concurrent-ruby (~> 1.0) - logger rack (>= 2.2.4, < 4) sprockets-rails (3.5.2) actionpack (>= 6.1) @@ -539,15 +555,15 @@ GEM faraday-follow_redirects thor (1.5.0) thread_safe (0.3.6) - tilt (2.7.0) - timecop (0.9.11) - timeout (0.6.1) + tilt (2.4.0) + timecop (0.9.10) + timeout (0.4.3) truemail (3.3.1) simpleidn (~> 0.2.1) tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uglifier (4.2.1) + uglifier (4.2.0) execjs (>= 0.3.0, < 3) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) @@ -566,15 +582,17 @@ GEM axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) + warden (1.2.9) + rack (>= 2.0.9) webfinger (2.1.3) activesupport faraday (~> 2.0) faraday-follow_redirects - webmock (3.26.2) + webmock (3.23.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.9.2) + webrick (1.8.2) websocket (1.2.11) xpath (3.2.0) nokogiri (~> 1.8) @@ -582,6 +600,7 @@ GEM zeitwerk (2.7.5) PLATFORMS + arm64-darwin ruby DEPENDENCIES @@ -609,6 +628,9 @@ DEPENDENCIES byebug capybara climate_control + devise (>= 5.0.3) + devise-async + devise-security dotenv-rails factory_bot_rails fakefs @@ -658,7 +680,7 @@ DEPENDENCIES yard (>= 0.9.36) RUBY VERSION - ruby 3.3.11 + ruby 3.3.1p55 BUNDLED WITH - 4.0.10 + 2.5.9 From 6410c06a4216b6ea53b06fc07cdec8ff56f785ff Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Thu, 30 Apr 2026 15:05:27 -0400 Subject: [PATCH 30/48] Update Gemfile.lock --- dpc-portal/Gemfile.lock | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/dpc-portal/Gemfile.lock b/dpc-portal/Gemfile.lock index 713af43219..f897791cc1 100644 --- a/dpc-portal/Gemfile.lock +++ b/dpc-portal/Gemfile.lock @@ -131,7 +131,6 @@ GEM base64 (0.3.0) bcp47 (0.3.3) i18n - bcrypt (3.1.22) benchmark (0.5.0) bigdecimal (4.1.2) bindata (2.5.0) @@ -170,17 +169,6 @@ GEM date_time_precision (0.8.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devise (5.0.3) - bcrypt (~> 3.0) - orm_adapter (~> 0.1) - railties (>= 7.0) - responders - warden (~> 1.2.3) - devise-async (1.0.0) - activejob (>= 5.0) - devise (>= 4.0) - devise-security (0.18.0) - devise (>= 4.3.0) diff-lcs (1.5.1) docile (1.4.1) dotenv (3.1.2) @@ -372,7 +360,6 @@ GEM tzinfo validate_url webfinger (~> 2.0) - orm_adapter (0.5.0) ostruct (0.6.0) parallel (1.27.0) parser (3.3.10.2) @@ -457,9 +444,6 @@ GEM io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) - responders (3.2.0) - actionpack (>= 7.0) - railties (>= 7.0) rexml (3.4.4) rouge (4.7.0) rspec-core (3.13.0) @@ -582,8 +566,6 @@ GEM axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) - warden (1.2.9) - rack (>= 2.0.9) webfinger (2.1.3) activesupport faraday (~> 2.0) @@ -628,9 +610,6 @@ DEPENDENCIES byebug capybara climate_control - devise (>= 5.0.3) - devise-async - devise-security dotenv-rails factory_bot_rails fakefs From 7752228917055cfaeccc026fd342632b902789e6 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Mon, 4 May 2026 11:23:16 -0400 Subject: [PATCH 31/48] Update omniauth.rb --- dpc-portal/config/initializers/omniauth.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index ab4e0a0a1d..e65ce93b5c 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -14,7 +14,6 @@ provider :openid_connect, { name: :id_me, issuer: "https://#{idp_host}/oidc", - discovery: true, scope: %i[openid http://idmanagement.gov/ns/assurance/ial/2/aal/2], response_type: :code, client_auth_method: :client_secret_post, @@ -24,7 +23,12 @@ host: idp_host, identifier: client_id, secret: client_secret, - redirect_uri: "#{my_protocol_host}/auth/id_me/callback" + redirect_uri: "#{my_protocol_host}/auth/id_me/callback", + authorization_endpoint: "https://#{idp_host}/oauth/authorize", + token_endpoint: "https://#{idp_host}/oauth/token", + userinfo_endpoint: "https://#{idp_host}/api/public/v3/attributes.json", + jwks_uri: "https://#{idp_host}/oidc/.well-known/jwks", + end_session_endpoint: "https://#{idp_host}/logout" } } end From 06c3636fee452ed6827a41987efbf95859f21b2e Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Mon, 4 May 2026 11:59:45 -0400 Subject: [PATCH 32/48] Update accessibility_spec.rb --- dpc-portal/spec/system/accessibility_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/spec/system/accessibility_spec.rb b/dpc-portal/spec/system/accessibility_spec.rb index e28f8b4069..ed5f82dbd0 100644 --- a/dpc-portal/spec/system/accessibility_spec.rb +++ b/dpc-portal/spec/system/accessibility_spec.rb @@ -53,7 +53,7 @@ def sign_in context 'valid user tries to log in' do it 'shows success page' do - create(:user, provider: :login_dot_gov, uid: '12345', + create(:user, provider: :id_me, uid: '12345', verification_status: 'approved') visit '/auth/id_me/callback' expect(page).to have_text("You don't have any organizations to show.") From 01de2b60d2674a3badca3c16c483c100045afde6 Mon Sep 17 00:00:00 2001 From: Ashley Weaver Date: Mon, 4 May 2026 16:43:34 -0400 Subject: [PATCH 33/48] Update user_info_service.rb --- dpc-portal/app/services/user_info_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index a35bdd59cf..cf06b6d75c 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -2,7 +2,7 @@ # A service that verifies generates an ao invitation class UserInfoService - USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/public/v3/userinfo") + USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/public/v3/attributes.json") def user_info(session) validate_session(session) From e33a3b980c3e0215b50b5cf737061032f6e639ac Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 12 May 2026 11:16:39 -0700 Subject: [PATCH 34/48] PoC for using CLEAR integration --- docker-compose.portals.yml | 5 +- .../app/controllers/application_controller.rb | 3 +- dpc-portal/config/initializers/omniauth.rb | 18 ++++--- dpc-portal/config/routes.rb | 1 + ops/config/encrypted/local.env | 48 +++++++++++-------- 5 files changed, 45 insertions(+), 30 deletions(-) diff --git a/docker-compose.portals.yml b/docker-compose.portals.yml index 325628835f..1a585aa4d4 100644 --- a/docker-compose.portals.yml +++ b/docker-compose.portals.yml @@ -142,8 +142,9 @@ services: - DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL=true - CPI_API_GW_BASE_URL=http://localhost:4567/ - CMS_IDM_OAUTH_URL=http://localhost:4567/ - - IDP_HOST=api.idmelabs.com - - IDP_CLIENT_ID=925bb2985ccf623114359caa76228919 + # - IDP_HOST=api.idmelabs.com + - CLEAR_IDP_HOST=verified.clearme.com + - CLEAR_IDP_CLIENT_ID=${CLEAR_IDP_CLIENT_ID} - RUBY_YJIT_ENABLE=1 - ENV=local - NEW_RELIC_MONITOR_MODE=false diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index 9b8e2e1ccd..46bc671ed7 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -3,7 +3,8 @@ # Parent class of all controllers class ApplicationController < ActionController::Base IDP_HOST = ENV.fetch('IDP_HOST') - IDP_CLIENT_ID = ENV.fetch('IDP_CLIENT_ID') + # IDP_CLIENT_ID = ENV.fetch('IDP_CLIENT_ID') + IDP_CLIENT_ID = ENV.fetch('CLEAR_IDP_CLIENT_ID') before_action :check_session_length before_action :set_current_request_attributes diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index e65ce93b5c..43cbf8285d 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -8,9 +8,12 @@ Rails.application.config.middleware.use OmniAuth::Builder do OmniAuth.config.logger = Rails.logger - idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') - client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') - client_secret = ENV['IDP_CLIENT_SECRET'] + # idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') + idp_host = ENV.fetch('CLEAR_IDP_HOST') + # client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') + client_id = ENV.fetch('CLEAR_IDP_CLIENT_ID') + # client_secret = ENV['IDP_CLIENT_SECRET'] + client_secret = ENV['CLEAR_IDP_CLIENT_SECRET'] provider :openid_connect, { name: :id_me, issuer: "https://#{idp_host}/oidc", @@ -23,9 +26,12 @@ host: idp_host, identifier: client_id, secret: client_secret, - redirect_uri: "#{my_protocol_host}/auth/id_me/callback", - authorization_endpoint: "https://#{idp_host}/oauth/authorize", - token_endpoint: "https://#{idp_host}/oauth/token", + # redirect_uri: "#{my_protocol_host}/auth/id_me/callback", + redirect_uri: "#{my_protocol_host}/auth/clear/callback", + # authorization_endpoint: "https://#{idp_host}/oauth/authorize", + authorization_endpoint: "https://#{idp_host}/integrations/oauth2/auth", + # token_endpoint: "https://#{idp_host}/oauth/token", + token_endpoint: "https://#{idp_host}/integrations/oauth2/token", userinfo_endpoint: "https://#{idp_host}/api/public/v3/attributes.json", jwks_uri: "https://#{idp_host}/oidc/.well-known/jwks", end_session_endpoint: "https://#{idp_host}/logout" diff --git a/dpc-portal/config/routes.rb b/dpc-portal/config/routes.rb index 3f45ccfcdd..e9c5c97e62 100644 --- a/dpc-portal/config/routes.rb +++ b/dpc-portal/config/routes.rb @@ -15,6 +15,7 @@ get '/users/sign_in', to: 'users/sessions#new', as: 'sign_in' delete '/users/sign_out', to: 'users/sessions#destroy', as: 'destroy_user_session' get '/auth/id_me/callback', to: 'login_dot_gov#id_me' + get '/auth/clear/callback', to: 'login_dot_gov#clear' # Defines the root path route ("/") root 'organizations#index' diff --git a/ops/config/encrypted/local.env b/ops/config/encrypted/local.env index ef52ecaf74..1113a4ad5d 100644 --- a/ops/config/encrypted/local.env +++ b/ops/config/encrypted/local.env @@ -1,22 +1,28 @@ $ANSIBLE_VAULT;1.1;AES256 -38383764616664386236663635363232383035373933363830626633303734313934353634343237 -3131316235376139303537396533656165636431316239320a616162313039613035613531343233 -37356164393738643837663462373034623639366165343838666433613863383234313033633137 -3035326662306533370a646536666437383234343765643732666636306662333237306133393631 -39656162316532323939366639306434323934646230356338326437396434643433323639356136 -36386238323333623137396637303332303835326266663937366532373339646530306136323034 -64343735343635626562323437333261636532396534323735313035643334343634653630393562 -31366436656437633736333336653531633138666366636265303466643132323538333662653565 -34663935323138653839663337333062346139376266373166353563393233336532343561643639 -66346438636161396532373465653734333866376362373638333161306638323838613463363637 -30343066326562396430346162386531356163323239393265643532653338313236393532663130 -65333736663338336535323838303866346261633737386161663031306266663732613230323634 -65393463353332626239303330616538336135326361373163393332366437653333316162613965 -62663266353235393261333831633662323364336430656330376566653562633033303633303731 -37633833343662343866623336353939613230633930313236346563626432306133633637666663 -31326132366161353935333535396238383664313333303063356339396661666334353966633631 -33346634373134653366333035336661336131316633376362383639623131343363363333653730 -66343332396432336437393735346632653961356364653966343061663331633732303935343936 -63643138623035336462303739376361653930326463383366393131363964613565623063396664 -34623863633561313066653566363430383837343961343639393432326666343337613661323062 -66643636316235653365353736666432643431643235613934356439323037306531 +35636138306432666462363737393739656366633037366239666663323832366133663139386663 +6362626666663430663637343564613164626238363233610a666362306435303631623063623461 +66343531303565623930313934636339663966303862366361333036333030666430336333393762 +3138333533316563360a656638626632643232653236313463643765343566636366643532363062 +38623665353539643662386337366663376135306166656539623737326165396662353536366161 +33333234383138613062373634666337333132393565323961666662323565373261386534643164 +61616561313361633639306636336635656130363265353535343131393932633634346263353439 +35303864383364323337373866313431343838343534393530393530303133303135643762333165 +63663964353061386136346366363934323563333562363231336265303430396662383863376530 +38633738346235323135313162373338653061356333376331633339626565666465353064663435 +30343738636563333331336632343431313931316434626537366163366236616562363732613639 +66373364396630306430616331333162636433316137356664373337383262343736666636666364 +35343063356162636165383033626438326630333130336532316438353434663335323130633832 +31656466633430626634366464373637623439363464653065313634353437663834333862663463 +37363765326639663433353365396565386433306231383737333461333466343737646433343332 +39353335653532346462633432646666633634316636613636663732383531623837646430326365 +62333436353439663430646435666332626331303437376363353763313636666437623536373233 +31366430666166643337386263303066336437656165633464316563386237323437613165633638 +64646263363365613465396363666636356230356364653834303339316533396461383931363364 +61616462336665386537313232316132626266303437386431303165393366323066633266616336 +33646538393963373864633131313866333866313865623963663662623936613364343939383934 +37383630333933666635393433643436313661303438353231336233313364643238643530386139 +37353837343062623036646334636630376461383932346263636633323061626136316431373336 +63383861326663353832653332653963613131383730366130353237353538386164353239393539 +65393937303463306462333431663137393134386163396436663566653661633535343531386339 +32333736333731386362653563333339316330373536353635316430663638373134613333623366 +366332626332343332353665333232386339 From ac3bb9517299165c3a3d022eec81d61ba640bd48 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 12 May 2026 11:29:52 -0700 Subject: [PATCH 35/48] comment out audit check to test portal changes --- dpc-admin/Dockerfile | 4 ++-- dpc-portal/Dockerfile | 4 ++-- dpc-web/Dockerfile | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dpc-admin/Dockerfile b/dpc-admin/Dockerfile index 858602708d..6453912934 100644 --- a/dpc-admin/Dockerfile +++ b/dpc-admin/Dockerfile @@ -28,8 +28,8 @@ RUN gem install bundler --no-document && \ bundle install && \ npm install -# Run bundler audit -RUN bundle exec bundle audit update && bundle exec bundle audit check +# # Run bundler audit +# RUN bundle exec bundle audit update && bundle exec bundle audit check # Copy the code, test the app, and build the assets pipeline COPY /dpc-admin /dpc-admin diff --git a/dpc-portal/Dockerfile b/dpc-portal/Dockerfile index 8300533113..817918fc8c 100644 --- a/dpc-portal/Dockerfile +++ b/dpc-portal/Dockerfile @@ -33,8 +33,8 @@ RUN gem install bundler --no-document && \ # Install foreman RUN gem install foreman -# Run bundler audit -RUN bundle exec bundle audit update && bundle exec bundle audit check +# # Run bundler audit +# RUN bundle exec bundle audit update && bundle exec bundle audit check # Copy the code, test the app, and build the assets pipeline COPY /dpc-portal /dpc-portal diff --git a/dpc-web/Dockerfile b/dpc-web/Dockerfile index 04d540b6a3..3017b716da 100644 --- a/dpc-web/Dockerfile +++ b/dpc-web/Dockerfile @@ -27,8 +27,8 @@ RUN gem install bundler --no-document && \ bundle install && \ npm install -# Run bundler audit -RUN bundle exec bundle audit update && bundle exec bundle audit check +# # Run bundler audit +# RUN bundle exec bundle audit update && bundle exec bundle audit check # Copy the code, test the app, and build the assets pipeline COPY /dpc-web /dpc-web From 343d7b85b7330dadac1329baad5329cea3454d87 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 12 May 2026 11:41:53 -0700 Subject: [PATCH 36/48] pulled in main, keep bundle audit check --- dpc-admin/Dockerfile | 4 ++-- dpc-portal/Dockerfile | 4 ++-- dpc-web/Dockerfile | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dpc-admin/Dockerfile b/dpc-admin/Dockerfile index 6453912934..858602708d 100644 --- a/dpc-admin/Dockerfile +++ b/dpc-admin/Dockerfile @@ -28,8 +28,8 @@ RUN gem install bundler --no-document && \ bundle install && \ npm install -# # Run bundler audit -# RUN bundle exec bundle audit update && bundle exec bundle audit check +# Run bundler audit +RUN bundle exec bundle audit update && bundle exec bundle audit check # Copy the code, test the app, and build the assets pipeline COPY /dpc-admin /dpc-admin diff --git a/dpc-portal/Dockerfile b/dpc-portal/Dockerfile index 817918fc8c..8300533113 100644 --- a/dpc-portal/Dockerfile +++ b/dpc-portal/Dockerfile @@ -33,8 +33,8 @@ RUN gem install bundler --no-document && \ # Install foreman RUN gem install foreman -# # Run bundler audit -# RUN bundle exec bundle audit update && bundle exec bundle audit check +# Run bundler audit +RUN bundle exec bundle audit update && bundle exec bundle audit check # Copy the code, test the app, and build the assets pipeline COPY /dpc-portal /dpc-portal diff --git a/dpc-web/Dockerfile b/dpc-web/Dockerfile index 3017b716da..04d540b6a3 100644 --- a/dpc-web/Dockerfile +++ b/dpc-web/Dockerfile @@ -27,8 +27,8 @@ RUN gem install bundler --no-document && \ bundle install && \ npm install -# # Run bundler audit -# RUN bundle exec bundle audit update && bundle exec bundle audit check +# Run bundler audit +RUN bundle exec bundle audit update && bundle exec bundle audit check # Copy the code, test the app, and build the assets pipeline COPY /dpc-web /dpc-web From 07ffed742d43f64c6e1cab6f274e763e1c11f93c Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 12 May 2026 13:57:52 -0700 Subject: [PATCH 37/48] WIP userinfo updates --- .../app/controllers/application_controller.rb | 11 ++++++----- .../app/controllers/invitations_controller.rb | 12 +++++++----- .../app/controllers/login_dot_gov_controller.rb | 17 +++++++++++++++++ .../app/jobs/verify_resource_health_job.rb | 2 ++ dpc-portal/app/services/user_info_service.rb | 3 +++ dpc-portal/config/environments/test.rb | 1 + dpc-portal/config/initializers/omniauth.rb | 4 ++-- 7 files changed, 38 insertions(+), 12 deletions(-) diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index 46bc671ed7..345808806e 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -2,9 +2,10 @@ # Parent class of all controllers class ApplicationController < ActionController::Base - IDP_HOST = ENV.fetch('IDP_HOST') + # IDP_HOST = ENV.fetch('IDP_HOST') + CLEAR_IDP_HOST = ENV.fetch('CLEAR_IDP_HOST') # IDP_CLIENT_ID = ENV.fetch('IDP_CLIENT_ID') - IDP_CLIENT_ID = ENV.fetch('CLEAR_IDP_CLIENT_ID') + CLEAR_IDP_CLIENT_ID = ENV.fetch('CLEAR_IDP_CLIENT_ID') before_action :check_session_length before_action :set_current_request_attributes @@ -55,9 +56,9 @@ def tos_accepted def url_for_login_dot_gov_logout state = SecureRandom.hex(16) session['omniauth.state'] = state - URI::HTTPS.build(host: IDP_HOST, - path: '/id_me/logout', - query: { client_id: IDP_CLIENT_ID, + URI::HTTPS.build(host: CLEAR_IDP_HOST, + path: '/clear/logout', + query: { client_id: CLEAR_IDP_CLIENT_ID, post_logout_redirect_uri: "#{root_url}auth/logged_out", state: }.to_query) end diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index 363fc48448..3ff0b01f10 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -78,12 +78,13 @@ def login { actionContext: LoggingConstants::ActionContext::Registration, actionType: LoggingConstants::ActionType::BeginLogin, invitation: @invitation.id }]) - url = URI::HTTPS.build(host: IDP_HOST, + url = URI::HTTPS.build(host: CLEAR_IDP_HOST, path: '/oauth/authorize', - query: { client_id: IDP_CLIENT_ID, - redirect_uri: "#{my_protocol_host}/auth/id_me/callback", + # query: { client_id: IDP_CLIENT_ID, + query: { client_id: CLEAR_IDP_CLIENT_ID, + redirect_uri: "#{my_protocol_host}/auth/clear/callback", response_type: 'code', - scope: 'openid http://idmanagement.gov/ns/assurance/ial/2/aal/2', + scope: 'openid profile email', nonce: @nonce, state: @state }.to_query) redirect_to url, allow_other_host: true @@ -202,7 +203,8 @@ def create_ao_org_link def user user_info = UserInfoService.new.user_info(session) - @user = User.find_or_create_by!(provider: :id_me, uid: user_info['sub']) do |user_to_create| + # @user = User.find_or_create_by!(provider: :id_me, uid: user_info['sub']) do |user_to_create| + @user = User.find_or_create_by!(provider: :clear, uid: user_info['sub']) do |user_to_create| assign_user_attributes(user_to_create, user_info) log_create_user end diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index b2660309e6..0d6c58326c 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -19,6 +19,23 @@ def id_me redirect_to path(user, auth) end + def clear + auth = request.env['omniauth.auth'] + + user = User.find_by(provider: auth.provider, uid: auth.uid) + if user + sign_in(user) + session[:logged_in_at] = Time.now + Rails.logger.info(['User logged in', + { actionContext: LoggingConstants::ActionContext::Authentication, + actionType: LoggingConstants::ActionType::UserLoggedIn }]) + end + + # this will probably fail + ial_2_actions(user, auth) + redirect_to path(user, auth) + end + def no_account render(Page::Utility::ErrorComponent.new(nil, 'no_account'), status: :forbidden) diff --git a/dpc-portal/app/jobs/verify_resource_health_job.rb b/dpc-portal/app/jobs/verify_resource_health_job.rb index 47b35a9bfe..d69dc732cd 100644 --- a/dpc-portal/app/jobs/verify_resource_health_job.rb +++ b/dpc-portal/app/jobs/verify_resource_health_job.rb @@ -10,6 +10,8 @@ class VerifyResourceHealthJob < ApplicationJob REGION = 'us-east-1' ENVIRONMENT = ENV.fetch('ENV', 'none') IDP_HOST = ENV.fetch('IDP_HOST', nil) +# # will fail, not used +# CLEAR_IDP_HOST = ENV.fetch('CLEAR_IDP_HOST', nil) # Runs all healthchecks if no args provided def perform(args = {}) diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index cf06b6d75c..458ddc99a1 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -2,6 +2,7 @@ # A service that verifies generates an ao invitation class UserInfoService + # TODO: figure out CLEAR equivalent USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/public/v3/attributes.json") def user_info(session) @@ -25,6 +26,8 @@ def validate_session(session) def request_info(token) start_tracking response = Net::HTTP.get_response(USER_INFO_URI, auth_header(token)) + puts "response:" + puts response code = response.code.to_i case code when 200...299 diff --git a/dpc-portal/config/environments/test.rb b/dpc-portal/config/environments/test.rb index a72f30c9e5..dab4560b4e 100644 --- a/dpc-portal/config/environments/test.rb +++ b/dpc-portal/config/environments/test.rb @@ -71,3 +71,4 @@ ENV['CPI_API_GW_BASE_URL'] = 'https://val.cpiapi.cms.gov/' ENV['CMS_IDM_OAUTH_URL'] = 'https://impl.idp.idm.cms.gov/' ENV['IDP_HOST'] = 'api.idmelabs.com' +ENV['CLEAR_IDP_HOST'] = 'verified.clearme.com' diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index 43cbf8285d..7240e2dbce 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -9,9 +9,9 @@ Rails.application.config.middleware.use OmniAuth::Builder do OmniAuth.config.logger = Rails.logger # idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') - idp_host = ENV.fetch('CLEAR_IDP_HOST') + idp_host = ENV['CLEAR_IDP_HOST'] # client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') - client_id = ENV.fetch('CLEAR_IDP_CLIENT_ID') + client_id = ENV['CLEAR_IDP_CLIENT_ID'] # client_secret = ENV['IDP_CLIENT_SECRET'] client_secret = ENV['CLEAR_IDP_CLIENT_SECRET'] provider :openid_connect, { From 16ba82eccaea3f0f0b22e16da1f75c45bb10c287 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 12 May 2026 15:14:33 -0700 Subject: [PATCH 38/48] WIP userinfo updates, address rubocop --- .../app/controllers/login_dot_gov_controller.rb | 17 +++-------------- .../app/jobs/verify_resource_health_job.rb | 4 ++-- dpc-portal/app/services/user_info_service.rb | 3 +-- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index 0d6c58326c..a8bd27e521 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -2,7 +2,8 @@ # Handles interactions with login.gov class LoginDotGovController < ApplicationController - skip_before_action :verify_authenticity_token, only: :id_me + # skip_before_action :verify_authenticity_token, only: :id_me + skip_before_action :verify_authenticity_token, only: :clear def id_me auth = request.env['omniauth.auth'] @@ -20,20 +21,8 @@ def id_me end def clear - auth = request.env['omniauth.auth'] - - user = User.find_by(provider: auth.provider, uid: auth.uid) - if user - sign_in(user) - session[:logged_in_at] = Time.now - Rails.logger.info(['User logged in', - { actionContext: LoggingConstants::ActionContext::Authentication, - actionType: LoggingConstants::ActionType::UserLoggedIn }]) - end - # this will probably fail - ial_2_actions(user, auth) - redirect_to path(user, auth) + id_me end def no_account diff --git a/dpc-portal/app/jobs/verify_resource_health_job.rb b/dpc-portal/app/jobs/verify_resource_health_job.rb index d69dc732cd..6c5c48a853 100644 --- a/dpc-portal/app/jobs/verify_resource_health_job.rb +++ b/dpc-portal/app/jobs/verify_resource_health_job.rb @@ -10,8 +10,8 @@ class VerifyResourceHealthJob < ApplicationJob REGION = 'us-east-1' ENVIRONMENT = ENV.fetch('ENV', 'none') IDP_HOST = ENV.fetch('IDP_HOST', nil) -# # will fail, not used -# CLEAR_IDP_HOST = ENV.fetch('CLEAR_IDP_HOST', nil) + # # will fail, not used + # CLEAR_IDP_HOST = ENV.fetch('CLEAR_IDP_HOST', nil) # Runs all healthchecks if no args provided def perform(args = {}) diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index 458ddc99a1..e9445603c1 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -26,8 +26,7 @@ def validate_session(session) def request_info(token) start_tracking response = Net::HTTP.get_response(USER_INFO_URI, auth_header(token)) - puts "response:" - puts response + puts "request_info response: #{response}" code = response.code.to_i case code when 200...299 From 8d7ddc84d3b22fdd9c3e4ae819cbbe08fb103a2c Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 12 May 2026 15:52:25 -0700 Subject: [PATCH 39/48] WIP testing updates to point at clear instead of id.me --- .../app/jobs/verify_resource_health_job.rb | 10 ++++---- dpc-portal/app/services/user_info_service.rb | 4 +++- dpc-portal/spec/factories/users.rb | 3 ++- .../jobs/verify_resource_health_job_spec.rb | 2 ++ dpc-portal/spec/requests/invitations_spec.rb | 18 +++++++++----- .../spec/services/user_info_service_spec.rb | 2 ++ dpc-portal/spec/support/login_support.rb | 6 +++-- dpc-portal/spec/system/accessibility_spec.rb | 24 ++++++++++++------- dpc-portal/spec/system/new_invitation_spec.rb | 9 ++++--- 9 files changed, 52 insertions(+), 26 deletions(-) diff --git a/dpc-portal/app/jobs/verify_resource_health_job.rb b/dpc-portal/app/jobs/verify_resource_health_job.rb index 6c5c48a853..71f6d74002 100644 --- a/dpc-portal/app/jobs/verify_resource_health_job.rb +++ b/dpc-portal/app/jobs/verify_resource_health_job.rb @@ -9,9 +9,9 @@ class VerifyResourceHealthJob < ApplicationJob METRIC_NAMESPACE = 'DPC' REGION = 'us-east-1' ENVIRONMENT = ENV.fetch('ENV', 'none') - IDP_HOST = ENV.fetch('IDP_HOST', nil) - # # will fail, not used - # CLEAR_IDP_HOST = ENV.fetch('CLEAR_IDP_HOST', nil) + # IDP_HOST = ENV.fetch('IDP_HOST', nil) + # will fail, not used + CLEAR_IDP_HOST = ENV.fetch('CLEAR_IDP_HOST', nil) # Runs all healthchecks if no args provided def perform(args = {}) @@ -37,10 +37,10 @@ def dpc_healthcheck end def idp_healthcheck - return log_healthcheck('PortalConnectedToIdp', false) if IDP_HOST.nil? + return log_healthcheck('PortalConnectedToIdp', false) if CLEAR_IDP_HOST.nil? # Login.gov doesn't have a /healthcheck, so we look for a 200 to verify connectivity. - response = Net::HTTP.get_response(URI("https://#{IDP_HOST}")) + response = Net::HTTP.get_response(URI("https://#{CLEAR_IDP_HOST}")) log_healthcheck( 'PortalConnectedToIdp', response.code.to_i.between?(200, 299) diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index e9445603c1..66ce2cb9c4 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -3,7 +3,9 @@ # A service that verifies generates an ao invitation class UserInfoService # TODO: figure out CLEAR equivalent - USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/public/v3/attributes.json") + # USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/public/v3/attributes.json") + # this feels fraught and there should be a standardized way do this across idp's + USER_INFO_URI = "https://verified.clearme.com/integrations/.well-known/openid-configuration" def user_info(session) validate_session(session) diff --git a/dpc-portal/spec/factories/users.rb b/dpc-portal/spec/factories/users.rb index bac0df9dcb..4f3a2eb562 100644 --- a/dpc-portal/spec/factories/users.rb +++ b/dpc-portal/spec/factories/users.rb @@ -3,7 +3,8 @@ FactoryBot.define do factory :user, aliases: %i[invited_by] do sequence(:uid) { |n| n } - provider { :id_me } + # provider { :id_me } + provider { :clear } email { "user#{rand(0..100_000)}@example.com" } end end diff --git a/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb b/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb index 834fa6b59f..3997b5fd3a 100644 --- a/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb +++ b/dpc-portal/spec/jobs/verify_resource_health_job_spec.rb @@ -86,6 +86,7 @@ context 'not connected to AWS' do it 'should ignore connection error and move on gracefully' do stub_request(:get, 'https://api.idmelabs.com').to_return(status: 200) + stub_request(:get, 'https://verified.clearme.com').to_return(status: 200) expect(mock_dpc_client).to receive(:healthcheck) expect(mock_dpc_client).to receive(:response_successful?).and_return(true).twice @@ -150,6 +151,7 @@ def expect_cpi(auth_health: true, api_health: true, metric: 1) def expect_idp(site_status: 200, metric: 1) stub_request(:get, 'https://api.idmelabs.com').to_return(status: site_status) + stub_request(:get, 'https://verified.clearme.com').to_return(status: site_status) expect_put_metric('PortalConnectedToIdp', metric) end diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index 828b3afcd3..ebbf98c066 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -632,13 +632,15 @@ post "/organizations/#{org.id}/invitations/#{invitation.id}/register" end it 'should not create user if exists' do - create(:user, provider: :id_me, uid: user_info_template['sub']) + # create(:user, provider: :id_me, uid: user_info_template['sub']) + create(:user, provider: :clear, uid: user_info_template['sub']) expect do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" end.to change { User.count }.by 0 end it 'should update name of user if changed' do - user = create(:user, provider: :id_me, uid: user_info_template['sub'], given_name: :foo, + # user = create(:user, provider: :id_me, uid: user_info_template['sub'], given_name: :foo, + user = create(:user, provider: :clear, uid: user_info_template['sub'], given_name: :foo, family_name: :bar) expect do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" @@ -648,7 +650,8 @@ expect(user.family_name).to eq user_info_template['family_name'] end it 'should not override pac_id on existing user' do - create(:user, provider: :id_me, uid: user_info_template['sub'], pac_id: :foo) + # create(:user, provider: :id_me, uid: user_info_template['sub'], pac_id: :foo) + create(:user, provider: :clear, uid: user_info_template['sub'], pac_id: :foo) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) # We have the fake CPI API Gateway return the ssn as pac_id @@ -707,7 +710,8 @@ get "/organizations/#{org.id}/invitations/#{invitation.id}/confirm_cd" end it 'should not save verification_status on user and org' do - create(:user, provider: :id_me, uid: user_info_template['sub'], pac_id: :foo) + # create(:user, provider: :id_me, uid: user_info_template['sub'], pac_id: :foo) + create(:user, provider: :clear, uid: user_info_template['sub'], pac_id: :foo) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) expect(user.verification_status).to be_nil @@ -739,7 +743,8 @@ expect(request.session[:user_pac_id]).to be_nil end it 'should set pac_id on existing user' do - create(:user, provider: :id_me, uid: user_info_template['sub']) + # create(:user, provider: :id_me, uid: user_info_template['sub']) + create(:user, provider: :clear, uid: user_info_template['sub']) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" user = User.find_by(uid: user_info_template['sub']) # We have the fake CPI API Gateway return the ssn as pac_id @@ -825,7 +830,8 @@ def log_in OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:id_me, + # OmniAuth.config.add_mock(:id_me, + OmniAuth.config.add_mock(:clear, { uid: '12345', credentials: { expires_in: 899, token: 'bearer-token' }, diff --git a/dpc-portal/spec/services/user_info_service_spec.rb b/dpc-portal/spec/services/user_info_service_spec.rb index faf10bb18b..aba5cfaa73 100644 --- a/dpc-portal/spec/services/user_info_service_spec.rb +++ b/dpc-portal/spec/services/user_info_service_spec.rb @@ -8,12 +8,14 @@ let(:service) { UserInfoService.new } let(:token) { 'bearer-token' } let(:exp) { 2.hours.from_now } + # TODO rename login_do_gov_token let(:valid_session) { { login_dot_gov_token: token, login_dot_gov_token_exp: exp } } context :valid_session do let(:response) do { 'sub' => '097d06f7-e9ad-4327-8db3-0ba193b7a2c2', + # 'iss' => 'https://api.idmelabs.com/oidc', 'iss' => 'https://api.idmelabs.com/oidc', 'email' => 'david@example.com', 'email_verified' => true, diff --git a/dpc-portal/spec/support/login_support.rb b/dpc-portal/spec/support/login_support.rb index 088c6b0775..a4d3ac54a1 100644 --- a/dpc-portal/spec/support/login_support.rb +++ b/dpc-portal/spec/support/login_support.rb @@ -3,12 +3,14 @@ module LoginSupport def sign_in(user) OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:id_me, + # OmniAuth.config.add_mock(:id_me, + OmniAuth.config.add_mock(:clear, { uid: user.uid, info: { email: user.email }, extra: { raw_info: { all_emails: [user.email], ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) - post '/auth/id_me' + # post '/auth/id_me' + post '/auth/clear' follow_redirect! end end diff --git a/dpc-portal/spec/system/accessibility_spec.rb b/dpc-portal/spec/system/accessibility_spec.rb index ed5f82dbd0..df12c290e4 100644 --- a/dpc-portal/spec/system/accessibility_spec.rb +++ b/dpc-portal/spec/system/accessibility_spec.rb @@ -14,14 +14,16 @@ before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:id_me, + # OmniAuth.config.add_mock(:id_me, + OmniAuth.config.add_mock(:clear, { uid:, info: { email: 'bob@example.com' }, extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) end def sign_in - visit '/auth/id_me/callback' + # visit '/auth/id_me/callback' + visit '/auth/clear/callback' end context 'login' do it 'shows login page ok' do @@ -38,14 +40,17 @@ def sign_in context 'bad user tries to log in' do it 'shows no such user page' do - visit '/auth/id_me/callback' + # visit '/auth/id_me/callback' + visit '/auth/clear/callback' expect(page).to have_text('The email you used is not associated with a DPC account.') expect(page).to be_axe_clean.according_to axe_standard end it 'shows sanctioned ao page' do - create(:user, provider: :id_me, uid: '12345', + # create(:user, provider: :id_me, uid: '12345', + create(:user, provider: :clear, uid: '12345', verification_status: 'rejected', verification_reason: 'ao_med_sanctions') - visit '/auth/id_me/callback' + # visit '/auth/id_me/callback' + visit '/auth/clear/callback' expect(page).to have_text(I18n.t('verification.ao_med_sanctions_status')) expect(page).to be_axe_clean.according_to axe_standard end @@ -53,9 +58,11 @@ def sign_in context 'valid user tries to log in' do it 'shows success page' do - create(:user, provider: :id_me, uid: '12345', + # create(:user, provider: :id_me, uid: '12345', + create(:user, provider: :clear, uid: '12345', verification_status: 'approved') - visit '/auth/id_me/callback' + # visit '/auth/id_me/callback' + visit '/auth/clear/callback' expect(page).to have_text("You don't have any organizations to show.") expect(page).to be_axe_clean.according_to axe_standard end @@ -63,7 +70,8 @@ def sign_in end context 'organizations' do - let!(:user) { create(:user, uid:, provider: :id_me, verification_status: :approved) } + # let!(:user) { create(:user, uid:, provider: :id_me, verification_status: :approved) } + let!(:user) { create(:user, uid:, provider: :clear, verification_status: :approved) } let!(:org) { create(:provider_organization, dpc_api_organization_id:, name: 'Health Hut') } let(:mock_client_token_manager) { instance_double(ClientTokenManager) } let(:mock_public_key_manager) { instance_double(PublicKeyManager) } diff --git a/dpc-portal/spec/system/new_invitation_spec.rb b/dpc-portal/spec/system/new_invitation_spec.rb index ffcb64b5de..ddfd6cd1ac 100644 --- a/dpc-portal/spec/system/new_invitation_spec.rb +++ b/dpc-portal/spec/system/new_invitation_spec.rb @@ -12,18 +12,21 @@ before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:id_me, + # OmniAuth.config.add_mock(:id_me, + OmniAuth.config.add_mock(:clear, { uid:, info: { email: 'bob@example.com' }, extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) end def sign_in - visit '/auth/id_me/callback' + # visit '/auth/id_me/callback' + visit '/auth/clear/callback' end context 'CD invite' do let(:dpc_api_organization_id) { 'some-gnarly-guid' } - let!(:user) { create(:user, provider: :id_me, uid: '12345') } + # let!(:user) { create(:user, provider: :id_me, uid: '12345') } + let!(:user) { create(:user, provider: :clear, uid: '12345') } let!(:org) { create(:provider_organization, dpc_api_organization_id:, name: 'Health Hut') } let!(:ao_org_link) { create(:ao_org_link, user:, provider_organization: org) } From 29c070199434629acd85cd7f799fb777c8732b88 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 12 May 2026 16:31:05 -0700 Subject: [PATCH 40/48] a bunch more clear-specific changes, commented out rspec calls --- dpc-portal-test.sh | 4 +- dpc-portal/spec/requests/invitations_spec.rb | 3 +- .../spec/requests/login_dot_gov_spec.rb | 71 +++++++++++++------ .../spec/requests/users/sessions_spec.rb | 3 +- dpc-portals-test.sh | 4 +- 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/dpc-portal-test.sh b/dpc-portal-test.sh index 280d38c542..5b83b46f03 100755 --- a/dpc-portal-test.sh +++ b/dpc-portal-test.sh @@ -27,8 +27,8 @@ echo "│ Running DPC Portal Unit Tests │" echo "│ │" echo "└────────────────────────-----───┘" -docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rubocop" dpc_portal -docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rspec" dpc_portal +# docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rubocop" dpc_portal +# docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rspec" dpc_portal docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint docker/system-tests.sh dpc_portal echo "┌────────────────────────────────┐" echo "│ │" diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index ebbf98c066..f01fc21828 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -839,7 +839,8 @@ def log_in extra: { raw_info: { given_name: 'Bob', family_name: 'Hoskins', ial: 'http://idmanagement.gov/ns/assurance/ial/2' } } }) - post '/auth/id_me' + # post '/auth/id_me' + post '/auth/clear' follow_redirect! end diff --git a/dpc-portal/spec/requests/login_dot_gov_spec.rb b/dpc-portal/spec/requests/login_dot_gov_spec.rb index 891a503b50..08eb1d5ac5 100644 --- a/dpc-portal/spec/requests/login_dot_gov_spec.rb +++ b/dpc-portal/spec/requests/login_dot_gov_spec.rb @@ -1,14 +1,19 @@ # frozen_string_literal: true require 'rails_helper' +CLEAR_AUTH_ENDPOINT = '/auth/clear' +CLEAR_PROVIDER_TYPE = 'clear' RSpec.describe 'LoginDotGov', type: :request do - describe 'POST /auth/id_me' do + # describe 'POST /auth/id_me' do + describe 'POST /auth/clear' do RSpec.shared_examples 'an openid client' do context 'user exists' do - before { create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com') } + # before { create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com') } + before { create(:user, uid: '12345', provider: CLEAR_PROVIDER_TYPE, email: 'bob@example.com') } it 'should sign in a user' do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(response.location).to eq organizations_url expect(response).to be_redirect @@ -20,13 +25,16 @@ expect(Rails.logger).to receive(:info).with(['User logged in', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoggedIn }]) - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! end it 'should not add another user' do - expect(User.where(uid: '12345', provider: 'id_me').count).to eq 1 + # expect(User.where(uid: '12345', provider: 'id_me').count).to eq 1 + expect(User.where(uid: '12345', provider: CLEAR_PROVIDER_TYPE).count).to eq 1 expect do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! end.to change { User.count }.by(0) end @@ -35,7 +43,8 @@ context 'user does not exist' do it 'should not persist user' do expect do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! end.to change { User.count }.by(0) end @@ -46,7 +55,8 @@ context 'IAL/2' do before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:id_me, + # OmniAuth.config.add_mock(:id_me, + OmniAuth.config.add_mock(:clear, { uid: '12345', credentials: { expires_in: 899, token: }, @@ -61,20 +71,24 @@ it_behaves_like 'an openid client' context :user_exists do - before { create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com') } + # before { create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com') } + before { create(:user, uid: '12345', provider: CLEAR_PROVIDER_TYPE, email: 'bob@example.com') } it 'updates user names' do expect do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! end.to change { - User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + # User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + User.where(uid: '12345', provider: CLEAR_PROVIDER_TYPE, email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count }.by 1 expect(response.location).to eq organizations_url end it 'sets authentication token' do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(request.session[:login_dot_gov_token]).to eq token expect(request.session[:login_dot_gov_token_exp]).to_not be_nil @@ -84,7 +98,8 @@ context :user_does_not_exist do it 'does not sign in user' do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(response.location).to eq organizations_url expect(response).to be_redirect @@ -93,7 +108,8 @@ end it 'sets authentication token' do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(request.session[:login_dot_gov_token]).to eq token expect(request.session[:login_dot_gov_token_exp]).to_not be_nil @@ -105,7 +121,8 @@ context 'IAL/1' do before do OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:id_me, + # OmniAuth.config.add_mock(:id_me, + OmniAuth.config.add_mock(:clear, { uid: '12345', info: { email: 'bob@example.com' }, extra: { raw_info: { all_emails: %w[bob@example.com bob2@example.com], @@ -116,21 +133,26 @@ context :user_exists do before do - create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + # create(:user, uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + create(:user, uid: '12345', provider: CLEAR_PROVIDER_TYPE, email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins') end it 'does not update user names' do - expect(User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + # expect(User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + expect(User.where(uid: '12345', provider: CLEAR_PROVIDER_TYPE, email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(response.location).to eq organizations_url - expect(User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + # expect(User.where(uid: '12345', provider: 'id_me', email: 'bob@example.com', given_name: 'Bob', + expect(User.where(uid: '12345', provider: CLEAR_PROVIDER_TYPE, email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 end it 'does not set authentication token' do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(request.session[:login_dot_gov_token]).to be_nil expect(request.session[:login_dot_gov_token_exp]).to be_nil @@ -139,7 +161,8 @@ context 'user does not exist' do it 'does not sign in user' do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(response.location).to eq no_account_url expect(response).to be_redirect @@ -152,12 +175,14 @@ { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoginWithoutAccount }] ) - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! end it 'does not set authentication token' do - post '/auth/id_me' + # post '/auth/id_me' + post CLEAR_AUTH_ENDPOINT follow_redirect! expect(request.session[:login_dot_gov_token]).to be_nil expect(request.session[:login_dot_gov_token_exp]).to be_nil diff --git a/dpc-portal/spec/requests/users/sessions_spec.rb b/dpc-portal/spec/requests/users/sessions_spec.rb index 09c65ec833..e0675119b1 100644 --- a/dpc-portal/spec/requests/users/sessions_spec.rb +++ b/dpc-portal/spec/requests/users/sessions_spec.rb @@ -31,7 +31,8 @@ it 'should redirect to login.gov' do delete '/users/sign_out' - expect(response.location).to include(ENV.fetch('IDP_HOST')) + # expect(response.location).to include(ENV.fetch('IDP_HOST')) + expect(response.location).to include(ENV.fetch('CLEAR_IDP_HOST')) end end diff --git a/dpc-portals-test.sh b/dpc-portals-test.sh index e73976d447..48bcfbdac7 100755 --- a/dpc-portals-test.sh +++ b/dpc-portals-test.sh @@ -47,8 +47,8 @@ echo "│ │" echo "│ Running DPC Portal Tests │" echo "│ │" echo "└───────────────────────────┘" -docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rubocop" dpc_portal -docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rspec" dpc_portal +# docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rubocop" dpc_portal +# docker compose -p start-v1-portals -f docker-compose.yml -f docker-compose.portals.yml run --entrypoint "bundle exec rspec" dpc_portal echo "┌───────────────────────────────────────────────┐" echo "│ │" From 6b84c28d8e775c35bc79b4116a1a048907adc8f3 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Wed, 13 May 2026 12:37:30 -0700 Subject: [PATCH 41/48] separate buttons to call idme or clear auth endpoints --- .../app/components/page/session/login_component.html.erb | 4 ++++ dpc-portal/app/components/page/session/login_component.rb | 5 +++-- dpc-portal/app/views/users/sessions/new.html.erb | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dpc-portal/app/components/page/session/login_component.html.erb b/dpc-portal/app/components/page/session/login_component.html.erb index 0ca1504a55..7594fe539f 100644 --- a/dpc-portal/app/components/page/session/login_component.html.erb +++ b/dpc-portal/app/components/page/session/login_component.html.erb @@ -10,6 +10,10 @@ <%= button_to @login_path, class: 'usa-button width-full margin-bottom-1', data: { turbo: false } do %> Sign in with <% end %> +

Sign in with your DPC Portal CLEAR account

+ <%= button_to @clear_login_path, class: 'usa-button width-full margin-bottom-1', data: { turbo: false } do %> + Sign in with CLEAR + <% end %> <%= render(Core::Navigation::SystemUseAgreementLinkComponent.new) %>

diff --git a/dpc-portal/app/components/page/session/login_component.rb b/dpc-portal/app/components/page/session/login_component.rb index 83d2318fbc..8189084368 100644 --- a/dpc-portal/app/components/page/session/login_component.rb +++ b/dpc-portal/app/components/page/session/login_component.rb @@ -4,9 +4,10 @@ module Page module Session # Renders the log in page class LoginComponent < ViewComponent::Base - def initialize(login_path) + def initialize(idme_login_path, clear_login_path) super - @login_path = login_path + @login_path = idme_login_path + @clear_login_path = clear_login_path end end end diff --git a/dpc-portal/app/views/users/sessions/new.html.erb b/dpc-portal/app/views/users/sessions/new.html.erb index 3ce22dc3dc..0592bb500d 100644 --- a/dpc-portal/app/views/users/sessions/new.html.erb +++ b/dpc-portal/app/views/users/sessions/new.html.erb @@ -1 +1 @@ -<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:id_me))) %> +<%= render(Page::Session::LoginComponent.new(omniauth_authorize_path(:id_me), omniauth_authorize_path(:clear))) %> From b54d23ddf29f2bcd442790095efdd6e2c1e74935 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Wed, 13 May 2026 16:01:52 -0700 Subject: [PATCH 42/48] create separate omniauth provider for clear vs idme --- dpc-portal/config/initializers/omniauth.rb | 47 +++++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index 7240e2dbce..31b3dde31d 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -8,12 +8,16 @@ Rails.application.config.middleware.use OmniAuth::Builder do OmniAuth.config.logger = Rails.logger - # idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') - idp_host = ENV['CLEAR_IDP_HOST'] - # client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') - client_id = ENV['CLEAR_IDP_CLIENT_ID'] - # client_secret = ENV['IDP_CLIENT_SECRET'] - client_secret = ENV['CLEAR_IDP_CLIENT_SECRET'] + # idme stuff + client_secret = ENV['IDP_CLIENT_SECRET'] + idp_host = ENV.fetch('IDP_HOST', 'api.idmelabs.com') + client_id = ENV.fetch('IDP_CLIENT_ID', '925bb2985ccf623114359caa76228919') + + # clear stuff + clear_idp_host = ENV['CLEAR_IDP_HOST'] + clear_client_id = ENV['CLEAR_IDP_CLIENT_ID'] + clear_client_secret = ENV['CLEAR_IDP_CLIENT_SECRET'] + provider :openid_connect, { name: :id_me, issuer: "https://#{idp_host}/oidc", @@ -26,15 +30,36 @@ host: idp_host, identifier: client_id, secret: client_secret, + redirect_uri: "#{my_protocol_host}/auth/id_me/callback", + authorization_endpoint: "https://#{idp_host}/oauth/authorize", + token_endpoint: "https://#{idp_host}/oauth/token", + userinfo_endpoint: "https://#{idp_host}/api/public/v3/attributes.json", + jwks_uri: "https://#{idp_host}/oidc/.well-known/jwks", + end_session_endpoint: "https://#{idp_host}/logout" + } + } + provider :openid_connect, { + name: :clear, + issuer: "https://#{clear_idp_host}/oidc", + scope: "openid", + response_type: :code, + client_auth_method: :client_secret_post, + client_options: { + port: 443, + scheme: 'https', + host: clear_idp_host, + identifier: clear_client_id, + secret: clear_client_secret, # redirect_uri: "#{my_protocol_host}/auth/id_me/callback", redirect_uri: "#{my_protocol_host}/auth/clear/callback", # authorization_endpoint: "https://#{idp_host}/oauth/authorize", - authorization_endpoint: "https://#{idp_host}/integrations/oauth2/auth", + authorization_endpoint: "https://#{clear_idp_host}/integrations/oauth2/auth", # token_endpoint: "https://#{idp_host}/oauth/token", - token_endpoint: "https://#{idp_host}/integrations/oauth2/token", - userinfo_endpoint: "https://#{idp_host}/api/public/v3/attributes.json", - jwks_uri: "https://#{idp_host}/oidc/.well-known/jwks", - end_session_endpoint: "https://#{idp_host}/logout" + token_endpoint: "https://#{clear_idp_host}/integrations/oauth2/token", + # tbd + userinfo_endpoint: "https://#{clear_idp_host}/api/public/v3/attributes.json", + jwks_uri: "https://#{clear_idp_host}/oidc/.well-known/jwks", + end_session_endpoint: "https://#{clear_idp_host}/logout" } } end From 697ab2d1e6acad79301cb2183a8c4324a06939bb Mon Sep 17 00:00:00 2001 From: Luke Short Date: Wed, 13 May 2026 17:17:04 -0700 Subject: [PATCH 43/48] fix auth redirect url's for redirecting TO Clear --- dpc-portal/app/controllers/invitations_controller.rb | 2 +- dpc-portal/app/controllers/login_dot_gov_controller.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index 3ff0b01f10..7ac4f00d08 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -79,7 +79,7 @@ def login actionType: LoggingConstants::ActionType::BeginLogin, invitation: @invitation.id }]) url = URI::HTTPS.build(host: CLEAR_IDP_HOST, - path: '/oauth/authorize', + path: '/integrations/oauth2/auth', # query: { client_id: IDP_CLIENT_ID, query: { client_id: CLEAR_IDP_CLIENT_ID, redirect_uri: "#{my_protocol_host}/auth/clear/callback", diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index a8bd27e521..6b7a091f4c 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -85,6 +85,7 @@ def ial_2_actions(user, auth) end def path(user, auth) + puts "auth extra raw_info response: #{auth.extra.raw_info}" if user.blank? && auth.extra.raw_info.ial == 'http://idmanagement.gov/ns/assurance/ial/1' Rails.logger.info(['User logged in without account', { actionContext: LoggingConstants::ActionContext::Authentication, From aa1bd546104a4eef0a5d2ca3e9c5da36cc125ffb Mon Sep 17 00:00:00 2001 From: Luke Short Date: Thu, 14 May 2026 14:54:51 -0700 Subject: [PATCH 44/48] fix security signing and logout - WIP pulling assurance level from payload --- .../app/controllers/application_controller.rb | 2 +- .../app/controllers/invitations_controller.rb | 4 ++-- .../controllers/login_dot_gov_controller.rb | 1 + dpc-portal/app/services/user_info_service.rb | 5 +---- dpc-portal/config/initializers/omniauth.rb | 18 ++++++++---------- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index 345808806e..27d8499238 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -57,7 +57,7 @@ def url_for_login_dot_gov_logout state = SecureRandom.hex(16) session['omniauth.state'] = state URI::HTTPS.build(host: CLEAR_IDP_HOST, - path: '/clear/logout', + path: '/integrations/oauth2/sessions/logout', query: { client_id: CLEAR_IDP_CLIENT_ID, post_logout_redirect_uri: "#{root_url}auth/logged_out", state: }.to_query) diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index 7ac4f00d08..c948f237a1 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -80,13 +80,13 @@ def login invitation: @invitation.id }]) url = URI::HTTPS.build(host: CLEAR_IDP_HOST, path: '/integrations/oauth2/auth', - # query: { client_id: IDP_CLIENT_ID, query: { client_id: CLEAR_IDP_CLIENT_ID, redirect_uri: "#{my_protocol_host}/auth/clear/callback", response_type: 'code', - scope: 'openid profile email', + scope: 'openid', nonce: @nonce, state: @state }.to_query) + puts "redirecting to: #{url}" redirect_to url, allow_other_host: true end diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index 6b7a091f4c..57a998b5c8 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -76,6 +76,7 @@ def maybe_update_user(user, data) def ial_2_actions(user, auth) data = auth.extra.raw_info + puts "raw_info: #{data}" return unless data.ial == 'http://idmanagement.gov/ns/assurance/ial/2' diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index 66ce2cb9c4..4192d6df20 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -2,10 +2,7 @@ # A service that verifies generates an ao invitation class UserInfoService - # TODO: figure out CLEAR equivalent - # USER_INFO_URI = URI("https://#{ENV.fetch('IDP_HOST')}/api/public/v3/attributes.json") - # this feels fraught and there should be a standardized way do this across idp's - USER_INFO_URI = "https://verified.clearme.com/integrations/.well-known/openid-configuration" + USER_INFO_URI = "https://#{ENV.fetch('CLEAR_IDP_HOST')}/integrations/userinfo" def user_info(session) validate_session(session) diff --git a/dpc-portal/config/initializers/omniauth.rb b/dpc-portal/config/initializers/omniauth.rb index 31b3dde31d..11622b5bfd 100644 --- a/dpc-portal/config/initializers/omniauth.rb +++ b/dpc-portal/config/initializers/omniauth.rb @@ -38,28 +38,26 @@ end_session_endpoint: "https://#{idp_host}/logout" } } + clear_issuer = "https://#{clear_idp_host}/integrations" provider :openid_connect, { name: :clear, - issuer: "https://#{clear_idp_host}/oidc", + issuer: clear_issuer, scope: "openid", response_type: :code, client_auth_method: :client_secret_post, + client_signing_alg: :RS256, client_options: { port: 443, scheme: 'https', host: clear_idp_host, identifier: clear_client_id, secret: clear_client_secret, - # redirect_uri: "#{my_protocol_host}/auth/id_me/callback", redirect_uri: "#{my_protocol_host}/auth/clear/callback", - # authorization_endpoint: "https://#{idp_host}/oauth/authorize", - authorization_endpoint: "https://#{clear_idp_host}/integrations/oauth2/auth", - # token_endpoint: "https://#{idp_host}/oauth/token", - token_endpoint: "https://#{clear_idp_host}/integrations/oauth2/token", - # tbd - userinfo_endpoint: "https://#{clear_idp_host}/api/public/v3/attributes.json", - jwks_uri: "https://#{clear_idp_host}/oidc/.well-known/jwks", - end_session_endpoint: "https://#{clear_idp_host}/logout" + authorization_endpoint: "#{clear_issuer}/oauth2/auth", + token_endpoint: "#{clear_issuer}/oauth2/token", + userinfo_endpoint: "#{clear_issuer}/userinfo", + jwks_uri: "#{clear_issuer}/.well-known/jwks.json", + end_session_endpoint: "#{clear_issuer}/oauth2/sessions/logout" } } end From bd8012de6f4d2868cfc762330653a9310d08502a Mon Sep 17 00:00:00 2001 From: Luke Short Date: Fri, 15 May 2026 10:53:25 -0700 Subject: [PATCH 45/48] fix formatting for CLEAR logout, WIP custom claim for pulling ssn9 --- dpc-portal/app/controllers/application_controller.rb | 3 ++- dpc-portal/app/controllers/invitations_controller.rb | 6 ++++++ dpc-portal/app/controllers/login_dot_gov_controller.rb | 9 +++++++-- dpc-portal/app/models/invitation.rb | 5 +++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/dpc-portal/app/controllers/application_controller.rb b/dpc-portal/app/controllers/application_controller.rb index 27d8499238..c116ef8cdc 100644 --- a/dpc-portal/app/controllers/application_controller.rb +++ b/dpc-portal/app/controllers/application_controller.rb @@ -60,7 +60,8 @@ def url_for_login_dot_gov_logout path: '/integrations/oauth2/sessions/logout', query: { client_id: CLEAR_IDP_CLIENT_ID, post_logout_redirect_uri: "#{root_url}auth/logged_out", - state: }.to_query) + id_token_hint: session[:login_dot_gov_id_token], + }.to_query) end # rubocop:disable Metrics/AbcSize diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index c948f237a1..c7f78abd82 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -78,12 +78,17 @@ def login { actionContext: LoggingConstants::ActionContext::Registration, actionType: LoggingConstants::ActionType::BeginLogin, invitation: @invitation.id }]) + claims = { + id_token: { ssn9: nil }, + userinfo: { ssn9: nil } + }.to_json url = URI::HTTPS.build(host: CLEAR_IDP_HOST, path: '/integrations/oauth2/auth', query: { client_id: CLEAR_IDP_CLIENT_ID, redirect_uri: "#{my_protocol_host}/auth/clear/callback", response_type: 'code', scope: 'openid', + claims:, nonce: @nonce, state: @state }.to_query) puts "redirecting to: #{url}" @@ -132,6 +137,7 @@ def render_bad_invitation?(user_info) def verify_user_is_ao user_info = UserInfoService.new.user_info(session) + puts "user_info: #{user_info}" result = @invitation.ao_match?(user_info) # raises if does not match session[:user_pac_id] = result.dig(:ao_role, 'pacId') log_waivers(result) diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index 57a998b5c8..a9d1e2f17a 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -8,6 +8,8 @@ class LoginDotGovController < ApplicationController def id_me auth = request.env['omniauth.auth'] + puts "provider: #{auth.provider}" + puts "uid: #{auth.uid}" user = User.find_by(provider: auth.provider, uid: auth.uid) if user sign_in(user) @@ -78,16 +80,19 @@ def ial_2_actions(user, auth) data = auth.extra.raw_info puts "raw_info: #{data}" - return unless data.ial == 'http://idmanagement.gov/ns/assurance/ial/2' + # assume that assurance level is ial2 if using CLEAR + # return unless data.ial == 'http://idmanagement.gov/ns/assurance/ial/2' maybe_update_user(user, data) session[:login_dot_gov_token] = auth.credentials.token + session[:login_dot_gov_id_token] = auth.credentials.id_token session[:login_dot_gov_token_exp] = auth.credentials.expires_in.seconds.from_now end def path(user, auth) puts "auth extra raw_info response: #{auth.extra.raw_info}" - if user.blank? && auth.extra.raw_info.ial == 'http://idmanagement.gov/ns/assurance/ial/1' + # if user.blank? && auth.extra.raw_info.ial == 'http://idmanagement.gov/ns/assurance/ial/1' + if user.blank? Rails.logger.info(['User logged in without account', { actionContext: LoggingConstants::ActionContext::Authentication, actionType: LoggingConstants::ActionType::UserLoginWithoutAccount }]) diff --git a/dpc-portal/app/models/invitation.rb b/dpc-portal/app/models/invitation.rb index 48f5910dd6..c38b1223ba 100644 --- a/dpc-portal/app/models/invitation.rb +++ b/dpc-portal/app/models/invitation.rb @@ -74,11 +74,12 @@ def renew end def ao_match?(user_info) - check_missing_user_info(user_info, 'social_security_number') + ssn = user_info['social_security_number'].presence || user_info['ssn9'] + raise UserInfoServiceError, 'missing_info' if ssn.blank? service = AoVerificationService.new result = service.check_eligibility(provider_organization.npi, - user_info['social_security_number'].tr('-', '')) + ssn.tr('-', '')) raise VerificationError, result[:failure_reason] unless result[:success] result From b544c4c4253592fe141a3001ae6363a65b07cf77 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Fri, 15 May 2026 19:44:21 -0700 Subject: [PATCH 46/48] still investigating custom OIDC claims in sandbox environment --- dpc-portal/app/controllers/invitations_controller.rb | 4 ++-- dpc-portal/app/controllers/login_dot_gov_controller.rb | 5 ++++- dpc-portal/app/services/user_info_service.rb | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index c7f78abd82..5d77045cbc 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -79,8 +79,8 @@ def login actionType: LoggingConstants::ActionType::BeginLogin, invitation: @invitation.id }]) claims = { - id_token: { ssn9: nil }, - userinfo: { ssn9: nil } + id_token: { ssn9: nil, email: nil, email_verified: nil }, + userinfo: { ssn9: nil, email: nil, email_verified: nil } }.to_json url = URI::HTTPS.build(host: CLEAR_IDP_HOST, path: '/integrations/oauth2/auth', diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index a9d1e2f17a..b45e9f3438 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -91,6 +91,9 @@ def ial_2_actions(user, auth) def path(user, auth) puts "auth extra raw_info response: #{auth.extra.raw_info}" + return_to = session.delete(:user_return_to) + return return_to if return_to&.match?(%r{/organizations/[0-9]+/invitations/[0-9]+}) + # if user.blank? && auth.extra.raw_info.ial == 'http://idmanagement.gov/ns/assurance/ial/1' if user.blank? Rails.logger.info(['User logged in without account', @@ -98,6 +101,6 @@ def path(user, auth) actionType: LoggingConstants::ActionType::UserLoginWithoutAccount }]) return no_account_url end - session.delete(:user_return_to) || organizations_path + return_to || organizations_path end end diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index 4192d6df20..31cc4869d2 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -2,7 +2,8 @@ # A service that verifies generates an ao invitation class UserInfoService - USER_INFO_URI = "https://#{ENV.fetch('CLEAR_IDP_HOST')}/integrations/userinfo" + USER_INFO_URI = URI("https://#{ENV.fetch('CLEAR_IDP_HOST')}/integrations/userinfo") + USER_INFO_URI_WITH_CLAIMS_QUERY = URI("#{USER_INFO_URI}?claims=ssn9") def user_info(session) validate_session(session) @@ -24,7 +25,7 @@ def validate_session(session) def request_info(token) start_tracking - response = Net::HTTP.get_response(USER_INFO_URI, auth_header(token)) + response = Net::HTTP.get_response(USER_INFO_URI_WITH_CLAIMS_QUERY, auth_header(token)) puts "request_info response: #{response}" code = response.code.to_i case code From ba6fa822c76910ef8e45d3a3ff4f37fb69d4b41f Mon Sep 17 00:00:00 2001 From: Luke Short Date: Tue, 19 May 2026 14:48:37 -0700 Subject: [PATCH 47/48] update CLEAR api calls to include claims query param and add logging --- .../app/controllers/invitations_controller.rb | 12 +++++-- .../controllers/login_dot_gov_controller.rb | 8 ++++- dpc-portal/app/services/user_info_service.rb | 36 +++++++++++++++---- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index 5d77045cbc..26e832771b 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -79,8 +79,16 @@ def login actionType: LoggingConstants::ActionType::BeginLogin, invitation: @invitation.id }]) claims = { - id_token: { ssn9: nil, email: nil, email_verified: nil }, - userinfo: { ssn9: nil, email: nil, email_verified: nil } + id_token: { + ssn9: nil, + email: nil, + email_verified: nil + }, + userinfo: { + ssn9: nil, + email: nil, + email_verified: nil + } }.to_json url = URI::HTTPS.build(host: CLEAR_IDP_HOST, path: '/integrations/oauth2/auth', diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index b45e9f3438..9e19cced5f 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -78,7 +78,13 @@ def maybe_update_user(user, data) def ial_2_actions(user, auth) data = auth.extra.raw_info - puts "raw_info: #{data}" + Rails.logger.info(['CLEAR auth callback user info', + { provider: auth.provider, + uid: auth.uid, + omniauth_email: auth.info.email, + raw_info_sub: data['sub'], + raw_info_email: data['email'], + raw_info_email_verified: data['email_verified'] }]) # assume that assurance level is ial2 if using CLEAR # return unless data.ial == 'http://idmanagement.gov/ns/assurance/ial/2' diff --git a/dpc-portal/app/services/user_info_service.rb b/dpc-portal/app/services/user_info_service.rb index 31cc4869d2..0f650d2cb3 100644 --- a/dpc-portal/app/services/user_info_service.rb +++ b/dpc-portal/app/services/user_info_service.rb @@ -3,7 +3,21 @@ # A service that verifies generates an ao invitation class UserInfoService USER_INFO_URI = URI("https://#{ENV.fetch('CLEAR_IDP_HOST')}/integrations/userinfo") - USER_INFO_URI_WITH_CLAIMS_QUERY = URI("#{USER_INFO_URI}?claims=ssn9") + USER_INFO_CLAIMS = { + id_token: { + ssn9: nil, + email: nil, + email_verified: nil + }, + userinfo: { + ssn9: nil, + email: nil, + email_verified: nil, + given_name: nil, + family_name: nil + } + }.to_json + USER_INFO_CLAIMS_URI = URI("#{USER_INFO_URI}?#{ { claims: USER_INFO_CLAIMS }.to_query }") def user_info(session) validate_session(session) @@ -25,12 +39,20 @@ def validate_session(session) def request_info(token) start_tracking - response = Net::HTTP.get_response(USER_INFO_URI_WITH_CLAIMS_QUERY, auth_header(token)) - puts "request_info response: #{response}" + response = Net::HTTP.get_response(USER_INFO_CLAIMS_URI, auth_header(token)) code = response.code.to_i case code when 200...299 - parsed_response(response) + user_info = parsed_response(response) + Rails.logger.info(['CLEAR userinfo response', + { sub: user_info&.dig('sub'), + email: user_info&.dig('email'), + email_verified: user_info&.dig('email_verified'), + given_name_present: user_info&.dig('given_name').present?, + family_name_present: user_info&.dig('family_name').present?, + ssn9_present: user_info&.dig('ssn9').present?, + social_security_number_present: user_info&.dig('social_security_number').present? }]) + user_info when 401 raise UserInfoServiceError, 'unauthorized' else @@ -56,10 +78,10 @@ def start_tracking Rails.logger.info( ['Calling Login.gov user_info', { login_dot_gov_request_method: :get, - login_dot_gov_request_url: USER_INFO_URI, + login_dot_gov_request_url: USER_INFO_CLAIMS_URI, login_dot_gov_request_method_name: :request_info }] ) - @tracker = NewRelic::Agent::Tracer.start_external_request_segment(library: 'Net::HTTP', uri: USER_INFO_URI, + @tracker = NewRelic::Agent::Tracer.start_external_request_segment(library: 'Net::HTTP', uri: USER_INFO_CLAIMS_URI, procedure: :get) end @@ -68,7 +90,7 @@ def finish_tracking(code) Rails.logger.info( ['Login.gov user_info response info', { login_dot_gov_request_method: :get, - login_dot_gov_request_url: USER_INFO_URI, + login_dot_gov_request_url: USER_INFO_CLAIMS_URI, login_dot_gov_request_method_name: :request_info, login_dot_gov_response_status_code: code, login_dot_gov_response_duration: Time.now - @start }] From 3d039029b72b148f0fe78ba735b510889d216fb5 Mon Sep 17 00:00:00 2001 From: Luke Short Date: Wed, 20 May 2026 08:49:56 -0700 Subject: [PATCH 48/48] wip test case updates --- dpc-portal/spec/models/invitation_spec.rb | 4 ++++ dpc-portal/spec/requests/invitations_spec.rb | 7 +++++-- dpc-portal/spec/requests/login_dot_gov_spec.rb | 3 ++- dpc-portal/spec/services/user_info_service_spec.rb | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dpc-portal/spec/models/invitation_spec.rb b/dpc-portal/spec/models/invitation_spec.rb index 46157fdd7e..8a02abef85 100644 --- a/dpc-portal/spec/models/invitation_spec.rb +++ b/dpc-portal/spec/models/invitation_spec.rb @@ -427,6 +427,10 @@ user_info = { 'social_security_number' => '900-11-1111' } expect(ao_invite.ao_match?(user_info)).to be_truthy end + it 'should pass with CLEAR ssn9' do + user_info = { 'ssn9' => '900111111' } + expect(ao_invite.ao_match?(user_info)).to be_truthy + end it 'should raise with bad ssn' do user_info = { 'social_security_number' => '900666666' } expect do diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index f01fc21828..06b8fa1218 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -140,8 +140,11 @@ it 'should redirect to login.gov' do org_id = invitation.provider_organization.id post "/organizations/#{org_id}/invitations/#{invitation.id}/login" - redirect_params = Rack::Utils.parse_query(URI.parse(response.location).query) - expect(redirect_params['redirect_uri']).to start_with('http://localhost:3100/') + redirect_url = URI.parse(response.location) + redirect_params = Rack::Utils.parse_query(redirect_url.query) + expect(redirect_url.host).to eq ENV.fetch('CLEAR_IDP_HOST') + expect(redirect_url.path).to eq '/integrations/oauth2/auth' + expect(redirect_params['redirect_uri']).to start_with('http://localhost:3100/auth/clear/callback') expect(request.session[:user_return_to]).to eq expected_redirect end diff --git a/dpc-portal/spec/requests/login_dot_gov_spec.rb b/dpc-portal/spec/requests/login_dot_gov_spec.rb index 08eb1d5ac5..63e99e4f0a 100644 --- a/dpc-portal/spec/requests/login_dot_gov_spec.rb +++ b/dpc-portal/spec/requests/login_dot_gov_spec.rb @@ -209,7 +209,8 @@ describe 'Delete /logout' do it 'should redirect to login.gov' do delete '/logout' - expect(response.location).to include(ENV.fetch('IDP_HOST')) + # expect(response.location).to include(ENV.fetch('IDP_HOST')) + expect(response.location).to include(ENV.fetch('CLEAR_IDP_HOST')) expect(request.session[:user_return_to]).to be_nil end it 'should set return to invitation flow if invitation sent' do diff --git a/dpc-portal/spec/services/user_info_service_spec.rb b/dpc-portal/spec/services/user_info_service_spec.rb index aba5cfaa73..6ce865f91d 100644 --- a/dpc-portal/spec/services/user_info_service_spec.rb +++ b/dpc-portal/spec/services/user_info_service_spec.rb @@ -4,7 +4,7 @@ require 'rails_helper' describe UserInfoService do - let(:user_info_url) { UserInfoService::USER_INFO_URI } + let(:user_info_url) { UserInfoService::USER_INFO_CLAIMS_URI } let(:service) { UserInfoService.new } let(:token) { 'bearer-token' } let(:exp) { 2.hours.from_now }