require 'spec_helper'
describe Socialcast::CommandLine::ProvisionUser do
let!(:credentials) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'credentials.yml')) }
let!(:ldap_default_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap.yml')) }
let!(:ldap_blank_basedn_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_blank_basedn.yml')) }
let!(:ldap_connection_permission_mapping_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_connection_permission_mapping.yml')) }
let!(:ldap_multiple_connection_mapping_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_multiple_connection_mappings.yml')) }
let!(:ldap_multiple_connection_permission_mapping_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_multiple_connection_permission_mappings.yml')) }
let!(:ldap_with_account_type_without_roles_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_account_type_without_roles.yml')) }
let!(:ldap_with_class_ldap_attribute_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_class_ldap_attribute.yml')) }
let!(:ldap_with_custom_attributes_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_custom_attributes.yml')) }
let!(:ldap_with_manager_attribute_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_manager_attribute.yml')) }
let!(:ldap_with_roles_without_account_type_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_roles_without_account_type.yml')) }
let!(:ldap_with_unique_identifier_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_unique_identifier.yml')) }
let!(:ldap_with_profile_photo_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_with_profile_photo.yml')) }
let!(:ldap_without_account_type_or_roles_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_without_account_type_or_roles.yml')) }
let!(:ldap_without_options_config) { YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'ldap_without_options.yml')) }
let(:ldap) do
ldap_instance = double(Net::LDAP, :auth => nil, :encryption => nil)
ldap_instance.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).and_return(ldap_instance)
ldap_instance
end
describe "#provision" do
let(:result) { '' }
before do
Zlib::GzipWriter.stub(:open).and_yield(result)
Socialcast::CommandLine.stub(:credentials).and_return(credentials)
File.stub(:open).with(/users.xml.gz/, anything).and_yield(result)
end
context "when the entry has an email" do
before do
entry = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
end
it "puts the user in the output file" do
result.should =~ /user@example.com/
end
end
context "when the entry has a unique_identifier" do
before do
entry = create_entry 'user', :uid => 'userID', :givenName => 'first name', :sn => 'last name'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'uid', 'isMemberOf'])).and_yield(entry)
RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
Socialcast::CommandLine::ProvisionUser.new(ldap_with_unique_identifier_config, {}).provision
end
it "puts the user in the output file" do
result.should =~ /userID/
end
end
context "when the entry has no email or unique_identifier" do
before do
entry = create_entry 'user', :mail => '', :givenName => 'first name', :sn => 'last name'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
RestClient::Resource.any_instance.should_not_receive(:post)
end
it "does not put the user in the output file" do
expect do
Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
end.to raise_error(Socialcast::CommandLine::ProvisionUser::ProvisionError, "Skipping upload to Socialcast since no users were found")
end
end
context "attribute mappings" do
shared_examples "attributes are mapped properly" do
it do
users = Array.wrap(expected_attribute_xml).inject('') do |users_str, user_xml|
users_str << %Q[
#{user_xml}
member
]
end
result.gsub(/\s/, '').should == %Q[
#{users}
].gsub(/\s/, '')
end
end
before do
RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
end
context "with mappings at the global level" do
before do
entry = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
end
let(:expected_attribute_xml) do
%Q[first name
last name
user@example.com
]
end
it_behaves_like "attributes are mapped properly"
end
context "with mappings at the connection level" do
before do
provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {})
ldap_instance1 = double(Net::LDAP, :encryption => nil, :auth => nil)
ldap_instance1.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance1)
entry1 = create_entry 'user', :mailCon => 'user@example.com', :givenName => 'first name', :sn => 'last name'
ldap_instance1.should_receive(:search).once.with(hash_including(:attributes => ['mailCon', 'isMemberOf'])).and_yield(entry1)
ldap_instance2 = double(Net::LDAP, :encryption => nil, :auth => nil)
ldap_instance2.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance2)
entry2 = create_entry 'user', :mailCon2 => 'user2@example.com', :firstName => 'first name2', :sn => 'last name2'
ldap_instance2.should_receive(:search).once.with(hash_including(:attributes => ['mailCon2', 'firstName', 'isMemberOf'])).and_yield(entry2)
provision_instance.provision
end
let(:expected_attribute_xml) do
[%Q[
user@example.com
],
%Q[first name2
user2@example.com
]
]
end
it_behaves_like "attributes are mapped properly"
end
context "with custom attribute mappings" do
before do
entry = create_entry 'user', :mail => 'user@example.com', :custom_ldap1 => 'custom value 1', :custom_ldap2 => 'custom value 2'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['custom_ldap1', 'custom_ldap2', 'mail', 'isMemberOf'])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_with_custom_attributes_config, {}).provision
end
let(:expected_attribute_xml) do
%Q[
user@example.com
custom_attr1
custom value 1
custom_attr2
custom value 2
]
end
it_behaves_like "attributes are mapped properly"
end
context "with manager" do
before do
provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_with_manager_attribute_config, {})
ldap_instance = double(Net::LDAP, :encryption => nil, :auth => nil)
ldap_instance.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.and_return(ldap_instance)
user_entry = create_entry 'user', :mail => 'user@example.com', :ldap_manager => 'cn=theboss,dc=example,dc=com'
manager_entry = create_entry 'theboss', :mail => 'boss@example.com'
ldap_instance.should_receive(:search).once.ordered.and_yield(user_entry).and_yield(manager_entry)
ldap_instance.should_receive(:search).once.ordered.and_yield(user_entry).and_yield(manager_entry)
provision_instance.provision
end
let(:expected_attribute_xml) do
[%Q[
user@example.com
manager_email
boss@example.com
],
%Q[
boss@example.com
manager_email
]]
end
it_behaves_like "attributes are mapped properly"
end
context "with an ldap mapping that has the same name as a class" do
before do
module TestLdapAttributeMapping end
entry = create_entry 'user', :test_ldap_attribute_mapping => 'user@example.com'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['test_ldap_attribute_mapping', 'isMemberOf'])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_with_class_ldap_attribute_config, {}).provision
end
after do
Object.send(:remove_const, :TestLdapAttributeMapping)
end
let(:expected_attribute_xml) do
%Q[
user@example.com
]
end
it_behaves_like "attributes are mapped properly"
end
context "without options specified" do
before do
entry = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_without_options_config, {}).provision
end
let(:expected_attribute_xml) do
%Q[first name
last name
user@example.com
]
end
it_behaves_like "attributes are mapped properly"
end
context "with profile_photo attribute mappings" do
before do
entry = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'memberof'])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_with_profile_photo_config, {}).provision
end
let(:expected_attribute_xml) do
%Q[first name
last name
user@example.com
]
end
it_behaves_like "attributes are mapped properly"
end
end
context "permission attribute mappings" do
shared_examples "permission attributes are mapped properly" do
it do
users = Array.wrap(expected_permission_xml).inject('') do |users_str, permission_xml|
users_str << %Q[
first name
last name
user@example.com
#{permission_xml}
]
end
result.gsub(/\s/, '').should == %Q[
#{users}
].gsub(/\s/, '')
end
end
let(:entry) { create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name', :isMemberOf => ldap_groups }
let(:ldap_group_attribute) { 'isMemberOf' }
before do
RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
end
context "with roles for an external contributor" do
let(:ldap_groups) { ["cn=External,dc=example,dc=com", "cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
before do
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
end
let(:expected_permission_xml) do
%Q[external]
end
it_behaves_like "permission attributes are mapped properly"
end
context "with roles for a member" do
let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
before do
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_default_config, {}).provision
end
let(:expected_permission_xml) do
%Q[member
sbi_admin
town_hall_admin
]
end
it_behaves_like "permission attributes are mapped properly"
end
context "with account_types mapping and no role mappings" do
let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
before do
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_with_account_type_without_roles_config, {}).provision
end
let(:expected_permission_xml) do
%Q[member]
end
it_behaves_like "permission attributes are mapped properly"
end
context "with role mappings and no account_type mapping" do
let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
before do
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_with_roles_without_account_type_config, {}).provision
end
let(:expected_permission_xml) do
%Q[member
sbi_admin
town_hall_admin
]
end
it_behaves_like "permission attributes are mapped properly"
end
context "without account_type or roles mappings" do
let(:ldap_groups) { ["cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"] }
before do
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', ldap_group_attribute])).and_yield(entry)
Socialcast::CommandLine::ProvisionUser.new(ldap_without_account_type_or_roles_config, {}).provision
end
let(:expected_permission_xml) do
%Q[member]
end
it_behaves_like "permission attributes are mapped properly"
end
context "with permission mappings at the connection level" do
let(:ldap_group_attribute) { 'memberOf' }
let(:ldap_groups) { }
before do
provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_permission_mapping_config, {})
ldap_instance1 = double(Net::LDAP, :encryption => nil, :auth => nil)
ldap_instance1.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance1)
entry1 = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name', :memberOf => ["cn=External,dc=example,dc=com", "cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"]
ldap_instance1.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'memberOf'])).and_yield(entry1)
ldap_instance2 = double(Net::LDAP, :encryption => nil, :auth => nil)
ldap_instance2.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance2)
entry2 = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name', :member => ["cn=Contractors,dc=example,dc=com", "cn=SbiAdmins,dc=example,dc=com", "cn=TownHallAdmins,dc=example,dc=com"]
ldap_instance2.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'member'])).and_yield(entry2)
provision_instance.provision
end
let(:expected_permission_xml) do
[%Q[member
reach_admin
],
%Q[member
sbi_admin
]]
end
it_behaves_like "permission attributes are mapped properly"
end
end
context "with no basedn configured" do
before do
RestClient::Resource.any_instance.should_receive(:post).once.with(hash_including(:file => result), { :accept => :json })
provision_instance = Socialcast::CommandLine::ProvisionUser.new(ldap_blank_basedn_config, {})
root_entry = create_entry('domain', :namingcontexts => ['dc=foo,dc=com', 'dc=bar,dc=com'])
ldap_instance = double(Net::LDAP, :encryption => nil, :auth => nil)
ldap_instance.should_receive(:search_root_dse).once.and_return(root_entry)
ldap_instance.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.and_return(ldap_instance)
user_entry = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
ldap_instance.should_receive(:search).once.ordered.with(hash_including(:base => 'dc=foo,dc=com', :attributes => ['givenName', 'sn', 'mail', 'isMemberOf']))
ldap_instance.should_receive(:search).once.ordered.with(hash_including(:base => 'dc=bar,dc=com', :attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(user_entry)
provision_instance.provision
end
it "searches all basedns and puts the user in the output file" do
result.should =~ /user@example.com/
end
end
end
describe "#each_user_hash" do
let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_default_config) }
before do
entry = create_entry 'user', :mail => 'user@example.com', :givenName => 'first name', :sn => 'last name'
ldap.should_receive(:search).once.with(hash_including(:attributes => ['givenName', 'sn', 'mail', 'isMemberOf'])).and_yield(entry)
end
it do
expect do |blk|
provision_instance.each_user_hash(&blk)
end.to yield_with_args(HashWithIndifferentAccess.new({
'first_name' => 'first name',
'last_name' => 'last name',
'contact_info' => {
'email' => 'user@example.com',
},
'custom_fields' => [],
'account_type' => 'member',
'roles' => []
}))
end
end
describe "#fetch_user_hash" do
context "when the first connector returns the entry" do
let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {}) }
let(:entry) { create_entry 'user', :mailCon => 'user@example.com' }
before do
filter = Net::LDAP::Filter.construct('(&(mail=*)(mailCon=user@example.com))')
ldap.should_receive(:search).once
.with(hash_including(:attributes => ['mailCon', 'isMemberOf'], :filter => filter))
.and_yield(entry)
end
it "returns the entry" do
provision_instance.fetch_user_hash('user@example.com', :identifying_field => 'email').should == {
'account_type' => 'member',
'contact_info' => {
'email' => 'user@example.com'
},
'custom_fields' => [],
'roles' => []
}
end
end
context "when another connector returns the entry" do
let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {}) }
let(:entry) { create_entry 'user', :mailCon2 => 'user@example.com', :firstName => 'first name' }
before do
ldap_instance1 = double(Net::LDAP, :auth => nil)
ldap_instance1.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance1)
filter1 = Net::LDAP::Filter.construct('(&(mail=*)(mailCon=user@example.com))')
ldap_instance1.should_receive(:search).once.ordered
.with(hash_including(:attributes => ['mailCon', 'isMemberOf'], :filter => filter1))
ldap_instance2 = double(Net::LDAP, :auth => nil)
ldap_instance2.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance2)
filter2 = Net::LDAP::Filter.construct('(&(mail=*)(mailCon2=user@example.com))')
ldap_instance2.should_receive(:search).once.ordered
.with(hash_including(:attributes => ['mailCon2', 'firstName', 'isMemberOf'], :filter => filter2))
.and_yield(entry)
end
it "returns the entry" do
provision_instance.fetch_user_hash('user@example.com', :identifying_field => 'email').should == {
'account_type' => 'member',
'contact_info' => {
'email' => 'user@example.com'
},
'first_name' => 'first name',
'custom_fields' => [],
'roles' => []
}
end
end
context "when no connectors return the entry" do
let(:provision_instance) { Socialcast::CommandLine::ProvisionUser.new(ldap_multiple_connection_mapping_config, {}) }
before do
ldap_instance1 = double(Net::LDAP, :auth => nil)
ldap_instance1.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance1)
filter1 = Net::LDAP::Filter.construct('(&(mail=*)(mailCon=user@example.com))')
ldap_instance1.should_receive(:search)
.with(hash_including(:attributes => ['mailCon', 'isMemberOf'], :filter => filter1))
ldap_instance2 = double(Net::LDAP, :auth => nil)
ldap_instance2.should_receive(:open).and_yield
Net::LDAP.should_receive(:new).once.ordered.and_return(ldap_instance2)
filter2 = Net::LDAP::Filter.construct('(&(mail=*)(mailCon2=user@example.com))')
ldap_instance2.should_receive(:search).once.ordered
.with(hash_including(:attributes => ['mailCon2', 'firstName', 'isMemberOf'], :filter => filter2))
end
it "returns nil" do
provision_instance.fetch_user_hash('user@example.com', :identifying_field => 'email').should be_nil
end
end
end
end