Skip to content
This repository has been archived by the owner on Mar 3, 2020. It is now read-only.

Commit

Permalink
Added browser.authenticate for http basic authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
halogenandtoast committed Jun 15, 2012
1 parent 6535c1b commit 5b5067f
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/capybara/driver/webkit/browser.rb
Expand Up @@ -6,6 +6,10 @@ def initialize(connection)
@connection = connection
end

def authenticate(username, password)
command("Authenticate", username, password)
end

def visit(url)
command "Visit", url
end
Expand Down
27 changes: 27 additions & 0 deletions spec/driver_spec.rb
@@ -1,5 +1,6 @@
require 'spec_helper'
require 'capybara/driver/webkit'
require 'base64'

describe Capybara::Driver::Webkit do
subject { Capybara::Driver::Webkit.new(@app, :browser => $webkit_browser) }
Expand Down Expand Up @@ -1711,4 +1712,30 @@ def which_for(character)
end.to raise_error(Capybara::Driver::Webkit::WebkitInvalidResponseError)
end
end

describe "basic auth" do
before(:all) do
@app = lambda do |env|
if env["REQUEST_PATH"] == "/hello/world"
[200, {"Content-Type" => "text/html", "Content-Length" => "0"}, [""]]
else
if env["HTTP_AUTHORIZATION"]
header = env["HTTP_AUTHORIZATION"]
[200, {"Content-Type" => "text/html", "Content-Length" => header.length.to_s}, [header]]
else
html = "401 Unauthorized."
[401,
{"Content-Type" => "text/html", "Content-Length" => html.length.to_s, "WWW-Authenticate" => 'Basic realm="Secure Area"'},
[html]]
end
end
end
end

it "can authenticate a request" do
subject.browser.authenticate('user', 'password')
subject.visit("/")
subject.body.should include("Basic "+Base64.encode64("user:password").strip)
end
end
end
18 changes: 18 additions & 0 deletions src/Authenticate.cpp
@@ -0,0 +1,18 @@
#include "Authenticate.h"
#include "WebPage.h"
#include "NetworkAccessManager.h"

Authenticate::Authenticate(WebPageManager *manager, QStringList &arguments, QObject *parent) : Command(manager, arguments, parent) {
}

void Authenticate::start() {
QString username = arguments()[0];
QString password = arguments()[1];

NetworkAccessManager* networkAccessManager = qobject_cast<NetworkAccessManager*>(page()->networkAccessManager());
networkAccessManager->setUserName(username);
networkAccessManager->setPassword(password);

emit finished(new Response(true));
}

12 changes: 12 additions & 0 deletions src/Authenticate.h
@@ -0,0 +1,12 @@
#include "Command.h"

class WebPage;

class Authenticate : public Command {
Q_OBJECT

public:
Authenticate(WebPageManager *manager, QStringList &arguments, QObject *parent = 0);
virtual void start();
};

1 change: 1 addition & 0 deletions src/CommandFactory.cpp
Expand Up @@ -29,6 +29,7 @@
#include "GetWindowHandles.h"
#include "GetWindowHandle.h"
#include "WebPageManager.h"
#include "Authenticate.h"

CommandFactory::CommandFactory(WebPageManager *manager, QObject *parent) : QObject(parent) {
m_manager = manager;
Expand Down
16 changes: 16 additions & 0 deletions src/NetworkAccessManager.cpp
@@ -1,9 +1,11 @@
#include "NetworkAccessManager.h"
#include "WebPage.h"
#include <iostream>
#include <fstream>


NetworkAccessManager::NetworkAccessManager(QObject *parent):QNetworkAccessManager(parent) {
connect(this, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*,QAuthenticator*)));
}

QNetworkReply* NetworkAccessManager::createRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, QIODevice * outgoingData = 0) {
Expand All @@ -27,3 +29,17 @@ void NetworkAccessManager::resetHeaders() {
m_headers.clear();
};

void NetworkAccessManager::setUserName(const QString &userName) {
m_userName = userName;
}

void NetworkAccessManager::setPassword(const QString &password) {
m_password = password;
}


void NetworkAccessManager::provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) {
Q_UNUSED(reply);
authenticator->setUser(m_userName);
authenticator->setPassword(m_password);
}
7 changes: 7 additions & 0 deletions src/NetworkAccessManager.h
Expand Up @@ -10,10 +10,17 @@ class NetworkAccessManager : public QNetworkAccessManager {
NetworkAccessManager(QObject *parent = 0);
void addHeader(QString key, QString value);
void resetHeaders();
void setUserName(const QString &userName);
void setPassword(const QString &password);

protected:
QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice * outgoingData);
QString m_userName;
QString m_password;

private:
QHash<QString, QString> m_headers;

private slots:
void provideAuthentication(QNetworkReply *reply, QAuthenticator *authenticator);
};
1 change: 1 addition & 0 deletions src/find_command.h
Expand Up @@ -30,3 +30,4 @@ CHECK_COMMAND(SetSkipImageLoading)
CHECK_COMMAND(WindowFocus)
CHECK_COMMAND(GetWindowHandles)
CHECK_COMMAND(GetWindowHandle)
CHECK_COMMAND(Authenticate)
2 changes: 2 additions & 0 deletions src/webkit_server.pro
Expand Up @@ -2,6 +2,7 @@ TEMPLATE = app
TARGET = webkit_server
DESTDIR = .
HEADERS = \
Authenticate.h \
IgnoreSslErrors.h \
ResizeWindow.h \
CurrentUrl.h \
Expand Down Expand Up @@ -45,6 +46,7 @@ HEADERS = \
GetWindowHandle.h \

SOURCES = \
Authenticate.cpp \
IgnoreSslErrors.cpp \
ResizeWindow.cpp \
CurrentUrl.cpp \
Expand Down

5 comments on commit 5b5067f

@samwgoldman
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will setting authentication persist for an entire test run, or will capybara-webkit reset it for each example? Can I unauthenticate? If the challenge fails, what happens?

@halogenandtoast
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently I don't think it's being reset so it will persist. No you cannot unauthenticate with this commit. If the challenge fails it up to the application to return a response so it depends on what type of response your app will return. The typical response to return on a failed authentication is a 403, returning a 401 will cause an infinite loop since that would be asking for authentication again. I currently don't have a guard in for such a scenario.

@halogenandtoast
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the question of unauthenticating... how would you unauthenticate using a normal browser... that doesn't seem like a concern for an integration test.

@halogenandtoast
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In between tests it will be reset. You may however see the following header:

HTTP_AUTHORIZATION: Basic Og==

Which is what would happen if you tried to authorize with an empty username and password.

@sweatpantsninja
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does one access the browser object? I'm seeing this error
undefined local variable or method `browser' for #RSpec::Core::ExampleGroup::Nested_2::Nested_2:0xbfa62ec

for this code https://gist.github.com/2963178

Please sign in to comment.