wip(lldap): Consultant

This commit is contained in:
Madeleine Sydney
2025-02-19 20:30:39 -07:00
45 changed files with 3054 additions and 55 deletions

View File

@@ -0,0 +1,123 @@
(ns ldap-nginx-plumber.main
(:require [clojure.spec.alpha :as spec]
[clojure.string :as str]
[org.httpkit.server :as http]
[clj-ldap.client :as ldap]
[babashka.cli :as cli]
[clojure.pprint :refer [pprint]])
(:import [java.util Base64]
[java.nio.charset StandardCharsets])
(:gen-class))
(defn- port? [x]
(and (nat-int? x)
(<= 0 x 65535)))
(def cli-spec
{:spec
{:port {:coerce :int
:desc "Port to listen on"
:alias :p
:validate port?
:require true}
:base-dn {:coerce :string
:desc "Base DN for LDAP searches"
:require true}
:ldap-host {:coerce :string
:require true}
:ldap-port {:coerce :int
:validate port?
:default 389}
:bind-dn {:coerce :string
:require true}
:bind-password {:coerce :string
:require true}}})
(def ^:dynamic *opts*)
(defonce ldap-connection-pool
(atom nil))
(defn- base64->utf8 [base64]
(try (-> (.decode (Base64/getDecoder) base64)
(String. StandardCharsets/UTF_8))
(catch java.lang.IllegalArgumentException _))
nil)
(defn- response [status & {:as more}]
(apply merge
{:status status
:headers {"Content-Type" "text/plain"}}
more))
(defn- consultant-app [req]
(printf "\n%s received request:\n" (.toString (java.util.Date.)))
(pprint req)
(try
(if-let [[_ user pass] (some->> (get-in [req :headers] "Authorization")
str/lower-case
(re-matches #"basic ([a-zA-Z0-9+/=]+)")
second
base64->utf8
(re-matches #"([^:]+):(.*)"))]
(response 200 :body "yay!")
(response
401
:headers {"WWW-Authenticate" "Basic realm=\"Restricted\""
"Cache-Control" "no-cache"}))
(catch Exception e
(println "`consultant-app` threw an error:")
(prn e)
(response 500))))
(defonce consultant-server (atom nil))
(defn- stop-consultant! []
(when @consultant-server
;; Graceful shutdown: wait 100ms for existing requests to be finished.
;; :timeout is optional, when no timeout, stop immediately.
(http/server-stop! @consultant-server {:timeout 100})
(reset! consultant-server nil)))
(defn- start-consultant [& {:keys [port] :as opts}]
(binding [*opts* opts]
(if @consultant-server
(throw (ex-info "Refusing to start the server whilst a previous lingers" {}))
(reset! consultant-server
(http/run-server #'consultant-app
{:port port
:legacy-return-value? false})))))
(defn- connect-to-ldap
[& {:keys [ldap-host ldap-port bind-dn bind-password]}]
(reset! ldap-connection-pool
(or @ldap-connection-pool
(ldap/connect {:host {:address ldap-host
:port ldap-port}
:max-connections 8
:bind-dn bind-dn
:password bind-password}))))
(defn- main* [& opts]
(and (apply connect-to-ldap opts)
(apply start-consultant opts)))
(comment
(let [ask (let [base-dn "dc=identify,dc=deertopia,dc=net"]
(consultant-app {:port 8080 :ldap-host "192.168.68.79" :ldap-port 3890
:base-dn base-dn
:bind-dn (str "uid=nginx-bind-user,ou=people," base-dn)
:bind-password "secret123"}))]
(ask {})))
(comment ; Start on :8080
(let [base-dn "dc=identify,dc=deertopia,dc=net"]
(main* :port 8080 :ldap-host "192.168.68.79" :ldap-port 3890 :base-dn base-dn
:bind-dn (str "uid=nginx-bind-user,ou=people," base-dn)
:bind-password "secret123")))
(comment ; Shutdown
(stop-consultant!))
(defn -main [& args]
(main* (cli/parse-opts args cli-spec)))