Getting a password from command line and repl

504 views
Skip to first unread message

Guillermo Vayá Pérez

unread,
Apr 11, 2017, 6:10:08 PM4/11/17
to Clojure
Hi,
I'm currently trying to get a password from the user in case it wasn't supplied by other means, but it seems to be a bit more difficult than I expected. I've already searched both the web and this mail list finding only partial solutions to the problem. I must say I'm not completely fluent in Clojure, so I'm sure there is a lot of room for improvement both in my solution and also the way I write it. Help on both is appreciated.

Up to now what I've got so far are a couple of solutions that I'm not happy with, I'll put some comments on each one to clarify any problems:


(defn- get-passkey1
  []
  ;; - not a good method, it can be copied back selecting where
  ;; text appears to be (cursor moves although nothing is printed)
  ;; - code is ugly as hell
  ;; - ansi control codes are not portable
  (print (str "password: \033[8m"))
  (flush)
  (let [pass (read-line)]
    (print (str "\033[K\033[0m")) ;; k should kill the line, but since read-line expects a newline,
                                  ;; I don't get to really kill it, so the pass is exposed
    (flush)
    pass))

(defn- get-passkey2
  []
  ;; - doesn't work as it initially fails due to a null pointer both with repl and lein run
  ;; - improves a little if run with lein trampoline run (not ideal), but 
  ;; complains that there is no readPassword method that matches.
  (let [console (System/console)]
    (.readPassword console "Password: %s")))

;; this one needs (:import [jline.console ConsoleReader])
(defn- get-passkey3
  []
  ;; - in a repl this freezes the interpreter
  ;; - seems to work fine with lein run, but getting the whole
  ;; library for two lines of code seems expensive to me.
  ;; - still I'd go this route if this was the way to go (no freezing at least)
  (print "Enter password")
  (flush)
  (let [cr (ConsoleReader.)]
    (.readLine cr "password?" \*)))

The best solution I've worked up to now is a mix between 1 and 3, detecting if I'm in a repl (via another ugly hack to see if there are command line parameters) and choose between them to sort each one's shortcommings.

I'm wondering if there is a better way (and hoping there is) or if I can fix the issues with any of the implementations so I don't have to apply patch of code over patch of code.

Thanks for any help given,

Guillermo Vayá

Denis Fuenzalida

unread,
Apr 12, 2017, 9:08:16 PM4/12/17
to Clojure
I had a look and the problem with approach #2 is that the signature of readLine expects 2 args: a format string and an array of objects to be used in the prompt. And it returns an array of chars, so you need something like:

(let [console (System/console)
          chars   (.readPassword console "%s" (into-array [prompt]))]
  (apply str chars))

I read in https://groups.google.com/forum/#!topic/clojure/ymDZj7T35x4 that you can check also the contents of the *ns* var to determine if you are running in a REPL or not.

I created a dummy Leiningen project with the following functions:

(defn read-password [prompt]
  (if (= "user" (str (.getName *ns*)))
    (do
      (print (format "%s [will be echoed to the screen]" prompt))
      (flush)
      (read-line))
    (let [console (System/console)
          chars   (.readPassword console "%s" (into-array [prompt]))]
      (apply str chars))))

(defn -main [& args]
  (let [password (read-password "Enter your password:")]
    (println (format "Your pasword is '%s'" password))))

When running with `lein run` it echoes to the screen as expected. When ran as `java -jar target/uberjar/readpass-0.1.0-SNAPSHOT-standalone.jar` it uses the version that doesn't echo.

-- Denis

Guillermo Vayá Pérez

unread,
May 3, 2017, 5:17:12 PM5/3/17
to Clojure
Thank you! I'll try it that way.

sorry it took me so long to notice, I thought I'd be notified or maybe I missed it.
Reply all
Reply to author
Forward
0 new messages