rename AccountAuthorizationConfig* to AuthenticationProvider*
I couldn't take it anymore. it was driving me crazy Change-Id: Ib40addff56e98a6e0a6d80f246c208e0ce0cb762 Reviewed-on: https://gerrit.instructure.com/148026 Tested-by: Jenkins Reviewed-by: James Williams <jamesw@instructure.com> Product-Review: Cody Cutrer <cody@instructure.com> QA-Review: Cody Cutrer <cody@instructure.com>
This commit is contained in:
parent
af039d8bb8
commit
41794a9383
|
@ -211,9 +211,9 @@
|
|||
# }
|
||||
# }
|
||||
#
|
||||
class AccountAuthorizationConfigsController < ApplicationController
|
||||
class AuthenticationProvidersController < ApplicationController
|
||||
before_action :require_context, :require_root_account_management
|
||||
include Api::V1::AccountAuthorizationConfig
|
||||
include Api::V1::AuthenticationProvider
|
||||
|
||||
# @API List authentication providers
|
||||
# Returns a paginated list of authentication providers
|
||||
|
@ -228,7 +228,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
if api_request?
|
||||
render json: aacs_json(@account.authentication_providers.active)
|
||||
else
|
||||
@presenter = AccountAuthorizationConfigsPresenter.new(@account)
|
||||
@presenter = AuthenticationProvidersPresenter.new(@account)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -649,7 +649,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
end
|
||||
format.json do
|
||||
msg = "duplicate provider #{account_config.auth_type}"
|
||||
render json: { errors: [ { message: msg } ] }, status: 422
|
||||
render json: { errors: [ { message: msg } ] }, status: 422
|
||||
end
|
||||
end
|
||||
return
|
||||
|
@ -662,7 +662,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
flash[:error] = account_config.errors.full_messages
|
||||
redirect_to(account_authentication_providers_path(@account))
|
||||
end
|
||||
format.json { raise ActiveRecord::RecordInvalid.new(account_config) }
|
||||
format.json { raise ActiveRecord::RecordInvalid, account_config }
|
||||
end
|
||||
return
|
||||
end
|
||||
|
@ -715,7 +715,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
flash[:error] = aac.errors.full_messages
|
||||
redirect_to(account_authentication_providers_path(@account))
|
||||
end
|
||||
format.json { raise ActiveRecord::RecordInvalid.new(aac) }
|
||||
format.json { raise ActiveRecord::RecordInvalid, aac }
|
||||
end
|
||||
return
|
||||
end
|
||||
|
@ -885,8 +885,8 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
|
||||
if results.empty?
|
||||
return render(
|
||||
:json => {:errors => {:account => t(:account_required, 'must be LDAP-authenticated')}},
|
||||
:status_code => 400
|
||||
:json => {:errors => {:account => t(:account_required, 'must be LDAP-authenticated')}},
|
||||
:status_code => 400
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -942,7 +942,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
protected
|
||||
def filter_data(data)
|
||||
auth_type = data.delete(:auth_type)
|
||||
klass = AccountAuthorizationConfig.find_sti_class(auth_type)
|
||||
klass = AuthenticationProvider.find_sti_class(auth_type)
|
||||
federated_attributes = data[:federated_attributes]
|
||||
federated_attributes = {} if federated_attributes == ""
|
||||
federated_attributes = federated_attributes.to_unsafe_h if federated_attributes.is_a?(ActionController::Parameters)
|
||||
|
@ -950,8 +950,8 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
data[:federated_attributes] = federated_attributes if federated_attributes
|
||||
data[:auth_type] = auth_type
|
||||
if data[:auth_type] == 'ldap'
|
||||
data[:auth_over_tls] = 'start_tls' unless data.has_key?(:auth_over_tls)
|
||||
data[:auth_over_tls] = AccountAuthorizationConfig::LDAP.auth_over_tls_setting(data[:auth_over_tls])
|
||||
data[:auth_over_tls] = 'start_tls' unless data.key?(:auth_over_tls)
|
||||
data[:auth_over_tls] = AuthenticationProvider::LDAP.auth_over_tls_setting(data[:auth_over_tls])
|
||||
end
|
||||
data
|
||||
end
|
||||
|
@ -970,8 +970,7 @@ class AccountAuthorizationConfigsController < ApplicationController
|
|||
def update_account_settings_from_hash(data)
|
||||
return if data.empty?
|
||||
data.each do |setting, value|
|
||||
setting_val = value.present? ? value : nil
|
||||
@account.public_send("#{setting}=".to_sym, setting_val)
|
||||
@account.public_send("#{setting}=".to_sym, value.presence)
|
||||
end
|
||||
@account.save!
|
||||
end
|
|
@ -144,7 +144,7 @@ class Login::CanvasController < ApplicationController
|
|||
def maybe_render_mobile_login(status = nil)
|
||||
if mobile_device?
|
||||
@login_handle_name = @domain_root_account.login_handle_name_with_inference
|
||||
@login_handle_is_email = @login_handle_name == AccountAuthorizationConfig.default_login_handle_name
|
||||
@login_handle_is_email = @login_handle_name == AuthenticationProvider.default_login_handle_name
|
||||
js_env(
|
||||
GOOGLE_ANALYTICS_KEY: Setting.get('google_analytics_key', nil),
|
||||
)
|
||||
|
|
|
@ -28,8 +28,8 @@ class Login::Oauth2Controller < Login::OauthBaseController
|
|||
def create
|
||||
return unless validate_request
|
||||
|
||||
@aac = AccountAuthorizationConfig.find(jwt['aac_id'])
|
||||
raise ActiveRecord::RecordNotFound unless @aac.is_a?(AccountAuthorizationConfig::Oauth2)
|
||||
@aac = AuthenticationProvider.find(jwt['aac_id'])
|
||||
raise ActiveRecord::RecordNotFound unless @aac.is_a?(AuthenticationProvider::Oauth2)
|
||||
|
||||
unique_id = nil
|
||||
provider_attributes = {}
|
||||
|
|
|
@ -35,7 +35,7 @@ class Login::OauthController < Login::OauthBaseController
|
|||
|
||||
def create
|
||||
@aac = @domain_root_account.authentication_providers.active.find(params[:id])
|
||||
raise ActiveRecord::RecordNotFound unless @aac.is_a?(AccountAuthorizationConfig::Oauth)
|
||||
raise ActiveRecord::RecordNotFound unless @aac.is_a?(AuthenticationProvider::Oauth)
|
||||
|
||||
oauth_state = session.delete(:oauth)
|
||||
request_token = OAuth::RequestToken.new(@aac.consumer,
|
||||
|
|
|
@ -399,7 +399,7 @@ class Login::SamlController < ApplicationController
|
|||
|
||||
logout_current_user
|
||||
|
||||
private_key = AccountAuthorizationConfig::SAML.private_key
|
||||
private_key = AuthenticationProvider::SAML.private_key
|
||||
private_key = nil if aac.sig_alg.nil?
|
||||
forward_url = SAML2::Bindings::HTTPRedirect.encode(logout_response,
|
||||
relay_state: relay_state,
|
||||
|
@ -418,7 +418,7 @@ class Login::SamlController < ApplicationController
|
|||
# This needs to be publicly available since external SAML
|
||||
# servers need to be able to access it without being authenticated.
|
||||
# It is used to disclose our SAML configuration settings.
|
||||
metadata = AccountAuthorizationConfig::SAML.sp_metadata_for_account(@domain_root_account, request.host_with_port)
|
||||
metadata = AuthenticationProvider::SAML.sp_metadata_for_account(@domain_root_account, request.host_with_port)
|
||||
render xml: metadata.to_xml
|
||||
end
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ class LoginController < ApplicationController
|
|||
|
||||
if session[:login_aac]
|
||||
# The AAC could have been deleted since the user logged in
|
||||
aac = AccountAuthorizationConfig.where(id: session[:login_aac]).first
|
||||
aac = AuthenticationProvider.where(id: session[:login_aac]).first
|
||||
redirect = aac.try(:user_logout_redirect, self, @current_user)
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import I18n from 'i18n!account_authorization_configs'
|
||||
import I18n from 'i18n!authentication_providers'
|
||||
import Select from '@instructure/ui-core/lib/components/Select'
|
||||
|
||||
class AuthTypePicker extends React.Component {
|
||||
|
|
|
@ -64,8 +64,8 @@ class Account < ActiveRecord::Base
|
|||
has_many :developer_key_account_bindings, inverse_of: :account, dependent: :destroy
|
||||
has_many :authentication_providers,
|
||||
-> { order(:position) },
|
||||
extend: AccountAuthorizationConfig::FindWithType,
|
||||
class_name: "AccountAuthorizationConfig"
|
||||
inverse_of: :account,
|
||||
extend: AuthenticationProvider::FindWithType
|
||||
|
||||
has_many :account_reports
|
||||
has_many :grading_standards, -> { where("workflow_state<>'deleted'") }, as: :context, inverse_of: :context
|
||||
|
@ -303,7 +303,7 @@ class Account < ActiveRecord::Base
|
|||
def enable_canvas_authentication
|
||||
return unless root_account?
|
||||
# for migrations creating a new db
|
||||
return unless AccountAuthorizationConfig::Canvas.columns_hash.key?('workflow_state')
|
||||
return unless AuthenticationProvider::Canvas.columns_hash.key?('workflow_state')
|
||||
return if authentication_providers.active.where(auth_type: 'canvas').exists?
|
||||
authentication_providers.create!(auth_type: 'canvas')
|
||||
end
|
||||
|
@ -959,12 +959,12 @@ class Account < ActiveRecord::Base
|
|||
if login_handle_name_is_customized?
|
||||
self.login_handle_name
|
||||
elsif self.delegated_authentication?
|
||||
AccountAuthorizationConfig.default_delegated_login_handle_name
|
||||
AuthenticationProvider.default_delegated_login_handle_name
|
||||
end
|
||||
end
|
||||
|
||||
def login_handle_name_with_inference
|
||||
customized_login_handle_name || AccountAuthorizationConfig.default_login_handle_name
|
||||
customized_login_handle_name || AuthenticationProvider.default_login_handle_name
|
||||
end
|
||||
|
||||
def self_and_all_sub_accounts
|
||||
|
@ -1106,7 +1106,7 @@ class Account < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def delegated_authentication?
|
||||
authentication_providers.active.first.is_a?(AccountAuthorizationConfig::Delegated)
|
||||
authentication_providers.active.first.is_a?(AuthenticationProvider::Delegated)
|
||||
end
|
||||
|
||||
def forgot_password_external_url
|
||||
|
|
|
@ -16,331 +16,5 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require 'net-ldap'
|
||||
require 'net_ldap_extensions'
|
||||
|
||||
class AccountAuthorizationConfig < ActiveRecord::Base
|
||||
include Workflow
|
||||
validates :auth_filter, length: {maximum: maximum_text_length, allow_nil: true, allow_blank: true}
|
||||
|
||||
workflow do
|
||||
state :active
|
||||
state :deleted
|
||||
end
|
||||
|
||||
self.inheritance_column = :auth_type
|
||||
|
||||
def self.subclass_from_attributes?(attrs)
|
||||
false
|
||||
end
|
||||
|
||||
# we have a lot of old data that didn't actually use STI,
|
||||
# so we shim it
|
||||
def self.find_sti_class(type_name)
|
||||
return self if type_name.blank? # super no longer does this in Rails 4
|
||||
case type_name
|
||||
when 'cas', 'ldap', 'saml'
|
||||
const_get(type_name.upcase)
|
||||
when 'clever', 'facebook', 'google', 'microsoft', 'twitter'
|
||||
const_get(type_name.classify)
|
||||
when 'canvas'
|
||||
Canvas
|
||||
when 'github'
|
||||
GitHub
|
||||
when 'linkedin'
|
||||
LinkedIn
|
||||
when 'openid_connect'
|
||||
OpenIDConnect
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def self.sti_name
|
||||
display_name.try(:underscore)
|
||||
end
|
||||
|
||||
def self.singleton?
|
||||
false
|
||||
end
|
||||
|
||||
def self.enabled?
|
||||
true
|
||||
end
|
||||
|
||||
def self.display_name
|
||||
name.try(:demodulize)
|
||||
end
|
||||
|
||||
scope :active, ->{ where("workflow_state <> 'deleted'") }
|
||||
belongs_to :account
|
||||
has_many :pseudonyms, foreign_key: :authentication_provider_id
|
||||
acts_as_list scope: { account: self, workflow_state: [nil, 'active'] }
|
||||
|
||||
VALID_AUTH_TYPES = %w[canvas cas clever facebook github google ldap linkedin microsoft openid_connect saml twitter].freeze
|
||||
validates_inclusion_of :auth_type, in: VALID_AUTH_TYPES, message: "invalid auth_type, must be one of #{VALID_AUTH_TYPES.join(',')}"
|
||||
validates_presence_of :account_id
|
||||
validate :validate_federated_attributes
|
||||
|
||||
# create associate model find to accept auth types, and just return the first one of that
|
||||
# type
|
||||
module FindWithType
|
||||
def find(*args)
|
||||
if VALID_AUTH_TYPES.include?(args.first)
|
||||
where(auth_type: args.first).first!
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.recognized_params
|
||||
[].freeze
|
||||
end
|
||||
|
||||
def self.deprecated_params
|
||||
[].freeze
|
||||
end
|
||||
|
||||
SENSITIVE_PARAMS = [].freeze
|
||||
|
||||
def self.login_button?
|
||||
Rails.root.join("public/images/sso_buttons/sso-#{sti_name}.svg").exist?
|
||||
end
|
||||
|
||||
def destroy
|
||||
self.send(:remove_from_list_for_destroy)
|
||||
self.workflow_state = 'deleted'
|
||||
self.save!
|
||||
enable_canvas_authentication
|
||||
send_later_if_production(:soft_delete_pseudonyms)
|
||||
true
|
||||
end
|
||||
alias_method :destroy_permanently!, :destroy
|
||||
|
||||
def auth_password=(password)
|
||||
return if password.blank?
|
||||
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 auth_provider_filter
|
||||
self
|
||||
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
|
||||
|
||||
# allowable attributes for federated_attributes setting; nil means anything
|
||||
# is allowed
|
||||
def self.recognized_federated_attributes
|
||||
[].freeze
|
||||
end
|
||||
|
||||
def settings
|
||||
read_attribute(:settings) || {}
|
||||
end
|
||||
|
||||
def federated_attributes=(value)
|
||||
value = {} unless value.is_a?(Hash)
|
||||
settings_will_change! unless value == federated_attributes
|
||||
settings['federated_attributes'] = value
|
||||
end
|
||||
|
||||
def federated_attributes
|
||||
settings['federated_attributes'] ||= {}
|
||||
end
|
||||
|
||||
def federated_attributes_for_api
|
||||
if jit_provisioning?
|
||||
federated_attributes
|
||||
else
|
||||
result = {}
|
||||
federated_attributes.each do |(canvas_attribute_name, provider_attribute_config)|
|
||||
next if provider_attribute_config['provisioning_only']
|
||||
result[canvas_attribute_name] = provider_attribute_config['attribute']
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
CANVAS_ALLOWED_FEDERATED_ATTRIBUTES = %w{
|
||||
admin_roles
|
||||
display_name
|
||||
email
|
||||
given_name
|
||||
integration_id
|
||||
locale
|
||||
name
|
||||
sis_user_id
|
||||
sortable_name
|
||||
surname
|
||||
time_zone
|
||||
}.freeze
|
||||
|
||||
def provision_user(unique_id, provider_attributes = {})
|
||||
User.transaction(requires_new: true) do
|
||||
pseudonym = account.pseudonyms.build
|
||||
pseudonym.user = User.create!(name: unique_id) { |u| u.workflow_state = 'registered' }
|
||||
pseudonym.authentication_provider = self
|
||||
pseudonym.unique_id = unique_id
|
||||
pseudonym.save!
|
||||
apply_federated_attributes(pseudonym, provider_attributes, purpose: :provisioning)
|
||||
pseudonym
|
||||
end
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
uncached do
|
||||
pseudonyms.active.by_unique_id(unique_id).take!
|
||||
end
|
||||
end
|
||||
|
||||
def apply_federated_attributes(pseudonym, provider_attributes, purpose: :login)
|
||||
user = pseudonym.user
|
||||
|
||||
canvas_attributes = translate_provider_attributes(provider_attributes,
|
||||
purpose: purpose)
|
||||
given_name = canvas_attributes.delete('given_name')
|
||||
surname = canvas_attributes.delete('surname')
|
||||
if given_name || surname
|
||||
user.name = "#{given_name} #{surname}"
|
||||
user.sortable_name = if given_name.present? && surname.present?
|
||||
"#{surname}, #{given_name}"
|
||||
else
|
||||
"#{given_name}#{surname}"
|
||||
end
|
||||
end
|
||||
|
||||
canvas_attributes.each do |(attribute, value)|
|
||||
case attribute
|
||||
when 'admin_roles'
|
||||
role_names = value.is_a?(String) ? value.split(',').map(&:strip) : value
|
||||
account = pseudonym.account
|
||||
existing_account_users = account.account_users.merge(user.account_users).preload(:role).to_a
|
||||
roles = role_names.map do |role_name|
|
||||
account.get_account_role_by_name(role_name)
|
||||
end.compact
|
||||
roles_to_add = roles - existing_account_users.map(&:role)
|
||||
account_users_to_delete = existing_account_users.select { |au| au.active? && !roles.include?(au.role) }
|
||||
account_users_to_activate = existing_account_users.select { |au| au.deleted? && roles.include?(au.role) }
|
||||
roles_to_add.each do |role|
|
||||
account.account_users.create!(user: user, role: role)
|
||||
end
|
||||
account_users_to_delete.each(&:destroy)
|
||||
account_users_to_activate.each(&:reactivate)
|
||||
when 'sis_user_id', 'integration_id'
|
||||
pseudonym[attribute] = value
|
||||
when 'display_name'
|
||||
user.short_name = value
|
||||
when 'email'
|
||||
cc = user.communication_channels.email.by_path(value).first
|
||||
cc ||= user.communication_channels.email.new(path: value)
|
||||
cc.workflow_state = 'active'
|
||||
cc.save! if cc.changed?
|
||||
when 'locale'
|
||||
# convert _ to -, be lenient about case, and perform fallbacks
|
||||
value = value.tr('_', '-')
|
||||
lowercase_locales = I18n.available_locales.map(&:to_s).map(&:downcase)
|
||||
while value.include?('-')
|
||||
break if lowercase_locales.include?(value.downcase)
|
||||
value = value.sub(/(?:x-)?-[^-]*$/, '')
|
||||
end
|
||||
if (i = lowercase_locales.index(value.downcase))
|
||||
user.locale = I18n.available_locales[i].to_s
|
||||
end
|
||||
else
|
||||
user.send("#{attribute}=", value)
|
||||
end
|
||||
end
|
||||
if pseudonym.changed?
|
||||
unless pseudonym.save
|
||||
Rails.logger.warn("Unable to save federated pseudonym: #{pseudonym.errors}")
|
||||
end
|
||||
end
|
||||
if user.changed?
|
||||
unless user.save
|
||||
Rails.logger.warn("Unable to save federated user: #{user.errors}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def statsd_prefix
|
||||
"auth.account_#{Shard.global_id_for(account_id)}.config_#{self.global_id}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_federated_attributes
|
||||
bad_keys = federated_attributes.keys - CANVAS_ALLOWED_FEDERATED_ATTRIBUTES
|
||||
unless bad_keys.empty?
|
||||
errors.add(:federated_attributes, "#{bad_keys.join(', ')} is not an attribute that can be federated")
|
||||
return
|
||||
end
|
||||
|
||||
# normalize values to { attribute: <attribute>, provisioning_only: true|false }
|
||||
federated_attributes.keys.each do |key|
|
||||
case federated_attributes[key]
|
||||
when String
|
||||
federated_attributes[key] = { 'attribute' => federated_attributes[key], 'provisioning_only' => false }
|
||||
when Hash
|
||||
bad_keys = federated_attributes[key].keys - ['attribute', 'provisioning_only']
|
||||
unless bad_keys.empty?
|
||||
errors.add(:federated_attributes, "unrecognized key #{bad_keys.join(', ')} in #{key} attribute definition")
|
||||
return
|
||||
end
|
||||
federated_attributes[key]['provisioning_only'] =
|
||||
::Canvas::Plugin.value_to_boolean(federated_attributes[key]['provisioning_only'])
|
||||
else
|
||||
errors.add(:federated_attributes, "invalid attribute definition for #{key}")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return if self.class.recognized_federated_attributes.nil?
|
||||
bad_values = federated_attributes.values.map { |v| v['attribute'] } - self.class.recognized_federated_attributes
|
||||
unless bad_values.empty?
|
||||
errors.add(:federated_attributes, "#{bad_values.join(', ')} is not a valid attribute")
|
||||
end
|
||||
end
|
||||
|
||||
def translate_provider_attributes(provider_attributes, purpose:)
|
||||
result = {}
|
||||
federated_attributes.each do |(canvas_attribute_name, provider_attribute_config)|
|
||||
next if purpose != :provisioning && provider_attribute_config['provisioning_only']
|
||||
provider_attribute_name = provider_attribute_config['attribute']
|
||||
|
||||
if provider_attributes.key?(provider_attribute_name)
|
||||
result[canvas_attribute_name] = provider_attributes[provider_attribute_name]
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def soft_delete_pseudonyms
|
||||
pseudonyms.find_each(&:destroy)
|
||||
end
|
||||
|
||||
def enable_canvas_authentication
|
||||
return if account.non_canvas_auth_configured?
|
||||
account.enable_canvas_authentication
|
||||
end
|
||||
end
|
||||
|
||||
# so it doesn't get mixed up with ::CAS, ::LinkedIn and ::Twitter
|
||||
require_dependency 'account_authorization_config/canvas'
|
||||
require_dependency 'account_authorization_config/cas'
|
||||
require_dependency 'account_authorization_config/google'
|
||||
require_dependency 'account_authorization_config/linked_in'
|
||||
require_dependency 'account_authorization_config/twitter'
|
||||
# backcompat
|
||||
AccountAuthorizationConfig = AuthenticationProvider
|
||||
|
|
|
@ -0,0 +1,366 @@
|
|||
#
|
||||
# Copyright (C) 2011 - present 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 'net-ldap'
|
||||
require 'net_ldap_extensions'
|
||||
|
||||
class AuthenticationProvider < ActiveRecord::Base
|
||||
include Workflow
|
||||
validates :auth_filter, length: {maximum: maximum_text_length, allow_nil: true, allow_blank: true}
|
||||
|
||||
workflow do
|
||||
state :active
|
||||
state :deleted
|
||||
end
|
||||
|
||||
self.inheritance_column = :auth_type
|
||||
# backcompat while authentication_providers might be a view
|
||||
self.primary_key = 'id'
|
||||
|
||||
def self.subclass_from_attributes?(_)
|
||||
false
|
||||
end
|
||||
|
||||
# we have a lot of old data that didn't actually use STI,
|
||||
# so we shim it
|
||||
def self.find_sti_class(type_name)
|
||||
return self if type_name.blank? # super no longer does this in Rails 4
|
||||
case type_name
|
||||
when 'cas', 'ldap', 'saml'
|
||||
const_get(type_name.upcase)
|
||||
when 'clever', 'facebook', 'google', 'microsoft', 'twitter'
|
||||
const_get(type_name.classify)
|
||||
when 'canvas'
|
||||
Canvas
|
||||
when 'github'
|
||||
GitHub
|
||||
when 'linkedin'
|
||||
LinkedIn
|
||||
when 'openid_connect'
|
||||
OpenIDConnect
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def self.sti_name
|
||||
display_name.try(:underscore)
|
||||
end
|
||||
|
||||
def self.singleton?
|
||||
false
|
||||
end
|
||||
|
||||
def self.enabled?
|
||||
true
|
||||
end
|
||||
|
||||
def self.display_name
|
||||
name.try(:demodulize)
|
||||
end
|
||||
|
||||
# Drop and recreate the authentication_providers view, if it exists.
|
||||
#
|
||||
# to be used from migrations that existed before the table rename. should
|
||||
# only be used from inside a transaction.
|
||||
def self.maybe_recreate_view
|
||||
if (view_exists = connection.view_exists?("authentication_providers"))
|
||||
connection.execute("DROP VIEW #{connection.quote_table_name('authentication_providers')}")
|
||||
end
|
||||
yield
|
||||
if view_exists
|
||||
connection.execute("CREATE VIEW #{connection.quote_table_name('authentication_providers')} AS SELECT * FROM #{connection.quote_table_name('account_authorization_configs')}")
|
||||
end
|
||||
end
|
||||
|
||||
scope :active, ->{ where("workflow_state <> 'deleted'") }
|
||||
belongs_to :account
|
||||
has_many :pseudonyms, foreign_key: :authentication_provider_id, inverse_of: :authentication_provider
|
||||
acts_as_list scope: { account: self, workflow_state: [nil, 'active'] }
|
||||
|
||||
VALID_AUTH_TYPES = %w[canvas cas clever facebook github google ldap linkedin microsoft openid_connect saml twitter].freeze
|
||||
validates :auth_type,
|
||||
inclusion: { in: VALID_AUTH_TYPES,
|
||||
message: "invalid auth_type, must be one of #{VALID_AUTH_TYPES.join(',')}" }
|
||||
validates :account_id, presence: true
|
||||
validate :validate_federated_attributes
|
||||
|
||||
# create associate model find to accept auth types, and just return the first one of that
|
||||
# type
|
||||
module FindWithType
|
||||
def find(*args)
|
||||
if VALID_AUTH_TYPES.include?(args.first)
|
||||
where(auth_type: args.first).first!
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.recognized_params
|
||||
[].freeze
|
||||
end
|
||||
|
||||
def self.deprecated_params
|
||||
[].freeze
|
||||
end
|
||||
|
||||
SENSITIVE_PARAMS = [].freeze
|
||||
|
||||
def self.login_button?
|
||||
Rails.root.join("public/images/sso_buttons/sso-#{sti_name}.svg").exist?
|
||||
end
|
||||
|
||||
def destroy
|
||||
self.send(:remove_from_list_for_destroy)
|
||||
self.workflow_state = 'deleted'
|
||||
self.save!
|
||||
enable_canvas_authentication
|
||||
send_later_if_production(:soft_delete_pseudonyms)
|
||||
true
|
||||
end
|
||||
alias destroy_permanently! destroy
|
||||
|
||||
def auth_password=(password)
|
||||
return if password.blank?
|
||||
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 auth_provider_filter
|
||||
self
|
||||
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
|
||||
|
||||
# allowable attributes for federated_attributes setting; nil means anything
|
||||
# is allowed
|
||||
def self.recognized_federated_attributes
|
||||
[].freeze
|
||||
end
|
||||
|
||||
def settings
|
||||
read_attribute(:settings) || {}
|
||||
end
|
||||
|
||||
def federated_attributes=(value)
|
||||
value = {} unless value.is_a?(Hash)
|
||||
settings_will_change! unless value == federated_attributes
|
||||
settings['federated_attributes'] = value
|
||||
end
|
||||
|
||||
def federated_attributes
|
||||
settings['federated_attributes'] ||= {}
|
||||
end
|
||||
|
||||
def federated_attributes_for_api
|
||||
if jit_provisioning?
|
||||
federated_attributes
|
||||
else
|
||||
result = {}
|
||||
federated_attributes.each do |(canvas_attribute_name, provider_attribute_config)|
|
||||
next if provider_attribute_config['provisioning_only']
|
||||
result[canvas_attribute_name] = provider_attribute_config['attribute']
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
CANVAS_ALLOWED_FEDERATED_ATTRIBUTES = %w{
|
||||
admin_roles
|
||||
display_name
|
||||
email
|
||||
given_name
|
||||
integration_id
|
||||
locale
|
||||
name
|
||||
sis_user_id
|
||||
sortable_name
|
||||
surname
|
||||
time_zone
|
||||
}.freeze
|
||||
|
||||
def provision_user(unique_id, provider_attributes = {})
|
||||
User.transaction(requires_new: true) do
|
||||
pseudonym = account.pseudonyms.build
|
||||
pseudonym.user = User.create!(name: unique_id) { |u| u.workflow_state = 'registered' }
|
||||
pseudonym.authentication_provider = self
|
||||
pseudonym.unique_id = unique_id
|
||||
pseudonym.save!
|
||||
apply_federated_attributes(pseudonym, provider_attributes, purpose: :provisioning)
|
||||
pseudonym
|
||||
end
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
uncached do
|
||||
pseudonyms.active.by_unique_id(unique_id).take!
|
||||
end
|
||||
end
|
||||
|
||||
def apply_federated_attributes(pseudonym, provider_attributes, purpose: :login)
|
||||
user = pseudonym.user
|
||||
|
||||
canvas_attributes = translate_provider_attributes(provider_attributes,
|
||||
purpose: purpose)
|
||||
given_name = canvas_attributes.delete('given_name')
|
||||
surname = canvas_attributes.delete('surname')
|
||||
if given_name || surname
|
||||
user.name = "#{given_name} #{surname}"
|
||||
user.sortable_name = if given_name.present? && surname.present?
|
||||
"#{surname}, #{given_name}"
|
||||
else
|
||||
"#{given_name}#{surname}"
|
||||
end
|
||||
end
|
||||
|
||||
canvas_attributes.each do |(attribute, value)|
|
||||
case attribute
|
||||
when 'admin_roles'
|
||||
role_names = value.is_a?(String) ? value.split(',').map(&:strip) : value
|
||||
account = pseudonym.account
|
||||
existing_account_users = account.account_users.merge(user.account_users).preload(:role).to_a
|
||||
roles = role_names.map do |role_name|
|
||||
account.get_account_role_by_name(role_name)
|
||||
end.compact
|
||||
roles_to_add = roles - existing_account_users.map(&:role)
|
||||
account_users_to_delete = existing_account_users.select { |au| au.active? && !roles.include?(au.role) }
|
||||
account_users_to_activate = existing_account_users.select { |au| au.deleted? && roles.include?(au.role) }
|
||||
roles_to_add.each do |role|
|
||||
account.account_users.create!(user: user, role: role)
|
||||
end
|
||||
account_users_to_delete.each(&:destroy)
|
||||
account_users_to_activate.each(&:reactivate)
|
||||
when 'sis_user_id', 'integration_id'
|
||||
pseudonym[attribute] = value
|
||||
when 'display_name'
|
||||
user.short_name = value
|
||||
when 'email'
|
||||
cc = user.communication_channels.email.by_path(value).first
|
||||
cc ||= user.communication_channels.email.new(path: value)
|
||||
cc.workflow_state = 'active'
|
||||
cc.save! if cc.changed?
|
||||
when 'locale'
|
||||
# convert _ to -, be lenient about case, and perform fallbacks
|
||||
value = value.tr('_', '-')
|
||||
lowercase_locales = I18n.available_locales.map(&:to_s).map(&:downcase)
|
||||
while value.include?('-')
|
||||
break if lowercase_locales.include?(value.downcase)
|
||||
value = value.sub(/(?:x-)?-[^-]*$/, '')
|
||||
end
|
||||
if (i = lowercase_locales.index(value.downcase))
|
||||
user.locale = I18n.available_locales[i].to_s
|
||||
end
|
||||
else
|
||||
user.send("#{attribute}=", value)
|
||||
end
|
||||
end
|
||||
if pseudonym.changed?
|
||||
unless pseudonym.save
|
||||
Rails.logger.warn("Unable to save federated pseudonym: #{pseudonym.errors}")
|
||||
end
|
||||
end
|
||||
if user.changed?
|
||||
unless user.save
|
||||
Rails.logger.warn("Unable to save federated user: #{user.errors}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def statsd_prefix
|
||||
"auth.account_#{Shard.global_id_for(account_id)}.config_#{self.global_id}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_federated_attributes
|
||||
bad_keys = federated_attributes.keys - CANVAS_ALLOWED_FEDERATED_ATTRIBUTES
|
||||
unless bad_keys.empty?
|
||||
errors.add(:federated_attributes, "#{bad_keys.join(', ')} is not an attribute that can be federated")
|
||||
return
|
||||
end
|
||||
|
||||
# normalize values to { attribute: <attribute>, provisioning_only: true|false }
|
||||
federated_attributes.each_key do |key|
|
||||
case federated_attributes[key]
|
||||
when String
|
||||
federated_attributes[key] = { 'attribute' => federated_attributes[key], 'provisioning_only' => false }
|
||||
when Hash
|
||||
bad_keys = federated_attributes[key].keys - ['attribute', 'provisioning_only']
|
||||
unless bad_keys.empty?
|
||||
errors.add(:federated_attributes, "unrecognized key #{bad_keys.join(', ')} in #{key} attribute definition")
|
||||
return
|
||||
end
|
||||
federated_attributes[key]['provisioning_only'] =
|
||||
::Canvas::Plugin.value_to_boolean(federated_attributes[key]['provisioning_only'])
|
||||
else
|
||||
errors.add(:federated_attributes, "invalid attribute definition for #{key}")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return if self.class.recognized_federated_attributes.nil?
|
||||
bad_values = federated_attributes.values.map { |v| v['attribute'] } - self.class.recognized_federated_attributes
|
||||
unless bad_values.empty?
|
||||
errors.add(:federated_attributes, "#{bad_values.join(', ')} is not a valid attribute")
|
||||
end
|
||||
end
|
||||
|
||||
def translate_provider_attributes(provider_attributes, purpose:)
|
||||
result = {}
|
||||
federated_attributes.each do |(canvas_attribute_name, provider_attribute_config)|
|
||||
next if purpose != :provisioning && provider_attribute_config['provisioning_only']
|
||||
provider_attribute_name = provider_attribute_config['attribute']
|
||||
|
||||
if provider_attributes.key?(provider_attribute_name)
|
||||
result[canvas_attribute_name] = provider_attributes[provider_attribute_name]
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def soft_delete_pseudonyms
|
||||
pseudonyms.find_each(&:destroy)
|
||||
end
|
||||
|
||||
def enable_canvas_authentication
|
||||
return if account.non_canvas_auth_configured?
|
||||
account.enable_canvas_authentication
|
||||
end
|
||||
end
|
||||
|
||||
# so it doesn't get mixed up with ::CAS, ::LinkedIn and ::Twitter
|
||||
require_dependency 'authentication_provider/canvas'
|
||||
require_dependency 'authentication_provider/cas'
|
||||
require_dependency 'authentication_provider/google'
|
||||
require_dependency 'authentication_provider/linked_in'
|
||||
require_dependency 'authentication_provider/twitter'
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::Canvas < AccountAuthorizationConfig
|
||||
class AuthenticationProvider::Canvas < AuthenticationProvider
|
||||
def self.sti_name
|
||||
'canvas'
|
||||
end
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
require 'casclient'
|
||||
|
||||
class AccountAuthorizationConfig::CAS < AccountAuthorizationConfig::Delegated
|
||||
class AuthenticationProvider::CAS < AuthenticationProvider::Delegated
|
||||
|
||||
def self.sti_name
|
||||
'cas'.freeze
|
|
@ -16,8 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::Clever < AccountAuthorizationConfig::Oauth2
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
class AuthenticationProvider::Clever < AuthenticationProvider::Oauth2
|
||||
include AuthenticationProvider::PluginSettings
|
||||
self.plugin = :clever
|
||||
plugin_settings :client_id, client_secret: :client_secret_dec
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::Delegated < AccountAuthorizationConfig
|
||||
class AuthenticationProvider::Delegated < AuthenticationProvider
|
||||
after_create :disable_open_registration
|
||||
|
||||
def disable_open_registration
|
||||
|
@ -32,8 +32,8 @@ class AccountAuthorizationConfig::Delegated < AccountAuthorizationConfig
|
|||
# Canvas or LDAP primary provider; go to the login url cause it won't
|
||||
# auto-log them back in
|
||||
primary_auth = account.authentication_providers.active.first
|
||||
if primary_auth.is_a?(AccountAuthorizationConfig::Canvas) ||
|
||||
primary_auth.is_a?(AccountAuthorizationConfig::LDAP)
|
||||
if primary_auth.is_a?(AuthenticationProvider::Canvas) ||
|
||||
primary_auth.is_a?(AuthenticationProvider::LDAP)
|
||||
return controller.login_url
|
||||
end
|
||||
# otherwise, just go to a landing page
|
|
@ -16,8 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::Facebook < AccountAuthorizationConfig::Oauth2
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
class AuthenticationProvider::Facebook < AuthenticationProvider::Oauth2
|
||||
include AuthenticationProvider::PluginSettings
|
||||
|
||||
self.plugin = :facebook
|
||||
plugin_settings :app_id, app_secret: :app_secret_dec
|
|
@ -16,8 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::GitHub < AccountAuthorizationConfig::Oauth2
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
class AuthenticationProvider::GitHub < AuthenticationProvider::Oauth2
|
||||
include AuthenticationProvider::PluginSettings
|
||||
self.plugin = :github
|
||||
plugin_settings :domain, :client_id, client_secret: :client_secret_dec
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::Google < AccountAuthorizationConfig::OpenIDConnect
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
class AuthenticationProvider::Google < AuthenticationProvider::OpenIDConnect
|
||||
include AuthenticationProvider::PluginSettings
|
||||
self.plugin = :google_drive
|
||||
plugin_settings :client_id, client_secret: :client_secret_dec
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::LDAP < AccountAuthorizationConfig
|
||||
class AuthenticationProvider::LDAP < AuthenticationProvider
|
||||
def self.sti_name
|
||||
'ldap'.freeze
|
||||
end
|
||||
|
@ -156,10 +156,10 @@ class AccountAuthorizationConfig::LDAP < AccountAuthorizationConfig
|
|||
rescue Timeout::Error
|
||||
self.errors.add(:ldap_bind_test, t("Timeout when searching"))
|
||||
return false
|
||||
rescue
|
||||
rescue => e
|
||||
self.errors.add(
|
||||
:ldap_search_test,
|
||||
t(:test_search_failed, "Search failed with the following error: %{error}", :error => $!)
|
||||
t(:test_search_failed, "Search failed with the following error: %{error}", :error => e)
|
||||
)
|
||||
return false
|
||||
end
|
||||
|
@ -174,10 +174,10 @@ class AccountAuthorizationConfig::LDAP < AccountAuthorizationConfig
|
|||
:ldap_login_test,
|
||||
t(:test_login_auth_failed, "Authentication failed")
|
||||
)
|
||||
rescue Net::LDAP::LdapError
|
||||
rescue Net::LDAP::LdapError => e
|
||||
self.errors.add(
|
||||
:ldap_login_test,
|
||||
t(:test_login_auth_exception, "Exception on login: %{error}", :error => $!)
|
||||
t(:test_login_auth_exception, "Exception on login: %{error}", :error => e)
|
||||
)
|
||||
end
|
||||
false
|
|
@ -16,8 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::LinkedIn < AccountAuthorizationConfig::Oauth2
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
class AuthenticationProvider::LinkedIn < AuthenticationProvider::Oauth2
|
||||
include AuthenticationProvider::PluginSettings
|
||||
self.plugin = :linked_in
|
||||
plugin_settings :client_id, client_secret: :client_secret_dec
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::Microsoft < AccountAuthorizationConfig::OpenIDConnect
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
class AuthenticationProvider::Microsoft < AuthenticationProvider::OpenIDConnect
|
||||
include AuthenticationProvider::PluginSettings
|
||||
self.plugin = :microsoft
|
||||
plugin_settings :application_id, application_secret: :application_secret_dec
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
require 'oauth2'
|
||||
require 'canvas/core_ext/oauth2'
|
||||
|
||||
class AccountAuthorizationConfig::Oauth < AccountAuthorizationConfig::Delegated
|
||||
class AuthenticationProvider::Oauth < AuthenticationProvider::Delegated
|
||||
|
||||
SENSITIVE_PARAMS = [ :consumer_secret ].freeze
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
require 'oauth2'
|
||||
require 'canvas/core_ext/oauth2'
|
||||
|
||||
class AccountAuthorizationConfig::Oauth2 < AccountAuthorizationConfig::Delegated
|
||||
class AuthenticationProvider::Oauth2 < AuthenticationProvider::Delegated
|
||||
|
||||
SENSITIVE_PARAMS = [ :client_secret ].freeze
|
||||
|
||||
|
@ -43,7 +43,7 @@ class AccountAuthorizationConfig::Oauth2 < AccountAuthorizationConfig::Delegated
|
|||
client.auth_code.get_token(code, { redirect_uri: redirect_uri }.merge(token_options))
|
||||
end
|
||||
|
||||
def provider_attributes(token)
|
||||
def provider_attributes(_token)
|
||||
{}
|
||||
end
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::OpenIDConnect < AccountAuthorizationConfig::Oauth2
|
||||
class AuthenticationProvider::OpenIDConnect < AuthenticationProvider::Oauth2
|
||||
def self.sti_name
|
||||
self == OpenIDConnect ? 'openid_connect'.freeze : super
|
||||
end
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module AccountAuthorizationConfig::PluginSettings
|
||||
module AuthenticationProvider::PluginSettings
|
||||
module ClassMethods
|
||||
def singleton?
|
||||
true
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
require 'saml2'
|
||||
|
||||
class AccountAuthorizationConfig::SAML < AccountAuthorizationConfig::Delegated
|
||||
class AuthenticationProvider::SAML < AuthenticationProvider::Delegated
|
||||
def self.sti_name
|
||||
'saml'.freeze
|
||||
end
|
||||
|
@ -35,7 +35,8 @@ class AccountAuthorizationConfig::SAML < AccountAuthorizationConfig::Delegated
|
|||
end
|
||||
|
||||
def self.recognized_params
|
||||
[ :log_in_url,
|
||||
[
|
||||
:log_in_url,
|
||||
:log_out_url,
|
||||
:requested_authn_context,
|
||||
:certificate_fingerprint,
|
||||
|
@ -81,33 +82,33 @@ class AccountAuthorizationConfig::SAML < AccountAuthorizationConfig::Delegated
|
|||
end
|
||||
|
||||
def download_metadata
|
||||
return unless metadata_uri.present?
|
||||
return if metadata_uri.blank?
|
||||
return unless metadata_uri_changed? || idp_entity_id_changed?
|
||||
|
||||
Federation.descendants.each do |federation|
|
||||
# someone's trying to cheat; switch to our more efficient implementation
|
||||
self.metadata_uri = federation::URN if metadata_uri == federation.endpoint
|
||||
|
||||
if metadata_uri == federation::URN
|
||||
unless idp_entity_id.present?
|
||||
errors.add(:idp_entity_id, :present)
|
||||
return
|
||||
end
|
||||
next unless metadata_uri == federation::URN
|
||||
|
||||
begin
|
||||
entity = federation.metadata[idp_entity_id]
|
||||
unless entity
|
||||
errors.add(:idp_entity_id, t("Entity %{entity_id} not found in %{federation_name} Metadata",
|
||||
entity_id: idp_entity_id, federation_name: federation.class_name))
|
||||
return
|
||||
end
|
||||
populate_from_metadata(entity)
|
||||
rescue => e
|
||||
::Canvas::Errors.capture_exception(:saml_federation, e)
|
||||
errors.add(:metadata_uri, e.message)
|
||||
end
|
||||
if idp_entity_id.blank?
|
||||
errors.add(:idp_entity_id, :present)
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
entity = federation.metadata[idp_entity_id]
|
||||
unless entity
|
||||
errors.add(:idp_entity_id, t("Entity %{entity_id} not found in %{federation_name} Metadata",
|
||||
entity_id: idp_entity_id, federation_name: federation.class_name))
|
||||
return
|
||||
end
|
||||
populate_from_metadata(entity)
|
||||
rescue => e
|
||||
::Canvas::Errors.capture_exception(:saml_federation, e)
|
||||
errors.add(:metadata_uri, e.message)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
|
@ -127,7 +128,7 @@ class AccountAuthorizationConfig::SAML < AccountAuthorizationConfig::Delegated
|
|||
end
|
||||
|
||||
def self.saml_default_entity_id_for_account(account)
|
||||
if !account.settings[:saml_entity_id]
|
||||
unless account.settings[:saml_entity_id]
|
||||
account.settings[:saml_entity_id] = "http://#{HostUrl.context_host(account)}/saml2"
|
||||
account.save!
|
||||
end
|
||||
|
@ -180,8 +181,8 @@ class AccountAuthorizationConfig::SAML < AccountAuthorizationConfig::Delegated
|
|||
self.sig_alg ||= 'RSA-SHA1'
|
||||
when false
|
||||
self.sig_alg = nil
|
||||
# else nil
|
||||
# don't change the user settings
|
||||
# else nil
|
||||
# don't change the user settings
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -194,7 +195,7 @@ class AccountAuthorizationConfig::SAML < AccountAuthorizationConfig::Delegated
|
|||
raise "Must be a single Entity" unless entity.is_a?(SAML2::Entity)
|
||||
populate_from_metadata(entity)
|
||||
end
|
||||
alias_method :metadata=, :populate_from_metadata_xml
|
||||
alias metadata= populate_from_metadata_xml
|
||||
|
||||
def populate_from_metadata_url(url)
|
||||
::Canvas.timeout_protection("saml_metadata_fetch") do
|
||||
|
@ -446,11 +447,10 @@ class AccountAuthorizationConfig::SAML < AccountAuthorizationConfig::Delegated
|
|||
session[:name_identifier_format],
|
||||
name_qualifier: session[:name_qualifier],
|
||||
sp_name_qualifier: session[:sp_name_qualifier]),
|
||||
session[:session_index]
|
||||
)
|
||||
session[:session_index])
|
||||
|
||||
# sign the response
|
||||
private_key = AccountAuthorizationConfig::SAML.private_key
|
||||
private_key = AuthenticationProvider::SAML.private_key
|
||||
private_key = nil if sig_alg.nil?
|
||||
result = SAML2::Bindings::HTTPRedirect.encode(logout_request,
|
||||
private_key: private_key,
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
require 'saml2'
|
||||
|
||||
class AccountAuthorizationConfig::SAML::Federation < AccountAuthorizationConfig::SAML::MetadataRefresher
|
||||
class AuthenticationProvider::SAML::Federation < AuthenticationProvider::SAML::MetadataRefresher
|
||||
class << self
|
||||
def metadata
|
||||
Shard.default.activate do
|
||||
|
@ -32,7 +32,7 @@ class AccountAuthorizationConfig::SAML::Federation < AccountAuthorizationConfig:
|
|||
end
|
||||
|
||||
def refresh_providers(shard_scope: Shard.in_current_region, providers: nil)
|
||||
providers ||= AccountAuthorizationConfig::SAML.active.
|
||||
providers ||= AuthenticationProvider::SAML.active.
|
||||
where(metadata_uri: self::URN).shard(shard_scope)
|
||||
|
||||
# don't even bother checking the federation if no one is using it
|
||||
|
@ -94,5 +94,5 @@ end
|
|||
|
||||
# make sure to force these to eager load, otherwise we may try to iterate
|
||||
# all federations, but there won't be any
|
||||
require_dependency 'account_authorization_config/saml/in_common'
|
||||
require_dependency 'account_authorization_config/saml/uk_federation'
|
||||
require_dependency 'authentication_provider/saml/in_common'
|
||||
require_dependency 'authentication_provider/saml/uk_federation'
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
require 'saml2'
|
||||
|
||||
class AccountAuthorizationConfig::SAML::InCommon < AccountAuthorizationConfig::SAML::Federation
|
||||
class AuthenticationProvider::SAML::InCommon < AuthenticationProvider::SAML::Federation
|
||||
URN = 'urn:mace:incommon'.freeze
|
||||
|
||||
class << self
|
||||
|
@ -29,7 +29,7 @@ class AccountAuthorizationConfig::SAML::InCommon < AccountAuthorizationConfig::S
|
|||
protected
|
||||
|
||||
def cert
|
||||
Rails.root.join("config/saml/inc-md-cert.pem").read
|
||||
Rails.root.join("config", "saml", "inc-md-cert.pem").read
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,13 +18,13 @@
|
|||
|
||||
require 'saml2'
|
||||
|
||||
class AccountAuthorizationConfig::SAML::MetadataRefresher
|
||||
class AuthenticationProvider::SAML::MetadataRefresher
|
||||
class << self
|
||||
def refresh_providers(shard_scope: Shard.current, providers: nil)
|
||||
federations = AccountAuthorizationConfig::SAML::Federation.descendants.map { |federation| federation::URN }
|
||||
providers ||= AccountAuthorizationConfig::SAML.active.
|
||||
where.not(metadata_uri: [nil, ""] + federations).
|
||||
shard(shard_scope)
|
||||
federations = AuthenticationProvider::SAML::Federation.descendants.map { |federation| federation::URN }
|
||||
providers ||= AuthenticationProvider::SAML.active.
|
||||
where.not(metadata_uri: [nil, ""] + federations).
|
||||
shard(shard_scope)
|
||||
|
||||
providers.each do |provider|
|
||||
begin
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
require 'saml2'
|
||||
|
||||
class AccountAuthorizationConfig::SAML::UKFederation < AccountAuthorizationConfig::SAML::Federation
|
||||
class AuthenticationProvider::SAML::UKFederation < AuthenticationProvider::SAML::Federation
|
||||
URN = 'http://ukfederation.org.uk'.freeze
|
||||
|
||||
class << self
|
||||
|
@ -29,7 +29,7 @@ class AccountAuthorizationConfig::SAML::UKFederation < AccountAuthorizationConfi
|
|||
protected
|
||||
|
||||
def cert
|
||||
Rails.root.join("config/saml/ukfederation.pem").read
|
||||
Rails.root.join("config", "saml", "ukfederation.pem").read
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,8 +16,8 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
class AccountAuthorizationConfig::Twitter < AccountAuthorizationConfig::Oauth
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
class AuthenticationProvider::Twitter < AuthenticationProvider::Oauth
|
||||
include AuthenticationProvider::PluginSettings
|
||||
self.plugin = :twitter
|
||||
plugin_settings :consumer_key, consumer_secret: :consumer_secret_dec
|
||||
|
|
@ -26,10 +26,10 @@ class Pseudonym < ActiveRecord::Base
|
|||
has_many :sis_enrollments, class_name: 'Enrollment', inverse_of: :sis_pseudonym
|
||||
belongs_to :communication_channel
|
||||
belongs_to :sis_communication_channel, :class_name => 'CommunicationChannel'
|
||||
belongs_to :authentication_provider, class_name: 'AccountAuthorizationConfig'
|
||||
belongs_to :authentication_provider
|
||||
MAX_UNIQUE_ID_LENGTH = 100
|
||||
|
||||
CAS_TICKET_EXPIRED = 'expired'
|
||||
CAS_TICKET_EXPIRED = 'expired'.freeze
|
||||
CAS_TICKET_TTL = 1.day
|
||||
|
||||
validates_length_of :unique_id, :maximum => MAX_UNIQUE_ID_LENGTH
|
||||
|
@ -136,8 +136,8 @@ class Pseudonym < ActiveRecord::Base
|
|||
def self.custom_find_by_unique_id(unique_id)
|
||||
return unless unique_id
|
||||
active.by_unique_id(unique_id).where("authentication_provider_id IS NULL OR EXISTS (?)",
|
||||
AccountAuthorizationConfig.active.where(auth_type: ['canvas', 'ldap']).
|
||||
where("authentication_provider_id=account_authorization_configs.id")).first
|
||||
AuthenticationProvider.active.where(auth_type: ['canvas', 'ldap']).
|
||||
where("authentication_provider_id=authentication_providers.id")).first
|
||||
end
|
||||
|
||||
def self.for_auth_configuration(unique_id, aac)
|
||||
|
@ -359,7 +359,7 @@ class Pseudonym < ActiveRecord::Base
|
|||
def managed_password?
|
||||
if authentication_provider
|
||||
# explicit provider we can be sure if it's managed or not
|
||||
!authentication_provider.is_a?(AccountAuthorizationConfig::Canvas)
|
||||
!authentication_provider.is_a?(AuthenticationProvider::Canvas)
|
||||
else
|
||||
# otherwise we have to guess
|
||||
!!(self.sis_user_id && account.non_canvas_auth_configured?)
|
||||
|
@ -367,7 +367,7 @@ class Pseudonym < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def passwordable?
|
||||
authentication_provider.is_a?(AccountAuthorizationConfig::Canvas) ||
|
||||
authentication_provider.is_a?(AuthenticationProvider::Canvas) ||
|
||||
(!authentication_provider && account.canvas_authentication?)
|
||||
end
|
||||
|
||||
|
@ -422,14 +422,14 @@ class Pseudonym < ActiveRecord::Base
|
|||
|
||||
def ldap_bind_result(password_plaintext)
|
||||
aps = case authentication_provider
|
||||
when AccountAuthorizationConfig::LDAP
|
||||
when AuthenticationProvider::LDAP
|
||||
[authentication_provider]
|
||||
when nil
|
||||
account.authentication_providers.active.where(auth_type: 'ldap')
|
||||
#when AccountAuthorizationConfig::Canvas
|
||||
# when AuthenticationProvider::Canvas
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
aps.each do |config|
|
||||
res = config.ldap_bind_result(self.unique_id, password_plaintext)
|
||||
return res if res
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# 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/>.
|
||||
|
||||
class AccountAuthorizationConfigsPresenter
|
||||
class AuthenticationProvidersPresenter
|
||||
include ActionView::Helpers::FormTagHelper
|
||||
include ActionView::Helpers::FormOptionsHelper
|
||||
|
||||
|
@ -30,8 +30,8 @@ class AccountAuthorizationConfigsPresenter
|
|||
end
|
||||
|
||||
def new_auth_types
|
||||
AccountAuthorizationConfig::VALID_AUTH_TYPES.map do |auth_type|
|
||||
klass = AccountAuthorizationConfig.find_sti_class(auth_type)
|
||||
AuthenticationProvider::VALID_AUTH_TYPES.map do |auth_type|
|
||||
klass = AuthenticationProvider.find_sti_class(auth_type)
|
||||
next unless klass.enabled?
|
||||
next if klass.singleton? && configs.any? { |aac| aac.is_a?(klass) }
|
||||
klass
|
||||
|
@ -39,12 +39,12 @@ class AccountAuthorizationConfigsPresenter
|
|||
end
|
||||
|
||||
def needs_unknown_user_url?
|
||||
configs.any? { |c| c.is_a?(AccountAuthorizationConfig::Delegated) }
|
||||
configs.any? { |c| c.is_a?(AuthenticationProvider::Delegated) }
|
||||
end
|
||||
|
||||
def login_url_options(aac)
|
||||
options = { controller: "login/#{aac.auth_type}", action: :new }
|
||||
if !aac.is_a?(AccountAuthorizationConfig::LDAP) &&
|
||||
if !aac.is_a?(AuthenticationProvider::LDAP) &&
|
||||
configs.many? { |other| other.auth_type == aac.auth_type }
|
||||
options[:id] = aac
|
||||
end
|
||||
|
@ -64,18 +64,17 @@ class AccountAuthorizationConfigsPresenter
|
|||
end
|
||||
|
||||
def ldap_configs
|
||||
configs.select{|c| c.is_a?(AccountAuthorizationConfig::LDAP) }
|
||||
configs.select{|c| c.is_a?(AuthenticationProvider::LDAP) }
|
||||
end
|
||||
|
||||
def saml_configs
|
||||
configs.select{|c| c.is_a?(AccountAuthorizationConfig::SAML) }
|
||||
configs.select{|c| c.is_a?(AuthenticationProvider::SAML) }
|
||||
end
|
||||
|
||||
def cas_configs
|
||||
configs.select{|c| c.is_a?(AccountAuthorizationConfig::CAS) }
|
||||
configs.select{|c| c.is_a?(AuthenticationProvider::CAS) }
|
||||
end
|
||||
|
||||
|
||||
def sso_options
|
||||
new_auth_types.map do |auth_type|
|
||||
{
|
||||
|
@ -118,11 +117,11 @@ class AccountAuthorizationConfigsPresenter
|
|||
end
|
||||
|
||||
def saml_enabled?
|
||||
AccountAuthorizationConfig::SAML.enabled?
|
||||
AuthenticationProvider::SAML.enabled?
|
||||
end
|
||||
|
||||
def login_placeholder
|
||||
AccountAuthorizationConfig.default_delegated_login_handle_name
|
||||
AuthenticationProvider.default_delegated_login_handle_name
|
||||
end
|
||||
|
||||
def login_name
|
||||
|
@ -130,7 +129,7 @@ class AccountAuthorizationConfigsPresenter
|
|||
end
|
||||
|
||||
def new_config(auth_type)
|
||||
account.authentication_providers.new(auth_type)
|
||||
AuthenticationProvider.new(auth_type: auth_type, account: account)
|
||||
end
|
||||
|
||||
def parent_reg_selected
|
||||
|
@ -138,7 +137,7 @@ class AccountAuthorizationConfigsPresenter
|
|||
end
|
||||
|
||||
def available_federated_attributes(aac)
|
||||
AccountAuthorizationConfig::CANVAS_ALLOWED_FEDERATED_ATTRIBUTES - aac.federated_attributes.keys
|
||||
AuthenticationProvider::CANVAS_ALLOWED_FEDERATED_ATTRIBUTES - aac.federated_attributes.keys
|
||||
end
|
||||
|
||||
def federated_provider_attribute(aac, canvas_attribute = nil, selected = nil)
|
|
@ -144,7 +144,7 @@
|
|||
</tr>
|
||||
|
||||
<% unless @account.non_canvas_auth_configured?
|
||||
# if the account has a account_authorization_config,
|
||||
# if the account has a authentication_provider,
|
||||
# we'll control this setting from that page
|
||||
%>
|
||||
<tr>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
%>
|
||||
|
||||
<% unless AccountAuthorizationConfig::Clever.globally_configured? %>
|
||||
<% unless AuthenticationProvider::Clever.globally_configured? %>
|
||||
<p><%= mt(<<-TEXT, clever_url: "http://apps.clever.com/signup", callback_url: clever_callback_url)
|
||||
You will need to [register an application on Clever](%{clever_url}).
|
||||
You should configure %{callback_url} as the Redirect URL.
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
%>
|
||||
|
||||
<% unless AccountAuthorizationConfig::Facebook.globally_configured? %>
|
||||
<% unless AuthenticationProvider::Facebook.globally_configured? %>
|
||||
<p><%= mt(<<-TEXT, facebook_url: "https://developers.facebook.com/", callback_url: oauth2_login_callback_url)
|
||||
You will need to create a [new app on Facebook](%{facebook_url}), and enable Client OAuth Login
|
||||
in Advanced Settings. You should configure %{callback_url} as the Valid OAuth redirect URI.
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
%>
|
||||
|
||||
<% unless AccountAuthorizationConfig::GitHub.globally_configured? %>
|
||||
<% unless AuthenticationProvider::GitHub.globally_configured? %>
|
||||
<p><%= mt(<<-TEXT, github_url: "https://github.com/settings/developers", callback_url: oauth2_login_callback_url)
|
||||
You will need to [register an application on GitHub](%{github_url}).
|
||||
You should configure %{callback_url} as the Authorization callback URL.
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
%>
|
||||
|
||||
<% unless AccountAuthorizationConfig::Google.globally_configured? %>
|
||||
<% unless AuthenticationProvider::Google.globally_configured? %>
|
||||
<p><%= mt(<<-TEXT, google_url: "https://console.developers.google.com/project", callback_url: oauth2_login_callback_url)
|
||||
You will need to create a web application in the [Google Developer Console](%{google_url}).
|
||||
You should add %{callback_url} as a redirect URI.
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
%>
|
||||
|
||||
<% unless AccountAuthorizationConfig::LinkedIn.globally_configured? %>
|
||||
<% unless AuthenticationProvider::LinkedIn.globally_configured? %>
|
||||
<p><%= mt(:description, <<-TEXT, linkedin_url: 'https://www.linkedin.com/secure/developer', callback_url: oauth2_login_callback_url)
|
||||
You will need to [register an application on LinkedIn](%{linkedin_url}).
|
||||
You should add %{callback_url} as an OAuth 2.0 authorized redirect URL, and set the Application Status to Live
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
%>
|
||||
|
||||
<% unless AccountAuthorizationConfig::Microsoft.globally_configured? %>
|
||||
<% unless AuthenticationProvider::Microsoft.globally_configured? %>
|
||||
<p><%= mt(<<-TEXT, ms_app_url: "https://apps.dev.microsoft.com/", callback_url: oauth2_login_callback_url)
|
||||
You will need to register a [new app with Microsoft](%{ms_app_url}).
|
||||
You should configure %{callback_url} as the Redirect URI.
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
%>
|
||||
|
||||
<% unless AccountAuthorizationConfig::Twitter.globally_configured? %>
|
||||
<% unless AuthenticationProvider::Twitter.globally_configured? %>
|
||||
<p><%= mt(<<-TEXT, twitter_url: "https://apps.twitter.com/", callback_url: oauth_login_callback_url)
|
||||
You will need to [register an application on Twitter](%{twitter_url}).
|
||||
You should configure %{callback_url} as the Callback URL.
|
|
@ -82,7 +82,6 @@
|
|||
|
||||
<h2><%= t("Current Provider") %></h2>
|
||||
|
||||
|
||||
<% if @presenter.configs.empty? %>
|
||||
<div id="no_auth">
|
||||
<%= t(:no_auth_type_description, "This account does not currently integrate "\
|
||||
|
@ -100,7 +99,7 @@
|
|||
locals: {
|
||||
account: @account,
|
||||
presenter: @presenter,
|
||||
aac: @presenter.new_config(auth_type: auth_type.sti_name)
|
||||
aac: @presenter.new_config(auth_type.sti_name)
|
||||
} %>
|
||||
<% end %>
|
||||
|
||||
|
@ -114,4 +113,4 @@
|
|||
locals: { account: @account, presenter: @presenter } %>
|
||||
<% end %>
|
||||
|
||||
<% js_bundle :account_authorization_configs %>
|
||||
<% js_bundle :authentication_providers %>
|
|
@ -1603,3 +1603,17 @@ if CANVAS_RAILS5_1
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# fake Rails into grabbing correct column information for a table rename in-progress
|
||||
module TableRename
|
||||
RENAMES = { 'authentication_providers' => 'account_authorization_configs' }.freeze
|
||||
|
||||
def columns(table_name)
|
||||
if (old_name = RENAMES[table_name])
|
||||
table_name = old_name if connection.table_exists?(old_name)
|
||||
end
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::ConnectionAdapters::SchemaCache.prepend(TableRename)
|
||||
|
|
|
@ -173,17 +173,17 @@ Rails.configuration.after_initialize do
|
|||
with_each_shard_by_database(Version::Partitioner, :process)
|
||||
end
|
||||
|
||||
if AccountAuthorizationConfig::SAML.enabled?
|
||||
Delayed::Periodic.cron 'AccountAuthorizationConfig::SAML::MetadataRefresher.refresh_providers', '15 0 * * *' do
|
||||
with_each_shard_by_database(AccountAuthorizationConfig::SAML::MetadataRefresher,
|
||||
if AuthenticationProvider::SAML.enabled?
|
||||
Delayed::Periodic.cron 'AuthenticationProvider::SAML::MetadataRefresher.refresh_providers', '15 0 * * *' do
|
||||
with_each_shard_by_database(AuthenticationProvider::SAML::MetadataRefresher,
|
||||
:refresh_providers)
|
||||
end
|
||||
|
||||
AccountAuthorizationConfig::SAML::Federation.descendants.each do |federation|
|
||||
Delayed::Periodic.cron "AccountAuthorizationConfig::SAML::#{federation.class_name}.refresh_providers", '45 0 * * *' do
|
||||
AuthenticationProvider::SAML::Federation.descendants.each do |federation|
|
||||
Delayed::Periodic.cron "AuthenticationProvider::SAML::#{federation.class_name}.refresh_providers", '45 0 * * *' do
|
||||
DatabaseServer.send_in_each_region(federation,
|
||||
:refresh_providers,
|
||||
singleton: "AccountAuthorizationConfig::SAML::#{federation.class_name}.refresh_providers")
|
||||
singleton: "AuthenticationProvider::SAML::#{federation.class_name}.refresh_providers")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -599,17 +599,17 @@ CanvasRails::Application.routes.draw do
|
|||
resources :account_notifications, only: [:create, :update, :destroy]
|
||||
concerns :announcements
|
||||
resources :submissions
|
||||
delete 'authentication_providers' => 'account_authorization_configs#destroy_all', as: :remove_all_authentication_providers
|
||||
put 'sso_settings' => 'account_authorization_configs#update_sso_settings',
|
||||
delete 'authentication_providers' => 'authentication_providers#destroy_all', as: :remove_all_authentication_providers
|
||||
put 'sso_settings' => 'authentication_providers#update_sso_settings',
|
||||
as: :update_sso_settings
|
||||
|
||||
resources :authentication_providers, controller: :account_authorization_configs, only: [:index, :create, :update, :destroy]
|
||||
get 'test_ldap_connections' => 'account_authorization_configs#test_ldap_connection'
|
||||
get 'test_ldap_binds' => 'account_authorization_configs#test_ldap_bind'
|
||||
get 'test_ldap_searches' => 'account_authorization_configs#test_ldap_search'
|
||||
match 'test_ldap_logins' => 'account_authorization_configs#test_ldap_login', via: [:get, :post]
|
||||
get 'saml_testing' => 'account_authorization_configs#saml_testing'
|
||||
get 'saml_testing_stop' => 'account_authorization_configs#saml_testing_stop'
|
||||
resources :authentication_providers, only: [:index, :create, :update, :destroy]
|
||||
get 'test_ldap_connections' => 'authentication_providers#test_ldap_connection'
|
||||
get 'test_ldap_binds' => 'authentication_providers#test_ldap_bind'
|
||||
get 'test_ldap_searches' => 'authentication_providers#test_ldap_search'
|
||||
match 'test_ldap_logins' => 'authentication_providers#test_ldap_login', via: [:get, :post]
|
||||
get 'saml_testing' => 'authentication_providers#saml_testing'
|
||||
get 'saml_testing_stop' => 'authentication_providers#saml_testing_stop'
|
||||
|
||||
get 'external_tools/sessionless_launch' => 'external_tools#sessionless_launch'
|
||||
resources :external_tools do
|
||||
|
@ -1364,7 +1364,7 @@ CanvasRails::Application.routes.draw do
|
|||
get 'accounts/:account_id/admins', action: :index, as: 'account_admins'
|
||||
end
|
||||
|
||||
scope(controller: :account_authorization_configs) do
|
||||
scope(controller: :authentication_providers) do
|
||||
get 'accounts/:account_id/sso_settings', action: :show_sso_settings, as: 'account_show_sso_settings_url'
|
||||
put 'accounts/:account_id/sso_settings', action: :update_sso_settings, as: 'account_update_sso_settings_url'
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class DisableOpenRegistrationForDelegatedAuth < ActiveRecord::Migration[4.2]
|
|||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
scope = Account.root_accounts.joins(:authentication_providers).readonly(false)
|
||||
scope = Account.root_accounts.joins("INNER JOIN #{connection.quote_table_name('account_authorization_configs')} ON account_id=accounts.id").readonly(false)
|
||||
scope.where('account_authorization_configs.auth_type' => ['cas', 'saml']).each do |account|
|
||||
account.settings = { :open_registration => false }
|
||||
account.save!
|
||||
|
|
|
@ -18,15 +18,19 @@
|
|||
class AddSamlRequestedAuthnContext < ActiveRecord::Migration[4.2]
|
||||
tag :predeploy
|
||||
|
||||
class AuthenticationProvider < ActiveRecord::Base
|
||||
self.table_name = 'account_authorization_configs'
|
||||
end
|
||||
|
||||
def self.up
|
||||
add_column :account_authorization_configs, :requested_authn_context, :string
|
||||
|
||||
AccountAuthorizationConfig.where(auth_type: "saml").each do |aac|
|
||||
AuthenticationProvider.where(auth_type: "saml").each do |aac|
|
||||
# This was the hard-coded value before
|
||||
aac.requested_authn_context = Onelogin::Saml::AuthnContexts::PASSWORD_PROTECTED_TRANSPORT
|
||||
aac.save!
|
||||
end
|
||||
AccountAuthorizationConfig.reset_column_information
|
||||
AuthenticationProvider.reset_column_information
|
||||
end
|
||||
|
||||
def self.down
|
||||
|
|
|
@ -21,21 +21,14 @@ class AddSamlProperties < ActiveRecord::Migration[4.2]
|
|||
def self.up
|
||||
add_column :account_authorization_configs, :idp_entity_id, :string
|
||||
add_column :account_authorization_configs, :position, :integer
|
||||
if connection.adapter_name =~ /postgres/i
|
||||
update <<-SQL
|
||||
UPDATE #{AccountAuthorizationConfig.quoted_table_name} aac
|
||||
SET position =
|
||||
CASE WHEN (SELECT count(*) FROM #{AccountAuthorizationConfig.quoted_table_name} WHERE account_id = aac.account_id) > 1
|
||||
THEN aac.id
|
||||
ELSE 1
|
||||
END;
|
||||
SQL
|
||||
else
|
||||
update <<-SQL
|
||||
UPDATE #{AccountAuthorizationConfig.quoted_table_name}
|
||||
SET position = account_authorization_configs.id;
|
||||
SQL
|
||||
end
|
||||
update <<-SQL
|
||||
UPDATE #{connection.quote_table_name('account_authorization_configs')} aac
|
||||
SET position =
|
||||
CASE WHEN (SELECT count(*) FROM #{connection.quote_table_name('account_authorization_configs')} WHERE account_id = aac.account_id) > 1
|
||||
THEN aac.id
|
||||
ELSE 1
|
||||
END;
|
||||
SQL
|
||||
end
|
||||
|
||||
def self.down
|
||||
|
|
|
@ -19,8 +19,10 @@ class DropLdapAuthSettingsFromAacs < ActiveRecord::Migration[4.2]
|
|||
tag :postdeploy
|
||||
|
||||
def up
|
||||
remove_column :account_authorization_configs, :login_handle_name
|
||||
remove_column :account_authorization_configs, :change_password_url
|
||||
AuthenticationProvider.maybe_recreate_view do
|
||||
remove_column :account_authorization_configs, :login_handle_name
|
||||
remove_column :account_authorization_configs, :change_password_url
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
@ -18,10 +18,16 @@
|
|||
class PopulateUnknownUserUrl < ActiveRecord::Migration[4.2]
|
||||
tag :predeploy
|
||||
|
||||
class AuthenticationProvider < ActiveRecord::Base
|
||||
self.table_name = 'account_authorization_configs'
|
||||
|
||||
belongs_to :account
|
||||
end
|
||||
|
||||
def up
|
||||
AccountAuthorizationConfig.select("*, unknown_user_url AS uuu").find_each do |aac|
|
||||
AuthenticationProvider.select("*, unknown_user_url AS uuu").find_each do |aac|
|
||||
account = aac.account
|
||||
if !account.unknown_user_url.present? && aac['uuu'].present?
|
||||
if account.unknown_user_url.blank? && aac['uuu'].present?
|
||||
account.unknown_user_url = aac['uuu']
|
||||
account.save!
|
||||
end
|
||||
|
|
|
@ -19,7 +19,9 @@ class DropUnknownUserUrlFromAccountAuthorizationConfigs < ActiveRecord::Migratio
|
|||
tag :postdeploy
|
||||
|
||||
def up
|
||||
remove_column :account_authorization_configs, :unknown_user_url
|
||||
AuthenticationProvider.maybe_recreate_view do
|
||||
remove_column :account_authorization_configs, :unknown_user_url
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
@ -20,7 +20,7 @@ class GrandfatherCanvasAuthentication < ActiveRecord::Migration[4.2]
|
|||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
AccountAuthorizationConfig::Canvas.reset_column_information
|
||||
AuthenticationProvider::Canvas.reset_column_information
|
||||
Account.root_accounts.each do |account|
|
||||
if account.settings[:canvas_authentication] != false || !account.authentication_providers.active.exists?
|
||||
account.enable_canvas_authentication
|
||||
|
@ -29,6 +29,6 @@ class GrandfatherCanvasAuthentication < ActiveRecord::Migration[4.2]
|
|||
end
|
||||
|
||||
def down
|
||||
AccountAuthorizationConfig.where(auth_type: 'canvas').delete_all
|
||||
AuthenticationProvider.where(auth_type: 'canvas').delete_all
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,9 @@ class ChangeAuthFilterToText < ActiveRecord::Migration[4.2]
|
|||
tag :postdeploy
|
||||
|
||||
def self.up
|
||||
change_column :account_authorization_configs, :auth_filter, :text
|
||||
AuthenticationProvider.maybe_recreate_view do
|
||||
change_column :account_authorization_configs, :auth_filter, :text
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
|
|
|
@ -18,8 +18,12 @@
|
|||
class MoveSamlEntityIdToAccount < ActiveRecord::Migration[4.2]
|
||||
tag :postdeploy
|
||||
|
||||
class AuthenticationProvider < ActiveRecord::Base
|
||||
self.table_name = 'account_authorization_configs'
|
||||
end
|
||||
|
||||
def up
|
||||
AccountAuthorizationConfig::SAML.active.where.not(entity_id: nil).each do |ap|
|
||||
AuthenticationProvider.where(workflow_state: 'active', auth_type: 'saml').where.not(entity_id: nil).each do |ap|
|
||||
ap.account.settings[:saml_entity_id] ||= ap.entity_id
|
||||
ap.account.save!
|
||||
end
|
||||
|
|
|
@ -29,21 +29,23 @@ class AddBackDefaultStringLimitsP1 < ActiveRecord::Migration[4.2]
|
|||
add_string_limit_if_missing :access_tokens, :token_hint
|
||||
add_string_limit_if_missing :access_tokens, :crypted_refresh_token
|
||||
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_host
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_base
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_username
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_crypted_password
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_password_salt
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_type
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_over_tls
|
||||
add_string_limit_if_missing :account_authorization_configs, :log_in_url
|
||||
add_string_limit_if_missing :account_authorization_configs, :log_out_url
|
||||
add_string_limit_if_missing :account_authorization_configs, :identifier_format
|
||||
add_string_limit_if_missing :account_authorization_configs, :entity_id
|
||||
add_string_limit_if_missing :account_authorization_configs, :requested_authn_context
|
||||
add_string_limit_if_missing :account_authorization_configs, :idp_entity_id
|
||||
add_string_limit_if_missing :account_authorization_configs, :workflow_state
|
||||
add_string_limit_if_missing :account_authorization_configs, :metadata_uri
|
||||
AuthenticationProvider.maybe_recreate_view do
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_host
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_base
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_username
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_crypted_password
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_password_salt
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_type
|
||||
add_string_limit_if_missing :account_authorization_configs, :auth_over_tls
|
||||
add_string_limit_if_missing :account_authorization_configs, :log_in_url
|
||||
add_string_limit_if_missing :account_authorization_configs, :log_out_url
|
||||
add_string_limit_if_missing :account_authorization_configs, :identifier_format
|
||||
add_string_limit_if_missing :account_authorization_configs, :entity_id
|
||||
add_string_limit_if_missing :account_authorization_configs, :requested_authn_context
|
||||
add_string_limit_if_missing :account_authorization_configs, :idp_entity_id
|
||||
add_string_limit_if_missing :account_authorization_configs, :workflow_state
|
||||
add_string_limit_if_missing :account_authorization_configs, :metadata_uri
|
||||
end
|
||||
|
||||
add_string_limit_if_missing :account_notifications, :subject
|
||||
add_string_limit_if_missing :account_notifications, :icon
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
class FixEmptyHostedDomainForGoogle < ActiveRecord::Migration[4.2]
|
||||
tag :postdeploy
|
||||
|
||||
def self.up
|
||||
DataFixup::SetEmptyGoogleHostedDomainToNull.send_later_if_production_enqueue_args(
|
||||
:run, { priority: Delayed::LOWER_PRIORITY, max_attempts: 1 })
|
||||
class AuthenticationProvider < ActiveRecord::Base
|
||||
self.table_name = 'account_authorization_configs'
|
||||
end
|
||||
|
||||
def self.down
|
||||
def self.up
|
||||
AuthenticationProvider.where(auth_type: 'google', auth_filter: '').update_all(auth_filter: nil)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,8 +18,16 @@
|
|||
class DisableNoTlsForLdap < ActiveRecord::Migration[5.0]
|
||||
tag :postdeploy
|
||||
|
||||
class AuthenticationProvider < ActiveRecord::Base
|
||||
self.table_name = 'account_authorization_configs'
|
||||
|
||||
def auth_over_tls
|
||||
::AuthenticationProvider::LDAP.auth_over_tls_setting(read_attribute(:auth_over_tls))
|
||||
end
|
||||
end
|
||||
|
||||
def up
|
||||
AccountAuthorizationConfig::LDAP.active.each do |ap|
|
||||
AuthenticationProvider.where(auth_type: 'ldap', workflow_state: 'active').each do |ap|
|
||||
ap.update_attribute(:auth_over_tls, 'start_tls') unless ap.auth_over_tls
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (C) 2011 - present Instructure, Inc.
|
||||
# Copyright (C) 2018 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
|
@ -16,8 +16,14 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module DataFixup::SetEmptyGoogleHostedDomainToNull
|
||||
def self.run
|
||||
AccountAuthorizationConfig::Google.where(auth_filter: '').update_all(auth_filter: nil)
|
||||
class CreateAuthenticationProvidersView < ActiveRecord::Migration[5.1]
|
||||
tag :predeploy
|
||||
|
||||
def up
|
||||
execute("CREATE VIEW #{connection.quote_table_name('authentication_providers')} AS SELECT * FROM #{connection.quote_table_name('account_authorization_configs')}")
|
||||
end
|
||||
|
||||
def down
|
||||
execute("DROP VIEW #{connection.quote_table_name('authentication_providers')}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (C) 2018 - present 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/>.
|
||||
|
||||
class RenameAccountAuthorizationConfigsToAuthenticationProviders < ActiveRecord::Migration[5.1]
|
||||
tag :postdeploy
|
||||
|
||||
def up
|
||||
execute("DROP VIEW #{connection.quote_table_name('authentication_providers')}")
|
||||
rename_table :account_authorization_configs, :authentication_providers
|
||||
end
|
||||
|
||||
def down
|
||||
rename_table :authentication_providers, :account_authorization_configs
|
||||
execute("CREATE VIEW #{connection.quote_table_name('authentication_providers')} AS SELECT * FROM #{connection.quote_table_name('account_authorization_configs')}")
|
||||
end
|
||||
end
|
|
@ -50,7 +50,7 @@ class ObjectView < HashView
|
|||
end
|
||||
|
||||
# Some @object descriptions have multiple JSON parts.
|
||||
# See e.g. AccountAuthorizationConfig
|
||||
# See e.g. AuthenticationProvider
|
||||
def clean_json_text_parts
|
||||
clean_json_text.gsub(/(\})\s+(\{)/, "\\1#{SEP}\\2").split(SEP)
|
||||
end
|
||||
|
@ -76,4 +76,4 @@ class ObjectView < HashView
|
|||
def self.strip_comments(str)
|
||||
str.gsub(%r(//[^\n\"]+$), '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,7 +26,7 @@ describe RuboCop::Cop::Specs::EnsureSpecExtension do
|
|||
context "top level context" do
|
||||
it 'does not warn for *_spec.rb extension' do
|
||||
inspect_source(%{
|
||||
context AccountAuthorizationConfig::BlueDragoon do
|
||||
context AuthenticationProvider::BlueDragoon do
|
||||
describe '#fire' do
|
||||
it 'rains fire' do
|
||||
expect(1).to eq(1)
|
||||
|
@ -41,7 +41,7 @@ describe RuboCop::Cop::Specs::EnsureSpecExtension do
|
|||
context "top level describe" do
|
||||
it 'does not warn for *_spec.rb extension' do
|
||||
inspect_source(%{
|
||||
describe AccountAuthorizationConfig::GreenDragoon do
|
||||
describe AuthenticationProvider::GreenDragoon do
|
||||
describe '#green' do
|
||||
it 'smells bad' do
|
||||
expect(1).to eq(1)
|
||||
|
@ -62,7 +62,7 @@ describe RuboCop::Cop::Specs::EnsureSpecExtension do
|
|||
context "top level context" do
|
||||
it 'warns for *_spec.rb extension' do
|
||||
inspect_source(%{
|
||||
context AccountAuthorizationConfig::BlueDragoon do
|
||||
context AuthenticationProvider::BlueDragoon do
|
||||
describe '#fire' do
|
||||
it 'rains fire' do
|
||||
expect(1).to eq(1)
|
||||
|
@ -79,7 +79,7 @@ describe RuboCop::Cop::Specs::EnsureSpecExtension do
|
|||
context "top level describe" do
|
||||
it 'warns for *_spec.rb extension' do
|
||||
inspect_source(%{
|
||||
describe AccountAuthorizationConfig::GreenDragoon do
|
||||
describe AuthenticationProvider::GreenDragoon do
|
||||
describe '#green' do
|
||||
it 'smells bad' do
|
||||
expect(1).to eq(1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module Consts
|
||||
APP_COFFEE_PATH = "app/coffeescripts/calendar/CalendarEvent.coffee".freeze
|
||||
APP_COFFEE_BUNDLE_PATH = "app/coffeescripts/bundles/account_authorization_configs.coffee".freeze
|
||||
APP_COFFEE_BUNDLE_PATH = "app/coffeescripts/bundles/authentication_providers.coffee".freeze
|
||||
COFFEE_SPEC_PATH = "spec/coffeescripts/calendar/CalendarSpec.coffee".freeze
|
||||
|
||||
APP_JSX_PATH = "app/jsx/dashboard_card/DashboardCardAction.js".freeze
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module Api::V1::AccountAuthorizationConfig
|
||||
module Api::V1::AuthenticationProvider
|
||||
include Api::V1::Json
|
||||
|
||||
def aacs_json(aacs)
|
|
@ -211,26 +211,29 @@ module Canvas::Security
|
|||
def self.re_encrypt_data(encryption_key)
|
||||
{
|
||||
Account => {
|
||||
:encrypted_column => :turnitin_crypted_secret,
|
||||
:salt_column => :turnitin_salt,
|
||||
:key => 'instructure_turnitin_secret_shared' },
|
||||
AccountAuthorizationConfig => {
|
||||
:encrypted_column => :auth_crypted_password,
|
||||
:salt_column => :auth_password_salt,
|
||||
:key => 'instructure_auth' },
|
||||
:encrypted_column => :turnitin_crypted_secret,
|
||||
:salt_column => :turnitin_salt,
|
||||
:key => 'instructure_turnitin_secret_shared'
|
||||
},
|
||||
AuthenticationProvider => {
|
||||
:encrypted_column => :auth_crypted_password,
|
||||
:salt_column => :auth_password_salt,
|
||||
:key => 'instructure_auth'
|
||||
},
|
||||
UserService => {
|
||||
:encrypted_column => :crypted_password,
|
||||
:salt_column => :password_salt,
|
||||
:key => 'instructure_user_service' },
|
||||
:encrypted_column => :crypted_password,
|
||||
:salt_column => :password_salt,
|
||||
:key => 'instructure_user_service'
|
||||
},
|
||||
User => {
|
||||
:encrypted_column => :otp_secret_key_enc,
|
||||
:salt_column => :otp_secret_key_salt,
|
||||
:key => 'otp_secret_key'
|
||||
:encrypted_column => :otp_secret_key_enc,
|
||||
:salt_column => :otp_secret_key_salt,
|
||||
:key => 'otp_secret_key'
|
||||
}
|
||||
}.each do |(model, definition)|
|
||||
model.where("#{definition[:encrypted_column]} IS NOT NULL").
|
||||
select([:id, definition[:encrypted_column], definition[:salt_column]]).
|
||||
find_each do |instance|
|
||||
select([:id, definition[:encrypted_column], definition[:salt_column]]).
|
||||
find_each do |instance|
|
||||
cleartext = Canvas::Security.decrypt_password(instance.read_attribute(definition[:encrypted_column]),
|
||||
instance.read_attribute(definition[:salt_column]),
|
||||
definition[:key],
|
||||
|
|
|
@ -16,15 +16,24 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module DataFixup::PopulateAccountAuthSettings
|
||||
class AuthenticationProvider < ActiveRecord::Base
|
||||
belongs_to :account
|
||||
end
|
||||
|
||||
def self.run
|
||||
AccountAuthorizationConfig.select("*, login_handle_name AS lhn, change_password_url AS cpu").find_each do |aac|
|
||||
AuthenticationProvider.table_name = if AuthenticationProvider.connection.table_exists?('account_authorization_configs')
|
||||
'account_authorization_configs'
|
||||
else
|
||||
'authentication_providers'
|
||||
end
|
||||
|
||||
AuthenticationProvider.select("*, login_handle_name AS lhn, change_password_url AS cpu").find_each do |aac|
|
||||
account = aac.account
|
||||
if !account.login_handle_name.present? && aac['lhn'].present?
|
||||
if account.login_handle_name.blank? && aac['lhn'].present?
|
||||
account.login_handle_name = aac['lhn']
|
||||
end
|
||||
|
||||
if !account.change_password_url.present? && aac['cpu'].present?
|
||||
if account.change_password_url.blank? && aac['cpu'].present?
|
||||
account.change_password_url = aac['cpu']
|
||||
end
|
||||
account.save!
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import I18n from 'i18n!account_authorization_configs'
|
||||
import I18n from 'i18n!authentication_providers'
|
||||
import htmlEscape from './str/htmlEscape'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
|
|
@ -190,7 +190,7 @@ describe "API Authentication", type: :request do
|
|||
end
|
||||
|
||||
it "should execute for saml login" do
|
||||
skip("requires SAML extension") unless AccountAuthorizationConfig::SAML.enabled?
|
||||
skip("requires SAML extension") unless AuthenticationProvider::SAML.enabled?
|
||||
account_with_saml(account: Account.default)
|
||||
flow do
|
||||
allow_any_instance_of(Onelogin::Saml::Response).to receive(:settings=)
|
||||
|
|
|
@ -46,7 +46,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
context "/index" do
|
||||
def call_index(status=200)
|
||||
api_call(:get, "/api/v1/accounts/#{@account.id}/authentication_providers",
|
||||
{ :controller => 'account_authorization_configs', :action => 'index', :account_id => @account.id.to_s, :format => 'json' },
|
||||
{ :controller => 'authentication_providers', :action => 'index', :account_id => @account.id.to_s, :format => 'json' },
|
||||
{}, {}, :expected_status => status)
|
||||
end
|
||||
|
||||
|
@ -73,7 +73,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
|
||||
def call_create(params, status = 200)
|
||||
json = api_call(:post, "/api/v1/accounts/#{@account.id}/authentication_providers",
|
||||
{ :controller => 'account_authorization_configs', :action => 'create', :account_id => @account.id.to_s, :format => 'json' },
|
||||
{ :controller => 'authentication_providers', :action => 'create', :account_id => @account.id.to_s, :format => 'json' },
|
||||
params, {}, :expected_status => status)
|
||||
@account.reload
|
||||
json
|
||||
|
@ -165,7 +165,11 @@ describe "AuthenticationProviders API", type: :request do
|
|||
|
||||
it "should error if empty post params sent" do
|
||||
json = call_create({}, 422)
|
||||
expect(json['errors'].first).to eq({ 'field' => 'auth_type', 'message' => "invalid auth_type, must be one of #{AccountAuthorizationConfig::VALID_AUTH_TYPES.join(',')}", 'error_code' => 'inclusion' })
|
||||
expect(json['errors'].first).to eq({
|
||||
'field' => 'auth_type',
|
||||
'message' => "invalid auth_type, must be one of #{AuthenticationProvider::VALID_AUTH_TYPES.join(',')}",
|
||||
'error_code' => 'inclusion'
|
||||
})
|
||||
end
|
||||
|
||||
it "should return unauthorized error" do
|
||||
|
@ -189,7 +193,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
context "/show" do
|
||||
def call_show(id, status = 200)
|
||||
api_call(:get, "/api/v1/accounts/#{@account.id}/authentication_providers/#{id}",
|
||||
{ :controller => 'account_authorization_configs', :action => 'show', :account_id => @account.id.to_s, :id => id.to_param, :format => 'json' },
|
||||
{ :controller => 'authentication_providers', :action => 'show', :account_id => @account.id.to_s, :id => id.to_param, :format => 'json' },
|
||||
{}, {}, :expected_status => status)
|
||||
end
|
||||
|
||||
|
@ -248,7 +252,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
context "/update" do
|
||||
def call_update(id, params, status = 200)
|
||||
json = api_call(:put, "/api/v1/accounts/#{@account.id}/authentication_providers/#{id}",
|
||||
{ :controller => 'account_authorization_configs', :action => 'update', :account_id => @account.id.to_s, :id => id.to_param, :format => 'json' },
|
||||
{ :controller => 'authentication_providers', :action => 'update', :account_id => @account.id.to_s, :id => id.to_param, :format => 'json' },
|
||||
params, {}, :expected_status => status)
|
||||
@account.reload
|
||||
json
|
||||
|
@ -338,7 +342,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
context "/destroy" do
|
||||
def call_destroy(id, status = 200)
|
||||
json = api_call(:delete, "/api/v1/accounts/#{@account.id}/authentication_providers/#{id}",
|
||||
{ :controller => 'account_authorization_configs', :action => 'destroy', :account_id => @account.id.to_s, :id => id.to_param, :format => 'json' },
|
||||
{ :controller => 'authentication_providers', :action => 'destroy', :account_id => @account.id.to_s, :id => id.to_param, :format => 'json' },
|
||||
{}, {}, :expected_status => status)
|
||||
@account.reload
|
||||
json
|
||||
|
@ -395,7 +399,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
api_call(:put,
|
||||
sso_path,
|
||||
{
|
||||
controller: 'account_authorization_configs',
|
||||
controller: 'authentication_providers',
|
||||
action: 'update_sso_settings',
|
||||
account_id: @account.id.to_s,
|
||||
format: 'json'
|
||||
|
@ -459,7 +463,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
response = api_call(:get,
|
||||
sso_path,
|
||||
{
|
||||
controller: "account_authorization_configs",
|
||||
controller: "authentication_providers",
|
||||
action: "show_sso_settings",
|
||||
account_id: @account.id.to_s,
|
||||
format: 'json'
|
||||
|
@ -475,10 +479,11 @@ describe "AuthenticationProviders API", type: :request do
|
|||
describe "API JSON" do
|
||||
describe 'federated_attributes' do
|
||||
it 'excludes provisioning only attributes when jit_provisioning is off' do
|
||||
aac = AccountAuthorizationConfig::SAML.new(
|
||||
aac = AuthenticationProvider::SAML.new(
|
||||
federated_attributes: { 'integration_id' => { 'attribute' => 'internal_id' },
|
||||
'sis_user_id' => { 'attribute' => 'external_id',
|
||||
'provisioning_only' => true }})
|
||||
'provisioning_only' => true }}
|
||||
)
|
||||
expect(aac.federated_attributes_for_api).to eq('integration_id' => 'internal_id')
|
||||
end
|
||||
|
||||
|
@ -487,7 +492,7 @@ describe "AuthenticationProviders API", type: :request do
|
|||
'provisioning_only' => false },
|
||||
'sis_user_id' => { 'attribute' => 'external_id',
|
||||
'provisioning_only' => true }}
|
||||
aac = AccountAuthorizationConfig::SAML.new(federated_attributes: federated_attributes,
|
||||
aac = AuthenticationProvider::SAML.new(federated_attributes: federated_attributes,
|
||||
jit_provisioning: true)
|
||||
expect(aac.federated_attributes_for_api).to eq(federated_attributes)
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe AccountAuthorizationConfigsController do
|
||||
describe AuthenticationProvidersController do
|
||||
|
||||
let!(:account) { Account.create! }
|
||||
|
|
@ -29,7 +29,7 @@ describe Login::CasController do
|
|||
st.success = response.is_success?
|
||||
st
|
||||
end
|
||||
allow_any_instance_of(AccountAuthorizationConfig::CAS).to receive(:client).and_return(cas_client) if use_mock
|
||||
allow_any_instance_of(AuthenticationProvider::CAS).to receive(:client).and_return(cas_client) if use_mock
|
||||
end
|
||||
|
||||
it "should logout with specific cas ticket" do
|
||||
|
|
|
@ -20,7 +20,7 @@ require_relative '../../spec_helper'
|
|||
|
||||
describe Login::SamlController do
|
||||
before do
|
||||
skip("requires SAML extension") unless AccountAuthorizationConfig::SAML.enabled?
|
||||
skip("requires SAML extension") unless AuthenticationProvider::SAML.enabled?
|
||||
end
|
||||
|
||||
it "should scope logins to the correct domain root account" do
|
||||
|
|
|
@ -64,7 +64,7 @@ module Factories
|
|||
def account_with_cas(opts={})
|
||||
@account = opts[:account]
|
||||
@account ||= Account.create!
|
||||
config = AccountAuthorizationConfig::CAS.new
|
||||
config = AuthenticationProvider::CAS.new
|
||||
cas_url = opts[:cas_url] || "https://localhost/cas"
|
||||
config.auth_type = "cas"
|
||||
config.auth_base = cas_url
|
||||
|
@ -77,7 +77,7 @@ module Factories
|
|||
def account_with_saml(opts={})
|
||||
@account = opts[:account]
|
||||
@account ||= Account.create!
|
||||
config = AccountAuthorizationConfig::SAML.new
|
||||
config = AuthenticationProvider::SAML.new
|
||||
config.idp_entity_id = "saml_entity"
|
||||
config.auth_type = "saml"
|
||||
config.log_in_url = opts[:saml_log_in_url] if opts[:saml_log_in_url]
|
||||
|
|
|
@ -24,7 +24,7 @@ describe AccountsController do
|
|||
|
||||
context "SAML meta data" do
|
||||
before(:each) do
|
||||
skip("requires SAML extension") unless AccountAuthorizationConfig::SAML.enabled?
|
||||
skip("requires SAML extension") unless AuthenticationProvider::SAML.enabled?
|
||||
@account = Account.create!(:name => "test")
|
||||
end
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ describe 'login' do
|
|||
|
||||
context "SAML" do
|
||||
before do
|
||||
skip("requires SAML extension") unless AccountAuthorizationConfig::SAML.enabled?
|
||||
skip("requires SAML extension") unless AuthenticationProvider::SAML.enabled?
|
||||
end
|
||||
|
||||
it 'redirects to the discovery page when hitting a deep link while unauthenticated' do
|
||||
|
|
|
@ -103,8 +103,8 @@ describe "acts_as_list" do
|
|||
|
||||
describe "base scope" do
|
||||
it "scopes by the base class rather then the STI class" do
|
||||
scope = AccountAuthorizationConfig::CAS.new.list_scope_base
|
||||
expect(scope.to_sql).to_not(match(/auth_type/))
|
||||
scope = AuthenticationProvider::CAS.new.list_scope_base
|
||||
expect(scope.to_sql).not_to(match(/auth_type/))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ describe "Api::V1::Pseudonym" do
|
|||
let(:api) { Harness.new }
|
||||
|
||||
it "includes the authentication_provider_type if there is one" do
|
||||
aac = AccountAuthorizationConfig.new(auth_type: "ldap")
|
||||
aac = AuthenticationProvider.new(auth_type: "ldap")
|
||||
pseudonym.authentication_provider = aac
|
||||
json = api.pseudonym_json(pseudonym, user, session)
|
||||
expect(json[:authentication_provider_type]).to eq("ldap")
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2011 - present 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.rb')
|
||||
require 'db/migrate/20111121175219_disable_open_registration_for_delegated_auth.rb'
|
||||
|
||||
describe 'DisableOpenRegistrationForDelegatedAuth' do
|
||||
describe "up" do
|
||||
it "should work" do
|
||||
@cas_account = Account.create!
|
||||
@saml_account = Account.create!
|
||||
@ldap_account = Account.create!
|
||||
@normal_account = Account.create!
|
||||
@all_accounts = [@cas_account, @saml_account, @ldap_account, @normal_account]
|
||||
@cas_account.authentication_providers.create!(:auth_type => 'cas')
|
||||
@saml_account.authentication_providers.create!(:auth_type => 'saml')
|
||||
@ldap_account.authentication_providers.create!(:auth_type => 'ldap')
|
||||
@all_accounts.each do |account|
|
||||
# have to bypass the settings= logic for weeding these out since they don't
|
||||
# apply
|
||||
account.write_attribute(:settings, { :open_registration => true })
|
||||
account.save!
|
||||
expect(account.open_registration?).to be_truthy
|
||||
end
|
||||
|
||||
DisableOpenRegistrationForDelegatedAuth.up
|
||||
|
||||
@all_accounts.each(&:reload)
|
||||
expect(@cas_account.open_registration?).to be_falsey
|
||||
expect(@saml_account.open_registration?).to be_falsey
|
||||
expect(@ldap_account.open_registration?).to be_truthy
|
||||
expect(@normal_account.open_registration?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
|
||||
|
||||
describe AccountAuthorizationConfig::Google do
|
||||
describe AuthenticationProvider::Google do
|
||||
it 'rejects non-matching hd' do
|
||||
ap = AccountAuthorizationConfig::Google.new
|
||||
ap = AuthenticationProvider::Google.new
|
||||
ap.hosted_domain = 'instructure.com'
|
||||
expect(Canvas::Security).to receive(:decode_jwt).and_return({'hd' => 'school.edu', 'sub' => '123'})
|
||||
userinfo = double('userinfo', parsed: {})
|
||||
|
@ -30,7 +30,7 @@ describe AccountAuthorizationConfig::Google do
|
|||
end
|
||||
|
||||
it 'rejects missing hd' do
|
||||
ap = AccountAuthorizationConfig::Google.new
|
||||
ap = AuthenticationProvider::Google.new
|
||||
ap.hosted_domain = 'instructure.com'
|
||||
expect(Canvas::Security).to receive(:decode_jwt).and_return({'sub' => '123'})
|
||||
token = double('token', params: {}, options: {})
|
||||
|
@ -39,7 +39,7 @@ describe AccountAuthorizationConfig::Google do
|
|||
end
|
||||
|
||||
it "accepts when hosted domain isn't required" do
|
||||
ap = AccountAuthorizationConfig::Google.new
|
||||
ap = AuthenticationProvider::Google.new
|
||||
expect(Canvas::Security).to receive(:decode_jwt).once.and_return({'sub' => '123'})
|
||||
token = double('token', params: {}, options: {})
|
||||
|
||||
|
@ -47,7 +47,7 @@ describe AccountAuthorizationConfig::Google do
|
|||
end
|
||||
|
||||
it "it sets hosted domain to nil if empty string" do
|
||||
ap = AccountAuthorizationConfig::Google.new
|
||||
ap = AuthenticationProvider::Google.new
|
||||
ap.hosted_domain = ''
|
||||
expect(ap.hosted_domain).to be_nil
|
||||
end
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
|
||||
|
||||
describe AccountAuthorizationConfig::LDAP do
|
||||
describe AuthenticationProvider::LDAP do
|
||||
it "should not escape auth_filter" do
|
||||
@account = Account.new
|
||||
@account_config = @account.authentication_providers.build(
|
||||
|
@ -32,7 +32,7 @@ describe AccountAuthorizationConfig::LDAP do
|
|||
|
||||
describe "#test_ldap_search" do
|
||||
it "should validate filter syntax" do
|
||||
aac = AccountAuthorizationConfig::LDAP.new
|
||||
aac = AuthenticationProvider::LDAP.new
|
||||
aac.auth_type = 'ldap'
|
||||
aac.ldap_filter = 'bob'
|
||||
expect(aac.test_ldap_search).to be_falsey
|
||||
|
@ -49,14 +49,14 @@ describe AccountAuthorizationConfig::LDAP do
|
|||
before(:once) do
|
||||
@account = Account.new
|
||||
@account.save!
|
||||
@aac = AccountAuthorizationConfig::LDAP.new(account: @account)
|
||||
@aac = AuthenticationProvider::LDAP.new(account: @account)
|
||||
@aac.auth_type = 'ldap'
|
||||
@aac.ldap_filter = 'bob'
|
||||
@aac.save!
|
||||
end
|
||||
|
||||
it "should not attempt to bind with a blank password" do
|
||||
aac = AccountAuthorizationConfig::LDAP.new
|
||||
aac = AuthenticationProvider::LDAP.new
|
||||
aac.auth_type = 'ldap'
|
||||
aac.ldap_filter = 'bob'
|
||||
expect(aac).to receive(:ldap_connection).never
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
require_relative '../../spec_helper.rb'
|
||||
|
||||
describe AccountAuthorizationConfig::OpenIDConnect do
|
||||
describe AuthenticationProvider::OpenIDConnect do
|
||||
|
||||
describe '#scope_for_options' do
|
||||
it 'automatically infers according to requested claims' do
|
||||
|
@ -62,7 +62,7 @@ describe AccountAuthorizationConfig::OpenIDConnect do
|
|||
|
||||
describe "#user_logout_url" do
|
||||
it "returns the end_session_endpoint" do
|
||||
ap = AccountAuthorizationConfig::OpenIDConnect.new(end_session_endpoint: "http://somewhere/logout")
|
||||
ap = AuthenticationProvider::OpenIDConnect.new(end_session_endpoint: "http://somewhere/logout")
|
||||
expect(ap.user_logout_redirect(nil, nil)).to eq "http://somewhere/logout"
|
||||
end
|
||||
end
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
require_relative '../../spec_helper.rb'
|
||||
|
||||
describe AccountAuthorizationConfig::PluginSettings do
|
||||
describe AuthenticationProvider::PluginSettings do
|
||||
let(:klass) do
|
||||
Class.new(AccountAuthorizationConfig) do
|
||||
include AccountAuthorizationConfig::PluginSettings
|
||||
Class.new(AuthenticationProvider) do
|
||||
include AuthenticationProvider::PluginSettings
|
||||
self.plugin = :custom_plugin
|
||||
|
||||
def noninherited_method
|
|
@ -18,12 +18,12 @@
|
|||
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe AccountAuthorizationConfig::SAML::InCommon do
|
||||
let(:subject) { AccountAuthorizationConfig::SAML::InCommon }
|
||||
describe AuthenticationProvider::SAML::InCommon do
|
||||
let(:subject) { AuthenticationProvider::SAML::InCommon }
|
||||
|
||||
describe ".refresh_providers" do
|
||||
before do
|
||||
allow_any_instance_of(AccountAuthorizationConfig::SAML).to receive(:download_metadata).and_return(nil)
|
||||
allow_any_instance_of(AuthenticationProvider::SAML).to receive(:download_metadata).and_return(nil)
|
||||
end
|
||||
|
||||
let!(:saml) { Account.default.authentication_providers.create!(auth_type: 'saml',
|
|
@ -18,15 +18,15 @@
|
|||
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe AccountAuthorizationConfig::SAML::MetadataRefresher do
|
||||
let(:subject) { AccountAuthorizationConfig::SAML::MetadataRefresher }
|
||||
describe AuthenticationProvider::SAML::MetadataRefresher do
|
||||
let(:subject) { AuthenticationProvider::SAML::MetadataRefresher }
|
||||
|
||||
describe ".refresh_providers" do
|
||||
before do
|
||||
allow_any_instance_of(AccountAuthorizationConfig::SAML).to receive(:download_metadata).and_return(nil)
|
||||
allow_any_instance_of(AuthenticationProvider::SAML).to receive(:download_metadata).and_return(nil)
|
||||
end
|
||||
|
||||
let (:saml1) { Account.default.authentication_providers.create!(auth_type: 'saml', metadata_uri: '1') }
|
||||
let(:saml1) { Account.default.authentication_providers.create!(auth_type: 'saml', metadata_uri: '1') }
|
||||
|
||||
it "keeps going even if one fails" do
|
||||
saml2 = Account.default.authentication_providers.create!(auth_type: 'saml', metadata_uri: '2')
|
||||
|
@ -63,8 +63,8 @@ describe AccountAuthorizationConfig::SAML::MetadataRefresher do
|
|||
end
|
||||
|
||||
it "ignores nil/blank metadata_uris" do
|
||||
AccountAuthorizationConfig::SAML.where(id: saml1.id).update_all(metadata_uri: nil)
|
||||
saml2 = Account.default.authentication_providers.create!(auth_type: 'saml', metadata_uri: '')
|
||||
AuthenticationProvider::SAML.where(id: saml1.id).update_all(metadata_uri: nil)
|
||||
Account.default.authentication_providers.create!(auth_type: 'saml', metadata_uri: '')
|
||||
expect(subject).to receive(:refresh_if_necessary).never
|
||||
|
||||
subject.refresh_providers
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
|
||||
|
||||
describe AccountAuthorizationConfig::SAML do
|
||||
describe AuthenticationProvider::SAML do
|
||||
before(:each) do
|
||||
skip("requires SAML extension") unless AccountAuthorizationConfig::SAML.enabled?
|
||||
skip("requires SAML extension") unless AuthenticationProvider::SAML.enabled?
|
||||
@account = Account.create!(:name => "account")
|
||||
@file_that_exists = File.expand_path(__FILE__)
|
||||
end
|
||||
|
@ -36,7 +36,7 @@ describe AccountAuthorizationConfig::SAML do
|
|||
|
||||
s = @account.authentication_providers.build(:auth_type => 'saml').saml_settings
|
||||
|
||||
expect(s.encryption_configured?).to be_truthy
|
||||
expect(s).to be_encryption_configured
|
||||
end
|
||||
|
||||
it "should load the tech contact settings" do
|
||||
|
@ -115,21 +115,21 @@ describe AccountAuthorizationConfig::SAML do
|
|||
describe "download_metadata" do
|
||||
it 'requires an entity id for InCommon' do
|
||||
saml = Account.default.authentication_providers.new(auth_type: 'saml',
|
||||
metadata_uri: AccountAuthorizationConfig::SAML::InCommon::URN)
|
||||
metadata_uri: AuthenticationProvider::SAML::InCommon::URN)
|
||||
expect(saml).not_to be_valid
|
||||
expect(saml.errors.first.first).to eq :idp_entity_id
|
||||
end
|
||||
|
||||
it 'changes InCommon URI to the URN for it' do
|
||||
saml = Account.default.authentication_providers.new(auth_type: 'saml',
|
||||
metadata_uri: AccountAuthorizationConfig::SAML::InCommon.endpoint)
|
||||
metadata_uri: AuthenticationProvider::SAML::InCommon.endpoint)
|
||||
expect(saml).not_to be_valid
|
||||
expect(saml.metadata_uri).to eq AccountAuthorizationConfig::SAML::InCommon::URN
|
||||
expect(saml.metadata_uri).to eq AuthenticationProvider::SAML::InCommon::URN
|
||||
end
|
||||
|
||||
it "overwrite sig_alg field as appropriate" do
|
||||
# defaults to RSA-SHA256
|
||||
saml = AccountAuthorizationConfig::SAML.new
|
||||
saml = AuthenticationProvider::SAML.new
|
||||
expect(saml.sig_alg).to eq SAML2::Bindings::HTTPRedirect::SigAlgs::RSA_SHA256
|
||||
|
||||
entity = SAML2::Entity.new
|
||||
|
@ -159,21 +159,21 @@ describe AccountAuthorizationConfig::SAML do
|
|||
|
||||
describe '.resolve_saml_key_path' do
|
||||
it "returns nil for nil" do
|
||||
expect(AccountAuthorizationConfig::SAML.resolve_saml_key_path(nil)).to be_nil
|
||||
expect(AuthenticationProvider::SAML.resolve_saml_key_path(nil)).to be_nil
|
||||
end
|
||||
|
||||
it "returns nil for nonexistent paths" do
|
||||
expect(AccountAuthorizationConfig::SAML.resolve_saml_key_path('/tmp/does_not_exist')).to be_nil
|
||||
expect(AuthenticationProvider::SAML.resolve_saml_key_path('/tmp/does_not_exist')).to be_nil
|
||||
end
|
||||
|
||||
it "returns abolute paths unmodified when the file exists" do
|
||||
Tempfile.open('samlkey') do |samlkey|
|
||||
expect(AccountAuthorizationConfig::SAML.resolve_saml_key_path(samlkey.path)).to eq samlkey.path
|
||||
expect(AuthenticationProvider::SAML.resolve_saml_key_path(samlkey.path)).to eq samlkey.path
|
||||
end
|
||||
end
|
||||
|
||||
it "interprets relative paths from the config dir" do
|
||||
expect(AccountAuthorizationConfig::SAML.resolve_saml_key_path('initializers')).to eq Rails.root.join('config', 'initializers').to_s
|
||||
expect(AuthenticationProvider::SAML.resolve_saml_key_path('initializers')).to eq Rails.root.join('config', 'initializers').to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -203,7 +203,9 @@ describe AccountAuthorizationConfig::SAML do
|
|||
ap = @account.authentication_providers.build(:auth_type => 'saml')
|
||||
ap.federated_attributes = { 'display_name' => { 'attribute' => 'name' } }
|
||||
ap.save!
|
||||
entity = AccountAuthorizationConfig::SAML.sp_metadata_for_account(@account)
|
||||
# ignore invalid saml key configuration in specs
|
||||
allow(AuthenticationProvider::SAML).to receive(:private_keys).and_return({})
|
||||
entity = AuthenticationProvider::SAML.sp_metadata_for_account(@account)
|
||||
expect(entity.roles.last.attribute_consuming_services.length).to eq 1
|
||||
expect(entity.roles.last.attribute_consuming_services.first.requested_attributes.length). to eq 1
|
||||
expect(entity.roles.last.attribute_consuming_services.first.requested_attributes.first.name). to eq 'name'
|
|
@ -18,13 +18,13 @@
|
|||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
||||
|
||||
describe AccountAuthorizationConfig do
|
||||
describe AuthenticationProvider do
|
||||
|
||||
let(:account){ Account.default }
|
||||
|
||||
context "password" do
|
||||
it "should decrypt the password to the original value" do
|
||||
c = AccountAuthorizationConfig.new
|
||||
c = AuthenticationProvider.new
|
||||
c.auth_password = "asdf"
|
||||
expect(c.auth_decrypted_password).to eql("asdf")
|
||||
c.auth_password = "2t87aot72gho8a37gh4g[awg'waegawe-,v-3o7fya23oya2o3"
|
||||
|
@ -65,7 +65,7 @@ describe AccountAuthorizationConfig do
|
|||
let!(:aac){ account.authentication_providers.create!(auth_type: 'facebook') }
|
||||
|
||||
it "still reloads ok" do
|
||||
expect { aac.reload }.to_not raise_error
|
||||
expect { aac.reload }.not_to raise_error
|
||||
end
|
||||
|
||||
it "works through associations that use the provided module" do
|
||||
|
@ -76,22 +76,23 @@ describe AccountAuthorizationConfig do
|
|||
|
||||
describe "#auth_provider_filter" do
|
||||
it "includes nil for legacy auth types" do
|
||||
aac = AccountAuthorizationConfig.new(auth_type: "cas")
|
||||
aac = AuthenticationProvider.new(auth_type: "cas")
|
||||
expect(aac.auth_provider_filter).to eq([nil, aac])
|
||||
end
|
||||
|
||||
it "is just the AAC for oauth types" do
|
||||
aac = AccountAuthorizationConfig.new(auth_type: "facebook")
|
||||
aac = AuthenticationProvider.new(auth_type: "facebook")
|
||||
expect(aac.auth_provider_filter).to eq(aac)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
let!(:aac){ account.authentication_providers.create!(auth_type: 'cas') }
|
||||
let!(:aac) { account.authentication_providers.create!(auth_type: 'cas') }
|
||||
|
||||
it "retains the database row" do
|
||||
aac.destroy
|
||||
found = AccountAuthorizationConfig.find(aac.id)
|
||||
expect(found).to_not be_nil
|
||||
found = AuthenticationProvider.find(aac.id)
|
||||
expect(found).not_to be_nil
|
||||
end
|
||||
|
||||
it "sets workflow_state upon destroy" do
|
||||
|
@ -102,8 +103,8 @@ describe AccountAuthorizationConfig do
|
|||
|
||||
it "is aliased with #destroy_permanently!" do
|
||||
aac.destroy_permanently!
|
||||
found = AccountAuthorizationConfig.find(aac.id)
|
||||
expect(found).to_not be_nil
|
||||
found = AuthenticationProvider.find(aac.id)
|
||||
expect(found).not_to be_nil
|
||||
end
|
||||
|
||||
it "soft-deletes associated pseudonyms" do
|
||||
|
@ -117,20 +118,22 @@ describe AccountAuthorizationConfig do
|
|||
end
|
||||
|
||||
describe ".active" do
|
||||
let!(:aac){ account.authentication_providers.create!(auth_type: 'cas') }
|
||||
let!(:aac) { account.authentication_providers.create!(auth_type: 'cas') }
|
||||
|
||||
it "finds an aac that isn't deleted" do
|
||||
expect(AccountAuthorizationConfig.active).to include(aac)
|
||||
expect(AuthenticationProvider.active).to include(aac)
|
||||
end
|
||||
|
||||
it "ignores aacs which have been deleted" do
|
||||
aac.destroy
|
||||
expect(AccountAuthorizationConfig.active).to_not include(aac)
|
||||
expect(AuthenticationProvider.active).not_to include(aac)
|
||||
end
|
||||
end
|
||||
|
||||
describe "list-i-ness" do
|
||||
let!(:aac1){ account.authentication_providers.create!(auth_type: 'facebook') }
|
||||
let!(:aac2){ account.authentication_providers.create!(auth_type: 'github') }
|
||||
let!(:aac1) { account.authentication_providers.create!(auth_type: 'facebook') }
|
||||
let!(:aac2) { account.authentication_providers.create!(auth_type: 'github') }
|
||||
|
||||
before do
|
||||
account.authentication_providers.where(auth_type: 'canvas').first.destroy
|
||||
end
|
||||
|
@ -176,14 +179,14 @@ describe AccountAuthorizationConfig do
|
|||
it "allows valid provider attributes" do
|
||||
aac = Account.default.authentication_providers.new(auth_type: 'saml',
|
||||
federated_attributes: { 'integration_id' => 'internal_id'})
|
||||
allow(AccountAuthorizationConfig::SAML).to receive(:recognized_federated_attributes).and_return(['internal_id'])
|
||||
allow(AuthenticationProvider::SAML).to receive(:recognized_federated_attributes).and_return(['internal_id'])
|
||||
expect(aac).to be_valid
|
||||
end
|
||||
|
||||
it "doesn't allow invalid provider attributes" do
|
||||
aac = Account.default.authentication_providers.new(auth_type: 'saml',
|
||||
federated_attributes: { 'integration_id' => 'garbage'})
|
||||
allow(AccountAuthorizationConfig::SAML).to receive(:recognized_federated_attributes).and_return(['internal_id'])
|
||||
allow(AuthenticationProvider::SAML).to receive(:recognized_federated_attributes).and_return(['internal_id'])
|
||||
expect(aac).not_to be_valid
|
||||
end
|
||||
|
|
@ -4150,10 +4150,10 @@ describe Course do
|
|||
|
||||
it "should be preferred if delegated authentication is configured" do
|
||||
account = Account.create!
|
||||
account.settings[:open_registration] = true
|
||||
account.save!
|
||||
account.authentication_providers.create!(:auth_type => 'cas')
|
||||
account.authentication_providers.first.move_to_bottom
|
||||
account.settings[:open_registration] = true
|
||||
account.save!
|
||||
course_factory(account: account)
|
||||
expect(@course.user_list_search_mode_for(nil)).to eq :preferred
|
||||
expect(@course.user_list_search_mode_for(user_factory)).to eq :preferred
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue