Class: SAML::URLService
- Inherits:
-
Object
- Object
- SAML::URLService
- Defined in:
- lib/saml/url_service.rb
Overview
This class is responsible for providing the URLs for the various SSO and SLO endpoints
Direct Known Subclasses
Constant Summary collapse
- VIRTUAL_HOST_MAPPINGS =
{ 'https://api.vets.gov' => { base_redirect: 'https://www.vets.gov' }, 'https://staging-api.vets.gov' => { base_redirect: 'https://staging.vets.gov' }, 'https://dev-api.vets.gov' => { base_redirect: 'https://dev.vets.gov' }, 'https://api.va.gov' => { base_redirect: 'https://www.va.gov' }, 'https://staging-api.va.gov' => { base_redirect: 'https://staging.va.gov' }, 'https://dev-api.va.gov' => { base_redirect: 'https://dev.va.gov' }, 'http://localhost:3000' => { base_redirect: "http://#{localhost_redirect}:3001" }, 'http://127.0.0.1:3000' => { base_redirect: "http://#{localhost_ip_redirect}:3001" } }.freeze
- LOGIN_REDIRECT_PARTIAL =
'/auth/login/callback'
- LOGOUT_REDIRECT_PARTIAL =
'/logout/'
- BROKER_CODE =
'iam'
- WEB_CLIENT_ID =
'web'
- MOBILE_CLIENT_ID =
'mobile'
- UNIFIED_SIGN_IN_CLIENTS =
%w[vaweb mhv myvahealth ebenefits vamobile vaoccmobile].freeze
- TERMS_OF_USE_DECLINED_PATH =
'/terms-of-use/declined'
- SKIP_MHV_ACCOUNT_CREATION_CLIENTS =
%w[mhv custom].freeze
Instance Attribute Summary collapse
-
#authn_context ⇒ Object
readonly
Returns the value of attribute authn_context.
-
#query_params ⇒ Object
readonly
Returns the value of attribute query_params.
-
#saml_settings ⇒ Object
readonly
Returns the value of attribute saml_settings.
-
#session ⇒ Object
readonly
Returns the value of attribute session.
-
#tracker ⇒ Object
readonly
Returns the value of attribute tracker.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Instance Method Summary collapse
- #add_query(url, params) ⇒ Object private
-
#base_redirect_url ⇒ Object
REDIRECT_URLS.
- #build_authn_context(assurance_level_url, identity_provider) ⇒ Object private
-
#build_sso_url(link_authn_context, authn_con_compare = AuthnContext::EXACT) ⇒ Object
private
Builds the urls to trigger various SSO policies: mhv, dslogon, idme, mfa, or verify flows.
- #callback_verify_url ⇒ Object
- #current_host ⇒ Object private
- #custom_url(authn) ⇒ Object
- #idme_signup_url(authn_context) ⇒ Object
-
#initialize(saml_settings, session: nil, user: nil, params: {}, loa3_context: LOA::IDME_LOA3_VETS) ⇒ URLService
constructor
A new instance of URLService.
-
#initialize_tracker(params) ⇒ Object
private
Initialize a new SAMLRequestTracker, if a valid previous SAML UUID is given, copy over the payload and created_at timestamp.
- #login_redirect_url(auth: 'success', code: nil) ⇒ Object
-
#login_url(type, authn_context, identity_provider, authn_con_compare = AuthnContext::EXACT) ⇒ Object
SIGN ON URLS.
- #logingov_signup_url(authn_context) ⇒ Object
- #logout_redirect_url ⇒ Object
- #mfa_url ⇒ Object
- #previous_saml_uuid(params) ⇒ Object private
- #relay_state_params ⇒ Object private
- #save_saml_request_tracker(uuid, authn_context) ⇒ Object private
-
#slo_url ⇒ Object
SIGN OFF URLS.
-
#ssoe_slo_url ⇒ Object
logout URL for SSOe.
- #url_settings ⇒ Object private
- #verify_url ⇒ Object
Constructor Details
#initialize(saml_settings, session: nil, user: nil, params: {}, loa3_context: LOA::IDME_LOA3_VETS) ⇒ URLService
Returns a new instance of URLService.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/saml/url_service.rb', line 33 def initialize(saml_settings, session: nil, user: nil, params: {}, loa3_context: LOA::IDME_LOA3_VETS) unless %w[new saml_callback saml_logout_callback ssoe_slo_callback].include?(params[:action]) raise Common::Exceptions::RoutingError, params[:path] end if session.present? @session = session @user = user @authn_context = user&.authn_context end @saml_settings = saml_settings @loa3_context = loa3_context if (params[:action] == 'saml_callback') && params[:RelayState].present? @type = JSON.parse(CGI.unescapeHTML(params[:RelayState]))['type'] end @query_params = {} @tracker = initialize_tracker(params) Sentry.set_extras(params:) Sentry.set_user(session:, user:) end |
Instance Attribute Details
#authn_context ⇒ Object (readonly)
Returns the value of attribute authn_context.
31 32 33 |
# File 'lib/saml/url_service.rb', line 31 def authn_context @authn_context end |
#query_params ⇒ Object (readonly)
Returns the value of attribute query_params.
31 32 33 |
# File 'lib/saml/url_service.rb', line 31 def query_params @query_params end |
#saml_settings ⇒ Object (readonly)
Returns the value of attribute saml_settings.
31 32 33 |
# File 'lib/saml/url_service.rb', line 31 def saml_settings @saml_settings end |
#session ⇒ Object (readonly)
Returns the value of attribute session.
31 32 33 |
# File 'lib/saml/url_service.rb', line 31 def session @session end |
#tracker ⇒ Object (readonly)
Returns the value of attribute tracker.
31 32 33 |
# File 'lib/saml/url_service.rb', line 31 def tracker @tracker end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
31 32 33 |
# File 'lib/saml/url_service.rb', line 31 def type @type end |
#user ⇒ Object (readonly)
Returns the value of attribute user.
31 32 33 |
# File 'lib/saml/url_service.rb', line 31 def user @user end |
Instance Method Details
#add_query(url, params) ⇒ Object (private)
216 217 218 219 220 221 222 223 224 |
# File 'lib/saml/url_service.rb', line 216 def add_query(url, params) if params.any? uri = URI.parse(url) uri.query = Rack::Utils.parse_nested_query(uri.query).merge(params).to_query uri.to_s else url end end |
#base_redirect_url ⇒ Object
REDIRECT_URLS
58 59 60 |
# File 'lib/saml/url_service.rb', line 58 def base_redirect_url VIRTUAL_HOST_MAPPINGS[current_host][:base_redirect] end |
#build_authn_context(assurance_level_url, identity_provider) ⇒ Object (private)
191 192 193 194 |
# File 'lib/saml/url_service.rb', line 191 def build_authn_context(assurance_level_url, identity_provider) assurance_level_url = [assurance_level_url] unless assurance_level_url.is_a?(Array) assurance_level_url.push(identity_provider) end |
#build_sso_url(link_authn_context, authn_con_compare = AuthnContext::EXACT) ⇒ Object (private)
Builds the urls to trigger various SSO policies: mhv, dslogon, idme, mfa, or verify flows. link_authn_context is the new proposed authn_context
181 182 183 184 185 186 187 188 189 |
# File 'lib/saml/url_service.rb', line 181 def build_sso_url(link_authn_context, authn_con_compare = AuthnContext::EXACT) @query_params[:RelayState] = relay_state_params new_url_settings = url_settings new_url_settings.authn_context = link_authn_context new_url_settings.authn_context_comparison = authn_con_compare saml_auth_request = OneLogin::RubySaml::Authrequest.new save_saml_request_tracker(saml_auth_request.uuid, link_authn_context) saml_auth_request.create(new_url_settings, query_params) end |
#callback_verify_url ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/saml/url_service.rb', line 132 def callback_verify_url link_authn_context = case type when 'logingov' build_authn_context([IAL::LOGIN_GOV_IAL2, AAL::LOGIN_GOV_AAL2], AuthnContext::LOGIN_GOV) when 'mhv', 'mhv_verified' build_authn_context('myhealthevet_loa3', AuthnContext::MHV) when 'dslogon' build_authn_context('dslogon_loa3', AuthnContext::DSLOGON) end build_sso_url(link_authn_context) end |
#current_host ⇒ Object (private)
205 206 207 208 |
# File 'lib/saml/url_service.rb', line 205 def current_host uri = URI.parse(saml_settings.assertion_consumer_service_url) URI.join(uri, '/').to_s.chop end |
#custom_url(authn) ⇒ Object
104 105 106 107 |
# File 'lib/saml/url_service.rb', line 104 def custom_url(authn) @type = 'custom' build_sso_url(authn) end |
#idme_signup_url(authn_context) ⇒ Object
91 92 93 94 95 |
# File 'lib/saml/url_service.rb', line 91 def idme_signup_url(authn_context) @type = 'signup' @query_params[:op] = 'signup' build_sso_url(build_authn_context(authn_context, AuthnContext::ID_ME)) end |
#initialize_tracker(params) ⇒ Object (private)
Initialize a new SAMLRequestTracker, if a valid previous SAML UUID is given, copy over the payload and created_at timestamp. This is useful for a user that has to go through the upleveling process.
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/saml/url_service.rb', line 236 def initialize_tracker(params) uuid = previous_saml_uuid(params) previous = uuid && SAMLRequestTracker.find(uuid) type = previous&.payload_attr(:type) || params[:type] transaction_id = previous&.payload_attr(:transaction_id) || SecureRandom.uuid redirect = previous&.payload_attr(:redirect) || params[:redirect] application = previous&.payload_attr(:application) || params[:application] || 'vaweb' post_login = previous&.payload_attr(:post_login) || params[:postLogin] # if created_at is set to nil (meaning no previous tracker to use), it # will be initialized to the current time when it is saved SAMLRequestTracker.new( payload: { type:, transaction_id:, redirect:, application:, post_login: }.compact, created_at: previous&.created_at ) end |
#login_redirect_url(auth: 'success', code: nil) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/saml/url_service.rb', line 62 def login_redirect_url(auth: 'success', code: nil) return verify_url if auth == 'success' && user.loa[:current] < user.loa[:highest] # if the original auth request was an inbound ssoe autologin (type custom) # and authentication failed, set 'force-needed' so the FE can silently fail # authentication and NOT show the user an error page auth = 'force-needed' if auth != 'success' && @tracker&.payload_attr(:type) == 'custom' @query_params[:type] = type if type @query_params[:auth] = auth if auth != 'success' @query_params[:code] = code if code if Settings.saml_ssoe.relay.present? add_query(Settings.saml_ssoe.relay, query_params) else add_query("#{base_redirect_url}#{LOGIN_REDIRECT_PARTIAL}", query_params) end end |
#login_url(type, authn_context, identity_provider, authn_con_compare = AuthnContext::EXACT) ⇒ Object
SIGN ON URLS
86 87 88 89 |
# File 'lib/saml/url_service.rb', line 86 def login_url(type, authn_context, identity_provider, authn_con_compare = AuthnContext::EXACT) @type = type build_sso_url(build_authn_context(authn_context, identity_provider), authn_con_compare) end |
#logingov_signup_url(authn_context) ⇒ Object
97 98 99 100 101 102 |
# File 'lib/saml/url_service.rb', line 97 def logingov_signup_url(authn_context) @type = 'signup' build_sso_url( build_authn_context(authn_context, AuthnContext::LOGIN_GOV) ) end |
#logout_redirect_url ⇒ Object
81 82 83 |
# File 'lib/saml/url_service.rb', line 81 def logout_redirect_url "#{base_redirect_url}#{LOGOUT_REDIRECT_PARTIAL}" end |
#mfa_url ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/saml/url_service.rb', line 146 def mfa_url @type = 'mfa' link_authn_context = case authn_context when LOA::IDME_LOA1_VETS, LOA::IDME_LOA3_VETS, LOA::IDME_LOA3 build_authn_context('multifactor', AuthnContext::ID_ME) when 'myhealthevet', 'myhealthevet_loa3' build_authn_context('myhealthevet_multifactor', AuthnContext::MHV) when 'dslogon', 'dslogon_loa3' build_authn_context('dslogon_multifactor', AuthnContext::DSLOGON) when SAML::UserAttributes::SSOe::INBOUND_AUTHN_CONTEXT "#{@user.identity.sign_in[:service_name]}_multifactor" end build_sso_url(link_authn_context) end |
#previous_saml_uuid(params) ⇒ Object (private)
226 227 228 229 230 231 |
# File 'lib/saml/url_service.rb', line 226 def previous_saml_uuid(params) if params[:action] == 'saml_callback' resp = SAML::Responses::Login.new(params[:SAMLResponse] || '', settings: @saml_settings) resp&.in_response_to end end |
#relay_state_params ⇒ Object (private)
196 197 198 199 200 201 202 203 |
# File 'lib/saml/url_service.rb', line 196 def relay_state_params rs_params = { originating_request_id: RequestStore.store['request_id'], type: } rs_params[:review_instance_slug] = Settings.review_instance_slug unless Settings.review_instance_slug.nil? rs_params.to_json end |
#save_saml_request_tracker(uuid, authn_context) ⇒ Object (private)
258 259 260 261 262 |
# File 'lib/saml/url_service.rb', line 258 def save_saml_request_tracker(uuid, authn_context) @tracker.uuid = uuid @tracker.payload[:authn_context] = authn_context @tracker.save end |
#slo_url ⇒ Object
SIGN OFF URLS
163 164 165 166 167 168 169 170 |
# File 'lib/saml/url_service.rb', line 163 def slo_url @type = 'slo' logout_request = OneLogin::RubySaml::Logoutrequest.new # cache the request for session.token lookup when we receive the response SingleLogoutRequest.create(uuid: logout_request.uuid, token: session.token) Rails.logger.info "New SP SLO having logout_request '#{logout_request.uuid}' for userid '#{session.uuid}'" logout_request.create(url_settings, RelayState: relay_state_params) end |
#ssoe_slo_url ⇒ Object
logout URL for SSOe
173 174 175 |
# File 'lib/saml/url_service.rb', line 173 def ssoe_slo_url Settings.saml_ssoe.logout_url end |
#url_settings ⇒ Object (private)
210 211 212 213 214 |
# File 'lib/saml/url_service.rb', line 210 def url_settings url_settings = saml_settings.dup url_settings.name_identifier_value = session&.uuid url_settings end |
#verify_url ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/saml/url_service.rb', line 109 def verify_url # For verification from a login callback, type should be the initial login policy. # In that case, it will have been set to the type from RelayState. @type ||= 'verify' return callback_verify_url if %w[logingov mhv dslogon].include?(type) link_authn_context = case authn_context when LOA::IDME_LOA1_VETS, 'multifactor' build_authn_context(@loa3_context, AuthnContext::ID_ME) when IAL::LOGIN_GOV_IAL1 build_authn_context([IAL::LOGIN_GOV_IAL2, AAL::LOGIN_GOV_AAL2], AuthnContext::LOGIN_GOV) when 'myhealthevet', 'myhealthevet_multifactor' build_authn_context('myhealthevet_loa3', AuthnContext::MHV) when 'dslogon', 'dslogon_multifactor' build_authn_context('dslogon_loa3', AuthnContext::DSLOGON) when SAML::UserAttributes::SSOe::INBOUND_AUTHN_CONTEXT "#{@user.identity.sign_in[:service_name]}_loa3" end build_sso_url(link_authn_context, AuthnContext::EXACT) end |