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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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 74dd2cfde130eccd0729e93c2fec29e6c340f416 Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Tue, 5 May 2026 10:06:01 -0700 Subject: [PATCH 13/13] DPC-5159 multi csp user POC (#2896) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not for merge. ## 🎫 Ticket https://jira.cms.gov/browse/DPC-5159 ## 🛠 Changes - New model with migration: IdpUid to store foreign keys for CSPs - Updated login flow to use IdpUid - Updated user-creation flow in invitations controller to use IdpUid ## ℹ️ Context We need to support the ability of each user to log in to the portal with multiple CSPs. Note: because of the way we fake the CPI API Gateway, most Authrorized Officials share the same PacId. Therefore, unlike in production, where each user will have their own PacId, we cannot bind multiple CSPs to the same user by PacId in local, dev, test, and sandbox environments. That is why we use the email address to deduplicate all users in the lower environments. We do want to test this flow, which is why we also bind AOs on PacId while running automated tests. ## 🧪 Validation Updated Manual tests. Logged in as same user using multiple IdPs. --------- Co-authored-by: jose-verdance --- .../app/controllers/invitations_controller.rb | 35 ++++++++-- .../controllers/login_dot_gov_controller.rb | 2 +- dpc-portal/app/models/idp_uid.rb | 6 ++ dpc-portal/config/locales/en.yml | 2 + .../migrate/20260129150757_create_idp_uids.rb | 18 +++++ dpc-portal/db/schema.rb | 11 ++- dpc-portal/spec/factories/idp_uids.rb | 8 +++ dpc-portal/spec/models/idp_uid_spec.rb | 11 +++ dpc-portal/spec/requests/invitations_spec.rb | 70 +++++++++++++++---- .../spec/requests/login_dot_gov_spec.rb | 27 ++++--- dpc-portal/spec/support/login_support.rb | 5 +- dpc-portal/spec/system/accessibility_spec.rb | 11 +-- dpc-portal/spec/system/new_invitation_spec.rb | 3 +- 13 files changed, 172 insertions(+), 37 deletions(-) create mode 100644 dpc-portal/app/models/idp_uid.rb create mode 100644 dpc-portal/db/migrate/20260129150757_create_idp_uids.rb create mode 100644 dpc-portal/spec/factories/idp_uids.rb create mode 100644 dpc-portal/spec/models/idp_uid_spec.rb diff --git a/dpc-portal/app/controllers/invitations_controller.rb b/dpc-portal/app/controllers/invitations_controller.rb index f18d8ed446..6973c85102 100644 --- a/dpc-portal/app/controllers/invitations_controller.rb +++ b/dpc-portal/app/controllers/invitations_controller.rb @@ -179,6 +179,12 @@ def create_link status: :unprocessable_entity) false end + rescue MultiUserMatchError => e + logger.error(['User matches too many existing users', + { actionContext: LoggingConstants::ActionContext::Registration, error: e.message }]) + + render(Page::Utility::ErrorComponent.new(@invitation, 'multi_user_match')) + nil end def create_cd_org_link @@ -203,14 +209,33 @@ 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| - assign_user_attributes(user_to_create, user_info) - log_create_user - end + # Unique PacIds only available in prod + @user = if @invitation.authorized_official? && (ENV['ENV'] == 'prod' || Rails.env.test?) + ao_user(user_info) + else + User.find_or_create_by(email: @invitation.invited_email) do |user_to_create| + assign_user_attributes(user_to_create, user_info) + log_create_user + end + end + IdpUid.find_or_create_by!(user: @user, provider: :login_dot_gov, uid: user_info['sub']) update_user(user_info) @user end + def ao_user(user_info) + matching_users = User.where('email = ? OR pac_id = ?', @invitation.invited_email, session[:user_pac_id]) + raise MultiUserMatchError, "too many matching users | pac_id: #{session[:user_pac_id]}" if matching_users.size > 1 + + return matching_users.first if matching_users.present? + + user = User.new + assign_user_attributes(user, user_info) + user.save! + log_create_user + user + end + def assign_user_attributes(user_to_create, user_info) user_to_create.email = @invitation.invited_email user_to_create.given_name = user_info['given_name'] @@ -357,4 +382,6 @@ def log_waivers(role_and_waivers) actionType: LoggingConstants::ActionType::AoHasWaiver, invitation: @invitation.id }]) end + + class MultiUserMatchError < StandardError; end end diff --git a/dpc-portal/app/controllers/login_dot_gov_controller.rb b/dpc-portal/app/controllers/login_dot_gov_controller.rb index 64e72f850e..fc3e996d10 100644 --- a/dpc-portal/app/controllers/login_dot_gov_controller.rb +++ b/dpc-portal/app/controllers/login_dot_gov_controller.rb @@ -7,7 +7,7 @@ class LoginDotGovController < ApplicationController def openid_connect auth = request.env['omniauth.auth'] - user = User.find_by(provider: auth.provider, uid: auth.uid) + user = IdpUid.find_by(provider: auth.provider, uid: auth.uid)&.user if user sign_in(user) session[:logged_in_at] = Time.now diff --git a/dpc-portal/app/models/idp_uid.rb b/dpc-portal/app/models/idp_uid.rb new file mode 100644 index 0000000000..e99683af5e --- /dev/null +++ b/dpc-portal/app/models/idp_uid.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# Simple class for holding OIDC information linked to user +class IdpUid < ApplicationRecord + belongs_to :user +end diff --git a/dpc-portal/config/locales/en.yml b/dpc-portal/config/locales/en.yml index 76fba17573..1ecd679ff3 100644 --- a/dpc-portal/config/locales/en.yml +++ b/dpc-portal/config/locales/en.yml @@ -38,6 +38,8 @@ en: missing_info_text: Something happened on our end and we're unable to continue. Please contact dpcinfo@cms.hhs.gov. server_error_status: "Registration unavailable: external system error." server_error_text: We're unable to complete your request right now because a required external system is unavailable. Please try again later. + multi_user_match_status: multi_user_match_status + multi_user_match_text: multi_user_match_text manage_org: Manage your organization. tos_not_signed: You must sign DPC Terms of Service. sign_tos: Sign Terms of Service diff --git a/dpc-portal/db/migrate/20260129150757_create_idp_uids.rb b/dpc-portal/db/migrate/20260129150757_create_idp_uids.rb new file mode 100644 index 0000000000..3ee54a45fe --- /dev/null +++ b/dpc-portal/db/migrate/20260129150757_create_idp_uids.rb @@ -0,0 +1,18 @@ +class CreateIdpUids < ActiveRecord::Migration[8.0] + def up + create_table :idp_uids do |t| + t.string :provider + t.string :uid + t.integer :user_id + + t.timestamps + end + User.all.each do |user| + IdpUid.create!(user:, uid: user.uid, provider: user.provider) + end + add_index(:idp_uids, %i[provider uid]) + end + def down + drop_table :idp_uids + end +end diff --git a/dpc-portal/db/schema.rb b/dpc-portal/db/schema.rb index 26caccf043..f231474001 100644 --- a/dpc-portal/db/schema.rb +++ b/dpc-portal/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_09_19_165916) do +ActiveRecord::Schema[8.0].define(version: 2026_01_29_150757) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -69,6 +69,15 @@ t.index ["user_id"], name: "index_credential_audit_logs_on_user_id" end + create_table "idp_uids", force: :cascade do |t| + t.string "provider" + t.string "uid" + t.integer "user_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["provider", "uid"], name: "index_idp_uids_on_provider_and_uid" + end + create_table "invitations", force: :cascade do |t| t.bigint "provider_organization_id", null: false t.bigint "invited_by_id" diff --git a/dpc-portal/spec/factories/idp_uids.rb b/dpc-portal/spec/factories/idp_uids.rb new file mode 100644 index 0000000000..02d236648f --- /dev/null +++ b/dpc-portal/spec/factories/idp_uids.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :idp_uid do + provider { 'MyString' } + uid { 'MyString' } + end +end diff --git a/dpc-portal/spec/models/idp_uid_spec.rb b/dpc-portal/spec/models/idp_uid_spec.rb new file mode 100644 index 0000000000..38a9ed2048 --- /dev/null +++ b/dpc-portal/spec/models/idp_uid_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe IdpUid, type: :model do + it 'has a user' do + user = create(:user) + idp_uid = create(:idp_uid, user_id: user.id) + expect(idp_uid.user).to eq user + end +end diff --git a/dpc-portal/spec/requests/invitations_spec.rb b/dpc-portal/spec/requests/invitations_spec.rb index 26a243224a..628c23028a 100644 --- a/dpc-portal/spec/requests/invitations_spec.rb +++ b/dpc-portal/spec/requests/invitations_spec.rb @@ -633,14 +633,14 @@ 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, pac_id: user_info_template['social_security_number'], email: 'bob@testy.com') 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, - family_name: :bar) + user = create(:user, pac_id: user_info_template['social_security_number'], + email: 'bob@testy.com', given_name: :foo, family_name: :bar) expect do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" end.to change { User.count }.by 0 @@ -649,9 +649,10 @@ 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, email: user_info_template['email'], pac_id: :foo) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" - user = User.find_by(uid: user_info_template['sub']) + expect(response).to be_ok + user = User.find_by(email: user_info_template['email']) # We have the fake CPI API Gateway return the ssn as pac_id expect(user.pac_id).to eq 'foo' end @@ -708,9 +709,9 @@ 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, email: user_info_template['email']) post "/organizations/#{org.id}/invitations/#{invitation.id}/register" - user = User.find_by(uid: user_info_template['sub']) + user = User.find_by(email: user_info_template['email']) expect(user.verification_status).to be_nil expect(org.reload.verification_status).to be_nil end @@ -733,26 +734,69 @@ post "/organizations/#{org.id}/invitations/#{invitation.id}/confirm" end it 'should set pac_id on new user' do - post "/organizations/#{org.id}/invitations/#{invitation.id}/register" - user = User.find_by(uid: user_info_template['sub']) + expect do + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + end.to change { User.count }.by(1) + user = User.find_by(pac_id: user_info_template['social_security_number']) # We have the fake CPI API Gateway return the ssn as pac_id expect(user.pac_id).to eq user_info_template['social_security_number'] 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']) - post "/organizations/#{org.id}/invitations/#{invitation.id}/register" - user = User.find_by(uid: user_info_template['sub']) + create(:user, email: user_info_template['email']) + expect do + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + end.to change { User.count }.by 0 + user = User.find_by(email: user_info_template['email']) # We have the fake CPI API Gateway return the ssn as pac_id expect(user.pac_id).to eq user_info_template['social_security_number'] expect(request.session[:user_pac_id]).to be_nil end + it 'should add credential if user with pac id exists' do + user = create(:user, pac_id: user_info_template['social_security_number']) + expect do + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + end.to change { IdpUid.where(user:).count }.by 1 + end + it 'should add credential if user with pac id exists and non-matching credential exists' do + user = create(:user, pac_id: user_info_template['social_security_number']) + create(:idp_uid, user:, uid: user_info_template['sub'], provider: :other_idp) + expect do + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + end.to change { IdpUid.where(user:).count }.by 1 + end + it 'should not add credential if credential exists match on pac_id' do + user = create(:user, pac_id: user_info_template['social_security_number']) + create(:idp_uid, user:, uid: user_info_template['sub'], provider: :login_dot_gov) + expect do + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + end.to change { IdpUid.count }.by 0 + end + it 'should add credential if user with email exists' do + user = create(:user, email: user_info_template['email']) + expect do + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + end.to change { IdpUid.where(user:).count }.by 1 + end + it 'should not add credential if credential exists match on email' do + user = create(:user, email: user_info_template['email']) + create(:idp_uid, user:, uid: user_info_template['sub'], provider: :login_dot_gov) + expect do + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + end.to change { IdpUid.count }.by 0 + end it 'should save verification_status on user and org' do post "/organizations/#{org.id}/invitations/#{invitation.id}/register" - user = User.find_by(uid: user_info_template['sub']) + user = User.find_by(pac_id: user_info_template['social_security_number']) expect(user.verification_status).to eq('approved') expect(org.reload.verification_status).to eq('approved') end + it 'should fail if too many matches' do + create(:user, email: user_info_template['email']) + create(:user, pac_id: user_info_template['social_security_number']) + post "/organizations/#{org.id}/invitations/#{invitation.id}/register" + expect(response.body).to include(I18n.t('verification.multi_user_match_text')) + end end end 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..c4a87bd541 100644 --- a/dpc-portal/spec/requests/login_dot_gov_spec.rb +++ b/dpc-portal/spec/requests/login_dot_gov_spec.rb @@ -6,7 +6,10 @@ describe 'POST /auth/login_dot_gov' 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 do + user = create(:user, email: 'bob@example.com') + create(:idp_uid, user:, uid: '12345', provider: 'login_dot_gov') + end it 'should sign in a user' do post '/auth/login_dot_gov' follow_redirect! @@ -23,12 +26,12 @@ post '/auth/login_dot_gov' follow_redirect! end - it 'should not add another user' do - expect(User.where(uid: '12345', provider: 'login_dot_gov').count).to eq 1 + it 'should not add another user credential' do + expect(IdpUid.where(uid: '12345', provider: 'login_dot_gov').count).to eq 1 expect do post '/auth/login_dot_gov' follow_redirect! - end.to change { User.count }.by(0) + end.to change { IdpUid.count }.by(0) end end @@ -61,13 +64,16 @@ it_behaves_like 'an openid client' context :user_exists do - before { create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com') } + before do + user = create(:user, email: 'bob@example.com') + create(:idp_uid, user:, uid: '12345', provider: 'login_dot_gov') + end it 'updates user names' do expect do post '/auth/login_dot_gov' follow_redirect! end.to change { - User.where(uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', + User.where(email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count }.by 1 expect(response.location).to eq organizations_url @@ -116,16 +122,17 @@ context :user_exists do before do - create(:user, uid: '12345', provider: 'login_dot_gov', email: 'bob@example.com', given_name: 'Bob', - family_name: 'Hoskins') + user = create(:user, email: 'bob@example.com', given_name: 'Bob', + family_name: 'Hoskins') + create(:idp_uid, user:, uid: '12345', provider: 'login_dot_gov') 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(email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 post '/auth/login_dot_gov' 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(email: 'bob@example.com', given_name: 'Bob', family_name: 'Hoskins').count).to eq 1 end diff --git a/dpc-portal/spec/support/login_support.rb b/dpc-portal/spec/support/login_support.rb index 76300a4ba9..cdf4537eca 100644 --- a/dpc-portal/spec/support/login_support.rb +++ b/dpc-portal/spec/support/login_support.rb @@ -2,9 +2,10 @@ module LoginSupport def sign_in(user) + idp_uid = create(:idp_uid, user:, provider: :login_dot_gov) OmniAuth.config.test_mode = true - OmniAuth.config.add_mock(:login_dot_gov, - { uid: user.uid, + OmniAuth.config.add_mock(idp_uid.provider, + { uid: idp_uid.uid, info: { email: user.email }, extra: { raw_info: { all_emails: [user.email], ial: 'http://idmanagement.gov/ns/assurance/ial/1' } } }) diff --git a/dpc-portal/spec/system/accessibility_spec.rb b/dpc-portal/spec/system/accessibility_spec.rb index 1366a9859e..9692603a39 100644 --- a/dpc-portal/spec/system/accessibility_spec.rb +++ b/dpc-portal/spec/system/accessibility_spec.rb @@ -43,8 +43,8 @@ def sign_in 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', - verification_status: 'rejected', verification_reason: 'ao_med_sanctions') + user = create(:user, verification_status: 'rejected', verification_reason: 'ao_med_sanctions') + create(:idp_uid, user_id: user.id, provider: :login_dot_gov, uid: '12345') 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 @@ -53,8 +53,8 @@ def sign_in context 'valid user tries to log in' do it 'shows success page' do - create(:user, provider: :login_dot_gov, uid: '12345', - verification_status: 'approved') + user = create(:user, verification_status: 'approved') + create(:idp_uid, user_id: user.id, provider: :login_dot_gov, uid: '12345') 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 @@ -63,7 +63,8 @@ def sign_in end context 'organizations' do - let!(:user) { create(:user, uid:, provider: :login_dot_gov, verification_status: :approved) } + let!(:user) { create(:user, verification_status: :approved) } + let!(:idp_uid) { create(:idp_uid, user_id: user.id, uid:, provider: :login_dot_gov) } 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..1812c45b64 100644 --- a/dpc-portal/spec/system/new_invitation_spec.rb +++ b/dpc-portal/spec/system/new_invitation_spec.rb @@ -23,7 +23,8 @@ def sign_in 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) } + let!(:idp_uid) { create(:idp_uid, user_id: user.id, 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) }