watirfirewatir

How to do Basic Authentication using FireWatir on Ubuntu Linux?


I'm trying to use FireWatir (1.6.5) to access a site using Basic Authentication and I've been unable to find a solution that works on Firefox in Linux. Does FireWatir 1.6.5 support Basic Authentication on Linux? I've been searching the web for 2 days and can't get a straight answer anywhere as to how to do this.

The only thread I found that seemed helpful was this one ( http://groups.google.com/group/watir-general/browse_thread/thread/d8ab9a177d282ce4/fc1bf2319fb387d8?lnk=gst&q=basic+authentication#fc1bf2319fb387d8).

Aedorn Varanis says " Angrez's fork had the solution so I'm using that now. Thanks Angrez, works perfectly!", but he doesn't mention what he did to get things working.

Initially I tried to bypass the authentication dialog box by using:

browser.goto('http://admin:password@172.20.1.1')

However, this generates a "Confirm" dialog which says:

"You are about to log in to the site "172.20.1.1" with the username "admin"." [Cancel, OK]

This dialog blocks, and the goto call won't return until I click "OK".

Then I tried adding:

browser.startClicker("ok") browser.goto('http://admin:password@172.20.1.1')

But this ALSO generates the same "Confirm" dialog.

I tested out the startClicker functionality using the unit test /var/ lib/gems/1.8/gems/firewatir-1.6.5/unittests/html/JavascriptClick.html and it worked fine, which makes me think that using the startClicker method is NOT the correct way to take care of the Confirm dialog.

Anybody else found a way to get Basic Auth to work, or how to click the OK on the confirm dialog? I'm at my wits end...


Solution

  • With help from Aedorn Varanis I've got things working on Firefox in Linux.

    Aedorn sent me a "logon" method which issues a jssh command that checks for an "Authentication Required" dialog and if it exists, fills in the username/password and submits the dialog.

    I've copied and pasted what he sent me below:

    You use a method that looks like this:

    def logon(username, password, wait=3)
            jssh_command = "var length = getWindows().length; var win;var found=false; for(var i = 0; i < length; i++) { win = getWindows()[i]; if(win.document.title == \"Authentication Required\") { found = true; break; }} if(found) { var jsdocument = win.document; var dialog = jsdocument.getElementsByTagName(\"dialog\")[0];"
            jssh_command << " jsdocument.getElementsByTagName(\"textbox\")[0].value = \"#{username}\";"
            jssh_command << " jsdocument.getElementsByTagName(\"textbox\")[1].value = \"#{password}\";"
            jssh_command << " dialog.getButton(\"accept\").click(); }\n"
            sleep(wait)
            $jssh_socket.send(jssh_command,0)
            read_socket()
            wait()
    end
    

    Then you can call it within its own thread just before going to the site with the login requirement:

    Thread.new { logon(user, pass) }
    @ff.goto("http://some_url.com")
    sleep 3
    

    Increase the wait and sleep time if the page takes awhile to load. If your main process tries to run while the command is being sent through the JSSH socket, it will stall and sit there forever until killed. Also, there's no real way to detect if the authentication window comes up. That means you need to make sure it always works the same way every time, or it, too, causes problems. Finally, the method will always have to be in another thread, because once the authentication window comes up, it stops all other processing until it goes away. Other than that, it works.

    From this, I was able to subclass the FireWatir::Firefox class with a new Browser class which supports a "credentials=" method just like the Celerity::Browser does. So, just like using celerity, you can do:

    require 'browser'
    browser = Browser.new
    browser.credentials = 'user:pass'
    browser.goto('http://some.basic.auth.url')
    

    This will automatically fill in the Basic Auth dialog and log you into the site.

    I've posted the contents of my browser.rb file below (notice this works in ruby+firewatir and jruby+celerity in linux):

    ENGINE = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
    if ENGINE == 'ruby'
      require 'firewatir'
    
      class Browser < FireWatir::Firefox
    
        def initialize(options={})
          super(options)
          @username = nil
          @password = nil
        end
    
        def credentials=(string)
          username, password = string.split(":")
          if username.nil? or password.nil?
            raise "Invalid credentials: #{string})"
          else
            @username = username
            @password = password
          end
        end
    
        def goto(url, wait=3)
          if @username.nil? and @password.nil?
            return super(url)
          else
            t = Thread.new { logon(@username, @password, wait) }
            result = super(url)
            t.join
            return result
          end
        end
    
        private
    
        def logon(username, password, wait)
          jssh_command = "
            var length = getWindows().length;
            var win;
            var found = false;
            for (var i = 0; i < length; i++) {
              win = getWindows()[i];
              if(win.document.title == \"Authentication Required\") {
                found = true;
                break;
              }
            }
            if (found) {
              var jsdocument = win.document;
              var dialog = jsdocument.getElementsByTagName(\"dialog\")[0];
              jsdocument.getElementsByTagName(\"textbox\")[0].value = \"#{username}\";
              jsdocument.getElementsByTagName(\"textbox\")[1].value = \"#{password}\";
              dialog.getButton(\"accept\").click();
            }
          \n"
          sleep(wait)
          $jssh_socket.send(jssh_command,0)
          read_socket()
        end
      end
    elsif ENGINE == 'jruby'
      require 'celerity'
      class Browser < Celerity::Browser; end
    else
      raise "Ruby ENGINE '#{ENGINE}' not supported."
    end