Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add Fakeweb to test HTTP requests and break out the Resourceful backend.

  • Loading branch information...
commit ee3c109fde4ba8f669f2cbab6e537a437c87813b 1 parent 37a7feb
@wycats authored
View
2  Gemfile
@@ -5,4 +5,6 @@ gem "resourceful"
group :test do
gem "rspec", "2.0.0.beta.4"
+ gem "randexp"
+ gem "fakeweb"
end
View
22 Gemfile.lock
@@ -1,9 +1,17 @@
---
dependencies:
+ fakeweb:
+ group:
+ - :test
+ version: ">= 0"
rspec:
group:
- :test
version: = 2.0.0.beta.4
+ randexp:
+ group:
+ - :test
+ version: ">= 0"
resourceful:
group:
- :default
@@ -13,14 +21,26 @@ dependencies:
- :default
version: ">= 0"
specs:
+- ZenTest:
+ version: 4.2.1
+- RubyInline:
+ version: 3.8.4
+- sexp_processor:
+ version: 3.0.3
+- ParseTree:
+ version: 3.0.4
- addressable:
version: 2.1.1
+- fakeweb:
+ version: 1.2.8
- httpauth:
version: "0.1"
- nokogiri:
version: 1.4.1
- options:
version: 2.1.1
+- randexp:
+ version: 0.1.5
- resourceful:
version: 1.0.1
- rspec-core:
@@ -31,7 +51,7 @@ specs:
version: 2.0.0.beta.4
- rspec:
version: 2.0.0.beta.4
-hash: a43d56f8d314697489de4c828fd52b7f4bdfc00c
+hash: a8020b14fc570f9a3b5bbf74cf62cab4996fa988
sources:
- Rubygems:
uri: http://gemcutter.org
View
43 lib/alexandria.rb
@@ -1,5 +1,6 @@
require "resourceful"
require "alexandria/resourceful_ext"
+require "alexandria/resourceful_backend"
require "alexandria/constants"
# Alexandria is a library for connecting to APIs that work
@@ -16,15 +17,16 @@
# around the raw data access.
class Alexandria
- AUTH_URL = "https://www.google.com/accounts/ClientLogin"
- CONTENT_TYPE = "application/x-www-form-urlencoded"
-
# Create a new instance of the Alexandria connector.
#
# @param [String] user the username of the Google account
# @param [String] password the password of the Google account
- def initialize(user, password)
- @user, @password = user, password
+ # @param [optional, Object] backend the backend to use to
+ # connect to the Google service. By default, this uses
+ # the ResourcefulBackend, which uses the resourceful
+ # library to connect to Google over HTTP.
+ def initialize(user, password, backend = ResourcefulBackend.new)
+ @user, @password, @backend = user, password, backend
@tokens = {}
end
@@ -33,7 +35,7 @@ def initialize(user, password)
# a second time with the same service will always return
# the same token.
#
- # @param [String, Symbol] service the name of the service.
+ # @providedaram [String, Symbol] service the name of the service.
# If a String is provided, it must be a valid internal
# Google service name. If a Symbol is provided, it must
# be a valid service key. Alexandria defines the valid
@@ -45,31 +47,14 @@ def token_for(service)
service = Alexandria::ServiceNames[service]
@tokens[service] ||= begin
- data = body_parameters(
- :accountType => "HOSTED_OR_GOOGLE",
- :Email => @user,
- :Passwd => @password,
- :service => service,
- :source => "alexandria"
+ @backend.authenticate(
+ "accountType" => "HOSTED_OR_GOOGLE",
+ "Email" => @user,
+ "Passwd" => @password,
+ "service" => service,
+ "source" => "alexandria"
)
-
- http = Resourceful::HttpAccessor.new
- resource = http.resource(AUTH_URL)
- response = resource.post(data, :content_type => CONTENT_TYPE)
- response.body.match(/^Auth=([^\n]*)/)[1]
end
- rescue Resourceful::UnsuccessfulHttpRequestError => e
- response = e.http_response.body
- code = response.match(/^Error=([^\n]*)/)[1]
- raise AuthenticationFailure.new(code)
- end
-
-private
-
- def body_parameters(hash)
- uri = Addressable::URI.new
- uri.query_values = hash
- uri.query
end
end
View
4 lib/alexandria/constants.rb
@@ -12,12 +12,12 @@ def initialize(code)
def message
"Google responded to your authentication request with the " \
- "code #{@code}, which is describes as `#{@error}`"
+ "code #{@code}, which it describes as `#{@error}`"
end
end
module FailureCodes
- BadAuthentication = "The login request used a username or password that is not recognized. NOTE: This may also result from having specified an invalid service"
+ BadAuthentication = "The login request used a username or password that is not recognized."
NotVerified = "The account email address has not been verified. The user will need to access their Google account directly to resolve the issue before logging in using a non-Google application."
TermsNotAgreed = "The user has not agreed to terms. The user will need to access their Google account directly to resolve the issue before logging in using a non-Google application."
CaptchaRequired = "A CAPTCHA is required. (A response with this error code will also contain an image URL and a CAPTCHA token.)"
View
53 lib/alexandria/resourceful_backend.rb
@@ -0,0 +1,53 @@
+class Alexandria
+ class ResourcefulBackend
+ CONTENT_TYPE = "application/x-www-form-urlencoded"
+ AUTH_URL = "https://www.google.com/accounts/ClientLogin"
+
+ # Instantiate a new Resourceful backend.
+ #
+ # @param [Object] http an object that looks like a
+ # Resourceful HttpAccessor. It defaults to an instance
+ # of Resourceful::HttpAccessor
+ def initialize(http = Resourceful::HttpAccessor.new)
+ @http = http
+ end
+
+ # Authenticate with a set of parameters.
+ #
+ # @param [Hash] params The parameters to send to Google
+ # @option params [String] accountType
+ # "GOOGLE", "HOSTED", or "HOSTED_OR_GOOGLE"
+ # @option params [String] Email An email address or
+ # Google ID
+ # @option params [String] Passwd The user's password
+ # @option params [String] service The service to
+ # authenticate for.
+ # @option params [String] source A short string
+ # identifying the application
+ #
+ # @todo Add support for logintoken and logincaptcha
+ # parameters for CAPTCHA challenges
+ #
+ # @todo Handle other possible exceptions
+ def authenticate(params)
+ data = body_parameters(params)
+
+ resource = @http.resource(AUTH_URL)
+ response = resource.post(data, :content_type => CONTENT_TYPE)
+ response.body.match(/^Auth=([^\n]*)/)[1]
+ rescue Resourceful::UnsuccessfulHttpRequestError => e
+ response = e.http_response.body
+ code = response.match(/^Error=([^\n]*)/)[1]
+ raise AuthenticationFailure.new(code)
+ end
+
+ private
+
+ def body_parameters(hash)
+ uri = Addressable::URI.new
+ uri.query_values = hash
+ uri.query
+ end
+
+ end
+end
View
40 spec/resourceful_backend_spec.rb
@@ -0,0 +1,40 @@
+require "spec_helper"
+require "fakeweb"
+
+describe "With the Alexandria::ResourcefulBackend" do
+ before do |example|
+ backend_klass = Alexandria::ResourcefulBackend
+ url = backend_klass::AUTH_URL
+
+ @backend = backend_klass.new
+
+ p example.running_example.class
+
+ if example.running_example.metadata[:valid]
+ FakeWeb.register_uri(url,
+ :body => "Auth=#{/\w{224}/.gen}", :status => 200)
+ else
+ FakeWeb.register_uri(url,
+ :body => "Error=BadAuthentication", :status => 403)
+ end
+ end
+
+ after do
+ FakeWeb.clean_registry
+ end
+
+ it "using valid credentials, it returns a token", :valid => true do
+ response = @backend.authenticate(Alexandria::TestBackend.valid_params)
+ response.should be_kind_of(String)
+ end
+
+ it "using invalid credentials, it raises", :valid => false do
+ params = Alexandria::TestBackend.valid_params.merge("Passwd" => "s3krit")
+
+ lambda do
+ @backend.authenticate(params)
+ end.should raise_error(Alexandria::AuthenticationFailure)
+ end
+
+ # it_should_behave_like "Alexandria#token_for"
+end
View
25 spec/spec_helper.rb
@@ -1 +1,24 @@
-require "alexandria"
+require "alexandria"
+require "randexp"
+
+class Alexandria
+ class TestBackend
+ def self.valid_params
+ {
+ "accountType" => "HOSTED_OR_GOOGLE",
+ "Email" => "test@example.com",
+ "Passwd" => "s3kr17",
+ "service" => "cl",
+ "source" => "alexandria"
+ }
+ end
+
+ def authenticate(params)
+ if params == self.class.valid_params
+ /\w{224}/.gen
+ else
+ raise AuthenticationFailure.new("BadAuthentication")
+ end
+ end
+ end
+end
View
23 spec/token_spec.rb
@@ -0,0 +1,23 @@
+require "spec_helper"
+
+describe "Alexandria#token_for" do
+ before do
+ @backend = Alexandria::TestBackend.new
+ end
+
+ describe "using valid credentials" do
+ it "returns a valid token" do
+ @alexandria = Alexandria.new("test@example.com", "s3kr17", @backend)
+ @alexandria.token_for(:calendar).should be_kind_of(String)
+ end
+ end
+
+ describe "using invalid credentials" do
+ it "raises an exception" do
+ @alexandria = Alexandria.new("test@example.com", "s4uce", @backend)
+ lambda do
+ @alexandria.token_for(:calendar)
+ end.should raise_error(Alexandria::AuthenticationFailure)
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.