intuit_ids_aggcat 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +202 -0
- data/README.markdown +103 -0
- data/lib/intuit_ids_aggcat/client/intuit_xml_mappings.rb +396 -0
- data/lib/intuit_ids_aggcat/client/saml.rb +126 -0
- data/lib/intuit_ids_aggcat/client/services.rb +213 -0
- data/lib/intuit_ids_aggcat/core/configuration.rb +135 -0
- data/lib/intuit_ids_aggcat/core.rb +4 -0
- data/lib/intuit_ids_aggcat/rails.rb +106 -0
- data/lib/intuit_ids_aggcat/version.rb +3 -0
- data/lib/intuit_ids_aggcat.rb +23 -0
- data/spec/config/intuit_ids_aggcat.yml +5 -0
- data/spec/config/test.crt +17 -0
- data/spec/configuration_spec.rb +23 -0
- data/spec/rails_spec.rb +15 -0
- data/spec/saml_spec.rb +25 -0
- data/spec/services_spec.rb +109 -0
- data/spec/spec_helper.rb +7 -0
- metadata +166 -0
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
require 'rexml/document'
|
3
|
+
require 'xml/mapping'
|
4
|
+
require 'intuit_ids_aggcat/client/intuit_xml_mappings'
|
5
|
+
|
6
|
+
module IntuitIdsAggcat
|
7
|
+
|
8
|
+
module Client
|
9
|
+
|
10
|
+
class Services
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Gets all institutions supported by Intuit. If oauth_token_info isn't provided, new tokens are provisioned using "default" user
|
20
|
+
# consumer_key and consumer_secret will be retrieved from the Configuration class if not provided
|
21
|
+
def get_institutions oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens("default"), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret
|
22
|
+
response = oauth_get_request "https://financialdatafeed.platform.intuit.com/rest-war/v1/institutions", oauth_token_info, consumer_key, consumer_secret
|
23
|
+
institutions = Institutions.load_from_xml(response[:response_xml].root)
|
24
|
+
institutions.institutions
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Gets the institution details for id. If oauth_token_info isn't provided, new tokens are provisioned using "default" user
|
29
|
+
# consumer_key and consumer_secret will be retrieved from the Configuration class if not provided
|
30
|
+
def get_institution_detail id, oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens("default"), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret
|
31
|
+
response = oauth_get_request "https://financialdatafeed.platform.intuit.com/rest-war/v1/institutions/#{id}", oauth_token_info, consumer_key, consumer_secret
|
32
|
+
institutions = InstitutionDetail.load_from_xml(response[:response_xml].root)
|
33
|
+
institutions
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Deletes the customer's accounts from aggregation at Intuit.
|
38
|
+
# username must be provided, if no oauth_token_info is provided, new tokens will be provisioned using username
|
39
|
+
def delete_customer username, oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens(username), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret
|
40
|
+
url = "https://financialdatafeed.platform.intuit.com/rest-war/v1/customers/"
|
41
|
+
oauth_delete_request url, oauth_token_info
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Deletes the a specific account for a customer from aggregation at Intuit.
|
46
|
+
# username and account ID must be provided, if no oauth_token_info is provided, new tokens will be provisioned using username
|
47
|
+
def delete_account username, account_id, oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens(username), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret
|
48
|
+
puts "in gem, username = #{username}, account = #{account_id}."
|
49
|
+
url = "https://financialdatafeed.platform.intuit.com/rest-war/v1/accounts/#{account_id}"
|
50
|
+
oauth_delete_request url, oauth_token_info
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Discovers and adds accounts using credentials
|
55
|
+
# institution_id is the ID of the institution, username is the ID for this customer's accounts at Intuit and must be used for future requests,
|
56
|
+
# creds_hash is a hash object of key value pairs used for authentication
|
57
|
+
# If oauth_token is not provided, new tokens will be provisioned using the username provided
|
58
|
+
# Returns a hash produced by discover_account_data_to_hash with the following keys:
|
59
|
+
# discover_response : hash including the following keys:
|
60
|
+
# response_code: HTTP response code from Intuit
|
61
|
+
# response_xml : XML returned by Intuit
|
62
|
+
# accounts : Ruby hash with accounts if returned by discover call
|
63
|
+
# challenge_type : text description of the type of challenge requested, if applicable
|
64
|
+
# "none" | "choice" | "image" | "text"
|
65
|
+
# challenge : Ruby hash with the detail of the challenge if applicable
|
66
|
+
# challenge_session_id: challenge session ID to pass to challenge_response if this is a challenge
|
67
|
+
# challenge_node_id : challenge node ID to pass to challenge_response if this is a challenge
|
68
|
+
# description : text description of the result of the discover request
|
69
|
+
def discover_and_add_accounts_with_credentials institution_id, username, creds_hash, oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens(username), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret, timeout = 30
|
70
|
+
url = "https://financialdatafeed.platform.intuit.com/rest-war/v1/institutions/#{institution_id}/logins"
|
71
|
+
credentials_array = []
|
72
|
+
creds_hash.each do |k,v|
|
73
|
+
c = Credential.new
|
74
|
+
c.name = k
|
75
|
+
c.value = v
|
76
|
+
credentials_array.push c
|
77
|
+
end
|
78
|
+
creds = Credentials.new
|
79
|
+
creds.credential = credentials_array
|
80
|
+
il = InstitutionLogin.new
|
81
|
+
il.credentials = creds
|
82
|
+
daa = oauth_post_request url, il.save_to_xml.to_s, oauth_token_info
|
83
|
+
discover_account_data_to_hash daa
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Given a username, response text, challenge session ID and challenge node ID, passes the credentials to Intuit to begin aggregation
|
88
|
+
def challenge_response institution_id, username, response, challenge_session_id, challenge_node_id, oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens(username), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret
|
89
|
+
url = "https://financialdatafeed.platform.intuit.com/rest-war/v1/institutions/#{institution_id}/logins"
|
90
|
+
if !(response.kind_of?(Array) || response.respond_to?('each'))
|
91
|
+
response = [response]
|
92
|
+
end
|
93
|
+
|
94
|
+
cr = IntuitIdsAggcat::ChallengeResponses.new
|
95
|
+
cr.response = response
|
96
|
+
il = IntuitIdsAggcat::InstitutionLogin.new
|
97
|
+
il.challenge_responses = cr
|
98
|
+
daa = oauth_post_request url, il.save_to_xml.to_s, oauth_token_info, { "challengeSessionId" => challenge_session_id, "challengeNodeId" => challenge_node_id }
|
99
|
+
discover_account_data_to_hash daa
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Gets all accounts for a customer
|
104
|
+
def get_customer_accounts username, oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens(username), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret
|
105
|
+
url = "https://financialdatafeed.platform.intuit.com/rest-war/v1/accounts/"
|
106
|
+
response = oauth_get_request url, oauth_token_info
|
107
|
+
accounts = AccountList.load_from_xml(response[:response_xml].root)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Get transactions for a specific account and timeframe
|
112
|
+
def get_account_transactions username, account_id, start_date, end_date = nil, oauth_token_info = IntuitIdsAggcat::Client::Saml.get_tokens(username), consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret
|
113
|
+
txn_start = start_date.strftime("%Y-%m-%d")
|
114
|
+
url = "https://financialdatafeed.platform.intuit.com/rest-war/v1/accounts/#{account_id}/transactions?txnStartDate=#{txn_start}"
|
115
|
+
if !end_date.nil?
|
116
|
+
txn_end = end_date.strftime("%Y-%m-%d")
|
117
|
+
url = "#{url}&txnEndDate=#{txn_end}"
|
118
|
+
end
|
119
|
+
response = oauth_get_request url, oauth_token_info
|
120
|
+
xml = REXML::Document.new response[:response_xml].to_s
|
121
|
+
tl = IntuitIdsAggcat::TransactionList.load_from_xml xml.root
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Helper method for parsing discover account response data
|
126
|
+
def discover_account_data_to_hash daa
|
127
|
+
challenge_type = "none"
|
128
|
+
if daa[:response_code] == "201"
|
129
|
+
# return account list
|
130
|
+
accounts = AccountList.load_from_xml(daa[:response_xml].root)
|
131
|
+
{ discover_response: daa, accounts: accounts, challenge_type: challenge_type, challenge: nil, description: "Account information retrieved." }
|
132
|
+
elsif daa[:response_code] == "401" && daa[:challenge_session_id]
|
133
|
+
# return challenge
|
134
|
+
challenge = Challenges.load_from_xml(daa[:response_xml].root)
|
135
|
+
challenge_type = "unknown"
|
136
|
+
if challenge.save_to_xml.to_s.include?("<choice>")
|
137
|
+
challenge_type = "choice"
|
138
|
+
elsif challenge.save_to_xml.to_s.include?("image")
|
139
|
+
challenge_type ="image"
|
140
|
+
else
|
141
|
+
challenge_type = "text"
|
142
|
+
end
|
143
|
+
{ discover_response: daa, accounts: nil, challenge_type: challenge_type, challenge: challenge, challenge_session_id: daa[:challenge_session_id], challenge_node_id: daa[:challenge_node_id], description: "Multi-factor authentication required to retrieve accounts." }
|
144
|
+
elsif daa[:response_code] == "404"
|
145
|
+
{ discover_response: daa, accounts: nil, challenge_type: challenge_type, challenge: nil, description: "Institution not found." }
|
146
|
+
elsif daa[:response_code] == "408"
|
147
|
+
{ discover_response: daa, accounts: nil, challenge_type: challenge_type, challenge: nil, description: "Multi-factor authentication session expired." }
|
148
|
+
elsif daa[:response_code] == "500"
|
149
|
+
{ discover_response: daa, accounts: nil, challenge_type: challenge_type, challenge: nil, description: "Internal server error." }
|
150
|
+
elsif daa[:response_code] == "503"
|
151
|
+
{ discover_response: daa, accounts: nil, challenge_type: challenge_type, challenge: nil, description: "Problem at the finanical institution." }
|
152
|
+
else
|
153
|
+
{ discover_response: daa, accounts: nil, challenge_type: challenge_type, challenge: nil, description: "Unknown error." }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Helper method to issue post requests
|
159
|
+
def oauth_post_request url, body, oauth_token_info, headers = {}, consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret, timeout = 120
|
160
|
+
oauth_token = oauth_token_info[:oauth_token]
|
161
|
+
oauth_token_secret = oauth_token_info[:oauth_token_secret]
|
162
|
+
|
163
|
+
options = { :request_token_path => 'https://financialdatafeed.platform.intuit.com', :timeout => timeout }
|
164
|
+
options = options.merge({ :proxy => IntuitIdsAggcat.config.proxy}) if !IntuitIdsAggcat.config.proxy.nil?
|
165
|
+
consumer = OAuth::Consumer.new(consumer_key, consumer_secret, options)
|
166
|
+
access_token = OAuth::AccessToken.new(consumer, oauth_token, oauth_token_secret)
|
167
|
+
response = access_token.post(url, body, { "Content-Type"=>'application/xml', 'Host' => 'financialdatafeed.platform.intuit.com' }.merge(headers))
|
168
|
+
response_xml = REXML::Document.new response.body
|
169
|
+
|
170
|
+
# handle challenge responses from discoverAndAcccounts flow
|
171
|
+
challenge_session_id = challenge_node_id = nil
|
172
|
+
if !response["challengeSessionId"].nil?
|
173
|
+
challenge_session_id = response["challengeSessionId"]
|
174
|
+
challenge_node_id = response["challengeNodeId"]
|
175
|
+
end
|
176
|
+
|
177
|
+
{ :challenge_session_id => challenge_session_id, :challenge_node_id => challenge_node_id, :response_code => response.code, :response_xml => response_xml }
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Helper method to issue get requests
|
182
|
+
def oauth_get_request url, oauth_token_info, consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret, timeout = 120
|
183
|
+
oauth_token = oauth_token_info[:oauth_token]
|
184
|
+
oauth_token_secret = oauth_token_info[:oauth_token_secret]
|
185
|
+
|
186
|
+
options = { :request_token_path => 'https://financialdatafeed.platform.intuit.com', :timeout => timeout }
|
187
|
+
options = options.merge({ :proxy => IntuitIdsAggcat.config.proxy}) if !IntuitIdsAggcat.config.proxy.nil?
|
188
|
+
consumer = OAuth::Consumer.new(consumer_key, consumer_secret, options)
|
189
|
+
access_token = OAuth::AccessToken.new(consumer, oauth_token, oauth_token_secret)
|
190
|
+
response = access_token.get(url, { "Content-Type"=>'application/xml', 'Host' => 'financialdatafeed.platform.intuit.com' })
|
191
|
+
response_xml = REXML::Document.new response.body
|
192
|
+
{ :response_code => response.code, :response_xml => response_xml }
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# Helper method to issue delete requests
|
197
|
+
def oauth_delete_request url, oauth_token_info, consumer_key = IntuitIdsAggcat.config.oauth_consumer_key, consumer_secret = IntuitIdsAggcat.config.oauth_consumer_secret, timeout = 120
|
198
|
+
oauth_token = oauth_token_info[:oauth_token]
|
199
|
+
oauth_token_secret = oauth_token_info[:oauth_token_secret]
|
200
|
+
|
201
|
+
options = { :request_token_path => 'https://financialdatafeed.platform.intuit.com', :timeout => timeout }
|
202
|
+
options = options.merge({ :proxy => IntuitIdsAggcat.config.proxy}) if !IntuitIdsAggcat.config.proxy.nil?
|
203
|
+
consumer = OAuth::Consumer.new(consumer_key, consumer_secret, options)
|
204
|
+
access_token = OAuth::AccessToken.new(consumer, oauth_token, oauth_token_secret)
|
205
|
+
response = access_token.delete(url, { "Content-Type"=>'application/xml', 'Host' => 'financialdatafeed.platform.intuit.com' })
|
206
|
+
response_xml = REXML::Document.new response.body
|
207
|
+
{ :response_code => response.code, :response_xml => response_xml }
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module IntuitIdsAggcat
|
5
|
+
module Core
|
6
|
+
|
7
|
+
# A configuration object for the Intuit interface.
|
8
|
+
#
|
9
|
+
# == Configuring Credentials
|
10
|
+
#
|
11
|
+
# In order to do anything with the AggCat services you will need to assign credentials.
|
12
|
+
# The simplest method is to assing your credentials into the default
|
13
|
+
# configuration:
|
14
|
+
#
|
15
|
+
# AWS.config(:access_key_id => 'KEY', :secret_access_key => 'SECRET')
|
16
|
+
#
|
17
|
+
# You can also export them into your environment and they will be picked up
|
18
|
+
# automatically:
|
19
|
+
#
|
20
|
+
# export AWS_ACCESS_KEY_ID='YOUR_KEY_ID_HERE'
|
21
|
+
# export AWS_SECRET_ACCESS_KEY='YOUR_SECRET_KEY_HERE'
|
22
|
+
#
|
23
|
+
|
24
|
+
class Configuration
|
25
|
+
|
26
|
+
# Creates a new Configuration object.
|
27
|
+
def initialize options = {}
|
28
|
+
|
29
|
+
@created = options.delete(:__created__) || {}
|
30
|
+
options.each_pair do |opt_name, value|
|
31
|
+
opt_name = opt_name.to_sym
|
32
|
+
if self.class.accepted_options.include?(opt_name)
|
33
|
+
supplied[opt_name] = value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Hash] Returns a hash with your configured credentials.
|
40
|
+
def credentials
|
41
|
+
credentials = {}
|
42
|
+
[:saml_idp_id, :user_id].each do |opt|
|
43
|
+
if value = credential_provider.send(opt)
|
44
|
+
credentials[opt] = value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
credentials
|
48
|
+
end
|
49
|
+
|
50
|
+
def with options = {}
|
51
|
+
|
52
|
+
# symbolize option keys
|
53
|
+
options = options.inject({}) {|h,kv| h[kv.first.to_sym] = kv.last; h }
|
54
|
+
|
55
|
+
values = supplied.merge(options)
|
56
|
+
|
57
|
+
if supplied == values
|
58
|
+
self # nothing changed
|
59
|
+
else
|
60
|
+
self.class.new(values.merge(:__created__ => @created.dup))
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Hash] Returns a hash of all configuration values.
|
66
|
+
def to_h
|
67
|
+
self.class.accepted_options.inject({}) do |h,k|
|
68
|
+
h.merge(k => send(k))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
alias_method :to_hash, :to_h
|
72
|
+
|
73
|
+
# @return [Boolean] Returns true if the two configuration objects have
|
74
|
+
# the same values.
|
75
|
+
def eql? other
|
76
|
+
other.is_a?(self.class) and self.supplied == other.supplied
|
77
|
+
end
|
78
|
+
alias_method :==, :eql?
|
79
|
+
|
80
|
+
# @private
|
81
|
+
def inspect
|
82
|
+
"<#{self.class.name}>"
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def supplied
|
88
|
+
@supplied ||= {}
|
89
|
+
end
|
90
|
+
|
91
|
+
class << self
|
92
|
+
|
93
|
+
# @private
|
94
|
+
def accepted_options
|
95
|
+
@options ||= Set.new
|
96
|
+
end
|
97
|
+
|
98
|
+
# @private
|
99
|
+
def add_option name, default_value = nil, options = {}, &transform
|
100
|
+
|
101
|
+
accepted_options << name
|
102
|
+
|
103
|
+
define_method(name) do |&default_override|
|
104
|
+
|
105
|
+
value =
|
106
|
+
if supplied.has_key?(name)
|
107
|
+
supplied[name]
|
108
|
+
elsif default_override
|
109
|
+
default_override.call
|
110
|
+
else
|
111
|
+
default_value
|
112
|
+
end
|
113
|
+
|
114
|
+
transform ? transform.call(self, value) : value
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
alias_method("#{name}?", name) if options[:boolean]
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
add_option :certificate_path
|
125
|
+
add_option :certificate_string
|
126
|
+
add_option :certificate_password
|
127
|
+
add_option :issuer_id
|
128
|
+
add_option :oauth_consumer_key
|
129
|
+
add_option :oauth_consumer_secret
|
130
|
+
add_option :oauth_token_info
|
131
|
+
add_option :proxy
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module IntuitIdsAggcat
|
4
|
+
|
5
|
+
if Object.const_defined?(:Rails) and Rails.const_defined?(:Railtie)
|
6
|
+
|
7
|
+
# @private
|
8
|
+
class Railtie < Rails::Railtie
|
9
|
+
|
10
|
+
# configure our plugin on boot. other extension points such
|
11
|
+
# as configuration, rake tasks, etc, are also available
|
12
|
+
initializer "intuit_ids_aggcat.initialize" do |app|
|
13
|
+
IntuitIdsAggcat::Rails.setup
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
# A handful of useful Rails integration methods.
|
20
|
+
#
|
21
|
+
# If you require this gem inside a Rails application (via config.gem
|
22
|
+
# for rails 2 and bundler for rails 3) then {setup} is called
|
23
|
+
# automatically.
|
24
|
+
module Rails
|
25
|
+
|
26
|
+
# Adds extra functionality to Rails.
|
27
|
+
#
|
28
|
+
# Normailly this method is invoked automatically when you require this
|
29
|
+
# gem in a Rails Application:
|
30
|
+
#
|
31
|
+
# Rails 3+ (RAILS_ROOT/Gemfile)
|
32
|
+
#
|
33
|
+
# gem 'intuit_ids_aggcat'
|
34
|
+
#
|
35
|
+
|
36
|
+
# @return [nil]
|
37
|
+
def self.setup
|
38
|
+
load_yaml_config
|
39
|
+
log_to_rails_logger
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Loads Intuit IDS AggCat configuration options from +RAILS_ROOT/config/intuit_ids_aggcat.yml+.
|
44
|
+
#
|
45
|
+
# This configuration file is optional. You can omit this file and instead
|
46
|
+
# use ruby to configure the gem inside a configuration initialization script
|
47
|
+
# (e.g. RAILS_ROOT/config/intializers/intuit_ids_aggcat.rb).
|
48
|
+
#
|
49
|
+
# If you have a yaml configuration file it should be formatted like the
|
50
|
+
# standard +database.yml+ file in a Rails application. This means there
|
51
|
+
# should be one section for Rails environment:
|
52
|
+
#
|
53
|
+
# development:
|
54
|
+
# certificate_path: path to private key
|
55
|
+
# issuer_id: SAML issuer ID provided by intuit
|
56
|
+
# oauth_consumer_key: OAuth consumer key
|
57
|
+
# oauth_consumer_secret: OAuth consumer secret
|
58
|
+
#
|
59
|
+
# production:
|
60
|
+
# certificate_path: path to private key
|
61
|
+
# issuer_id: SAML issuer ID provided by intuit
|
62
|
+
# oauth_consumer_key: OAuth consumer key
|
63
|
+
# oauth_consumer_secret: OAuth consumer secret
|
64
|
+
|
65
|
+
def self.load_yaml_config
|
66
|
+
|
67
|
+
path = Pathname.new("#{rails_root}/config/intuit_ids_aggcat.yml")
|
68
|
+
|
69
|
+
if File.exists?(path)
|
70
|
+
cfg = YAML::load(ERB.new(File.read(path)).result)
|
71
|
+
unless cfg[rails_env]
|
72
|
+
raise "config/intuit_ids_aggcat.yml is missing a section for `#{rails_env}`"
|
73
|
+
end
|
74
|
+
IntuitIdsAggcat.config(cfg[rails_env])
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Configures gem to log to the Rails default logger.
|
81
|
+
# @return [nil]
|
82
|
+
def self.log_to_rails_logger
|
83
|
+
AWS.config(:logger => rails_logger)
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
# @private
|
88
|
+
protected
|
89
|
+
def self.rails_env
|
90
|
+
::Rails.respond_to?(:env) ? ::Rails.env : RAILS_ENV
|
91
|
+
end
|
92
|
+
|
93
|
+
# @private
|
94
|
+
protected
|
95
|
+
def self.rails_root
|
96
|
+
::Rails.respond_to?(:root) ? ::Rails.root.to_s : RAILS_ROOT
|
97
|
+
end
|
98
|
+
|
99
|
+
# @private
|
100
|
+
protected
|
101
|
+
def self.rails_logger
|
102
|
+
::Rails.respond_to?(:logger) ? ::Rails.logger : ::RAILS_DEFAULT_LOGGER
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "intuit_ids_aggcat/version"
|
2
|
+
require "intuit_ids_aggcat/core"
|
3
|
+
|
4
|
+
module IntuitIdsAggcat
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# @private
|
8
|
+
@@config = nil
|
9
|
+
@@client = nil
|
10
|
+
|
11
|
+
def config options = {}
|
12
|
+
@@config ||= Core::Configuration.new
|
13
|
+
@@config = @@config.with(options) unless options.empty?
|
14
|
+
@@config
|
15
|
+
end
|
16
|
+
|
17
|
+
def client
|
18
|
+
@@client ||= Client::Services.new
|
19
|
+
@@client
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIICvDCCAiWgAwIBAgIJAKqWuLF2VM4+MA0GCSqGSIb3DQEBBQUAMEkxCzAJBgNV
|
3
|
+
BAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTESMBAGA1UEBxMJQ2hhcmxv
|
4
|
+
dHRlMQ0wCwYDVQQKEwRUZXN0MB4XDTEyMTAyNjE4NDk1NloXDTEyMTEyNTE4NDk1
|
5
|
+
NlowSTELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRIwEAYD
|
6
|
+
VQQHEwlDaGFybG90dGUxDTALBgNVBAoTBFRlc3QwgZ8wDQYJKoZIhvcNAQEBBQAD
|
7
|
+
gY0AMIGJAoGBAL9zKDXCbw+Xc8IugYrK7ffJsHQYr8rZKr0hw3XksEYRHb/LqCSw
|
8
|
+
ZF5aUTY1ECGb0QlD9uYxYWTvmpqZcTRIJANQgqhr1CH4hpEXJAdTJVzdJMaxeCjO
|
9
|
+
F40cHTwyeMP5Ou5Vsq3gKWTnJkGvDkJXHCqRkwduLk6FiWOfat2I9CjjAgMBAAGj
|
10
|
+
gaswgagwHQYDVR0OBBYEFGdV8DGee/TMuZDgQW1wdZ1k0HuNMHkGA1UdIwRyMHCA
|
11
|
+
FGdV8DGee/TMuZDgQW1wdZ1k0HuNoU2kSzBJMQswCQYDVQQGEwJVUzEXMBUGA1UE
|
12
|
+
CBMOTm9ydGggQ2Fyb2xpbmExEjAQBgNVBAcTCUNoYXJsb3R0ZTENMAsGA1UEChME
|
13
|
+
VGVzdIIJAKqWuLF2VM4+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
|
14
|
+
IdnfNuBftWE97JyhPiADYGsV8l+2KE2mxgqQacwGTgorxiwjBph1l26WY8fb3LCK
|
15
|
+
xKNDrMfcDJfSxgI8xqw3AsjDTWSF3tOqKTS6ApIwKf7l6BIMCTD2Xc6PTTmgNV20
|
16
|
+
NlLkzu6mXIXIuzBrUXfHUSXIsSUly+dX6fHKxMlToU8=
|
17
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IntuitIdsAggcat::Core::Configuration do
|
4
|
+
it 'should configure issuer id' do
|
5
|
+
IntuitIdsAggcat.config(:issuer_id => "test_issuer")
|
6
|
+
IntuitIdsAggcat.config.issuer_id.should == "test_issuer"
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should configure oauth consumer key' do
|
10
|
+
IntuitIdsAggcat.config(:oauth_consumer_key => "consumer_key")
|
11
|
+
IntuitIdsAggcat.config.oauth_consumer_key.should == "consumer_key"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should configure oauth consumer secret' do
|
15
|
+
IntuitIdsAggcat.config(:oauth_consumer_secret => "secret")
|
16
|
+
IntuitIdsAggcat.config.oauth_consumer_secret.should == "secret"
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should configure certificate path' do
|
20
|
+
IntuitIdsAggcat.config(:certificate_path => "cert")
|
21
|
+
IntuitIdsAggcat.config.certificate_path.should == "cert"
|
22
|
+
end
|
23
|
+
end
|
data/spec/rails_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IntuitIdsAggcat::Rails do
|
4
|
+
it 'should load configuration from a YAML file' do
|
5
|
+
Rails = Object.new
|
6
|
+
::Rails.stub(:root).and_return("#{Dir.pwd}/spec")
|
7
|
+
::Rails.stub(:env).and_return("development")
|
8
|
+
IntuitIdsAggcat::Rails.load_yaml_config
|
9
|
+
IntuitIdsAggcat.config.issuer_id.should == "rails_test_id"
|
10
|
+
IntuitIdsAggcat.config.certificate_path.should == "test.key"
|
11
|
+
IntuitIdsAggcat.config.oauth_consumer_key.should == "rails_test_key"
|
12
|
+
IntuitIdsAggcat.config.oauth_consumer_secret.should == "rails_test_secret"
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
data/spec/saml_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe IntuitIdsAggcat::Client::Saml do
|
4
|
+
it 'should generate a SAML assertion' do
|
5
|
+
SecureRandom.stub(:uuid).and_return("6bf05546-89ee-4ec1-8cf7-e90c438cb147")
|
6
|
+
IntuitIdsAggcat::Client::Saml.get_saml_assertion_xml("rails_test_id", "test", "spec/config/test.key", nil, nil, Time.new(2008,6,21, 13,30,0, "+09:00")).should ==
|
7
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><saml2:Assertion xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_6bf0554689ee4ec18cf7e90c438cb147\" IssueInstant=\"2008-06-21T04:30:00.000Z\" Version=\"2.0\"><saml2:Issuer>rails_test_id</saml2:Issuer><ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/><ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><ds:Reference URI=\"#_6bf0554689ee4ec18cf7e90c438cb147\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><ds:DigestValue>nzk4auzjFeqgAeWIKFAvtjxHKqc=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>Yv5BHxMttOlN+N+LQnEYXDvzCpTTpqWegDJQTnJtqyohH5WEllszOrDad0ZugO4NToa179aJkb0bhSHRlUZ83BAR2WqZjTG8a9tSEd/PGAbUkGmzNiGF8kYTuXAq//PmED6HYAO/PAXzaK9kubMLO+ZlOnyD0eW7+t913A0W0VY=</ds:SignatureValue></ds:Signature><saml2:Subject><saml2:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test</saml2:NameID><saml2:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"/></saml2:Subject><saml2:Conditions NotBefore=\"2008-06-21T04:25:00.000Z\" NotOnOrAfter=\"2008-06-21T04:40:00.000Z\"><saml2:AudienceRestriction><saml2:Audience>rails_test_id</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant=\"2008-06-21T04:30:00.000Z\" SessionIndex=\"_6bf0554689ee4ec18cf7e90c438cb147\"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement></saml2:Assertion>\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return signed XML" do
|
11
|
+
IntuitIdsAggcat::Client::Saml.get_signed_info_xml("123", "test").should ==
|
12
|
+
"<ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/><ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/><ds:Reference URI=\"#123\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/><ds:DigestValue>test</ds:DigestValue></ds:Reference></ds:SignedInfo>"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return tokens" do
|
16
|
+
path = Pathname.new("spec/config/real_config.yml")
|
17
|
+
config = YAML::load(ERB.new(File.read(path)).result)
|
18
|
+
IntuitIdsAggcat.config(config)
|
19
|
+
tokens = IntuitIdsAggcat::Client::Saml.get_tokens "test"
|
20
|
+
tokens[:oauth_token_secret].should_not be_nil
|
21
|
+
tokens[:oauth_token].should_not be_nil
|
22
|
+
tokens[:token_expiry].should_not be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|