Skip to content

Commit

Permalink
Fix teampoltergeist#263 HTTP Basic Authentication
Browse files Browse the repository at this point in the history
* Use strip on Authorize header otherwise it forms bad request
* Combine both methods settings and manually set header
  • Loading branch information
route committed Oct 10, 2013
1 parent 8afd1cf commit fc7cc73
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 26 deletions.
17 changes: 3 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ and the following optional features:
* `page.status_code`
* `page.response_headers`
* `page.save_screenshot`
* `page.render_base64`
* `page.scroll_to`
* `page.driver.render_base64(format, options)`
* `page.driver.scroll_to(left, top)`
* `page.driver.basic_authorize(user, password)`
* cookie handling
* drag-and-drop
* basic http authentication

There are some additional features:

Expand Down Expand Up @@ -235,17 +235,6 @@ page.within_window fb_popup do
end
```

### Basic HTTP authentication ###

This method can be used for basic authentication:

``` ruby
page.driver.basic_authorize('username', 'password')
```

It actually appends `Authorize` header to bunch of your headers, so since you've
set it don't try to use `headers=` which will overwrite it.


## Customization ##

Expand Down
4 changes: 4 additions & 0 deletions lib/capybara/poltergeist/browser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ def cookies_enabled=(flag)
command 'cookies_enabled', !!flag
end

def set_http_auth(user, password)
command 'set_http_auth', user, password
end

def js_errors=(val)
command 'set_js_errors', !!val
end
Expand Down
4 changes: 4 additions & 0 deletions lib/capybara/poltergeist/client/browser.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ class Poltergeist.Browser
phantom.cookiesEnabled = flag
this.sendResponse(true)

set_http_auth: (user, password) ->
@page.setHttpAuth(user, password)
this.sendResponse(true)

set_js_errors: (value) ->
@js_errors = value
this.sendResponse(true)
Expand Down
5 changes: 5 additions & 0 deletions lib/capybara/poltergeist/client/compiled/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,11 @@ Poltergeist.Browser = (function() {
return this.sendResponse(true);
};

Browser.prototype.set_http_auth = function(user, password) {
this.page.setHttpAuth(user, password);
return this.sendResponse(true);
};

Browser.prototype.set_js_errors = function(value) {
this.js_errors = value;
return this.sendResponse(true);
Expand Down
5 changes: 5 additions & 0 deletions lib/capybara/poltergeist/client/compiled/web_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ Poltergeist.WebPage = (function() {
}
};

WebPage.prototype.setHttpAuth = function(user, password) {
this["native"].settings.userName = user;
return this["native"].settings.password = password;
};

WebPage.prototype.networkTraffic = function() {
return this._networkTraffic;
};
Expand Down
4 changes: 4 additions & 0 deletions lib/capybara/poltergeist/client/web_page.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class Poltergeist.WebPage
@_statusCode = response.status
@_responseHeaders = response.headers

setHttpAuth: (user, password) ->
@native.settings.userName = user
@native.settings.password = password

networkTraffic: ->
@_networkTraffic

Expand Down
10 changes: 7 additions & 3 deletions lib/capybara/poltergeist/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,14 @@ def cookies_enabled=(flag)
browser.cookies_enabled = flag
end

# Since PhantomJS doesn't send `Authorize` header with POST
# request at all, it's better to set header manually.
# * PhantomJS with set settings doesn't send `Authorize` on POST request
# * With manually set header PhantomJS makes next request with
# `Authorization: Basic Og==` header when settings are empty and the
# response was `401 Unauthorized` (which means Base64.encode64(':')).
# Combining both methods to reach proper behavior.
def basic_authorize(user, password)
credentials = ["#{user}:#{password}"].pack('m*')
browser.set_http_auth(user, password)
credentials = ["#{user}:#{password}"].pack('m*').strip
add_header('Authorization', "Basic #{credentials}")
end

Expand Down
33 changes: 31 additions & 2 deletions spec/integration/driver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -622,21 +622,50 @@ def create_screenshot(file, *args)
end

context 'basic http authentication' do
it 'does not set header' do
it 'denies without credentials' do
@session.visit '/poltergeist/basic_auth'

expect(@session.status_code).to eq(401)
expect(@session).not_to have_content('Welcome, authenticated client')
end

it 'sets header' do
it 'allows with given credentials' do
@driver.basic_authorize('login', 'pass')

@session.visit '/poltergeist/basic_auth'

expect(@session.status_code).to eq(200)
expect(@session).to have_content('Welcome, authenticated client')
end

it 'allows even overwriting headers' do
@driver.basic_authorize('login', 'pass')
@driver.headers = [{ 'Poltergeist' => 'true' }]

@session.visit '/poltergeist/basic_auth'

expect(@session.status_code).to eq(200)
expect(@session).to have_content('Welcome, authenticated client')
end

it 'denies with wrong credentials' do
@driver.basic_authorize('user', 'pass!')

@session.visit '/poltergeist/basic_auth'

expect(@session.status_code).to eq(401)
expect(@session).not_to have_content('Welcome, authenticated client')
end

it 'allows on POST request' do
@driver.basic_authorize('login', 'pass')

@session.visit '/poltergeist/basic_auth'
@session.click_button('Submit')

expect(@session.status_code).to eq(200)
expect(@session).to have_content('Authorized POST request')
end
end
end
end
27 changes: 20 additions & 7 deletions spec/support/test_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ class TestApp
POLTERGEIST_VIEWS = File.dirname(__FILE__) + "/views"
POLTERGEIST_PUBLIC = File.dirname(__FILE__) + "/public"

helpers do
def requires_credentials(login, password)
return if authorized?(login, password)
headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
halt 401, "Not authorized\n"
end

def authorized?(login, password)
@auth ||= Rack::Auth::Basic::Request.new(request.env)
@auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == [login, password]
end
end

get '/poltergeist/test.js' do
File.read("#{POLTERGEIST_PUBLIC}/test.js")
end
Expand Down Expand Up @@ -42,13 +55,13 @@ class TestApp
end

get '/poltergeist/basic_auth' do
auth = Rack::Auth::Basic::Request.new(request.env)
if auth.provided? and auth.basic? and auth.credentials and auth.credentials == ['login', 'pass']
'Welcome, authenticated client'
else
headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
halt 401, "Not authorized\n"
end
requires_credentials('login', 'pass')
render_view :basic_auth
end

post '/poltergeist/post_basic_auth' do
requires_credentials('login', 'pass')
'Authorized POST request'
end

get '/poltergeist/:view' do |view|
Expand Down
5 changes: 5 additions & 0 deletions spec/support/views/basic_auth.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Welcome, authenticated client

<form name="input" action="/poltergeist/post_basic_auth" method="post">
<input type="submit" value="Submit">
</form>

0 comments on commit fc7cc73

Please sign in to comment.