2020-10-27 00:50:13 +08:00
# frozen_string_literal: true
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
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
2021-07-22 05:20:34 +08:00
def load_pseudonym_from_inst_access_token ( token_string )
2021-09-14 03:34:47 +08:00
token = :: AuthenticationMethods :: InstAccessToken . parse ( token_string )
return false unless token
2021-07-19 14:40:20 +08:00
2021-09-14 03:34:47 +08:00
auth_context = :: AuthenticationMethods :: InstAccessToken . load_user_and_pseudonym_context ( token , @domain_root_account )
2021-07-19 14:40:20 +08:00
2021-09-14 03:34:47 +08:00
@current_user = auth_context [ :current_user ]
@current_pseudonym = auth_context [ :current_pseudonym ]
2021-07-19 14:40:20 +08:00
raise AccessTokenError unless @current_user && @current_pseudonym
2021-09-14 03:34:47 +08:00
if auth_context [ :real_current_user ]
@real_current_user = auth_context [ :real_current_user ]
@real_current_pseudonym = auth_context [ :real_current_pseudonym ]
logger . warn " [AUTH] #{ @real_current_user . name } ( #{ @real_current_user . id } ) impersonating #{ @current_user . name } on page #{ request . url } "
2021-07-19 14:40:20 +08:00
end
2021-07-09 15:40:31 +08:00
@authenticated_with_jwt = @authenticated_with_inst_access_token = true
2021-07-19 14:40:20 +08:00
end
2015-10-30 04:03:20 +08:00
def load_pseudonym_from_jwt
return unless api_request?
2021-09-23 00:25:11 +08:00
2015-10-30 04:03:20 +08:00
token_string = AuthenticationMethods . access_token ( request )
return unless token_string . present?
2021-07-22 05:20:34 +08:00
return if load_pseudonym_from_inst_access_token ( token_string )
2021-07-19 14:40:20 +08:00
2015-10-30 04:03:20 +08:00
begin
Add asymmetric encryption for service tokens
refs FOO-2410
test plan:
- in dynamic_settings.yml, add the following block:
```
store:
canvas:
services-jwt:
# these are all the same JWK but with different kid
# to generate a new key, run the following in a Canvas console:
#
# key = OpenSSL::PKey::RSA.generate(2048)
# key.public_key.to_jwk(kid: Time.now.utc.iso8601).to_json
jwk-past.json: "{\"kty\":\"RSA\",\"e\":\"AQAB\",\"n\":\"uX1MpfEMQCBUMcj0sBYI-iFaG5Nodp3C6OlN8uY60fa5zSBd83-iIL3n_qzZ8VCluuTLfB7rrV_tiX727XIEqQ\",\"kid\":\"2018-05-18T22:33:20Z_a\",\"d\":\"pYwR64x-LYFtA13iHIIeEvfPTws50ZutyGfpHN-kIZz3k-xVpun2Hgu0hVKZMxcZJ9DkG8UZPqD-zTDbCmCyLQ\",\"p\":\"6OQ2bi_oY5fE9KfQOcxkmNhxDnIKObKb6TVYqOOz2JM\",\"q\":\"y-UBef95njOrqMAxJH1QPds3ltYWr8QgGgccmcATH1M\",\"dp\":\"Ol_xkL7rZgNFt_lURRiJYpJmDDPjgkDVuafIeFTS4Ic\",\"dq\":\"RtzDY5wXr5TzrwWEztLCpYzfyAuF_PZj1cfs976apsM\",\"qi\":\"XA5wnwIrwe5MwXpaBijZsGhKJoypZProt47aVCtWtPE\"}"
jwk-present.json: "{\"kty\":\"RSA\",\"e\":\"AQAB\",\"n\":\"uX1MpfEMQCBUMcj0sBYI-iFaG5Nodp3C6OlN8uY60fa5zSBd83-iIL3n_qzZ8VCluuTLfB7rrV_tiX727XIEqQ\",\"kid\":\"2018-06-18T22:33:20Z_b\",\"d\":\"pYwR64x-LYFtA13iHIIeEvfPTws50ZutyGfpHN-kIZz3k-xVpun2Hgu0hVKZMxcZJ9DkG8UZPqD-zTDbCmCyLQ\",\"p\":\"6OQ2bi_oY5fE9KfQOcxkmNhxDnIKObKb6TVYqOOz2JM\",\"q\":\"y-UBef95njOrqMAxJH1QPds3ltYWr8QgGgccmcATH1M\",\"dp\":\"Ol_xkL7rZgNFt_lURRiJYpJmDDPjgkDVuafIeFTS4Ic\",\"dq\":\"RtzDY5wXr5TzrwWEztLCpYzfyAuF_PZj1cfs976apsM\",\"qi\":\"XA5wnwIrwe5MwXpaBijZsGhKJoypZProt47aVCtWtPE\"}"
jwk-future.json: "{\"kty\":\"RSA\",\"e\":\"AQAB\",\"n\":\"uX1MpfEMQCBUMcj0sBYI-iFaG5Nodp3C6OlN8uY60fa5zSBd83-iIL3n_qzZ8VCluuTLfB7rrV_tiX727XIEqQ\",\"kid\":\"2018-07-18T22:33:20Z_c\",\"d\":\"pYwR64x-LYFtA13iHIIeEvfPTws50ZutyGfpHN-kIZz3k-xVpun2Hgu0hVKZMxcZJ9DkG8UZPqD-zTDbCmCyLQ\",\"p\":\"6OQ2bi_oY5fE9KfQOcxkmNhxDnIKObKb6TVYqOOz2JM\",\"q\":\"y-UBef95njOrqMAxJH1QPds3ltYWr8QgGgccmcATH1M\",\"dp\":\"Ol_xkL7rZgNFt_lURRiJYpJmDDPjgkDVuafIeFTS4Ic\",\"dq\":\"RtzDY5wXr5TzrwWEztLCpYzfyAuF_PZj1cfs976apsM\",\"qi\":\"XA5wnwIrwe5MwXpaBijZsGhKJoypZProt47aVCtWtPE\"}"
```
- Ensure /internal/services/jwks loads correctly
- In console, ensure `CanvasSecurity::ServicesJwt.decrypt(Base64.decode64(CanvasSecurity::ServicesJwt.for_user('localhost', User.first)))`
and `CanvasSecurity::ServicesJwt.decrypt(Base64.decode64(CanvasSecurity::ServicesJwt.for_user('localhost', User.first, symmetric: true)))`
both work and produce sensible looking output
Change-Id: I13c6c35cc92ed12d03bf97e89e590614e11c6d47
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/275160
QA-Review: August Thornton <august@instructure.com>
Product-Review: August Thornton <august@instructure.com>
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Ethan Vizitei <evizitei@instructure.com>
Reviewed-by: Evan Battaglia <ebattaglia@instructure.com>
2021-10-05 03:20:31 +08:00
services_jwt = CanvasSecurity :: ServicesJwt . new ( token_string )
2015-10-30 04:03:20 +08:00
@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
2021-09-23 00:25:11 +08:00
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 )
2021-07-19 14:40:20 +08:00
logger . warn " [AUTH] #{ @real_current_user . name } ( #{ @real_current_user . id } ) impersonating #{ @current_user . name } on page #{ request . url } "
2016-03-31 00:20:05 +08:00
end
2015-10-30 04:03:20 +08:00
@authenticated_with_jwt = true
2020-10-30 22:47:20 +08:00
rescue JSON :: JWT :: InvalidFormat , # definitely not a JWT
Canvas :: Security :: TokenExpired , # it could be a JWT, but it's expired if so
Canvas :: Security :: InvalidToken # not formatted like a JWT
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
end
end
2019-08-08 23:24:44 +08:00
ALLOWED_SCOPE_INCLUDES = %w{ uuid }
def filter_includes ( key )
# no funny business
params . delete ( key ) unless params [ key ] . class == Array
return unless params . key? ( key )
2021-09-23 00:25:11 +08:00
2019-08-08 23:24:44 +08:00
params [ key ] & = ALLOWED_SCOPE_INCLUDES
end
2018-03-09 04:46:40 +08:00
def validate_scopes
2020-01-07 06:41:41 +08:00
return unless @access_token
2018-03-09 04:46:40 +08:00
2020-01-07 06:41:41 +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 )
scope_patterns = @access_token . url_scopes_for_method ( request_method ) . concat ( AccessToken . always_allowed_scopes )
if scope_patterns . any? { | scope | scope =~ request . path }
unless developer_key . try ( :allow_includes )
2019-08-08 23:24:44 +08:00
filter_includes ( :include )
filter_includes ( :includes )
2018-03-09 04:46:40 +08:00
end
2020-01-07 06:41:41 +08:00
else
raise AccessTokenScopeError
2018-03-09 04:46:40 +08:00
end
end
end
2019-08-29 07:16:24 +08:00
def self . graphql_type_authorized? ( access_token , type )
if access_token & . developer_key & . require_scopes
# allowing the root query type for now, but any other type is forbidden
type == " Query "
else
true
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? ||
2021-09-23 00:25:11 +08:00
( 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 )
2021-07-23 04:25:06 +08:00
raise AccessTokenError unless @access_token
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
2020-05-07 01:21:54 +08:00
@real_current_user = @access_token . real_user
@real_current_pseudonym = SisPseudonym . for ( @real_current_user , @domain_root_account , type : :implicit , require_sis : false ) if @real_current_user
2017-04-07 05:00:25 +08:00
@current_pseudonym = SisPseudonym . for ( @current_user , @domain_root_account , type : :implicit , require_sis : false )
2021-07-23 04:25:06 +08:00
@current_pseudonym = nil if ( @current_pseudonym & . suspended? && ! @real_current_pseudonym ) || @real_current_pseudonym & . suspended?
raise AccessTokenError unless @current_user && @current_pseudonym
2015-08-14 05:37:27 +08:00
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
2021-02-27 23:13:52 +08:00
RequestContext :: Generator . add_meta_header ( 'at' , @access_token . global_id )
RequestContext :: Generator . 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
2021-07-19 14:40:20 +08:00
load_pseudonym_from_access_token unless @current_pseudonym . present?
2011-07-20 01:47:59 +08:00
2021-07-23 04:25:06 +08:00
unless @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
2020-08-19 05:25:52 +08:00
else
@pseudonym_session = PseudonymSession . find_with_validation
if @pseudonym_session
@current_pseudonym = @pseudonym_session . record
@current_pseudonym . user . reload if @current_pseudonym . shard != @current_pseudonym . user . shard
# 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
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 &&
2021-09-23 00:25:11 +08:00
( session_refreshed_at = request . env [ 'encrypted_cookie_store.session_refreshed_at' ] ) &&
session_refreshed_at < invalid_before
2020-08-19 05:25:52 +08:00
logger . info " [AUTH] Invalidating session: Session created before user logged out. "
2021-07-23 04:25:06 +08:00
invalidate_session
return
2014-08-06 01:23:33 +08:00
end
2014-10-07 05:18:11 +08:00
2020-08-19 05:25:52 +08:00
if @current_pseudonym &&
2021-09-23 00:25:11 +08:00
session [ :cas_session ] &&
@current_pseudonym . cas_ticket_expired? ( session [ :cas_session ] )
2014-10-07 05:18:11 +08:00
2020-08-19 05:25:52 +08:00
logger . info " [AUTH] Invalidating session: CAS ticket expired - #{ session [ :cas_session ] } . "
2021-07-23 04:25:06 +08:00
invalidate_session
return
end
2014-10-07 05:18:11 +08:00
2021-07-23 04:25:06 +08:00
if @current_pseudonym . suspended?
logger . info " [AUTH] Invalidating session: Pseudonym is suspended. "
invalidate_session
return
2020-08-19 05:25:52 +08:00
end
2015-02-19 06:14:18 +08:00
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
2021-09-23 00:25:11 +08:00
2011-05-27 07:41:43 +08:00
@current_user = @current_pseudonym && @current_pseudonym . user
2011-02-01 09:57:29 +08:00
end
2011-07-20 01:47:59 +08:00
2020-08-18 21:17:43 +08:00
logger . info " [AUTH] inital load: pseud -> #{ @current_pseudonym & . id } , user -> #{ @current_user & . id } "
2011-02-01 09:57:29 +08:00
if @current_user && @current_user . unavailable?
2020-08-18 21:17:43 +08:00
logger . info " [AUTH] Invalid request: User is currently UNAVAILABLE "
2011-02-01 09:57:29 +08:00
@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
2021-09-23 00:25:11 +08:00
params_without_become . delete_if { | k , v | [ 'become_user_id' , 'become_teacher' , 'become_student' , 'me' ] . include? k }
2011-05-28 00:15:19 +08:00
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
2021-04-26 23:03:02 +08:00
nil
2011-12-28 00:52:51 +08:00
end
2020-10-13 10:06:46 +08:00
if user && @real_current_user
if @current_user != user
# if we're already masquerading from an access token, and now try to
# masquerade as someone else
2021-09-23 00:25:11 +08:00
render :json = > { :errors = > " Cannot change masquerade " } , :status = > :unauthorized
2020-10-13 10:06:46 +08:00
return false
2021-09-23 00:25:11 +08:00
# else: they do match, everything is already set
2020-10-13 10:06:46 +08:00
end
logger . warn " [AUTH] #{ @real_current_user . name } ( #{ @real_current_user . id } ) impersonating #{ @current_user . name } on page #{ request . url } via masquerade token "
2021-04-26 23:03:02 +08:00
elsif 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 )
2020-08-18 21:17:43 +08:00
logger . warn " [AUTH] #{ @real_current_user . name } ( #{ @real_current_user . id } ) impersonating #{ @current_user . name } on page #{ request . url } "
2021-04-27 03:57:31 +08:00
elsif api_request? # fail silently for UI, but not for API
result = { errors : " Invalid as_user_id " }
if user & . deleted? && user . merged_into_user_id && user . grants_right? ( @current_user , :read )
result [ :merged_into_user_id ] = user . merged_into_user_id
end
2021-04-26 23:03:02 +08:00
# this should maybe be 404, not 401, but we can't change it now
2021-04-27 03:57:31 +08:00
render json : result , status : :unauthorized
2011-12-28 00:52:51 +08:00
return false
2011-10-21 03:28:32 +08:00
end
2011-02-01 09:57:29 +08:00
end
2020-08-18 21:17:43 +08:00
logger . info " [AUTH] final user: #{ @current_user & . id } "
2011-02-01 09:57:29 +08:00
@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
2021-07-19 14:40:20 +08:00
def require_non_jwt_auth
if @authenticated_with_jwt
render (
2021-09-23 00:25:11 +08:00
json : { error : " cannot generate a JWT when authorized by a JWT " } ,
2021-07-19 14:40:20 +08:00
status : 403
)
end
end
2013-04-12 07:05:16 +08:00
def clean_return_to ( url )
return nil if url . blank?
2021-09-23 00:25:11 +08:00
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
2019-05-29 01:38:48 +08:00
return nil unless uri . path && uri . path [ 0 ] == '/'
2019-01-09 04:48:53 +08:00
return " #{ request . protocol } #{ request . host_with_port } #{ uri . path . sub ( %r{ /download$ } , '' ) } " if uri . path =~ %r{ /files/( \ d+~)? \ d+/download$ }
2021-09-23 00:25:11 +08:00
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
2021-09-23 00:25:11 +08:00
def store_location ( uri = nil , overwrite = true )
2011-02-01 09:57:29 +08:00
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 )
2020-09-12 05:17:25 +08:00
session . delete ( :return_to ) || default
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 )
2019-04-19 21:37:25 +08:00
redirect_back ( fallback_location : default )
2011-06-30 04:05:59 +08:00
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
2021-09-23 00:25:11 +08:00
2012-03-08 08:32:27 +08:00
respond_to do | format |
2018-07-27 10:12:42 +08:00
format . json { render_json_unauthorized }
format . all do
2012-08-15 04:24:39 +08:00
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 ) )
2018-07-27 10:12:42 +08:00
end
2013-03-27 00:57:36 +08:00
end
end
def render_json_unauthorized
add_www_authenticate_header if api_request? && ! @current_user
if @current_user
2018-07-27 10:12:42 +08:00
render json : {
status : I18n . t ( 'lib.auth.status_unauthorized' , 'unauthorized' ) ,
errors : [ { message : I18n . t ( 'lib.auth.not_authorized' , " user not authorized to perform that action " ) } ]
} ,
2021-09-23 00:25:11 +08:00
status : :unauthorized
2013-03-27 00:57:36 +08:00
else
2018-07-27 10:12:42 +08:00
render json : {
status : I18n . t ( 'lib.auth.status_unauthenticated' , 'unauthenticated' ) ,
errors : [ { :message = > I18n . t ( 'lib.auth.authentication_required' , " user authorization required " ) } ]
} ,
2021-09-23 00:25:11 +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
2021-07-23 04:25:06 +08:00
def invalidate_session
destroy_session
@current_pseudonym = nil
raise LoggedOutError if api_request? || request . format . json?
2021-08-17 06:29:39 +08:00
redirect_to_login unless params [ :controller ] & . start_with? ( 'login' ) && params [ :action ] == 'new'
2021-07-23 04:25:06 +08:00
end
2011-02-01 09:57:29 +08:00
end