Три великолепных вкуса, которые великолепно сочетаются
У вас есть Exchange Server, и вам нужен список сотрудников. Вы можете подумать: «Мы просто создадим базу данных для этого». Давайте действительно подумаем об этом. Если вы это сделаете, то у вас есть две базы данных для обновления при смене сотрудников. Почему бы нам просто не использовать Exchange Server? Вы можете спросить: «Как можно это сделать?» Я рад, что вы спросили.
Все, что нам действительно нужно сделать, это прочитать базу данных Active Directory, которая является сервером LDAP. Сервер LDAP имеет службы каталогов, которые могут предоставлять любой организованный набор записей, часто с иерархической структурой, такой как корпоративный каталог электронной почты. Аналогично, телефонный справочник — это список абонентов с адресом и номером телефона.
Как вы уже догадались, для этого есть Драгоценный камень. Это называется net / ldap . Давайте объединим этот драгоценный камень с нашим другом Синатрой и посмотрим, как это сделать.
Как только мы установим драгоценные камни, как мы начнем? Это где я нашел код, чтобы начать меня.
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
filter = Net::LDAP::Filter.eq(‘CN’, ‘john ivanoff’) | |
treebase = «dc=reputablecompany, dc=com» | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
puts «DN: #{entry.dn}« | |
entry.each do |attribute, values| | |
puts » #{attribute}:» | |
values.each do |value| | |
puts » —>#{value}« | |
end | |
end | |
end | |
p ldap.get_operation_result |
Это будет искать Exchange Server мое имя и вернет все, что знает обо мне.
Сохраните файл и запустите сервер.
$ ruby ad.rb
Он распечатает информацию TON, потому что я знаю, что я существую в Exchange Server. Не стесняйтесь заменить мое имя на имя, которое вы знаете, в Active Directory.
dn: | |
—>CN=John Ivanoff,OU=Reputable-OSG,DC=REPUTABLECOMPANY,DC=com | |
objectclass: | |
—>top | |
—>person | |
—>organizationalPerson | |
—>user | |
cn: | |
—>John Ivanoff | |
sn: | |
—>Ivanoff | |
l: | |
—>Dallas | |
st: | |
—>TX | |
title: | |
—>Web Developer | |
description: | |
—>Reputable — Reputable | |
postalcode: | |
—>75207 | |
givenname: | |
… | |
… | |
#<OpenStruct code=0, message=»Success»> |
Теперь мы можем видеть, что нам доступно для использования.
Я уверен, что мы хотели бы видеть список имен сотрудников. Давайте перейдем к тому значению «CN», которое обозначает Common Name . Фактически, этот атрибут LDAP может быть составлен из данного имени, присоединенного к SN, фамилии или фамилии. Как бы вы посмотрели все записи в CN?
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
filter = Net::LDAP::Filter.eq(‘CN’, ‘*’) | |
treebase = «dc= reputablecompany, dc=com» | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
puts «CN: #{entry.cn}« | |
end | |
p ldap.get_operation_result |
Сохраните файл и перезапустите сервер.
CN: [«IUSR_DDT95601«] | |
CN: [«TsInternetUser\nCNF:1a83d6ca-f929-4c28-86c5-7bcde52ad40«] | |
CN: [«IUSR_SERVER«] | |
CN: [«InterwebUser«] | |
CN: [«Group Creator«] | |
CN: [«Jimmy Jones«] | |
CN: [«Schema Admins«] | |
CN: [«Steve Dallas«] |
Разве это не красиво? Обратите внимание, что я заменил свое имя звездочкой (*).
Теперь мы показываем имена для каждой записи. Давайте добавим их отдел к выводу. Оглянитесь назад и найдите атрибут для этого.
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
filter = Net::LDAP::Filter.eq(‘CN’, ‘*’) | |
treebase = «dc=reputablecompany, dc=com» | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
puts «CN: #{entry.cn}« | |
puts «Department: #{entry.company}« | |
end | |
p ldap.get_operation_result |
Готовы посмотреть, работает ли это? Перезагрузите ваш сервер.
/Users/john/.rvm/gems/ruby-1.9.3-p194@AD/gems/net-ldap-0.3.1/lib/net/ldap/entry.rb:169:in ‘method_missing‘: undefined method ‘department‘ for #<Net::LDAP::Entry:0x007fc59384c9b0> (NoMethodError) | |
from rs.rb:14:in `block in <main>‘ | |
from /Users/john/.rvm/gems/ruby-1.9.3-p194@AD/gems/net-ldap-0.3.1/lib/net/ldap.rb:639:in `block in search‘ | |
from /Users/john/.rvm/gems/ruby-1.9.3-p194@AD/gems/net-ldap-0.3.1/lib/net/ldap.rb:1410:in `block in search‘ | |
from /Users/john/.rvm/gems/ruby-1.9.3-p194@AD/gems/net-ldap-0.3.1/lib/net/ldap.rb:1367:in `loop‘ | |
from /Users/john/.rvm/gems/ruby-1.9.3-p194@AD/gems/net-ldap-0.3.1/lib/net/ldap.rb:1367:in `search‘ | |
from /Users/john/.rvm/gems/ruby-1.9.3-p194@AD/gems/net-ldap-0.3.1/lib/net/ldap.rb:637:in `search‘ | |
from rs.rb:12:in `<main>‘ | |
iosdev:AD john$ |
‘method_missing’: неопределенный метод ‘отдел’ ОК, поэтому в записи нет отдела. Мы работаем с объектами здесь. Да, все является объектом, но если серьезно, мы возвращаем истинные «объекты», а не «объекты массива». Как бы вы проверили, существует ли метод отдела?
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
filter = Net::LDAP::Filter.eq(‘CN’, ‘* *’) | |
treebase = «dc=reputablecompany, dc=com» | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
puts «CN: #{entry.cn}« | |
if entry.respond_to?(«company») | |
puts «Department: #{entry.company}« | |
end | |
end | |
p ldap.get_operation_result |
Перезагрузите сервер, снова. Ваши пальцы скрещены?
… | |
CN: [«TsInternetUsers«] | |
CN: [«Group Policy«] | |
CN: [«Jimmy Jones«] | |
Department: [«Sales«] | |
CN: [«Steve Dallas«] | |
Department: [«Sales«] | |
CN: [«Ken Johnson«] | |
Department: [«Sales«] | |
CN: [«Backup Operators«] | |
CN: [«Backups«] | |
CN: [«Postmast«] | |
Department: [«Sales«] | |
… |
Похоже, что многие записи не имеют отделов.
Итак, что мы можем сделать со всей этой силой? Я думаю, нам следовало подумать об этом, прежде чем мы начали писать код Видишь, как это важно?
С точки зрения красивых URL, почему бы нам не сделать что-то вроде /attributeWeAreSearchingIn/searchTerm
.
Ну, мы действительно не хотим использовать CN, чтобы искать человека. Мы можем использовать «люди», например /people/john
. Это вернет всех людей (CN), которые начинаются с «Джон».
Как вы думаете, мы бы фильтровали имена?
Да, вы должны передать params [: id] в строку фильтра.
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
get ‘/people/:id’ do | |
filter = Net::LDAP::Filter.eq(‘cn’, params[:id] + ‘*’) | |
treebase = «dc=reputablecompany, dc=com» | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
puts «CN: #{entry.cn}« | |
if entry.respond_to?(«company») | |
puts «Department: #{entry.company}« | |
end | |
end | |
p ldap.get_operation_result | |
end |
Мы также поместили код в метод get
. Запустите его, откройте браузер и перейдите по адресу 127.0.0.1:4567/people/john. В терминале вы увидите вывод, если в Exchange Server есть какие-либо Johns. Если не заменить Джона на имя, которое вы знаете, там
Давайте перенесем вывод из терминала в веб-браузер.
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
get ‘/people/:id’ do | |
filter = Net::LDAP::Filter.eq(‘cn’, params[:id] + ‘*’) | |
treebase = «dc=reputablecompany, dc=com» | |
@b = Hash.new | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
department = «» | |
if entry.respond_to?(«department») | |
department = entry.department | |
end | |
name = «» | |
if entry.respond_to?(«cn») | |
name = entry.cn | |
end | |
@b[name] = {:department => department} | |
end | |
erb :people | |
end | |
__END__ | |
@@people | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset=»UTF-8″> | |
<meta name=»viewport» content=»user-scalable=yes, width=device-width» /> | |
<title>Home | AD Listing</title> | |
</head> | |
<body> | |
<% @b.each do |attribute, values| %> | |
<h1><%= attribute.first %></h1> | |
<ul> | |
<% values.each do |name, value|%> | |
<% if !value[0].nil? %> | |
<li><%= name.capitalize %>: | |
<%= value[0] %></li> | |
<% end %> | |
<% end %> | |
</ul> | |
<% end %> | |
</body> | |
</html> |
Отдел продаж, пожалуйста.
Как насчет фильтрации по отделам? Как бы вы это закодировали? Я знаю, что я буду делать.
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
get ‘/people/:id’ do | |
filter = Net::LDAP::Filter.eq(‘cn’, params[:id] + ‘*’) | |
treebase = «dc=reputablecompany, dc=com» | |
@b = Hash.new | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
department = «» | |
if entry.respond_to?(«department») | |
department = entry.department | |
end | |
name = «» | |
if entry.respond_to?(«cn») | |
name = entry.cn | |
end | |
@b[name] = {:department => department} | |
end | |
erb :people | |
end | |
get ‘/department/:id’ do | |
filter = Net::LDAP::Filter.eq(‘cn’, params[:id] + ‘*’) | |
treebase = «dc=reputablecompany, dc=com» | |
@b = Hash.new | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
department = «» | |
if entry.respond_to?(«department») | |
department = entry.department | |
end | |
name = «» | |
if entry.respond_to?(«cn») | |
name = entry.cn | |
end | |
@b[name] = {:department => department} | |
end | |
erb :people | |
end | |
__END__ | |
@@people | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset=»UTF-8″> | |
<meta name=»viewport» content=»user-scalable=yes, width=device-width» /> | |
<title>Home | AD Listing</title> | |
</head> | |
<body> | |
<% @b.each do |attribute, values| %> | |
<h1><%= attribute.first %></h1> | |
<ul> | |
<% values.each do |name, value|%> | |
<% if !value[0].nil? %> | |
<li><%= name.capitalize %>: | |
<%= value[0] %></li> | |
<% end %> | |
<% end %> | |
</ul> | |
<% end %> | |
</body> | |
</html> |
Да, скопируйте, вставьте и внесите несколько изменений.
Перезагрузите сервер и перейдите к 127.0.0.1:4567/department/sales. Вы увидите всех в отделе продаж.
Заинтересованные стороны сходят с ума от запросов на различные атрибуты поиска. Прежде чем мы это узнаем, существует огромный файл с большим количеством избыточного кода. Пришло время для перефакторинга.
require ‘sinatra’ | |
require ‘net/ldap’ | |
ldap = Net::LDAP.new | |
ldap.host = ‘192.168.1.1’ | |
ldap.port = 389 | |
ldap.auth «username», «password» | |
treebase = «dc=reputablecompany, dc=com» | |
[‘/people/:id’, ‘/title/:id’, ‘/email/:id’, ‘/department/:id’, ‘/location/:id’, ‘/phone/:id’].each do |route| | |
get route do | |
case route | |
when /title/ then search_in = «title» | |
when /email/ then search_in = «mail» | |
when /department/ then search_in = «department» | |
when /location/ then search_in = «l» | |
when /phone/ then search_in = «telephonenumber» | |
when /people/ then search_in = «cn» | |
else search_in = «cn» | |
end | |
@searching_in = route.gsub(/\//, »).gsub(/:id/ , »).capitalize | |
@search_for = params[:id] | |
filter = Net::LDAP::Filter.eq(search_in, @search_for+‘*’) | |
@b = Hash.new | |
ldap.search(:base => treebase, :filter => filter) do |entry| | |
if entry.respond_to?(«l») | |
title = «» | |
if entry.respond_to?(«title») | |
title = entry.title | |
end | |
email = «» | |
if entry.respond_to?(«mail») | |
email = entry.mail | |
end | |
telephonenumber = «» | |
if entry.respond_to?(«telephonenumber») | |
telephonenumber = entry.telephonenumber | |
end | |
location = «» | |
if entry.respond_to?(«l») | |
location = entry.l | |
end | |
department = «» | |
if entry.respond_to?(«department») | |
department = entry.department | |
end | |
name = «» | |
if entry.respond_to?(«cn») | |
name = entry.cn | |
end | |
@b[name] = {:email => email, :phone => telephonenumber, :location => location, :department => department, :title => title}#, :useraccountcontrol => useraccountcontrol} | |
end | |
end | |
erb :detail | |
end | |
end | |
__END__ | |
@@detail | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset=»UTF-8″> | |
<meta name=»viewport» content=»user-scalable=yes, width=device-width» /> | |
<title>AD Listing</title> | |
</head> | |
<body> | |
<% @b.each do |attribute, values| %> | |
<div class=»content»> | |
<p><%= attribute.first %></p> | |
<dl> | |
<% values.each do |name, value|%> | |
<% if !value[0].nil? %> | |
<dt><%= name.capitalize %></dt> | |
<% if name.to_s == ’email’ %> | |
<dd><a href=»mailto:<%= value[0] %>»><%= value[0] %></a></dd> | |
<% elsif name.to_s == ‘phone’ %> | |
<dd><%= value[0] %></dd> | |
<% else %> | |
<dd><a href=»/<%= name %>/<%= value[0] %>»><%= value[0] %></a></dd> | |
<% end %> | |
<% end %> | |
<% end %> | |
</dl> | |
</div> | |
<% end %> | |
</body> | |
</html> |
Нам нужно будет перезагрузить сервер. Если я зайду на 127.0.0.1:4567/location/dallas , в браузере я увижу всех в офисе в Далласе.
Теперь у вас всего одно место для содержания сотрудников. Мы также можем превратить это в API или подключить мобильное приложение. Возможности бесконечны!