Download - Ruby で扱う LDAP のススメ
Ruby で扱う LDAP のススメ- Ruby meets LDAP -
その選択肢と事例
- Choices and cases –
高瀬一彰 / @tasheeen
Who?
高瀬一彰 (Kazuaki Takase)
エイケア・システムズ株式会社
Lang: perl→php→ruby+javascript
Rails で LDAP 管理アプリ
Why?
Ruby with LDAP に関する情報の不足
LDAP 自体がマイナー
Ruby コミュニティに、LDAPを扱うための入り口となる情報を提供したい
LDAPって?
どんなライブラリがあるの?
どれを使えばいいの?
What?
LDAP の概略
LDAP ライブラリの紹介
事例紹介
コード例
参考資料一覧
LDAPの概略
LDAPの概略(1)
Keywords
LDAP
Entry
ObjectClass
Attribute
DIT
Distinguished Name
LDAPの概略(2)
Lightweight Directory Access Protocol
RFC4510などで定義
元々は DAP。その軽量版(=Lightweight)
Directory = 台帳・名簿
何でも登録可能
台帳として最適化したデータ構造を持つ
LDAPの概略(3)
EntryLDAPにおけるデータの基本単位
=台帳の登録単位。名簿なら「人」
≒RDBにおける行に近い
現実世界の「物(=オブジェクト)」を表す
エントリは属性を持つ
「人」に対する「名前」「電話番号」etc.
RDB と違う点
エントリがスキーマを持つ
LDAPの概略(4)
Object Classエントリが「何」か
OOP の「クラス」と同義
オブジェクトクラスによって持てる属性が変わる
Unix アカウントのエントリなら posixAccount
継承・抽象型・構造型・補助型などの概念とルール
posixAccountは補助型person 等と組み合わせる
LDAPの概略(5)
Attributes
一つの属性に複数の値を持っている場合が多い
台帳・名簿としての考え方
LDAPの概略(6)
Directory Information Tree (DIT)
ディレクトリデータベースを DIB
ツリー構造していると DIT
階層的に管理するのがコンセプト
エントリがツリーとして連なる
LDAPの概略(7)
Distinguished Name(DN)
Rlative Distinguished Name(RDN)
その階層における一意の名前
cn=Ruby Taro
エントリが持つ任意の属性がRDNになる
RDNを下から一番上まで繋げるとツリーで一意に特定される名前になる(DN)
cn=Ruby Taro,o=RubyKaigi2010,c=jp
Ruby with LDAP
Ruby with LDAP - libs
Obsolete !
Obsolete !
Ruby with LDAP - libs
character
install
for use
RFC 1823の実装拡張ライブラリ
Pure Ruby. ActiveRecordのLDAP版Rails と相性がいい抽象度が高い
gem install ruby-ldap gem install net-ldap gen install activeldap
require “rubygems”require “active_ldap”
require “rubygems”require “net-ldap”
require “rubygems”require “ldap”
advantage 速い! ポータビリティが高い 高度な抽象化と豊富な機能
weak point ドキュメントが少ない ruby-ldap より10倍程度遅い
net-ldap よりも3倍程度遅い
Ruby with LDAP – ruby-ldap
ruby-ldapRFC1823 (The LDAP Application Programming Interface)のRuby版実装
使い方は同梱のテストが参考になる
速い!
search
bind
add
modify
etc…
C Extension
Ruby with LDAP – net-ldap
net-ldapPure Ruby な LDAP API 実装
ポータビリティに富む
RDoc のドキュメントが結構しっかりしている
search
bind
add
modify
etc…
Pure Ruby
Ruby with LDAP - activeldap
activeldapActiveRecord を参考に作られた、LDAP API
ruby-ldap や net-ldap, JNDI を内部的に利用
クラスはツリーを抽象、インスタンスはエントリを抽象
ActiveLdap::Base
User(Subclass)
find
destroy
cn=
save
new
Cases
Case 1
事例提供者:カピバラさんhttp://sites.google.com/site/capibaraproject/home
Case カピバラさん
背景
社内の各システム毎にID情報が分散
既存のLDAPサーバがあった
既存の構造的には認証統一には難しかった
新規にLDAP作成
管理アプリ作成
管理ユーザ一覧/検索/新規作成/所属変更
パスワード初期化 等
ユーザパスワード変更
Case カピバラさん
パスワード初期化
ユーザ管理一覧検索新規作成所属変更
パスワード変更
Case カピバラさん
ruby-ldap
ActiveLdap は使いづらかったそうで回避(泣
確かに ruby-ldap の方がプリミティブな実装
ruby-ldapに限らず、LDAPについても情報が不足して困った
速度面・利便性について特に不満なし
Case 岡澤さん
ruby-ldap を利用
分散した社員マスタ/認証を統合
request
参照できる
パスワード変更などアカウント作成・編集・削除
ML
依頼があり次第作成・変更
管理作業
Case 岡澤さん
ruby-ldap
元々作った人が「Rails 関連は面倒」(笑
openldap のコマンドとほぼ同じような作りで、コマンドの使い方が判ると直観的に使えた
同様の理由で、デバッグ等もやりやすかった
Case 3
事例提供者:谷口さん
Case 4
事例提供者:高瀬
Case 高瀬
request
batch(script/runner)
ユーザ管理有効化/編集/権限付与退職処理パスワード初期化など
共有フォルダ管理サブシステム権限管理etc
共有フォルダ作成権限編集
サブシステム権限編集パスワード変更
sync
PasswordWarning(mail)
batch
(Create folder
& ACL settings)
activeldap
アカウント/権限管理
Case 高瀬
activeldapRails の機能を活用したかった
script/runner でライブラリ使えるの便利
validation や association 使えるの便利validationは作りによってはとっても遅くなる
uid の uinique チェックのため検索するなどのvalidation を多数含めたりすると
検索時に取得する属性の数を少なくして速くする等の工夫をすると吉
テストも Rails に統合VM でdevelopment, test 用の LDAP を作成
環境(development, test)によって接続先 LDAP 切替
Rspec でふつうにテスト
Code Examples
Code Examples – ruby-ldap (1)
Connection and Bind
LDAP::Conn.new / LDAP::Conn#bind
@conn = LDAP::Conn.new('localhost', 389)@conn.bind("cn=Manager,o=RubyKaigi2010,c=jp", pass)# => #<LDAP::Conn:0xb7f0d400>
LDAP::Conn.new で接続
SSL接続には LDAP::SSLCon.new を利用
LDAP::Conn#bind でバインドする
第三引数の定数で認証方式の指定
Default: LDAP_AUTH_SIMPLE
grep LDAP_AUTH ldap.c
SSL 接続には LDAP::SSLConn
Code Examples – ruby-ldap (2)
search
LDAP::Conn#search
検索結果を LDAP ::Entry で返す
ブロック必須
他にも結果を Hash で返す search2 があるSearch2 はブロック無しで検索可能
@conn.search("o=RubyKaigi2010,c=jp", LDAP::LDAP_SCOPE_SUBTREE,"(objectclass=*)") do |entry|entry # => #<LDAP::Entry:…>, #<LDAP::Entry:…>, …
end
Code Examples – ruby-ldap (3)
Add
LDAP::Conn#add
entry = {"objectclass" => ["top", "person"],"cn" => ["Ruby Taro"],"sn" => ["Ruby", "Taro"]}
# 追加先のDN を指定し、属性の内容を渡す@conn.add("cn=#{entry['cn'][0]},o=RubyKaigi2010,c=jp", entry)# => #<LDAP::Conn:0xXXXXXXXX>
HashやLDAP::MOD_ADD でエントリ追加
上記の例は Hash の場合
Code Examples – ruby-ldap (4)
mod_hash = {"sn" => ["hoge", "taro"]}
@conn.modify("cn=Ruby Taro,o=RubyKaigi2010,c=jp", mod_hash) # => #<LDAP::Conn:0xb7f0bbc8>
Modify
LDAP::Conn#modify
HashやLDAP::MOD_MOD でエントリ更新
上記の例は Hash の場合
Code Examples – ruby-ldap (5)
Gathering Error Information
LDAP::Error
[email protected]("cn=Ruby Taro,o=RubyKaigi2010,c=jp")
rescue => ee # => #<LDAP::ResultError: No such object>e.message # => "No such object“
@conn.err # => [email protected](@conn.err) # => "No such object"
end
原則として LDAP::Error が投げられる様子
Human Readable なメッセージは LDAP::Error 自身が持っている
エラーコードが欲しい場合に #<LDAP::Conn> に問い合わせる
Code Examples – net-ldap(1)
Connection and BIND
Net::LDAP.new
@ldap = Net::LDAP.new :host => 'localhost',:port => 389,:base => 'o=RubyKaigi2010,c=jp',:auth => {:method => :simple,:username => 'cn=Manager,o=RubyKaigi2010,c=jp',:password => password }
@ldap # => #<Net::LDAP:0xb7e086f4 @base="o=RubyKaigi2010,c=jp",…>
接続と bind を一緒に行うのが通例
Code Examples – net-ldap(2)
search
Net::LDAP#search
デフォルトでは “(objectClass=*)” で検索
フィルタを利用する場合は Net::LDAP::Filter を利用してフィルタを構成する
ブロックを渡してイテレーションさせることも可
@ldap.search # => [#<Net::LDAP::Entry: ...>, #<Net::LDAP::Entry ...>]
@ldap.search(:filter => Net::LDAP::Filter.eq('uid', 'matz')) # => [#<Net::LDAP::Entry: … :uid=>["matz"], …>]
Code Examples – net-ldap(3)
Add
Net::LDAP#add
attrs = {:objectclass => ["top", "inetOrgPerson", "posixAccount"],:cn => "ruby taro",:gidNumber => "1999",:homeDirectory => "/home/ruby_taro",:sn => ["ruby", "taro"],:uid => "ruby_taro",:uidNumber => "1999"
}dn = "uid=#{attrs[:uid]},ou=Users,o=RubyKaigi2010,c=jp"
@ldap.add(:dn => dn, :attributes => attrs) # => [email protected]_operation_result # => #<OpenStruct … "Success",…, code=0>
※何故か false を返してますが成功しています(僕の環境のせい?)
Code Examples – net-ldap(4)
Modify
Net::LDAP#modify
dn = "uid=ruby_taro,ou=Users,o=RubyKaigi2010,c=jp"
opts = [[:add, :mail, "[email protected]"],[:replace, :sn, ["hoge", "fuga"]],[:delete, :telephoneNumber]
]
@ldap.modify(:dn => dn, :operations => opts) # => false
※これも何故か false を返してますが成功しています(僕の環境のry)
Code Examples – net-ldap(5)
Gathering Error Information
Net::LDAP#get_operation_result
@ldap.add(:dn => dn, :attr => attr) # => false
@ldap.get_operation_result.code # => [email protected]_operation_result.message # => "Protocol Error"@ldap.get_operation_result.error_message # => "no attributes provided"@ldap.get_operation_result.matched_dn # => ""@ldap.get_operation_result # => #<OpenStruct …>
OpenStruct でエラー情報を返す
上記は objectClass の MUST の属性を設定していない場合の例
Code Examples – activeldap(1)
Connection and Bind
ActiveLdap::Base.setup_connection
ActiveLdap::Base.setup_connection :base => "o=RubyKaigi2010,c=jp",:bind_dn => "cn=Manager,o=RubyKaigi2010,c=jp",:password_block => lambda{pass},:logger => logger
この時点では接続が確立されず、何か操作した時に接続
サブクラス毎に接続先を変えること等も可能
Code Examples – activeldap(2)
class User < ActiveLdap::Baseldap_mapping :prefix => "ou=Users",
:classes => %w(person),:scope => :sub,:dn_attribute => "uid"
end
クラスを任意のツリーにマッピング
このクラスを利用して指定したツリー以下の検索等を行う
インスタンスを作成するとエントリにマッピングされる
belongs_to, has_many 等も利用できる
全体的に ActiveRecord と類似したインターフェース
Mapping
define subclass
Code Examples – activeldap(3)
User.find(:all) # => [#<User ...>, #<User ...>, ...]User.find(:first, :filter => “(cn=Ruby Taro)”) #=> #<User ...>
Search
ActiveLdap::Base.find
ActiveRecord と同様の検索方法
検索条件は :filter オプション
エントリとマッピング済みのインスタンスを返す
Code Examples – activeldap(4)
Add/Modify
ActiveLdap::Base#save , save!
user = User.new :sn => 'Ruby', :cn => "Ruby Taro"user.save # => true
user.sn = “Hoge”user.save # => true
インスタンスの状態に合わせて add/modify
新規オブジェクトなら add
既存オブジェクトなら modify
属性に複数の値を入れたい場合は配列で
Code Examples – activeldap(5)
Gathering Error Information
ActiveRecord::Validations#errors
user = User.new :sn => 'Ruby', :cn => "Ruby Taro"user.save # => false
user.errors # => #<ActiveRecord::Errors ...>user.errors.full_messages# => ["distinguishedName is duplicated: cn=Ruby Taro,ou=Users,o=RubyKaigi2010,c=jp"]
errors メソッドが ActiveRecord::Errors を返す
Rails で Webインターフェース等作る際は比較的楽
参考資料
参考資料
LDAP RFC 4510http://datatracker.ietf.org/doc/rfc4510/
Rubyist Magazine - ActiveLdap を使ってみよう(前編)- LDAPとはhttp://jp.rubyist.net/magazine/?0027-ActiveLdap#l4
ruby-ldap Ruby/LDAP (Homepage)http://ruby-ldap.sourceforge.net/
RDochttp://ruby-ldap.sourceforge.net/rdoc/
ソースコード。特に test ディレクトリ以下
net-ldap Net-ldap-0.1.1 Documentation (Homepage)http://net-ldap.rubyforge.org/
activeldap ActiveLdap を使ってみよう(前編)http://jp.rubyist.net/magazine/?0027-ActiveLdap
ActiveLdap を使ってみよう(後編)http://jp.rubyist.net/magazine/?0029-ActiveLdap
Rails で作る ActiveDirectory と連携した社内システムhttp://www.clear-code.com/archives/rails-seminar-technical-night/
ActiveLdap 日本語チュートリアルhttp://code.google.com/p/ruby-activeldap/wiki/TutorialJa
ありがとうございましたThank you.