add method for plugins to easily inject into the delegated auth workflow

refs #5948

adjust specs so that if a login does inject themselves in by adding
additonal redirects, we keep following them

Change-Id: I16e616066ea1bef1aa5ed97718cbd8ddbd2c27c5
Reviewed-on: https://gerrit.instructure.com/6536
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
This commit is contained in:
Cody Cutrer 2011-10-27 12:51:15 -06:00
parent b9981e4b9c
commit 794d59eef9
5 changed files with 145 additions and 97 deletions

View File

@ -72,8 +72,9 @@ class PseudonymSessionsController < ApplicationController
return
else
logger.warn "Received CAS login for unknown user: #{st.response.user}"
reset_session
session[:delegated_message] = t 'errors.no_matching_user', "Canvas doesn't have an account for user: %{user}", :user => st.response.user
redirect_to :action => :destroy
redirect_to(cas_client.logout_url(login_url :no_auto => true))
return
end
else
@ -88,6 +89,7 @@ class PseudonymSessionsController < ApplicationController
elsif @is_saml && !params[:no_auto]
initiate_saml_login(request.env['canvas.account_domain'])
end
flash[:delegated_message] = session.delete :delegated_message
end
def create

View File

@ -241,11 +241,11 @@ module AuthenticationMethods
reset_session_for_login
if @domain_root_account.account_authorization_config.log_in_url.present?
session[:exit_frame] = true
redirect_to(@domain_root_account.account_authorization_config.log_in_url)
delegated_auth_redirect(@domain_root_account.account_authorization_config.log_in_url)
else
config = { :cas_base_url => @domain_root_account.account_authorization_config.auth_base }
cas_client ||= CASClient::Client.new(config)
redirect_to(cas_client.add_service_to_login_url(login_url))
delegated_auth_redirect(cas_client.add_service_to_login_url(login_url))
end
end
@ -253,6 +253,10 @@ module AuthenticationMethods
reset_session_for_login
settings = @domain_root_account.account_authorization_config.saml_settings(preferred_account_domain)
request = Onelogin::Saml::AuthRequest.create(settings)
redirect_to(request)
delegated_auth_redirect(request)
end
def delegated_auth_redirect(uri)
redirect_to(uri)
end
end

View File

