Статьи

Синатра, Exchange Server и список сотрудников

Три великолепных вкуса, которые великолепно сочетаются

У вас есть 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

view raw
gistfile1.rb
hosted with ❤ by GitHub

Это будет искать 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»>

view raw
gistfile1.sh
hosted with ❤ by GitHub

Теперь мы можем видеть, что нам доступно для использования.

Я уверен, что мы хотели бы видеть список имен сотрудников. Давайте перейдем к тому значению «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

view raw
gistfile1.rb
hosted with ❤ by GitHub

Сохраните файл и перезапустите сервер.

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«]

view raw
gistfile1.sh
hosted with ❤ by GitHub

Разве это не красиво? Обратите внимание, что я заменил свое имя звездочкой (*).

Теперь мы показываем имена для каждой записи. Давайте добавим их отдел к выводу. Оглянитесь назад и найдите атрибут для этого.

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

view raw
gistfile1.rb
hosted with ❤ by GitHub

Готовы посмотреть, работает ли это? Перезагрузите ваш сервер.

/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$

view raw
gistfile1.sh
hosted with ❤ by GitHub

‘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

view raw
gistfile1.rb
hosted with ❤ by GitHub

Перезагрузите сервер, снова. Ваши пальцы скрещены?

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«]

view raw
gistfile1.sh
hosted with ❤ by GitHub

Похоже, что многие записи не имеют отделов.

Итак, что мы можем сделать со всей этой силой? Я думаю, нам следовало подумать об этом, прежде чем мы начали писать код Видишь, как это важно?

С точки зрения красивых 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

view raw
gistfile1.rb
hosted with ❤ by GitHub

Мы также поместили код в метод 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>

view raw
gistfile1.rb
hosted with ❤ by GitHub

Отдел продаж, пожалуйста.

Как насчет фильтрации по отделам? Как бы вы это закодировали? Я знаю, что я буду делать.

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>

view raw
gistfile1.rb
hosted with ❤ by GitHub

Да, скопируйте, вставьте и внесите несколько изменений.
Перезагрузите сервер и перейдите к 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>

view raw
gistfile1.rb
hosted with ❤ by GitHub

Нам нужно будет перезагрузить сервер. Если я зайду на 127.0.0.1:4567/location/dallas , в браузере я увижу всех в офисе в Далласе.

Теперь у вас всего одно место для содержания сотрудников. Мы также можем превратить это в API или подключить мобильное приложение. Возможности бесконечны!

Если вы хотите узнать больше о Синатре, обязательно посетите сайт книг JumpPoint . Мы выпустим Jump Start Sinatra в начале следующего года.