I created domain user 'jsmith' in Active Directory and i've added that domain account as a user in Dynamics CRM. My goal here is to execute code with a service account that is in the PrivUserGroup for the organization while impersonating 'jsmith'. I instantiate the CrmDataContext by passing it an instance of CrmConnection. When calling the constructor of the CrmConnection I pass it the name of my connection string in the application config file then I set the ImpersonatedUser property to the system user id of 'jsmith'. One thing to note is that I'm using a console application to run this. View my code below:
Connection String in app.config:
<add name="Crm" connectionString="Authentication Type=AD; Server=http://dev01/myorg; User ID=myorgdomain\sv-crm; Password=password123" />
CrmDataContext and GetEntities code:
var connection = new CrmConnection("Crm");
connection.ImpersonatedUser = Guid.Parse("1937F45C-8EB4-E011-8FE4-005056887B79");
var crm = new CrmDataContext(connection);
var contacts = crm.GetEntities("contacts")
if(contacts.Count() > 0) //the call to Count() is where the error gets thrown. Invalid user auth.
//do something
I have no issues when trying to impersonate with my own system user id which is tied to my AD domain account that I'm logged in as while running the tests. I get results back just fine so I know there is no issue with the service account that is being used to execute the code. I've even assigned 'jsmith' to the same business unit and put him in the same roles that I'm in (which is System Administrator) and I still get the Invalid user auth. What could I possibly be missing. Below is error information in the trace file on the server. In the trace information below the one thing that does stick out is the first line: "[2011-07-22 18:14:08.0] Process: w3wp |Organization:f827deb3-c6cc-df11-bc07-005056887b79 |Thread: 6 |Category: Exception |User: 822138f1-c574-e011-9dca-005056887b79 |Level: Error | CrmException..ctor*". The User id that is being display is my system user id. It seems like it would show the id of the service account from the connection string or the id 'jblow' who is being impersonated. Any ideas would be greatly appreciated.
[2011-07-22 18:14:08.0] Process: w3wp |Organization:f827deb3-c6cc-df11-bc07-005056887b79 |Thread: 6 |Category: Exception |User: 822138f1-c574-e011-9dca-005056887b79 |Level: Error | CrmException..ctor
at CrmException..ctor(Int32 errorCode, Object[] arguments)
at SecurityHelper.VerifyAndReturnCurrentCallerId(Guid userId, Guid callerId, Guid orgId)
at CrmWebService.get_CurrentCallerId()
at CrmService.Execute(Request request)
at RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at LogicalMethodInfo.Invoke(Object target, Object[] values)
at WebServiceHandler.Invoke()
at WebServiceHandler.CoreProcessRequest()
at SyncSessionlessHandler.ProcessRequest(HttpContext context)
at CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
at ApplicationStepManager.ResumeSteps(Exception error)
at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
at HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr)
at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)
>Crm Exception: Message: Invalid user auth., ErrorCode: -2147220988
[2011-07-22 18:14:08.0] Process: w3wp |Organization:f827deb3-c6cc-df11-bc07-005056887b79 |Thread: 6 |Category: Platform.Sdk |User: 822138f1-c574-e011-9dca-005056887b79 |Level: Error | CompositeSoapExtensionExceptionHandler.Handle
at CompositeSoapExtensionExceptionHandler.Handle(Stream to, Stream from, Exception exception)
at CrmAuthenticationSoapExtensionBase.ProcessMessage(SoapMessage message)
at SoapMessage.RunExtensions(SoapExtension[] extensions, Boolean throwOnException)
at SoapServerProtocol.WriteException(Exception e, Stream outputStream)
at WebServiceHandler.WriteException(Exception e)
at WebServiceHandler.Invoke()
at WebServiceHandler.CoreProcessRequest()
at SyncSessionlessHandler.ProcessRequest(HttpContext context)
at CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
at ApplicationStepManager.ResumeSteps(Exception error)
at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
at HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr)
at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)
>CrmSoapExtension detected CrmException:
System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> Microsoft.Crm.CrmException: Invalid user auth.
at Microsoft.Crm.Sdk.SecurityHelper.VerifyAndReturnCurrentCallerId(Guid userId, Guid callerId, Guid orgId)
at Microsoft.Crm.WebServices.Crm2007.CrmWebService.get_CurrentCallerId()
at Microsoft.Crm.Sdk.Crm2007.CrmService.Execute(Request request)
--- End of inner exception stack trace ---
UPDATE 7/25:
I decided to perform a test by making a call like I've been doing using the CrmDataContext where the CrmConnection.ImpersonatedUser is set to the jsmith id. Then I made another call (exact same query) using the old school approach where you build the QueryExpression and pass it into the CrmService where the CallerId property of the CrmAuthenticationToken is set to the id for jsmith. The using CrmDataContext failed with the "Invalid user auth" error but the other call using the CrmService and QueryExpression ran fine. I used fiddler to look at the raw http request for each of those calls. The raw request was the exact same for both calls except for one thing.... The Negotiate token in the Authorization header of the request. I made both calls, one right after the other, in my console app and they produce different Negotiate tokens. That's got to be the problem although I don't know how to fix. Seems like this is a bug in the Advanced Developer Extensions. Below are the raw http for both.
--using CrmDataContext
POST http://myserver/MSCRMServices/2007/CrmService.asmx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.235)
VsDebuggerCausalityData: uIDPo6mcKDyuc+pPqk3LRv81TrIAAAAA/j8K/SLE5EivZ+mzg1+doYkmNLjkHbFHmbD9UyYmHFEACQAA
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://schemas.microsoft.com/crm/2007/WebServices/Execute"
Accept-Encoding: gzip,gzip
Authorization: Negotiate YIIIrgYGKwYBBQUCoIIIojCCCJ6g.....
Host: myserver
Content-Length: 1281
Expect: 100-continue
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices">
<AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">0</AuthenticationType>
<OrganizationName xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">myorg</OrganizationName>
<CallerId xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">1937f45c-8eb4-e011-8fe4-005056887b79</CallerId>
</CrmAuthenticationToken>
</soap:Header>
<soap:Body>
<Execute xmlns="http://schemas.microsoft.com/crm/2007/WebServices">
<Request xsi:type="RetrieveMultipleRequest" ReturnDynamicEntities="true">
<Query xmlns:q1="http://schemas.microsoft.com/crm/2006/Query" xsi:type="q1:QueryExpression">
<q1:EntityName>contact</q1:EntityName>
<q1:ColumnSet xsi:type="q1:AllColumns" />
<q1:Distinct>false</q1:Distinct>
<q1:PageInfo>
<q1:PageNumber>1</q1:PageNumber>
<q1:Count>100</q1:Count>
</q1:PageInfo>
<q1:LinkEntities />
<q1:Criteria>
<q1:FilterOperator>And</q1:FilterOperator>
<q1:Conditions />
<q1:Filters />
</q1:Criteria>
<q1:Orders />
</Query>
</Request>
</Execute>
</soap:Body>
</soap:Envelope>
--call using CrmService with QueryExpression
POST http://myserver/MSCrmServices/2007/CrmService.asmx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.235)
VsDebuggerCausalityData: uIDPo8cVsRu/YZBCl+8cnC9j5fwAAAAAGni8rU7A/Uy4JYm/bi/S6d/soXPiw+xBoKSYCD/1KRIACQAA
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://schemas.microsoft.com/crm/2007/WebServices/Execute"
Authorization: Negotiate YIIG5wYGKwYBBQUCoIIG2zCCBtegMDAuBgkqhk.....
Host: myserver
Content-Length: 1219
Expect: 100-continue
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices">
<AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">0</AuthenticationType>
<OrganizationName xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">myorg</OrganizationName>
<CallerId xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">1937f45c-8eb4-e011-8fe4-005056887b79</CallerId>
</CrmAuthenticationToken>
</soap:Header>
<soap:Body>
<Execute xmlns="http://schemas.microsoft.com/crm/2007/WebServices">
<Request xsi:type="RetrieveMultipleRequest" ReturnDynamicEntities="false">
<Query xmlns:q1="http://schemas.microsoft.com/crm/2006/Query" xsi:type="q1:QueryExpression">
<q1:EntityName>contact</q1:EntityName>
<q1:ColumnSet xsi:type="q1:AllColumns" />
<q1:Distinct>false</q1:Distinct>
<q1:PageInfo>
<q1:PageNumber>1</q1:PageNumber>
<q1:Count>100</q1:Count>
</q1:PageInfo>
<q1:Criteria>
<q1:FilterOperator>And</q1:FilterOperator>
</q1:Criteria>
</Query>
</Request>
</Execute>
</soap:Body>
</soap:Envelope>
Sounds like you may have found a bug. I would open a free support ticket with Microsoft # 1-877-276-2464. They would be faster with resolution than what you're finding here and the outcome would be conclusive.
I never figured out the problem. But, my work-around is to just build a connection string at runtime that contains the username and password of the person i would want to impersonate via the CallerId property. So I want really be impersonated and I guess for my situation it really does not matter.
Related
We're attempting to integrate with the QuickBooks Online V2 api using Ruby 1.9.3 (not RoR).
Using the API Explorer and the Employee endpoint documentation we were able to get a simple list of test employees by using the Google Signet OAuth Gem.
require 'signet'
require 'signet/oauth_1/client'
#intialize oauth1 client
#client = Signet::OAuth1::Client.new(
:temporary_credential_uri => "https://oauth.intuit.com/oauth/v1/get_request_token",
:authorization_uri => "https://appcenter.intuit.com/Connect/Begin",
:token_credential_uri => "https://oauth.intuit.com/oauth/v1/get_access_token",
:client_credential_key => 'qyprdPEfJqU7eOze0Fby9iYhrUS5DQ',
:client_credential_secret => 'fuXsasJo4TrTEd3Yhv4TeMUizmtguh0JioIB5r2I',
:callback => "http://localhost:3000/callback/general"
)
#client.token_credential_key = 'qyprdJUtDSk7owxVfZlq7JeWO1mtpHBkSMD5GhB02PwIC6N0'
#client.token_credential_secret = 'Rq2ekgQWWL9frZAKpcgWef291mR0J5HBE354u5F3'
#setup request
original_request = [
'POST',
'https://qbo.sbfinance.intuit.com/resource/employees/v2/791630875',
# we also tried this url 'https://qbo.intuit.com/qbo28/resource/employees/v2/791630875',
[
['Content-Type', 'application/x-www-form-urlencoded'],
],
[]
]
#execute request
response = #client.fetch_protected_resource(:request => original_request)
puts response.body
As you can see the request is pretty straight-forward.
However once we create a request with a Filter in the body, we get an HTML page with the following error: HTTP Status 401 - message=Exception authenticating OAuth; errorCode=003200; statusCode=401
#setup request
original_request = [
'POST',
'https://qbo.intuit.com/qbo28/resource/employees/v2/791630875',
#'https://qbo.sbfinance.intuit.com/resource/employees/v2/791630875',
[
['Content-Type', 'application/x-www-form-urlencoded'],
],
["Filter=Name :EQUALS: Doe"]
]
We're using the Google OAuth gem, and I've verified the signature generation to be correct using these tools: LinkedIn Oauth Test Console and Beginners guide to OAuth signing requests. They both verify that the signature that Signet is generating is correct for the body I provide.
I've looked at a few SO Questions:
QuickBooks Online querying with filter returns 401 everytime
Unable to create(POST) objects (Account, customer...) on QB Windows using IDS and Sync Manager
But nothing has worked. Any help would be appreciated, we're willing to use a third party gem such as quickeebooks but we would rather not. I assume I'm just missing something simple here.
Please provide me with the following items so I can verify a working answer:
Your request parameters, including uri, header, body and exact client and access tokens (developer app tokens only please, I'll need to verify that I can generate the exact same request, including signature)
You basestring used for generating the HMAC-SHA1 signature. it will look something like
POST&https%3A%2F%2Fqbo.intuit.com%2Fqbo28%2Fresource%2Femployees%2Fv2%2F791630875&Filter%3DName%2520%253AEQUALS%253A%2520David%2520Test%26oauth_consumer_key%3DqyprdPEfJqU7eOze0Fby9iYhrUS5DQ%26oauth_nonce%3D-1787433535548338293%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1380089100%26oauth_token%3DqyprdJUtDSk7owxVfZlq7JeWO1mtpHBkSMD5GhB02PwIC6N0%26oauth_version%3D1.0
Your response, including header and body data
I had tried to use filter query with employee endpoint. It works fine.
EDIT - Sharing endpoint, filter and resultset related to Employee API Endpoint
https://qbo.intuit.com/qbo28/resource/employees/v2/791926875
Filter= Name :EQUALS: Manas Mukherjee
header - "Authorization: OAuth oauth_token="2eRrd7LhEtHrM1CrqWvy1kmSgeukEgFxW99E1xwhSsLCp1JB", oauth_consumer_key="qyprdXsaKh0a132eNs7NTJLufjfrzm", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1380084612", oauth_nonce="1556081845430558974", oauth_signature="IMjh%2FTx%2F7GMFDE6WQqZK8b6apjI%3D"[\r][\n]"
Content-Type: application/x-www-form-urlencoded
Data Set
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<qbo:SearchResults xmlns="http://www.intuit.com/sb/cdm/v2" xmlns:qbp="http://www.intuit.com/sb/cdm/qbopayroll/v1" xmlns:qbo="http://www.intuit.com/sb/cdm/qbo">
<qbo:CdmCollections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Employees">
<Employee>
<Id idDomain="QBO">20</Id>
<SyncToken>0</SyncToken>
<MetaData>
<CreateTime>2013-09-24T21:37:22-07:00</CreateTime>
<LastUpdatedTime>2013-09-24T21:37:22-07:00</LastUpdatedTime>
</MetaData>
<Name>Manas Mukherjee</Name>
<Address>
<Line1>ABC Str</Line1>
<City>London</City>
<PostalCode>4353543</PostalCode>
<GeoCode>LAT=51.5148382,LNG=-0.1264144</GeoCode>
</Address>
<GivenName>Manas</GivenName>
<MiddleName>Kr</MiddleName>
<FamilyName>Mukherjee</FamilyName>
<ShowAs>Manas Kr Mukherjee</ShowAs>
<BillableTime>false</BillableTime>
</Employee>
</qbo:CdmCollections>
<qbo:Count>1</qbo:Count>
<qbo:CurrentPage>1</qbo:CurrentPage>
</qbo:SearchResults>
OAuth header using your tokens
"Authorization: OAuth oauth_token="qyprdJUtDSk7owxVfZlq7JeWO1mtpHBkSMD5GhB02PwIC6N0", oauth_consumer_key="qyprdPEfJqU7eOze0Fby9iYhrUS5DQ", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1380089100", oauth_nonce="-1787433535548338293", oauth_signature="Vj67xMVhSKGjVSmGyOxt7SVv0i8%3D"[\r][\n]"
Endpoint - https://qbo.intuit.com/qbo28/resource/employees/v2/791630875
Post data to end point: Filter= Name :EQUALS: David Test
Content-Type: application/x-www-form-urlencoded
It works fine
Thanks
See this sample fiddler request with Filter for items in QBO. I cannot paste the fiddler log here. You can do it for similarly for Employee. The filters should go into the body and encode your header:
Request-
POST https://qbo.intuit.com/qbo1/resource/items/v2/723488155
HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Authorization: OAuth oauth_token="lvprdgF9q4mSQx5A6lKNm3NISXvwIpF16z",oauth_nonce="3740352e-20a4-4d45-af4f-2b783ee20e60",oauth_consumer_key="qyprd7I5WvVnPoiBh1ejZn",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1377106651",oauth_version="1.0",oauth_signature="1OAJXk5uH0sEpYpdhh%2BDMzjQFEs%3D"
Host: qbo.intuit.com
Content-Length: 28
Expect: 100-continue
PageNum=1&ResultsPerPage=100
Response Header-
HTTP/1.1 200 OK
Date: Wed, 21 Aug 2013 17:37:31 GMT
Server: Apache
Set-Cookie: qboeuid=10.129.32.5.1377106651774076; path=/; expires=Thu, 21-Aug-14 17:37:31 GMT; domain=.intuit.com
Set-Cookie: JSESSIONID=82DE11473B5246497B9FDCD8A6DA4C45.c1-pprdqboas30j; Path=/; Secure; HttpOnly
Vary: Accept-Encoding
Content-Type: application/xml;charset=UTF-8
Content-Length: 32525
I'd like to configure ServiceStack.net to authenticate using the OAuthProvider against Google. Here is my current configuration:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new OAuthProvider(appSettings,
"https://accounts.google.com/o/oauth2/auth",
"google",
"Google Client ID",
"Google Client secret")
}));
However, I get the following error:
response Status
error Code ArgumentNullException
message String reference not set to an instance of a String. Parameter name: s
stack Trace
[Auth: 8/19/2013 7:48:47 PM]: [REQUEST: {provider:google}] System.ArgumentNullException: String reference not set to an instance of a String. Parameter name: s at System.Text.Encoding.GetBytes(String s) at ServiceStack.ServiceInterface.Auth.OAuthUtils.PercentEncode(String s) at ServiceStack.ServiceInterface.Auth.OAuthAuthorizer.<>c__DisplayClass3.<MakeSignature>b__1(String k) at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() at System.Linq.Buffer`1..ctor(IEnumerable`1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source) at ServiceStack.ServiceInterface.Auth.OAuthAuthorizer.MakeSignature(String method, String base_uri, Dictionary`2 headers) at ServiceStack.ServiceInterface.Auth.OAuthAuthorizer.AcquireRequestToken() at ServiceStack.ServiceInterface.Auth.OAuthProvider.Authenticate(IServiceBase authService, IAuthSession session, Auth request) at ServiceStack.ServiceInterface.Auth.AuthService.Authenticate(Auth request, String provider, IAuthSession session, IAuthProvider oAuthConfig) at ServiceStack.ServiceInterface.Auth.AuthService.Post(Auth request) at lambda_method(Closure , Object , Object ) at ServiceStack.ServiceHost.ServiceRunner`1.Execute(IRequestContext requestContext, Object instance, TRequest request)
According to the network trace, nothing ever hits Google.
Thanks!
I also had this problem. My solution was to double check the Web.Config in the root of the service. I didn't have the ServiceStack OAuth Config setup correctly there.
Use the keys:
<add key="oauth.GoogleOpenId.RedirectUrl" value="http://bootstrapapi.apphb.com/friends"/>
<add key="oauth.GoogleOpenId.CallbackUrl" value="http://bootstrapapi.apphb.com/api/auth/GoogleOpenId"/>
and the Google IAuthProvider
new GoogleOpenIdOAuthProvider(appSettings), //Sign-in with Google OpenId
I am trying to access a SOAP service which has the following charateristics:
1. WSDL is available without authentication
2. The service is accessible over https with basic_auth
3. According to the SOAP service, the "Authorization" header needs to be included in every request
Here is my code for construction the soap client:
#wsdl="https://example.com/table.do&WSDL"
#proxy="http://internal.proxy.com:8080"
#basic_auth=["user","pass"]
#headers={"Authorization" => "Basic"}
client = Savon.client do |globals|
globals.wsdl #wsdl
globals.proxy #proxy
globals.basic_auth #basic_auth
globals.headers #headers
end
Here is the client.globals.inspect:
#<Savon::GlobalOptions:0x0000000382c7a8 #options={:encoding=>"UTF-8",
:soap_version=>1, :namespaces=>{}, :logger=>#<Logger:0x0000000382c730 #progname=nil,
#level=0, #default_formatter=#<Logger::Formatter:0x0000000382c708
#datetime_format=nil>,
#formatter=nil, #logdev=#<Logger::LogDevice:0x0000000382c6b8 #shift_size=nil,
#shift_age=nil, #filename=nil, #dev=#<IO:<STDOUT>>,
#mutex=#<Logger::LogDevice::LogDeviceMutex:0x0000000382c690 #mon_owner=nil,
#mon_count=0, #mon_mutex=#<Mutex:0x0000000382c640>>>>, :log=>true, :filters=>[],
:pretty_print_xml=>false, :raise_errors=>true, :strip_namespaces=>true,
:convert_response_tags_to=>#<Proc:0x0000000382c5c8#/usr/local/rvm/gems/ruby-2.0.0-p0/gems/savon-2.1.0/lib/savon/options.rb:48 (lambda)>,
:wsdl=>"https://example.com/table.do&WSDL",
:proxy=>"http://internal.proxy.com:8080",
:basic_auth=>["user", "pass"],
:headers=>{"Authorization"=>"Basic"}}>
when i call:
client.call(:get, message: { sys_id: "67d2f77ed9377840c53fc6da9c094635" })
The service is returning:
DEBUG -- : HTTPI GET request to xseaddev.service-now.com (net_http)
INFO -- : SOAP request: https://xseaddev.service-now.com/change_task_list.do?displayvalue=all&SOAP
INFO -- : SOAPAction: "http://www.service-now.com/change_task/get", Content-Type: text/xml;charset=UTF-8, Content-Length: 408
DEBUG -- : <?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.service-now.com" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="http://www.service-now.com/change_task"><env:Body><ins0:get><tns:sysId>67d2f77ed9377840c53fc6da9c094635</tns:sysId></ins0:get></env:Body></env:Envelope>
DEBUG -- : HTTPI POST request to xseaddev.service-now.com (net_http)
INFO -- : SOAP response (status 401)
Am i constructing the client incorrectly? I don't see any authentication headers being sent on either the GET or PoST requests.
Do i need to add/remove anything to get this working? Your help would be appreciated.
I imagine you should be able to do the following:
client = Savon.client(endpoint: ENDPOINT, namespace: NAMESPACE, basic_auth: ["user", "password"])
(Or the same in block form).
Check this page under the "Authentication" heading: http://savonrb.com/version2/globals.html
(I am doing the same, but with a WSDL).
I've run into an issue where the Savon Ruby Gem generates a SOAP API call that fails, however when I copy and paste the exact same XML message into SOAP-UI it succeeds.
I send this message:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tem="http://tempuri.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:vis="http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts">
<soapenv:Body>
<tem:CameraConfiguration>
<tem:request>
<vis:ClientToken>5555</vis:ClientToken>
<vis:DeviceID>26219</vis:DeviceID>
<vis:Enabled>1</vis:Enabled>
<vis:Interval>60</vis:Interval>
</tem:request>
</tem:CameraConfiguration>
</soapenv:Body>
To this API (A remote web camera configuration):
https://oapqa.onasset.com/Services/SecureOriginCommand.svc?wsdl
But it fails with this message:
SOAP response (status 500):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault><faultcode xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</faultcode>
<faultstring xml:lang="en-US">The message with Action 'oapSetSentryReportingIntervalRequest' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None)</faultstring>
</s:Fault>
</s:Body>
My first thought was that I must have made a typo in the action name. But
no, when I try the exact same message in SOAP-UI I get the following
success:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<CameraConfigurationResponse xmlns="http://tempuri.org/">
<CameraConfigurationResult xmlns:a="http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Error/>
<a:Result>true</a:Result>
</CameraConfigurationResult>
</CameraConfigurationResponse>
</s:Body>
</s:Envelope>
This leads me to believe that the issue is not being caused by the format
of my xml message but with the way I'm configuring my client. Here is the actual code:
Savon.configure do |config|
config.log = :debug
config.env_namespace = :soapenv
config.raise_errors = false
end
# TODO Enable ssl certficate verification
client = Savon::Client.new do
wsdl.document = TARGET_SO_WSDL
http.auth.ssl.verify_mode = :none
end
resp = client.request 'tem', 'CameraConfiguration' do
soap.namespaces['xmlns:vis'] = 'http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts'
soap.namespaces['xmlns:tem'] = 'http://tempuri.org/'
soap.body = {
'tem:request' => {
'vis:ClientToken' => ON_ASSET_API_KEY,
'vis:DeviceID' => webcam.gps_device.device_id,
'vis:Enabled' => 1,
'vis:Interval' => webcam.report_interval
}
}
end
I've spoken to the developer who maintains the API I'm trying to
access. I thought his response could provide a clue:
Binding on the RemoteSentryService was set to mexHttpBinding instead of mexHttpsBinding.
I don’t think this should give you a fault exception, because its working on .NET simulator client I have. And this endpoint is only used to generate the wsdl (MetaExchange Binding). But, given you are using a different client, I would still give it a shot.
I also regenerated the proxy from wsdl and updated my sample simulator and it looks good.
Is this issue a known issue with Savon and Microsoft SOAP
endpoints or HTTPS? Or is this issue something only I'm encountering?
Debugged it and noticed that Savon unfortunately doesn't send the correct SOAPAction HTTP header. FYI: After sending a SOAP request via soapUI, you can click on the "RAW" tab (vertically aligned on the request window) to investigate further.
Here's the complete example:
client = Savon::Client.new do
wsdl.document = TARGET_SO_WSDL
http.auth.ssl.verify_mode = :none
end
resp = client.request 'tem', 'CameraConfiguration' do
# Notice, that the SOAPAction needs to the wrapped in double quotes:
http.headers['SOAPAction'] = %("http://tempuri.org/ISecureOriginCommand/CameraConfiguration")
soap.namespaces['xmlns:vis'] = 'http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts'
soap.body = {
'tem:request' => {
'vis:ClientToken' => 5555,
'vis:DeviceID' => 26219,
'vis:Enabled' => 1,
'vis:Interval' => 60
}
}
end
Hope it works for you!
I've used SOAP before but many, many years ago and try to stay clear if I can help it.
Short story...
Using "Soap Client" OS X app I can get the response I wish.
If I use client.request :foo_web_serivce, :language_count, :body => { :foo_app_id => "...", :session_id => "..." } then I get a Status 200 but my results are -1 as I believe the app_id is not recognised.
If I try to replicate the Soap Client call (<LanguageCount>) rather than Savon's call (<foo_web_service:LanguageCount>) then I get an Status 500.
Please advise and thanks in advance.
Long Story...
Using SOAP Client I can see that the correct request XML is:
User-Agent: Mac OS X; WebServicesCore (357)
Content-Type: text/xml
Host: redacted
Soapaction: http://tempuri.org/redacted/LanguageCount
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<LanguageCount xmlns="http://tempuri.org/">
<fooAppID xsi:type="xsd:string">12345678-1234-1234-1234-123456789123</fooAppID>
<sessionID xsi:type="xsd:string">12345678-1234-1234-1234-123456789123</sessionID>
</LanguageCount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
However, when I try to do it in the console like so:
ruby-1.8.7-p302 > client = Savon::Client.new do |wsdl, http, wsse|
ruby-1.8.7-p302 > wsdl.document = "http://FooService.cloudapp.net/FooService.svc?wsdl"
ruby-1.8.7-p302 ?> end
=> #<Savon::Client:0x1055e2dc8 #http=#<HTTPI::Request:0x1055e2bc0>, #wsse=#<Savon::WSSE:0x1055e2b98>, #wsdl=#<Savon::WSDL::Document:0x1055e2c10 #request=#<HTTPI::Request:0x1055e2bc0>, #document="http://FooService.cloudapp.net/FooService.svc?wsdl">>
ruby-1.8.7-p302 >
ruby-1.8.7-p302 > client.request :language_count, :body => {:Foo_app_id => "12345678-1234-1234-1234-123456789123", :session_id => "12345678-1234-1234-1234-123456789123"}
Retrieving WSDL from: http://FooService.cloudapp.net/FooService.svc?wsdl
HTTPI executes HTTP GET using the net_http adapter
SOAP request: http://Fooservice.cloudapp.net/FooService.svc
SOAPAction: "LanguageCount", Content-Type: text/xml;charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:wsdl="http://tempuri.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><LanguageCount><FooAppId>12345678-1234-1234-1234-123456789123</FooAppId><sessionId>12345678-1234-1234-1234-123456789123</sessionId></LanguageCount></env:Body></env:Envelope>
HTTPI executes HTTP POST using the net_http adapter
SOAP response (status 500):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><s:Fault><faultcode xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</faultcode><faultstring xml:lang="en-US">The message with Action 'LanguageCount' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).</faultstring></s:Fault></s:Body></s:Envelope>
Savon::SOAP::Fault: (a:ActionNotSupported) The message with Action 'LanguageCount' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
But when I use the namespace of foo_web_service, I get:
ruby-1.8.7-p302 > client = Savon::Client.new do |wsdl, http, wsse| ruby-1.8.7-p302 > wsdl.document = "http://FooService.cloudapp.net/FooService.svc?wsdl" ruby-1.8.7-p302 ?> end =>
#<Savon::Client:0x105882498 #http=#<HTTPI::Request:0x105882358>, #wsse=#<Savon::WSSE:0x105882330>, #wsdl=#<Savon::WSDL::Document:0x105882380 #request=#<HTTPI::Request:0x105882358>, #document="http://FooService.cloudapp.net/FooService.svc?wsdl">> ruby-1.8.7-p302 > ruby-1.8.7-p302 > client.request :Foo_web_service, :language_count, :body => {:Foo_app_id
=> "12345678-1234-1234-1234-123456789123", :session_id => "12345678-1234-1234-1234-123456789123"} Retrieving WSDL from: http://FooService.cloudapp.net/FooService.svc?wsdl HTTPI executes HTTP GET using the net_http adapter SOAP request: http://Fooservice.cloudapp.net/FooService.svc SOAPAction: "http://tempuri.org/FooService/LanguageCount", Content-Type: text/xml;charset=UTF-8 <?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:Foo_web_service="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><Foo_web_service:LanguageCount><FooAppId>12345678-1234-1234-1234-123456789123</FooAppId><sessionId>12345678-1234-1234-1234-123456789123</sessionId></Foo_web_service:LanguageCount></env:Body></env:Envelope> HTTPI executes HTTP POST using the net_http adapter SOAP response (status 200): <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><LanguageCountResponse xmlns="http://tempuri.org/"><LanguageCountResult>-1</LanguageCountResult></LanguageCountResponse></s:Body></s:Envelope>
=> #<Savon::SOAP::Response:0x105a2cca8 #http=#<HTTPI::Response:0x105a28ce8 #body="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><LanguageCountResponse xmlns=\"http://tempuri.org/\"><LanguageCountResult>-1</LanguageCountResult></LanguageCountResponse></s:Body></s:Envelope>", #raw_body="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><LanguageCountResponse xmlns=\"http://tempuri.org/\"><LanguageCountResult>-1</LanguageCountResult></LanguageCountResponse></s:Body></s:Envelope>", #code=200, #headers={"x-powered-by"=>"ASP.NET", "x-aspnet-version"=>"4.0.30319", "content-type"=>"text/xml; charset=utf-8", "date"=>"Sat, 02 Jul 2011 23:40:56 GMT", "server"=>"Microsoft-IIS/7.0", "content-length"=>"214", "cache-control"=>"private"}>, #soap_fault=Savon::SOAP::Fault, #http_error=Savon::HTTP::Error> ruby-1.8.7-p302 >
Long story short, I had to make 3 changes.
Changed response = client.request :language_count to
response = client.request "LanguageCount"
Added :xmlns => "http://tempuri.org/" to the end of the client.request call
Explicitly called the SOAPaction like: client.http.headers["SOAPAction"] = '"http://tempuri.org/FooService/LanguageCount"'
Hope this helps someone as I've been in SOAP hell for the weekend and hopefully I'll stop someone else being there.