@ -127,90 +127,5 @@ describe PseudonymSessionsController do
session[:cas_login].should == true
Pseudonym.find(session[:pseudonym_credentials_id]).should == user2.pseudonyms.first
end
it "should log in and log out a user CAS has validated" do
account = account_with_cas({:account => Account.default})
user = user_with_pseudonym({:active_all => true})
get 'new'
response.should redirect_to(controller.cas_client.add_service_to_login_url(login_url))
stubby("yes\n#{user.pseudonyms.first.unique_id}\n", false)
get 'new', :ticket => 'ST-abcd'
response.should redirect_to(dashboard_url(:login_success => 1))
session[:cas_login].should == true
get 'destroy'
response.should redirect_to(controller.cas_client.logout_url(login_url))
end
it "should inform the user CAS validation denied" do
account = account_with_cas({:account => Account.default})
get 'new'
response.should redirect_to(controller.cas_client.add_service_to_login_url(login_url))
stubby("no\n\n", false)
get 'new', :ticket => 'ST-abcd'
response.should redirect_to(:action => 'new', :no_auto => true)
flash[:delegated_message].should match(/There was a problem logging in/)
end
it "should inform the user CAS validation failed" do
account = account_with_cas({:account => Account.default})
get 'new'
response.should redirect_to(controller.cas_client.add_service_to_login_url(login_url))
cas_client = controller.cas_client
def cas_client.validate_service_ticket(st)
raise "Nope"
end
get 'new', :ticket => 'ST-abcd'
response.should redirect_to(:action => 'new', :no_auto => true)
flash[:delegated_message].should match(/There was a problem logging in/)
end
it "should inform the user that CAS account doesn't exist" do
account = account_with_cas({:account => Account.default})
get 'new'
response.should redirect_to(controller.cas_client.add_service_to_login_url(login_url))
stubby("yes\nnonexistentuser\n")
get 'new', :ticket => 'ST-abcd'
response.should redirect_to(:action => 'destroy')
get 'destroy'
response.should redirect_to(:action => 'new', :no_auto => true)
flash[:delegated_message].should match(/Canvas doesn't have an account for user/)
end
it "should redirect to alternate CAS login page if so configured, and frame bust on login" do
account = account_with_cas({:account => Account.default, :cas_log_in_url => 'http://example.com/cas'})
get 'new'
response.should redirect_to('http://example.com/cas')
get 'new', :ticket => 'ST-abcd'
response.should render_template('shared/exit_frame')
end
it "should login case insensitively" do
account = account_with_cas({:account => Account.default})
user = user_with_pseudonym({:active_all => true})
get 'new'
response.should redirect_to(controller.cas_client.add_service_to_login_url(login_url))
stubby("yes\n#{user.pseudonyms.first.unique_id.capitalize}\n")
get 'new', :ticket => 'ST-abcd'
response.should redirect_to(dashboard_url(:login_success => 1))
session[:cas_login].should == true
end
end
end

View File

@ -0,0 +1,127 @@
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe PseudonymSessionsController do
def redirect_until(uri)
count = 0
while true
response.should be_redirect
return if response.location == uri
count += 1
count.should < 5
follow_redirect!
end
end
context "cas" do
def stubby(stub_response)
@cas_client = CASClient::Client.new({:cas_base_url => @account.account_authorization_config.auth_base})
@cas_client.instance_variable_set(:@stub_response, stub_response)
def @cas_client.validate_service_ticket(st)
st.response = CASClient::ValidationResponse.new(@stub_response)
end
PseudonymSessionsController.any_instance.stubs(:cas_client).returns(@cas_client)
end
it "should log in and log out a user CAS has validated" do
account_with_cas({:account => Account.default})
user = user_with_pseudonym({:active_all => true})
stubby("yes\n#{user.pseudonyms.first.unique_id}\n")
get login_url
redirect_until(@cas_client.add_service_to_login_url(login_url))
get login_url :ticket => 'ST-abcd'
response.should redirect_to(dashboard_url(:login_success => 1))
session[:cas_login].should == true
get logout_url
response.should redirect_to(@cas_client.logout_url(login_url))
end
it "should inform the user CAS validation denied" do
account_with_cas({:account => Account.default})
stubby("no\n\n")
get login_url
redirect_until(@cas_client.add_service_to_login_url(login_url))
get login_url :ticket => 'ST-abcd'
response.should redirect_to(login_url :no_auto => true)
flash[:delegated_message].should match(/There was a problem logging in/)
end
it "should inform the user CAS validation failed" do
account_with_cas({:account => Account.default})
stubby('')
def @cas_client.validate_service_ticket(st)
raise "Nope"
end
get login_url
redirect_until(@cas_client.add_service_to_login_url(login_url))
get login_url :ticket => 'ST-abcd'
response.should redirect_to(login_url :no_auto => true)
flash[:delegated_message].should match(/There was a problem logging in/)
end
it "should inform the user that CAS account doesn't exist" do
account_with_cas({:account => Account.default})
stubby("yes\nnonexistentuser\n")
get login_url
redirect_until(@cas_client.add_service_to_login_url(login_url))
get login_url :ticket => 'ST-abcd'
response.should redirect_to(@cas_client.logout_url(login_url :no_auto => true))
get login_url :no_auto => true
flash[:delegated_message].should match(/Canvas doesn't have an account for user/)
end
it "should redirect to alternate CAS login page if so configured, and frame bust on login" do
account_with_cas({:account => Account.default, :cas_log_in_url => 'http://example.com/cas'})
get login_url
redirect_until('http://example.com/cas')
get login_url :ticket => 'ST-abcd'
response.should render_template('shared/exit_frame')
end
it "should login case insensitively" do
account_with_cas({:account => Account.default})
user = user_with_pseudonym({:active_all => true})
stubby("yes\n#{user.pseudonyms.first.unique_id.capitalize}\n")
get login_url
redirect_until(@cas_client.add_service_to_login_url(login_url))
get login_url :ticket => 'ST-abcd'
response.should redirect_to(dashboard_url(:login_success => 1))
session[:cas_login].should == true
end
end
end

View File

@ -83,25 +83,25 @@ Spec::Runner.configure do |config|
end
def account_with_cas(opts={})
account = opts[:account]
account ||= Account.create!
@account = opts[:account]
@account ||= Account.create!
config = AccountAuthorizationConfig.new
cas_url = opts[:cas_url] || "https://localhost/cas"
config.auth_type = "cas"
config.auth_base = cas_url
config.log_in_url = opts[:cas_log_in_url] if opts[:cas_log_in_url]
account.account_authorization_configs << config
account
@account.account_authorization_configs << config
@account
end
def account_with_saml(opts={})
account = opts[:account]
account ||= Account.create!
@account = opts[:account]
@account ||= Account.create!
config = AccountAuthorizationConfig.new
config.auth_type = "saml"
config.log_in_url = opts[:saml_log_in_url] if opts[:saml_log_in_url]
account.account_authorization_configs << config
account
@account.account_authorization_configs << config
@account
end
def course(opts={})