jverzani / gWidgets2

Rewrite of gWidgets
35 stars 9 forks source link

Obscuring text values in gedits for passwords #81

Closed richierocks closed 9 years ago

richierocks commented 9 years ago

Is there a way to hide the contents of a gedit box in order to type sensitive information like passwords? That is, a way of automatically replacing the contents with asterisks.

My use case is that when using devtools::install_bitbucket with a private repository, you have to include your bitbucket password as a string. Forcing colleagues to look away from the screen is tiresome.

richierocks commented 9 years ago

I had a quick go at implementing the feature by overriding the keystroke handling.

library(methods)
library(gWidgets2tcltk)

passwordEntryFactory <- suppressWarnings(
  setRefClass(
    "PasswordEntry",
    fields = list(
      win   = "ANY",
      txt   = "ANY",
      pw    = "ANY",
      posn  = "ANY" 
    ),
    methods = list(
      initialize = function(...)
      {
        pw <<- ""
        posn <<- 0
        win <<- gwindow(
          "Enter your password", 
          visible = FALSE,
          height    = 25
        )
        txt <<- gedit(
          container = win
        )
        addHandlerKeystroke(
          txt,
          handler = function(h, ...)
          {
  #           message(
  #             "key = ", h$key, 
  #             "; pw = ", pw, 
  #             "; posn = ", posn, 
  #             "; text = ", svalue(h$obj)
  #           )
            switch(
              h$key,
              Return = {
                dispose(win)
                set_stars()
              },
              BackSpace = {
                if(nchar(svalue(h$obj)) < nchar(pw))
                {
                  pw <<- paste0(substring(pw, 1, posn - 1), substring(pw, posn + 1))
                  posn <<- posn - 1
                  set_stars()
                }
              },
              Delete = {
                if(nchar(svalue(h$obj)) < nchar(pw))
                {
                  pw <<- paste0(substring(pw, 1, posn), substring(pw, posn + 2))
                  set_stars()
                }
              },
              Shift_L = {
                #Nothing to do
              },
              Right = {
                posn <<- posn + 1
              },
              Left = {
                posn <<- posn - 1
              },
              Home = {
                posn <<- 0
              },
              End = {
                posn <<- nchar(svalue(h$obj))
              },
              {
                x <- if(nchar(h$key) > 1)
                {
                  switch(
                    h$key,
                    exclam       = "!",
                    at           = "@",
                    numbersign   = "#",
                    dollar       = "$",
                    percent      = "%",
                    asciicircum  = "^",
                    ampersand    = "&",
                    asterisk     = "*",
                    parenleft    = "(",
                    parenright   = ")",
                    minus        = "-",
                    underscore   = "_",
                    equal        = "=",
                    plus         = "+",
                    bracketleft  = "[",
                    bracketright = "]",
                    braceleft    = "{",
                    braceright   = "}",
                    backslash    = "\\",
                    bar          = "|",
                    semicolon    = ";",
                    colon        = ":",
                    quoteright   = "'",
                    quotedbl     = "\"",
                    comma        = ",",
                    less         = "<",
                    period       = ".",
                    greater      = ">",
                    slash        = "/",
                    question     = "?",
                    quoteleft    = "`",
                    asciitilde   = "~",
                    {
                      dispose(win)
                      stop(
                        "Sorry, the character ", 
                        sQuote(h$key), 
                        " is not yet supported",
                        call. = FALSE
                      )
                    }
                  )
                } else h$key
                pw <<- paste0(substring(pw, 1, posn), x, substring(pw, posn + 1))
                posn <<- posn + 1
                set_stars()
              }
            )
          }
        )
        visible(win) <- TRUE
        focus(win) <- TRUE
        focus(txt) <- TRUE
      },
      set_stars = function()
      {      
        svalue(txt) <- paste0(rep.int("*", nchar(pw)), collapse = "")
      }
    )
  )
)

enterPassword <- function()
{
  passwordEntry <- passwordEntryFactory$new()
  repeat{
    if(!isExtant(passwordEntry$win)) return(passwordEntry$pw)
    Sys.sleep(0.05)
  }
}

Usage is simply enterPassword(). It's pretty gross code though since you have to manually track the position of the cursor, and you can break it by clicking to a different position using the mouse. Also, I have no idea how unicode characters behave. Having a lower-level implementation would be better.

jverzani commented 9 years ago

Sorry, late to respond, but you should be able to set visible(edit_widget) <- FALSE to use a masking character. Probably not the best use of visible which has another generic meaning, but made some sense at the time...