mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 10:08:40 -05:00
(POOLER-113) Add support for multiple LDAP search bases
This commit updates vmpooler to support setting an array of search bases in addition to a single base provided as a string. Without this change it is not possible to specify multiple search bases to use with the LDAP authentication provider. Additionally, test coverage is added to the authentication helper method.
This commit is contained in:
parent
de813943e9
commit
9fa27af8e5
7 changed files with 218 additions and 71 deletions
|
|
@ -54,36 +54,64 @@ module Vmpooler
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authenticate_ldap(port, host, user_object, base, username_str, password_str)
|
||||||
|
require 'rubygems'
|
||||||
|
require 'net/ldap'
|
||||||
|
|
||||||
|
ldap = Net::LDAP.new(
|
||||||
|
:host => host,
|
||||||
|
:port => port,
|
||||||
|
:encryption => {
|
||||||
|
:method => :start_tls,
|
||||||
|
:tls_options => { :ssl_version => 'TLSv1' }
|
||||||
|
},
|
||||||
|
:base => base,
|
||||||
|
:auth => {
|
||||||
|
:method => :simple,
|
||||||
|
:username => "#{user_object}=#{username_str},#{base}",
|
||||||
|
:password => password_str
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return true if ldap.bind
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
def authenticate(auth, username_str, password_str)
|
def authenticate(auth, username_str, password_str)
|
||||||
case auth['provider']
|
case auth['provider']
|
||||||
when 'dummy'
|
when 'dummy'
|
||||||
return (username_str != password_str)
|
return (username_str != password_str)
|
||||||
when 'ldap'
|
when 'ldap'
|
||||||
require 'rubygems'
|
ldap_base = auth[:ldap]['base']
|
||||||
require 'net/ldap'
|
ldap_port = auth[:ldap]['port'] || 389
|
||||||
|
|
||||||
ldap = Net::LDAP.new(
|
if ldap_base.is_a? Array
|
||||||
:host => auth[:ldap]['host'],
|
ldap_base.each do |search_base|
|
||||||
:port => auth[:ldap]['port'] || 389,
|
result = authenticate_ldap(
|
||||||
:encryption => {
|
ldap_port,
|
||||||
:method => :start_tls,
|
auth[:ldap]['host'],
|
||||||
:tls_options => { :ssl_version => 'TLSv1' }
|
auth[:ldap]['user_object'],
|
||||||
},
|
search_base,
|
||||||
:base => auth[:ldap]['base'],
|
username_str,
|
||||||
:auth => {
|
password_str,
|
||||||
:method => :simple,
|
|
||||||
:username => "#{auth[:ldap]['user_object']}=#{username_str},#{auth[:ldap]['base']}",
|
|
||||||
:password => password_str
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
return true if result == true
|
||||||
if ldap.bind
|
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
result = authenticate_ldap(
|
||||||
|
ldap_port,
|
||||||
|
auth[:ldap]['host'],
|
||||||
|
auth[:ldap]['user_object'],
|
||||||
|
ldap_base,
|
||||||
|
username_str,
|
||||||
|
password_str,
|
||||||
|
)
|
||||||
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def export_tags(backend, hostname, tags)
|
def export_tags(backend, hostname, tags)
|
||||||
tags.each_pair do |tag, value|
|
tags.each_pair do |tag, value|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
module Vmpooler
|
|
||||||
class API
|
|
||||||
module Helpers
|
|
||||||
def authenticate(auth, username_str, password_str)
|
|
||||||
username_str == 'admin' and password_str == 's3cr3t'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe Vmpooler::API::V1 do
|
describe Vmpooler::API::V1 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
module Vmpooler
|
|
||||||
class API
|
|
||||||
module Helpers
|
|
||||||
def authenticate(auth, username_str, password_str)
|
|
||||||
username_str == 'admin' and password_str == 's3cr3t'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_set_tag?(vm, tag, value)
|
def has_set_tag?(vm, tag, value)
|
||||||
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
module Vmpooler
|
|
||||||
class API
|
|
||||||
module Helpers
|
|
||||||
def authenticate(auth, username_str, password_str)
|
|
||||||
username_str == 'admin' and password_str == 's3cr3t'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_set_tag?(vm, tag, value)
|
def has_set_tag?(vm, tag, value)
|
||||||
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
value == redis.hget("vmpooler__vm__#{vm}", "tag:#{tag}")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
module Vmpooler
|
|
||||||
class API
|
|
||||||
module Helpers
|
|
||||||
def authenticate(auth, username_str, password_str)
|
|
||||||
username_str == 'admin' and password_str == 's3cr3t'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe Vmpooler::API::V1 do
|
describe Vmpooler::API::V1 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rack/test'
|
require 'rack/test'
|
||||||
|
|
||||||
module Vmpooler
|
|
||||||
class API
|
|
||||||
module Helpers
|
|
||||||
def authenticate(auth, username_str, password_str)
|
|
||||||
username_str == 'admin' and password_str == 's3cr3t'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe Vmpooler::API::V1 do
|
describe Vmpooler::API::V1 do
|
||||||
include Rack::Test::Methods
|
include Rack::Test::Methods
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
require 'net/ldap'
|
||||||
|
|
||||||
# A class for testing purposes that includes the Helpers.
|
# A class for testing purposes that includes the Helpers.
|
||||||
# this is impersonating V1's `helpers do include Helpers end`
|
# this is impersonating V1's `helpers do include Helpers end`
|
||||||
|
|
@ -238,4 +239,172 @@ describe Vmpooler::API::Helpers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#authenticate' do
|
||||||
|
let(:username_str) { 'admin' }
|
||||||
|
let(:password_str) { 's3cr3t' }
|
||||||
|
|
||||||
|
context 'with dummy provider' do
|
||||||
|
let(:auth) {
|
||||||
|
{
|
||||||
|
'provider': 'dummy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it 'should return true' do
|
||||||
|
expect(subject).to receive(:authenticate).with(auth, username_str, password_str).and_return(true)
|
||||||
|
|
||||||
|
subject.authenticate(auth, username_str, password_str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with ldap provider' do
|
||||||
|
let(:host) { 'ldap.example.com' }
|
||||||
|
let(:base) { 'ou=user,dc=test,dc=com' }
|
||||||
|
let(:user_object) { 'uid' }
|
||||||
|
let(:auth) {
|
||||||
|
{
|
||||||
|
'provider' => 'ldap',
|
||||||
|
ldap: {
|
||||||
|
'host' => host,
|
||||||
|
'base' => base,
|
||||||
|
'user_object' => user_object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let(:default_port) { 389 }
|
||||||
|
it 'should attempt ldap authentication' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base, username_str, password_str)
|
||||||
|
|
||||||
|
subject.authenticate(auth, username_str, password_str)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return true when authentication is successful' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base, username_str, password_str).and_return(true)
|
||||||
|
|
||||||
|
expect(subject.authenticate(auth, username_str, password_str)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return false when authentication fails' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base, username_str, password_str).and_return(false)
|
||||||
|
|
||||||
|
expect(subject.authenticate(auth, username_str, password_str)).to be false
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an alternate port' do
|
||||||
|
let(:alternate_port) { 636 }
|
||||||
|
before(:each) do
|
||||||
|
auth[:ldap]['port'] = alternate_port
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should specify the alternate port when authenticating' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(alternate_port, host, user_object, base, username_str, password_str)
|
||||||
|
|
||||||
|
subject.authenticate(auth, username_str, password_str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with multiple search bases' do
|
||||||
|
let(:base) {
|
||||||
|
[
|
||||||
|
'ou=user,dc=test,dc=com',
|
||||||
|
'ou=service,ou=user,dc=test,dc=com'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
before(:each) do
|
||||||
|
auth[:ldap]['base'] = base
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should attempt to bind with each base' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[0], username_str, password_str)
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[1], username_str, password_str)
|
||||||
|
|
||||||
|
subject.authenticate(auth, username_str, password_str)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not search the second base when the first binds' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[0], username_str, password_str).and_return(true)
|
||||||
|
expect(subject).to_not receive(:authenticate_ldap).with(default_port, host, user_object, base[1], username_str, password_str)
|
||||||
|
|
||||||
|
subject.authenticate(auth, username_str, password_str)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should search the second base when the first bind fails' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[0], username_str, password_str).and_return(false)
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[1], username_str, password_str)
|
||||||
|
|
||||||
|
subject.authenticate(auth, username_str, password_str)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return true when any bind succeeds' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[0], username_str, password_str).and_return(false)
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[1], username_str, password_str).and_return(true)
|
||||||
|
|
||||||
|
expect(subject.authenticate(auth, username_str, password_str)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return false when all bind attempts fail' do
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[0], username_str, password_str).and_return(false)
|
||||||
|
expect(subject).to receive(:authenticate_ldap).with(default_port, host, user_object, base[1], username_str, password_str).and_return(false)
|
||||||
|
|
||||||
|
expect(subject.authenticate(auth, username_str, password_str)).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with unknown provider' do
|
||||||
|
let(:auth) {
|
||||||
|
{
|
||||||
|
'provider': 'mystery'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it 'should return false' do
|
||||||
|
expect(subject).to receive(:authenticate).with(auth, username_str, password_str).and_return(false)
|
||||||
|
subject.authenticate(auth, username_str, password_str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#authenticate_ldap' do
|
||||||
|
let(:port) { 389 }
|
||||||
|
let(:host) { 'ldap.example.com' }
|
||||||
|
let(:user_object) { 'uid' }
|
||||||
|
let(:base) { 'ou=users,dc=example,dc=com' }
|
||||||
|
let(:username_str) { 'admin' }
|
||||||
|
let(:password_str) { 's3cr3t' }
|
||||||
|
let(:ldap) { double('ldap') }
|
||||||
|
it 'should create a new ldap connection' do
|
||||||
|
allow(ldap).to receive(:bind)
|
||||||
|
expect(Net::LDAP).to receive(:new).with(
|
||||||
|
:host => host,
|
||||||
|
:port => port,
|
||||||
|
:encryption => {
|
||||||
|
:method => :start_tls,
|
||||||
|
:tls_options => { :ssl_version => 'TLSv1' }
|
||||||
|
},
|
||||||
|
:base => base,
|
||||||
|
:auth => {
|
||||||
|
:method => :simple,
|
||||||
|
:username => "#{user_object}=#{username_str},#{base}",
|
||||||
|
:password => password_str
|
||||||
|
}
|
||||||
|
).and_return(ldap)
|
||||||
|
|
||||||
|
subject.authenticate_ldap(port, host, user_object, base, username_str, password_str)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return true when a bind is successful' do
|
||||||
|
expect(Net::LDAP).to receive(:new).and_return(ldap)
|
||||||
|
expect(ldap).to receive(:bind).and_return(true)
|
||||||
|
|
||||||
|
expect(subject.authenticate_ldap(port, host, user_object, base, username_str, password_str)).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return false when a bind fails' do
|
||||||
|
expect(Net::LDAP).to receive(:new).and_return(ldap)
|
||||||
|
expect(ldap).to receive(:bind).and_return(false)
|
||||||
|
|
||||||
|
expect(subject.authenticate_ldap(port, host, user_object, base, username_str, password_str)).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue