dynamics_crm 0.4.1 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/dynamics_crm/client.rb +82 -24
- data/lib/dynamics_crm/version.rb +1 -1
- data/lib/dynamics_crm/xml/message_builder.rb +101 -3
- data/spec/fixtures/request_security_token_response_onpremise.xml +78 -0
- data/spec/lib/client_spec.rb +41 -19
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbd8933df47587a12b12a2115afbb57d2e8d2b1c
|
4
|
+
data.tar.gz: 8343d6d884713b0956e71a9ffcde12c6364302b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dec2cf3cd88e6bfbba5ec88fb0d7cf237539de43c2b1102ed3e736df9e20d41dab7713e519ffba54299e8e2c2cb3bd259794f49d37c0f5fbf62d8681cb86f5d0
|
7
|
+
data.tar.gz: 9cc80f8452b8dd72280ef9ac1ca5900bd96f77676ae2f905923e1ea18cc04fed3c338a4a4ce8bf16b7c3e520cf66af7e60d67a0f43733ce1b2fd1265c41e6690
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 0.5.0 (December 19, 2014)
|
2
|
+
|
3
|
+
* Adds On-Premise Authenticate support with specified hostname. PR #18
|
4
|
+
|
5
|
+
## 0.4.1 (November 23)
|
6
|
+
|
7
|
+
* Switch to TLS. SSLv3 has been disabled. PR #19
|
8
|
+
|
1
9
|
## 0.4.0 (October 11, 2014)
|
2
10
|
|
3
11
|
* Adds FetchXml module (Builder, Entity, LinkEntity) for building Xml document. (New dependency on builder)
|
data/lib/dynamics_crm/client.rb
CHANGED
@@ -8,6 +8,9 @@
|
|
8
8
|
# https://community.dynamics.com/crm/b/crmgirishraja/archive/2012/09/04/authentication-with-dynamics-crm-online-on-ocp-office-365.aspx
|
9
9
|
|
10
10
|
require 'forwardable'
|
11
|
+
require 'digest/sha1'
|
12
|
+
require 'openssl'
|
13
|
+
require 'open-uri'
|
11
14
|
|
12
15
|
module DynamicsCRM
|
13
16
|
|
@@ -16,26 +19,28 @@ module DynamicsCRM
|
|
16
19
|
include XML::MessageBuilder
|
17
20
|
|
18
21
|
attr_accessor :logger, :caller_id
|
22
|
+
attr_reader :hostname, :region, :organization_endpoint, :login_url
|
23
|
+
|
24
|
+
OCP_LOGIN_URL = 'https://login.microsoftonline.com/RST2.srf'
|
19
25
|
|
20
26
|
# Initializes Client instance.
|
21
27
|
# Requires: organization_name
|
22
28
|
# Optional: hostname
|
23
29
|
def initialize(config={organization_name: nil, hostname: nil, caller_id: nil, login_url: nil, region: nil})
|
24
|
-
raise RuntimeError.new("organization_name is required") if config[:organization_name].nil?
|
30
|
+
raise RuntimeError.new("organization_name or hostname is required") if config[:organization_name].nil? && config[:hostname].nil?
|
25
31
|
|
26
32
|
@organization_name = config[:organization_name]
|
27
33
|
@hostname = config[:hostname] || "#{@organization_name}.api.crm.dynamics.com"
|
28
34
|
@organization_endpoint = "https://#{@hostname}/XRMServices/2011/Organization.svc"
|
29
35
|
@caller_id = config[:caller_id]
|
30
36
|
|
31
|
-
|
32
37
|
# The Login URL and Region are located in the client's Organization WSDL.
|
33
38
|
# https://tinderboxdev.api.crm.dynamics.com/XRMServices/2011/Organization.svc?wsdl=wsdl0
|
34
39
|
#
|
35
40
|
# Login URL: Policy -> Issuer -> Address
|
36
41
|
# Region: SecureTokenService -> AppliesTo
|
37
|
-
@login_url = config[:login_url]
|
38
|
-
@region = config[:region] ||
|
42
|
+
@login_url = config[:login_url]
|
43
|
+
@region = config[:region] || determine_region
|
39
44
|
end
|
40
45
|
|
41
46
|
# Public: Authenticate User
|
@@ -51,24 +56,47 @@ module DynamicsCRM
|
|
51
56
|
@username = username
|
52
57
|
@password = password
|
53
58
|
|
54
|
-
|
59
|
+
auth_request = if on_premise?
|
60
|
+
build_on_premise_request(username, password, region, login_url)
|
61
|
+
else
|
62
|
+
build_ocp_request(username, password, region, login_url)
|
63
|
+
end
|
64
|
+
|
65
|
+
soap_response = post(login_url, auth_request)
|
55
66
|
|
56
67
|
document = REXML::Document.new(soap_response)
|
57
68
|
# Check for Fault
|
58
69
|
fault_xml = document.get_elements("//[local-name() = 'Fault']")
|
59
70
|
raise XML::Fault.new(fault_xml) if fault_xml.any?
|
60
71
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@
|
65
|
-
@
|
66
|
-
|
67
|
-
@
|
72
|
+
if on_premise?
|
73
|
+
@security_token0 = document.get_elements("//e:CipherValue").first.text.to_s
|
74
|
+
@security_token1 = document.get_elements("//xenc:CipherValue").last.text.to_s
|
75
|
+
@key_identifier = document.get_elements("//o:KeyIdentifier").first.text
|
76
|
+
@cert_issuer_name = document.get_elements("//X509IssuerName").first.text
|
77
|
+
@cert_serial_number = document.get_elements("//X509SerialNumber").first.text
|
78
|
+
@server_secret = document.get_elements("//trust:BinarySecret").first.text
|
79
|
+
|
80
|
+
@header_current_time = get_current_time
|
81
|
+
@header_expires_time = get_current_time_plus_hour
|
82
|
+
@timestamp = '<u:Timestamp xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" u:Id="_0"><u:Created>' + @header_current_time + '</u:Created><u:Expires>' + @header_expires_time + '</u:Expires></u:Timestamp>'
|
83
|
+
@digest_value = Digest::SHA1.base64digest @timestamp
|
84
|
+
@signature = '<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"></SignatureMethod><Reference URI="#_0"><Transforms><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod><DigestValue>' + @digest_value + '</DigestValue></Reference></SignedInfo>'
|
85
|
+
@signature_value = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), Base64.decode64(@server_secret), @signature)).chomp
|
68
86
|
else
|
69
|
-
|
87
|
+
cipher_values = document.get_elements("//CipherValue")
|
88
|
+
|
89
|
+
if cipher_values && cipher_values.length > 0
|
90
|
+
@security_token0 = cipher_values[0].text
|
91
|
+
@security_token1 = cipher_values[1].text
|
92
|
+
# Use local-name() to ignore namespace.
|
93
|
+
@key_identifier = document.get_elements("//[local-name() = 'KeyIdentifier']").first.text
|
94
|
+
else
|
95
|
+
raise RuntimeError.new(soap_response)
|
96
|
+
end
|
70
97
|
end
|
71
98
|
|
99
|
+
|
72
100
|
true
|
73
101
|
end
|
74
102
|
|
@@ -79,7 +107,7 @@ module DynamicsCRM
|
|
79
107
|
entity = XML::Entity.new(entity_name)
|
80
108
|
entity.attributes = XML::Attributes.new(attributes)
|
81
109
|
|
82
|
-
xml_response = post(
|
110
|
+
xml_response = post(organization_endpoint, create_request(entity))
|
83
111
|
return Response::CreateResult.new(xml_response)
|
84
112
|
end
|
85
113
|
|
@@ -89,7 +117,7 @@ module DynamicsCRM
|
|
89
117
|
column_set = XML::ColumnSet.new(columns)
|
90
118
|
request = retrieve_request(entity_name, guid, column_set)
|
91
119
|
|
92
|
-
xml_response = post(
|
120
|
+
xml_response = post(organization_endpoint, request)
|
93
121
|
return Response::RetrieveResult.new(xml_response)
|
94
122
|
end
|
95
123
|
|
@@ -108,7 +136,7 @@ module DynamicsCRM
|
|
108
136
|
query.criteria = XML::Criteria.new(criteria)
|
109
137
|
|
110
138
|
request = retrieve_multiple_request(query)
|
111
|
-
xml_response = post(
|
139
|
+
xml_response = post(organization_endpoint, request)
|
112
140
|
return Response::RetrieveMultipleResult.new(xml_response)
|
113
141
|
end
|
114
142
|
|
@@ -127,20 +155,20 @@ module DynamicsCRM
|
|
127
155
|
entity.attributes = XML::Attributes.new(attributes)
|
128
156
|
|
129
157
|
request = update_request(entity)
|
130
|
-
xml_response = post(
|
158
|
+
xml_response = post(organization_endpoint, request)
|
131
159
|
return Response::UpdateResponse.new(xml_response)
|
132
160
|
end
|
133
161
|
|
134
162
|
def delete(entity_name, guid)
|
135
163
|
request = delete_request(entity_name, guid)
|
136
164
|
|
137
|
-
xml_response = post(
|
165
|
+
xml_response = post(organization_endpoint, request)
|
138
166
|
return Response::DeleteResponse.new(xml_response)
|
139
167
|
end
|
140
168
|
|
141
169
|
def execute(action, parameters={}, response_class=nil)
|
142
170
|
request = execute_request(action, parameters)
|
143
|
-
xml_response = post(
|
171
|
+
xml_response = post(organization_endpoint, request)
|
144
172
|
|
145
173
|
response_class ||= Response::ExecuteResult
|
146
174
|
return response_class.new(xml_response)
|
@@ -148,13 +176,13 @@ module DynamicsCRM
|
|
148
176
|
|
149
177
|
def associate(entity_name, guid, relationship, related_entities)
|
150
178
|
request = associate_request(entity_name, guid, relationship, related_entities)
|
151
|
-
xml_response = post(
|
179
|
+
xml_response = post(organization_endpoint, request)
|
152
180
|
return Response::AssociateResponse.new(xml_response)
|
153
181
|
end
|
154
182
|
|
155
183
|
def disassociate(entity_name, guid, relationship, related_entities)
|
156
184
|
request = disassociate_request(entity_name, guid, relationship, related_entities)
|
157
|
-
xml_response = post(
|
185
|
+
xml_response = post(organization_endpoint, request)
|
158
186
|
return Response::DisassociateResponse.new(xml_response)
|
159
187
|
end
|
160
188
|
|
@@ -250,8 +278,37 @@ module DynamicsCRM
|
|
250
278
|
|
251
279
|
protected
|
252
280
|
|
253
|
-
def
|
281
|
+
def on_premise?
|
282
|
+
@on_premise ||= !(hostname =~ /\.dynamics\.com/i)
|
283
|
+
end
|
284
|
+
|
285
|
+
def organization_wsdl
|
286
|
+
wsdl = open(organization_endpoint + "?wsdl=wsdl0").read
|
287
|
+
@organization_wsdl ||= REXML::Document.new(wsdl)
|
288
|
+
end
|
254
289
|
|
290
|
+
def login_url
|
291
|
+
@login_url ||= if on_premise?
|
292
|
+
(organization_wsdl.document.get_elements("//ms-xrm:Identifier").first.text + "/13/usernamemixed").gsub("http://", "https://")
|
293
|
+
else
|
294
|
+
OCP_LOGIN_URL
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def determine_region
|
299
|
+
case hostname
|
300
|
+
when /crm5\.dynamics\.com/
|
301
|
+
"urn:crmapac:dynamics.com"
|
302
|
+
when /crm4\.dynamics\.com/
|
303
|
+
"urn:crmemea:dynamics.com"
|
304
|
+
when /\.dynamics\.com/
|
305
|
+
"urn:crmna:dynamics.com"
|
306
|
+
else
|
307
|
+
organization_endpoint
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def post(url, request)
|
255
312
|
log_xml("REQUEST", request)
|
256
313
|
|
257
314
|
c = Curl::Easy.new(url) do |http|
|
@@ -261,7 +318,7 @@ module DynamicsCRM
|
|
261
318
|
http.headers["Content-length"] = request.length
|
262
319
|
|
263
320
|
http.ssl_verify_peer = false
|
264
|
-
http.timeout =
|
321
|
+
http.timeout = 120
|
265
322
|
http.follow_location = true
|
266
323
|
http.ssl_version = 1
|
267
324
|
# http.verbose = 1
|
@@ -270,8 +327,9 @@ module DynamicsCRM
|
|
270
327
|
if c.http_post(request)
|
271
328
|
response = c.body_str
|
272
329
|
else
|
273
|
-
|
330
|
+
# Do something here on error.
|
274
331
|
end
|
332
|
+
c.close
|
275
333
|
|
276
334
|
log_xml("RESPONSE", response)
|
277
335
|
|
data/lib/dynamics_crm/version.rb
CHANGED
@@ -6,8 +6,14 @@ module DynamicsCRM
|
|
6
6
|
SecureRandom.uuid
|
7
7
|
end
|
8
8
|
|
9
|
+
# have a bit of flexiblity in the create time to handle when system clocks are out of sync
|
9
10
|
def get_current_time
|
10
|
-
|
11
|
+
# 5.minutes = 5 * 60
|
12
|
+
(Time.now - (5 * 60)).utc.strftime '%Y-%m-%dT%H:%M:%SZ'
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_current_time_plus_hour
|
16
|
+
(Time.now.utc + (60*60)).strftime '%Y-%m-%dT%H:%M:%SZ'
|
11
17
|
end
|
12
18
|
|
13
19
|
def get_tomorrow_time
|
@@ -20,7 +26,7 @@ module DynamicsCRM
|
|
20
26
|
# urn:crmna:dynamics.com - North America
|
21
27
|
# urn:crmemea:dynamics.com - Europe, the Middle East and Africa
|
22
28
|
# urn:crmapac:dynamics.com - Asia Pacific
|
23
|
-
def build_ocp_request(username, password,
|
29
|
+
def build_ocp_request(username, password, region = "urn:crmna:dynamics.com", login_url = Client::OCP_LOGIN_URL)
|
24
30
|
%Q{
|
25
31
|
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
|
26
32
|
xmlns:a="http://www.w3.org/2005/08/addressing"
|
@@ -59,6 +65,41 @@ module DynamicsCRM
|
|
59
65
|
}
|
60
66
|
end
|
61
67
|
|
68
|
+
def build_on_premise_request(username, password, region = "", login_url = nil)
|
69
|
+
%Q{
|
70
|
+
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
|
71
|
+
xmlns:a="http://www.w3.org/2005/08/addressing">
|
72
|
+
<s:Header>
|
73
|
+
<a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>
|
74
|
+
<a:MessageID>urn:uuid:#{uuid()}</a:MessageID>
|
75
|
+
<a:ReplyTo>
|
76
|
+
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
|
77
|
+
</a:ReplyTo>
|
78
|
+
<Security s:mustUnderstand="1" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
79
|
+
<u:Timestamp u:Id="#{uuid()}">
|
80
|
+
<u:Created>#{get_current_time}</u:Created>
|
81
|
+
<u:Expires>#{get_current_time_plus_hour}</u:Expires>
|
82
|
+
</u:Timestamp>
|
83
|
+
<UsernameToken u:Id="#{uuid()}">
|
84
|
+
<Username>#{REXML::Text.new(username).to_s}</Username>
|
85
|
+
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">#{REXML::Text.new(password).to_s}</Password>
|
86
|
+
</UsernameToken>
|
87
|
+
</Security>
|
88
|
+
<a:To s:mustUnderstand="1">#{login_url}</a:To>
|
89
|
+
</s:Header>
|
90
|
+
<s:Body>
|
91
|
+
<trust:RequestSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
|
92
|
+
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
|
93
|
+
<a:EndpointReference>
|
94
|
+
<a:Address>#{region}</a:Address>
|
95
|
+
</a:EndpointReference>
|
96
|
+
</wsp:AppliesTo>
|
97
|
+
<trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
|
98
|
+
</trust:RequestSecurityToken>
|
99
|
+
</s:Body>
|
100
|
+
</s:Envelope>
|
101
|
+
}
|
102
|
+
end
|
62
103
|
|
63
104
|
def build_envelope(action, &block)
|
64
105
|
%Q{
|
@@ -72,9 +113,66 @@ module DynamicsCRM
|
|
72
113
|
end
|
73
114
|
|
74
115
|
def build_header(action)
|
116
|
+
if @on_premise
|
117
|
+
build_on_premise_header(action)
|
118
|
+
else
|
119
|
+
build_ocp_header(action)
|
120
|
+
end
|
121
|
+
end
|
75
122
|
|
76
|
-
|
123
|
+
def build_on_premise_header(action)
|
124
|
+
%Q{
|
125
|
+
<s:Header>
|
126
|
+
<a:Action s:mustUnderstand="1">http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/#{action}</a:Action>
|
127
|
+
<a:MessageID>urn:uuid:#{uuid()}</a:MessageID>
|
128
|
+
<a:ReplyTo>
|
129
|
+
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
|
130
|
+
</a:ReplyTo>
|
131
|
+
<a:To s:mustUnderstand="1">#{@organization_endpoint}</a:To>
|
132
|
+
<o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
133
|
+
#{@timestamp}
|
134
|
+
<xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
135
|
+
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
|
136
|
+
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
|
137
|
+
<e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
|
138
|
+
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
|
139
|
+
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
140
|
+
</e:EncryptionMethod>
|
141
|
+
<KeyInfo>
|
142
|
+
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
143
|
+
<X509Data>
|
144
|
+
<X509IssuerSerial>
|
145
|
+
<X509IssuerName>#{@cert_issuer_name}</X509IssuerName>
|
146
|
+
<X509SerialNumber>#{@cert_serial_number}</X509SerialNumber>
|
147
|
+
</X509IssuerSerial>
|
148
|
+
</X509Data>
|
149
|
+
</o:SecurityTokenReference>
|
150
|
+
</KeyInfo>
|
151
|
+
<e:CipherData>
|
152
|
+
<e:CipherValue>#{@security_token0}</e:CipherValue>
|
153
|
+
</e:CipherData>
|
154
|
+
</e:EncryptedKey>
|
155
|
+
</KeyInfo>
|
156
|
+
<xenc:CipherData>
|
157
|
+
<xenc:CipherValue>#{@security_token1}</xenc:CipherValue>
|
158
|
+
</xenc:CipherData>
|
159
|
+
</xenc:EncryptedData>
|
160
|
+
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
|
161
|
+
#{@signature}
|
162
|
+
<SignatureValue>#{@signature_value}</SignatureValue>
|
163
|
+
<KeyInfo>
|
164
|
+
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
165
|
+
<o:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">#{@key_identifier}</o:KeyIdentifier>
|
166
|
+
</o:SecurityTokenReference>
|
167
|
+
</KeyInfo>
|
168
|
+
</Signature>
|
169
|
+
</o:Security>
|
170
|
+
</s:Header>
|
171
|
+
}
|
172
|
+
end
|
77
173
|
|
174
|
+
def build_ocp_header(action)
|
175
|
+
caller_id = @caller_id ? %Q{<CallerId xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">#{@caller_id}</CallerId>} : ""
|
78
176
|
%Q{
|
79
177
|
<s:Header>
|
80
178
|
<a:Action s:mustUnderstand="1">http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/#{action}</a:Action>
|
@@ -0,0 +1,78 @@
|
|
1
|
+
<s:Envelope xmlns:s='http://www.w3.org/2003/05/soap-envelope' xmlns:a='http://www.w3.org/2005/08/addressing' xmlns:u='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>
|
2
|
+
<s:Header>
|
3
|
+
<a:Action s:mustUnderstand='1'>http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal</a:Action>
|
4
|
+
<a:RelatesTo>urn:uuid:f07eb8c1-cda5-4998-a38d-94f95be07204</a:RelatesTo>
|
5
|
+
<o:Security s:mustUnderstand='1' xmlns:o='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'>
|
6
|
+
<u:Timestamp u:Id='_0'>
|
7
|
+
<u:Created>2014-11-18T19:46:16.557Z</u:Created>
|
8
|
+
<u:Expires>2014-11-18T19:51:16.557Z</u:Expires>
|
9
|
+
</u:Timestamp>
|
10
|
+
</o:Security>
|
11
|
+
</s:Header>
|
12
|
+
<s:Body>
|
13
|
+
<trust:RequestSecurityTokenResponseCollection xmlns:trust='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>
|
14
|
+
<trust:RequestSecurityTokenResponse>
|
15
|
+
<trust:Lifetime>
|
16
|
+
<wsu:Created xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>2014-11-18T19:46:16.542Z</wsu:Created>
|
17
|
+
<wsu:Expires xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'>2014-11-25T19:46:16.542Z</wsu:Expires>
|
18
|
+
</trust:Lifetime>
|
19
|
+
<wsp:AppliesTo xmlns:wsp='http://schemas.xmlsoap.org/ws/2004/09/policy'>
|
20
|
+
<wsa:EndpointReference xmlns:wsa='http://www.w3.org/2005/08/addressing'>
|
21
|
+
<wsa:Address>https://psavtest.crm.powerobjects.net/</wsa:Address>
|
22
|
+
</wsa:EndpointReference>
|
23
|
+
</wsp:AppliesTo>
|
24
|
+
<trust:RequestedSecurityToken>
|
25
|
+
<xenc:EncryptedData Type='http://www.w3.org/2001/04/xmlenc#Element' xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
|
26
|
+
<xenc:EncryptionMethod Algorithm='http://www.w3.org/2001/04/xmlenc#aes256-cbc'/>
|
27
|
+
<KeyInfo xmlns='http://www.w3.org/2000/09/xmldsig#'>
|
28
|
+
<e:EncryptedKey xmlns:e='http://www.w3.org/2001/04/xmlenc#'>
|
29
|
+
<e:EncryptionMethod Algorithm='http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'>
|
30
|
+
<DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>
|
31
|
+
</e:EncryptionMethod>
|
32
|
+
<KeyInfo>
|
33
|
+
<o:SecurityTokenReference xmlns:o='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'>
|
34
|
+
<X509Data>
|
35
|
+
<X509IssuerSerial>
|
36
|
+
<X509IssuerName>
|
37
|
+
SERIALNUMBER=12369287, CN=Go Daddy Secure
|
38
|
+
Certification Authority,
|
39
|
+
OU=http://certificates.godaddy.com/repository,
|
40
|
+
O="GoDaddy.com, Inc.", L=Scottsdale, S=Arizona, C=US
|
41
|
+
</X509IssuerName>
|
42
|
+
<X509SerialNumber>112094107365XXXXX</X509SerialNumber>
|
43
|
+
</X509IssuerSerial>
|
44
|
+
</X509Data>
|
45
|
+
</o:SecurityTokenReference>
|
46
|
+
</KeyInfo>
|
47
|
+
<e:CipherData>
|
48
|
+
<e:CipherValue>ydfdQsDU9ow4XhoBi+0+n+/9Z7DvfivMKlKR8wpPnMeY5fgfmmHPAWfnGhYNYPAXCaGleY7mCqqzLe2BlDQjMKUJ5f4jI3zfoy/3vSZwoJLxIOkklFbtku1ammaKAp+Om6PTqOySbpVWcaI3herX56EzF
|
49
|
+
</e:CipherValue>
|
50
|
+
</e:CipherData>
|
51
|
+
</e:EncryptedKey>
|
52
|
+
</KeyInfo>
|
53
|
+
<xenc:CipherData>
|
54
|
+
<xenc:CipherValue>GcCk8ivhLAAPEbQI8qScynWLReTWE0AC5NGXVGMdaoHTBSSyRUgSPpgZnCNWIqb+8rRI7l7aQNXn6L2ysxTZp+DvxontZc1uarKQWtKDskpfctdrRLfGhcfOb4NtyP9QbKQcdxYSCoQDVCQqrpYLIDHoV+0WLbI4hBQ1u+UzUxr99z+pNMKW6Z2uui+EY21dVX33HZpMWgB7bkzim8KjRulB7/WD1wBarVSIzYuxJdxXP1HcKiz7j/
|
55
|
+
</xenc:CipherValue>
|
56
|
+
</xenc:CipherData>
|
57
|
+
</xenc:EncryptedData>
|
58
|
+
</trust:RequestedSecurityToken>
|
59
|
+
<trust:RequestedProofToken>
|
60
|
+
<trust:BinarySecret>XZwQpJKfAy00NNWU1RwdtDpVyW/nfabuCq4H38GgKrM=</trust:BinarySecret>
|
61
|
+
</trust:RequestedProofToken>
|
62
|
+
<trust:RequestedAttachedReference>
|
63
|
+
<o:SecurityTokenReference k:TokenType='http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1' xmlns:o='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' xmlns:k='http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd'>
|
64
|
+
<o:KeyIdentifier ValueType='http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID'>_ed121435-64ea-45b0-9b15-e5769afdb746</o:KeyIdentifier>
|
65
|
+
</o:SecurityTokenReference>
|
66
|
+
</trust:RequestedAttachedReference>
|
67
|
+
<trust:RequestedUnattachedReference>
|
68
|
+
<o:SecurityTokenReference k:TokenType='http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1' xmlns:o='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' xmlns:k='http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd'>
|
69
|
+
<o:KeyIdentifier ValueType='http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID'>_ed121435-64ea-45b0-9b15-e5769afdb746</o:KeyIdentifier>
|
70
|
+
</o:SecurityTokenReference>
|
71
|
+
</trust:RequestedUnattachedReference>
|
72
|
+
<trust:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</trust:TokenType>
|
73
|
+
<trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
|
74
|
+
<trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>
|
75
|
+
</trust:RequestSecurityTokenResponse>
|
76
|
+
</trust:RequestSecurityTokenResponseCollection>
|
77
|
+
</s:Body>
|
78
|
+
</s:Envelope>
|
data/spec/lib/client_spec.rb
CHANGED
@@ -5,33 +5,55 @@ describe DynamicsCRM::Client do
|
|
5
5
|
let(:subject) { DynamicsCRM::Client.new(organization_name: "tinderboxdev")}
|
6
6
|
|
7
7
|
describe "#authenticate" do
|
8
|
-
it "
|
8
|
+
it "raises arugment error when no parameters are passed" do
|
9
|
+
expect { subject.authenticate() }.to raise_error(ArgumentError)
|
10
|
+
end
|
9
11
|
|
10
|
-
|
12
|
+
context "Online" do
|
13
|
+
it "authenticates with username and password" do
|
11
14
|
|
12
|
-
|
15
|
+
subject.stub(:post).and_return(fixture("request_security_token_response"))
|
13
16
|
|
14
|
-
|
15
|
-
subject.instance_variable_get("@security_token1").should start_with("CX7BFgRnW75tE6GiuRICjeVDV+6q4KDMKLyKmKe9A8U")
|
16
|
-
subject.instance_variable_get("@key_identifier").should == "D3xjUG3HGaQuKyuGdTWuf6547Lo="
|
17
|
-
end
|
17
|
+
subject.authenticate('testing', 'password')
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
subject.instance_variable_get("@security_token0").should start_with("tMFpDJbJHcZnRVuby5cYmRbCJo2OgOFLEOrUHj+wz")
|
20
|
+
subject.instance_variable_get("@security_token1").should start_with("CX7BFgRnW75tE6GiuRICjeVDV+6q4KDMKLyKmKe9A8U")
|
21
|
+
subject.instance_variable_get("@key_identifier").should == "D3xjUG3HGaQuKyuGdTWuf6547Lo="
|
22
|
+
end
|
23
|
+
|
24
|
+
# This is only method in this suite that actually sends a POST message to Dynamics.
|
25
|
+
# This covers the post() and fault parsing logic.
|
26
|
+
it "fails to authenticate with invalid credentials" do
|
27
|
+
begin
|
28
|
+
subject.authenticate('testuser@orgnam.onmicrosoft.com', 'qwerty')
|
29
|
+
fail("Expected Fault to be raised")
|
30
|
+
rescue DynamicsCRM::XML::Fault => f
|
31
|
+
f.code.should == "S:Sender"
|
32
|
+
f.subcode.should == "wst:FailedAuthentication"
|
33
|
+
f.reason.should == "Authentication Failure"
|
34
|
+
end
|
35
|
+
end
|
21
36
|
end
|
22
37
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
38
|
+
context "On-Premise" do
|
39
|
+
let(:subject) { DynamicsCRM::Client.new(organization_name: "psavtest", hostname: "psavtest.crm.powerobjects.net")}
|
40
|
+
|
41
|
+
it "authenticates with username and password" do
|
42
|
+
|
43
|
+
subject.stub(:post).and_return(fixture("request_security_token_response_onpremise"))
|
44
|
+
|
45
|
+
subject.authenticate('testing', 'password')
|
46
|
+
|
47
|
+
subject.instance_variable_get("@security_token0").should start_with("ydfdQsDU9ow4XhoBi+0+n+/9Z7Dvfi")
|
48
|
+
subject.instance_variable_get("@security_token1").should start_with("GcCk8ivhLAAPEbQI8qScynWLReTWE0AC5")
|
49
|
+
subject.instance_variable_get("@key_identifier").should == "_ed121435-64ea-45b0-9b15-e5769afdb746"
|
50
|
+
|
51
|
+
subject.instance_variable_get("@cert_issuer_name").strip.should start_with("SERIALNUMBER=12369287, CN=Go Daddy Secure")
|
52
|
+
subject.instance_variable_get("@cert_serial_number").should == "112094107365XXXXX"
|
53
|
+
subject.instance_variable_get("@server_secret").should == "XZwQpJKfAy00NNWU1RwdtDpVyW/nfabuCq4H38GgKrM="
|
33
54
|
end
|
34
55
|
end
|
56
|
+
|
35
57
|
end
|
36
58
|
|
37
59
|
describe "#retrieve" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamics_crm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Heth
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: curb
|
@@ -182,6 +182,7 @@ files:
|
|
182
182
|
- spec/fixtures/lose_opportunity_response.xml
|
183
183
|
- spec/fixtures/receiver_fault.xml
|
184
184
|
- spec/fixtures/request_security_token_response.xml
|
185
|
+
- spec/fixtures/request_security_token_response_onpremise.xml
|
185
186
|
- spec/fixtures/retrieve_account_all_columns.xml
|
186
187
|
- spec/fixtures/retrieve_all_entities.xml
|
187
188
|
- spec/fixtures/retrieve_attribute_identifier_response.xml
|
@@ -232,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
232
233
|
version: '0'
|
233
234
|
requirements: []
|
234
235
|
rubyforge_project:
|
235
|
-
rubygems_version: 2.
|
236
|
+
rubygems_version: 2.4.5
|
236
237
|
signing_key:
|
237
238
|
specification_version: 4
|
238
239
|
summary: Ruby gem for integrating with MS Dynamics 2011/2013 SOAP API
|
@@ -245,6 +246,7 @@ test_files:
|
|
245
246
|
- spec/fixtures/lose_opportunity_response.xml
|
246
247
|
- spec/fixtures/receiver_fault.xml
|
247
248
|
- spec/fixtures/request_security_token_response.xml
|
249
|
+
- spec/fixtures/request_security_token_response_onpremise.xml
|
248
250
|
- spec/fixtures/retrieve_account_all_columns.xml
|
249
251
|
- spec/fixtures/retrieve_all_entities.xml
|
250
252
|
- spec/fixtures/retrieve_attribute_identifier_response.xml
|