├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── queries.sql ├── sqldap.ini.example └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.ini 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ahash" 5 | version = "0.4.6" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "f6789e291be47ace86a60303502173d84af8327e3627ecf334356ee0f87a164c" 8 | 9 | [[package]] 10 | name = "aho-corasick" 11 | version = "0.7.15" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 14 | dependencies = [ 15 | "memchr", 16 | ] 17 | 18 | [[package]] 19 | name = "anyhow" 20 | version = "1.0.34" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" 23 | 24 | [[package]] 25 | name = "async-trait" 26 | version = "0.1.41" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0" 29 | dependencies = [ 30 | "proc-macro2", 31 | "quote", 32 | "syn", 33 | ] 34 | 35 | [[package]] 36 | name = "atty" 37 | version = "0.2.14" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 40 | dependencies = [ 41 | "hermit-abi", 42 | "libc", 43 | "winapi", 44 | ] 45 | 46 | [[package]] 47 | name = "autocfg" 48 | version = "1.0.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 51 | 52 | [[package]] 53 | name = "bitflags" 54 | version = "1.2.1" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 57 | 58 | [[package]] 59 | name = "byteorder" 60 | version = "1.3.4" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 63 | 64 | [[package]] 65 | name = "bytes" 66 | version = "1.0.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 69 | 70 | [[package]] 71 | name = "cc" 72 | version = "1.0.62" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" 75 | 76 | [[package]] 77 | name = "cfg-if" 78 | version = "0.1.10" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 81 | 82 | [[package]] 83 | name = "chrono" 84 | version = "0.4.19" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 87 | dependencies = [ 88 | "libc", 89 | "num-integer", 90 | "num-traits", 91 | "time", 92 | "winapi", 93 | ] 94 | 95 | [[package]] 96 | name = "colored" 97 | version = "1.9.3" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" 100 | dependencies = [ 101 | "atty", 102 | "lazy_static", 103 | "winapi", 104 | ] 105 | 106 | [[package]] 107 | name = "core-foundation" 108 | version = "0.9.1" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 111 | dependencies = [ 112 | "core-foundation-sys", 113 | "libc", 114 | ] 115 | 116 | [[package]] 117 | name = "core-foundation-sys" 118 | version = "0.8.2" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 121 | 122 | [[package]] 123 | name = "derive_more" 124 | version = "0.99.11" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" 127 | dependencies = [ 128 | "proc-macro2", 129 | "quote", 130 | "syn", 131 | ] 132 | 133 | [[package]] 134 | name = "dlv-list" 135 | version = "0.2.2" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "1b391911b9a786312a10cb9d2b3d0735adfd5a8113eb3648de26a75e91b0826c" 138 | dependencies = [ 139 | "rand", 140 | ] 141 | 142 | [[package]] 143 | name = "foreign-types" 144 | version = "0.3.2" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 147 | dependencies = [ 148 | "foreign-types-shared", 149 | ] 150 | 151 | [[package]] 152 | name = "foreign-types-shared" 153 | version = "0.1.1" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 156 | 157 | [[package]] 158 | name = "form_urlencoded" 159 | version = "1.0.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" 162 | dependencies = [ 163 | "matches", 164 | "percent-encoding", 165 | ] 166 | 167 | [[package]] 168 | name = "futures" 169 | version = "0.3.8" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" 172 | dependencies = [ 173 | "futures-channel", 174 | "futures-core", 175 | "futures-executor", 176 | "futures-io", 177 | "futures-sink", 178 | "futures-task", 179 | "futures-util", 180 | ] 181 | 182 | [[package]] 183 | name = "futures-channel" 184 | version = "0.3.8" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" 187 | dependencies = [ 188 | "futures-core", 189 | "futures-sink", 190 | ] 191 | 192 | [[package]] 193 | name = "futures-core" 194 | version = "0.3.8" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" 197 | 198 | [[package]] 199 | name = "futures-executor" 200 | version = "0.3.8" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" 203 | dependencies = [ 204 | "futures-core", 205 | "futures-task", 206 | "futures-util", 207 | ] 208 | 209 | [[package]] 210 | name = "futures-io" 211 | version = "0.3.8" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" 214 | 215 | [[package]] 216 | name = "futures-macro" 217 | version = "0.3.8" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" 220 | dependencies = [ 221 | "proc-macro-hack", 222 | "proc-macro2", 223 | "quote", 224 | "syn", 225 | ] 226 | 227 | [[package]] 228 | name = "futures-sink" 229 | version = "0.3.8" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" 232 | 233 | [[package]] 234 | name = "futures-task" 235 | version = "0.3.8" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" 238 | dependencies = [ 239 | "once_cell", 240 | ] 241 | 242 | [[package]] 243 | name = "futures-util" 244 | version = "0.3.8" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" 247 | dependencies = [ 248 | "futures-channel", 249 | "futures-core", 250 | "futures-io", 251 | "futures-macro", 252 | "futures-sink", 253 | "futures-task", 254 | "memchr", 255 | "pin-project", 256 | "pin-utils", 257 | "proc-macro-hack", 258 | "proc-macro-nested", 259 | "slab", 260 | ] 261 | 262 | [[package]] 263 | name = "getrandom" 264 | version = "0.1.15" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" 267 | dependencies = [ 268 | "cfg-if", 269 | "libc", 270 | "wasi 0.9.0+wasi-snapshot-preview1", 271 | ] 272 | 273 | [[package]] 274 | name = "hashbrown" 275 | version = "0.9.1" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 278 | dependencies = [ 279 | "ahash", 280 | ] 281 | 282 | [[package]] 283 | name = "hermit-abi" 284 | version = "0.1.17" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 287 | dependencies = [ 288 | "libc", 289 | ] 290 | 291 | [[package]] 292 | name = "home" 293 | version = "0.5.3" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" 296 | dependencies = [ 297 | "winapi", 298 | ] 299 | 300 | [[package]] 301 | name = "idna" 302 | version = "0.2.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 305 | dependencies = [ 306 | "matches", 307 | "unicode-bidi", 308 | "unicode-normalization", 309 | ] 310 | 311 | [[package]] 312 | name = "lazy_static" 313 | version = "1.4.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 316 | 317 | [[package]] 318 | name = "lber" 319 | version = "0.3.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "8a99b520993b21a6faab32643cf4726573dc18ca4cf2d48cbeb24d248c86c930" 322 | dependencies = [ 323 | "byteorder", 324 | "bytes", 325 | "nom", 326 | ] 327 | 328 | [[package]] 329 | name = "ldap3" 330 | version = "0.9.2" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "3a26304b072ca0d9c809841ca0746c661d62ac09a77cab4781a373aa4f0ad4b7" 333 | dependencies = [ 334 | "async-trait", 335 | "bytes", 336 | "futures", 337 | "futures-util", 338 | "lazy_static", 339 | "lber", 340 | "log", 341 | "maplit", 342 | "native-tls", 343 | "nom", 344 | "percent-encoding", 345 | "thiserror", 346 | "tokio", 347 | "tokio-native-tls", 348 | "tokio-stream", 349 | "tokio-util", 350 | "url", 351 | ] 352 | 353 | [[package]] 354 | name = "libc" 355 | version = "0.2.92" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" 358 | 359 | [[package]] 360 | name = "log" 361 | version = "0.4.11" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 364 | dependencies = [ 365 | "cfg-if", 366 | ] 367 | 368 | [[package]] 369 | name = "maplit" 370 | version = "1.0.2" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 373 | 374 | [[package]] 375 | name = "matches" 376 | version = "0.1.8" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 379 | 380 | [[package]] 381 | name = "memchr" 382 | version = "2.3.4" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 385 | 386 | [[package]] 387 | name = "mio" 388 | version = "0.7.11" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" 391 | dependencies = [ 392 | "libc", 393 | "log", 394 | "miow", 395 | "ntapi", 396 | "winapi", 397 | ] 398 | 399 | [[package]] 400 | name = "miow" 401 | version = "0.3.7" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 404 | dependencies = [ 405 | "winapi", 406 | ] 407 | 408 | [[package]] 409 | name = "native-tls" 410 | version = "0.2.6" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" 413 | dependencies = [ 414 | "lazy_static", 415 | "libc", 416 | "log", 417 | "openssl", 418 | "openssl-probe", 419 | "openssl-sys", 420 | "schannel", 421 | "security-framework", 422 | "security-framework-sys", 423 | "tempfile", 424 | ] 425 | 426 | [[package]] 427 | name = "nom" 428 | version = "2.2.1" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff" 431 | 432 | [[package]] 433 | name = "ntapi" 434 | version = "0.3.6" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 437 | dependencies = [ 438 | "winapi", 439 | ] 440 | 441 | [[package]] 442 | name = "num-integer" 443 | version = "0.1.44" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 446 | dependencies = [ 447 | "autocfg", 448 | "num-traits", 449 | ] 450 | 451 | [[package]] 452 | name = "num-traits" 453 | version = "0.2.14" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 456 | dependencies = [ 457 | "autocfg", 458 | ] 459 | 460 | [[package]] 461 | name = "once_cell" 462 | version = "1.5.2" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" 465 | 466 | [[package]] 467 | name = "openssl" 468 | version = "0.10.30" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" 471 | dependencies = [ 472 | "bitflags", 473 | "cfg-if", 474 | "foreign-types", 475 | "lazy_static", 476 | "libc", 477 | "openssl-sys", 478 | ] 479 | 480 | [[package]] 481 | name = "openssl-probe" 482 | version = "0.1.2" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 485 | 486 | [[package]] 487 | name = "openssl-sys" 488 | version = "0.9.58" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" 491 | dependencies = [ 492 | "autocfg", 493 | "cc", 494 | "libc", 495 | "pkg-config", 496 | "vcpkg", 497 | ] 498 | 499 | [[package]] 500 | name = "ordered-multimap" 501 | version = "0.3.1" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" 504 | dependencies = [ 505 | "dlv-list", 506 | "hashbrown", 507 | ] 508 | 509 | [[package]] 510 | name = "percent-encoding" 511 | version = "2.1.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 514 | 515 | [[package]] 516 | name = "pin-project" 517 | version = "1.0.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" 520 | dependencies = [ 521 | "pin-project-internal", 522 | ] 523 | 524 | [[package]] 525 | name = "pin-project-internal" 526 | version = "1.0.1" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" 529 | dependencies = [ 530 | "proc-macro2", 531 | "quote", 532 | "syn", 533 | ] 534 | 535 | [[package]] 536 | name = "pin-project-lite" 537 | version = "0.2.6" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" 540 | 541 | [[package]] 542 | name = "pin-utils" 543 | version = "0.1.0" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 546 | 547 | [[package]] 548 | name = "pkg-config" 549 | version = "0.3.19" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 552 | 553 | [[package]] 554 | name = "ppv-lite86" 555 | version = "0.2.10" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 558 | 559 | [[package]] 560 | name = "proc-macro-hack" 561 | version = "0.5.19" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" 564 | 565 | [[package]] 566 | name = "proc-macro-nested" 567 | version = "0.1.6" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 570 | 571 | [[package]] 572 | name = "proc-macro2" 573 | version = "1.0.24" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 576 | dependencies = [ 577 | "unicode-xid", 578 | ] 579 | 580 | [[package]] 581 | name = "quote" 582 | version = "1.0.7" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 585 | dependencies = [ 586 | "proc-macro2", 587 | ] 588 | 589 | [[package]] 590 | name = "rand" 591 | version = "0.7.3" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 594 | dependencies = [ 595 | "getrandom", 596 | "libc", 597 | "rand_chacha", 598 | "rand_core", 599 | "rand_hc", 600 | ] 601 | 602 | [[package]] 603 | name = "rand_chacha" 604 | version = "0.2.2" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 607 | dependencies = [ 608 | "ppv-lite86", 609 | "rand_core", 610 | ] 611 | 612 | [[package]] 613 | name = "rand_core" 614 | version = "0.5.1" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 617 | dependencies = [ 618 | "getrandom", 619 | ] 620 | 621 | [[package]] 622 | name = "rand_hc" 623 | version = "0.2.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 626 | dependencies = [ 627 | "rand_core", 628 | ] 629 | 630 | [[package]] 631 | name = "redox_syscall" 632 | version = "0.1.57" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 635 | 636 | [[package]] 637 | name = "regex" 638 | version = "1.4.2" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 641 | dependencies = [ 642 | "aho-corasick", 643 | "memchr", 644 | "regex-syntax", 645 | "thread_local", 646 | ] 647 | 648 | [[package]] 649 | name = "regex-syntax" 650 | version = "0.6.21" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 653 | 654 | [[package]] 655 | name = "remove_dir_all" 656 | version = "0.5.3" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 659 | dependencies = [ 660 | "winapi", 661 | ] 662 | 663 | [[package]] 664 | name = "rust-ini" 665 | version = "0.16.0" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "dcdbf249a931ceb503aa80e08ca016675dbc7884eda8e2d88e85533d4dbade62" 668 | dependencies = [ 669 | "cfg-if", 670 | "ordered-multimap", 671 | ] 672 | 673 | [[package]] 674 | name = "schannel" 675 | version = "0.1.19" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 678 | dependencies = [ 679 | "lazy_static", 680 | "winapi", 681 | ] 682 | 683 | [[package]] 684 | name = "security-framework" 685 | version = "2.0.0" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" 688 | dependencies = [ 689 | "bitflags", 690 | "core-foundation", 691 | "core-foundation-sys", 692 | "libc", 693 | "security-framework-sys", 694 | ] 695 | 696 | [[package]] 697 | name = "security-framework-sys" 698 | version = "2.0.0" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" 701 | dependencies = [ 702 | "core-foundation-sys", 703 | "libc", 704 | ] 705 | 706 | [[package]] 707 | name = "simple_logger" 708 | version = "1.11.0" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "cd57f17c093ead1d4a1499dc9acaafdd71240908d64775465543b8d9a9f1d198" 711 | dependencies = [ 712 | "atty", 713 | "chrono", 714 | "colored", 715 | "log", 716 | "winapi", 717 | ] 718 | 719 | [[package]] 720 | name = "slab" 721 | version = "0.4.2" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 724 | 725 | [[package]] 726 | name = "sqldap" 727 | version = "0.1.0" 728 | dependencies = [ 729 | "anyhow", 730 | "derive_more", 731 | "home", 732 | "ldap3", 733 | "rust-ini", 734 | "simple_logger", 735 | "sqlparser", 736 | "term-table", 737 | "term_size", 738 | ] 739 | 740 | [[package]] 741 | name = "sqlparser" 742 | version = "0.6.1" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "53fa7478852b3ea28f0d21a42b2d7dade24ba4aa72e22bf66982e4b587a7f608" 745 | dependencies = [ 746 | "log", 747 | ] 748 | 749 | [[package]] 750 | name = "syn" 751 | version = "1.0.48" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" 754 | dependencies = [ 755 | "proc-macro2", 756 | "quote", 757 | "unicode-xid", 758 | ] 759 | 760 | [[package]] 761 | name = "tempfile" 762 | version = "3.1.0" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 765 | dependencies = [ 766 | "cfg-if", 767 | "libc", 768 | "rand", 769 | "redox_syscall", 770 | "remove_dir_all", 771 | "winapi", 772 | ] 773 | 774 | [[package]] 775 | name = "term-table" 776 | version = "1.3.0" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "dea05ef801340799f4c96296e9e8cf79cdfa66ef66fe991522591fde77405105" 779 | dependencies = [ 780 | "lazy_static", 781 | "regex", 782 | "unicode-width", 783 | ] 784 | 785 | [[package]] 786 | name = "term_size" 787 | version = "0.3.2" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" 790 | dependencies = [ 791 | "libc", 792 | "winapi", 793 | ] 794 | 795 | [[package]] 796 | name = "thiserror" 797 | version = "1.0.22" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" 800 | dependencies = [ 801 | "thiserror-impl", 802 | ] 803 | 804 | [[package]] 805 | name = "thiserror-impl" 806 | version = "1.0.22" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" 809 | dependencies = [ 810 | "proc-macro2", 811 | "quote", 812 | "syn", 813 | ] 814 | 815 | [[package]] 816 | name = "thread_local" 817 | version = "1.0.1" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 820 | dependencies = [ 821 | "lazy_static", 822 | ] 823 | 824 | [[package]] 825 | name = "time" 826 | version = "0.1.44" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 829 | dependencies = [ 830 | "libc", 831 | "wasi 0.10.0+wasi-snapshot-preview1", 832 | "winapi", 833 | ] 834 | 835 | [[package]] 836 | name = "tinyvec" 837 | version = "1.0.1" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575" 840 | dependencies = [ 841 | "tinyvec_macros", 842 | ] 843 | 844 | [[package]] 845 | name = "tinyvec_macros" 846 | version = "0.1.0" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 849 | 850 | [[package]] 851 | name = "tokio" 852 | version = "1.4.0" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" 855 | dependencies = [ 856 | "autocfg", 857 | "bytes", 858 | "libc", 859 | "memchr", 860 | "mio", 861 | "pin-project-lite", 862 | "tokio-macros", 863 | ] 864 | 865 | [[package]] 866 | name = "tokio-macros" 867 | version = "1.1.0" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" 870 | dependencies = [ 871 | "proc-macro2", 872 | "quote", 873 | "syn", 874 | ] 875 | 876 | [[package]] 877 | name = "tokio-native-tls" 878 | version = "0.3.0" 879 | source = "registry+https://github.com/rust-lang/crates.io-index" 880 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 881 | dependencies = [ 882 | "native-tls", 883 | "tokio", 884 | ] 885 | 886 | [[package]] 887 | name = "tokio-stream" 888 | version = "0.1.5" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "e177a5d8c3bf36de9ebe6d58537d8879e964332f93fb3339e43f618c81361af0" 891 | dependencies = [ 892 | "futures-core", 893 | "pin-project-lite", 894 | "tokio", 895 | ] 896 | 897 | [[package]] 898 | name = "tokio-util" 899 | version = "0.6.5" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f" 902 | dependencies = [ 903 | "bytes", 904 | "futures-core", 905 | "futures-sink", 906 | "log", 907 | "pin-project-lite", 908 | "tokio", 909 | ] 910 | 911 | [[package]] 912 | name = "unicode-bidi" 913 | version = "0.3.4" 914 | source = "registry+https://github.com/rust-lang/crates.io-index" 915 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 916 | dependencies = [ 917 | "matches", 918 | ] 919 | 920 | [[package]] 921 | name = "unicode-normalization" 922 | version = "0.1.14" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "b7f98e67a4d84f730d343392f9bfff7d21e3fca562b9cb7a43b768350beeddc6" 925 | dependencies = [ 926 | "tinyvec", 927 | ] 928 | 929 | [[package]] 930 | name = "unicode-width" 931 | version = "0.1.8" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 934 | 935 | [[package]] 936 | name = "unicode-xid" 937 | version = "0.2.1" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 940 | 941 | [[package]] 942 | name = "url" 943 | version = "2.2.0" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" 946 | dependencies = [ 947 | "form_urlencoded", 948 | "idna", 949 | "matches", 950 | "percent-encoding", 951 | ] 952 | 953 | [[package]] 954 | name = "vcpkg" 955 | version = "0.2.10" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" 958 | 959 | [[package]] 960 | name = "wasi" 961 | version = "0.9.0+wasi-snapshot-preview1" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 964 | 965 | [[package]] 966 | name = "wasi" 967 | version = "0.10.0+wasi-snapshot-preview1" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 970 | 971 | [[package]] 972 | name = "winapi" 973 | version = "0.3.9" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 976 | dependencies = [ 977 | "winapi-i686-pc-windows-gnu", 978 | "winapi-x86_64-pc-windows-gnu", 979 | ] 980 | 981 | [[package]] 982 | name = "winapi-i686-pc-windows-gnu" 983 | version = "0.4.0" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 986 | 987 | [[package]] 988 | name = "winapi-x86_64-pc-windows-gnu" 989 | version = "0.4.0" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 992 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sqldap" 3 | version = "0.1.0" 4 | authors = ["Frederik Delaere "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | sqlparser = "0.6.1" 11 | simple_logger = "1.11.0" 12 | rust-ini = "0.16.0" 13 | anyhow = "1.0.34" 14 | ldap3 = "0.9.2" 15 | term-table = "1.3.0" 16 | home = "0.5.3" 17 | term_size = "0.3.2" 18 | derive_more = "0.99.11" 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLDAP 2 | 3 | Ever wanted to query AD or LDAP with SQL like queries ? 4 | 5 | I'm going to answer this question myself: yes ! 6 | Why ? Because I never could remember all the ldapsearch 7 | arguments and filters, etc. 8 | 9 | But after building this tool I am now a master of ldapsearch :) 10 | 11 | ## Supported queries: 12 | 13 | * simple select queries: `SELECT * FROM "$tablename" WHERE var=val;` 14 | * `show databases;` `show tables;` 15 | 16 | ## Work in Progress: 17 | 18 | * update queries: Don't use this in a production environment !!! 19 | 20 | 21 | ## Example queries: 22 | 23 | `@group` should be defined in `sqldap.ini` (see `sqldap.ini.example`) 24 | 25 | Select from default (first) configured server: 26 | ```bash 27 | [user@awesome ~]$ sqldap 'SELECT memberuid FROM @group WHERE cn=groupname' 28 | ``` 29 | 30 | Select from a different configured Active Directory server named `examplead` 31 | ```bash 32 | [user@awesome ~]$ sqldap 'SELECT gid FROM @group ' examplead 33 | ``` 34 | 35 | #### Don't use `>` and `<` 36 | `>` and `<` are not supported, work your way around this problem by using: `>=` and `<=` 37 | 38 | 39 | #### This is a valid query: 40 | ```bash 41 | [user@awesome ~]$ sqldap 'SELECT uid,cn,passwordretrycount,ou FROM @people WHERE passwordretrycount>=3' 42 | ``` 43 | 44 | #### This is an invalid query: 45 | ```bash 46 | [user@awesome ~]$ sqldap 'SELECT uid,cn,passwordretrycount,ou FROM @people WHERE passwordretrycount>2'` 47 | ``` 48 | 49 | #### You can also run queries that are defined in the `sqldap.ini` config file: 50 | ```bash 51 | [user@awesome ~]$ sqldap @passwordretrycount 52 | ``` 53 | 54 | #### When you pass a filename as an argument, then sqldap parses the file and executes the queries that are defined this file 55 | 56 | ```bash 57 | [user@awesome ~]$ sqldap queries.sql 58 | 59 | Using config file /home/user/sqldap/sqldap.ini 60 | Using server: exampleldap (ldap://ldap.example.com:389) 61 | 62 | +-------+-------------+--------------------+----------+ 63 | | uid | cn | passwordretrycount | ou | 64 | +-------+-------------+--------------------+----------+ 65 | | user1 | Username1 | 3 | group1 | 66 | +-------+-------------+--------------------+----------+ 67 | | user2 | Username2 | 3 | group2 | 68 | +-------+-------------+--------------------+----------+ 69 | | user3 | Username3 | 3 | group3 | 70 | +-------+-------------+--------------------+----------+ 71 | 72 | [user@awesome ~]$ 73 | 74 | ``` 75 | 76 | ### Compiling from source 77 | First you'll have to install Rust on your system: 78 | ```bash 79 | [user@awesome ~]$ curl https://sh.rustup.rs -sSf | sh 80 | ``` 81 | Now run the following command in the root of the project: 82 | 83 | ```bash 84 | [user@awesome ~]$ cargo build --release 85 | ``` 86 | 87 | The ```sqldap``` binary can be found in the ```target/release/``` folder. 88 | -------------------------------------------------------------------------------- /queries.sql: -------------------------------------------------------------------------------- 1 | SELECT uid,passwordRetryCount,cn,ou FROM @people WHERE passwordRetryCount>=3; 2 | SELECT cn FROM @group WHERE memberuid=username; 3 | @testquery; 4 | -------------------------------------------------------------------------------- /sqldap.ini.example: -------------------------------------------------------------------------------- 1 | [exampleldap] 2 | connection=ldap://ldap.domain.com:389 3 | binddn=uid=administrator,ou=Admins,ou=example,dc=domain,dc=com 4 | bindpassword=YOURPASSWORD 5 | [exampleldap.tables] 6 | people=ou=people,o=department,ou=example,dc=domain,dc=com 7 | group=ou=Group,o=department,ou=example,dc=domain,dc=com 8 | [exampleldap.queries] 9 | passwordretrycount=SELECT uid,cn,passwordretrycount,ou FROM @people WHERE passwordretrycount>=3 10 | testquery=SELECT cn FROM @group WHERE memberuid=username 11 | 12 | [examplead] 13 | connection=ldaps://ad.domain.com:636 14 | binddn="CN=Administrator,OU=group,OU=Users,OU=department,DC=example,DC=com" 15 | bindpassword='secretpassword' 16 | [examplead.tables] 17 | people=OU=Users,OU=department,DC=example,DC=com 18 | group=OU=Groups,OU=department,DC=example,DC=com 19 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | 13 | #![warn(clippy::all)] 14 | 15 | //use std::fs; 16 | 17 | use simple_logger::SimpleLogger; 18 | use sqlparser::ast::Expr; 19 | use sqlparser::ast::Ident; 20 | use sqlparser::ast::SelectItem; 21 | use sqlparser::ast::SetExpr; 22 | use sqlparser::ast::Statement; 23 | use sqlparser::ast::TableFactor; 24 | use sqlparser::dialect::*; 25 | use sqlparser::parser::Parser; 26 | //use sqlparser::ast::Expr::BinaryOp; 27 | 28 | //use derive_more::Into; 29 | 30 | use std::collections::HashMap; 31 | use std::collections::HashSet; 32 | 33 | use term_table::{ 34 | row::Row, 35 | table_cell::{Alignment, TableCell}, 36 | }; 37 | use term_table::{Table, TableStyle}; 38 | 39 | use ini::Ini; 40 | 41 | use anyhow::{bail, Context, Result}; 42 | 43 | use ldap3::Mod::Replace; 44 | use ldap3::{LdapConn, Mod, Scope, SearchEntry, LdapConnSettings}; 45 | 46 | const USAGE: &str = r#" 47 | Usage: 48 | $ sqldap filename.sql [server] 49 | or 50 | $ sqldap "SELECT uid FROM dc=example,dc=com WHERE gid=100" [server] 51 | 52 | When server is not provided then the first found server in sqldap.ini 53 | will be used as server. 54 | "#; 55 | 56 | const CONFIG_FILE_EXAMPLE: &str = r#" 57 | To create a config file, you can use the folling template 58 | 59 | [dirserver1] 60 | connection=ldap://ldap.example.com:389 61 | binddn=uid=admin,ou=Admins,dc=example,dc=com 62 | bindpassword=SECRETPASSWORD 63 | [dirserver1.tables] 64 | people=ou=people,dc=example,dc=com 65 | group=ou=Group,dc=example,dc=com 66 | 67 | [dirserver2] 68 | connection=ldap://ad.example.com:389 69 | "#; 70 | 71 | fn parse_selection(selection: Expr) -> Result { 72 | //println!("{:#?}", selection); 73 | let mut result_string = String::from(""); 74 | 75 | match selection { 76 | Expr::BinaryOp { left, op, right } => { 77 | if op.to_string() == "OR" { 78 | result_string += "|"; 79 | } else if op.to_string() == "AND" { 80 | result_string += "&"; 81 | } 82 | match *left { 83 | Expr::Identifier(ident) => result_string = result_string + "(" + &ident.to_string(), 84 | Expr::Value(value) => result_string = result_string + "(" + &value.to_string(), 85 | Expr::Wildcard => { 86 | result_string += "(*"; 87 | } 88 | _ => result_string += &parse_selection(*left).context("Cannot parse selection")?, 89 | } 90 | if op.to_string() == "=" { 91 | result_string += &op.to_string(); 92 | } 93 | if op.to_string() == ">=" { 94 | result_string += &op.to_string(); 95 | } 96 | if op.to_string() == "<=" { 97 | result_string += &op.to_string(); 98 | } 99 | match *right { 100 | Expr::Identifier(ident) => { 101 | let val = ident 102 | .to_string() 103 | .trim_end_matches('\"') 104 | .trim_start_matches('\"') 105 | .to_string(); 106 | let val = val.replace("%", "*"); 107 | result_string = result_string + &val + ")"; 108 | } 109 | Expr::Value(value) => { 110 | //let val = value.to_string().trim_end_matches("\"").trim_start_matches("\"").to_string(); 111 | //let val = val.replace("%","*"); 112 | //result_string = result_string + &val.to_string() + ")"; 113 | result_string = result_string + &value.to_string() + ")"; 114 | } 115 | Expr::Wildcard => { 116 | result_string += "*)"; 117 | } 118 | _ => result_string += &parse_selection(*right).context("Cannot parse selection")?, 119 | } 120 | //println!("BinaryOp right: {}", right); 121 | } 122 | Expr::Nested(expression) => { 123 | //println!("Detected Nested"); 124 | result_string = "(".to_owned() + &result_string; 125 | result_string += &parse_selection(*expression).context("Cannot parse selection")?; 126 | result_string += ")"; 127 | } 128 | _ => bail!("Non supported variant"), 129 | } 130 | 131 | Ok(result_string) 132 | } 133 | 134 | // TODO: UPDATE 135 | // see: https://docs.rs/ldap3/0.8.1/ldap3/struct.Ldap.html#method.modifydn 136 | // or: https://docs.rs/ldap3/0.8.1/ldap3/struct.Ldap.html#method.modify 137 | #[derive(Clone, PartialEq)] 138 | enum QueryType { 139 | SELECT = 0, 140 | SHOW = 1, 141 | UPDATE = 2, 142 | } 143 | 144 | struct SqldapQuery { 145 | qtype: QueryType, 146 | identifiers: Vec, 147 | wildcard: bool, 148 | table: String, 149 | filter: String, 150 | var: String, 151 | } 152 | 153 | fn parse_query( 154 | dialect: Box, 155 | sql: &str, 156 | tables: HashMap, 157 | ldap: &mut LdapConn, 158 | ) -> Result { 159 | let parse_result = Parser::parse_sql(&*dialect, &sql); 160 | let parse_result = parse_result.context("Cannot parse sql")?; 161 | 162 | let mut query: SqldapQuery = SqldapQuery { 163 | identifiers: Vec::new(), 164 | wildcard: false, 165 | table: String::from(""), 166 | filter: String::from(""), 167 | qtype: QueryType::SELECT, 168 | var: String::from(""), 169 | }; 170 | 171 | for stmt in parse_result { 172 | //println!("{}", stmt); 173 | match stmt { 174 | Statement::Query(s) => { 175 | query.qtype = QueryType::SELECT; 176 | match s.body { 177 | SetExpr::Select(s) => { 178 | for expr in s.projection { 179 | match expr { 180 | SelectItem::UnnamedExpr(expr) => { 181 | match expr { 182 | Expr::Identifier(ident) => { 183 | query.identifiers.push(ident.to_string()) 184 | } //println!("--> {}", ident), 185 | _ => bail!("Unsupported SelectItem::UnnamedExpr(expr)"), 186 | } 187 | } 188 | SelectItem::Wildcard => query.wildcard = true, 189 | _ => bail!("Unsupported SelectItem"), 190 | } 191 | } 192 | for table in s.from { 193 | match table.relation { 194 | TableFactor::Table { 195 | name, 196 | alias: _, 197 | args: _, 198 | with_hints: _, 199 | } => { 200 | query.table = name.to_string(); 201 | query.table = query 202 | .table 203 | .trim_end_matches('\"') 204 | .trim_start_matches('\"') 205 | .to_string(); 206 | for (table, value) in &tables { 207 | query.table = 208 | query.table.replace(&format!("@{}", table), value); 209 | } 210 | } 211 | _ => bail!("Unsupported TableFactor"), 212 | } 213 | } 214 | //println!("->{:#?}", s.selection); 215 | let binary_op = if s.selection.is_some() { 216 | let mut result = parse_selection(s.selection.unwrap()) 217 | .context("Cannot parse selection")?; 218 | if !result.starts_with('(') { 219 | result = format!("({})", result); 220 | } 221 | result 222 | } else { 223 | String::from("") 224 | }; 225 | query.filter = binary_op; 226 | } 227 | _ => bail!("Unsupported Statement::Query(s)"), 228 | } 229 | } 230 | //Statement::Update { table_name: table_name, assignments: assignments, selection: selection } => { 231 | Statement::Update { 232 | table_name, 233 | assignments, 234 | selection, 235 | } => { 236 | query.qtype = QueryType::UPDATE; 237 | //println!("{:#?}", table_name); 238 | //println!("{:#?}", assignments); 239 | //println!("{:#?}", selection); 240 | //for table in table_name { 241 | match table_name { 242 | sqlparser::ast::ObjectName(ident) => { 243 | for i in ident { 244 | match i { 245 | Ident { 246 | value: val, 247 | quote_style: _, 248 | } => { 249 | println!("table_name: {}", &val); 250 | //query.var = val; 251 | query.table = val; 252 | } 253 | } 254 | } 255 | } 256 | } 257 | let mut mod_vec = Vec::new(); 258 | for assignment in assignments { 259 | //println!("{:#?}", parse_selection(assignment.value)); 260 | //println!("{:#?} {:#?}", assignment.id, assignment.value); 261 | //let mut var = String::from(""); 262 | let mut val = HashSet::new(); 263 | let var = match assignment.id { 264 | Ident { 265 | value: val, 266 | quote_style: _, 267 | } => { 268 | query.identifiers.push(val.to_string()); 269 | //println!("SET: {}", val); 270 | format!("{}", val) 271 | //query.var = val; 272 | } 273 | }; 274 | let newval = match assignment.value { 275 | Expr::Value(value) => { 276 | value.to_string() 277 | //let tmp = value.to_string().clone(); 278 | //println!("{}", &value); 279 | } 280 | Expr::Identifier(ident) => { 281 | ident.to_string() 282 | //let tmp = ident.to_string().clone(); 283 | //val.insert(tmp.as_str()); 284 | //println!("{}", &ident); 285 | } 286 | _ => bail!("Only Value and Identifier are supported."), 287 | }; 288 | val.insert(format!("{}", newval)); 289 | mod_vec.push(Replace(format!("{}", &var), val)); 290 | } 291 | query.identifiers.push("entrydn".to_string()); 292 | //} 293 | //println!("->{:#?}", s.selection); 294 | // THIS SHOULD BE EMPTY 295 | // as ldap modify is not compatible with an sql where clause 296 | // the table_name should be the full dn 297 | // so where uid=username cannot be used, the table_name should be 298 | // uid=username,ou=group,ou=people,o=department,dc=example,dc=com 299 | 300 | // OR 301 | 302 | // the only way around this is first do a query for all entries 303 | // where uid=username or uidnumber>=10 or whatever (with a possible wildcard) 304 | // get the full dns for these entries, and then run ldap modify on all of 305 | // them through a loop 306 | 307 | let binary_op = if selection.is_some() { 308 | let mut result = 309 | parse_selection(selection.unwrap()).context("Cannot parse selection")?; 310 | if !result.starts_with('(') { 311 | result = format!("({})", result); 312 | } 313 | result 314 | } else { 315 | String::from("") 316 | }; 317 | //println!("->{:#?}", &binary_op); 318 | query.filter = binary_op; 319 | 320 | //println!("{:#?}", query.identifiers); 321 | let (rs, _res) = ldap 322 | .search( 323 | &query.table, 324 | Scope::Subtree, 325 | &query.filter, 326 | query.identifiers.clone(), 327 | )? 328 | .success() 329 | .context("Ldap search failed")?; 330 | 331 | //let mut result = Vec::new(); 332 | //let mut key = String::from(""); 333 | 334 | //let mut mod_vec_update = Vec::new(); 335 | for entry in rs { 336 | let mut got_error = false; 337 | let se = SearchEntry::construct(entry); 338 | 339 | let mut entrydn = String::from(""); 340 | for a in se.attrs { 341 | let (key_attr, val_attr) = a; 342 | 343 | if val_attr.len() > 1 { 344 | println!("ERROR: attribute {} is an array, which cannot be modified. Please use delete or add.", &key_attr); 345 | // REMARK: maybe we should get for example all memberOf values, store them in a Vec, update 346 | // the value we want to update and send this in the Replace mod 347 | /* 348 | for i in mod_vec.iter() { 349 | match i { 350 | Mod::Replace(key, val) => { 351 | if key == &key_attr { 352 | println!("key: {}", key); 353 | mod_vec_update.push(Replace(format!("{}", &key), val)); 354 | } else { 355 | mod_vec_update.push(Replace(format!("{}", &key), val)); 356 | } 357 | } 358 | _ => println!("hoi"), 359 | } 360 | } 361 | */ 362 | got_error = true; 363 | } else { 364 | if key_attr == "entrydn" { 365 | entrydn = val_attr[0].clone(); 366 | //println!("{} -> {:?} ", &val_attr[0], mod_vec); 367 | /* 368 | 369 | let res = ldap 370 | .with_controls(RelaxRules.critical()) 371 | .modify("uid=inejge,ou=People,dc=example,dc=org", mod_vec)? 372 | .success()?; 373 | 374 | */ 375 | } 376 | } 377 | } 378 | if !got_error { 379 | println!("updating: {} -> {:?} ", &entrydn, mod_vec); 380 | println!("---------------"); 381 | } else { 382 | println!("{} will not be updated ", &entrydn); 383 | } 384 | } 385 | 386 | //println!("{:#?}", result); 387 | } 388 | Statement::ShowVariable { variable: var } => { 389 | query.qtype = QueryType::SHOW; 390 | //println!("{:?}", var); 391 | match var { 392 | Ident { 393 | value: val, 394 | quote_style: _, 395 | } => { 396 | //println!("{}", val); 397 | query.var = val; 398 | } 399 | } 400 | } 401 | _ => bail!("Only SELECT AND UPDATE (work in progress) queries are supported."), 402 | } 403 | } 404 | 405 | //if query.filter.is_empty() && query.qtype.clone() == QueryType::SELECT { 406 | if query.filter.is_empty() && query.qtype == QueryType::SELECT { 407 | query.filter = "(objectClass=*)".to_string(); 408 | println!("'WHERE attr=val' was not supplied, added (objectClass=*) as search filter.\n"); 409 | } 410 | 411 | Ok(query) 412 | } 413 | 414 | fn get_config_cwd(filename: &str) -> Option { 415 | let cwd = match std::env::current_dir() { 416 | Ok(path) => Some(path.display().to_string()), 417 | Err(_e) => None, 418 | }; 419 | 420 | if cwd.is_some() { 421 | return Some(format!("{}/{}", cwd.unwrap(), filename)); 422 | } 423 | 424 | None 425 | } 426 | 427 | fn get_config_home(filename: &str) -> Option { 428 | let home = match home::home_dir() { 429 | Some(path) => Some(path.display().to_string()), 430 | None => None, 431 | }; 432 | 433 | if home.is_some() { 434 | return Some(format!("{}/.{}", home.unwrap(), filename)); 435 | } 436 | 437 | None 438 | } 439 | 440 | fn get_config_etc(filename: &str) -> Option { 441 | if std::path::Path::new("/etc/").exists() { 442 | return Some(format!("/etc/{}", filename)); 443 | } 444 | 445 | None 446 | } 447 | 448 | fn get_config_usage(filename: &str) -> Result { 449 | let mut config_usage = vec![ 450 | "".to_string(), 451 | "".to_string(), 452 | "sqldap searches for a config file in the following locations,".to_string(), 453 | "in the folling order:".to_string(), 454 | "".to_string(), 455 | ]; 456 | 457 | let cwd = get_config_cwd(filename); 458 | if let Some(cwd_config) = cwd { 459 | config_usage.push(format!(" - {}", cwd_config)); 460 | } 461 | 462 | let home = get_config_home(filename); 463 | if let Some(home_config) = home { 464 | config_usage.push(format!(" - {}", home_config)); 465 | } 466 | 467 | let etc = get_config_etc(filename); 468 | if let Some(etc_config) = etc { 469 | config_usage.push(format!(" - {}", etc_config)); 470 | } 471 | 472 | /* 473 | let config_file_example = r#" 474 | To create a config file, you can use the folling template 475 | 476 | [dirserver1] 477 | connection=ldap://ldap.example.com:389 478 | binddn=uid=admin,ou=Admins,dc=example,dc=com 479 | bindpassword=SECRETPASSWORD 480 | [dirserver1.tables] 481 | people=ou=people,dc=example,dc=com 482 | group=ou=Group,dc=example,dc=com 483 | 484 | [dirserver2] 485 | connection=ldap://ad.example.com:389 486 | "#; 487 | */ 488 | 489 | config_usage.push(CONFIG_FILE_EXAMPLE.to_string()); 490 | 491 | Ok(config_usage.join("\n")) 492 | } 493 | 494 | fn load_config(filename: &str) -> Result { 495 | let cwd = get_config_cwd(filename); 496 | if let Some(cwd_config) = cwd { 497 | let conf = Ini::load_from_file(&cwd_config); 498 | if let Ok(config) = conf { 499 | println!("Using config file {}", cwd_config); 500 | return Ok(config); 501 | } 502 | } 503 | 504 | let home = get_config_home(filename); 505 | if let Some(home_config) = home { 506 | let conf = Ini::load_from_file(&home_config); 507 | if let Ok(config) = conf { 508 | println!("Using config file {}", home_config); 509 | return Ok(config); 510 | } 511 | } 512 | 513 | let etc = get_config_home(filename); 514 | if let Some(etc_config) = etc { 515 | let conf = Ini::load_from_file(&etc_config); 516 | if let Ok(config) = conf { 517 | println!("Using config file {}", etc_config); 518 | return Ok(config); 519 | } 520 | } 521 | 522 | bail!("Couldn't find the required config file.") 523 | } 524 | 525 | fn fits_on_screen(table_data: &str) -> bool { 526 | if let Some(table_width) = table_data.find('\n') { 527 | if let Some((w, _h)) = term_size::dimensions() { 528 | if table_width > w { 529 | return false; 530 | } 531 | } 532 | } 533 | true 534 | } 535 | 536 | fn main() -> Result<()> { 537 | SimpleLogger::new() 538 | .with_level(ldap3::log::LevelFilter::Error) 539 | .init() 540 | .unwrap(); 541 | 542 | let filename = "sqldap.ini"; 543 | let conf = 544 | load_config(filename).context(get_config_usage(filename).context("Cannot get usage")?)?; 545 | 546 | // now parse command line args 547 | // first arg should be the name of the section in the config file 548 | let tmpsql = match std::env::args().nth(1) { 549 | //.context(USAGE)?; 550 | Some(sql) => sql, 551 | None => { 552 | println!("{}", USAGE); 553 | std::process::exit(0); 554 | } 555 | }; 556 | let mut servername = match std::env::args().nth(2) { 557 | Some(n) => n, 558 | None => String::from(""), 559 | }; //.context(USAGE)?; 560 | 561 | // if we have received an sql file instead of a direct query 562 | // read the file 563 | let sql = if std::path::Path::new(&tmpsql).exists() { 564 | std::fs::read_to_string(&tmpsql).context("Cannot read sql file")? 565 | } else { 566 | tmpsql 567 | }; 568 | 569 | // if sql starts with @ 570 | // then the query is configured in 571 | // sqldap.ini 572 | // eg: 573 | /* 574 | [testldap] 575 | connection=ldap://ldap.domain.com:389 576 | binddn=uid=administrator,ou=Admins,ou=domain,dc=com 577 | bindpassword=DIFFIculT 578 | [testldap.tables] 579 | people=ou=people,o=department,dc=domain,dc=be 580 | group=ou=Group,o=department,dc=domain,dc=be 581 | [testldap.queries] 582 | passwordretrycount=SELECT uid FROM @people WHERE passwordretrycount>=3 583 | */ 584 | 585 | let sql = sql.replace(";\n", ";"); 586 | let mut sql = sql.split(';').collect::>(); 587 | 588 | // search for queries starting with '@' 589 | let mut found_at = false; 590 | for tmp in sql.iter() { 591 | if tmp.starts_with('@') { 592 | found_at = true; 593 | } 594 | } 595 | 596 | let mut connection = None; 597 | let mut binddn = None; 598 | let mut bindpassword = None; 599 | let mut tables = HashMap::new(); 600 | 601 | for (sec, prop) in &conf { 602 | if servername.is_empty() && sec.is_some() { 603 | servername = sec.unwrap().to_string(); 604 | } 605 | if sec.is_some() && servername == sec.unwrap() { 606 | for (key, value) in prop.iter() { 607 | if key == "connection" { 608 | connection = Some(value) 609 | } 610 | if key == "binddn" { 611 | binddn = Some(value) 612 | } 613 | if key == "bindpassword" { 614 | bindpassword = Some(value) 615 | } 616 | } 617 | } 618 | if sec.is_some() && format!("{}.tables", servername) == sec.unwrap() { 619 | for (key, value) in prop.iter() { 620 | tables.insert(key.to_string(), value.to_string()); 621 | } 622 | } 623 | 624 | // replace @somequery with the preconfigured query from sqldap.ini 625 | if found_at && sec.is_some() && format!("{}.queries", servername) == sec.unwrap() { 626 | for (key, value) in prop.iter() { 627 | for query in sql.iter_mut() { 628 | if *query == format!("@{}", key) { 629 | *query = value; 630 | } 631 | } 632 | } 633 | } 634 | } 635 | 636 | let connection = connection.context(format!( 637 | "Key 'connection' in section {} could not be found.", 638 | servername 639 | ))?; 640 | 641 | println!("Using server: {} ({})\n", servername, connection); 642 | for sqlquery in sql.iter() { 643 | if sqlquery.is_empty() || sqlquery == &"\n" { 644 | continue; 645 | } 646 | let dialect = Box::new(GenericDialect {}); 647 | /* 648 | let dialect: Box = match std::env::args().nth(3).unwrap_or_default().as_ref() { 649 | "--ansi" => Box::new(AnsiDialect {}), 650 | "--postgres" => Box::new(PostgreSqlDialect {}), 651 | "--ms" => Box::new(MsSqlDialect {}), 652 | "--generic" | "" => Box::new(GenericDialect {}), 653 | s => panic!("Unexpected parameter: {}", s), 654 | }; 655 | */ 656 | //let query = 657 | // parse_query(dialect, &sqlquery, tables.clone()).context("Query is not supported")?; 658 | 659 | //let mut ldap = LdapConn::new(connection).context("Cannot connect to LDAP")?; 660 | let mut ldap = LdapConn::with_settings(LdapConnSettings::new().set_no_tls_verify(true),connection).context("Cannot connect to LDAP")?; 661 | if binddn.is_some() && bindpassword.is_some() { 662 | let _res = ldap 663 | .simple_bind(binddn.unwrap(), bindpassword.unwrap())? 664 | .success() 665 | .context("ldap simple_bind failed")?; 666 | } 667 | 668 | let query = parse_query(dialect, &sqlquery, tables.clone(), &mut ldap) 669 | .context("Query is not supported")?; 670 | 671 | if query.qtype == QueryType::SHOW { 672 | if query.var.to_uppercase() == "TABLES" { 673 | if !tables.is_empty() { 674 | let mut table = Table::new(); 675 | table.style = TableStyle::simple(); 676 | let mut row = Vec::new(); 677 | row.push(TableCell::new_with_alignment( 678 | "Table name", 679 | 1, 680 | Alignment::Left, 681 | )); 682 | row.push(TableCell::new_with_alignment( 683 | "Configured dn", 684 | 1, 685 | Alignment::Left, 686 | )); 687 | table.add_row(Row::new(row)); 688 | 689 | for (key, value) in &tables { 690 | //println!("{} ({})", table, value); 691 | let mut row = Vec::new(); 692 | row.push(TableCell::new_with_alignment(key, 1, Alignment::Left)); 693 | row.push(TableCell::new_with_alignment(value, 1, Alignment::Left)); 694 | table.add_row(Row::new(row)); 695 | } 696 | if sql.len() > 1 { 697 | println!("Results for query '{}': \n", sqlquery); 698 | } 699 | println!("{}", table.render()); 700 | } else { 701 | println!("No tables are configured in your config file."); 702 | println!("See example config file below.\n"); 703 | println!("{}", CONFIG_FILE_EXAMPLE); 704 | } 705 | } else if query.var.to_uppercase() == "DATABASES" { 706 | let mut table = Table::new(); 707 | table.style = TableStyle::simple(); 708 | let mut row = Vec::new(); 709 | row.push(TableCell::new_with_alignment( 710 | "Database name", 711 | 1, 712 | Alignment::Left, 713 | )); 714 | row.push(TableCell::new_with_alignment( 715 | "Configured server", 716 | 1, 717 | Alignment::Left, 718 | )); 719 | table.add_row(Row::new(row)); 720 | 721 | for (sec, prop) in &conf { 722 | if sec.is_some() 723 | && sec.unwrap().find(".tables").is_none() 724 | && sec.unwrap().find(".queries").is_none() 725 | { 726 | let mut row = Vec::new(); 727 | row.push(TableCell::new_with_alignment( 728 | sec.unwrap(), 729 | 1, 730 | Alignment::Left, 731 | )); 732 | for (key, value) in prop.iter() { 733 | if key == "connection" { 734 | row.push(TableCell::new_with_alignment(value, 1, Alignment::Left)); 735 | } 736 | } 737 | table.add_row(Row::new(row)); 738 | //println!("{:?}", prop); 739 | } 740 | } 741 | if sql.len() > 1 { 742 | println!("Results for query '{}': \n", sqlquery); 743 | } 744 | println!("{}", table.render()); 745 | } 746 | } else if query.qtype == QueryType::SELECT { 747 | let (rs, _res) = ldap 748 | .search( 749 | &query.table, 750 | Scope::Subtree, 751 | &query.filter, 752 | query.identifiers.clone(), 753 | )? 754 | .success() 755 | .context("Ldap search failed")?; 756 | 757 | let identifier_len = query.identifiers.len() as usize; 758 | let one_table = identifier_len == 1; 759 | 760 | if sql.len() > 1 { 761 | println!("Results for query '{}': \n", sqlquery); 762 | } 763 | 764 | // if we requested only one field, only show one table 765 | let result = if !one_table || query.wildcard { 766 | let mut result = Vec::new(); 767 | for entry in rs { 768 | let se = SearchEntry::construct(entry); 769 | 770 | let mut keyval = HashMap::new(); 771 | for a in se.attrs { 772 | let (key, value) = a; 773 | keyval.insert(key, value.join("\n")); 774 | } 775 | result.push(keyval); 776 | } 777 | result 778 | } else { 779 | let mut result = Vec::new(); 780 | let mut key = String::from(""); 781 | for entry in rs { 782 | let se = SearchEntry::construct(entry); 783 | 784 | for a in se.attrs { 785 | let (key_attr, val_attr) = a; 786 | key = key_attr; 787 | result.push(val_attr.join("\n")); 788 | } 789 | } 790 | let mut keyval = HashMap::new(); 791 | keyval.insert(key, result.join("\n")); 792 | let mut result = Vec::new(); 793 | result.push(keyval); 794 | 795 | result 796 | }; 797 | 798 | if binddn.is_some() && bindpassword.is_some() { 799 | ldap.unbind().context("ldap unbind failed")?; 800 | } 801 | 802 | // the reason we stored everything in a vec is because 803 | // ldap3 doesn't return the requested fields in the same 804 | // order they where requested, eg: cn,uid,passwordretrycount 805 | // every new run they are resulted in a different order 806 | // this fixes this problem 807 | 808 | let mut table = Table::new(); 809 | table.style = TableStyle::simple(); 810 | 811 | if !query.wildcard { 812 | let mut row = Vec::new(); 813 | 814 | // print header 815 | for key in &query.identifiers { 816 | row.push(TableCell::new_with_alignment(key, 1, Alignment::Left)); 817 | } 818 | if !row.is_empty() { 819 | table.add_row(Row::new(row)); 820 | } 821 | 822 | // print data 823 | for result_entry in result.iter() { 824 | let mut row = Vec::new(); 825 | 826 | for key in &query.identifiers { 827 | let val = result_entry.get(key); 828 | if let Some(value) = val { 829 | row.push(TableCell::new_with_alignment(value, 1, Alignment::Left)); 830 | } else { 831 | row.push(TableCell::new_with_alignment("", 1, Alignment::Left)); 832 | } 833 | } 834 | if !row.is_empty() { 835 | table.add_row(Row::new(row)); 836 | } 837 | } 838 | 839 | let table_data = table.render(); 840 | 841 | if !fits_on_screen(&table_data) { 842 | for result_entry in result.iter() { 843 | if !one_table { 844 | table = Table::new(); 845 | table.style = TableStyle::simple(); 846 | } 847 | for key in &query.identifiers { 848 | let val = result_entry.get(&key.to_string()); 849 | if let Some(value) = val { 850 | let mut row = Vec::new(); 851 | row.push(TableCell::new_with_alignment(key, 1, Alignment::Left)); 852 | row.push(TableCell::new_with_alignment(value, 1, Alignment::Left)); 853 | table.add_row(Row::new(row)); 854 | } 855 | } 856 | if !one_table { 857 | if !table.rows.is_empty() { 858 | println!("{}", table.render()); 859 | } else { 860 | println!("Nothing found."); 861 | } 862 | } 863 | } 864 | if one_table { 865 | if !table.rows.is_empty() { 866 | println!("{}", table.render()); 867 | } else { 868 | println!("Nothing found."); 869 | } 870 | } 871 | } else { 872 | if !table.rows.is_empty() { 873 | println!("{}", table_data); 874 | } else { 875 | println!("Nothing found."); 876 | } 877 | } 878 | } else { 879 | let mut keys = Vec::new(); 880 | 881 | for result_entry in result.iter() { 882 | //for (key, _val) in result_entry { 883 | for key in result_entry.keys() { 884 | keys.push(key.to_string()); 885 | } 886 | } 887 | 888 | keys.sort(); // sort the rows 889 | keys.dedup(); // remove duplicates 890 | 891 | let mut row = Vec::new(); 892 | for key in keys.iter() { 893 | row.push(TableCell::new_with_alignment(key, 1, Alignment::Left)); 894 | } 895 | if !row.is_empty() { 896 | table.add_row(Row::new(row)); 897 | } 898 | 899 | for result_entry in result.iter() { 900 | let mut row = Vec::new(); 901 | for key in keys.iter() { 902 | let val = result_entry.get(&key.to_string()); 903 | if let Some(value) = val { 904 | row.push(TableCell::new_with_alignment(value, 1, Alignment::Left)); 905 | } else { 906 | row.push(TableCell::new_with_alignment("", 1, Alignment::Left)); 907 | } 908 | } 909 | if !row.is_empty() { 910 | table.add_row(Row::new(row)); 911 | } 912 | } 913 | 914 | let table_data = table.render(); 915 | 916 | if !fits_on_screen(&table_data) { 917 | for result_entry in result { 918 | if !one_table { 919 | table = Table::new(); 920 | table.style = TableStyle::simple(); 921 | } 922 | for key in keys.iter() { 923 | let val = result_entry.get(&key.to_string()); 924 | if let Some(value) = val { 925 | let mut row = Vec::new(); 926 | row.push(TableCell::new_with_alignment(key, 1, Alignment::Left)); 927 | row.push(TableCell::new_with_alignment(value, 1, Alignment::Left)); 928 | table.add_row(Row::new(row)); 929 | } 930 | } 931 | if !one_table { 932 | if !table.rows.is_empty() { 933 | println!("{}", table.render()); 934 | println!("\nConsider replacing * with a subset of the folling fields: \n"); 935 | println!("{}", keys.join(",")); 936 | } else { 937 | println!("Nothing found."); 938 | } 939 | } 940 | } 941 | if one_table { 942 | if !table.rows.is_empty() { 943 | println!("{}", table.render()); 944 | println!( 945 | "\nConsider replacing * with a subset of the folling fields: \n" 946 | ); 947 | println!("{}", keys.join(",")); 948 | } else { 949 | println!("Nothing found."); 950 | } 951 | } 952 | } else { 953 | if !table.rows.is_empty() { 954 | println!("{}", table_data); 955 | } else { 956 | println!("Nothing found."); 957 | } 958 | } 959 | } 960 | } 961 | } 962 | Ok(()) 963 | } 964 | --------------------------------------------------------------------------------