ruby-ldap 0.9.11 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +18 -0
- data/FAQ +5 -9
- data/NOTES +29 -0
- data/README +22 -18
- data/TODO +10 -0
- data/clientauth.c +605 -0
- data/conn.c +24 -1
- data/entry.c +9 -9
- data/extconf.rb +70 -29
- data/ldap.c +67 -0
- data/lib/ldap/control.rb +3 -3
- data/lib/ldap/ldif.rb +264 -269
- data/lib/ldap/schema.rb +39 -33
- data/mod.c +7 -3
- data/rbldap.h +8 -6
- data/test/cookbooks/apt/metadata.rb +13 -0
- data/test/cookbooks/apt/providers/repository.rb +73 -0
- data/test/cookbooks/apt/recipes/cacher-client.rb +44 -0
- data/test/cookbooks/apt/recipes/cacher.rb +45 -0
- data/test/cookbooks/apt/recipes/default.rb +50 -0
- data/test/cookbooks/apt/resources/repository.rb +30 -0
- data/test/cookbooks/nginx/attributes/default.rb +35 -0
- data/test/cookbooks/nginx/definitions/nginx_site.rb +35 -0
- data/test/cookbooks/nginx/metadata.rb +86 -0
- data/test/cookbooks/nginx/recipes/default.rb +56 -0
- data/test/cookbooks/nginx/recipes/source.rb +143 -0
- data/test/cookbooks/openldap/attributes/default.rb +61 -0
- data/test/cookbooks/openldap/metadata.rb +99 -0
- data/test/cookbooks/openldap/recipes/auth.rb +70 -0
- data/test/cookbooks/openldap/recipes/client.rb +28 -0
- data/test/cookbooks/openldap/recipes/default.rb +18 -0
- data/test/cookbooks/openldap/recipes/server.rb +110 -0
- data/test/cookbooks/postgresql/attributes/default.rb +68 -0
- data/test/cookbooks/postgresql/metadata.rb +15 -0
- data/test/cookbooks/postgresql/recipes/client.rb +27 -0
- data/test/cookbooks/postgresql/recipes/default.rb +20 -0
- data/test/cookbooks/postgresql/recipes/server.rb +36 -0
- data/test/cookbooks/postgresql/recipes/server_debian.rb +51 -0
- data/test/cookbooks/postgresql/recipes/server_redhat.rb +84 -0
- data/test/cookbooks/sqlite/metadata.rb +11 -0
- data/test/cookbooks/sqlite/recipes/default.rb +26 -0
- data/test/cookbooks/vagrant_main/recipes/default.rb +12 -0
- data/test/moz_cert.rb +105 -0
- data/test/setup.rb +2 -2
- data/win/wldap32.def +257 -0
- metadata +78 -55
data/conn.c
CHANGED
@@ -31,6 +31,7 @@ rb_ldap_conn_free (RB_LDAP_DATA * ldapdata)
|
|
31
31
|
{
|
32
32
|
ldap_unbind (ldapdata->ldap);
|
33
33
|
};
|
34
|
+
xfree(ldapdata);
|
34
35
|
};
|
35
36
|
|
36
37
|
static void
|
@@ -167,6 +168,26 @@ rb_ldap_conn_s_open (int argc, VALUE argv[], VALUE klass)
|
|
167
168
|
return conn;
|
168
169
|
};
|
169
170
|
|
171
|
+
/*
|
172
|
+
* call-seq:
|
173
|
+
* LDAP::Conn.open_uri(uri) => LDAP::Conn
|
174
|
+
*
|
175
|
+
* Return a new LDAP::Conn connection to the server described with +uri+.
|
176
|
+
*/
|
177
|
+
VALUE
|
178
|
+
rb_ldap_conn_s_open_uri (VALUE klass, VALUE uri)
|
179
|
+
{
|
180
|
+
LDAP *cldap = NULL;
|
181
|
+
int rc;
|
182
|
+
|
183
|
+
rc = ldap_initialize (&cldap, StringValueCStr (uri));
|
184
|
+
|
185
|
+
if (rc || cldap == NULL)
|
186
|
+
rb_raise (rb_eLDAP_ResultError, "can't open an LDAP session");
|
187
|
+
|
188
|
+
return rb_ldap_conn_new (klass, cldap);
|
189
|
+
};
|
190
|
+
|
170
191
|
/*
|
171
192
|
* call-seq:
|
172
193
|
* conn.start_tls => nil
|
@@ -742,7 +763,7 @@ rb_ldap_conn_get_errno (VALUE self)
|
|
742
763
|
GET_LDAP_DATA (self, ldapdata);
|
743
764
|
|
744
765
|
#ifdef USE_NETSCAPE_SDK
|
745
|
-
cerr = ldap_get_lderrno (ldapdata->ldap, NULL, NULL);
|
766
|
+
int cerr = ldap_get_lderrno (ldapdata->ldap, NULL, NULL);
|
746
767
|
err = INT2NUM (cerr);
|
747
768
|
#else
|
748
769
|
# ifdef USE_OPENLDAP1
|
@@ -1515,6 +1536,7 @@ rb_ldap_conn_modify_s (VALUE self, VALUE dn, VALUE attrs)
|
|
1515
1536
|
|
1516
1537
|
ldapdata->err = ldap_modify_s (ldapdata->ldap, c_dn, c_attrs);
|
1517
1538
|
Check_LDAP_Result (ldapdata->err);
|
1539
|
+
free(c_attrs);
|
1518
1540
|
|
1519
1541
|
return self;
|
1520
1542
|
};
|
@@ -1777,6 +1799,7 @@ Init_ldap_conn ()
|
|
1777
1799
|
rb_ldap_conn_s_allocate, 0);
|
1778
1800
|
#endif
|
1779
1801
|
rb_define_singleton_method (rb_cLDAP_Conn, "open", rb_ldap_conn_s_open, -1);
|
1802
|
+
rb_define_singleton_method (rb_cLDAP_Conn, "open_uri", rb_ldap_conn_s_open_uri, 1);
|
1780
1803
|
rb_define_singleton_method (rb_cLDAP_Conn, "set_option",
|
1781
1804
|
rb_ldap_conn_s_set_option, 2);
|
1782
1805
|
rb_define_singleton_method (rb_cLDAP_Conn, "get_option",
|
data/entry.c
CHANGED
@@ -12,6 +12,7 @@ VALUE rb_cLDAP_Entry;
|
|
12
12
|
void
|
13
13
|
rb_ldap_entry_free (RB_LDAPENTRY_DATA * edata)
|
14
14
|
{
|
15
|
+
xfree(edata);
|
15
16
|
/* edata->msg is valid in a block given by each search operation */
|
16
17
|
/* ldap_msgfree should be called after ldap_search */
|
17
18
|
}
|
@@ -22,7 +23,7 @@ rb_ldap_entry_new (LDAP * ldap, LDAPMessage * msg)
|
|
22
23
|
VALUE val;
|
23
24
|
RB_LDAPENTRY_DATA *edata;
|
24
25
|
val = Data_Make_Struct (rb_cLDAP_Entry, RB_LDAPENTRY_DATA,
|
25
|
-
0,
|
26
|
+
0, rb_ldap_entry_free, edata);
|
26
27
|
edata->ldap = ldap;
|
27
28
|
edata->msg = msg;
|
28
29
|
return val;
|
@@ -112,7 +113,7 @@ rb_ldap_entry_get_attributes (VALUE self)
|
|
112
113
|
RB_LDAPENTRY_DATA *edata;
|
113
114
|
VALUE vals;
|
114
115
|
char *attr;
|
115
|
-
BerElement *ber;
|
116
|
+
BerElement *ber = NULL;
|
116
117
|
|
117
118
|
GET_LDAPENTRY_DATA (self, edata);
|
118
119
|
|
@@ -122,15 +123,14 @@ rb_ldap_entry_get_attributes (VALUE self)
|
|
122
123
|
attr = ldap_next_attribute (edata->ldap, edata->msg, ber))
|
123
124
|
{
|
124
125
|
rb_ary_push (vals, rb_tainted_str_new2 (attr));
|
126
|
+
ldap_memfree(attr);
|
125
127
|
}
|
126
128
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
#endif
|
133
|
-
*/
|
129
|
+
#if !defined(USE_OPENLDAP1)
|
130
|
+
if( ber != NULL ){
|
131
|
+
ber_free(ber, 0);
|
132
|
+
}
|
133
|
+
#endif
|
134
134
|
|
135
135
|
return vals;
|
136
136
|
}
|
data/extconf.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
#
|
3
3
|
# extconf.rb for ldap extension
|
4
4
|
# $Id: extconf.rb,v 1.7 2006/04/18 23:49:56 ianmacd Exp $
|
@@ -10,13 +10,14 @@ $INTERACTIVE = false
|
|
10
10
|
|
11
11
|
if( ARGV.include?("--help") )
|
12
12
|
print <<EOF
|
13
|
-
--with-ldap-dir
|
13
|
+
--with-ldap-dir specify the LDAP directory.
|
14
14
|
--with-ldap-include specify the directory containing ldap.h and lber.h.
|
15
|
-
--with-ldap-lib
|
16
|
-
--with-netscape
|
17
|
-
--with-
|
18
|
-
--with-
|
19
|
-
--with-
|
15
|
+
--with-ldap-lib specify the directory containing the LDAP libraries.
|
16
|
+
--with-netscape build with Netscape SDK.
|
17
|
+
--with-mozilla build with Mozilla SDK (Enables certificate authentication).
|
18
|
+
--with-openldap1 build with OpenLDAP 1.x.
|
19
|
+
--with-openldap2 build with OpenLDAP 2.x.
|
20
|
+
--with-wldap32 Active Directory Client API.
|
20
21
|
|
21
22
|
The following are library configuration options:
|
22
23
|
--with-libcrypto=crypto, --without-libcrypto
|
@@ -40,7 +41,9 @@ def find_files(dir = nil)
|
|
40
41
|
search_dirs =
|
41
42
|
["/usr/local", "/usr", "/opt"] +
|
42
43
|
Dir.glob("/usr/local/./*ldap*").collect{|d| d.gsub(/\/\.\//, "/")} +
|
43
|
-
Dir.glob("/usr/./*ldap*").collect{|d| d.gsub(/\/\.\//, "/")
|
44
|
+
Dir.glob("/usr/./*ldap*").collect{|d| d.gsub(/\/\.\//, "/") +
|
45
|
+
Dir.glob("/usr/lib{64,}/mozldap/*ldap*") + ["/usr/include/mozldap"]
|
46
|
+
}
|
44
47
|
end
|
45
48
|
for d in search_dirs
|
46
49
|
h = File.join(d,"include","ldap.h")
|
@@ -48,21 +51,21 @@ def find_files(dir = nil)
|
|
48
51
|
if( File.exist?(h) )
|
49
52
|
l = Dir.glob(l)[0]
|
50
53
|
if( l )
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
54
|
+
if( $INTERACTIVE )
|
55
|
+
print("--with-ldap-dir=#{d} [y/n]")
|
56
|
+
ans = $stdin.gets
|
57
|
+
ans.chop!
|
58
|
+
if( ans == "y" )
|
59
|
+
result = [d, File.basename(l).split(".")[0][3..-1], File.basename(h)]
|
60
|
+
return result
|
61
|
+
break
|
62
|
+
end
|
63
|
+
else
|
64
|
+
print("--with-ldap-dir=#{d}\n")
|
65
|
+
result = [d, File.basename(l).split(".")[0][3..-1], File.basename(h)]
|
66
|
+
return result
|
67
|
+
break
|
68
|
+
end
|
66
69
|
end
|
67
70
|
end
|
68
71
|
end
|
@@ -83,6 +86,9 @@ def ldap_with_config(arg, default = nil)
|
|
83
86
|
end
|
84
87
|
|
85
88
|
$use_netscape = ldap_with_config("netscape")
|
89
|
+
if ldap_with_config("mozilla")
|
90
|
+
$use_netscape = '6'
|
91
|
+
end
|
86
92
|
$use_openldap1 = ldap_with_config("openldap1")
|
87
93
|
$use_openldap2 = ldap_with_config("openldap2")
|
88
94
|
$use_wldap32 = ldap_with_config("wldap32")
|
@@ -100,9 +106,17 @@ if( !($use_netscape || $use_openldap1 || $use_openldap2 || $use_wldap32) )
|
|
100
106
|
when /^ssldap50+$/, /^ldap50+$/
|
101
107
|
print("--with-netscape=5")
|
102
108
|
$use_netscape = "5"
|
109
|
+
when /^ssldap60+$/, /^ldap60+$/
|
110
|
+
print("--with-netscape=6")
|
111
|
+
$use_netscape = "6"
|
103
112
|
else
|
104
|
-
|
105
|
-
|
113
|
+
if RUBY_PLATFORM =~ /-(:?mingw32|mswin32)/
|
114
|
+
print("--with-wldap32\n")
|
115
|
+
$use_wldap32 = true
|
116
|
+
else
|
117
|
+
print("--with-openldap2\n")
|
118
|
+
$use_openldap2 = true
|
119
|
+
end
|
106
120
|
end
|
107
121
|
end
|
108
122
|
if( $use_netscape == true )
|
@@ -127,6 +141,27 @@ if( $use_netscape )
|
|
127
141
|
$libns = ldap_with_config("libns", "nspr4,plc4,plds4").split(",")
|
128
142
|
$liblber = ldap_with_config("liblber", "lber50")
|
129
143
|
$libssl = ldap_with_config("libssl", "ssl3")
|
144
|
+
when /^6/
|
145
|
+
%x{pkg-config --exists 'mozldap >= 6.0 nspr >= 4.0'}
|
146
|
+
|
147
|
+
if $? == 0
|
148
|
+
puts 'Mozzilla LDAP libs will be used.'
|
149
|
+
$mozlibs = %x{pkg-config mozldap nspr --libs}.chomp
|
150
|
+
$mozincs = %x{pkg-config mozldap nspr --cflags}.chomp
|
151
|
+
else
|
152
|
+
puts 'pkg-config reported that no right mozilla LDAP libs were found'
|
153
|
+
puts 'we need mozldap >= 6.0 and nspr >= 4.0'
|
154
|
+
exit 1
|
155
|
+
end
|
156
|
+
|
157
|
+
$defs << "-DUSE_NETSCAPE_SDK -DUSE_SSL_CLIENTAUTH"
|
158
|
+
#$libnsl = ldap_with_config("libnsl", "nsl")
|
159
|
+
#$libpthread = ldap_with_config("libpthread", "pthread")
|
160
|
+
$libresolv = ldap_with_config("libresolv", "resolv")
|
161
|
+
$libldap = ldap_with_config("libldap", "ldap60")
|
162
|
+
$libns = ldap_with_config("libns", "nspr4,plc4,plds4").split(",")
|
163
|
+
$liblber = ldap_with_config("liblber", "lber60")
|
164
|
+
$libssl = ldap_with_config("libssl", "ssl3")
|
130
165
|
end
|
131
166
|
end
|
132
167
|
|
@@ -173,6 +208,10 @@ if( $use_wldap32 )
|
|
173
208
|
have_header("winldap.h")
|
174
209
|
have_header("winlber.h")
|
175
210
|
have_header("sys/time.h")
|
211
|
+
elsif $use_netscape =~ /^6/
|
212
|
+
# mozilla
|
213
|
+
pkg_config('mozldap')
|
214
|
+
pkg_config('nspr')
|
176
215
|
else
|
177
216
|
ldap_h = have_header("ldap.h")
|
178
217
|
lber_h = have_header("lber.h")
|
@@ -187,6 +226,7 @@ else
|
|
187
226
|
have_header("openssl/crypto.h") || have_header("crypto.h")
|
188
227
|
end
|
189
228
|
|
229
|
+
$LIBS << ' -pthread'
|
190
230
|
for l in [$libcrypto, $libssl, $libnsl, $libpthread, $libresolv,
|
191
231
|
$libns, $liblber, $libldap_r, $libldap].flatten
|
192
232
|
if( l )
|
@@ -194,7 +234,7 @@ for l in [$libcrypto, $libssl, $libnsl, $libpthread, $libresolv,
|
|
194
234
|
end
|
195
235
|
end
|
196
236
|
|
197
|
-
have_func("ldap_init")
|
237
|
+
have_func("ldap_init", 'ldap.h')
|
198
238
|
have_func("ldap_set_option")
|
199
239
|
have_func("ldap_get_option")
|
200
240
|
have_func("ldap_start_tls_s") if $use_openldap2
|
@@ -242,7 +282,7 @@ end
|
|
242
282
|
$run_test += " #{$slapd} #{$schema_dir}"
|
243
283
|
|
244
284
|
|
245
|
-
File.open("Makefile","a")
|
285
|
+
File.open("Makefile","a") do |f|
|
246
286
|
f.print <<EOF
|
247
287
|
|
248
288
|
test::
|
@@ -263,6 +303,7 @@ doc:
|
|
263
303
|
unit:
|
264
304
|
\t(cd test; $(RUBY_INSTALL_NAME) tc_ldif.rb)
|
265
305
|
|
266
|
-
.PHONY:
|
306
|
+
.PHONY: doc
|
267
307
|
EOF
|
268
|
-
|
308
|
+
|
309
|
+
end
|
data/ldap.c
CHANGED
@@ -88,6 +88,65 @@ rb_ldap_dn2ufn (VALUE self, VALUE dn)
|
|
88
88
|
}
|
89
89
|
}
|
90
90
|
|
91
|
+
VALUE
|
92
|
+
rb_ldap_explode_dn (VALUE self, VALUE dn, VALUE notypes)
|
93
|
+
{
|
94
|
+
char **c_arr, **p;
|
95
|
+
char *c_dn;
|
96
|
+
VALUE ary;
|
97
|
+
|
98
|
+
if (dn == Qnil)
|
99
|
+
{
|
100
|
+
return Qnil;
|
101
|
+
}
|
102
|
+
|
103
|
+
c_dn = StringValueCStr (dn);
|
104
|
+
if ((c_arr = ldap_explode_dn (c_dn, RTEST (notypes) ? 1 : 0)))
|
105
|
+
{
|
106
|
+
ary = rb_ary_new ();
|
107
|
+
for (p = c_arr; *p != NULL; p++)
|
108
|
+
{
|
109
|
+
rb_ary_push (ary, rb_tainted_str_new2 (*p));
|
110
|
+
}
|
111
|
+
ldap_value_free (c_arr);
|
112
|
+
|
113
|
+
return ary;
|
114
|
+
}
|
115
|
+
else
|
116
|
+
{
|
117
|
+
return Qnil;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
VALUE
|
122
|
+
rb_ldap_explode_rdn (VALUE self, VALUE rdn, VALUE notypes)
|
123
|
+
{
|
124
|
+
char **c_arr, **p;
|
125
|
+
char *c_dn;
|
126
|
+
VALUE ary;
|
127
|
+
|
128
|
+
if (rdn == Qnil)
|
129
|
+
{
|
130
|
+
return Qnil;
|
131
|
+
}
|
132
|
+
|
133
|
+
c_dn = StringValueCStr (rdn);
|
134
|
+
if ((c_arr = ldap_explode_rdn (c_dn, RTEST (notypes) ? 1 : 0)))
|
135
|
+
{
|
136
|
+
ary = rb_ary_new ();
|
137
|
+
for (p = c_arr; *p != NULL; p++) {
|
138
|
+
rb_ary_push (ary, rb_tainted_str_new2 (*p));
|
139
|
+
}
|
140
|
+
ldap_value_free (c_arr);
|
141
|
+
|
142
|
+
return ary;
|
143
|
+
}
|
144
|
+
else
|
145
|
+
{
|
146
|
+
return Qnil;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
91
150
|
/*
|
92
151
|
* call-seq:
|
93
152
|
* LDAP.mod(mod_type, attr, vals) => LDAP::Mod
|
@@ -165,6 +224,9 @@ extern void Init_ldap_entry ();
|
|
165
224
|
extern void Init_ldap_conn ();
|
166
225
|
extern void Init_ldap_sslconn ();
|
167
226
|
extern void Init_ldap_saslconn ();
|
227
|
+
#ifdef USE_SSL_CLIENTAUTH
|
228
|
+
extern void Init_ldap_clientauth ();
|
229
|
+
#endif
|
168
230
|
extern void Init_ldap_mod ();
|
169
231
|
extern void Init_ldap_misc ();
|
170
232
|
|
@@ -246,6 +308,8 @@ Init_ldap ()
|
|
246
308
|
|
247
309
|
|
248
310
|
rb_define_module_function (rb_mLDAP, "err2string", rb_ldap_err2string, 1);
|
311
|
+
rb_define_module_function (rb_mLDAP, "explode_dn", rb_ldap_explode_dn, 2);
|
312
|
+
rb_define_module_function (rb_mLDAP, "explode_rdn", rb_ldap_explode_rdn, 2);
|
249
313
|
rb_define_module_function (rb_mLDAP, "dn2ufn", rb_ldap_dn2ufn, 1);
|
250
314
|
rb_define_module_function (rb_mLDAP, "mod", rb_ldap_mod_s_new, -1);
|
251
315
|
rb_define_module_function (rb_mLDAP, "hash2mods", rb_ldap_hash2mods, 2);
|
@@ -571,6 +635,9 @@ Init_ldap ()
|
|
571
635
|
Init_ldap_conn ();
|
572
636
|
Init_ldap_sslconn ();
|
573
637
|
Init_ldap_saslconn ();
|
638
|
+
#ifdef USE_SSL_CLIENTAUTH
|
639
|
+
Init_ldap_clientauth();
|
640
|
+
#endif
|
574
641
|
Init_ldap_entry ();
|
575
642
|
Init_ldap_mod ();
|
576
643
|
Init_ldap_misc ();
|
data/lib/ldap/control.rb
CHANGED
@@ -17,7 +17,7 @@ module LDAP
|
|
17
17
|
#
|
18
18
|
def Control.encode( *vals )
|
19
19
|
encoded_vals = []
|
20
|
-
|
20
|
+
|
21
21
|
vals.each do |val|
|
22
22
|
encoded_vals <<
|
23
23
|
case val
|
@@ -29,7 +29,7 @@ module LDAP
|
|
29
29
|
# What other types may exist?
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
OpenSSL::ASN1::Sequence.new( encoded_vals ).to_der
|
34
34
|
end
|
35
35
|
|
@@ -40,7 +40,7 @@ module LDAP
|
|
40
40
|
values = []
|
41
41
|
|
42
42
|
OpenSSL::ASN1::decode( self.value ).value.each do |val|
|
43
|
-
|
43
|
+
values << val.value
|
44
44
|
end
|
45
45
|
|
46
46
|
values
|
data/lib/ldap/ldif.rb
CHANGED
@@ -37,22 +37,22 @@ module LDAP
|
|
37
37
|
#
|
38
38
|
def send( conn )
|
39
39
|
if @change_type == :MODRDN
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
# TODO: How do we deal with 'newsuperior'?
|
41
|
+
# The LDAP API's ldap_modrdn2_s() function doesn't seem to use it.
|
42
|
+
return conn.modrdn( @dn, @attrs['newrdn'], @attrs['deleteoldrdn'] )
|
43
43
|
end
|
44
44
|
|
45
45
|
# Mask out the LDAP_MOD_BVALUES bit, as it's irrelevant here.
|
46
46
|
case @change_type & ~LDAP_MOD_BVALUES
|
47
47
|
when LDAP_MOD_ADD
|
48
|
-
|
49
|
-
|
48
|
+
@controls == [] ? conn.add( @dn, @attrs ) :
|
49
|
+
conn.add_ext( @dn, @attrs, @controls, [] )
|
50
50
|
when LDAP_MOD_DELETE
|
51
|
-
|
52
|
-
|
51
|
+
@controls == [] ? conn.delete( @dn ) :
|
52
|
+
conn.delete_ext( @dn, @controls, [] )
|
53
53
|
when LDAP_MOD_REPLACE
|
54
|
-
|
55
|
-
|
54
|
+
@controls == [] ? conn.modify( @dn, @mods ) :
|
55
|
+
conn.modify_ext( @dn, @mods, @controls, [] )
|
56
56
|
end
|
57
57
|
|
58
58
|
self
|
@@ -75,7 +75,7 @@ module LDAP
|
|
75
75
|
#
|
76
76
|
%w[ creatorsname createtimestamp modifiersname modifytimestamp
|
77
77
|
entrycsn entryuuid structuralobjectclass ].each do |attr|
|
78
|
-
|
78
|
+
@attrs.delete( attr )
|
79
79
|
end
|
80
80
|
|
81
81
|
# Clean out duplicate attribute values.
|
@@ -132,7 +132,7 @@ module LDAP
|
|
132
132
|
unless url.sub!( %r(^file://), '' )
|
133
133
|
raise ArgumentError, "Bad external file reference: #{url}"
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
# Slurp an external file.
|
137
137
|
# TODO: Support other URL types in the future.
|
138
138
|
File.open( url ).readlines( nil )[0]
|
@@ -150,10 +150,10 @@ module LDAP
|
|
150
150
|
sep = '::'
|
151
151
|
val = base64_encode( val, true )
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
firstline_len = LINE_LENGTH - ( "%s%s " % [ attr, sep ] ).length
|
155
155
|
ldif << "%s%s %s\n" % [ attr, sep, val.slice!( 0..firstline_len ) ]
|
156
|
-
|
156
|
+
|
157
157
|
while val.length > 0
|
158
158
|
ldif << " %s\n" % val.slice!( 0..LINE_LENGTH - 1 )
|
159
159
|
end
|
@@ -182,173 +182,172 @@ module LDAP
|
|
182
182
|
hash = {}
|
183
183
|
mods = {}
|
184
184
|
mod_type = nil
|
185
|
-
|
185
|
+
|
186
186
|
lines.each do |line|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
187
|
+
# Skip (continued) comments.
|
188
|
+
if line =~ /^#/ || ( comment && line[0..0] == ' ' )
|
189
|
+
comment = true
|
190
|
+
next
|
191
|
+
end
|
192
|
+
|
193
|
+
# Skip blank lines.
|
194
|
+
next if line =~ /^$/
|
195
|
+
|
196
|
+
# Reset mod type if this entry has more than one mod to make.
|
197
|
+
# A '-' continuation is only valid if we've already had a
|
198
|
+
# 'changetype: modify' line.
|
199
|
+
if line =~ /^-$/ && change_type == LDAP_MOD_REPLACE
|
200
|
+
next
|
201
|
+
end
|
202
|
+
|
203
|
+
line.chomp!
|
204
|
+
|
205
|
+
# N.B. Attributes and values can be separated by one or two colons,
|
206
|
+
# or one colon and a '<'. Either of these is then followed by zero
|
207
|
+
# or one spaces.
|
208
|
+
if md = line.match( /^[^ ].*?((:[:<]?) ?)/ )
|
209
|
+
|
210
|
+
# If previous value was Base64-encoded and is not continued,
|
211
|
+
# we need to decode it now.
|
212
|
+
if sep == '::'
|
213
|
+
if mod_type
|
214
|
+
mods[mod_type][attr][-1] =
|
215
|
+
base64_decode( mods[mod_type][attr][-1] )
|
216
|
+
bvalues << attr if unsafe_char?( mods[mod_type][attr][-1] )
|
217
|
+
else
|
218
|
+
hash[attr][-1] = base64_decode( hash[attr][-1] )
|
219
|
+
bvalues << attr if unsafe_char?( hash[attr][-1] )
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Found a attr/value line.
|
224
|
+
attr, val = line.split( md[1], 2 )
|
225
|
+
attr.downcase!
|
226
|
+
|
227
|
+
# Attribute must be ldap-oid / (ALPHA *(attr-type-chars))
|
228
|
+
if attr !~ /^(?:(?:\d+\.)*\d+|[[:alnum:]-]+)(?:;[[:alnum:]-]+)*$/
|
229
|
+
raise LDIFError, "Invalid attribute: #{attr}"
|
230
|
+
end
|
231
|
+
|
232
|
+
if attr == 'dn'
|
233
|
+
header = false
|
234
|
+
change_type = nil
|
235
|
+
controls = []
|
236
|
+
end
|
237
|
+
sep = md[2]
|
238
|
+
|
239
|
+
val = read_file( val ) if sep == ':<'
|
240
|
+
|
241
|
+
case attr
|
242
|
+
when 'version'
|
243
|
+
# Check the LDIF version.
|
244
|
+
if header
|
245
|
+
if val != '1'
|
246
|
+
raise LDIFError, "Unsupported LDIF version: #{val}"
|
247
|
+
else
|
248
|
+
header = false
|
249
|
+
next
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
when 'changetype'
|
254
|
+
change_type = case val
|
255
|
+
when 'add' then LDAP_MOD_ADD
|
256
|
+
when 'delete' then LDAP_MOD_DELETE
|
257
|
+
when 'modify' then LDAP_MOD_REPLACE
|
258
|
+
when /^modr?dn$/ then :MODRDN
|
259
|
+
end
|
260
|
+
|
261
|
+
raise LDIFError, "Invalid change type: #{attr}" unless change_type
|
262
|
+
|
263
|
+
when 'add', 'delete', 'replace'
|
264
|
+
unless change_type == LDAP_MOD_REPLACE
|
265
|
+
raise LDIFError, "Cannot #{attr} here."
|
266
|
+
end
|
267
|
+
|
268
|
+
mod_type = case attr
|
269
|
+
when 'add' then LDAP_MOD_ADD
|
270
|
+
when 'delete' then LDAP_MOD_DELETE
|
271
|
+
when 'replace' then LDAP_MOD_REPLACE
|
272
|
+
end
|
273
|
+
|
274
|
+
# In this case val is actually an attribute and should be lowercased.
|
275
|
+
mods[mod_type] ||= {}
|
276
|
+
mods[mod_type][val.downcase] ||= []
|
277
|
+
|
278
|
+
when 'control'
|
279
|
+
oid, criticality = val.split( / /, 2 )
|
280
|
+
|
281
|
+
unless oid =~ /(?:\d+\.)*\d+/
|
282
|
+
raise LDIFError, "Bad control OID: #{oid}"
|
283
|
+
end
|
284
|
+
|
285
|
+
if criticality
|
286
|
+
md = criticality.match( /(:[:<]?) ?/ )
|
287
|
+
ctl_sep = md[1] if md
|
288
|
+
criticality, value = criticality.split( /:[:<]? ?/, 2 )
|
289
|
+
|
290
|
+
if criticality !~ /^(?:true|false)$/
|
291
|
+
raise LDIFError, "Bad control criticality: #{criticality}"
|
292
|
+
end
|
293
|
+
|
294
|
+
# Convert 'true' or 'false'. to_boolean would be nice. :-)
|
295
|
+
criticality = eval( criticality )
|
296
|
+
end
|
297
|
+
|
298
|
+
if value
|
299
|
+
value = base64_decode( value ) if ctl_sep == '::'
|
300
|
+
value = read_file( value ) if ctl_sep == ':<'
|
301
|
+
value = Control.encode( value )
|
302
|
+
end
|
303
|
+
|
304
|
+
controls << Control.new( oid, value, criticality )
|
305
|
+
|
306
|
+
else
|
307
|
+
# Convert modrdn's deleteoldrdn from '1' to true, anything else
|
308
|
+
# to false. Should probably raise an exception if not '0' or '1'.
|
309
|
+
#
|
310
|
+
if change_type == :MODRDN && attr == 'deleteoldrdn'
|
311
|
+
val = val == '1' ? true : false
|
312
|
+
end
|
313
|
+
|
314
|
+
if change_type == LDAP_MOD_REPLACE
|
315
|
+
mods[mod_type][attr] << val
|
316
|
+
else
|
317
|
+
hash[attr] ||= []
|
318
|
+
hash[attr] << val
|
319
|
+
end
|
320
|
+
|
321
|
+
comment = false
|
322
|
+
|
323
|
+
# Make a note of this attribute if value is binary.
|
324
|
+
bvalues << attr if unsafe_char?( val )
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
else
|
329
|
+
# Check last line's separator: if not a binary value, the
|
330
|
+
# continuation line must be indented. If a comment makes it this
|
331
|
+
# far, that's also an error.
|
332
|
+
#
|
333
|
+
if sep == ':' && line[0..0] != ' ' || comment
|
334
|
+
raise LDIFError, "Improperly continued line: #{line}"
|
335
|
+
end
|
336
|
+
|
337
|
+
# OK; this is a valid continuation line.
|
338
|
+
|
339
|
+
# Append line except for initial space.
|
340
|
+
line[0] = '' if line[0..0] == ' '
|
341
|
+
|
342
|
+
if change_type == LDAP_MOD_REPLACE
|
343
|
+
# Append to last value of current mod type.
|
344
|
+
mods[mod_type][attr][-1] << line
|
345
|
+
else
|
346
|
+
# Append to last value.
|
347
|
+
hash[attr][-1] << line
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
352
351
|
end
|
353
352
|
|
354
353
|
# If last value in LDIF entry was Base64-encoded, we need to decode
|
@@ -356,11 +355,11 @@ module LDAP
|
|
356
355
|
if sep == '::'
|
357
356
|
if mod_type
|
358
357
|
mods[mod_type][attr][-1] =
|
359
|
-
|
360
|
-
|
358
|
+
base64_decode( mods[mod_type][attr][-1] )
|
359
|
+
bvalues << attr if unsafe_char?( mods[mod_type][attr][-1] )
|
361
360
|
else
|
362
361
|
hash[attr][-1] = base64_decode( hash[attr][-1] )
|
363
|
-
|
362
|
+
bvalues << attr if unsafe_char?( hash[attr][-1] )
|
364
363
|
end
|
365
364
|
end
|
366
365
|
|
@@ -377,47 +376,43 @@ module LDAP
|
|
377
376
|
|
378
377
|
case change_type
|
379
378
|
when LDAP_MOD_ADD
|
379
|
+
mods[LDAP_MOD_ADD] = []
|
380
380
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
ct = LDAP_MOD_ADD
|
388
|
-
end
|
381
|
+
hash.each do |attr_local, val|
|
382
|
+
if bvalues.include?( attr_local )
|
383
|
+
ct = LDAP_MOD_ADD | LDAP_MOD_BVALUES
|
384
|
+
else
|
385
|
+
ct = LDAP_MOD_ADD
|
386
|
+
end
|
389
387
|
|
390
|
-
|
391
|
-
|
388
|
+
mods[LDAP_MOD_ADD] << LDAP.mod( ct, attr_local, val )
|
389
|
+
end
|
392
390
|
|
393
391
|
when LDAP_MOD_DELETE
|
394
|
-
|
395
|
-
# Nothing to do.
|
392
|
+
# Nothing to do.
|
396
393
|
|
397
394
|
when LDAP_MOD_REPLACE
|
395
|
+
raise LDIFError, "mods should not be empty" if mods == {}
|
398
396
|
|
399
|
-
|
397
|
+
new_mods = {}
|
400
398
|
|
401
|
-
|
399
|
+
mods.each do |mod_type_local,attrs|
|
400
|
+
attrs.each_key do |attr_local|
|
401
|
+
if bvalues.include?( attr_local )
|
402
|
+
mt = mod_type_local | LDAP_MOD_BVALUES
|
403
|
+
else
|
404
|
+
mt = mod_type_local
|
405
|
+
end
|
402
406
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
else
|
408
|
-
mt = mod_type_local
|
409
|
-
end
|
410
|
-
|
411
|
-
new_mods[mt] ||= {}
|
412
|
-
new_mods[mt][attr_local] = mods[mod_type_local][attr_local]
|
413
|
-
end
|
414
|
-
end
|
407
|
+
new_mods[mt] ||= {}
|
408
|
+
new_mods[mt][attr_local] = mods[mod_type_local][attr_local]
|
409
|
+
end
|
410
|
+
end
|
415
411
|
|
416
|
-
|
412
|
+
mods = new_mods
|
417
413
|
|
418
414
|
when :MODRDN
|
419
|
-
|
420
|
-
# Nothing to do.
|
415
|
+
# Nothing to do.
|
421
416
|
|
422
417
|
end
|
423
418
|
|
@@ -439,53 +434,53 @@ module LDAP
|
|
439
434
|
def LDIF.parse_file( file, sort=false ) # :yield: record
|
440
435
|
|
441
436
|
File.open( file ) do |f|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
437
|
+
entries = []
|
438
|
+
entry = false
|
439
|
+
header = true
|
440
|
+
version = false
|
441
|
+
|
442
|
+
while line = f.gets
|
443
|
+
|
444
|
+
if line =~ /^dn:/
|
445
|
+
header = false
|
446
|
+
|
447
|
+
if entry && ! version
|
448
|
+
if block_given?
|
449
|
+
yield parse_entry( entry )
|
450
|
+
else
|
451
|
+
entries << parse_entry( entry )
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
if version
|
456
|
+
entry << line
|
457
|
+
version = false
|
458
|
+
else
|
459
|
+
entry = [ line ]
|
460
|
+
end
|
461
|
+
|
462
|
+
next
|
463
|
+
end
|
464
|
+
|
465
|
+
if header && line.downcase =~ /^version/
|
466
|
+
entry = [ line ]
|
467
|
+
version = true
|
468
|
+
next
|
469
|
+
end
|
470
|
+
|
471
|
+
entry << line
|
472
|
+
end
|
473
|
+
|
474
|
+
if block_given?
|
475
|
+
yield parse_entry( entry )
|
476
|
+
nil
|
477
|
+
else
|
478
|
+
entries << parse_entry( entry )
|
479
|
+
|
480
|
+
# Sort entries if sorting has been requested.
|
481
|
+
entries.sort! { |x,y| x.dn.length <=> y.dn.length } if sort
|
482
|
+
entries
|
483
|
+
end
|
489
484
|
|
490
485
|
end
|
491
486
|
|
@@ -503,10 +498,10 @@ module LDAP
|
|
503
498
|
# TODO: Need to dynamically assemble this case statement to add
|
504
499
|
# OpenLDAP's increment change type, etc.
|
505
500
|
change_type = case mod.mod_op & ~LDAP_MOD_BVALUES
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
501
|
+
when LDAP_MOD_ADD then 'add'
|
502
|
+
when LDAP_MOD_DELETE then 'delete'
|
503
|
+
when LDAP_MOD_REPLACE then 'replace'
|
504
|
+
end
|
510
505
|
|
511
506
|
ldif << "-\n" if plural
|
512
507
|
ldif << LDIF.to_ldif( change_type, mod.mod_type )
|
@@ -529,9 +524,9 @@ module LDAP
|
|
529
524
|
ldif = "dn: %s\n" % get_dn
|
530
525
|
|
531
526
|
get_attributes.each do |attr|
|
532
|
-
|
533
|
-
|
534
|
-
|
527
|
+
get_values( attr ).each do |val|
|
528
|
+
ldif << LDIF.to_ldif( attr, [ val ] )
|
529
|
+
end
|
535
530
|
end
|
536
531
|
|
537
532
|
LDIF::Entry.new( ldif )
|
@@ -540,7 +535,7 @@ module LDAP
|
|
540
535
|
alias_method :to_s, :to_ldif
|
541
536
|
end
|
542
537
|
|
543
|
-
|
538
|
+
|
544
539
|
class Mod
|
545
540
|
|
546
541
|
# Convert an LDAP::Mod with the DN given in +dn+ to LDIF.
|
@@ -552,11 +547,11 @@ module LDAP
|
|
552
547
|
# OpenLDAP's increment change type, etc.
|
553
548
|
case mod_op & ~LDAP_MOD_BVALUES
|
554
549
|
when LDAP_MOD_ADD
|
555
|
-
|
550
|
+
ldif << "changetype: add\n"
|
556
551
|
when LDAP_MOD_DELETE
|
557
|
-
|
552
|
+
ldif << "changetype: delete\n"
|
558
553
|
when LDAP_MOD_REPLACE
|
559
|
-
|
554
|
+
return LDIF.mods_to_ldif( dn, self )
|
560
555
|
end
|
561
556
|
|
562
557
|
ldif << LDIF.to_ldif( mod_type, mod_vals )
|