add redirect config to account authorization
fixes: CNVS-9194 This adds a configuration to CAS and SAML which will redirect to when an unknown user is authorized from CAS or SAML but we do not have a pseudonym for them in canvas yet. Instead of logging them out of CAS or SAML it will redirect them to a custom url. By default if no url is configured it will redirect to the login page or cas login page. Test Plan: CAS: - Setup a User on the CAS server that can login. - Make sure the user is not in Canvas and wont be matched up to any other Canvas pseudonyms. - Log in to CAS and Canvas. - Canvas should redirect to cas_login_url. - Configure the Canvas CAS setting unknown user url to redirect to a custom url. - Log in to CAS and Canvas - Canvas should redirect to the custom url specified. - The user should not be logged out of CAS. SAML: - Setup a User on the SAML server that can login. - Make sure the user is not in Canvas and wont be matched up to any other Canvas pseudonyms. - Log in to SAML and Canvas. - Canvas should redirect to login_url and provide a flash message stating that the user could not be found. - Configure the Canvas SAML settings Unknown User Url to redirect to a custom url. - Log in to SAML and Canvas - Canvas should redirect to the custom url specified. - The user should not be logged out of SAML. Change-Id: I29a78f8ec60c94caecf63547584d8ae804ffc9de Reviewed-on: https://gerrit.instructure.com/38472 Reviewed-by: Cody Cutrer <cody@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Matt Fairbourn <mfairbourn@instructure.com>
This commit is contained in:
parent
bd906e75bc
commit
fbc53265e4
|
@ -106,6 +106,11 @@
|
|||
# "description": "Valid for SAML authorization.",
|
||||
# "example": "nameid",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "unknown_user_url": {
|
||||
# "description": "Valid for SAML and CAS authorization.",
|
||||
# "example": "https://canvas.instructure.com/login",
|
||||
# "type": "string"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
@ -178,6 +183,11 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
# An alternate SSO URL for logging into CAS. You probably should not set
|
||||
# this.
|
||||
#
|
||||
# - unkown_user_url [Optional]
|
||||
#
|
||||
# A url to redirect to when a user is authorized through CAS but is not
|
||||
# found in Canvas.
|
||||
#
|
||||
# For SAML authentication services, the additional recognized parameters are:
|
||||
#
|
||||
# - idp_entity_id
|
||||
|
@ -201,6 +211,11 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
#
|
||||
# Forgot Password URL. Leave blank for default Canvas behavior.
|
||||
#
|
||||
# - unkown_user_url [Optional]
|
||||
#
|
||||
# A url to redirect to when a user is authorized through SAML but is not
|
||||
# found in Canvas.
|
||||
#
|
||||
# - identifier_format
|
||||
#
|
||||
# The SAML service's identifier format. Must be one of:
|
||||
|
@ -273,30 +288,30 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
# @example_request
|
||||
# # Create LDAP config
|
||||
# curl 'https://<canvas>/api/v1/accounts/<account_id>/account_authorization_configs' \
|
||||
# -F 'auth_type=ldap' \
|
||||
# -F 'auth_host=ldap.mydomain.edu' \
|
||||
# -F 'auth_filter=(sAMAccountName={{login}})' \
|
||||
# -F 'auth_username=username' \
|
||||
# -F 'auth_password=bestpasswordever' \
|
||||
# -F 'position=1' \
|
||||
# -F 'auth_type=ldap' \
|
||||
# -F 'auth_host=ldap.mydomain.edu' \
|
||||
# -F 'auth_filter=(sAMAccountName={{login}})' \
|
||||
# -F 'auth_username=username' \
|
||||
# -F 'auth_password=bestpasswordever' \
|
||||
# -F 'position=1' \
|
||||
# -H 'Authorization: Bearer <token>'
|
||||
#
|
||||
# @example_request
|
||||
# # Create SAML config
|
||||
# curl 'https://<canvas>/api/v1/accounts/<account_id>/account_authorization_configs' \
|
||||
# -F 'auth_type=saml' \
|
||||
# -F 'idp_entity_id=<idp_entity_id>' \
|
||||
# -F 'log_in_url=<login_url>' \
|
||||
# -F 'log_out_url=<logout_url>' \
|
||||
# -F 'certificate_fingerprint=<fingerprint>' \
|
||||
# -F 'auth_type=saml' \
|
||||
# -F 'idp_entity_id=<idp_entity_id>' \
|
||||
# -F 'log_in_url=<login_url>' \
|
||||
# -F 'log_out_url=<logout_url>' \
|
||||
# -F 'certificate_fingerprint=<fingerprint>' \
|
||||
# -H 'Authorization: Bearer <token>'
|
||||
#
|
||||
# @example_request
|
||||
# # Create CAS config
|
||||
# curl 'https://<canvas>/api/v1/accounts/<account_id>/account_authorization_configs' \
|
||||
# -F 'auth_type=cas' \
|
||||
# -F 'auth_base=cas.mydomain.edu' \
|
||||
# -F 'log_in_url=<login_url>' \
|
||||
# -F 'auth_type=cas' \
|
||||
# -F 'auth_base=cas.mydomain.edu' \
|
||||
# -F 'log_in_url=<login_url>' \
|
||||
# -H 'Authorization: Bearer <token>'
|
||||
#
|
||||
# _Deprecated_ Examples:
|
||||
|
@ -386,8 +401,8 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
# @example_request
|
||||
# # update SAML config
|
||||
# curl -XPUT 'https://<canvas>/api/v1/accounts/<account_id>/account_authorization_configs/<id>' \
|
||||
# -F 'idp_entity_id=<new_idp_entity_id>' \
|
||||
# -F 'log_in_url=<new_url>' \
|
||||
# -F 'idp_entity_id=<new_idp_entity_id>' \
|
||||
# -F 'log_in_url=<new_url>' \
|
||||
# -H 'Authorization: Bearer <token>'
|
||||
#
|
||||
# @returns AccountAuthorizationConfig
|
||||
|
@ -494,14 +509,14 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
# If you have multiple IdPs configured, you can set a `discovery_url`.
|
||||
# If that is set, canvas will forward all users to that URL when they need to
|
||||
# be authenticated. That page will need to then help the user figure out where
|
||||
# they need to go to log in.
|
||||
# they need to go to log in.
|
||||
#
|
||||
# If no discovery url is configured, the 1st auth config will be used to
|
||||
# If no discovery url is configured, the 1st auth config will be used to
|
||||
# attempt to authenticate the user.
|
||||
#
|
||||
# @example_request
|
||||
# curl -XPUT 'https://<canvas>/api/v1/accounts/<account_id>/account_authorization_configs/discovery_url' \
|
||||
# -F 'discovery_url=<new_url>' \
|
||||
# -F 'discovery_url=<new_url>' \
|
||||
# -H 'Authorization: Bearer <token>'
|
||||
#
|
||||
# @returns DiscoveryUrl
|
||||
|
@ -521,7 +536,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
|
||||
# @API Delete discovery url
|
||||
# Clear discovery url
|
||||
#
|
||||
#
|
||||
# @example_request
|
||||
# curl -XDELETE 'https://<canvas>/api/v1/accounts/<account_id>/account_authorization_configs/discovery_url' \
|
||||
# -H 'Authorization: Bearer <token>'
|
||||
|
@ -608,7 +623,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
end
|
||||
redirect_to :account_account_authorization_configs
|
||||
end
|
||||
|
||||
|
||||
def saml_testing
|
||||
if @account.saml_authentication?
|
||||
@account_config = @account.account_authorization_config
|
||||
|
@ -625,13 +640,13 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def saml_testing_stop
|
||||
if @account_config = @account.account_authorization_config
|
||||
@account_config.finish_debugging
|
||||
end
|
||||
|
||||
render :json => {:status => "ok"}
|
||||
if @account_config = @account.account_authorization_config
|
||||
@account_config.finish_debugging
|
||||
end
|
||||
|
||||
render :json => {:status => "ok"}
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -78,10 +78,11 @@ class PseudonymSessionsController < ApplicationController
|
|||
successful_login(@user, @pseudonym)
|
||||
return
|
||||
else
|
||||
logger.warn "Received CAS login for unknown user: #{st.user}"
|
||||
unknown_user_url = @domain_root_account.account_authorization_config.unknown_user_url || cas_login_url(:no_auto=>'true')
|
||||
logger.warn "Received CAS login for unknown user: #{st.user}, redirecting to: #{unknown_user_url}."
|
||||
reset_session
|
||||
session[:delegated_message] = t 'errors.no_matching_user', "Canvas doesn't have an account for user: %{user}", :user => st.user
|
||||
redirect_to(cas_client.logout_url(cas_login_url :no_auto => true))
|
||||
flash[:delegated_message] = t 'errors.no_matching_user', "Canvas doesn't have an account for user: %{user}", :user => st.user
|
||||
redirect_to unknown_user_url
|
||||
return
|
||||
end
|
||||
else
|
||||
|
@ -379,13 +380,13 @@ class PseudonymSessionsController < ApplicationController
|
|||
|
||||
successful_login(@user, @pseudonym)
|
||||
else
|
||||
unknown_user_url = aac.unknown_user_url || login_url(:no_auto => 'true')
|
||||
increment_saml_stat("errors.unknown_user")
|
||||
message = "Received SAML login request for unknown user: #{unique_id}"
|
||||
message = "Received SAML login request for unknown user: #{unique_id} redirecting to: #{unknown_user_url}."
|
||||
logger.warn message
|
||||
aac.debug_set(:canvas_login_fail_message, message) if debugging
|
||||
# the saml message has to survive a couple redirects
|
||||
session[:delegated_message] = t 'errors.no_matching_user', "Canvas doesn't have an account for user: %{user}", :user => unique_id
|
||||
logout_user_action
|
||||
flash[:delegated_message] = t 'errors.no_matching_user', "Canvas doesn't have an account for user: %{user}", :user => unique_id
|
||||
redirect_to unknown_user_url
|
||||
end
|
||||
elsif response.auth_failure?
|
||||
increment_saml_stat("normal.login_failure")
|
||||
|
|
|
@ -29,11 +29,11 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
acts_as_list :scope => :account
|
||||
|
||||
attr_accessible :account, :auth_port, :auth_host, :auth_base, :auth_username,
|
||||
:auth_password, :auth_password_salt, :auth_type, :auth_over_tls,
|
||||
:log_in_url, :log_out_url, :identifier_format,
|
||||
:certificate_fingerprint, :entity_id, :change_password_url,
|
||||
:login_handle_name, :ldap_filter, :auth_filter, :requested_authn_context,
|
||||
:login_attribute, :idp_entity_id
|
||||
:auth_password, :auth_password_salt, :auth_type, :auth_over_tls,
|
||||
:log_in_url, :log_out_url, :identifier_format,
|
||||
:certificate_fingerprint, :entity_id, :change_password_url,
|
||||
:login_handle_name, :ldap_filter, :auth_filter, :requested_authn_context,
|
||||
:login_attribute, :idp_entity_id, :unknown_user_url
|
||||
|
||||
before_validation :set_saml_defaults, :if => Proc.new { |aac| aac.saml_authentication? }
|
||||
|
||||
|
@ -51,7 +51,7 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
def self.recognized_params(auth_type)
|
||||
case auth_type
|
||||
when 'cas'
|
||||
[ :auth_type, :auth_base, :log_in_url, :login_handle_name ]
|
||||
[ :auth_type, :auth_base, :log_in_url, :login_handle_name, :unknown_user_url ]
|
||||
when 'ldap'
|
||||
[ :auth_type, :auth_host, :auth_port, :auth_over_tls, :auth_base,
|
||||
:auth_filter, :auth_username, :auth_password, :change_password_url,
|
||||
|
@ -59,7 +59,7 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
when 'saml'
|
||||
[ :auth_type, :log_in_url, :log_out_url, :change_password_url, :requested_authn_context,
|
||||
:certificate_fingerprint, :identifier_format, :login_handle_name,
|
||||
:login_attribute, :idp_entity_id, :position]
|
||||
:login_attribute, :idp_entity_id, :position, :unknown_user_url ]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
@ -67,14 +67,14 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
|
||||
def self.auth_over_tls_setting(value)
|
||||
case value
|
||||
when nil, '', false, 'false', 'f', 0, '0'
|
||||
nil
|
||||
when true, 'true', 't', 1, '1', 'simple_tls', :simple_tls
|
||||
'simple_tls'
|
||||
when 'start_tls', :start_tls
|
||||
'start_tls'
|
||||
else
|
||||
raise ArgumentError("invalid auth_over_tls setting: #{value}")
|
||||
when nil, '', false, 'false', 'f', 0, '0'
|
||||
nil
|
||||
when true, 'true', 't', 1, '1', 'simple_tls', :simple_tls
|
||||
'simple_tls'
|
||||
when 'start_tls', :start_tls
|
||||
'start_tls'
|
||||
else
|
||||
raise ArgumentError("invalid auth_over_tls setting: #{value}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -92,12 +92,12 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
ldap.auth self.auth_username, self.auth_decrypted_password
|
||||
ldap
|
||||
end
|
||||
|
||||
|
||||
def set_saml_defaults
|
||||
self.entity_id ||= saml_default_entity_id
|
||||
self.requested_authn_context = nil if self.requested_authn_context.blank?
|
||||
end
|
||||
|
||||
|
||||
def self.saml_login_attributes
|
||||
{
|
||||
'NameID' => 'nameid',
|
||||
|
@ -105,24 +105,24 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
t(:saml_eppn_domain_stripped, "%{eppn} (domain stripped)", :eppn => "eduPersonPrincipalName") =>'eduPersonPrincipalName_stripped'
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def sanitized_ldap_login(login)
|
||||
[ [ '\\', '\5c' ], [ '*', '\2a' ], [ '(', '\28' ], [ ')', '\29' ], [ "\00", '\00' ] ].each do |re|
|
||||
login.gsub!(re[0], re[1])
|
||||
end
|
||||
login
|
||||
end
|
||||
|
||||
|
||||
def ldap_filter(login = nil)
|
||||
filter = self.auth_filter
|
||||
filter = filter.gsub(/\{\{login\}\}/, sanitized_ldap_login(login)) if login
|
||||
filter
|
||||
end
|
||||
|
||||
|
||||
def change_password_url
|
||||
read_attribute(:change_password_url).blank? ? nil : read_attribute(:change_password_url)
|
||||
end
|
||||
|
||||
|
||||
def ldap_filter=(new_filter)
|
||||
self.auth_filter = new_filter
|
||||
end
|
||||
|
@ -134,21 +134,21 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def auth_password=(password)
|
||||
return if password.nil? or password == ''
|
||||
self.auth_crypted_password, self.auth_password_salt = Canvas::Security.encrypt_password(password, 'instructure_auth')
|
||||
end
|
||||
|
||||
|
||||
def auth_decrypted_password
|
||||
return nil unless self.auth_password_salt && self.auth_crypted_password
|
||||
Canvas::Security.decrypt_password(self.auth_crypted_password, self.auth_password_salt, 'instructure_auth')
|
||||
end
|
||||
|
||||
|
||||
def self.saml_default_entity_id_for_account(account)
|
||||
"http://#{HostUrl.context_host(account)}/saml2"
|
||||
end
|
||||
|
||||
|
||||
def saml_default_entity_id
|
||||
AccountAuthorizationConfig.saml_default_entity_id_for_account(self.account)
|
||||
end
|
||||
|
@ -170,26 +170,26 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
@saml_settings.name_identifier_format = self.identifier_format
|
||||
@saml_settings.requested_authn_context = self.requested_authn_context
|
||||
end
|
||||
|
||||
|
||||
@saml_settings
|
||||
end
|
||||
|
||||
|
||||
def self.saml_settings_for_account(account, current_host=nil)
|
||||
app_config = ConfigFile.load('saml') || {}
|
||||
domains = HostUrl.context_hosts(account, current_host)
|
||||
|
||||
|
||||
settings = Onelogin::Saml::Settings.new
|
||||
settings.sp_slo_url = "#{HostUrl.protocol}://#{domains.first}/saml_logout"
|
||||
settings.assertion_consumer_service_url = domains.map { |domain| "#{HostUrl.protocol}://#{domain}/saml_consume" }
|
||||
settings.tech_contact_name = app_config[:tech_contact_name] || 'Webmaster'
|
||||
settings.tech_contact_email = app_config[:tech_contact_email] || ''
|
||||
|
||||
|
||||
if account.saml_authentication?
|
||||
settings.issuer = account.account_authorization_config.entity_id
|
||||
else
|
||||
settings.issuer = saml_default_entity_id_for_account(account)
|
||||
end
|
||||
|
||||
|
||||
encryption = app_config[:encryption]
|
||||
if encryption.is_a?(Hash)
|
||||
settings.xmlsec_certificate = resolve_saml_key_path(encryption[:certificate])
|
||||
|
@ -197,7 +197,7 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
|
||||
settings.xmlsec_additional_privatekeys = Array(encryption[:additional_private_keys]).map { |apk| resolve_saml_key_path(apk) }.compact
|
||||
end
|
||||
|
||||
|
||||
settings
|
||||
end
|
||||
|
||||
|
@ -212,15 +212,15 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
|
||||
path.exist? ? path.to_s : nil
|
||||
end
|
||||
|
||||
|
||||
def email_identifier?
|
||||
if self.saml_authentication?
|
||||
return self.identifier_format == Onelogin::Saml::NameIdentifiers::EMAIL
|
||||
end
|
||||
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
def password_authentication?
|
||||
!['cas', 'ldap', 'saml'].member?(self.auth_type)
|
||||
end
|
||||
|
@ -232,23 +232,23 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
def cas_authentication?
|
||||
self.auth_type == 'cas'
|
||||
end
|
||||
|
||||
|
||||
def ldap_authentication?
|
||||
self.auth_type == 'ldap'
|
||||
end
|
||||
|
||||
|
||||
def saml_authentication?
|
||||
self.auth_type == 'saml'
|
||||
end
|
||||
|
||||
|
||||
def self.default_login_handle_name
|
||||
t(:default_login_handle_name, "Email")
|
||||
end
|
||||
|
||||
|
||||
def self.default_delegated_login_handle_name
|
||||
t(:default_delegated_login_handle_name, "Login")
|
||||
end
|
||||
|
||||
|
||||
def self.serialization_excludes; [:auth_crypted_password, :auth_password_salt]; end
|
||||
|
||||
def test_ldap_connection
|
||||
|
@ -336,38 +336,38 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
def debugging?
|
||||
!!Rails.cache.fetch(debug_key(:debugging))
|
||||
end
|
||||
|
||||
|
||||
def debugging_keys
|
||||
[:debugging, :request_id, :to_idp_url, :to_idp_xml, :idp_response_encoded,
|
||||
[:debugging, :request_id, :to_idp_url, :to_idp_xml, :idp_response_encoded,
|
||||
:idp_in_response_to, :fingerprint_from_idp, :idp_response_xml_encrypted,
|
||||
:idp_response_xml_decrypted, :idp_login_destination, :is_valid_login_response,
|
||||
:login_response_validation_error, :login_to_canvas_success, :canvas_login_fail_message,
|
||||
:logged_in_user_id, :logout_request_id, :logout_to_idp_url, :logout_to_idp_xml,
|
||||
:idp_logout_response_encoded, :idp_logout_in_response_to,
|
||||
:login_response_validation_error, :login_to_canvas_success, :canvas_login_fail_message,
|
||||
:logged_in_user_id, :logout_request_id, :logout_to_idp_url, :logout_to_idp_xml,
|
||||
:idp_logout_response_encoded, :idp_logout_in_response_to,
|
||||
:idp_logout_response_xml_encrypted, :idp_logout_destination]
|
||||
end
|
||||
|
||||
|
||||
def finish_debugging
|
||||
debugging_keys.each { |key| Rails.cache.delete(debug_key(key)) }
|
||||
end
|
||||
|
||||
|
||||
def start_debugging
|
||||
finish_debugging # clear old data
|
||||
debug_set(:debugging, t('debug.wait_for_login', "Waiting for attempted login"))
|
||||
end
|
||||
|
||||
|
||||
def debug_get(key)
|
||||
Rails.cache.fetch(debug_key(key))
|
||||
end
|
||||
|
||||
|
||||
def debug_set(key, value)
|
||||
Rails.cache.write(debug_key(key), value, :expires_in => debug_expire)
|
||||
end
|
||||
|
||||
|
||||
def debug_key(key)
|
||||
['aac_debugging', self.id, key.to_s].cache_key
|
||||
end
|
||||
|
||||
|
||||
def debug_expire
|
||||
Setting.get('aac_debug_expire_minutes', 30).minutes
|
||||
end
|
||||
|
@ -384,10 +384,10 @@ class AccountAuthorizationConfig < ActiveRecord::Base
|
|||
Canvas.timeout_protection("ldap:#{self.global_id}",
|
||||
raise_on_timeout: true,
|
||||
fallback_timeout_length: default_timeout) do
|
||||
ldap = self.ldap_connection
|
||||
filter = self.ldap_filter(unique_id)
|
||||
ldap.bind_as(:base => ldap.base, :filter => filter, :password => password_plaintext)
|
||||
end
|
||||
ldap = self.ldap_connection
|
||||
filter = self.ldap_filter(unique_id)
|
||||
ldap.bind_as(:base => ldap.base, :filter => filter, :password => password_plaintext)
|
||||
end
|
||||
rescue => e
|
||||
ErrorReport.log_exception(:ldap, e, :account => self.account)
|
||||
if e.is_a?(Timeout::Error)
|
||||
|
|
|
@ -34,6 +34,16 @@
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="vertical-align: top; width: 200px;"><%= f.blabel :unknown_user_url, :en => "Unknown User Url" %></td>
|
||||
<td style="vertical-align: top;" class="nobr">
|
||||
<%= f.text_field :unknown_user_url, :class => "auth_form", :style => "width: 450px;", :placeholder => cas_login_url(:no_auto=>'true') %>
|
||||
<span class="auth_info auth_unknown_user_url"><%= @account_config.unknown_user_url %></span>
|
||||
<span class="auth_form" style="font-size: smaller;">
|
||||
<br><%= t(:unknown_user_url_description, "The url to redirect to when an authenticated user is not found in Canvas.") %>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<div class="form-actions">
|
||||
|
|
|
@ -203,6 +203,14 @@
|
|||
<p class="help-block"><%= t(:login_handle_name_description, "The label used for unique login identifiers. Examples: Login, Username, Student ID, etc.") %></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="input01"><%= f.blabel :unkown_user_url, :en => "Unknown User Url" %></label>
|
||||
<div class="controls">
|
||||
<%= f.text_field :unknown_user_url, :class => "input-xlarge", :placeholder => login_url(:no_auto=>'true') %>
|
||||
<p class="help-block"><%= t(:unkown_user_url_description, "The url to redirect to when an authenticated user is not found in Canvas.") %></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if @saml_configs.length > 1 %>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="input01"><%= f.blabel :position, :en => "Position" %></label>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
class AddUnknownUserUrlToAccountAuthorizationConfig < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def change
|
||||
add_column :account_authorization_configs, :unknown_user_url, :string
|
||||
end
|
||||
end
|
|
@ -203,6 +203,7 @@ describe "AccountAuthorizationConfigs API", type: :request do
|
|||
@saml_hash['change_password_url'] = nil
|
||||
@saml_hash['requested_authn_context'] = nil
|
||||
@saml_hash['login_attribute'] = 'nameid'
|
||||
@saml_hash['unknown_user_url'] = nil
|
||||
json.should == @saml_hash
|
||||
end
|
||||
|
||||
|
@ -230,6 +231,7 @@ describe "AccountAuthorizationConfigs API", type: :request do
|
|||
@cas_hash['log_in_url'] = nil
|
||||
@cas_hash['id'] = aac.id
|
||||
@cas_hash['position'] = 1
|
||||
@cas_hash['unknown_user_url'] = nil
|
||||
json.should == @cas_hash
|
||||
end
|
||||
|
||||
|
|
|
@ -250,6 +250,33 @@ describe PseudonymSessionsController do
|
|||
Pseudonym.find(session['pseudonym_credentials_id']).should == user2.pseudonyms.first
|
||||
end
|
||||
|
||||
it "should redirect when a user is authenticted but is not found in canvas" do
|
||||
ConfigFile.stub('saml', {})
|
||||
unique_id = 'foo@example.com'
|
||||
|
||||
account = account_with_saml
|
||||
|
||||
controller.stubs(:saml_response).returns(
|
||||
stub('response', :is_valid? => true, :success_status? => true, :name_id => unique_id, :name_qualifier => nil, :session_index => nil, :process => nil)
|
||||
)
|
||||
|
||||
# We dont want to log them out of everything.
|
||||
controller.expects(:logout_user_action).never
|
||||
controller.request.env['canvas.domain_root_account'] = account
|
||||
|
||||
# Default to Login url
|
||||
get 'saml_consume', :SAMLResponse => "foo"
|
||||
response.should redirect_to(login_url(:no_auto => 'true'))
|
||||
session[:saml_unique_id].should be_nil
|
||||
|
||||
# Redirect to a specifiec url
|
||||
unknown_user_url = "https://example.com/unknown_user"
|
||||
account.account_authorization_config.unknown_user_url = unknown_user_url
|
||||
get 'saml_consume', :SAMLResponse => "foo"
|
||||
response.should redirect_to(unknown_user_url)
|
||||
session[:saml_unique_id].should be_nil
|
||||
end
|
||||
|
||||
context "multiple authorization configs" do
|
||||
before :once do
|
||||
@account = Account.create!
|
||||
|
@ -760,6 +787,29 @@ describe PseudonymSessionsController do
|
|||
Pseudonym.find(session['pseudonym_credentials_id']).should == user2.pseudonyms.first
|
||||
end
|
||||
|
||||
it "should redirect when a user is authorized but not found in canvas" do
|
||||
unique_id = 'foo@example.com'
|
||||
|
||||
account = account_with_cas
|
||||
stubby("yes\n#{unique_id}\n")
|
||||
|
||||
# We dont want to log them out of everything.
|
||||
controller.expects(:logout_user_action).never
|
||||
controller.request.env['canvas.domain_root_account'] = account
|
||||
|
||||
# Default to Login url
|
||||
get 'new', :ticket => 'ST-abcd'
|
||||
response.should redirect_to(cas_login_url(:no_auto => 'true'))
|
||||
session[:cas_session].should be_nil
|
||||
|
||||
# Redirect to a specific url
|
||||
unknown_user_url = "https://example.com/unknown_user"
|
||||
account.account_authorization_config.unknown_user_url = unknown_user_url
|
||||
get 'new', :ticket => 'ST-abcd'
|
||||
response.should redirect_to(unknown_user_url)
|
||||
session[:cas_session].should be_nil
|
||||
end
|
||||
|
||||
it "should log out correctly if the user is from a different account" do
|
||||
account = account_with_cas
|
||||
user_with_pseudonym(active_all: true, account: account)
|
||||
|
|
|
@ -98,11 +98,26 @@ describe PseudonymSessionsController do
|
|||
redirect_until(@cas_client.add_service_to_login_url(cas_login_url))
|
||||
|
||||
get cas_login_url :ticket => 'ST-abcd'
|
||||
response.should redirect_to(@cas_client.logout_url(cas_login_url :no_auto => true))
|
||||
response.should redirect_to(cas_login_url(:no_auto => true))
|
||||
get cas_login_url :no_auto => true
|
||||
flash[:delegated_message].should match(/Canvas doesn't have an account for user/)
|
||||
end
|
||||
|
||||
it "should redirect to a custom url if the user CAS account doesn't exist" do
|
||||
redirect_url = login_url(:no_auto => 'true')
|
||||
aac = Account.default.account_authorization_config
|
||||
aac.unknown_user_url = redirect_url
|
||||
aac.save
|
||||
|
||||
stubby("yes\nnonexistentuser\n")
|
||||
|
||||
get login_url
|
||||
redirect_until(@cas_client.add_service_to_login_url(cas_login_url))
|
||||
|
||||
get cas_login_url :ticket => 'ST-abcd'
|
||||
response.should redirect_to(redirect_url)
|
||||
end
|
||||
|
||||
it "should login case insensitively" do
|
||||
user = user_with_pseudonym({:active_all => true})
|
||||
|
||||
|
|
|
@ -120,6 +120,38 @@ describe "account" do
|
|||
dialog.find_element(:css, 'label[for="pseudonym_unique_id"]').text.should == "CAS Username:*"
|
||||
end
|
||||
|
||||
context "cas" do
|
||||
it "should be able to set unknown user url option" do
|
||||
get "/accounts/#{Account.default.id}/account_authorization_configs"
|
||||
click_option('#add_auth_select', 'cas', :value)
|
||||
f("#account_authorization_config_0_login_handle_name").should be_displayed
|
||||
|
||||
unknown_user_url = 'https://example.com/unknown_user'
|
||||
f("#account_authorization_config_0_unknown_user_url").send_keys(unknown_user_url)
|
||||
expect_new_page_load { submit_form('#auth_form') }
|
||||
|
||||
Account.default.account_authorization_configs.first.unknown_user_url.should == unknown_user_url
|
||||
end
|
||||
end
|
||||
|
||||
context "saml" do
|
||||
it "should be able to set unknown user url option" do
|
||||
get "/accounts/#{Account.default.id}/account_authorization_configs"
|
||||
click_option('#add_auth_select', 'saml', :value)
|
||||
|
||||
saml_div = f('#saml_div')
|
||||
saml_div.find_element(:css, 'button.element_toggler.btn').click
|
||||
|
||||
f("#account_authorization_config_idp_entity_id").should be_displayed
|
||||
|
||||
unknown_user_url = 'https://example.com/unknown_user'
|
||||
f("#account_authorization_config_unknown_user_url").send_keys(unknown_user_url)
|
||||
expect_new_page_load { submit_form('#saml_config__form') }
|
||||
|
||||
Account.default.account_authorization_configs.first.unknown_user_url.should == unknown_user_url
|
||||
end
|
||||
end
|
||||
|
||||
it "should be able to create a new course" do
|
||||
get "/accounts/#{Account.default.id}"
|
||||
f('.add_course_link').click
|
||||
|
|
Loading…
Reference in New Issue