1<?php 2/** 3 * Copyright 2010-2017 Horde LLC (http://www.horde.org/) 4 * 5 * @package Ldap 6 * @subpackage UnitTests 7 * @author Jan Schneider <jan@horde.org> 8 * @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL-3.0 9 */ 10class Horde_Ldap_UtilTest extends Horde_Test_Case 11{ 12 /** 13 * Test escapeDNValue() 14 */ 15 public function testEscapeDNValue() 16 { 17 $dnval = ' ' . chr(22) . ' t,e+s"t,\\v<a>l;u#e=! '; 18 $expected = '\20\20\16 t\,e\+s\"t\,\\\\v\<a\>l\;u\#e\=!\20\20\20\20'; 19 20 // String call. 21 $this->assertEquals( 22 array($expected), 23 Horde_Ldap_Util::escapeDNValue($dnval)); 24 25 // Array call. 26 $this->assertEquals( 27 array($expected), 28 Horde_Ldap_Util::escapeDNValue(array($dnval))); 29 30 // Multiple arrays. 31 $this->assertEquals( 32 array($expected, $expected, $expected), 33 Horde_Ldap_Util::escapeDNValue(array($dnval, $dnval, $dnval))); 34 } 35 36 /** 37 * Test unescapeDNValue() 38 */ 39 public function testUnescapeDNValue() 40 { 41 $dnval = '\\20\\20\\16\\20t\\,e\\+s \\"t\\,\\\\v\\<a\\>l\\;u\\#e\\=!\\20\\20\\20\\20'; 42 $expected = ' ' . chr(22) . ' t,e+s "t,\\v<a>l;u#e=! '; 43 44 // String call. 45 $this->assertEquals( 46 array($expected), 47 Horde_Ldap_Util::unescapeDNValue($dnval)); 48 49 // Array call. 50 $this->assertEquals( 51 array($expected), 52 Horde_Ldap_Util::unescapeDNValue(array($dnval))); 53 54 // Multiple arrays. 55 $this->assertEquals( 56 array($expected, $expected, $expected), 57 Horde_Ldap_Util::unescapeDNValue(array($dnval, $dnval, $dnval))); 58 } 59 60 /** 61 * Test escaping of filter values. 62 */ 63 public function testEscapeFilterValue() 64 { 65 $expected = 't\28e,s\29t\2av\5cal\1eue'; 66 $filterval = 't(e,s)t*v\\al' . chr(30) . 'ue'; 67 68 // String call 69 $this->assertEquals( 70 array($expected), 71 Horde_Ldap_Util::escapeFilterValue($filterval)); 72 73 // Array call. 74 $this->assertEquals( 75 array($expected), 76 Horde_Ldap_Util::escapeFilterValue(array($filterval))); 77 78 // Multiple arrays. 79 $this->assertEquals( 80 array($expected, $expected, $expected), 81 Horde_Ldap_Util::escapeFilterValue(array($filterval, $filterval, $filterval))); 82 } 83 84 /** 85 * Test unescaping of filter values. 86 */ 87 public function testUnescapeFilterValue() 88 { 89 $expected = 't(e,s)t*v\\al' . chr(30) . 'ue'; 90 $filterval = 't\28e,s\29t\2av\5cal\1eue'; 91 92 // String call 93 $this->assertEquals( 94 array($expected), 95 Horde_Ldap_Util::unescapeFilterValue($filterval)); 96 97 // Array call. 98 $this->assertEquals( 99 array($expected), 100 Horde_Ldap_Util::unescapeFilterValue(array($filterval))); 101 102 // Multiple arrays. 103 $this->assertEquals( 104 array($expected, $expected, $expected), 105 Horde_Ldap_Util::unescapeFilterValue(array($filterval, $filterval, $filterval))); 106 } 107 108 /** 109 * Test asc2hex32() 110 */ 111 public function testAsc2hex32() 112 { 113 $expected = '\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'; 114 $str = ''; 115 for ($i = 0; $i < 127; $i++) { 116 $str .= chr($i); 117 } 118 $this->assertEquals($expected, Horde_Ldap_Util::asc2hex32($str)); 119 } 120 121 /** 122 * Test HEX unescaping 123 */ 124 public function testHex2asc() 125 { 126 $expected = ''; 127 for ($i = 0; $i < 127; $i++) { 128 $expected .= chr($i); 129 } 130 $str = '\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d\1e\1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'; 131 $this->assertEquals($expected, Horde_Ldap_Util::hex2asc($str)); 132 } 133 134 /** 135 * Tests splitRDNMultivalue() 136 * 137 * In addition to the above test of the basic split correction, we test 138 * here the functionality of multivalued RDNs. 139 */ 140 public function testSplitRDNMultivalue() 141 { 142 // One value. 143 $rdn = 'CN=J. Smith'; 144 $expected = array('CN=J. Smith'); 145 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 146 $this->assertEquals($expected, $split); 147 148 // Two values. 149 $rdn = 'OU=Sales+CN=J. Smith'; 150 $expected = array('OU=Sales', 'CN=J. Smith'); 151 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 152 $this->assertEquals($expected, $split); 153 154 // Several multivals. 155 $rdn = 'OU=Sales+CN=J. Smith+L=London+C=England'; 156 $expected = array('OU=Sales', 'CN=J. Smith', 'L=London', 'C=England'); 157 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 158 $this->assertEquals($expected, $split); 159 160 // Unescaped "+" in value. 161 $rdn = 'OU=Sa+les+CN=J. Smith'; 162 $expected = array('OU=Sa+les', 'CN=J. Smith'); 163 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 164 $this->assertEquals($expected, $split); 165 166 // Unescaped "+" in attr name. 167 $rdn = 'O+U=Sales+CN=J. Smith'; 168 $expected = array('O+U=Sales', 'CN=J. Smith'); 169 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 170 $this->assertEquals($expected, $split); 171 172 // Unescaped "+" in attr name + value. 173 $rdn = 'O+U=Sales+CN=J. Sm+ith'; 174 $expected = array('O+U=Sales', 'CN=J. Sm+ith'); 175 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 176 $this->assertEquals($expected, $split); 177 178 // Unescaped "+" in attribute name, but not first attribute. This 179 // documents a known bug. However, unfortunately we can't know wether 180 // the "C+" belongs to value "Sales" or attribute "C+N". To solve 181 // this, we must ask the schema which we do not right now. The problem 182 // is located in _correct_dn_splitting(). 183 $rdn = 'OU=Sales+C+N=J. Smith'; 184 // The "C+" is treaten as value of "OU". 185 $expected = array('OU=Sales+C', 'N=J. Smith'); 186 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 187 $this->assertEquals($expected, $split); 188 189 // Escaped "+" in attribute name and value. 190 $rdn = 'O\+U=Sales+CN=J. Sm\+ith'; 191 $expected = array('O\+U=Sales', 'CN=J. Sm\+ith'); 192 $split = Horde_Ldap_Util::splitRDNMultivalue($rdn); 193 $this->assertEquals($expected, $split); 194 } 195 196 /** 197 * Tests attribute splitting ('foo=bar' => array('foo', 'bar')) 198 */ 199 public function testSplitAttributeString() 200 { 201 $attr_str = 'foo=bar'; 202 203 // Properly. 204 $expected = array('foo', 'bar'); 205 $split = Horde_Ldap_Util::splitAttributeString($attr_str); 206 $this->assertEquals($expected, $split); 207 208 // Escaped "=". 209 $attr_str = "fo\=o=b\=ar"; 210 $expected = array('fo\=o', 'b\=ar'); 211 $split = Horde_Ldap_Util::splitAttributeString($attr_str); 212 $this->assertEquals($expected, $split); 213 214 // Escaped "=" and unescaped = later on. 215 $attr_str = "fo\=o=b=ar"; 216 $expected = array('fo\=o', 'b=ar'); 217 $split = Horde_Ldap_Util::splitAttributeString($attr_str); 218 $this->assertEquals($expected, $split); 219 } 220 221 /** 222 * Tests Ldap_explode_dn() 223 */ 224 public function testExplodeDN() 225 { 226 $dn = 'ou=Sales+CN=J. Smith,dc=example,dc=net'; 227 $expected_casefold_none = array( 228 array('CN=J. Smith', 'ou=Sales'), 229 'dc=example', 230 'dc=net' 231 ); 232 $expected_casefold_upper = array( 233 array('CN=J. Smith', 'OU=Sales'), 234 'DC=example', 235 'DC=net' 236 ); 237 $expected_casefold_lower = array( 238 array('cn=J. Smith', 'ou=Sales'), 239 'dc=example', 240 'dc=net' 241 ); 242 $expected_onlyvalues = array( 243 array('J. Smith', 'Sales'), 244 'example', 245 'net' 246 ); 247 $expected_reverse = array_reverse($expected_casefold_upper); 248 249 250 $dn_exploded_cnone = Horde_Ldap_Util::explodeDN($dn, array('casefold' => 'none')); 251 $this->assertEquals($expected_casefold_none, $dn_exploded_cnone, 'Option casefold none failed'); 252 253 $dn_exploded_cupper = Horde_Ldap_Util::explodeDN($dn, array('casefold' => 'upper')); 254 $this->assertEquals($expected_casefold_upper, $dn_exploded_cupper, 'Option casefold upper failed'); 255 256 $dn_exploded_clower = Horde_Ldap_Util::explodeDN($dn, array('casefold' => 'lower')); 257 $this->assertEquals($expected_casefold_lower, $dn_exploded_clower, 'Option casefold lower failed'); 258 259 $dn_exploded_onlyval = Horde_Ldap_Util::explodeDN($dn, array('onlyvalues' => true)); 260 $this->assertEquals($expected_onlyvalues, $dn_exploded_onlyval, 'Option onlyval failed'); 261 262 $dn_exploded_reverse = Horde_Ldap_Util::explodeDN($dn, array('reverse' => true)); 263 $this->assertEquals($expected_reverse, $dn_exploded_reverse, 'Option reverse failed'); 264 265 $this->assertEquals( 266 array('CN=J\\, Smith', 'DC=example', 'DC=net'), 267 Horde_Ldap_Util::explodeDN('cn=J\\, Smith,dc=example,dc=net')); 268 } 269 270 /** 271 * Tests if canonicalDN() works. 272 * 273 * Note: This tests depend on the default options of canonicalDN(). 274 */ 275 public function testCanonicalDN() 276 { 277 // Test empty dn (is valid according to RFC). 278 $this->assertEquals('', Horde_Ldap_Util::canonicalDN('')); 279 280 // Default options with common DN. 281 $testdn = 'cn=beni,DC=php,c=net'; 282 $expected = 'CN=beni,DC=php,C=net'; 283 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn)); 284 285 // Casefold tests with common DN. 286 $expected_up = 'CN=beni,DC=php,C=net'; 287 $expected_lo = 'cn=beni,dc=php,c=net'; 288 $expected_no = 'cn=beni,DC=php,c=net'; 289 $this->assertEquals($expected_up, Horde_Ldap_Util::canonicalDN($testdn, array('casefold' => 'upper'))); 290 $this->assertEquals($expected_lo, Horde_Ldap_Util::canonicalDN($testdn, array('casefold' => 'lower'))); 291 $this->assertEquals($expected_no, Horde_Ldap_Util::canonicalDN($testdn, array('casefold' => 'none'))); 292 293 // Reverse. 294 $expected_rev = 'C=net,DC=php,CN=beni'; 295 $this->assertEquals($expected_rev, Horde_Ldap_Util::canonicalDN($testdn, array('reverse' => true)), 'Option reverse failed'); 296 297 // DN as arrays. 298 $dn_index = array('cn=beni', 'dc=php', 'c=net'); 299 $dn_assoc = array('cn' => 'beni', 'dc' => 'php', 'c' => 'net'); 300 $expected = 'CN=beni,DC=php,C=net'; 301 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($dn_index)); 302 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($dn_assoc)); 303 304 // DN with multiple RDN value. 305 $testdn = 'ou=dev+cn=beni,DC=php,c=net'; 306 $testdn_index = array(array('ou=dev', 'cn=beni'), 'DC=php', 'c=net'); 307 $testdn_assoc = array(array('ou' => 'dev', 'cn' => 'beni'), 'DC' => 'php', 'c' => 'net'); 308 $expected = 'CN=beni+OU=dev,DC=php,C=net'; 309 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn)); 310 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn_assoc)); 311 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($expected)); 312 313 // Test DN with OID. 314 $testdn = 'OID.2.5.4.3=beni,dc=php,c=net'; 315 $expected = '2.5.4.3=beni,DC=php,C=net'; 316 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn)); 317 318 // Test with leading and ending spaces. 319 $testdn = 'cn= beni ,DC=php,c=net'; 320 $expected = 'CN=\20\20beni\20\20,DC=php,C=net'; 321 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn)); 322 323 // Test with escaped commas. Doesn't work at the moment because 324 // canonicalDN() escapes attribute values, which break if they are 325 // already escaped. 326 $testdn = 'cn=beni\\,hi\=ll,DC=php,c=net'; 327 $expected = 'CN=beni\\,hi\=ll,DC=php,C=net'; 328 // $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($testdn)); 329 330 // Test with to-be escaped characters in attribute value. 331 $specialchars = array( 332 ',' => '\,', 333 '+' => '\+', 334 '"' => '\"', 335 '\\' => '\\\\', 336 '<' => '\<', 337 '>' => '\>', 338 ';' => '\;', 339 '#' => '\#', 340 '=' => '\=', 341 chr(18) => '\12', 342 '/' => '\/' 343 ); 344 foreach ($specialchars as $char => $escape) { 345 $test_string = 'CN=be' . $char . 'ni,DC=ph' . $char . 'p,C=net'; 346 $test_index = array('CN=be' . $char . 'ni', 'DC=ph' . $char . 'p', 'C=net'); 347 $test_assoc = array('CN' => 'be' . $char . 'ni', 'DC' => 'ph' . $char . 'p', 'C' => 'net'); 348 $expected = 'CN=be' . $escape . 'ni,DC=ph' . $escape . 'p,C=net'; 349 350 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($test_string), 'String escaping test (' . $char . ') failed'); 351 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($test_index), 'Indexed array escaping test (' . $char . ') failed'); 352 $this->assertEquals($expected, Horde_Ldap_Util::canonicalDN($test_assoc), 'Associative array encoding test (' . $char . ') failed'); 353 } 354 } 355} 356