2011-02-01 09:57:29 +08:00
|
|
|
#
|
2017-04-28 03:58:43 +08:00
|
|
|
# Copyright (C) 2011 - present Instructure, Inc.
|
2011-02-01 09:57:29 +08:00
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
#
|
|
|
|
|
|
|
|
module AuthenticationMethods
|
2012-08-15 04:24:39 +08:00
|
|
|
|
2011-03-08 05:02:56 +08:00
|
|
|
def load_pseudonym_from_policy
|
2013-06-01 00:44:29 +08:00
|
|
|
if (policy_encoded = params['Policy']) &&
|
2011-03-08 05:02:56 +08:00
|
|
|
(signature = params['Signature']) &&
|
2014-06-24 00:38:51 +08:00
|
|
|
signature == Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), Attachment.shared_secret, policy_encoded)).gsub(/\n/, '') &&
|
2011-03-08 05:02:56 +08:00
|
|
|
(policy = JSON.parse(Base64.decode64(policy_encoded)) rescue nil) &&
|
|
|
|
policy['conditions'] &&
|
|
|
|
(credential = policy['conditions'].detect{ |cond| cond.is_a?(Hash) && cond.has_key?("pseudonym_id") })
|
|
|
|
@policy_pseudonym_id = credential['pseudonym_id']
|
|
|
|
# so that we don't have to explicitly skip verify_authenticity_token
|
|
|
|
params[self.class.request_forgery_protection_token] ||= form_authenticity_token
|
|
|
|
end
|
|
|
|
yield if block_given?
|
|
|
|
end
|
|
|
|
|
2011-12-23 04:26:05 +08:00
|
|
|
class AccessTokenError < Exception
|
|
|
|
end
|
|
|
|
|
2018-03-09 04:46:40 +08:00
|
|
|
class AccessTokenScopeError < StandardError
|
|
|
|
end
|
|
|
|
|
2014-08-06 01:23:33 +08:00
|
|
|
class LoggedOutError < Exception
|
|
|
|
end
|
|
|
|
|
2013-08-14 06:45:58 +08:00
|
|
|
def self.access_token(request, params_method = :params)
|
2014-07-24 01:14:22 +08:00
|
|
|
auth_header = request.authorization
|
2013-08-14 06:45:58 +08:00
|
|
|
if auth_header.present? && (header_parts = auth_header.split(' ', 2)) && header_parts[0] == 'Bearer'
|
2011-12-23 04:26:05 +08:00
|
|
|
header_parts[1]
|
2013-08-14 06:45:58 +08:00
|
|
|
else
|
|
|
|
request.send(params_method)['access_token'].presence
|
2011-12-23 04:26:05 +08:00
|
|
|
end
|
2013-08-14 06:45:58 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.user_id(request)
|
|
|
|
request.session[:user_id]
|
|
|
|
end
|
|
|
|
|
2015-10-30 04:03:20 +08:00
|
|
|
def load_pseudonym_from_jwt
|
|
|
|
return unless api_request?
|
|
|
|
token_string = AuthenticationMethods.access_token(request)
|
|
|
|
return unless token_string.present?
|
|
|
|
begin
|
|
|
|
services_jwt = Canvas::Security::ServicesJwt.new(token_string)
|
|
|
|
@current_user = User.find(services_jwt.user_global_id)
|
2017-04-07 05:00:25 +08:00
|
|
|
@current_pseudonym = SisPseudonym.for(@current_user, @domain_root_account, type: :implicit, require_sis: false)
|
2015-10-30 04:03:20 +08:00
|
|
|
unless @current_user && @current_pseudonym
|
|
|
|
raise AccessTokenError
|
|
|
|
end
|
2016-03-31 00:20:05 +08:00
|
|
|
if services_jwt.masquerading_user_global_id
|
|
|
|
@real_current_user = User.find(services_jwt.masquerading_user_global_id)
|
2017-04-07 05:00:25 +08:00
|
|
|
@real_current_pseudonym = SisPseudonym.for(@real_current_user, @domain_root_account, type: :implicit, require_sis: false)
|
2016-03-31 00:20:05 +08:00
|
|
|
logger.warn "#{@real_current_user.name}(#{@real_current_user.id}) impersonating #{@current_user.name} on page #{request.url}"
|
|
|
|
end
|
2015-10-30 04:03:20 +08:00
|
|
|
@authenticated_with_jwt = true
|
2016-02-05 08:45:25 +08:00
|
|
|
rescue JSON::JWT::InvalidFormat, # definitely not a JWT
|
|
|
|
Canvas::Security::TokenExpired, # it could be a JWT, but it's expired if so
|
2017-09-12 03:05:02 +08:00
|
|
|
Canvas::Security::InvalidToken # Looks like garbage
|
2016-03-03 00:53:31 +08:00
|
|
|
# these will happen for some configurations (no consul)
|
|
|
|
# and for some normal use cases (old token, access token),
|
|
|
|
# so we can return and move on
|
2015-10-30 04:03:20 +08:00
|
|
|
return
|
2017-01-12 00:13:28 +08:00
|
|
|
rescue Imperium::TimeoutError => exception # Something went wrong in the Network
|
2016-03-03 00:53:31 +08:00
|
|
|
# these are indications of infrastructure of data problems
|
|
|
|
# so we should log them for resolution, but recover gracefully
|
|
|
|
Canvas::Errors.capture_exception(:jwt_check, exception)
|
2015-10-30 04:03:20 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-09 04:46:40 +08:00
|
|
|
def validate_scopes
|
2018-06-09 02:59:00 +08:00
|
|
|
if @access_token && @domain_root_account.feature_enabled?(:developer_key_management_and_scoping)
|
2018-03-09 04:46:40 +08:00
|
|
|
developer_key = @access_token.developer_key
|
|
|
|
request_method = request.method.casecmp('HEAD') == 0 ? 'GET' : request.method.upcase
|
|
|
|
|
|
|
|
if developer_key.try(:require_scopes)
|
|
|
|
if @access_token.url_scopes_for_method(request_method).any? { |scope| scope =~ request.path }
|
|
|
|
params.delete :include
|
|
|
|
params.delete :includes
|
|
|
|
else
|
|
|
|
raise AccessTokenScopeError
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-08-14 06:45:58 +08:00
|
|
|
def load_pseudonym_from_access_token
|
2017-06-14 01:21:27 +08:00
|
|
|
return unless api_request? ||
|
|
|
|
(params[:controller] == 'oauth2_provider' && params[:action] == 'destroy') ||
|
|
|
|
(params[:controller] == 'login' && params[:action] == 'session_token')
|
2013-08-14 06:45:58 +08:00
|
|
|
|
|
|
|
token_string = AuthenticationMethods.access_token(request)
|
2011-12-23 04:26:05 +08:00
|
|
|
|
|
|
|
if token_string
|
2012-10-18 04:04:22 +08:00
|
|
|
@access_token = AccessToken.authenticate(token_string)
|
|
|
|
if !@access_token
|
2011-12-23 04:26:05 +08:00
|
|
|
raise AccessTokenError
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
2015-08-14 05:37:27 +08:00
|
|
|
|
2018-01-27 01:59:47 +08:00
|
|
|
account = access_token_account(@domain_root_account, @access_token)
|
|
|
|
raise AccessTokenError unless @access_token.authorized_for_account?(account)
|
2015-08-14 05:37:27 +08:00
|
|
|
|
2011-07-28 06:43:25 +08:00
|
|
|
@current_user = @access_token.user
|
2017-04-07 05:00:25 +08:00
|
|
|
@current_pseudonym = SisPseudonym.for(@current_user, @domain_root_account, type: :implicit, require_sis: false)
|
2015-08-14 05:37:27 +08:00
|
|
|
|
2011-12-22 02:59:05 +08:00
|
|
|
unless @current_user && @current_pseudonym
|
2011-12-23 04:26:05 +08:00
|
|
|
raise AccessTokenError
|
2011-07-28 06:43:25 +08:00
|
|
|
end
|
2018-03-09 04:46:40 +08:00
|
|
|
validate_scopes
|
2011-07-28 06:43:25 +08:00
|
|
|
@access_token.used!
|
2015-03-07 04:44:32 +08:00
|
|
|
|
|
|
|
RequestContextGenerator.add_meta_header('at', @access_token.global_id)
|
2015-08-12 23:36:00 +08:00
|
|
|
RequestContextGenerator.add_meta_header('dk', @access_token.global_developer_key_id) if @access_token.developer_key_id
|
2011-07-20 01:47:59 +08:00
|
|
|
end
|
2011-12-23 04:26:05 +08:00
|
|
|
end
|
|
|
|
|
2018-01-27 01:59:47 +08:00
|
|
|
def access_token_account(domain_root_account, access_token)
|
|
|
|
dev_key_account_id = access_token.dev_key_account_id
|
|
|
|
if dev_key_account_id.blank? || domain_root_account.id == dev_key_account_id
|
|
|
|
domain_root_account
|
|
|
|
else
|
|
|
|
get_context
|
|
|
|
(@context && Context.get_account(@context)) || domain_root_account
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-12-23 04:26:05 +08:00
|
|
|
def load_user
|
|
|
|
@current_user = @current_pseudonym = nil
|
2014-11-15 00:45:04 +08:00
|
|
|
|
|
|
|
masked_authenticity_token # ensure that the cookie is set
|
2011-12-23 04:26:05 +08:00
|
|
|
|
2015-10-30 04:03:20 +08:00
|
|
|
load_pseudonym_from_jwt
|
|
|
|
|
|
|
|
unless @current_pseudonym.present?
|
|
|
|
load_pseudonym_from_access_token
|
|
|
|
end
|
2011-07-20 01:47:59 +08:00
|
|
|
|
2011-12-23 04:26:05 +08:00
|
|
|
if !@current_pseudonym
|
2011-10-04 23:21:15 +08:00
|
|
|
if @policy_pseudonym_id
|
2014-09-18 01:29:06 +08:00
|
|
|
@current_pseudonym = Pseudonym.where(id: @policy_pseudonym_id).first
|
2018-08-17 02:40:41 +08:00
|
|
|
elsif (@pseudonym_session = PseudonymSession.with_scope(find_options: Pseudonym.eager_load(:user)) { PseudonymSession.find })
|
2013-04-26 07:06:20 +08:00
|
|
|
@current_pseudonym = @pseudonym_session.record
|
|
|
|
|
|
|
|
# if the session was created before the last time the user explicitly
|
|
|
|
# logged out (of any session for any of their pseudonyms), invalidate
|
|
|
|
# this session
|
2014-05-08 03:49:12 +08:00
|
|
|
invalid_before = @current_pseudonym.user.last_logged_out
|
|
|
|
# they logged out in the future?!? something's busted; just ignore it -
|
|
|
|
# either my clock is off or whoever set this value's clock is off
|
|
|
|
invalid_before = nil if invalid_before && invalid_before > Time.now.utc
|
|
|
|
if invalid_before &&
|
2013-06-22 06:07:00 +08:00
|
|
|
(session_refreshed_at = request.env['encrypted_cookie_store.session_refreshed_at']) &&
|
|
|
|
session_refreshed_at < invalid_before
|
|
|
|
|
2014-11-25 01:11:13 +08:00
|
|
|
logger.info "Invalidating session: Session created before user logged out."
|
2013-06-22 06:07:00 +08:00
|
|
|
destroy_session
|
|
|
|
@current_pseudonym = nil
|
2014-08-06 01:23:33 +08:00
|
|
|
if api_request? || request.format.json?
|
|
|
|
raise LoggedOutError
|
|
|
|
end
|
2013-04-26 07:06:20 +08:00
|
|
|
end
|
2014-10-07 05:18:11 +08:00
|
|
|
|
2015-02-19 06:14:18 +08:00
|
|
|
if @current_pseudonym &&
|
|
|
|
session[:cas_session] &&
|
refactor PseudonymSessionsController
fixes CNVS-20394
split it into appropriate concerns. main points are:
* /login never renders a login form - it redirects forward to the
default auth controller based on the first account
authorization config (or discovery url on the account)
* /login/canvas is the new home of the old login form. this form is
never rendered in-situ anymore - other places that used to render
it now redirect to /login (and then forward to here), reducing
their knowledge of SSO
* /login/ldap ends up at the same place (cause LDAP auth is handled
transparently)
* /login/cas and /login/saml redirect forward to the first SSO
configuration of the appropriate type. /login/:auth_type/:id can
be used to select a specific one
* if an SSO fails, it redirects back to /login with flash[:error]
set. this can forward to the discovery url appropriately, or
render an error page appropriately (the old no_auto=1, but now
it's not layered on top of the login partial that didn't show a
login form)
* ?canvas_login=1 is deprecated. just go directly to /login/canvas
* /saml_consume, /saml_logout are deprecated. they are processed
directly by /login/saml and /login/saml/logout
* /login/:id is deprecated - it forwards to /login/:auth_type/:id
as appropriate (presumably only saml, since that was the only
one that previously should have been using these links)
* OTP has been split into its own controller, and separated into
multiple actions instead of one all-in-one action
* /logout has been vastly simplified. the login controller should
set session[:login_aac], and on logout it will check with that
AAC for a url to redirect to after logout, instead of /login.
SSO logout is handled by each controller if they support it
test plan:
* regression test the following functionality -
* login with canvas auth
* login with LDAP auth
* login with SAML auth - and multiple SAMLs
* login with CAS auth
* MFA (configure, using, auto-setup)
* Canvas as OAuth Provider flow
* redirects to the login page when you're not
logged in
* failure of SAML/CAS (i.e. can't find user)
show a decent error page and allows retry
* "sticky" site admin auth (site admin is CAS/SAML,
going directly to another domain logs you in with
site admin)
Change-Id: I1bb9d81a101939f812cbd5020e20749e883fdc0f
Reviewed-on: https://gerrit.instructure.com/53220
QA-Review: August Thornton <august@instructure.com>
Tested-by: Jenkins
Reviewed-by: Ethan Vizitei <evizitei@instructure.com>
Product-Review: Cody Cutrer <cody@instructure.com>
2015-05-01 03:58:57 +08:00
|
|
|
@current_pseudonym.cas_ticket_expired?(session[:cas_session])
|
2014-10-07 05:18:11 +08:00
|
|
|
|
2015-02-19 06:14:18 +08:00
|
|
|
logger.info "Invalidating session: CAS ticket expired - #{session[:cas_session]}."
|
|
|
|
destroy_session
|
|
|
|
@current_pseudonym = nil
|
2014-10-07 05:18:11 +08:00
|
|
|
|
2015-02-19 06:14:18 +08:00
|
|
|
raise LoggedOutError if api_request? || request.format.json?
|
2014-10-07 05:18:11 +08:00
|
|
|
|
2015-02-19 06:14:18 +08:00
|
|
|
redirect_to_login
|
|
|
|
end
|
2014-10-07 05:18:11 +08:00
|
|
|
end
|
|
|
|
|
2011-05-27 07:41:43 +08:00
|
|
|
if params[:login_success] == '1' && !@current_pseudonym
|
|
|
|
# they just logged in successfully, but we can't find the pseudonym now?
|
|
|
|
# sounds like somebody hates cookies.
|
|
|
|
return redirect_to(login_url(:needs_cookies => '1'))
|
|
|
|
end
|
|
|
|
@current_user = @current_pseudonym && @current_pseudonym.user
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
2011-07-20 01:47:59 +08:00
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
if @current_user && @current_user.unavailable?
|
|
|
|
@current_pseudonym = nil
|
2011-12-23 04:26:05 +08:00
|
|
|
@current_user = nil
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
|
2013-08-14 06:45:58 +08:00
|
|
|
# required by the user throttling middleware
|
|
|
|
session[:user_id] = @current_user.global_id if @current_user
|
|
|
|
|
2011-07-09 02:59:34 +08:00
|
|
|
if @current_user && %w(become_user_id me become_teacher become_student).any? { |k| params.key?(k) }
|
|
|
|
request_become_user = nil
|
2011-02-01 09:57:29 +08:00
|
|
|
if params[:become_user_id]
|
2014-09-18 01:29:06 +08:00
|
|
|
request_become_user = User.where(id: params[:become_user_id]).first
|
2011-02-01 09:57:29 +08:00
|
|
|
elsif params.keys.include?('me')
|
2011-07-09 02:59:34 +08:00
|
|
|
request_become_user = @current_user
|
2011-02-01 09:57:29 +08:00
|
|
|
elsif params.keys.include?('become_teacher')
|
|
|
|
course = Course.find(params[:course_id] || params[:id]) rescue nil
|
|
|
|
teacher = course.teachers.first if course
|
|
|
|
if teacher
|
2011-07-09 02:59:34 +08:00
|
|
|
request_become_user = teacher
|
2011-02-01 09:57:29 +08:00
|
|
|
else
|
2011-05-07 02:44:34 +08:00
|
|
|
flash[:error] = I18n.t('lib.auth.errors.teacher_not_found', "No teacher found")
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
elsif params.keys.include?('become_student')
|
|
|
|
course = Course.find(params[:course_id] || params[:id]) rescue nil
|
|
|
|
student = course.students.first if course
|
|
|
|
if student
|
2011-07-09 02:59:34 +08:00
|
|
|
request_become_user = student
|
2011-02-01 09:57:29 +08:00
|
|
|
else
|
2011-05-07 02:44:34 +08:00
|
|
|
flash[:error] = I18n.t('lib.auth.errors.student_not_found', "No student found")
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
end
|
2011-07-09 02:59:34 +08:00
|
|
|
|
2012-02-21 06:57:32 +08:00
|
|
|
if request_become_user && request_become_user.id != session[:become_user_id].to_i && request_become_user.can_masquerade?(@current_user, @domain_root_account)
|
2011-05-28 00:15:19 +08:00
|
|
|
params_without_become = params.dup
|
|
|
|
params_without_become.delete_if {|k,v| [ 'become_user_id', 'become_teacher', 'become_student', 'me' ].include? k }
|
|
|
|
params_without_become[:only_path] = true
|
2017-07-21 23:33:57 +08:00
|
|
|
session[:masquerade_return_to] = url_for(params_without_become.to_unsafe_h)
|
2011-07-09 02:59:34 +08:00
|
|
|
return redirect_to user_masquerade_url(request_become_user.id)
|
2011-05-28 00:15:19 +08:00
|
|
|
end
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
|
2012-01-24 02:56:25 +08:00
|
|
|
as_user_id = api_request? && params[:as_user_id].presence
|
2011-10-07 05:24:25 +08:00
|
|
|
as_user_id ||= session[:become_user_id]
|
2011-12-28 00:52:51 +08:00
|
|
|
if as_user_id
|
|
|
|
begin
|
|
|
|
user = api_find(User, as_user_id)
|
|
|
|
rescue ActiveRecord::RecordNotFound
|
|
|
|
end
|
2012-02-21 06:57:32 +08:00
|
|
|
if user && user.can_masquerade?(@current_user, @domain_root_account)
|
2011-10-21 03:28:32 +08:00
|
|
|
@real_current_user = @current_user
|
|
|
|
@current_user = user
|
2012-02-07 08:03:33 +08:00
|
|
|
@real_current_pseudonym = @current_pseudonym
|
2017-04-07 05:00:25 +08:00
|
|
|
@current_pseudonym = SisPseudonym.for(@current_user, @domain_root_account, type: :implicit, require_sis: false)
|
2011-10-21 03:28:32 +08:00
|
|
|
logger.warn "#{@real_current_user.name}(#{@real_current_user.id}) impersonating #{@current_user.name} on page #{request.url}"
|
2011-12-28 00:52:51 +08:00
|
|
|
elsif api_request?
|
|
|
|
# fail silently for UI, but not for API
|
|
|
|
render :json => {:errors => "Invalid as_user_id"}, :status => :unauthorized
|
|
|
|
return false
|
2011-10-21 03:28:32 +08:00
|
|
|
end
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
@current_user
|
|
|
|
end
|
|
|
|
private :load_user
|
2011-12-23 04:26:05 +08:00
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
def require_user
|
2013-02-13 04:58:04 +08:00
|
|
|
if @current_user && @current_pseudonym
|
|
|
|
true
|
|
|
|
else
|
2012-03-08 08:32:27 +08:00
|
|
|
redirect_to_login
|
2013-02-13 04:58:04 +08:00
|
|
|
false
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
protected :require_user
|
|
|
|
|
2013-04-12 07:05:16 +08:00
|
|
|
def clean_return_to(url)
|
|
|
|
return nil if url.blank?
|
2014-05-05 23:26:26 +08:00
|
|
|
begin
|
|
|
|
uri = URI.parse(url)
|
2015-07-24 23:32:11 +08:00
|
|
|
rescue URI::Error
|
2014-05-05 23:26:26 +08:00
|
|
|
return nil
|
|
|
|
end
|
2015-04-28 01:29:42 +08:00
|
|
|
return nil unless uri.path[0] == '/'
|
2013-04-12 07:05:16 +08:00
|
|
|
return "#{request.protocol}#{request.host_with_port}#{uri.path}#{uri.query && "?#{uri.query}"}#{uri.fragment && "##{uri.fragment}"}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def return_to(url, fallback)
|
|
|
|
url = clean_return_to(url) || clean_return_to(fallback)
|
|
|
|
redirect_to url
|
|
|
|
end
|
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
def store_location(uri=nil, overwrite=true)
|
|
|
|
if overwrite || !session[:return_to]
|
2013-12-10 02:46:21 +08:00
|
|
|
uri ||= request.get? ? request.fullpath : request.referrer
|
2013-04-12 07:05:16 +08:00
|
|
|
session[:return_to] = clean_return_to(uri)
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
protected :store_location
|
|
|
|
|
|
|
|
def redirect_back_or_default(default)
|
|
|
|
redirect_to(session[:return_to] || default)
|
2012-05-09 10:27:16 +08:00
|
|
|
session.delete(:return_to)
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
protected :redirect_back_or_default
|
|
|
|
|
2011-06-30 04:05:59 +08:00
|
|
|
def redirect_to_referrer_or_default(default)
|
|
|
|
redirect_to(:back)
|
|
|
|
rescue ActionController::RedirectBackError
|
|
|
|
redirect_to(default)
|
|
|
|
end
|
|
|
|
|
2012-03-08 08:32:27 +08:00
|
|
|
def redirect_to_login
|
2015-11-05 01:23:11 +08:00
|
|
|
return unless fix_ms_office_redirects
|
2012-03-08 08:32:27 +08:00
|
|
|
respond_to do |format|
|
2012-08-15 04:24:39 +08:00
|
|
|
format.html {
|
|
|
|
store_location
|
|
|
|
flash[:warning] = I18n.t('lib.auth.errors.not_authenticated', "You must be logged in to access this page") unless request.path == '/'
|
2017-01-13 04:24:07 +08:00
|
|
|
redirect_to login_url(params.permit(:canvas_login, :authentication_provider))
|
2012-08-15 04:24:39 +08:00
|
|
|
}
|
2013-03-27 00:57:36 +08:00
|
|
|
format.json { render_json_unauthorized }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_json_unauthorized
|
|
|
|
add_www_authenticate_header if api_request? && !@current_user
|
|
|
|
if @current_user
|
|
|
|
render :json => {
|
|
|
|
:status => I18n.t('lib.auth.status_unauthorized', 'unauthorized'),
|
integrate the active_model-better_errors gem
refs CNVS-6040
This gem will help us output json error responses in the API using error
codes, since by itself ActiveRecord::Errors just deals in human-readable
i18n'd strings, and doesn't store detailed machine-readable information
on the error.
BetterErrors is mostly compatible, there's a few differences that mean I
had to change some unrelated code:
* errors[field_name] always returns an array, even if there's only one
error on the field. This is an improvement IMO.
* errors is indexed by symbol, not by string
* iterating over the errors object now yields
|attr, error_object| rather than |attr, string_message|
This includes a backport of the gem to rails 2.3.
On rails 3, we just use the vanilla gem.
The error codes aren't yet documented in the API docs, support for doing
that will come in a subsequent commit.
test plan: specs, plus you can hit the one api endpoint i've converted
so far -- account authorization configs. try to create an invalid
config, such as adding both cas and ldap configs to the same account,
and verify the error response formatting
Change-Id: Iaadd843ca9ff3f52c64e0256d82b64595c5559fb
Reviewed-on: https://gerrit.instructure.com/26178
Reviewed-by: Brian Palmer <brianp@instructure.com>
Product-Review: Brian Palmer <brianp@instructure.com>
QA-Review: Brian Palmer <brianp@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
2013-11-09 07:56:31 +08:00
|
|
|
:errors => [{ :message => I18n.t('lib.auth.not_authorized', "user not authorized to perform that action") }]
|
2013-03-27 00:57:36 +08:00
|
|
|
},
|
|
|
|
:status => :unauthorized
|
|
|
|
else
|
|
|
|
render :json => {
|
|
|
|
:status => I18n.t('lib.auth.status_unauthenticated', 'unauthenticated'),
|
integrate the active_model-better_errors gem
refs CNVS-6040
This gem will help us output json error responses in the API using error
codes, since by itself ActiveRecord::Errors just deals in human-readable
i18n'd strings, and doesn't store detailed machine-readable information
on the error.
BetterErrors is mostly compatible, there's a few differences that mean I
had to change some unrelated code:
* errors[field_name] always returns an array, even if there's only one
error on the field. This is an improvement IMO.
* errors is indexed by symbol, not by string
* iterating over the errors object now yields
|attr, error_object| rather than |attr, string_message|
This includes a backport of the gem to rails 2.3.
On rails 3, we just use the vanilla gem.
The error codes aren't yet documented in the API docs, support for doing
that will come in a subsequent commit.
test plan: specs, plus you can hit the one api endpoint i've converted
so far -- account authorization configs. try to create an invalid
config, such as adding both cas and ldap configs to the same account,
and verify the error response formatting
Change-Id: Iaadd843ca9ff3f52c64e0256d82b64595c5559fb
Reviewed-on: https://gerrit.instructure.com/26178
Reviewed-by: Brian Palmer <brianp@instructure.com>
Product-Review: Brian Palmer <brianp@instructure.com>
QA-Review: Brian Palmer <brianp@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
2013-11-09 07:56:31 +08:00
|
|
|
:errors => [{ :message => I18n.t('lib.auth.authentication_required', "user authorization required") }]
|
2013-03-27 00:57:36 +08:00
|
|
|
},
|
|
|
|
:status => :unauthorized
|
2012-03-08 08:32:27 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-03-27 00:57:36 +08:00
|
|
|
def add_www_authenticate_header
|
|
|
|
response['WWW-Authenticate'] = %{Bearer realm="canvas-lms"}
|
|
|
|
end
|
|
|
|
|
2011-02-01 09:57:29 +08:00
|
|
|
# Reset the session, and copy the specified keys over to the new session.
|
|
|
|
# Please consider the security implications of any keys you copy over.
|
|
|
|
def reset_session_saving_keys(*keys)
|
|
|
|
# can't use slice, because session has a different ctor than a normal hash
|
|
|
|
saved = {}
|
2011-11-03 04:59:41 +08:00
|
|
|
keys.each { |k| saved[k] = session[k] if session[k] }
|
2011-02-01 09:57:29 +08:00
|
|
|
reset_session
|
2011-11-03 04:59:41 +08:00
|
|
|
saved.each_pair { |k, v| session[k] = v }
|
2011-02-01 09:57:29 +08:00
|
|
|
end
|
|
|
|
end
|