reward_station 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/Gemfile +2 -2
- data/README.md +121 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/award_points/award_points.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/award_points/award_points_invalid_token.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_point_summary/return_point_summary.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_point_summary/return_point_summary_invalid_token.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_popular_products/return_popular_products.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_popular_products/return_popular_products_invalid_token.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_token/return_token.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_token/return_token_invalid.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_user/return_user.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_user/return_user_invalid_token.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/return_user/return_user_invalid_user.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/update_user/create_user.xml +0 -0
- data/{spec/fixtures/savon/reward_station → lib/responses}/update_user/create_user_exists.xml +0 -0
- data/lib/reward_station.rb +10 -1
- data/lib/reward_station/errors.rb +31 -0
- data/lib/reward_station/service.rb +167 -0
- data/lib/reward_station/version.rb +1 -1
- data/lib/saml/auth_response.rb +174 -0
- data/lib/savon/macros.rb +12 -0
- data/lib/savon/mock.rb +70 -0
- data/lib/savon/mock_response.rb +45 -0
- data/reward_station.gemspec +5 -3
- data/spec/reward_station/service_spec.rb +326 -0
- data/spec/spec_helper.rb +1 -8
- metadata +44 -24
- data/lib/xceleration/reward_station.rb +0 -122
- data/spec/savon_helper.rb +0 -129
- data/spec/xceleration/reward_station_spec.rb +0 -348
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,126 @@
|
|
1
1
|
### Xceleration Reward Station
|
2
2
|
|
3
|
+
Client library for Xceleration rewardstation.com service
|
4
|
+
|
3
5
|
|
4
6
|
## Basic Usage
|
5
7
|
|
8
|
+
#Initialization
|
9
|
+
reward_station = RewardStation::Service.new :client_id => "112112", :client_password => "fsdftr#"
|
10
|
+
|
11
|
+
# Return Token
|
12
|
+
Request access token
|
13
|
+
|
14
|
+
token = reward_station.return_token
|
15
|
+
|
16
|
+
# Award Points
|
17
|
+
Update award points
|
18
|
+
|
19
|
+
user_id = "130"
|
20
|
+
points = 10
|
21
|
+
description = "Action 'Call to client' "
|
22
|
+
program_id = 90 # optional
|
23
|
+
point_reasond_code_id = 129 # optional
|
24
|
+
|
25
|
+
confirmation_number = reward_station.award_points user_id, points, description, program_id, point_reason_code_id
|
26
|
+
|
27
|
+
# Create User
|
28
|
+
|
29
|
+
user_attributes = reward_station.create_user :organization_id => '150',
|
30
|
+
:email => 'john5@company.com',
|
31
|
+
:first_name => 'John',
|
32
|
+
:last_name => 'Smith',
|
33
|
+
:user_name => 'john5@company.com',
|
34
|
+
:balance => 0
|
35
|
+
puts user_attributes.inspect
|
36
|
+
# {
|
37
|
+
# :user_id => '6727',
|
38
|
+
# :client_id => '100080',
|
39
|
+
# :user_name => 'john5@company.com',
|
40
|
+
# :email => 'john5@company.com',
|
41
|
+
# :encrypted_password => nil,
|
42
|
+
# :first_name => 'John',
|
43
|
+
# :last_name => 'Smith',
|
44
|
+
# :address_one => nil,
|
45
|
+
# :address_two => nil,
|
46
|
+
# :city => nil,
|
47
|
+
# :state_code => nil,
|
48
|
+
# :province => nil,
|
49
|
+
# :postal_code => nil,
|
50
|
+
# :country_code => 'USA',
|
51
|
+
# :phone => nil,
|
52
|
+
# :organization_id => '150',
|
53
|
+
# :organization_name => nil,
|
54
|
+
# :rep_type_id => '0',
|
55
|
+
# :client_region_id => '0',
|
56
|
+
#
|
57
|
+
# :is_active => true,
|
58
|
+
# :point_balance => '0',
|
59
|
+
# :manager_id => '0',
|
60
|
+
# :error_message => nil
|
61
|
+
# }
|
62
|
+
|
63
|
+
|
64
|
+
## Single-Sign-On
|
65
|
+
|
66
|
+
Basic SSO logic implemented in SAML::AuthResponse class. Example usage of AuthResponse:
|
67
|
+
|
68
|
+
#SessionController
|
69
|
+
|
70
|
+
require 'saml/auth_response'
|
71
|
+
|
72
|
+
class SessionController < ApplicationController
|
73
|
+
|
74
|
+
def create
|
75
|
+
@user_session = UserSession.new(params[:user_session])
|
76
|
+
if @user_session.save
|
77
|
+
if session[:saml_request].present?
|
78
|
+
sso_params(session[:saml_request], session[:relay_state], current_user)
|
79
|
+
session[:saml_request] = session[:relay_state] = nil
|
80
|
+
render :template => "session/signon"
|
81
|
+
return
|
82
|
+
...
|
83
|
+
end
|
84
|
+
...
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def signon
|
89
|
+
if current_user.present?
|
90
|
+
sso_params(params[:SAMLRequest], params[:RelayState], current_user)
|
91
|
+
|
92
|
+
render :template => "session/signon"
|
93
|
+
else
|
94
|
+
session[:saml_request] = params[:SAMLRequest]
|
95
|
+
session[:relay_state] = params[:RelayState]
|
96
|
+
redirect_to signin_url
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def destroy
|
101
|
+
current_user_session.destroy
|
102
|
+
redirect_to signin_url
|
103
|
+
end
|
104
|
+
|
105
|
+
protected
|
106
|
+
|
107
|
+
def sso_params saml_request, relay_state, user
|
108
|
+
@saml_response = SAML::AuthResponse.new(saml_request).response_url(user.xceleration_id)
|
109
|
+
@relay_state = relay_state
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# signon.html.erb
|
114
|
+
|
115
|
+
<html>
|
116
|
+
<body>
|
117
|
+
<form id="sso_form" action="http://www6.rewardstation.net/sso/100080/AssertionService.aspx?binding=urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" method="post">
|
118
|
+
<input type="hidden" name="SAMLResponse" value="<%= @saml_response %>"/>
|
119
|
+
<input type="hidden" name="RelayState" value="<%= @relay_state %>"/>
|
120
|
+
</form>
|
121
|
+
<script type="text/javascript">
|
122
|
+
document.getElementById('sso_form').submit();
|
123
|
+
</script>
|
124
|
+
</body>
|
125
|
+
</html>
|
126
|
+
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/{spec/fixtures/savon/reward_station → lib/responses}/return_token/return_token_invalid.xml
RENAMED
File without changes
|
File without changes
|
data/{spec/fixtures/savon/reward_station → lib/responses}/return_user/return_user_invalid_token.xml
RENAMED
File without changes
|
data/{spec/fixtures/savon/reward_station → lib/responses}/return_user/return_user_invalid_user.xml
RENAMED
File without changes
|
File without changes
|
data/{spec/fixtures/savon/reward_station → lib/responses}/update_user/create_user_exists.xml
RENAMED
File without changes
|
data/lib/reward_station.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
module RewardStation
|
2
|
+
|
3
|
+
module NestingError
|
4
|
+
attr_reader :cause
|
5
|
+
|
6
|
+
def initialize message = nil, cause = $!
|
7
|
+
@cause = cause
|
8
|
+
super(message || cause && cause.message)
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_backtrace bt
|
12
|
+
if cause
|
13
|
+
cause.backtrace.reverse.each do |line|
|
14
|
+
bt.last == line ? bt.pop : break
|
15
|
+
end
|
16
|
+
bt << "cause: #{cause.class.name}: #{cause}"
|
17
|
+
bt.concat cause.backtrace
|
18
|
+
end
|
19
|
+
super bt
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class InvalidAccount < StandardError; end
|
24
|
+
class InvalidToken < StandardError; end
|
25
|
+
class InvalidUser < StandardError; end
|
26
|
+
class UserAlreadyExists < StandardError; end
|
27
|
+
|
28
|
+
class ConnectionError < StandardError
|
29
|
+
include NestingError
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module RewardStation
|
2
|
+
class Service
|
3
|
+
|
4
|
+
def initialize options = {}
|
5
|
+
[:client_id, :client_password].each do |arg|
|
6
|
+
raise ArgumentError, "Missing required option '#{arg}'" unless options.has_key? arg
|
7
|
+
end
|
8
|
+
|
9
|
+
@client_id = options[:client_id]
|
10
|
+
@client_password = options[:client_password]
|
11
|
+
@token = options[:token]
|
12
|
+
@organization_id = options[:organization_id]
|
13
|
+
|
14
|
+
@program_id = options[:program_id]
|
15
|
+
@point_reason_code_id = options[:point_reason_code_id]
|
16
|
+
|
17
|
+
if options[:new_token_callback]
|
18
|
+
raise ArgumentError, "new_token_callback option should be proc or lambda" unless options[:new_token_callback].is_a?(Proc)
|
19
|
+
@new_token_callback = options[:new_token_callback]
|
20
|
+
end
|
21
|
+
|
22
|
+
@mode = :default
|
23
|
+
if options[:mode]
|
24
|
+
raise ArgumentError, "supported modes :default and :mock" unless [:mock, :default].include?(options[:mode].to_sym)
|
25
|
+
@mode = options[:mode].to_sym
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_token_callback &block
|
30
|
+
@new_token_callback = block
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
def client
|
35
|
+
@@client ||= Savon::Client.new do |wsdl|
|
36
|
+
wsdl.document = File.join(File.dirname(__FILE__), '..', 'wsdl', 'reward_services.xml')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def logger
|
41
|
+
@@logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def logger
|
46
|
+
Service.logger
|
47
|
+
end
|
48
|
+
|
49
|
+
def return_token
|
50
|
+
result = request :return_token, :body => {
|
51
|
+
'AccountNumber' => @client_id,
|
52
|
+
'AccountCode' => @client_password
|
53
|
+
}
|
54
|
+
|
55
|
+
logger.info "xceleration token #{result[:token]}"
|
56
|
+
|
57
|
+
result[:token]
|
58
|
+
end
|
59
|
+
|
60
|
+
def return_user user_id
|
61
|
+
request_with_token(:return_user, :body => { 'UserID' => user_id} )[:user_profile]
|
62
|
+
end
|
63
|
+
|
64
|
+
def award_points user_id, points, description, program_id = nil, point_reason_code_id = nil
|
65
|
+
request_with_token(:award_points, :body => {
|
66
|
+
'UserID' => user_id,
|
67
|
+
'Points' => points,
|
68
|
+
'ProgramID' => program_id || @program_id,
|
69
|
+
'PointReasonCodeID' => point_reason_code_id || @point_reason_code_id,
|
70
|
+
'Description' => description
|
71
|
+
})[:confirmation_number]
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def return_point_summary user_id
|
76
|
+
request_with_token(:return_point_summary, :body => {
|
77
|
+
'clientId' => @client_id,
|
78
|
+
'userId' => user_id
|
79
|
+
})[:point_summary_collection][:point_summary]
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_user user_id, attrs = {}
|
83
|
+
|
84
|
+
organization_id = attrs[:organization_id] || @organization_id
|
85
|
+
email = attrs[:email] || ""
|
86
|
+
first_name = attrs[:first_name] || ""
|
87
|
+
last_name = attrs[:last_name] || ""
|
88
|
+
user_name = attrs[:user_name] || email
|
89
|
+
balance = attrs[:balance] || 0
|
90
|
+
|
91
|
+
request_with_token(:update_user , :body => {
|
92
|
+
'updateUser' => {
|
93
|
+
'UserID' => user_id,
|
94
|
+
'ClientID' => @client_id,
|
95
|
+
'UserName' => user_name,
|
96
|
+
'FirstName' => first_name,
|
97
|
+
'LastName' => last_name,
|
98
|
+
'CountryCode' => 'USA',
|
99
|
+
'Email' => email,
|
100
|
+
'IsActive' => true,
|
101
|
+
'PointBalance' => balance,
|
102
|
+
'OrganizationID' => organization_id
|
103
|
+
}
|
104
|
+
})[:update_user]
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def create_user attrs = {}
|
109
|
+
update_user -1, attrs
|
110
|
+
end
|
111
|
+
|
112
|
+
def return_popular_products user_id
|
113
|
+
request_with_token(:return_popular_products , :body => { 'userId' => user_id} )[:products][:product]
|
114
|
+
end
|
115
|
+
|
116
|
+
protected
|
117
|
+
|
118
|
+
def mock?
|
119
|
+
@mode == :mock
|
120
|
+
end
|
121
|
+
|
122
|
+
def update_token
|
123
|
+
@token = return_token
|
124
|
+
@new_token_callback.call(@token) if @new_token_callback
|
125
|
+
end
|
126
|
+
|
127
|
+
def inject_token params = {}
|
128
|
+
(params[:body] ||= {})['Token'] = @token
|
129
|
+
end
|
130
|
+
|
131
|
+
def request_with_token method_name, params
|
132
|
+
update_token unless @token
|
133
|
+
inject_token params
|
134
|
+
request method_name, params
|
135
|
+
rescue InvalidToken
|
136
|
+
update_token
|
137
|
+
inject_token params
|
138
|
+
request method_name, params
|
139
|
+
end
|
140
|
+
|
141
|
+
def request method_name, params
|
142
|
+
|
143
|
+
if mock?
|
144
|
+
#TODO
|
145
|
+
end
|
146
|
+
|
147
|
+
response = Service.client.request(:wsdl, method_name , params).to_hash
|
148
|
+
|
149
|
+
logger.debug response.inspect
|
150
|
+
|
151
|
+
result = response[:"#{method_name}_response"][:"#{method_name}_result"]
|
152
|
+
|
153
|
+
unless (error_message = result.delete(:error_message).to_s).nil?
|
154
|
+
raise InvalidToken if error_message.start_with?("Invalid Token")
|
155
|
+
raise InvalidAccount if error_message.start_with?("Invalid Account Number")
|
156
|
+
raise InvalidUser if error_message.start_with?("Invalid User")
|
157
|
+
raise UserAlreadyExists if error_message.start_with?("User Name:") && error_message.end_with?("Please enter a different user name.")
|
158
|
+
end
|
159
|
+
|
160
|
+
result
|
161
|
+
rescue Savon::SOAP::Fault, Savon::HTTP::Error => ex
|
162
|
+
logger.error ex.to_s
|
163
|
+
logger.error ex.backtrace.inspect
|
164
|
+
raise ConnectionError.new
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module SAML
|
2
|
+
class AuthResponse
|
3
|
+
|
4
|
+
attr_accessor :request, :document, :logger
|
5
|
+
include Onelogin::Saml::Codeing
|
6
|
+
|
7
|
+
def initialize(request, logger = nil)
|
8
|
+
raise ArgumentError.new("Response cannot be nil") if request.nil?
|
9
|
+
self.logger = logger || ActiveRecord::Base.logger
|
10
|
+
self.request = inflate(decode(request))
|
11
|
+
self.document = Nokogiri::XML(self.request)
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_sp_response_to
|
15
|
+
document.xpath("//samlp:AuthnRequest").first["ID"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_sp_destination
|
19
|
+
Settings.sso.sp_destination
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_idp_issuer
|
23
|
+
Settings.host
|
24
|
+
end
|
25
|
+
|
26
|
+
# def get_sp_audience
|
27
|
+
# Settings.sso.sp_audience
|
28
|
+
# end
|
29
|
+
|
30
|
+
|
31
|
+
#
|
32
|
+
def xml_time_format time
|
33
|
+
time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def response_url(name_id, params={})
|
38
|
+
prepared_result = create(name_id)
|
39
|
+
base64_request = encode(prepared_result)
|
40
|
+
# deflated_request = deflate(create(name_id))
|
41
|
+
# base64_request = encode(deflated_request)
|
42
|
+
base64_request.gsub!(/\s/,"")
|
43
|
+
# encoded_response = escape(base64_request)
|
44
|
+
#
|
45
|
+
# encoded_response
|
46
|
+
## request_params = "?SAMLResponse=" + encoded_response
|
47
|
+
#
|
48
|
+
# params.each_pair do |key, value|
|
49
|
+
# request_params << "&#{key}=#{escape(value.to_s)}"
|
50
|
+
# end
|
51
|
+
# Settings.sso.sp_destination+ request_params
|
52
|
+
end
|
53
|
+
|
54
|
+
def create(name_id)
|
55
|
+
time_line = Time.now.utc
|
56
|
+
request_id = UUID.new.generate
|
57
|
+
assert_id = UUID.new.generate
|
58
|
+
|
59
|
+
time = xml_time_format(time_line)
|
60
|
+
assertion = build_assertion_content(name_id, assert_id, time_line)
|
61
|
+
|
62
|
+
# assertion_parsed = Nokogiri::XML(assertion)
|
63
|
+
# assertion_parsed.xpath("//saml:Assertion//saml:Issuer").first.add_next_sibling(make_signature(assertion, assert_id))
|
64
|
+
|
65
|
+
response = Builder::XmlMarkup.new
|
66
|
+
response.tag!('samlp:Response', {"xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
|
67
|
+
"xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion",
|
68
|
+
"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#",
|
69
|
+
"ID" => request_id,
|
70
|
+
"Version" => "2.0",
|
71
|
+
"InResponseTo" => get_sp_response_to,
|
72
|
+
"IssueInstant" => time,
|
73
|
+
"Destination" => get_sp_destination}) do
|
74
|
+
response.tag!("saml:Issuer", get_idp_issuer)
|
75
|
+
response.tag!("samlp:Status") do
|
76
|
+
response.tag!("samlp:StatusCode", "Value" => "urn:oasis:names:tc:SAML:2.0:status:Success")
|
77
|
+
end
|
78
|
+
response << build_assertion_content(name_id, assert_id, time_line, make_signature(assertion, assert_id))
|
79
|
+
#todo if insert node this way - need to canonicalize. nokogiri 1.4.4 has no possibility to do it. only it's branch can do it:(
|
80
|
+
# response << assertion_parsed.xpath("//saml:Assertion").canonicalize.to_s
|
81
|
+
end
|
82
|
+
response.target!
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def build_assertion_content(name_id, assert_id, time_line, sign =nil)
|
87
|
+
time_and_ten = xml_time_format(time_line + 10.minutes)
|
88
|
+
time = xml_time_format(time_line)
|
89
|
+
|
90
|
+
xml = Builder::XmlMarkup.new
|
91
|
+
|
92
|
+
xml.tag!('saml:Assertion', {"xmlns:saml"=>"urn:oasis:names:tc:SAML:2.0:assertion", "ID"=>assert_id, "Version"=>"2.0", "IssueInstant"=> time}) do
|
93
|
+
xml.tag!("saml:Issuer", get_idp_issuer)
|
94
|
+
|
95
|
+
xml << sign if sign.present?
|
96
|
+
|
97
|
+
xml.tag! "saml:Subject" do
|
98
|
+
xml.tag!("saml:NameID", {"Format"=>"urn:oasis:names:tc:SAML:2.0:nameid-format:transient"}, name_id)
|
99
|
+
xml.tag!("saml:SubjectConfirmation", {"Method" => "urn:oasis:names:tc:SAML:2.0:cm:bearer"}) do
|
100
|
+
xml.tag!("saml:SubjectConfirmationData", {"InResponseTo" => get_sp_response_to, "Recipient" => get_sp_destination, "NotOnOrAfter" => time_and_ten})
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# xml.tag!("saml:Conditions", {"NotBefore" => time, "NotOnOrAfter" => time_and_ten}) do
|
105
|
+
# xml.tag!("saml:AudienceRestriction") do
|
106
|
+
# xml.tag!("saml:Audience", get_sp_audience)
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
|
110
|
+
xml.tag!("saml:AuthnStatement", {"AuthnInstant" => time, "SessionIndex" => assert_id}) do
|
111
|
+
xml.tag!("saml:AuthnContext") do
|
112
|
+
xml.tag!("saml:AuthnContextClassRef", "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
xml.target!
|
117
|
+
end
|
118
|
+
|
119
|
+
def make_signature(assertion, assert_id)
|
120
|
+
certificate = File.read("#{Rails.root}/config/cert/#{Rails.env}/idp.ignite.com.crt")
|
121
|
+
# certificate = File.read("#{Rails.root}/config/cert/development/IdpCertificate.cer")
|
122
|
+
certificate.gsub!("-----BEGIN CERTIFICATE-----", "")
|
123
|
+
certificate.gsub!("-----END CERTIFICATE-----", "")
|
124
|
+
|
125
|
+
sign_info_xml_builder = Builder::XmlMarkup.new
|
126
|
+
sign_info_xml_builder.tag!("ds:SignedInfo", {"xmlns:ds"=>"http://www.w3.org/2000/09/xmldsig#"}) do
|
127
|
+
sign_info_xml_builder.tag!("ds:CanonicalizationMethod", {"Algorithm"=>"http://www.w3.org/2001/10/xml-exc-c14n#"})
|
128
|
+
sign_info_xml_builder.tag!("ds:SignatureMethod", {"Algorithm"=>"http://www.w3.org/2000/09/xmldsig#rsa-sha1"})
|
129
|
+
sign_info_xml_builder.tag!("ds:Reference", {"URI" => "##{assert_id}"}) do
|
130
|
+
sign_info_xml_builder.tag!("ds:Transforms") do
|
131
|
+
sign_info_xml_builder.tag!("ds:Transform", {"Algorithm"=>"http://www.w3.org/2000/09/xmldsig#enveloped-signature"})
|
132
|
+
sign_info_xml_builder.tag!("ds:Transform", {"Algorithm"=>"http://www.w3.org/2001/10/xml-exc-c14n#"})
|
133
|
+
end
|
134
|
+
sign_info_xml_builder.tag!("ds:DigestMethod", {"Algorithm"=>"http://www.w3.org/2000/09/xmldsig#sha1"})
|
135
|
+
sign_info_xml_builder.tag!("ds:DigestValue", {"URI" => assert_id}, signature_digest_value(assertion))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
sign_info_xml = sign_info_xml_builder.target!
|
139
|
+
s_value = signature_sign_value(sign_info_xml)
|
140
|
+
xml = Builder::XmlMarkup.new
|
141
|
+
xml.tag!('ds:Signature', {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}) do
|
142
|
+
xml << sign_info_xml
|
143
|
+
xml.tag!("ds:SignatureValue", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}, s_value)
|
144
|
+
xml.tag!('ds:KeyInfo') do
|
145
|
+
xml.tag!('ds:X509Data') do
|
146
|
+
xml.tag!('ds:X509Certificate', certificate)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
xml.target!
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
def signature_sign_value(xml)
|
155
|
+
document = XMLSecurity::SignedDocument.new(xml)
|
156
|
+
canoner = XML::Util::XmlCanonicalizer.new(false, true)
|
157
|
+
signed_info_element = REXML::XPath.first(document, "//ds:SignedInfo", {"ds"=>"http://www.w3.org/2000/09/xmldsig#"})
|
158
|
+
canon_string = canoner.canonicalize(signed_info_element)
|
159
|
+
# private_key = OpenSSL::PKey::RSA.new(File.read("#{Rails.root}/config/cert/#{Rails.env}/idp.ignite.com.key"))
|
160
|
+
#private_key = OpenSSL::PKey::RSA.new(File.read("#{Rails.root}/config/cert/development/SPkey.key"))
|
161
|
+
private_key = OpenSSL::PKey::RSA.new(File.read("#{Rails.root}/config/cert/development/IgniteKeyDecrypted.key"))
|
162
|
+
|
163
|
+
sig = private_key.sign(OpenSSL::Digest::SHA1.new, canon_string)
|
164
|
+
Base64.encode64(sig).chomp
|
165
|
+
end
|
166
|
+
|
167
|
+
def signature_digest_value(xml)
|
168
|
+
document = XMLSecurity::SignedDocument.new(xml)
|
169
|
+
canoner = XML::Util::XmlCanonicalizer.new(false, true)
|
170
|
+
canon_hashed_element = canoner.canonicalize(document)
|
171
|
+
Base64.encode64(Digest::SHA1.digest(canon_hashed_element)).chomp
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|