scashin133-net-ldap 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ require 'common'
2
+
3
+ =begin
4
+ class TestEntry < Test::Unit::TestCase
5
+ Commented out until I can make it a spec.
6
+ context "An instance of Entry" do
7
+ setup do
8
+ @entry = Net::LDAP::Entry.new 'cn=Barbara,o=corp'
9
+ end
10
+
11
+ should "be initialized with the DN" do
12
+ assert_equal 'cn=Barbara,o=corp', @entry.dn
13
+ end
14
+
15
+ should 'return an empty array when accessing a nonexistent attribute (index lookup)' do
16
+ assert_equal [], @entry['sn']
17
+ end
18
+
19
+ should 'return an empty array when accessing a nonexistent attribute (method call)' do
20
+ assert_equal [], @entry.sn
21
+ end
22
+
23
+ should 'create an attribute on assignment (index lookup)' do
24
+ @entry['sn'] = 'Jensen'
25
+ assert_equal ['Jensen'], @entry['sn']
26
+ end
27
+
28
+ should 'create an attribute on assignment (method call)' do
29
+ @entry.sn = 'Jensen'
30
+ assert_equal ['Jensen'], @entry.sn
31
+ end
32
+
33
+ should 'have attributes accessible by index lookup' do
34
+ @entry['sn'] = 'Jensen'
35
+ assert_equal ['Jensen'], @entry['sn']
36
+ end
37
+
38
+ should 'have attributes accessible using a Symbol as the index' do
39
+ @entry[:sn] = 'Jensen'
40
+ assert_equal ['Jensen'], @entry[:sn]
41
+ end
42
+
43
+ should 'have attributes accessible by method call' do
44
+ @entry['sn'] = 'Jensen'
45
+ assert_equal ['Jensen'], @entry.sn
46
+ end
47
+
48
+ should 'ignore case of attribute names' do
49
+ @entry['sn'] = 'Jensen'
50
+ assert_equal ['Jensen'], @entry.sn
51
+ assert_equal ['Jensen'], @entry.Sn
52
+ assert_equal ['Jensen'], @entry.SN
53
+ assert_equal ['Jensen'], @entry['sn']
54
+ assert_equal ['Jensen'], @entry['Sn']
55
+ assert_equal ['Jensen'], @entry['SN']
56
+ end
57
+ end
58
+ end
59
+ =end
@@ -0,0 +1,115 @@
1
+ require 'common'
2
+
3
+ class TestFilter < Test::Unit::TestCase
4
+ Filter = Net::LDAP::Filter
5
+
6
+ def test_bug_7534_rfc2254
7
+ assert_equal("(cn=Tim Wizard)",
8
+ Filter.from_rfc2254("(cn=Tim Wizard)").to_rfc2254)
9
+ end
10
+
11
+ def test_invalid_filter_string
12
+ assert_raises(Net::LDAP::LdapError) { Filter.from_rfc2254("") }
13
+ end
14
+
15
+ def test_invalid_filter
16
+ assert_raises(Net::LDAP::LdapError) {
17
+ # This test exists to prove that our constructor blocks unknown filter
18
+ # types. All filters must be constructed using helpers.
19
+ Filter.__send__(:new, :xx, nil, nil)
20
+ }
21
+ end
22
+
23
+ def test_to_s
24
+ assert_equal("(uid=george *)", Filter.eq("uid", "george *").to_s)
25
+ end
26
+
27
+ def test_apostrophe
28
+ assert_equal("(uid=O'Keefe)", Filter.construct("uid=O'Keefe").to_rfc2254)
29
+ end
30
+
31
+ def test_c2
32
+ assert_equal("(uid=george *)",
33
+ Filter.from_rfc2254("uid=george *").to_rfc2254)
34
+ assert_equal("(uid:=george *)",
35
+ Filter.from_rfc2254("uid:=george *").to_rfc2254)
36
+ assert_equal("(uid=george*)",
37
+ Filter.from_rfc2254(" ( uid = george* ) ").to_rfc2254)
38
+ assert_equal("(!(uid=george*))",
39
+ Filter.from_rfc2254("uid!=george*").to_rfc2254)
40
+ assert_equal("(uid<=george*)",
41
+ Filter.from_rfc2254("uid <= george*").to_rfc2254)
42
+ assert_equal("(uid>=george*)",
43
+ Filter.from_rfc2254("uid>=george*").to_rfc2254)
44
+ assert_equal("(&(uid=george*)(mail=*))",
45
+ Filter.from_rfc2254("(& (uid=george* ) (mail=*))").to_rfc2254)
46
+ assert_equal("(|(uid=george*)(mail=*))",
47
+ Filter.from_rfc2254("(| (uid=george* ) (mail=*))").to_rfc2254)
48
+ assert_equal("(!(mail=*))",
49
+ Filter.from_rfc2254("(! (mail=*))").to_rfc2254)
50
+ end
51
+
52
+ def test_filters_from_ber
53
+ [
54
+ Net::LDAP::Filter.eq("objectclass", "*"),
55
+ Net::LDAP::Filter.pres("objectclass"),
56
+ Net::LDAP::Filter.eq("objectclass", "ou"),
57
+ Net::LDAP::Filter.ge("uid", "500"),
58
+ Net::LDAP::Filter.le("uid", "500"),
59
+ (~ Net::LDAP::Filter.pres("objectclass")),
60
+ (Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou")),
61
+ (Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou") & Net::LDAP::Filter.pres("sn")),
62
+ (Net::LDAP::Filter.pres("objectclass") | Net::LDAP::Filter.pres("ou") | Net::LDAP::Filter.pres("sn")),
63
+
64
+ Net::LDAP::Filter.eq("objectclass", "*aaa"),
65
+ Net::LDAP::Filter.eq("objectclass", "*aaa*bbb"),
66
+ Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc"),
67
+ Net::LDAP::Filter.eq("objectclass", "aaa*bbb"),
68
+ Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc"),
69
+ Net::LDAP::Filter.eq("objectclass", "abc*def*1111*22*g"),
70
+ Net::LDAP::Filter.eq("objectclass", "*aaa*"),
71
+ Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*"),
72
+ Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc*"),
73
+ Net::LDAP::Filter.eq("objectclass", "aaa*"),
74
+ Net::LDAP::Filter.eq("objectclass", "aaa*bbb*"),
75
+ Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc*"),
76
+ ].each {|ber|
77
+ f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
78
+ assert(f == ber)
79
+ assert_equal(f.to_ber, ber.to_ber)
80
+ }
81
+ end
82
+
83
+ def test_ber_from_rfc2254_filter
84
+ [
85
+ Net::LDAP::Filter.construct("objectclass=*"),
86
+ Net::LDAP::Filter.construct("objectclass=ou"),
87
+ Net::LDAP::Filter.construct("uid >= 500"),
88
+ Net::LDAP::Filter.construct("uid <= 500"),
89
+ Net::LDAP::Filter.construct("(!(uid=*))"),
90
+ Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*))"),
91
+ Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*)(sn=*))"),
92
+ Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*))"),
93
+ Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*)(sn=*))"),
94
+
95
+ Net::LDAP::Filter.construct("objectclass=*aaa"),
96
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb"),
97
+ Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
98
+ Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
99
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc"),
100
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb"),
101
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc"),
102
+ Net::LDAP::Filter.construct("objectclass=abc*def*1111*22*g"),
103
+ Net::LDAP::Filter.construct("objectclass=*aaa*"),
104
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb*"),
105
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc*"),
106
+ Net::LDAP::Filter.construct("objectclass=aaa*"),
107
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb*"),
108
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc*"),
109
+ ].each {|ber|
110
+ f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
111
+ assert(f == ber)
112
+ assert_equal(f.to_ber, ber.to_ber)
113
+ }
114
+ end
115
+ end
data/test/test_ldif.rb ADDED
@@ -0,0 +1,68 @@
1
+ # $Id: testldif.rb 61 2006-04-18 20:55:55Z blackhedd $
2
+
3
+ require 'common'
4
+
5
+ require 'net/ldif'
6
+ require 'digest/sha1'
7
+ require 'base64'
8
+
9
+ class TestLdif < Test::Unit::TestCase
10
+ TestLdifFilename = "#{File.dirname(__FILE__)}/testdata.ldif"
11
+
12
+ def test_empty_ldif
13
+ ds = Net::LDAP::Dataset.read_ldif(StringIO.new)
14
+ assert_equal(true, ds.empty?)
15
+ end
16
+
17
+ def test_ldif_with_comments
18
+ str = ["# Hello from LDIF-land", "# This is an unterminated comment"]
19
+ io = StringIO.new(str[0] + "\r\n" + str[1])
20
+ ds = Net::LDAP::Dataset::read_ldif(io)
21
+ assert_equal(str, ds.comments)
22
+ end
23
+
24
+ def test_ldif_with_password
25
+ psw = "goldbricks"
26
+ hashed_psw = "{SHA}" + Base64::encode64(Digest::SHA1.digest(psw)).chomp
27
+
28
+ ldif_encoded = Base64::encode64(hashed_psw).chomp
29
+ ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: Goldbrick\r\nuserPassword:: #{ldif_encoded}\r\n\r\n"))
30
+ recovered_psw = ds["Goldbrick"][:userpassword].shift
31
+ assert_equal(hashed_psw, recovered_psw)
32
+ end
33
+
34
+ def test_ldif_with_continuation_lines
35
+ ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
36
+ assert_equal(true, ds.has_key?("abcdefg hijklmn"))
37
+ end
38
+
39
+ # TODO, INADEQUATE. We need some more tests
40
+ # to verify the content.
41
+ def test_ldif
42
+ File.open(TestLdifFilename, "r") {|f|
43
+ ds = Net::LDAP::Dataset::read_ldif(f)
44
+ assert_equal(13, ds.length)
45
+ }
46
+ end
47
+
48
+ # Must test folded lines and base64-encoded lines as well as normal ones.
49
+ def test_to_ldif
50
+ data = File.open(TestLdifFilename, "rb") { |f| f.read }
51
+ io = StringIO.new(data)
52
+
53
+ # added .lines to turn to array because 1.9 doesn't have
54
+ # .grep on basic strings
55
+ entries = data.lines.grep(/^dn:\s*/) { $'.chomp }
56
+ dn_entries = entries.dup
57
+
58
+ ds = Net::LDAP::Dataset::read_ldif(io) { |type, value|
59
+ case type
60
+ when :dn
61
+ assert_equal(dn_entries.first, value)
62
+ dn_entries.shift
63
+ end
64
+ }
65
+ assert_equal(entries.size, ds.size)
66
+ assert_equal(entries.sort, ds.to_ldif.grep(/^dn:\s*/) { $'.chomp })
67
+ end
68
+ end
@@ -0,0 +1,17 @@
1
+ # $Id: testpsw.rb 72 2006-04-24 21:58:14Z blackhedd $
2
+
3
+ require 'common'
4
+
5
+ class TestPassword < Test::Unit::TestCase
6
+
7
+ def test_psw
8
+ assert_equal(
9
+ "{MD5}xq8jwrcfibi0sZdZYNkSng==",
10
+ Net::LDAP::Password.generate( :md5, "cashflow" ))
11
+
12
+ assert_equal(
13
+ "{SHA}YE4eGkN4BvwNN1f5R7CZz0kFn14=",
14
+ Net::LDAP::Password.generate( :sha, "cashflow" ))
15
+ end
16
+
17
+ end
data/test/test_snmp.rb ADDED
@@ -0,0 +1,114 @@
1
+ # $Id: testsnmp.rb 231 2006-12-21 15:09:29Z blackhedd $
2
+
3
+ require 'common'
4
+ require 'net/snmp'
5
+
6
+ class TestSnmp < Test::Unit::TestCase
7
+ SnmpGetRequest = "0'\002\001\000\004\006public\240\032\002\002?*\002\001\000\002\001\0000\0160\f\006\b+\006\001\002\001\001\001\000\005\000"
8
+ SnmpGetResponse = "0+\002\001\000\004\006public\242\036\002\002'\017\002\001\000\002\001\0000\0220\020\006\b+\006\001\002\001\001\001\000\004\004test"
9
+
10
+ SnmpGetRequestXXX = "0'\002\001\000\004\006xxxxxx\240\032\002\002?*\002\001\000\002\001\0000\0160\f\006\b+\006\001\002\001\001\001\000\005\000"
11
+
12
+ def test_invalid_packet
13
+ data = "xxxx"
14
+ assert_raise(Net::BER::BerError) {
15
+ ary = data.read_ber(Net::SNMP::AsnSyntax)
16
+ }
17
+ end
18
+
19
+ # The method String#read_ber! added by Net::BER consumes a well-formed BER
20
+ # object from the head of a string. If it doesn't find a complete,
21
+ # well-formed BER object, it returns nil and leaves the string unchanged.
22
+ # If it finds an object, it returns the object and removes it from the
23
+ # head of the string. This is good for handling partially-received data
24
+ # streams, such as from network connections.
25
+ def _test_consume_string
26
+ data = "xxx"
27
+ assert_equal(nil, data.read_ber!)
28
+ assert_equal("xxx", data)
29
+
30
+ data = SnmpGetRequest + "!!!"
31
+ ary = data.read_ber!(Net::SNMP::AsnSyntax)
32
+ assert_equal("!!!", data)
33
+ assert ary.is_a?(Array)
34
+ assert ary.is_a?(Net::BER::BerIdentifiedArray)
35
+ end
36
+
37
+ def test_weird_packet
38
+ assert_raise(Net::SnmpPdu::Error) {
39
+ Net::SnmpPdu.parse("aaaaaaaaaaaaaa")
40
+ }
41
+ end
42
+
43
+ def test_get_request
44
+ data = SnmpGetRequest.dup
45
+ pkt = data.read_ber(Net::SNMP::AsnSyntax)
46
+ assert pkt.is_a?(Net::BER::BerIdentifiedArray)
47
+ assert_equal(48, pkt.ber_identifier) # Constructed [0], signifies GetRequest
48
+
49
+ pdu = Net::SnmpPdu.parse(pkt)
50
+ assert_equal(:get_request, pdu.pdu_type)
51
+ assert_equal(16170, pdu.request_id) # whatever was in the test data. 16170 is not magic.
52
+ assert_equal([[[1, 3, 6, 1, 2, 1, 1, 1, 0], nil]], pdu.variables)
53
+
54
+ assert_equal(pdu.to_ber_string, SnmpGetRequest)
55
+ end
56
+
57
+ def test_empty_pdu
58
+ pdu = Net::SnmpPdu.new
59
+ assert_raise(Net::SnmpPdu::Error) { pdu.to_ber_string }
60
+ end
61
+
62
+ def test_malformations
63
+ pdu = Net::SnmpPdu.new
64
+ pdu.version = 0
65
+ pdu.version = 2
66
+ assert_raise(Net::SnmpPdu::Error) { pdu.version = 100 }
67
+
68
+ pdu.pdu_type = :get_request
69
+ pdu.pdu_type = :get_next_request
70
+ pdu.pdu_type = :get_response
71
+ pdu.pdu_type = :set_request
72
+ pdu.pdu_type = :trap
73
+ assert_raise(Net::SnmpPdu::Error) { pdu.pdu_type = :something_else }
74
+ end
75
+
76
+ def test_make_response
77
+ pdu = Net::SnmpPdu.new
78
+ pdu.version = 0
79
+ pdu.community = "public"
80
+ pdu.pdu_type = :get_response
81
+ pdu.request_id = 9999
82
+ pdu.error_status = 0
83
+ pdu.error_index = 0
84
+ pdu.add_variable_binding [1, 3, 6, 1, 2, 1, 1, 1, 0], "test"
85
+
86
+ assert_equal(SnmpGetResponse, pdu.to_ber_string)
87
+ end
88
+
89
+ def test_make_bad_response
90
+ pdu = Net::SnmpPdu.new
91
+ assert_raise(Net::SnmpPdu::Error) {pdu.to_ber_string}
92
+ pdu.pdu_type = :get_response
93
+ pdu.request_id = 999
94
+ pdu.to_ber_string
95
+ # Not specifying variables doesn't create an error. (Maybe it should?)
96
+ end
97
+
98
+ def test_snmp_integers
99
+ c32 = Net::SNMP::Counter32.new(100)
100
+ assert_equal("A\001d", c32.to_ber)
101
+ g32 = Net::SNMP::Gauge32.new(100)
102
+ assert_equal("B\001d", g32.to_ber)
103
+ t32 = Net::SNMP::TimeTicks32.new(100)
104
+ assert_equal("C\001d", t32.to_ber)
105
+ end
106
+
107
+ def test_community
108
+ data = SnmpGetRequestXXX.dup
109
+ ary = data.read_ber(Net::SNMP::AsnSyntax)
110
+ pdu = Net::SnmpPdu.parse(ary)
111
+ assert_equal("xxxxxx", pdu.community)
112
+ end
113
+
114
+ end
@@ -0,0 +1,101 @@
1
+ # $Id: testdata.ldif 50 2006-04-17 17:57:33Z blackhedd $
2
+ #
3
+ # This is test-data for an LDAP server in LDIF format.
4
+ #
5
+ dn: dc=bayshorenetworks,dc=com
6
+ objectClass: dcObject
7
+ objectClass: organization
8
+ o: Bayshore Networks LLC
9
+ dc: bayshorenetworks
10
+
11
+ dn: cn=Manager,dc=bayshorenetworks,dc=com
12
+ objectClass: organizationalrole
13
+ cn: Manager
14
+
15
+ dn: ou=people,dc=bayshorenetworks,dc=com
16
+ objectClass: organizationalunit
17
+ ou: people
18
+
19
+ dn: ou=privileges,dc=bayshorenetworks,dc=com
20
+ objectClass: organizationalunit
21
+ ou: privileges
22
+
23
+ dn: ou=roles,dc=bayshorenetworks,dc=com
24
+ objectClass: organizationalunit
25
+ ou: roles
26
+
27
+ dn: ou=office,dc=bayshorenetworks,dc=com
28
+ objectClass: organizationalunit
29
+ ou: office
30
+
31
+ dn: mail=nogoodnik@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
32
+ cn: Bob Fosse
33
+ mail: nogoodnik@steamheat.net
34
+ sn: Fosse
35
+ ou: people
36
+ objectClass: top
37
+ objectClass: inetorgperson
38
+ objectClass: authorizedperson
39
+ hasAccessRole: uniqueIdentifier=engineer,ou=roles
40
+ hasAccessRole: uniqueIdentifier=ldapadmin,ou=roles
41
+ hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
42
+ hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
43
+ hasAccessRole: uniqueIdentifier=ogilvy_eagle_user,ou=roles
44
+ hasAccessRole: uniqueIdentifier=greenplug_user,ou=roles
45
+ hasAccessRole: uniqueIdentifier=brandplace_logging_user,ou=roles
46
+ hasAccessRole: uniqueIdentifier=brandplace_report_user,ou=roles
47
+ hasAccessRole: uniqueIdentifier=workorder_user,ou=roles
48
+ hasAccessRole: uniqueIdentifier=bayshore_eagle_user,ou=roles
49
+ hasAccessRole: uniqueIdentifier=bayshore_eagle_superuser,ou=roles
50
+ hasAccessRole: uniqueIdentifier=kledaras_user,ou=roles
51
+
52
+ dn: mail=elephant@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
53
+ cn: Gwen Verdon
54
+ mail: elephant@steamheat.net
55
+ sn: Verdon
56
+ ou: people
57
+ objectClass: top
58
+ objectClass: inetorgperson
59
+ objectClass: authorizedperson
60
+ hasAccessRole: uniqueIdentifier=brandplace_report_user,ou=roles
61
+ hasAccessRole: uniqueIdentifier=engineer,ou=roles
62
+ hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
63
+ hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
64
+ hasAccessRole: uniqueIdentifier=ldapadmin,ou=roles
65
+
66
+ dn: uniqueIdentifier=engineering,ou=privileges,dc=bayshorenetworks,dc=com
67
+ uniqueIdentifier: engineering
68
+ ou: privileges
69
+ objectClass: accessPrivilege
70
+
71
+ dn: uniqueIdentifier=engineer,ou=roles,dc=bayshorenetworks,dc=com
72
+ uniqueIdentifier: engineer
73
+ ou: roles
74
+ objectClass: accessRole
75
+ hasAccessPrivilege: uniqueIdentifier=engineering,ou=privileges
76
+
77
+ dn: uniqueIdentifier=ldapadmin,ou=roles,dc=bayshorenetworks,dc=com
78
+ uniqueIdentifier: ldapadmin
79
+ ou: roles
80
+ objectClass: accessRole
81
+
82
+ dn: uniqueIdentifier=ldapsuperadmin,ou=roles,dc=bayshorenetworks,dc=com
83
+ uniqueIdentifier: ldapsuperadmin
84
+ ou: roles
85
+ objectClass: accessRole
86
+
87
+ dn: mail=catperson@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
88
+ cn: Sid Sorokin
89
+ mail: catperson@steamheat.net
90
+ sn: Sorokin
91
+ ou: people
92
+ objectClass: top
93
+ objectClass: inetorgperson
94
+ objectClass: authorizedperson
95
+ hasAccessRole: uniqueIdentifier=engineer,ou=roles
96
+ hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
97
+ hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
98
+ hasAccessRole: uniqueIdentifier=ogilvy_eagle_user,ou=roles
99
+ hasAccessRole: uniqueIdentifier=greenplug_user,ou=roles
100
+ hasAccessRole: uniqueIdentifier=workorder_user,ou=roles
101
+
@@ -0,0 +1,210 @@
1
+ # $Id$
2
+ #
3
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
4
+ # Gmail account: garbagecat10.
5
+ #
6
+ # This is an LDAP server intended for unit testing of Net::LDAP.
7
+ # It implements as much of the protocol as we have the stomach
8
+ # to implement but serves static data. Use ldapsearch to test
9
+ # this server!
10
+ #
11
+ # To make this easier to write, we use the Ruby/EventMachine
12
+ # reactor library.
13
+ #
14
+
15
+ #------------------------------------------------
16
+
17
+ module LdapServer
18
+
19
+ LdapServerAsnSyntax = {
20
+ :application => {
21
+ :constructed => {
22
+ 0 => :array, # LDAP BindRequest
23
+ 3 => :array # LDAP SearchRequest
24
+ },
25
+ :primitive => {
26
+ 2 => :string, # ldapsearch sends this to unbind
27
+ }
28
+ },
29
+ :context_specific => {
30
+ :primitive => {
31
+ 0 => :string, # simple auth (password)
32
+ 7 => :string # present filter
33
+ },
34
+ :constructed => {
35
+ 3 => :array # equality filter
36
+ },
37
+ }
38
+ }
39
+
40
+ def post_init
41
+ $logger.info "Accepted LDAP connection"
42
+ @authenticated = false
43
+ end
44
+
45
+ def receive_data data
46
+ @data ||= ""; @data << data
47
+ while pdu = @data.read_ber!(LdapServerAsnSyntax)
48
+ begin
49
+ handle_ldap_pdu pdu
50
+ rescue
51
+ $logger.error "closing connection due to error #{$!}"
52
+ close_connection
53
+ end
54
+ end
55
+ end
56
+
57
+ def handle_ldap_pdu pdu
58
+ tag_id = pdu[1].ber_identifier
59
+ case tag_id
60
+ when 0x60
61
+ handle_bind_request pdu
62
+ when 0x63
63
+ handle_search_request pdu
64
+ when 0x42
65
+ # bizarre thing, it's a null object (primitive application-2)
66
+ # sent by ldapsearch to request an unbind (or a kiss-off, not sure which)
67
+ close_connection_after_writing
68
+ else
69
+ $logger.error "received unknown packet-type #{tag_id}"
70
+ close_connection_after_writing
71
+ end
72
+ end
73
+
74
+ def handle_bind_request pdu
75
+ # TODO, return a proper LDAP error instead of blowing up on version error
76
+ if pdu[1][0] != 3
77
+ send_ldap_response 1, pdu[0].to_i, 2, "", "We only support version 3"
78
+ elsif pdu[1][1] != "cn=bigshot,dc=bayshorenetworks,dc=com"
79
+ send_ldap_response 1, pdu[0].to_i, 48, "", "Who are you?"
80
+ elsif pdu[1][2].ber_identifier != 0x80
81
+ send_ldap_response 1, pdu[0].to_i, 7, "", "Keep it simple, man"
82
+ elsif pdu[1][2] != "opensesame"
83
+ send_ldap_response 1, pdu[0].to_i, 49, "", "Make my day"
84
+ else
85
+ @authenticated = true
86
+ send_ldap_response 1, pdu[0].to_i, 0, pdu[1][1], "I'll take it"
87
+ end
88
+ end
89
+
90
+
91
+
92
+ #--
93
+ # Search Response ::=
94
+ # CHOICE {
95
+ # entry [APPLICATION 4] SEQUENCE {
96
+ # objectName LDAPDN,
97
+ # attributes SEQUENCE OF SEQUENCE {
98
+ # AttributeType,
99
+ # SET OF AttributeValue
100
+ # }
101
+ # },
102
+ # resultCode [APPLICATION 5] LDAPResult
103
+ # }
104
+ def handle_search_request pdu
105
+ unless @authenticated
106
+ # NOTE, early exit.
107
+ send_ldap_response 5, pdu[0].to_i, 50, "", "Who did you say you were?"
108
+ return
109
+ end
110
+
111
+ treebase = pdu[1][0]
112
+ if treebase != "dc=bayshorenetworks,dc=com"
113
+ send_ldap_response 5, pdu[0].to_i, 32, "", "unknown treebase"
114
+ return
115
+ end
116
+
117
+ msgid = pdu[0].to_i.to_ber
118
+
119
+ # pdu[1][7] is the list of requested attributes.
120
+ # If it's an empty array, that means that *all* attributes were requested.
121
+ requested_attrs = if pdu[1][7].length > 0
122
+ pdu[1][7].map {|a| a.downcase}
123
+ else
124
+ :all
125
+ end
126
+
127
+ filters = pdu[1][6]
128
+ if filters.length == 0
129
+ # NOTE, early exit.
130
+ send_ldap_response 5, pdu[0].to_i, 53, "", "No filter specified"
131
+ end
132
+
133
+ # TODO, what if this returns nil?
134
+ filter = Net::LDAP::Filter.parse_ldap_filter( filters )
135
+
136
+ $ldif.each {|dn, entry|
137
+ if filter.match( entry )
138
+ attrs = []
139
+ entry.each {|k, v|
140
+ if requested_attrs == :all or requested_attrs.include?(k.downcase)
141
+ attrvals = v.map {|v1| v1.to_ber}.to_ber_set
142
+ attrs << [k.to_ber, attrvals].to_ber_sequence
143
+ end
144
+ }
145
+
146
+ appseq = [dn.to_ber, attrs.to_ber_sequence].to_ber_appsequence(4)
147
+ pkt = [msgid.to_ber, appseq].to_ber_sequence
148
+ send_data pkt
149
+ end
150
+ }
151
+
152
+
153
+ send_ldap_response 5, pdu[0].to_i, 0, "", "Was that what you wanted?"
154
+ end
155
+
156
+
157
+
158
+ def send_ldap_response pkt_tag, msgid, code, dn, text
159
+ send_data( [msgid.to_ber, [code.to_ber, dn.to_ber, text.to_ber].to_ber_appsequence(pkt_tag) ].to_ber )
160
+ end
161
+
162
+ end
163
+
164
+
165
+ #------------------------------------------------
166
+
167
+ # Rather bogus, a global method, which reads a HARDCODED filename
168
+ # parses out LDIF data. It will be used to serve LDAP queries out of this server.
169
+ #
170
+ def load_test_data
171
+ ary = File.readlines( "./testdata.ldif" )
172
+ hash = {}
173
+ while line = ary.shift and line.chomp!
174
+ if line =~ /^dn:[\s]*/i
175
+ dn = $'
176
+ hash[dn] = {}
177
+ while attr = ary.shift and attr.chomp! and attr =~ /^([\w]+)[\s]*:[\s]*/
178
+ hash[dn][$1.downcase] ||= []
179
+ hash[dn][$1.downcase] << $'
180
+ end
181
+ end
182
+ end
183
+ hash
184
+ end
185
+
186
+
187
+ #------------------------------------------------
188
+
189
+ if __FILE__ == $0
190
+
191
+ require 'rubygems'
192
+ require 'eventmachine'
193
+
194
+ require 'logger'
195
+ $logger = Logger.new $stderr
196
+
197
+ $logger.info "adding ../lib to loadpath, to pick up dev version of Net::LDAP."
198
+ $:.unshift "../lib"
199
+
200
+ $ldif = load_test_data
201
+
202
+ require 'net/ldap'
203
+
204
+ EventMachine.run {
205
+ $logger.info "starting LDAP server on 127.0.0.1 port 3890"
206
+ EventMachine.start_server "127.0.0.1", 3890, LdapServer
207
+ EventMachine.add_periodic_timer 60, proc {$logger.info "heartbeat"}
208
+ }
209
+ end
210
+