canvas-lms/spec/models/developer_key_spec.rb

1003 lines
32 KiB
Ruby

#
# 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__) + '/../sharding_spec_helper.rb')
require File.expand_path(File.dirname(__FILE__) + '/../lti_1_3_spec_helper')
describe DeveloperKey do
let(:account) { Account.create! }
let(:developer_key_saved) do
DeveloperKey.create(
name: 'test',
email: 'test@test.com',
redirect_uri: 'http://test.com',
account_id: account.id
)
end
# Tests that use this key will run faster because they don't need to
# save an account and a developer_key to the db
let(:developer_key_not_saved) do
DeveloperKey.new(
name: 'test',
email: 'test@test.com',
redirect_uri: 'http://test.com',
)
end
describe 'default values for is_lti_key' do
let(:public_jwk) do
key_hash = Lti::RSAKeyPair.new.public_jwk.to_h
key_hash['kty'] = key_hash['kty'].to_s
key_hash
end
let(:public_jwk_url) { "https://hello.world.com" }
it 'throws error if public jwk and public jwk are absent' do
expect { DeveloperKey.create!(is_lti_key: true) }.to raise_error ActiveRecord::RecordInvalid
end
it 'validates when public jwk is present' do
expect { DeveloperKey.create!(is_lti_key: true, public_jwk: public_jwk) }.to_not raise_error
end
it 'validates when public jwk url is present' do
expect { DeveloperKey.create!(is_lti_key: true, public_jwk_url: public_jwk_url) }.to_not raise_error
end
end
describe 'external tool management' do
specs_require_sharding
include_context 'lti_1_3_spec_helper'
let(:developer_key) { @shard1.activate { DeveloperKey.create! } }
let(:shard_1_account) { @shard1.activate { account_model } }
let(:shard_1_tool) do
tool = nil
@shard1.activate do
tool = ContextExternalTool.create!(
name: 'shard 1 tool',
workflow_state: 'public',
developer_key: developer_key,
context: shard_1_account,
url: 'https://www.test.com',
consumer_key: 'key',
shared_secret: 'secret'
)
DeveloperKeyAccountBinding.create!(
developer_key: tool.developer_key,
account: shard_1_account,
workflow_state: 'on'
)
end
tool
end
let(:shard_2_account) { @shard2.activate { account_model } }
let(:shard_2_tool) do
tool = nil
@shard2.activate do
tool = ContextExternalTool.create!(
name: 'shard 2 tool',
workflow_state: 'public',
developer_key: developer_key,
context: shard_2_account,
url: 'https://www.test.com',
consumer_key: 'key',
shared_secret: 'secret'
)
DeveloperKeyAccountBinding.create!(
developer_key: tool.developer_key,
account: shard_2_account,
workflow_state: 'off'
)
end
tool
end
describe '#restore_external_tools!' do
before do
developer_key
@shard1.activate { tool_configuration }
shard_1_tool.update!(root_account: shard_1_account)
shard_2_tool.update!(root_account: shard_2_account)
subject
end
context 'when account is site admin' do
subject do
@shard1.activate do
developer_key.restore_external_tools!(account)
run_jobs
end
end
let(:account) { Account.site_admin }
it 'restores tools in non-disabled states' do
expect(shard_1_tool.reload.workflow_state).to eq 'public'
end
it 'restores tools in disabled states' do
expect(shard_2_tool.reload.workflow_state).to eq 'disabled'
end
end
end
describe '#disable_external_tools!' do
before do
developer_key
@shard1.activate { tool_configuration }
shard_1_tool
shard_2_tool
subject
end
context 'when account is site admin' do
subject do
@shard1.activate do
developer_key.disable_external_tools!(account)
run_jobs
end
end
let(:account) { Account.site_admin }
it 'disables tools on shard 1' do
expect(shard_1_tool.reload.workflow_state).to eq 'disabled'
end
it 'disables tools on shard 2' do
expect(shard_2_tool.reload.workflow_state).to eq 'disabled'
end
end
context 'account is not site admin' do
subject do
@shard1.activate do
developer_key.disable_external_tools!(account)
run_jobs
end
end
let(:account) { shard_1_tool.root_account }
it 'disables associated tools on the active shard' do
expect(shard_1_tool.reload.workflow_state).to eq 'disabled'
end
it 'does not disable tools on inactive shards' do
expect(shard_2_tool.reload.workflow_state).to eq 'public'
end
end
end
describe '#enable_external_tools!' do
subject do
@shard1.activate do
developer_key.enable_external_tools!(account)
run_jobs
end
end
before do
developer_key
@shard1.activate { tool_configuration.update!(privacy_level: 'anonymous') }
shard_1_tool.update!(workflow_state: 'disabled')
shard_2_tool.update!(workflow_state: 'disabled')
subject
end
context 'account is site admin' do
let(:account) { Account.site_admin }
it 'enables tools on shard 1' do
expect(shard_1_tool.reload.workflow_state).to eq 'anonymous'
end
it 'enables tools on shard 2' do
expect(shard_2_tool.reload.workflow_state).to eq 'anonymous'
end
end
context 'is_site_admin is false' do
let(:account) { shard_1_tool.root_account }
it 'enables tools on shard 1' do
expect(shard_1_tool.reload.workflow_state).to eq 'anonymous'
end
it 'does not enable tools on shard 2' do
expect(shard_2_tool.reload.workflow_state).to eq 'disabled'
end
end
end
describe '#update_external_tools!' do
subject do
@shard1.activate do
tool_configuration.settings['title'] = new_title
tool_configuration.save!
developer_key.update_external_tools!
run_jobs
end
end
let(:new_title) { 'New Title!' }
before do
developer_key
@shard1.activate { tool_configuration.update!(privacy_level: 'anonymous') }
shard_1_tool.update!(workflow_state: 'disabled')
shard_2_tool.update!(workflow_state: 'disabled')
end
context 'when site admin key' do
before do
developer_key.update!(account: nil)
subject
run_jobs
end
it 'updates tools on all shard 1' do
expect(shard_1_tool.reload.name).to eq new_title
end
it 'updates tools on shard 2' do
expect(shard_2_tool.reload.name).to eq new_title
end
end
context 'when non-site admin key' do
before do
developer_key.update!(account: shard_1_account)
subject
run_jobs
end
it 'updates tools on shard 1' do
expect(shard_1_tool.reload.name).to eq new_title
end
it 'does not update tools on shard 2' do
expect(shard_2_tool.reload.name).to eq 'shard 2 tool'
end
end
end
end
describe 'usable_in_context?' do
let(:account) { account_model }
let(:developer_key) { DeveloperKey.create!(account: account) }
shared_examples_for 'a boolean indicating the key is usable in context' do
subject { developer_key.usable_in_context?(context) }
let(:context) { raise 'set in examples' }
context 'when the key is usable and the binding is on' do
before do
developer_key.account_binding_for(account).update!(workflow_state: 'on')
end
it { is_expected.to eq true }
end
context 'when the key is not usable' do
before { developer_key.update!(workflow_state: 'deleted') }
it { is_expected.to eq false }
end
context 'when the binding is not on' do
it { is_expected.to eq false }
end
end
context 'when the context is an account' do
it_behaves_like 'a boolean indicating the key is usable in context' do
let(:context) { account }
end
end
context 'when the context is a course' do
it_behaves_like 'a boolean indicating the key is usable in context' do
let(:context) { course_model(account: account) }
end
end
end
describe "site_admin_lti scope" do
specs_require_sharding
include_context 'lti_1_3_spec_helper'
context "when root account and site admin keys exist" do
subject do
DeveloperKey.site_admin_lti(
[
root_account_key,
site_admin_key,
lti_site_admin_key
].map(&:global_id)
)
end
let(:root_account_key) do
@shard1.activate do
a = account_model
DeveloperKey.create!(
account: a,
tool_configuration: tool_configuration.dup
)
end
end
let(:site_admin_key) do
Account.site_admin.shard.activate do
DeveloperKey.create!
end
end
let(:lti_site_admin_key) do
Account.site_admin.shard.activate do
k = DeveloperKey.create!
Lti::ToolConfiguration.create!(
developer_key: k,
settings: settings.merge(public_jwk: tool_config_public_jwk)
)
k
end
end
it { is_expected.to match_array [lti_site_admin_key] }
end
end
describe "sets a default value" do
it "when visible is not specified" do
expect(developer_key_not_saved.valid?).to eq(true)
expect(developer_key_not_saved.visible).to eq(false)
end
it "is false for site admin generated keys" do
key = DeveloperKey.create!(
name: 'test',
email: 'test@test.com',
redirect_uri: 'http://test.com',
account_id: nil
)
expect(key.visible).to eq(false)
end
it "is true for non site admin generated keys" do
key = DeveloperKey.create!(
name: 'test',
email: 'test@test.com',
redirect_uri: 'http://test.com',
account_id: account.id
)
expect(key.visible).to eq(true)
end
end
describe 'callbacks' do
describe 'public_jwk validations' do
subject do
developer_key_saved.save
end
before { developer_key_saved.generate_rsa_keypair! }
context 'when the kty is not "RSA"' do
before { developer_key_saved.public_jwk['kty'] = 'foo' }
it { is_expected.to eq false }
end
context 'when the alg is not "RS256"' do
before { developer_key_saved.public_jwk['alg'] = 'foo' }
it { is_expected.to eq false }
end
context 'when required claims are missing' do
before { developer_key_saved.update public_jwk: {foo: 'bar'} }
it { is_expected.to eq false }
end
end
it 'does validate scopes' do
expect do
DeveloperKey.create!(
scopes: ['not_a_valid_scope']
)
end.to raise_exception ActiveRecord::RecordInvalid
end
context 'when api token scoping FF is enabled' do
let(:valid_scopes) do
%w(url:POST|/api/v1/courses/:course_id/quizzes/:id/validate_access_code
url:GET|/api/v1/audit/grade_change/courses/:course_id/assignments/:assignment_id/graders/:grader_id)
end
describe 'before_save' do
subject do
key.save!
key.require_scopes
end
context 'when a public jwk is set' do
let(:key) do
developer_key_not_saved.generate_rsa_keypair!
developer_key_not_saved
end
it { is_expected.to eq true }
end
context 'when a public jwk is not set' do
let(:key) { developer_key_not_saved }
it { is_expected.to eq false }
end
context 'when a key requires scopes but has no public jwk' do
let(:key) do
developer_key_not_saved.update!(
require_scopes: true,
public_jwk: nil
)
developer_key_not_saved
end
it { is_expected.to eq true }
end
end
describe 'after_update' do
let(:user) { user_model }
let(:developer_key_with_scopes) { DeveloperKey.create!(scopes: valid_scopes) }
let(:access_token) { user.access_tokens.create!(developer_key: developer_key_with_scopes) }
let(:valid_scopes) do
[
"url:GET|/api/v1/courses/:course_id/quizzes",
"url:GET|/api/v1/courses/:course_id/quizzes/:id",
"url:GET|/api/v1/courses/:course_id/users",
"url:GET|/api/v1/courses/:id",
"url:GET|/api/v1/users/:user_id/profile",
"url:POST|/api/v1/courses/:course_id/assignments",
"url:POST|/api/v1/courses/:course_id/quizzes",
]
end
before { access_token }
it 'deletes its associated access tokens if scopes are removed' do
developer_key_with_scopes.update!(scopes: [valid_scopes.first])
expect(developer_key_with_scopes.access_tokens).to be_empty
end
it 'does not delete its associated access tokens if scopes are not changed' do
developer_key_with_scopes.update!(email: 'test@test.com')
expect(developer_key_with_scopes.access_tokens).to match_array [access_token]
end
it 'does not delete its associated access tokens if a new scope was added' do
developer_key_with_scopes.update!(scopes: valid_scopes.push("url:PUT|/api/v1/courses/:course_id/quizzes/:id"))
expect(developer_key_with_scopes.access_tokens).to match_array [access_token]
end
end
it 'raises an error if scopes contain invalid scopes' do
expect do
DeveloperKey.create!(
scopes: ['not_a_valid_scope']
)
end.to raise_exception('Validation failed: Scopes cannot contain not_a_valid_scope')
end
it 'does not raise an error if all scopes are valid scopes' do
expect do
DeveloperKey.create!(
scopes: valid_scopes
)
end.not_to raise_exception
end
end
context 'when site admin' do
let(:key) { DeveloperKey.create!(account: nil) }
it 'it creates a binding on save' do
expect(key.developer_key_account_bindings.find_by(account: Account.site_admin)).to be_present
end
describe 'destroy_external_tools!' do
include_context 'lti_1_3_spec_helper'
specs_require_sharding
subject do
@shard1.activate { ContextExternalTool.active }.merge(
@shard2.activate { ContextExternalTool.active }
)
end
context 'when developer key is an LTI key' do
let(:shard_1_account) { @shard1.activate { account_model } }
let(:shard_2_account) { @shard2.activate { account_model } }
let(:configuration) { Account.site_admin.shard.activate { tool_configuration } }
let(:shard_1_tool) do
t = @shard1.activate { configuration.new_external_tool(shard_1_account) }
t.save!
t
end
let(:shard_2_tool) do
t = @shard2.activate { configuration.new_external_tool(shard_2_account) }
t.save!
t
end
before do
shard_1_tool
shard_2_tool
developer_key.update!(account: nil)
end
it 'destroys associated tools across all shards' do
developer_key.destroy
run_jobs
expect(subject).to be_empty
end
context 'when tools are installed at the course level' do
let(:shard_1_course) { shard_1_account.shard.activate { course_model(account: shard_1_account) } }
let(:shard_1_course_tool) do
t = @shard1.activate { configuration.new_external_tool(shard_1_course) }
t.save!
t
end
before do
shard_1_course_tool
end
it 'destroys associated tools across all shards' do
developer_key.destroy
run_jobs
expect(subject).to be_empty
end
end
end
end
end
context 'when not site admin' do
it 'it creates a binding on save' do
key = DeveloperKey.create!(account: account)
expect(key.developer_key_account_bindings.find_by(account: account)).to be_present
end
describe 'destroy_external_tools!' do
include_context 'lti_1_3_spec_helper'
specs_require_sharding
subject { ContextExternalTool.active }
let(:account) { account_model }
let(:tool) do
t = tool_configuration.new_external_tool(account)
t.save!
t
end
before do
tool
end
context 'when developer key is an LTI key' do
it 'destroys associated tools on the current shard' do
developer_key.destroy
run_jobs
expect(subject).to be_empty
end
context 'when tools are installed at the course level' do
let(:course) { course_model(account: account) }
let(:course_tool) do
t = tool_configuration.new_external_tool(course)
t.save!
t
end
before { course_tool }
it 'destroys associated tools on the current shard' do
developer_key.destroy
run_jobs
expect(subject).to be_empty
end
end
end
end
end
end
describe 'associations' do
let(:developer_key_account_binding) { developer_key_saved.developer_key_account_bindings.first }
it 'destroys developer key account bindings when destroyed' do
binding_id = developer_key_account_binding.id
developer_key_saved.destroy_permanently!
expect(DeveloperKeyAccountBinding.find_by(id: binding_id)).to be_nil
end
it 'has many context external tools' do
tool = ContextExternalTool.create!(
context: account,
consumer_key: 'key',
shared_secret: 'secret',
name: 'test tool',
url: 'http://www.tool.com/launch',
developer_key: developer_key_saved
)
expect(developer_key_saved.context_external_tools).to match_array [
tool
]
end
end
describe '#account_binding_for' do
let(:site_admin_key) { DeveloperKey.create!(account: nil) }
let(:root_account_key) { DeveloperKey.create!(account: root_account) }
let(:root_account) { account_model }
context 'when account passed is nil' do
it 'returns nil' do
expect(site_admin_key.account_binding_for(nil)).to be_nil
expect(root_account_key.account_binding_for(nil)).to be_nil
end
end
context 'when site admin' do
context 'when binding state is "allow"' do
before do
site_admin_key.developer_key_account_bindings.create!(
account: root_account, workflow_state: DeveloperKeyAccountBinding::ALLOW_STATE
)
end
it 'finds the site admin binding when requesting site admin account' do
binding = site_admin_key.account_binding_for(Account.site_admin)
expect(binding.account).to eq Account.site_admin
end
it 'finds the root account binding when requesting root account' do
site_admin_key.developer_key_account_bindings.first.update!(workflow_state: DeveloperKeyAccountBinding::ALLOW_STATE)
binding = site_admin_key.account_binding_for(root_account)
expect(binding.account).to eq root_account
end
end
context 'when binding state is "on" or "off"' do
before do
site_admin_key.developer_key_account_bindings.create!(account: root_account, workflow_state: 'on')
sa_binding = site_admin_key.developer_key_account_bindings.find_by(account: Account.site_admin)
sa_binding.update!(workflow_state: 'off')
end
it 'finds the site admin binding when requesting site admin account' do
binding = site_admin_key.account_binding_for(Account.site_admin)
expect(binding.account).to eq Account.site_admin
end
it 'finds the site admin binding when requesting root account' do
binding = site_admin_key.account_binding_for(root_account)
expect(binding.account).to eq Account.site_admin
end
end
end
context 'when not site admin' do
context 'when binding state is "allow"' do
it 'finds the root account binding when requesting root account' do
binding = root_account_key.account_binding_for(root_account)
expect(binding.account).to eq root_account
end
end
context 'when binding state is "on" or "off"' do
before do
root_account_key.developer_key_account_bindings.create!(account: Account.site_admin, workflow_state: 'on')
ra_binding = root_account_key.developer_key_account_bindings.find_by(account: root_account)
ra_binding.update!(workflow_state: 'off')
end
it 'finds the site admin binding when requesting site admin account' do
binding = root_account_key.account_binding_for(Account.site_admin)
expect(binding.account).to eq Account.site_admin
end
it 'finds the root account binding when requesting root account' do
binding = root_account_key.account_binding_for(root_account)
expect(binding.account).to eq root_account
end
end
end
context 'sharding' do
specs_require_sharding
let(:root_account_shard) { @shard1 }
let(:root_account) { root_account_shard.activate { account_model } }
let(:sa_developer_key) { Account.site_admin.shard.activate { DeveloperKey.create!(name: 'SA Key') } }
let(:root_account_binding) do
root_account_shard.activate do
DeveloperKeyAccountBinding.create!(
account_id: root_account.id,
developer_key_id: sa_developer_key.global_id
)
end
end
let(:sa_account_binding) { sa_developer_key.developer_key_account_bindings.find_by(account: Account.site_admin) }
context 'when developer key binding is on the site admin shard' do
it 'finds the site admin binding if it is set to "on"' do
root_account_binding.update!(workflow_state: 'on')
sa_account_binding.update!(workflow_state: 'off')
binding = root_account_shard.activate { sa_developer_key.account_binding_for(root_account) }
expect(binding.account).to eq Account.site_admin
end
it 'finds the site admin binding if it is set to "off"' do
root_account_binding.update!(workflow_state: 'off')
sa_account_binding.update!(workflow_state: 'on')
binding = root_account_shard.activate { sa_developer_key.account_binding_for(root_account) }
expect(binding.account).to eq Account.site_admin
end
it 'finds the root account binding if site admin binding is set to "allow"' do
root_account_binding.update!(workflow_state: 'on')
sa_account_binding.update!(workflow_state: 'allow')
binding = root_account_shard.activate { sa_developer_key.account_binding_for(root_account) }
expect(binding.account).to eq root_account
end
end
end
end
describe "default" do
context "sharding" do
specs_require_sharding
it "should always create the default key on the default shard" do
@shard1.activate do
expect(DeveloperKey.default.shard).to be_default
end
end
it 'sets new developer keys to auto expire tokens' do
expect(developer_key_saved.auto_expire_tokens).to be_truthy
end
it 'uses integer special keys properly because the query does not like strings' do
# this test mirrors what happens in production when retrieving keys, but does not test it
# directly because there's a short circuit clause in 'get_special_key' that pops out with a
# different finder because of the transactions-in-test issue. this confirms that setting
# a key id does not translate it to a string and therefore can be used with 'where(id: key_id)'
# safely
Setting.set('rspec_developer_key_id', developer_key_saved.id)
key_id = Setting.get('rspec_developer_key_id', nil)
expect(DeveloperKey.where(id: key_id).first).to eq(developer_key_saved)
end
end
end
it "allows non-http redirect URIs" do
developer_key_not_saved.redirect_uri = 'tealpass://somewhere.edu/authentication'
developer_key_not_saved.redirect_uris = ['tealpass://somewhere.edu/authentication']
expect(developer_key_not_saved).to be_valid
end
it "returns the correct count of access_tokens" do
expect(developer_key_saved.access_token_count).to eq 0
AccessToken.create!(:user => user_model, :developer_key => developer_key_saved)
AccessToken.create!(:user => user_model, :developer_key => developer_key_saved)
AccessToken.create!(:user => user_model, :developer_key => developer_key_saved)
expect(developer_key_saved.access_token_count).to eq 3
end
it "returns the last_used_at value for a key" do
expect(developer_key_saved.last_used_at).to be_nil
at = AccessToken.create!(:user => user_model, :developer_key => developer_key_saved)
at.used!
expect(developer_key_saved.last_used_at).not_to be_nil
end
describe '#generate_rsa_keypair!' do
context 'when "public_jwk" is already set' do
subject do
developer_key.generate_rsa_keypair!
developer_key
end
let(:developer_key) do
key = DeveloperKey.create!
key.generate_rsa_keypair!
key.save!
key
end
let(:public_jwk) { developer_key.public_jwk }
context 'when "override" is false' do
it 'does not change the "public_jwk"' do
expect(subject.public_jwk).to eq public_jwk
end
it 'does not change the "private_jwk" attribute' do
previous_private_key = developer_key.private_jwk
expect(subject.private_jwk).to eq previous_private_key
end
end
context 'when "override: is true' do
subject do
developer_key.generate_rsa_keypair!(overwrite: true)
developer_key
end
it 'does change the "public_jwk"' do
previous_public_key = developer_key.public_jwk
expect(subject.public_jwk).not_to eq previous_public_key
end
it 'does change the "private_jwk"' do
previous_private_key = developer_key.private_jwk
expect(subject.private_jwk).not_to eq previous_private_key
end
end
end
context 'when "public_jwk" is not set' do
subject { DeveloperKey.new }
before { subject.generate_rsa_keypair! }
it 'populates the "public_jwk" column with a public key' do
expect(subject.public_jwk['kty']).to eq Lti::RSAKeyPair::KTY
end
it 'populates the "private_jwk" attribute with a private key' do
expect(subject.private_jwk['kty']).to eq Lti::RSAKeyPair::KTY.to_sym
end
end
end
describe "#redirect_domain_matches?" do
it "should match domains exactly, and sub-domains" do
developer_key_not_saved.redirect_uri = "http://example.com/a/b"
expect(developer_key_not_saved.redirect_domain_matches?("http://example.com/a/b")).to be_truthy
# other paths on the same domain are ok
expect(developer_key_not_saved.redirect_domain_matches?("http://example.com/other")).to be_truthy
# completely separate domain
expect(developer_key_not_saved.redirect_domain_matches?("http://example2.com/a/b")).to be_falsey
# not a sub-domain
expect(developer_key_not_saved.redirect_domain_matches?("http://wwwexample.com/a/b")).to be_falsey
expect(developer_key_not_saved.redirect_domain_matches?("http://example.com.evil/a/b")).to be_falsey
expect(developer_key_not_saved.redirect_domain_matches?("http://www.example.com.evil/a/b")).to be_falsey
# sub-domains are ok
expect(developer_key_not_saved.redirect_domain_matches?("http://www.example.com/a/b")).to be_truthy
expect(developer_key_not_saved.redirect_domain_matches?("http://a.b.example.com/a/b")).to be_truthy
expect(developer_key_not_saved.redirect_domain_matches?("http://a.b.example.com/other")).to be_truthy
end
it "does not allow subdomains when it matches in redirect_uris" do
developer_key_not_saved.redirect_uris << "http://example.com/a/b"
expect(developer_key_not_saved.redirect_domain_matches?("http://example.com/a/b")).to eq true
# other paths on the same domain are NOT ok
expect(developer_key_not_saved.redirect_domain_matches?("http://example.com/other")).to eq false
# sub-domains are not ok either
expect(developer_key_not_saved.redirect_domain_matches?("http://www.example.com/a/b")).to eq false
expect(developer_key_not_saved.redirect_domain_matches?("http://a.b.example.com/a/b")).to eq false
expect(developer_key_not_saved.redirect_domain_matches?("http://a.b.example.com/other")).to eq false
end
end
context "Account scoped keys" do
shared_examples "authorized_for_account?" do
it "should allow access to its own account" do
expect(@key.authorized_for_account?(Account.find(@account.id))).to be true
end
it "shouldn't allow access to a foreign account" do
expect(@key.authorized_for_account?(@not_sub_account)).to be false
end
it "allows access if the account is in its account chain" do
sub_account = Account.create!(parent_account: @account)
expect(@key.authorized_for_account?(sub_account)).to be true
end
end
context 'with sharding' do
specs_require_sharding
before :once do
@account = Account.create!
@not_sub_account = Account.create!
@key = DeveloperKey.create!(:redirect_uri => "http://example.com/a/b", account: @account)
enable_developer_key_account_binding!(@key)
end
include_examples "authorized_for_account?"
describe '#by_cached_vendor_code' do
let(:vendor_code) { 'tool vendor code' }
let(:not_site_admin_shard) { @shard1 }
it 'finds keys in the site admin shard' do
site_admin_key = nil
Account.site_admin.shard.activate do
site_admin_key = DeveloperKey.create!(vendor_code: vendor_code)
end
not_site_admin_shard.activate do
expect(DeveloperKey.by_cached_vendor_code(vendor_code)).to include site_admin_key
end
end
it 'finds keys in the current shard' do
local_key = DeveloperKey.create!(vendor_code: vendor_code, account: account_model)
expect(DeveloperKey.by_cached_vendor_code(vendor_code)).to include local_key
end
end
end
context 'without sharding' do
before :once do
@account = Account.create!
@not_sub_account = Account.create!
@key = DeveloperKey.create!(:redirect_uri => "http://example.com/a/b", account: @account)
enable_developer_key_account_binding!(@key)
end
include_examples "authorized_for_account?"
end
end
it "doesn't allow the default key to be deleted" do
expect { DeveloperKey.default.destroy }.to raise_error "Please never delete the default developer key"
expect { DeveloperKey.default.deactivate }.to raise_error "Please never delete the default developer key"
end
end