f1api 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +5 -0
- data/Gemfile.lock +27 -0
- data/LICENSE +19 -0
- data/README.md +24 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/VERSION.yml +5 -0
- data/config/f1-oauth.yml +48 -0
- data/lib/f1api.rb +14 -0
- data/lib/f1api/activeresource/base.rb +19 -0
- data/lib/f1api/activeresource/connection.rb +39 -0
- data/lib/f1api/client.rb +62 -0
- data/lib/f1api/configuration.rb +63 -0
- data/lib/f1api/oauth.rb +54 -0
- data/lib/f1api/oauth/credentials_authentication.rb +59 -0
- data/lib/f1api/oauth/oauth_authentication.rb +72 -0
- data/test/fixtures/access_token.rb +10 -0
- data/test/fixtures/http.rb +13 -0
- data/test/fixtures/request_token.rb +10 -0
- data/test/unit/configuration_test.rb +60 -0
- data/test/unit/credentials_test.rb +59 -0
- data/test/unit/oauth_test.rb +74 -0
- metadata +185 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (3.0.3)
|
5
|
+
activesupport (= 3.0.3)
|
6
|
+
builder (~> 2.1.2)
|
7
|
+
i18n (~> 0.4)
|
8
|
+
activeresource (3.0.3)
|
9
|
+
activemodel (= 3.0.3)
|
10
|
+
activesupport (= 3.0.3)
|
11
|
+
activesupport (3.0.3)
|
12
|
+
builder (2.1.2)
|
13
|
+
i18n (0.4.2)
|
14
|
+
mocha (0.9.9)
|
15
|
+
rake
|
16
|
+
nokogiri (1.4.4)
|
17
|
+
oauth (0.4.4)
|
18
|
+
rake (0.8.7)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
activeresource
|
25
|
+
mocha
|
26
|
+
nokogiri
|
27
|
+
oauth (= 0.4.4)
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Fellowship Technologies
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Fellowship One REST API Ruby Client Library
|
2
|
+
===========================================
|
3
|
+
|
4
|
+
**NOTE**: This library is of alpha quality. It is not meant for use in production apps. It's definitely not feature complete and it may have bugs.
|
5
|
+
|
6
|
+
Introduction
|
7
|
+
------------
|
8
|
+
This library is an implementation of the Fellowship One REST API. Currently we are only fielding requests and handling the OAuth tokens. The goal is to have a library that works like ActiveResource.
|
9
|
+
|
10
|
+
Usage
|
11
|
+
-----
|
12
|
+
require 'f1api'
|
13
|
+
|
14
|
+
class Person < FellowshipOneAPI::Base
|
15
|
+
end
|
16
|
+
|
17
|
+
client.authorize!
|
18
|
+
# If using creds in YAML file:
|
19
|
+
# client.authorize! "username", "password"
|
20
|
+
|
21
|
+
Person.connect client
|
22
|
+
|
23
|
+
Person.find(12345)
|
24
|
+
Person.find("search", {:searchFor => "Dearing", :include => "communications,addresses"})
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
BASE_DIR = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
task :test do
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'mocha'
|
7
|
+
require "#{BASE_DIR}/test/fixtures/request_token.rb"
|
8
|
+
require "#{BASE_DIR}/test/fixtures/access_token.rb"
|
9
|
+
require "#{BASE_DIR}/test/fixtures/http.rb"
|
10
|
+
require "#{BASE_DIR}/lib/f1api"
|
11
|
+
Dir["#{BASE_DIR}/test/unit/*.rb"].each do |test|
|
12
|
+
require test
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
task :rdoc do
|
17
|
+
`rdoc -x 'test/*'`
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'jeweler'
|
21
|
+
Jeweler::Tasks.new do |gem|
|
22
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
23
|
+
gem.name = "f1api"
|
24
|
+
gem.homepage = "http://github.com/jessedearing/f1api"
|
25
|
+
gem.license = "MIT"
|
26
|
+
gem.summary = %Q{Consume the Fellowship One API in your apps using ActiveResource}
|
27
|
+
gem.description = %Q{Consumes the Fellowship One API in your apps using ActiveResource. Implements 2nd party credentials-based authenticaion and full OAuth implementation. }
|
28
|
+
gem.email = "jdearing@fellowshiptech.com"
|
29
|
+
gem.authors = ["Jesse Dearing"]
|
30
|
+
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
31
|
+
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
32
|
+
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
33
|
+
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
34
|
+
gem.add_runtime_dependency 'oauth', '= 0.4.4'
|
35
|
+
gem.add_development_dependency 'mocha'
|
36
|
+
gem.add_runtime_dependency 'activeresource'
|
37
|
+
end
|
38
|
+
Jeweler::RubygemsDotOrgTasks.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.9.0
|
data/VERSION.yml
ADDED
data/config/f1-oauth.yml
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
development:
|
2
|
+
consumer_key: ""
|
3
|
+
consumer_secret: ""
|
4
|
+
site_url: "https://{church_code}.staging.fellowshiponeapi.com"
|
5
|
+
# Store the church code if you are writing a 2nd Party implementation
|
6
|
+
# Otherwise, you'll want to ask for the church code from the user
|
7
|
+
church_code: ""
|
8
|
+
authentication_type: "oauth" #oauth or credentials
|
9
|
+
request_token_path: "/v1/Tokens/RequestToken"
|
10
|
+
access_token_path: "/v1/Tokens/AccessToken" #OAuth only
|
11
|
+
# OAuth authentication vars
|
12
|
+
portal_authorize_path: "/v1/PortalUser/Login"
|
13
|
+
weblink_authorize_path: "/v1/WeblinkUser/Login"
|
14
|
+
# Use the following for credentials based authentication
|
15
|
+
portal_credential_token_path: "/v1/PortalUser/AccessToken"
|
16
|
+
weblink_credential_token_path: "/v1/WeblinkUser/AccessToken"
|
17
|
+
test:
|
18
|
+
consumer_key: "123456789"
|
19
|
+
consumer_secret: "12345678-90ab-cdef-0123-4567890abcde"
|
20
|
+
site_url: "https://{church_code}.staging.fellowshiponeapi.com"
|
21
|
+
# Store the church code if you are writing a 2nd Party implementation
|
22
|
+
# Otherwise, you'll want to ask for the church code from the user
|
23
|
+
church_code: "test"
|
24
|
+
authentication_type: "oauth" #oauth or credentials
|
25
|
+
request_token_path: "/V1/Tokens/RequestToken"
|
26
|
+
access_token_path: "/V1/Tokens/AccessToken" #OAuth only
|
27
|
+
# OAuth authentication vars
|
28
|
+
portal_authorize_path: "/V1/PortalUser/Login"
|
29
|
+
weblink_authorize_path: "/V1/WeblinkUser/Login"
|
30
|
+
# Use the following for credentials based authentication
|
31
|
+
portal_credential_token_path: "/V1/PortalUser/AccessToken"
|
32
|
+
weblink_credential_token_path: "/V1/WeblinkUser/AccessToken"
|
33
|
+
production:
|
34
|
+
consumer_key: ""
|
35
|
+
consumer_secret: ""
|
36
|
+
site_url: "https://{church_code}.fellowshiponeapi.com"
|
37
|
+
# Store the church code if you are writing a 2nd Party implementation
|
38
|
+
# Otherwise, you'll want to ask for the church code from the user
|
39
|
+
church_code: ""
|
40
|
+
authentication_type: "oauth" #oauth or credentials
|
41
|
+
request_token_path: "/v1/Tokens/RequestToken"
|
42
|
+
access_token_path: "/v1/Tokens/AccessToken" #OAuth only
|
43
|
+
# OAuth authentication vars
|
44
|
+
portal_authorize_path: "/v1/PortalUser/Login"
|
45
|
+
weblink_authorize_path: "/v1/WeblinkUser/Login"
|
46
|
+
# Use the following for credentials based authentication
|
47
|
+
portal_credential_token_path: "/v1/PortalUser/AccessToken"
|
48
|
+
weblink_credential_token_path: "/v1/WeblinkUser/AccessToken"
|
data/lib/f1api.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}"
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'yaml'
|
4
|
+
require 'oauth'
|
5
|
+
require 'base64'
|
6
|
+
require 'uri'
|
7
|
+
require 'active_resource'
|
8
|
+
require "f1api/configuration"
|
9
|
+
require "f1api/oauth"
|
10
|
+
require "f1api/oauth/credentials_authentication"
|
11
|
+
require "f1api/oauth/oauth_authentication"
|
12
|
+
require "f1api/client"
|
13
|
+
require "f1api/activeresource/connection"
|
14
|
+
require "f1api/activeresource/base"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module FellowshipOneAPI # :nodoc:
|
2
|
+
# The Base class should be inherited by all model classes as it provides the facilities that the class will need
|
3
|
+
class Base < ActiveResource::Base
|
4
|
+
self.site = "#{Configuration.site_url}/v1"
|
5
|
+
# Creates a new connection
|
6
|
+
#
|
7
|
+
# ==Examples
|
8
|
+
# Person.connect(FellowshipOneAPI::Client.new)
|
9
|
+
#
|
10
|
+
# If the connection needs to be forcibly refreshed then you can pass true
|
11
|
+
# Person.connect(FellowshipOneAPI::Client.new, true)
|
12
|
+
def self.connect(client, refresh = false)
|
13
|
+
if(refresh or @connection.nil?)
|
14
|
+
@connection = Connection.new(client, client.consumer.site, format)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module FellowshipOneAPI
|
4
|
+
# Creating a wrapper for the ActiveResource::Connection class
|
5
|
+
class Connection < ActiveResource::Connection
|
6
|
+
# Pass in a new connection to the API
|
7
|
+
def initialize(f1api_connection, *args)
|
8
|
+
@f1api_connection = f1api_connection
|
9
|
+
super(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
# The request method that is passes the request through to the F1 API client
|
14
|
+
def request(method, path, *args)
|
15
|
+
if @f1api_connection == nil
|
16
|
+
super(method, path, *args)
|
17
|
+
else
|
18
|
+
response = @f1api_connection.request(method, path, *args)
|
19
|
+
|
20
|
+
if method == :get
|
21
|
+
response.body = transform_response response.body, path
|
22
|
+
end
|
23
|
+
handle_response(response)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
def transform_response(response_body, path)
|
29
|
+
n = Nokogiri::XML(response_body)
|
30
|
+
res = (n/"results")
|
31
|
+
if not (res.empty?)
|
32
|
+
resource = ((path.split '/')[2]).downcase
|
33
|
+
res[0].name = resource
|
34
|
+
end
|
35
|
+
|
36
|
+
n.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/f1api/client.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module FellowshipOneAPI
|
2
|
+
# ==The Fellowship One API client class
|
3
|
+
# Takes an HTTP request and passes it through the OAuth library which added the approperiate headers and querystring
|
4
|
+
# parameters.
|
5
|
+
# ===Examples
|
6
|
+
# [Simple client using OAuth and default YAML config values]
|
7
|
+
# client = FellowshipOneAPI::Client.new
|
8
|
+
#
|
9
|
+
# client.authorize!
|
10
|
+
#
|
11
|
+
# client.request(:get, '/v1/People/123.xml')
|
12
|
+
#
|
13
|
+
# [Using credentials based authentication (2nd party)]
|
14
|
+
# client = FellowshipOneAPI::Client.new({:auth_type => :credentials})
|
15
|
+
#
|
16
|
+
# client.authorize!("username", "password")
|
17
|
+
#
|
18
|
+
# client.request(:get, '/v1/People/123.xml')
|
19
|
+
#
|
20
|
+
# [Authenticating against weblink passing credentials]
|
21
|
+
# client = FellowshipOneAPI::Client.new({:auth_type => :credentials, :auth_against => :weblink})
|
22
|
+
#
|
23
|
+
# client.authorize("weblinkuser", "weblinkpassword")
|
24
|
+
#
|
25
|
+
# client.request(:get, '/v1/People/123.xml')
|
26
|
+
#
|
27
|
+
# [Loading a client with an existing access token]
|
28
|
+
# client = FellowshipOneAPI::Client.new({:oauth_token => "123456", :oauth_token_secret => "987654"})
|
29
|
+
#
|
30
|
+
# client.request(:get, '/v1/People/123.xml')
|
31
|
+
class Client
|
32
|
+
# Creates a new instance of a client used to connect with the Fellowship One API
|
33
|
+
# The client can be configured with the following symbols:
|
34
|
+
# [+:auth_type+] - Can be _:credentials_ or _:oauth_ (_:oauth_ is the default)
|
35
|
+
# [+:auth_against+] - Can be _:portal_ or _:weblink_ (_:portal_ is the default)
|
36
|
+
# [+:oauth_token+] - The access token
|
37
|
+
# [+:oauth_token_secret+] - The access token secret
|
38
|
+
def initialize(args = {})
|
39
|
+
args[:auth_type] ||= Configuration.authentication_type.to_sym
|
40
|
+
if args[:auth_type] == :credentials
|
41
|
+
extend OAuth::CredentialsAuthentication
|
42
|
+
else
|
43
|
+
extend OAuth::OAuthAuthentication
|
44
|
+
end
|
45
|
+
|
46
|
+
if(args[:auth_against])
|
47
|
+
load_consumer_config args[:auth_against]
|
48
|
+
else
|
49
|
+
load_consumer_config
|
50
|
+
end
|
51
|
+
|
52
|
+
if(args[:oauth_token] and args[:oauth_token_secret])
|
53
|
+
@oauth_access_token = ::OAuth::AccessToken.from_hash(@oauth_consumer, args[:oauth_token], args[:oauth_token_secret])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Passes through the request to the OAuth library to be signed and set out HTTP
|
58
|
+
def request(*args)
|
59
|
+
@oauth_access_token.request(*args)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module FellowshipOneAPI # :nodoc:
|
2
|
+
# This accesses the YAML-based F1 API config file
|
3
|
+
#
|
4
|
+
# This class was written to take rails environment variables like +RAILS_ENV+ and +Rails.root+ into account
|
5
|
+
class Configuration
|
6
|
+
# Explictly defines where the configuration file is
|
7
|
+
def self.file_path=(path)
|
8
|
+
@file_path
|
9
|
+
end
|
10
|
+
|
11
|
+
# Gets the specified key from the configuration file
|
12
|
+
# [Example] FellowshipTechAPIClient.Configuration["consumer_key"] <i># "2"</i>
|
13
|
+
#
|
14
|
+
# FellowshipTechAPIClient.Configuration["consumer_secret"] <i># "12345678-9abc-def0-1234-567890abcdef"</i>
|
15
|
+
def self.[](value)
|
16
|
+
load_yaml if @config_yaml.nil?
|
17
|
+
val = @config_yaml[self.environment][value]
|
18
|
+
# if we have the string has "{church_code}" then we'll substitute it
|
19
|
+
if val =~ /\{church_code\}/ and value != "church_code"
|
20
|
+
return val.gsub("{church_code}", self["church_code"])
|
21
|
+
end
|
22
|
+
return val
|
23
|
+
end
|
24
|
+
|
25
|
+
# Gets the current environment
|
26
|
+
def self.environment
|
27
|
+
@environment ||= "development"
|
28
|
+
@environment ||= ::Rails.env if defined? ::Rails
|
29
|
+
@environment
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set the current environment
|
33
|
+
def self.environment=(env_value)
|
34
|
+
@environment = env_value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Overridden method_missing to facilitate a more pleasing ruby-like syntax for accessing
|
38
|
+
# configuration values
|
39
|
+
def self.method_missing(name, *args, &block)
|
40
|
+
return self[name.to_s] unless self[name.to_s].nil?
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Loads the YAML file
|
47
|
+
#
|
48
|
+
# Starts by looking to see if file_path is defined then checks in current directory (.) and then your Rails.root and then the config
|
49
|
+
# directory off of the base directory of the gem
|
50
|
+
def self.load_yaml
|
51
|
+
if not @file_path.nil?
|
52
|
+
@config_yaml = YAML.load_file(@file_path)
|
53
|
+
elsif File.exists? "./f1-oauth.yml"
|
54
|
+
@config_yaml = YAML.load_file("./f1-oauth.yml")
|
55
|
+
elsif defined? ::Rails
|
56
|
+
@config_yaml = YAML.load_file("#{::Rails.root}/config/f1-oauth.yml")
|
57
|
+
else
|
58
|
+
path = File.dirname(__FILE__) + "/../../config/f1-oauth.yml"
|
59
|
+
@config_yaml = YAML.load_file(path)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/f1api/oauth.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module FellowshipOneAPI # :nodoc:
|
2
|
+
# Wrapper around the OAuth v1.0 specification using the +oauth+ gem.
|
3
|
+
#
|
4
|
+
# The Fellowship One API has two methods of authentication:
|
5
|
+
# [OAuthAuthentication] This is the default method if no method is declared. This method allows Fellowship Tech to handle
|
6
|
+
# the authentication and we redirect back to your app.
|
7
|
+
# [CredentialsAuthentication] The methods lets the consumer submit credentials and verify authentication.
|
8
|
+
module OAuth
|
9
|
+
# The OAuth consumer key.
|
10
|
+
# This will get set automatically from the YAML config file if not set explictly
|
11
|
+
attr_accessor :oauth_consumer_key
|
12
|
+
alias :consumer_key :oauth_consumer_key
|
13
|
+
alias :consumer_key= :oauth_consumer_key=
|
14
|
+
|
15
|
+
# The OAuth consumer secret.
|
16
|
+
# This will get set automatically from the YAML config file if not set explictly
|
17
|
+
attr_accessor :oauth_consumer_secret
|
18
|
+
alias :consumer_secret :oauth_consumer_secret
|
19
|
+
alias :consumer_secret= :oauth_consumer_secret=
|
20
|
+
|
21
|
+
# The OAuth access token object where all requests are made off of
|
22
|
+
attr_reader :oauth_access_token
|
23
|
+
alias :access_token :oauth_access_token
|
24
|
+
|
25
|
+
# The OAuth consumer object
|
26
|
+
attr_reader :oauth_consumer
|
27
|
+
alias :consumer :oauth_consumer
|
28
|
+
|
29
|
+
# The URI for the resource of the authenticated user
|
30
|
+
attr_reader :authenticated_user_uri
|
31
|
+
|
32
|
+
# Creates the OAuth consumer object
|
33
|
+
def load_consumer_config(type = :portal)
|
34
|
+
case type
|
35
|
+
when :portal
|
36
|
+
authorize_path = FellowshipOneAPI::Configuration.portal_authorize_path
|
37
|
+
when :weblink
|
38
|
+
authorize_path = FellowshipOneAPI::Configuration.weblink_authorize_path
|
39
|
+
end
|
40
|
+
|
41
|
+
@oauth_consumer_key ||= FellowshipOneAPI::Configuration.consumer_key
|
42
|
+
@oauth_consumer_secret ||= FellowshipOneAPI::Configuration.consumer_secret
|
43
|
+
|
44
|
+
@oauth_consumer = ::OAuth::Consumer.new(@oauth_consumer_key,
|
45
|
+
@oauth_consumer_secret,
|
46
|
+
{:site => FellowshipOneAPI::Configuration.site_url,
|
47
|
+
:request_token_path => FellowshipOneAPI::Configuration.request_token_path,
|
48
|
+
:access_token_path => FellowshipOneAPI::Configuration.access_token_path,
|
49
|
+
:authorize_path => authorize_path })
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module FellowshipOneAPI # :nodoc:
|
2
|
+
module OAuth
|
3
|
+
# Implements the Credentials method of authentication. You must manage the credentials.
|
4
|
+
module CredentialsAuthentication
|
5
|
+
include OAuth
|
6
|
+
# Authorizes a user
|
7
|
+
# +username+:: The username of the user
|
8
|
+
# +password+:: The password of the user
|
9
|
+
# +type+:: Can be :portal or :weblink based on which credentials you want to authenticate against
|
10
|
+
# Returns the URI for the authenticated user
|
11
|
+
def authorize!(username, password, type = :portal)
|
12
|
+
load_consumer_config(type) if @oauth_consumer.nil?
|
13
|
+
|
14
|
+
@oauth_request = @oauth_consumer.get_request_token
|
15
|
+
cred = URI.encode(Base64.encode64("#{username} #{password}"))
|
16
|
+
|
17
|
+
case type
|
18
|
+
when :portal
|
19
|
+
auth_url = FellowshipOneAPI::Configuration.portal_credential_token_path
|
20
|
+
when :weblink
|
21
|
+
auth_url = FellowshipOneAPI::Configuration.weblink_credential_token_path
|
22
|
+
end
|
23
|
+
|
24
|
+
response = @oauth_consumer.request(:post, auth_url, nil, {}, "ec=#{cred}", {'Content-Type' => 'application/x-www-form-urlencoded'})
|
25
|
+
|
26
|
+
handle_response_code(response)
|
27
|
+
|
28
|
+
# Gettting the URI of the authenticated user
|
29
|
+
@authenticated_user_uri = response["Content-Location"]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def handle_response_code(response)
|
34
|
+
case response.code.to_i
|
35
|
+
when (200..299)
|
36
|
+
@oauth_access_token = ::OAuth::AccessToken.from_hash(@oauth_consumer, parse_access_token(response.body))
|
37
|
+
when (300..399)
|
38
|
+
# redirect
|
39
|
+
# TODO: actually redirect instead of throwing error
|
40
|
+
response.error!
|
41
|
+
when (400..499)
|
42
|
+
raise OAuth::Unauthorized, response
|
43
|
+
else
|
44
|
+
response.error!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Parse returned OAuth access token key/secret pair
|
49
|
+
def parse_access_token(response)
|
50
|
+
oauth_hash = {}
|
51
|
+
response.split('&').each do |val|
|
52
|
+
kv = val.split('=')
|
53
|
+
oauth_hash.merge!({kv[0].to_sym => kv[1]})
|
54
|
+
end
|
55
|
+
oauth_hash
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module FellowshipOneAPI # :nodoc:
|
2
|
+
module OAuth
|
3
|
+
# Implements the pure OAuth method of authentication. This allows the Fellowship One API to
|
4
|
+
# manage the authentication process.
|
5
|
+
module OAuthAuthentication
|
6
|
+
include OAuth
|
7
|
+
# The OAuth request object
|
8
|
+
attr_reader :oauth_request
|
9
|
+
|
10
|
+
# The OAuth authorization URI
|
11
|
+
attr_reader :oauth_authorize_url
|
12
|
+
alias :authorize_url :oauth_authorize_url
|
13
|
+
|
14
|
+
# Gets a new request token and return the authorize URI
|
15
|
+
# +type+:: Can be :portal or :weblink based on which credentials you want to authenticate against
|
16
|
+
def authorize!(type = :portal)
|
17
|
+
load_consumer_config(type) if oauth_consumer.nil?
|
18
|
+
|
19
|
+
@oauth_request = oauth_consumer.get_request_token
|
20
|
+
@oauth_authorize_url = oauth_request.authorize_url
|
21
|
+
|
22
|
+
@oauth_consumer.instance_eval do
|
23
|
+
# The token request reponse is scoped only in the token_request method, but I need to get access to the response
|
24
|
+
# headers so that I can pull back the Content-Location header and get the authenticated user URI
|
25
|
+
def token_request(http_method, path, token = nil, request_options = {}, *arguments)
|
26
|
+
@tr_response = request(http_method, path, token, request_options, *arguments)
|
27
|
+
case @tr_response.code.to_i
|
28
|
+
|
29
|
+
when (200..299)
|
30
|
+
if block_given?
|
31
|
+
yield @tr_response.body
|
32
|
+
else
|
33
|
+
# symbolize keys
|
34
|
+
# TODO this could be considered unexpected behavior; symbols or not?
|
35
|
+
# TODO this also drops subsequent values from multi-valued keys
|
36
|
+
CGI.parse(@tr_response.body).inject({}) do |h,(k,v)|
|
37
|
+
h[k.strip.to_sym] = v.first
|
38
|
+
h[k.strip] = v.first
|
39
|
+
h
|
40
|
+
end
|
41
|
+
end
|
42
|
+
when (300..399)
|
43
|
+
# this is a redirect
|
44
|
+
@tr_response.error!
|
45
|
+
when (400..499)
|
46
|
+
raise OAuth::Unauthorized, @tr_response
|
47
|
+
else
|
48
|
+
@tr_response.error!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# The HTTP response from token_request
|
53
|
+
def token_request_response
|
54
|
+
@tr_response
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
oauth_authorize_url
|
59
|
+
end
|
60
|
+
|
61
|
+
# After a the user has been authenticated then we use the access token to access protected resources in the API.
|
62
|
+
# Since the authentication has taken place, we now know about the user that authenticated and
|
63
|
+
# have a URI to the record of that user.
|
64
|
+
#
|
65
|
+
# The URI for the authenticated user is returned.
|
66
|
+
def get_access_token
|
67
|
+
@oauth_access_token = oauth_request.get_access_token
|
68
|
+
@authenticated_user_uri = oauth_consumer.token_request_response["Content-Location"]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class OAuthTest < Test::Unit::TestCase
|
2
|
+
include FellowshipOneAPI
|
3
|
+
|
4
|
+
def setup
|
5
|
+
Configuration.environment = "test"
|
6
|
+
Configuration.file_path = "#{File.dirname(__FILE__)}/../../config/f1-oauth.yml"
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_method_missing
|
10
|
+
assert_raise NoMethodError do
|
11
|
+
Configuration.asdf
|
12
|
+
end
|
13
|
+
|
14
|
+
assert_nothing_raised do
|
15
|
+
Configuration.consumer_key
|
16
|
+
end
|
17
|
+
|
18
|
+
assert_equal Configuration["consumer_key"], Configuration.consumer_key
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_consumer_key
|
22
|
+
assert_equal("123456789", Configuration.consumer_key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_consumer_secret
|
26
|
+
assert_equal("12345678-90ab-cdef-0123-4567890abcde", Configuration.consumer_secret)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_site_url
|
30
|
+
assert_equal("https://test.staging.fellowshiponeapi.com", Configuration.site_url)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_church_code
|
34
|
+
assert_equal("test", Configuration.church_code)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_request_token_path
|
38
|
+
assert_equal("/V1/Tokens/RequestToken", Configuration.request_token_path)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_access_token_path
|
42
|
+
assert_equal("/V1/Tokens/AccessToken", Configuration.access_token_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_portal_authorize_path
|
46
|
+
assert_equal("/V1/PortalUser/Login", Configuration.portal_authorize_path)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_weblink_authorize_path
|
50
|
+
assert_equal("/V1/WeblinkUser/Login", Configuration.weblink_authorize_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_portal_credential_token_path
|
54
|
+
assert_equal("/V1/PortalUser/AccessToken", Configuration.portal_credential_token_path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_weblink_credential_token_path
|
58
|
+
assert_equal("/V1/WeblinkUser/AccessToken", Configuration.weblink_credential_token_path)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class CredentialsAuthenticationTest
|
2
|
+
include FellowshipOneAPI::OAuth::CredentialsAuthentication
|
3
|
+
attr_accessor :oauth_consumer
|
4
|
+
end
|
5
|
+
|
6
|
+
class CredentialsTest < Test::Unit::TestCase
|
7
|
+
include FellowshipOneAPI
|
8
|
+
def setup
|
9
|
+
Configuration.environment = "test"
|
10
|
+
Configuration.file_path = "#{File.dirname(__FILE__)}/../../config/f1-oauth.yml"
|
11
|
+
|
12
|
+
@test_username = "testuser"
|
13
|
+
@test_password = "testpass"
|
14
|
+
|
15
|
+
@cred_test = CredentialsAuthenticationTest.new
|
16
|
+
@cred_test.load_consumer_config
|
17
|
+
@mocked_access_token = AccessTokenFixture.get(@cred_test.oauth_consumer)
|
18
|
+
@mocked_request_token = RequestTokenFixture.get(@cred_test.oauth_consumer)
|
19
|
+
cred = URI.encode(Base64.encode64("#{@test_username} #{@test_password}"))
|
20
|
+
|
21
|
+
@mocked_user_uri = "#{Configuration.site_url}/V1/People/123456"
|
22
|
+
|
23
|
+
@cred_test.oauth_consumer.stubs(:get_request_token).returns(@mocked_request_token)
|
24
|
+
|
25
|
+
@cred_test.oauth_consumer.stubs(:request).with(:post, ::FellowshipOneAPI::Configuration.portal_credential_token_path,
|
26
|
+
nil, {}, "ec=#{cred}", {'Content-Type' => 'application/x-www-form-urlencoded'}).
|
27
|
+
returns(HttpFixture.new)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_portal_authorize!
|
31
|
+
actual = @cred_test.authorize! @test_username, @test_password
|
32
|
+
|
33
|
+
assert_equal(@mocked_user_uri, actual)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_weblink_authorize!
|
37
|
+
cred = URI.encode(Base64.encode64("#{@test_username} #{@test_password}"))
|
38
|
+
@cred_test.oauth_consumer = nil
|
39
|
+
@cred_test.load_consumer_config(:weblink)
|
40
|
+
@cred_test.oauth_consumer.expects(:get_request_token).returns(@mocked_request_token).at_least_once
|
41
|
+
@cred_test.oauth_consumer.expects(:request).with(:post, ::FellowshipOneAPI::Configuration.weblink_credential_token_path,
|
42
|
+
nil, {}, "ec=#{cred}", {'Content-Type' => 'application/x-www-form-urlencoded'}).returns(
|
43
|
+
HttpFixture.new).at_least_once
|
44
|
+
@cred_test.authorize! @test_username, @test_password, :weblink
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_get_access_token
|
48
|
+
@cred_test.authorize! @test_username, @test_password
|
49
|
+
|
50
|
+
assert_equal("access", @cred_test.access_token.token)
|
51
|
+
assert_equal("token", @cred_test.access_token.secret)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_authorized_user_uri
|
55
|
+
@cred_test.authorize! @test_username, @test_password
|
56
|
+
|
57
|
+
assert_equal(@mocked_user_uri, @cred_test.authenticated_user_uri)
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class OAuthAuthenticationTest
|
2
|
+
include FellowshipOneAPI::OAuth::OAuthAuthentication
|
3
|
+
attr_accessor :oauth_consumer
|
4
|
+
end
|
5
|
+
|
6
|
+
class OAuthTest < Test::Unit::TestCase
|
7
|
+
include FellowshipOneAPI
|
8
|
+
def setup
|
9
|
+
Configuration.environment = "test"
|
10
|
+
Configuration.file_path = "#{File.dirname(__FILE__)}/../../config/f1-oauth.yml"
|
11
|
+
|
12
|
+
@oauth_test = OAuthAuthenticationTest.new
|
13
|
+
@oauth_test.load_consumer_config
|
14
|
+
@mocked_request_token = RequestTokenFixture.get(@oauth_test.oauth_consumer)
|
15
|
+
@mocked_access_token = AccessTokenFixture.get(@oauth_test.oauth_consumer)
|
16
|
+
@oauth_test.oauth_consumer.stubs(:get_request_token).returns(@mocked_request_token)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_portal_authorize!
|
20
|
+
@oauth_test.oauth_consumer.expects(:get_request_token).returns(@mocked_request_token)
|
21
|
+
|
22
|
+
assert_equal("#{Configuration.site_url}#{Configuration.portal_authorize_path}?oauth_token=request", @oauth_test.authorize!)
|
23
|
+
|
24
|
+
assert_equal("request", @oauth_test.oauth_request.token)
|
25
|
+
assert_equal("token", @oauth_test.oauth_request.secret)
|
26
|
+
assert_equal("#{Configuration.site_url}#{Configuration.portal_authorize_path}?oauth_token=#{@oauth_test.oauth_request.token}", @oauth_test.authorize_url)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_weblink_authorize!
|
30
|
+
@oauth_test.oauth_consumer = nil
|
31
|
+
@oauth_test.load_consumer_config(:weblink)
|
32
|
+
@mocked_request_token = RequestTokenFixture.get(@oauth_test.oauth_consumer)
|
33
|
+
@oauth_test.oauth_consumer.expects(:get_request_token).returns(@mocked_request_token)
|
34
|
+
|
35
|
+
@oauth_test.authorize!(:weblink)
|
36
|
+
|
37
|
+
assert_equal("request", @oauth_test.oauth_request.token)
|
38
|
+
assert_equal("token", @oauth_test.oauth_request.secret)
|
39
|
+
assert_equal("#{Configuration.site_url}#{Configuration.weblink_authorize_path}?oauth_token=#{@oauth_test.oauth_request.token}", @oauth_test.authorize_url)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_get_access_token
|
43
|
+
@oauth_test.authorize!
|
44
|
+
|
45
|
+
@oauth_test.oauth_request.expects(:get_access_token).returns(@mocked_access_token)
|
46
|
+
@oauth_test.oauth_consumer.expects(:token_request_response).returns({"Content-Location" => ""})
|
47
|
+
@oauth_test.get_access_token
|
48
|
+
|
49
|
+
assert_equal("access", @oauth_test.access_token.token)
|
50
|
+
assert_equal("token", @oauth_test.access_token.secret)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_authorized_user_uri
|
54
|
+
mocked_user_uri = "#{Configuration.site_url}/V1/People/123456"
|
55
|
+
|
56
|
+
@oauth_test.authorize!
|
57
|
+
|
58
|
+
@oauth_test.oauth_request.expects(:get_access_token).returns(@mocked_access_token)
|
59
|
+
@oauth_test.oauth_consumer.expects(:token_request_response).returns({"Content-Location" => mocked_user_uri})
|
60
|
+
@oauth_test.get_access_token
|
61
|
+
|
62
|
+
assert_equal(mocked_user_uri, @oauth_test.authenticated_user_uri)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_change_auth_types
|
66
|
+
@oauth_test.authorize!
|
67
|
+
assert_equal("#{Configuration.site_url}#{Configuration.portal_authorize_path}", @oauth_test.oauth_consumer.authorize_url)
|
68
|
+
@oauth_test.load_consumer_config(:weblink)
|
69
|
+
@oauth_test.oauth_consumer.stubs(:get_request_token).returns(@mocked_request_token)
|
70
|
+
@oauth_test.authorize!
|
71
|
+
assert_equal("#{Configuration.site_url}#{Configuration.weblink_authorize_path}", @oauth_test.oauth_consumer.authorize_url)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: f1api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 9
|
8
|
+
- 0
|
9
|
+
version: 0.9.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jesse Dearing
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-19 00:00:00 -06:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: nokogiri
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
prerelease: false
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: oauth
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - "="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
- 4
|
43
|
+
- 4
|
44
|
+
version: 0.4.4
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: mocha
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
type: :runtime
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: *id003
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: activeresource
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: *id004
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: oauth
|
76
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - "="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
- 4
|
84
|
+
- 4
|
85
|
+
version: 0.4.4
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: *id005
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: mocha
|
91
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *id006
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: activeresource
|
104
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
type: :runtime
|
113
|
+
prerelease: false
|
114
|
+
version_requirements: *id007
|
115
|
+
description: "Consumes the Fellowship One API in your apps using ActiveResource. Implements 2nd party credentials-based authenticaion and full OAuth implementation. "
|
116
|
+
email: jdearing@fellowshiptech.com
|
117
|
+
executables: []
|
118
|
+
|
119
|
+
extensions: []
|
120
|
+
|
121
|
+
extra_rdoc_files:
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
files:
|
125
|
+
- Gemfile
|
126
|
+
- Gemfile.lock
|
127
|
+
- LICENSE
|
128
|
+
- README.md
|
129
|
+
- Rakefile
|
130
|
+
- VERSION
|
131
|
+
- VERSION.yml
|
132
|
+
- config/f1-oauth.yml
|
133
|
+
- lib/f1api.rb
|
134
|
+
- lib/f1api/activeresource/base.rb
|
135
|
+
- lib/f1api/activeresource/connection.rb
|
136
|
+
- lib/f1api/client.rb
|
137
|
+
- lib/f1api/configuration.rb
|
138
|
+
- lib/f1api/oauth.rb
|
139
|
+
- lib/f1api/oauth/credentials_authentication.rb
|
140
|
+
- lib/f1api/oauth/oauth_authentication.rb
|
141
|
+
- test/fixtures/access_token.rb
|
142
|
+
- test/fixtures/http.rb
|
143
|
+
- test/fixtures/request_token.rb
|
144
|
+
- test/unit/configuration_test.rb
|
145
|
+
- test/unit/credentials_test.rb
|
146
|
+
- test/unit/oauth_test.rb
|
147
|
+
has_rdoc: true
|
148
|
+
homepage: http://github.com/jessedearing/f1api
|
149
|
+
licenses:
|
150
|
+
- MIT
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
none: false
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
segments:
|
162
|
+
- 0
|
163
|
+
version: "0"
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
segments:
|
170
|
+
- 0
|
171
|
+
version: "0"
|
172
|
+
requirements: []
|
173
|
+
|
174
|
+
rubyforge_project:
|
175
|
+
rubygems_version: 1.3.7
|
176
|
+
signing_key:
|
177
|
+
specification_version: 3
|
178
|
+
summary: Consume the Fellowship One API in your apps using ActiveResource
|
179
|
+
test_files:
|
180
|
+
- test/fixtures/access_token.rb
|
181
|
+
- test/fixtures/http.rb
|
182
|
+
- test/fixtures/request_token.rb
|
183
|
+
- test/unit/configuration_test.rb
|
184
|
+
- test/unit/credentials_test.rb
|
185
|
+
- test/unit/oauth_test.rb
|