Add Lti Registration model
refs INTEROP-7952 flags=none why This commit adds a table and matching ActiveRecord model to facilitate Lti Registrations. This will be further used in building support for Dynamic Registration. test plan: Make sure the migrations run, and the `lti_ims_registrations` table is created. Change-Id: I1d3f6b46d08de7dd68254553191de65fdf72138e Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/313519 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Xander Moffatt <xmoffatt@instructure.com> QA-Review: Xander Moffatt <xmoffatt@instructure.com> Migration-Review: Jacob Burroughs <jburroughs@instructure.com> Product-Review: Paul Gray <paul.gray@instructure.com>
This commit is contained in:
parent
928a86a289
commit
6beb8cdc4b
|
@ -41,6 +41,7 @@ class DeveloperKey < ActiveRecord::Base
|
|||
|
||||
has_one :tool_consumer_profile, class_name: "Lti::ToolConsumerProfile", inverse_of: :developer_key
|
||||
has_one :tool_configuration, class_name: "Lti::ToolConfiguration", dependent: :destroy, inverse_of: :developer_key
|
||||
has_one :lti_registration, class_name: "Lti::IMS::Registration", dependent: :destroy, inverse_of: :developer_key
|
||||
serialize :scopes, Array
|
||||
|
||||
before_validation :normalize_public_jwk_url
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2020 - 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 Lti::IMS::Registration < ApplicationRecord
|
||||
self.table_name = "lti_ims_registrations"
|
||||
|
||||
REQUIRED_GRANT_TYPES = ["client_credentials", "implicit"].freeze
|
||||
REQUIRED_RESPONSE_TYPES = ["id_token"].freeze
|
||||
REQUIRED_APPLICATION_TYPE = "web"
|
||||
REQUIRED_TOKEN_ENDPOINT_AUTH_METHOD = "private_key_jwt"
|
||||
|
||||
validates :application_type,
|
||||
:grant_types,
|
||||
:response_types,
|
||||
:redirect_uris,
|
||||
:initiate_login_uri,
|
||||
:client_name,
|
||||
:jwks_uri,
|
||||
:token_endpoint_auth_method,
|
||||
:lti_tool_configuration,
|
||||
:developer_key,
|
||||
presence: true
|
||||
|
||||
validate :required_values_are_present,
|
||||
:redirect_uris_contains_uris,
|
||||
:lti_tool_configuration_is_valid,
|
||||
:scopes_are_valid
|
||||
|
||||
validates :initiate_login_uri,
|
||||
:jwks_uri,
|
||||
:logo_uri,
|
||||
:client_uri,
|
||||
:tos_uri,
|
||||
:policy_uri,
|
||||
format: { with: URI::DEFAULT_PARSER.make_regexp(["http", "https"]) }, allow_blank: true
|
||||
|
||||
belongs_to :developer_key, inverse_of: :lti_registration
|
||||
|
||||
def new_external_tool(context, existing_tool: nil)
|
||||
tool = existing_tool || ContextExternalTool.new(context: context)
|
||||
Importers::ContextExternalToolImporter.import_from_migration(
|
||||
importable_configuration,
|
||||
context,
|
||||
nil,
|
||||
tool,
|
||||
false
|
||||
)
|
||||
tool.developer_key = developer_key
|
||||
tool.workflow_state = "active"
|
||||
tool.use_1_3 = true
|
||||
tool
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def required_values_are_present
|
||||
if (REQUIRED_GRANT_TYPES - grant_types).present?
|
||||
errors.add(:grant_types, "Must include #{REQUIRED_GRANT_TYPES.join(", ")}")
|
||||
end
|
||||
if (REQUIRED_RESPONSE_TYPES - response_types).present?
|
||||
errors.add(:response_types, "Must include #{REQUIRED_RESPONSE_TYPES.join(", ")}")
|
||||
end
|
||||
|
||||
if token_endpoint_auth_method != REQUIRED_TOKEN_ENDPOINT_AUTH_METHOD
|
||||
errors.add(:token_endpoint_auth_method, "Must be 'private_key_jwt'")
|
||||
end
|
||||
|
||||
if application_type != REQUIRED_APPLICATION_TYPE
|
||||
errors.add(:application_type, "Must be 'web'")
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_uris_contains_uris
|
||||
return if redirect_uris.all? { |uri| uri.match? URI::DEFAULT_PARSER.make_regexp(["http", "https"]) }
|
||||
|
||||
errors.add(:redirect_uris, "Must only contain valid URIs")
|
||||
end
|
||||
|
||||
def scopes_are_valid
|
||||
invalid_scopes = scopes - TokenScopes::LTI_SCOPES.keys
|
||||
return if invalid_scopes.empty?
|
||||
|
||||
errors.add(:scopes, "Invalid scopes: #{invalid_scopes.join(", ")}")
|
||||
end
|
||||
|
||||
def lti_tool_configuration_is_valid
|
||||
config_errors = Schemas::Lti::IMS::LtiToolConfiguration.simple_validation_errors(
|
||||
lti_tool_configuration,
|
||||
error_format: :hash
|
||||
)
|
||||
return if config_errors.blank?
|
||||
|
||||
errors.add(
|
||||
:lti_tool_configuration,
|
||||
# Convert errors represented as a Hash to JSON
|
||||
config_errors.is_a?(Hash) ? config_errors.to_json : config_errors
|
||||
)
|
||||
end
|
||||
|
||||
# TODO: this method of only supports message/placement properties defined in
|
||||
# the Dynamic Registration specification. In the future we will need to add
|
||||
# support for all our custom top-level and placement-level properties
|
||||
# ("icon_url", "selection_height", etc.)
|
||||
def importable_configuration
|
||||
{
|
||||
"title" => client_name,
|
||||
"scopes" => scopes,
|
||||
"settings" => {
|
||||
"client_id" => global_developer_key_id
|
||||
}.merge(importable_placements),
|
||||
"public_jwk_url" => jwks_uri,
|
||||
"description" => lti_tool_configuration["description"],
|
||||
"custom_fields" => lti_tool_configuration["custom_parameters"],
|
||||
"target_link_uri" => lti_tool_configuration["target_link_uri"],
|
||||
"oidc_initiation_url" => initiate_login_uri,
|
||||
# TODO: How do we want to handle privacy level?
|
||||
"privacy_level" => "public",
|
||||
"url" => lti_tool_configuration["target_link_uri"],
|
||||
"domain" => lti_tool_configuration["domain"]
|
||||
}
|
||||
end
|
||||
|
||||
def importable_placements
|
||||
lti_tool_configuration["messages"].each_with_object({}) do |message, hash|
|
||||
# In an IMS Tool Registration, a single message can have multiple placements.
|
||||
# To correctly import this, we need to duplicate the message for each desired
|
||||
# placement.
|
||||
message["placements"].each do |placement|
|
||||
hash[placement] = {
|
||||
"custom_fields" => message["custom_parameters"],
|
||||
"message_type" => message["type"],
|
||||
"placement" => placement,
|
||||
"target_link_uri" => message["target_link_uri"],
|
||||
"text" => message["label"]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,6 +17,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/>.
|
||||
#
|
||||
require "lti_advantage"
|
||||
|
||||
module Lti
|
||||
class ResourcePlacement < ActiveRecord::Base
|
||||
|
@ -36,12 +37,13 @@ module Lti
|
|||
# Default placements for LTI 1 and LTI 2, ignored for LTI 1.3
|
||||
LEGACY_DEFAULT_PLACEMENTS = [ASSIGNMENT_SELECTION, LINK_SELECTION].freeze
|
||||
|
||||
PLACEMENTS = %i[account_navigation
|
||||
similarity_detection
|
||||
PLACEMENTS_BY_MESSAGE_TYPE = {
|
||||
LtiAdvantage::Messages::ResourceLinkRequest::MESSAGE_TYPE => %i[
|
||||
account_navigation
|
||||
assignment_edit
|
||||
assignment_menu
|
||||
assignment_index_menu
|
||||
assignment_group_menu
|
||||
assignment_index_menu
|
||||
assignment_menu
|
||||
assignment_selection
|
||||
assignment_view
|
||||
collaboration
|
||||
|
@ -50,11 +52,10 @@ module Lti
|
|||
course_home_sub_navigation
|
||||
course_navigation
|
||||
course_settings_sub_navigation
|
||||
discussion_topic_menu
|
||||
discussion_topic_index_menu
|
||||
editor_button
|
||||
file_menu
|
||||
discussion_topic_menu
|
||||
file_index_menu
|
||||
file_menu
|
||||
global_navigation
|
||||
homework_submission
|
||||
link_selection
|
||||
|
@ -62,18 +63,47 @@ module Lti
|
|||
module_group_menu
|
||||
module_index_menu
|
||||
module_index_menu_modal
|
||||
module_menu
|
||||
module_menu_modal
|
||||
module_menu
|
||||
post_grades
|
||||
quiz_menu
|
||||
quiz_index_menu
|
||||
quiz_menu
|
||||
resource_selection
|
||||
submission_type_selection
|
||||
similarity_detection
|
||||
student_context_card
|
||||
submission_type_selection
|
||||
tool_configuration
|
||||
user_navigation
|
||||
wiki_index_menu
|
||||
wiki_page_menu].freeze
|
||||
wiki_page_menu
|
||||
],
|
||||
LtiAdvantage::Messages::DeepLinkingRequest::MESSAGE_TYPE => %i[
|
||||
assignment_index_menu
|
||||
assignment_menu
|
||||
assignment_selection
|
||||
collaboration
|
||||
conference_selection
|
||||
discussion_topic_index_menu
|
||||
discussion_topic_menu
|
||||
editor_button
|
||||
file_menu
|
||||
homework_submission
|
||||
link_selection
|
||||
migration_selection
|
||||
module_index_menu
|
||||
module_index_menu_modal
|
||||
module_menu_modal
|
||||
module_menu
|
||||
quiz_index_menu
|
||||
quiz_menu
|
||||
resource_selection
|
||||
submission_type_selection
|
||||
wiki_index_menu
|
||||
wiki_page_menu
|
||||
]
|
||||
}.freeze
|
||||
|
||||
PLACEMENTS = PLACEMENTS_BY_MESSAGE_TYPE.values.flatten.uniq.freeze
|
||||
|
||||
PLACEMENT_LOOKUP = {
|
||||
"Canvas.placements.accountNavigation" => ACCOUNT_NAVIGATION,
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2020 - 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 CreateLtiIMSRegistrations < ActiveRecord::Migration[7.0]
|
||||
tag :predeploy
|
||||
|
||||
def up
|
||||
create_table :lti_ims_registrations do |t|
|
||||
t.jsonb :lti_tool_configuration, null: false
|
||||
t.references :developer_key, null: false, foreign_key: true, index: true
|
||||
t.string :application_type, null: false
|
||||
t.text :grant_types, array: true, default: [], null: false
|
||||
t.text :response_types, array: true, default: [], null: false
|
||||
t.text :redirect_uris, array: true, default: [], null: false
|
||||
t.text :initiate_login_uri, null: false
|
||||
t.string :client_name, null: false
|
||||
t.text :jwks_uri, null: false
|
||||
t.text :logo_uri
|
||||
t.string :token_endpoint_auth_method, null: false
|
||||
t.string :contacts, array: true, default: [], null: false, limit: 255
|
||||
t.text :client_uri
|
||||
t.text :policy_uri
|
||||
t.text :tos_uri
|
||||
t.text :scopes, array: true, default: [], null: false
|
||||
|
||||
t.references :root_account, foreign_key: { to_table: :accounts }, null: false, index: false
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_replica_identity "Lti::IMS::Registration", :root_account_id, 0
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :lti_ims_registrations
|
||||
end
|
||||
end
|
|
@ -22,12 +22,21 @@ module Schemas
|
|||
class Base
|
||||
delegate :validate, :valid?, to: :schema_checker
|
||||
|
||||
def self.simple_validation_errors(json_hash)
|
||||
def self.simple_validation_errors(json_hash, error_format: :string)
|
||||
error = new.validate(json_hash).to_a.first
|
||||
return nil if error.blank?
|
||||
|
||||
if error["data_pointer"].present?
|
||||
if error_format == :hash
|
||||
return {
|
||||
error: error["data"],
|
||||
field: error["data_pointer"],
|
||||
schema: error["schema"]
|
||||
}
|
||||
else
|
||||
return "#{error["data"]} #{error["data_pointer"]}. Schema: #{error["schema"]}"
|
||||
end
|
||||
end
|
||||
|
||||
"The following fields are required: #{error.dig("schema", "required").join(", ")}"
|
||||
end
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2020 - 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/>.
|
||||
#
|
||||
|
||||
# Schema for a https://purl.imsglobal.org/spec/lti-tool-configuration
|
||||
module Schemas::Lti::IMS
|
||||
class LtiToolConfiguration < Schemas::Base
|
||||
SCHEMA = {
|
||||
"type" => "object",
|
||||
"required" => %w[
|
||||
domain
|
||||
messages
|
||||
claims
|
||||
].freeze,
|
||||
"properties" => {
|
||||
"domain" => { "type" => "string" }.freeze,
|
||||
"secondary_domains" => {
|
||||
"type" => "array",
|
||||
"items" => {
|
||||
"type" => "string"
|
||||
}.freeze
|
||||
}.freeze,
|
||||
"target_link_uri" => { "type" => "string" }.freeze,
|
||||
"custom_parameters" => {
|
||||
"type" => "object",
|
||||
"additionalProperties" => {
|
||||
"type" => "string"
|
||||
}.freeze
|
||||
}.freeze,
|
||||
"description" => { "type" => "string" }.freeze,
|
||||
"messages" => {
|
||||
"type" => "array",
|
||||
"items" => {
|
||||
"type" => "object",
|
||||
"required" => [
|
||||
"type"
|
||||
].freeze,
|
||||
"properties" => {
|
||||
"type" => {
|
||||
"type" => "string",
|
||||
"enum" => Lti::ResourcePlacement::PLACEMENTS_BY_MESSAGE_TYPE.keys
|
||||
}.freeze,
|
||||
"target_link_uri" => { "type" => "string" }.freeze,
|
||||
"label" => { "type" => "string" }.freeze,
|
||||
"icon_uri" => { "type" => "string" }.freeze,
|
||||
"custom_parameters" => {
|
||||
"type" => "object",
|
||||
"additionalProperties" => {
|
||||
"type" => "string"
|
||||
}.freeze
|
||||
}.freeze,
|
||||
"placements" => {
|
||||
"type" => "array",
|
||||
"items" => {
|
||||
"type" => "string",
|
||||
"enum" => Lti::ResourcePlacement::PLACEMENTS.map(&:to_s)
|
||||
}.freeze
|
||||
}.freeze,
|
||||
}.freeze
|
||||
}
|
||||
}.freeze,
|
||||
"claims" => {
|
||||
"type" => "array",
|
||||
"items" => {
|
||||
"type" => "string"
|
||||
}.freeze
|
||||
}.freeze,
|
||||
}.freeze
|
||||
}.freeze
|
||||
|
||||
TYPE = "https://purl.imsglobal.org/spec/lti-tool-configuration"
|
||||
|
||||
def schema
|
||||
SCHEMA
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,242 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2023 - 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/>.
|
||||
#
|
||||
|
||||
module Lti::IMS
|
||||
describe Registration do
|
||||
let(:application_type) { :web }
|
||||
let(:grant_types) { [:client_credentials, :implicit] }
|
||||
let(:response_types) { [:id_token] }
|
||||
let(:redirect_uris) { ["http://example.com"] }
|
||||
let(:initiate_login_uri) { "http://example.com/login" }
|
||||
let(:client_name) { "Example Tool" }
|
||||
let(:jwks_uri) { "http://example.com/jwks" }
|
||||
let(:logo_uri) { "http://example.com/logo.png" }
|
||||
let(:client_uri) { "http://example.com/" }
|
||||
let(:tos_uri) { "http://example.com/tos" }
|
||||
let(:policy_uri) { "http://example.com/policy" }
|
||||
let(:token_endpoint_auth_method) { "private_key_jwt" }
|
||||
let(:lti_tool_configuration) do
|
||||
{
|
||||
domain: "example.com",
|
||||
messages: [],
|
||||
claims: []
|
||||
}
|
||||
end
|
||||
let(:scopes) { [] }
|
||||
|
||||
let(:registration) do
|
||||
r = Registration.new({
|
||||
application_type: application_type,
|
||||
grant_types: grant_types,
|
||||
response_types: response_types,
|
||||
redirect_uris: redirect_uris,
|
||||
initiate_login_uri: initiate_login_uri,
|
||||
client_name: client_name,
|
||||
jwks_uri: jwks_uri,
|
||||
logo_uri: logo_uri,
|
||||
client_uri: client_uri,
|
||||
tos_uri: tos_uri,
|
||||
policy_uri: policy_uri,
|
||||
token_endpoint_auth_method: token_endpoint_auth_method,
|
||||
lti_tool_configuration: lti_tool_configuration,
|
||||
scopes: scopes
|
||||
}.compact)
|
||||
r.developer_key = developer_key
|
||||
r
|
||||
end
|
||||
let(:developer_key) { DeveloperKey.create }
|
||||
|
||||
describe "validations" do
|
||||
subject { registration.validate }
|
||||
|
||||
context "when valid" do
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context "application_type" do
|
||||
context "is \"web\"" do
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context "is not \"web\"" do
|
||||
let(:application_type) { "native" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context "is not included" do
|
||||
let(:application_type) { nil }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "grant_types" do
|
||||
context "includes other types" do
|
||||
let(:grant_types) { %i[client_credentials implicit foo bar] }
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context "does not include implicit" do
|
||||
let(:grant_types) { [:client_credentials, :foo] }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context "does not include client_credentials" do
|
||||
let(:grant_types) { [:implicit, :foo] }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "response_types" do
|
||||
context "includes other types" do
|
||||
let(:response_types) { %i[id_token foo bar] }
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context "is not included" do
|
||||
let(:response_types) { nil }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context "does not include id_token" do
|
||||
let(:response_types) { [:foo, :bar] }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "redirect_uris" do
|
||||
context "includes valid uris" do
|
||||
let(:redirect_uris) { ["https://example.com", "https://example.com/foo"] }
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context "is not included" do
|
||||
let(:redirect_uris) { nil }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context "includes a non-url" do
|
||||
let(:redirect_uris) { ["https://example.com", "asdf"] }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "initiate_login_uri" do
|
||||
context "is not included" do
|
||||
let(:initiate_login_uri) { nil }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context "is a valid uri" do
|
||||
let(:initiate_login_uri) { "http://example.com/login" }
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
|
||||
context "is not a valid uri" do
|
||||
let(:initiate_login_uri) { "asdf" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "client_name" do
|
||||
context "is not included" do
|
||||
let(:client_name) { nil }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "jwks_uri" do
|
||||
context "is not included" do
|
||||
let(:jwks_uri) { nil }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context "is not a valid uri" do
|
||||
let(:jwks_uri) { "asdf" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "token_endpoint_auth_method" do
|
||||
context "is not \"private_key_jwt\"" do
|
||||
let(:token_endpoint_auth_method) { "asdf" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "logo_uri" do
|
||||
context "is not a valid uri" do
|
||||
let(:logo_uri) { "asdf" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "client_uri" do
|
||||
context "is not a valid uri" do
|
||||
let(:client_uri) { "asdf" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "tos_uri" do
|
||||
context "is not a valid uri" do
|
||||
let(:tos_uri) { "asdf" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "policy_uri" do
|
||||
context "is not a valid uri" do
|
||||
let(:policy_uri) { "asdf" }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
context "scopes" do
|
||||
context "contains invalid scopes" do
|
||||
let(:scopes) { ["asdf"] }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue