diff --git a/.circleci/glauth.cfg b/.circleci/glauth.cfg new file mode 100644 index 00000000..e76b33e1 --- /dev/null +++ b/.circleci/glauth.cfg @@ -0,0 +1,111 @@ +################# +# glauth.conf + +################# +# General configuration. +debug = true + +[ldap] + enabled = true + # run on a non privileged port + listen = "0.0.0.0:3893" + tls = false + +[ldaps] + enabled = false +################# +# Tracing section controls the tracer configuration +[tracing] + # if enabled is set to false, a no-op tracer will be used + enabled = true + # if both grpcEndpoint and httpEndpoint are unset, the default stdout provider will be used + # TODO add allowGRPCInsecure: right now grpc otlp is using the WithInsecure flag so traffic + # will always go without verifying server certificates + # grpcEndpoint = "otlp.monitoring.io:4317" + # httpEndpoint = "http://otlp.monitoring.io:4318" +################# +# The backend section controls the data store. +[backend] + datastore = "config" + baseDN = "dc=example,dc=com" + nameformat = "cn" + groupformat = "ou" + + # If you are using a client that requires reading the root DSE first + # such as SSSD + # anonymousdse = true + + ## Configure dn format to use structures like + ## "uid=serviceuser,cn=svcaccts,$BASEDN" instead of "cn=serviceuser,ou=svcaccts,$BASEDN" + ## to help ease migrations from other LDAP systems + # nameformat = "uid" + # groupformat = "cn" + + ## Configure ssh-key attribute name, default is 'sshPublicKey' + # sshkeyattr = "ipaSshPubKey" + +[behaviors] + # Ignore all capabilities restrictions, for instance allowing every user to perform a search + IgnoreCapabilities = false + # Enable a "fail2ban" type backoff mechanism temporarily banning repeated failed login attempts + LimitFailedBinds = true + # How many failed login attempts are allowed before a ban is imposed + NumberOfFailedBinds = 3 + # How long (in seconds) is the window for failed login attempts + PeriodOfFailedBinds = 10 + # How long (in seconds) is the ban duration + BlockFailedBindsFor = 60 + # Clean learnt IP addresses every N seconds + PruneSourceTableEvery = 600 + # Clean learnt IP addresses not seen in N seconds + PruneSourcesOlderThan = 600 + +################# +# The users section contains a hardcoded list of valid users. +# to create a passSHA256: echo -n "mysecret" | openssl dgst -sha256 +[[users]] + name = "admin_user" + uidnumber = 5001 + primarygroup = 5501 + mail = "admin_user@example.com" + passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood + [[users.customattributes]] + employeetype = ["Intern", "Temp"] + employeenumber = [12345, 54321] + [[users.capabilities]] + action = "search" + object = "dc=example,dc=com" + +# This user record shows all of the possible fields available +[[users]] + name = "sharding_user" + givenname="John" + sn="Doe" + mail = "sharding_user@example.com" + uidnumber = 5002 + primarygroup = 5501 + loginShell = "/bin/sh" + homeDir = "/root" + passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood + sshkeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA3UKCEllO2IZXgqNygiVb+dDLJJwVw3AJwV34t2jzR+/tUNVeJ9XddKpYQektNHsFmY93lJw5QDSbeH/mAC4KPoUM47EriINKEelRbyG4hC/ko/e2JWqEclPS9LP7GtqGmscXXo4JFkqnKw4TIRD52XI9n1syYM9Y8rJ88fjC/Lpn+01AB0paLVIfppJU35t0Ho9doHAEfEvcQA6tcm7FLJUvklAxc8WUbdziczbRV40KzDroIkXAZRjX7vXXhh/p7XBYnA0GO8oTa2VY4dTQSeDAUJSUxbzevbL0ll9Gi1uYaTDQyE5gbn2NfJSqq0OYA+3eyGtIVjFYZgi+txSuhw== rsa-key-20160209"] + passappsha256 = [ + "c32255dbf6fd6b64883ec8801f793bccfa2a860f2b1ae1315cd95cdac1338efa", # TestAppPw1 + "c9853d5f2599e90497e9f8cc671bd2022b0fb5d1bd7cfff92f079e8f8f02b8d3", # TestAppPw2 + "4939efa7c87095dacb5e7e8b8cfb3a660fa1f5edcc9108f6d7ec20ea4d6b3a88", # TestAppPw3 + ] + +################# +# The groups section contains a hardcoded list of valid users. +[[groups]] + name = "superheros" + gidnumber = 5501 + +[[groups]] + name = "svcaccts" + gidnumber = 5502 + +[[groups]] + name = "vpn" + gidnumber = 5503 + includegroups = [ 5501 ] + diff --git a/.circleci/pgcat_ldap.toml b/.circleci/pgcat_ldap.toml new file mode 100644 index 00000000..452eb110 --- /dev/null +++ b/.circleci/pgcat_ldap.toml @@ -0,0 +1,25 @@ + +[general] +host = "0.0.0.0" +port = 6432 +admin_username = "admin_user" +admin_password = "" +admin_auth_type = "ldap" +admin_auth_ldapurl = "ldap://127.0.0.1:3893" +admin_auth_ldapsuffix = "@example.com" + +[pools.sharded_db.users.0] +username = "sharding_user" +password = "sharding_user" +auth_type = "ldap" +auth_ldapurl = "ldap://127.0.0.1:3893" +auth_ldapsuffix = "@example.com" +pool_size = 10 +min_pool_size = 1 +pool_mode = "transaction" + +[pools.sharded_db.shards.0] +servers = [ + [ "127.0.0.1", 5432, "primary" ], +] +database = "shard0" diff --git a/.circleci/pgcat_trust.toml b/.circleci/pgcat_trust.toml new file mode 100644 index 00000000..7e33ac8a --- /dev/null +++ b/.circleci/pgcat_trust.toml @@ -0,0 +1,21 @@ + +[general] +host = "0.0.0.0" +port = 6432 +admin_username = "admin_user" +admin_password = "" +admin_auth_type = "trust" + +[pools.sharded_db.users.0] +username = "sharding_user" +password = "sharding_user" +auth_type = "trust" +pool_size = 10 +min_pool_size = 1 +pool_mode = "transaction" + +[pools.sharded_db.shards.0] +servers = [ + [ "127.0.0.1", 5432, "primary" ], +] +database = "shard0" diff --git a/Cargo.lock b/Cargo.lock index e2f89853..64a021e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,7 +94,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -104,7 +104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -211,9 +211,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -316,6 +316,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -384,23 +394,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -415,12 +414,33 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -751,7 +771,7 @@ checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ "socket2 0.5.3", "widestring", - "windows-sys", + "windows-sys 0.48.0", "winreg", ] @@ -769,7 +789,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -822,11 +842,45 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lber" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2df7f9fd9f64cf8f59e1a4a0753fe7d575a5b38d3d7ac5758dcee9357d83ef0a" +dependencies = [ + "bytes", + "nom", +] + +[[package]] +name = "ldap3" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dceb52eac140c8679326f619b963577ccf5a4ab025fe5e6db424d2fd3e4f400" +dependencies = [ + "async-trait", + "bytes", + "futures", + "futures-util", + "lazy_static", + "lber", + "log", + "native-tls", + "nom", + "percent-encoding", + "thiserror", + "tokio", + "tokio-native-tls", + "tokio-stream", + "tokio-util", + "url", +] + [[package]] name = "libc" -version = "0.2.147" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linked-hash-map" @@ -836,9 +890,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -919,6 +973,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -936,7 +996,25 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -953,6 +1031,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -997,6 +1085,50 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -1023,7 +1155,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -1053,6 +1185,7 @@ dependencies = [ "hyper-util", "itertools", "jemallocator", + "ldap3", "log", "lru", "md-5", @@ -1159,6 +1292,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "postgres-protocol" version = "0.6.5" @@ -1323,15 +1462,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.4" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1381,6 +1520,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1397,6 +1545,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.171" @@ -1515,7 +1686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1595,6 +1766,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "thiserror" version = "1.0.43" @@ -1668,7 +1851,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.4.9", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1682,6 +1865,16 @@ dependencies = [ "syn 2.0.26", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -1946,6 +2139,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -2080,7 +2279,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -2089,7 +2288,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -2098,13 +2306,29 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -2113,42 +2337,90 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winnow" version = "0.5.0" @@ -2165,5 +2437,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index f75e9185..15c9de68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,6 @@ lru = "0.12.0" [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = "0.5.0" + +[dependencies.ldap3] +version = "0.11.3" diff --git a/src/auth_passthrough.rs b/src/auth_passthrough.rs index 159847ed..13627f62 100644 --- a/src/auth_passthrough.rs +++ b/src/auth_passthrough.rs @@ -1,3 +1,4 @@ +use crate::config::AuthType; use crate::errors::Error; use crate::pool::ConnectionPool; use crate::server::Server; @@ -71,6 +72,9 @@ impl AuthPassthrough { pub async fn fetch_hash(&self, address: &crate::config::Address) -> Result { let auth_user = crate::config::User { username: self.user.clone(), + auth_type: AuthType::MD5, + auth_ldapsuffix: None, + auth_ldapurl: None, password: Some(self.password.clone()), server_username: None, server_password: None, diff --git a/src/client.rs b/src/client.rs index 23392b73..7830345e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,6 +2,7 @@ use crate::errors::{ClientIdentifier, Error}; use crate::pool::BanReason; /// Handle clients by pretending to be a PostgreSQL server. use bytes::{Buf, BufMut, BytesMut}; +use ldap3::{LdapConnAsync, LdapConnSettings}; use log::{debug, error, info, trace, warn}; use once_cell::sync::Lazy; use std::collections::{HashMap, VecDeque}; @@ -14,7 +15,9 @@ use tokio::sync::mpsc::Sender; use crate::admin::{generate_server_parameters_for_admin, handle_admin}; use crate::auth_passthrough::refetch_auth_hash; -use crate::config::{get_config, get_idle_client_in_transaction_timeout, Address, PoolMode}; +use crate::config::{ + get_config, get_idle_client_in_transaction_timeout, Address, AuthType, PoolMode, +}; use crate::constants::*; use crate::messages::*; use crate::plugins::PluginOutput; @@ -413,6 +416,37 @@ pub async fn startup_tls( } } +// Pass in username and password to authenticate against LDAP +async fn authenticate_ldap( + username: &str, + password: &str, + ldapurl: &str, + ldapsuffix: &str, +) -> bool { + // Connection to the LDAP Server + let ldap_conn_settings = LdapConnSettings::new(); + let (conn, mut ldap) = LdapConnAsync::with_settings(ldap_conn_settings, ldapurl) + .await + .unwrap(); + ldap3::drive!(conn); + + // Takes the username provided and converts it into an email for validation + // This is required because LDAP uses either the Distinguished name or Email in order to bind. Username alone will not work :/ + let email = format!("{}{}", username, ldapsuffix); + + // Attempts a simple bind using the passed in values of username and Password + println!("{:?}", password); + let result = ldap + .simple_bind(email.as_str(), password) + .await + .unwrap() + .success(); + ldap.unbind().await.unwrap(); + + // If the authentication is successful return true, else return false. + result.is_ok() +} + impl Client where S: tokio::io::AsyncRead + std::marker::Unpin, @@ -463,8 +497,8 @@ where .count() == 1; - // Kick any client that's not admin while we're in admin-only mode. if !admin && admin_only { + // Kick any client that's not admin while we're in admin-only mode. debug!( "Rejecting non-admin connection to {} when in admin only mode", pool_name @@ -481,72 +515,134 @@ where let process_id: i32 = rand::random(); let secret_key: i32 = rand::random(); - // Perform MD5 authentication. - // TODO: Add SASL support. - let salt = md5_challenge(&mut write).await?; + let mut prepared_statements_enabled = false; - let code = match read.read_u8().await { - Ok(p) => p, - Err(_) => { - return Err(Error::ClientSocketError( - "password code".into(), - client_identifier, - )) - } - }; + // Authenticate admin user. + let (transaction_mode, mut server_parameters) = if admin { + let config = get_config(); + // TODO: Add SASL support. + // Perform MD5 authentication. + match config.general.admin_auth_type { + AuthType::MD5 => { + let salt = md5_challenge(&mut write).await?; + + let code = match read.read_u8().await { + Ok(p) => p, + Err(_) => { + return Err(Error::ClientSocketError( + "password code".into(), + client_identifier, + )) + } + }; + + // PasswordMessage + if code as char != 'p' { + return Err(Error::ProtocolSyncError(format!( + "Expected p, got {}", + code as char + ))); + } - // PasswordMessage - if code as char != 'p' { - return Err(Error::ProtocolSyncError(format!( - "Expected p, got {}", - code as char - ))); - } + let len = match read.read_i32().await { + Ok(len) => len, + Err(_) => { + return Err(Error::ClientSocketError( + "password message length".into(), + client_identifier, + )) + } + }; - let len = match read.read_i32().await { - Ok(len) => len, - Err(_) => { - return Err(Error::ClientSocketError( - "password message length".into(), - client_identifier, - )) - } - }; + let mut password_response = vec![0u8; (len - 4) as usize]; - let mut password_response = vec![0u8; (len - 4) as usize]; + match read.read_exact(&mut password_response).await { + Ok(_) => (), + Err(_) => { + return Err(Error::ClientSocketError( + "password message".into(), + client_identifier, + )) + } + }; - match read.read_exact(&mut password_response).await { - Ok(_) => (), - Err(_) => { - return Err(Error::ClientSocketError( - "password message".into(), - client_identifier, - )) - } - }; + // Compare server and client hashes. + let password_hash = md5_hash_password( + &config.general.admin_username, + &config.general.admin_password, + &salt, + ); - let mut prepared_statements_enabled = false; + if password_hash != password_response { + let error = + Error::ClientGeneralError("Invalid password".into(), client_identifier); - // Authenticate admin user. - let (transaction_mode, mut server_parameters) = if admin { - let config = get_config(); + warn!("{}", error); + wrong_password(&mut write, username).await?; - // Compare server and client hashes. - let password_hash = md5_hash_password( - &config.general.admin_username, - &config.general.admin_password, - &salt, - ); + return Err(error); + } + } + AuthType::LDAP => { + clear_text_challenge(&mut write).await?; + let code = match read.read_u8().await { + Ok(p) => p, + Err(_) => { + return Err(Error::ClientSocketError( + "password code".into(), + client_identifier, + )) + } + }; + + // PasswordMessage + if code as char != 'p' { + return Err(Error::ProtocolSyncError(format!( + "Expected p, got {}", + code as char + ))); + } - if password_hash != password_response { - let error = Error::ClientGeneralError("Invalid password".into(), client_identifier); + let len = match read.read_i32().await { + Ok(len) => len, + Err(_) => { + return Err(Error::ClientSocketError( + "password message length".into(), + client_identifier, + )) + } + }; - warn!("{}", error); - wrong_password(&mut write, username).await?; + let mut password_response = vec![0u8; (len - 4) as usize]; - return Err(error); - } + match read.read_exact(&mut password_response).await { + Ok(_) => (), + Err(_) => { + return Err(Error::ClientSocketError( + "password message".into(), + client_identifier, + )) + } + }; + let str_password = String::from_utf8(password_response).unwrap(); + let str_password = str_password.trim_matches(char::from(0)); + let unsuccessful_auth = !authenticate_ldap( + &config.general.admin_username, + str_password, + &config.general.admin_auth_ldapurl.unwrap(), + &config.general.admin_auth_ldapsuffix.unwrap(), + ) + .await; + if unsuccessful_auth { + wrong_password(&mut write, username).await?; + return Err(Error::ClientGeneralError( + "Invalid password".into(), + client_identifier, + )); + } + } + } (false, generate_server_parameters_for_admin()) } // Authenticate normal user. @@ -573,92 +669,201 @@ where // Obtain the hash to compare, we give preference to that written in cleartext in config // if there is nothing set in cleartext and auth passthrough (auth_query) is configured, we use the hash obtained // when the pool was created. If there is no hash there, we try to fetch it one more time. - let password_hash = if let Some(password) = &pool.settings.user.password { - Some(md5_hash_password(username, password, &salt)) - } else { - if !get_config().is_auth_query_configured() { - wrong_password(&mut write, username).await?; - return Err(Error::ClientAuthImpossible(username.into())); - } + match pool.settings.user.auth_type { + // Perform MD5 authentication. + // TODO: Add SASL support. + AuthType::MD5 => { + let salt = md5_challenge(&mut write).await?; + + let code = match read.read_u8().await { + Ok(p) => p, + Err(_) => { + return Err(Error::ClientSocketError( + "password code".into(), + client_identifier, + )) + } + }; + + // PasswordMessage + if code as char != 'p' { + return Err(Error::ProtocolSyncError(format!( + "Expected p, got {}", + code as char + ))); + } - let mut hash = (*pool.auth_hash.read()).clone(); + let len = match read.read_i32().await { + Ok(len) => len, + Err(_) => { + return Err(Error::ClientSocketError( + "password message length".into(), + client_identifier, + )) + } + }; - if hash.is_none() { - warn!( - "Query auth configured \ - but no hash password found \ - for pool {}. Will try to refetch it.", - pool_name - ); + let mut password_response = vec![0u8; (len - 4) as usize]; - match refetch_auth_hash(&pool).await { - Ok(fetched_hash) => { - warn!("Password for {}, obtained. Updating.", client_identifier); + match read.read_exact(&mut password_response).await { + Ok(_) => (), + Err(_) => { + return Err(Error::ClientSocketError( + "password message".into(), + client_identifier, + )) + } + }; - { - let mut pool_auth_hash = pool.auth_hash.write(); - *pool_auth_hash = Some(fetched_hash.clone()); + let password_hash = if let Some(password) = &pool.settings.user.password { + Some(md5_hash_password(username, password, &salt)) + } else { + if !get_config().is_auth_query_configured() { + wrong_password(&mut write, username).await?; + return Err(Error::ClientAuthImpossible(username.into())); + } + + let mut hash = (*pool.auth_hash.read()).clone(); + + if hash.is_none() { + warn!( + "Query auth configured \ + but no hash password found \ + for pool {}. Will try to refetch it.", + pool_name + ); + + match refetch_auth_hash(&pool).await { + Ok(fetched_hash) => { + warn!( + "Password for {}, obtained. Updating.", + client_identifier + ); + + { + let mut pool_auth_hash = pool.auth_hash.write(); + *pool_auth_hash = Some(fetched_hash.clone()); + } + + hash = Some(fetched_hash); + } + + Err(err) => { + wrong_password(&mut write, username).await?; + + return Err(Error::ClientAuthPassthroughError( + err.to_string(), + client_identifier, + )); + } } + }; - hash = Some(fetched_hash); - } + Some(md5_hash_second_pass(&hash.unwrap(), &salt)) + }; + + // Once we have the resulting hash, we compare with what the client gave us. + // If they do not match and auth query is set up, we try to refetch the hash one more time + // to see if the password has changed since the pool was created. + // + // @TODO: we could end up fetching again the same password twice (see above). + if password_hash.unwrap() != password_response { + warn!( + "Invalid password {}, will try to refetch it.", + client_identifier + ); + + let fetched_hash = match refetch_auth_hash(&pool).await { + Ok(fetched_hash) => fetched_hash, + Err(err) => { + wrong_password(&mut write, username).await?; - Err(err) => { - wrong_password(&mut write, username).await?; + return Err(err); + } + }; + + let new_password_hash = md5_hash_second_pass(&fetched_hash, &salt); + + // Ok password changed in server an auth is possible. + if new_password_hash == password_response { + warn!( + "Password for {}, changed in server. Updating.", + client_identifier + ); - return Err(Error::ClientAuthPassthroughError( - err.to_string(), + { + let mut pool_auth_hash = pool.auth_hash.write(); + *pool_auth_hash = Some(fetched_hash); + } + } else { + wrong_password(&mut write, username).await?; + return Err(Error::ClientGeneralError( + "Invalid password".into(), client_identifier, )); } } - }; + } + AuthType::LDAP => { + clear_text_challenge(&mut write).await?; + let code = match read.read_u8().await { + Ok(p) => p, + Err(_) => { + return Err(Error::ClientSocketError( + "password code".into(), + client_identifier, + )) + } + }; + + // PasswordMessage + if code as char != 'p' { + return Err(Error::ProtocolSyncError(format!( + "Expected p, got {}", + code as char + ))); + } - Some(md5_hash_second_pass(&hash.unwrap(), &salt)) - }; + let len = match read.read_i32().await { + Ok(len) => len, + Err(_) => { + return Err(Error::ClientSocketError( + "password message length".into(), + client_identifier, + )) + } + }; - // Once we have the resulting hash, we compare with what the client gave us. - // If they do not match and auth query is set up, we try to refetch the hash one more time - // to see if the password has changed since the pool was created. - // - // @TODO: we could end up fetching again the same password twice (see above). - if password_hash.unwrap() != password_response { - warn!( - "Invalid password {}, will try to refetch it.", - client_identifier - ); + let mut password_response = vec![0u8; (len - 4) as usize]; - let fetched_hash = match refetch_auth_hash(&pool).await { - Ok(fetched_hash) => fetched_hash, - Err(err) => { + match read.read_exact(&mut password_response).await { + Ok(_) => (), + Err(_) => { + return Err(Error::ClientSocketError( + "password message".into(), + client_identifier, + )) + } + }; + let str_password = String::from_utf8(password_response).unwrap(); + let str_password = str_password.trim_matches(char::from(0)); + let unsuccessful_auth = !authenticate_ldap( + pool.settings.user.username.as_str(), + str_password, + &pool.settings.user.auth_ldapurl.clone().unwrap(), + &pool.settings.user.auth_ldapsuffix.clone().unwrap(), + ) + .await; + if unsuccessful_auth { wrong_password(&mut write, username).await?; - return Err(err); - } - }; - - let new_password_hash = md5_hash_second_pass(&fetched_hash, &salt); - - // Ok password changed in server an auth is possible. - if new_password_hash == password_response { - warn!( - "Password for {}, changed in server. Updating.", - client_identifier - ); - - { - let mut pool_auth_hash = pool.auth_hash.write(); - *pool_auth_hash = Some(fetched_hash); + return Err(Error::ClientGeneralError( + "Invalid password".into(), + client_identifier, + )); } - } else { - wrong_password(&mut write, username).await?; - return Err(Error::ClientGeneralError( - "Invalid password".into(), - client_identifier, - )); } } - let transaction_mode = pool.settings.pool_mode == PoolMode::Transaction; prepared_statements_enabled = transaction_mode && pool.prepared_statement_cache.is_some(); diff --git a/src/config.rs b/src/config.rs index c7aaf4c3..4642a870 100644 --- a/src/config.rs +++ b/src/config.rs @@ -208,6 +208,11 @@ impl Address { pub struct User { pub username: String, pub password: Option, + + #[serde(default = "User::default_auth_type")] + pub auth_type: AuthType, + pub auth_ldapsuffix: Option, + pub auth_ldapurl: Option, pub server_username: Option, pub server_password: Option, pub pool_size: u32, @@ -225,6 +230,9 @@ impl Default for User { User { username: String::from("postgres"), password: None, + auth_type: AuthType::MD5, + auth_ldapsuffix: None, + auth_ldapurl: None, server_username: None, server_password: None, pool_size: 15, @@ -239,6 +247,10 @@ impl Default for User { } impl User { + pub fn default_auth_type() -> AuthType { + AuthType::MD5 + } + fn validate(&self) -> Result<(), Error> { if let Some(min_pool_size) = self.min_pool_size { if min_pool_size > self.pool_size { @@ -334,6 +346,12 @@ pub struct General { pub admin_username: String, pub admin_password: String, + #[serde(default = "General::default_admin_auth_type")] + pub admin_auth_type: AuthType, + + pub admin_auth_ldapurl: Option, + pub admin_auth_ldapsuffix: Option, + #[serde(default = "General::default_validate_config")] pub validate_config: bool, @@ -348,6 +366,10 @@ impl General { "0.0.0.0".into() } + pub fn default_admin_auth_type() -> AuthType { + AuthType::MD5 + } + pub fn default_port() -> u16 { 5432 } @@ -456,6 +478,9 @@ impl Default for General { verify_server_certificate: false, admin_username: String::from("admin"), admin_password: String::from("admin"), + admin_auth_type: AuthType::MD5, + admin_auth_ldapurl: None, + admin_auth_ldapsuffix: None, validate_config: true, auth_query: None, auth_query_user: None, @@ -476,6 +501,15 @@ pub enum PoolMode { Session, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy, Hash)] +pub enum AuthType { + #[serde(alias = "LDAP", alias = "ldap")] + LDAP, + + #[serde(alias = "md5", alias = "MD5")] + MD5, +} + impl std::fmt::Display for PoolMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/src/messages.rs b/src/messages.rs index 6a114e13..21aeeb81 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -64,6 +64,20 @@ where write_all(stream, auth_ok).await } +/// Tell the client to use clearr text auth +pub async fn clear_text_challenge(stream: &mut S) -> Result<(), Error> +where + S: tokio::io::AsyncWrite + std::marker::Unpin, +{ + let mut auth_clear_text = BytesMut::with_capacity(9); + + auth_clear_text.put_u8(b'R'); + auth_clear_text.put_i32(8); + auth_clear_text.put_i32(3); + + write_all(stream, auth_clear_text).await +} + /// Generate md5 password challenge. pub async fn md5_challenge(stream: &mut S) -> Result<[u8; 4], Error> where diff --git a/tests/docker/Dockerfile b/tests/docker/Dockerfile index 6eccddd1..553dd613 100644 --- a/tests/docker/Dockerfile +++ b/tests/docker/Dockerfile @@ -6,6 +6,8 @@ RUN apt-get update && apt-get install llvm-11 psmisc postgresql-contrib postgres RUN cargo install cargo-binutils rustfilt RUN rustup component add llvm-tools-preview RUN sudo gem install bundler +RUN wget -O /usr/bin/glauth https://github.com/glauth/glauth/releases/download/v2.3.2/glauth-linux-amd64 +RUN chmod +x /usr/bin/glauth RUN wget -O toxiproxy-2.4.0.deb https://github.com/Shopify/toxiproxy/releases/download/v2.4.0/toxiproxy_2.4.0_linux_$(dpkg --print-architecture).deb && \ sudo dpkg -i toxiproxy-2.4.0.deb RUN wget -O go1.21.3.linux-$(dpkg --print-architecture).tar.gz https://go.dev/dl/go1.21.3.linux-$(dpkg --print-architecture).tar.gz && \ diff --git a/tests/python/conftest.py b/tests/python/conftest.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/python/test_pgcat.py b/tests/python/test_pgcat.py index dc2f11e5..335086bd 100644 --- a/tests/python/test_pgcat.py +++ b/tests/python/test_pgcat.py @@ -1,13 +1,40 @@ -import os + import signal import time import psycopg2 - import utils SHUTDOWN_TIMEOUT = 5 + +def _test_normal_ldap_auth(): + conn, cur = utils.connect_db_ldap(autocommit=False) + cur.execute("SELECT 1") + res = cur.fetchall() + print(res) + utils.cleanup_conn(conn, cur) + + +def _test_admin_ldap_auth(): + conn, cur = utils.connect_db_ldap(admin=True) + cur.execute("SHOW POOLS") + res = cur.fetchall() + print(res) + utils.cleanup_conn(conn, cur) + + +def test_ldap(): + utils.ldap_start() + utils.pgcat_ldap_start() + + _test_admin_ldap_auth() + _test_normal_ldap_auth() + + utils.glauth_send_signal(signal.SIGTERM) + utils.pg_cat_send_signal(signal.SIGTERM) + + def test_normal_db_access(): utils.pgcat_start() conn, cur = utils.connect_db(autocommit=False) @@ -256,3 +283,5 @@ def test_shutdown_logic(): utils.cleanup_conn(conn, cur) utils.pg_cat_send_signal(signal.SIGTERM) + + # - - - - - - - - - - - - - - - - - - diff --git a/tests/python/utils.py b/tests/python/utils.py index 5c49bce9..aa8269d5 100644 --- a/tests/python/utils.py +++ b/tests/python/utils.py @@ -1,24 +1,55 @@ -from typing import Tuple import os -import psutil import signal import time +from typing import Tuple +import psutil import psycopg2 PGCAT_HOST = "127.0.0.1" PGCAT_PORT = "6432" -def pgcat_start(): + +def _pgcat_start(config_path: str): pg_cat_send_signal(signal.SIGTERM) - os.system("./target/debug/pgcat .circleci/pgcat.toml &") + os.system(f"./target/debug/pgcat {config_path} &") + time.sleep(2) + + +def pgcat_start(): + _pgcat_start(config_path='.circleci/pgcat.toml') + + +def pgcat_ldap_start(): + _pgcat_start(config_path='.circleci/pgcat_ldap.toml') + + +def ldap_start(): + glauth_send_signal(signal.SIGTERM) + os.system("glauth -c .circleci/glauth.cfg &") time.sleep(2) +def glauth_send_signal(signal: signal.Signals): + try: + for proc in psutil.process_iter(["pid", "name"]): + if proc.name() == "glauth": + os.kill(proc.pid, signal) + except Exception as e: + # The process can be gone when we send this signal + print(e) + + if signal == signal.SIGTERM: + # Returns 0 if pgcat process exists + time.sleep(2) + if not os.system('pgrep glauth'): + raise Exception("glauth not closed after SIGTERM") + + def pg_cat_send_signal(signal: signal.Signals): try: for proc in psutil.process_iter(["pid", "name"]): - if "pgcat" == proc.name(): + if proc.name() == "pgcat": os.kill(proc.pid, signal) except Exception as e: # The process can be gone when we send this signal @@ -55,6 +86,30 @@ def connect_db( return (conn, cur) +def connect_db_ldap( + autocommit: bool = True, + admin: bool = False, +) -> Tuple[psycopg2.extensions.connection, psycopg2.extensions.cursor]: + + if admin: + user = "admin_user" + password = "dogood" + db = "pgcat" + else: + user = "sharding_user" + password = "dogood" + db = "sharded_db" + + conn = psycopg2.connect( + f"postgres://{user}:{password}@{PGCAT_HOST}:{PGCAT_PORT}/{db}?application_name=testing_pgcat", + connect_timeout=2, + ) + conn.autocommit = autocommit + cur = conn.cursor() + + return (conn, cur) + + def cleanup_conn(conn: psycopg2.extensions.connection, cur: psycopg2.extensions.cursor): cur.close() conn.close()