Savon 2.11.1 set soap header after client is instantiated Rails + SSRS - ruby-on-rails-3

I'm trying to integrate my rails app with a Sql Server Reports Services (SSRS) using Savon 2.11.1. I'm using the ReportServicesExecution2005 WSDL. The problem I have is that I have to add a session_id to the soap header after the client has been instantiated. This is because the session_id is generated by SSRS after the client makes a call to load_report.
client = Savon.client(wsdl: "https://somewsdl.asmx?wsdl", basic_auth: ["user", "pass"])
this call returns the session_id in the response:
response = client.call(:load_report, message: {report: "path/to/report"} )
the soap header needs to contain the session_id in the client when this call is made:
client.call(:set_execution_parameters, message: { report: "path/to/report", parameters: params } )
Tried this but it didn't work:
client.call(:set_execution_parameters, soap_header: {"session_id" => #session_id}, message: { report: "path/to/report", parameters: params } )
I get the following error:
(soap:Client) The session identifier is missing. A session identifier
is required for this operation. --->
Microsoft.ReportingServices.Diagnostics.Utilities.MissingSessionIdException:
The session identifier is missing. A session identifier is required
for this operation.
Thanks for the help an advance.

You should use the soap_header hash to pass session id like
response = client.call(:set_execution_parameters, :soap_header => { :session_id => sid })
Cheers

The solution I found(while clunky) was to create a new client via Savon and pass the existing client values plus the new execution_id/session_Id into the constructor. This did the trick. Code example below.
Initial Savon Client:
#exeClient = Savon.client(wsdl: "some_wsdl.asmx?wsdl", basic_auth: ["user", "pass"],
convert_request_keys_to: :camelcase, soap_header: {"execution_id" => ""} )
call load_report to get execution_id:
#data = #exeClient.call(:load_report, message: {report: "/path/to/report"} )
Access execution_id:
#report = #data.to_hash
#execution_id = #report[:load_report_response][:execution_info][:execution_id]
Create new client that has access to run the report via the execution_id:
#newClient = Savon.client(wsdl: "/path/to/report/wsdl", basic_auth: ["user", "pass"],
:soap_header => {:"tns:TrustedUserHeader" => {"tns:UserName"=> "" , "tns:UserToken" => ""}, :"tns:ExecutionHeader" => {"tns:ExecutionID" => #execution_id}})
Create Params Hash:
param1 = { :Parameter_value =>{
:"Name" => "nameOfParam",
:"Value" => current_user.company_id
}
}
Call setExecutionParameters:
response = #newClient.call(:set_execution_parameters, message: { "Parameters" => param1} )
Now it works. Note that its important how you specify namespaces with Savon. If you don't do it correctly, the namespace will not be specified on some tags; which will make the soap call fail.

Related

Successful Login to API; Unsuccessful at accessing any data due to being unauthorized

I am attempting to come up with a live leaderboard for my local club using the PDGA's (Professional Disc Golf Association) API. I am writing a Google Apps Script intending to auto populate a Google Sheet with a Club Ranking that can be refreshed as needed.
Right now, all I am trying to do is make pull of data to ensure I am able to begin using the API, but I can't seem to do even that.
For reference, here are the only two resources I have to work with regarding this specific API:
PDGA REST API Authentication
PDGA REST API Services
I have got the original login to work using this code:
function apiLogin() {
var LoginUrl = 'https://api.pdga.com/services/json/user/login';
var LoginDetails = {
'username' : Username,
'password' : Password
};
var LoginRequest = {
'method' : 'post',
'Content-Type' : 'application/json',
'payload' : LoginDetails
};
var LoginResponse = UrlFetchApp.fetch(LoginUrl, LoginRequest);
var json = LoginResponse.getContentText();
var LoginData = JSON.parse(json);
Logger.log(LoginData);
var SessionID = LoginData['sessid'];
var SessionName = LoginData['session_name'];
var Tok = LoginData['token'];
var playerFetchPar = {
'method' : 'get',
'Cookie' : SessionID + '=' + SessionName
};
var PlayerResponse = UrlFetchApp.fetch('https://api.pdga.com/services/json/players?pdga_number=1',playerFetchPar); //ERROR
Logger.log(PlayerResponse);
};
It's the last part when I am trying to call on data from a player that I get the following error message:
Exception: Request failed for https://api.pdga.com returned code 403. Truncated server response: ["Access denied for user anonymous"] (use muteHttpExceptions option to examine full response)
I am guessing that my screw up is in my interpretion of the parameter Cookie from that second link. In the initial response to make sure I was logging in properly, I received a session_name and sessid but I can't seem to figure out what is expected from Cookie. I am sorry if the answer is obvious, but any help would be greatly appreciated!
The documentation says
Cookie: session_name=sessid
You've used
Cookie: sessid=session_name
Reverse it:
'Cookie' : `${SessionName}=${SessionID}`
And you need to send it as a header:
const playerFetchPar = {
method : 'get',
headers: {'Cookie' : `${SessionName}=${SessionID}`}
};

Mock API when performing a WebTestCase and do $client->submitform() in Symfony 5

I have a Test which submits a form. This form usually results in performing doing a external API call. I want to mock that because I'm not interested in the API but in the action.
Whenever I call submitForm the client is still making an external call, but I don't want that.
The test also fails because the api expects a api key which the test does not have.
class SubscriptionControllerTest extends WebTestCase
{
public function testSubscribe(): void
{
$client = static::createClient();
$userRepository = static::$container->get(UserRepository::class);
$testUser = $userRepository->findOneBy(['email' => UserFixtures::$testUser]);
$clientMock = $this->createMock(ApiClient::class);
//replace the api with the mock one
self::$container->set(ApiClient::class, $clientMock);
$client->loginUser($testUser);
$client->request('GET', '/subscription/new');
$client->submitForm('btn-submit', [
'subscribe[firstName]' => 'firstname',
'subscribe[lastName]' => 'lastname'
]);
$this->assertResponseStatusCodeSame(201);
}
}
What am I doing wrong here?
So the problem is described in this post:
https://stackoverflow.com/a/19951344/3679577
TL:DR
2 requests are being done in my test. A GET and a submitForm (POST). First one uses the proper Mock, second request rebuilds the kernel and the mocks are gone.
My solution was to just use 1 request by submitting the form with a POST request. Using the CSRF token manager to generate a csrf token:
public function testSubscribe(): void
{
$client = static::createClient();
$userRepository = static::$container->get(UserRepository::class);
$testUser = $userRepository->findOneBy(['email' => UserFixtures::$testUser]);
$csrfTokenGenerator = $client->getContainer()->get('security.csrf.token_manager');
$apiClient = $this->createMock(ApiClient::class);
$client->getContainer()->set(ApiClient::class, $apiClient);
$client->loginUser($testUser);
$client->request('POST', '/subscription/new', [
'subscribe' => [
"firstName" => 'firstname',
"lastName" => "lastname",
"_token" => $csrfTokenGenerator->getToken('subscribe')->getValue()
]
]);
$this->assertResponseRedirects('/subscription/thankyou');
}

Paypal REST API invalid credentials

I use the REST api in my nodejs application.
All is working good with sandbox but when i update with live credentials i get:
{ [Error: Response Status : 401]
response:
{ error: 'invalid_client',
error_description: 'The client credentials are invalid',
httpStatusCode: 401 },
httpStatusCode: 401 }
I updated my account to buisness but still not working, i use the live endpoint and Live credentials.
What should i do in order to make this work?
I had the same issue using PayPalSDK/rest-sdk-nodejs and solved passing with the configuration parameters (host, client_id, client_secret, ...) also the parameter 'mode' set to 'live'. Otherwise the default mode used by the library is 'sandbox' and hence the impossibility to use the live credentials.
As matteo said, if you switch from dev to live environment, only updateing the client id and secret isn't enough. You need to set the ApiContext-Mode to "live".
PayPals PHP REST-API-SDK comes with some great samples. Take a look at the bootstrap.php in /vendor/paypal/rest-api-sdk-php/sample/ in line 84. There are some configurations happening, after getting the api context.
<?php
$apiContext = new ApiContext(
new OAuthTokenCredential(
$clientId,
$clientSecret
)
);
// Comment this line out and uncomment the PP_CONFIG_PATH
// 'define' block if you want to use static file
// based configuration
$apiContext->setConfig(
array(
'mode' => 'sandbox',
'log.LogEnabled' => true,
'log.FileName' => '../PayPal.log',
'log.LogLevel' => 'DEBUG', // PLEASE USE `INFO` LEVEL FOR LOGGING IN LIVE ENVIRONMENTS
'cache.enabled' => true,
// 'http.CURLOPT_CONNECTTIMEOUT' => 30
// 'http.headers.PayPal-Partner-Attribution-Id' => '123123123'
//'log.AdapterFactory' => '\PayPal\Log\DefaultLogFactory' // Factory class implementing \PayPal\Log\PayPalLogFactory
)
);

worklight http adapter authentication issue with apache

I'm working on a mobile prof-of-concept using IBM's Worklight (6.1) to retrieve info via HTTP server (Apache) running on a mainframe (z/OS). I'm using the HTTP adapter procedure to log-on and retrieve data but I so far no success logging on via Worklight HTTP adapter. If I open a browser and provide the 'user:password' headers, the log-in is successful but if I try it via Worklight procedure, the '401 authorization required' error is returned. The HTTP server error log shows:
.. (139)EDC5139I Operation not permitted. (errno2=0x0BE800DB): SAF
authentication failure for "/cgi-bin/itil_v11_main.sh": SAFRunAs
failure on switching SAF UID from Authorization header using
%%CLIENT%% .. user (\xe1\xcb: authentication failure for
"/cgi-bin/itil_v11_main.sh": Password Mismatch
That 'password mismatch' may suggest the 'headers' are not correct? Here's the procedure:
var user_id = 'userid';
var user_psw = 'userpassword';
var loginstring ;
var base64= new com.worklight.customcode.Base64Encoding();
function getITIL() {
loginstring = base64.encode(user_id+':'+user_psw);
var path = '/cgi-bin/itil_v11_main.sh';
var input = {
method : 'get',
headers : {
'Authorization' : 'Basic ' + loginstring
},
returnedContentType : 'html',
path : path
};
return WL.Server.invokeHttp(input);
}
It seems like you've implemented it correctly, however the complaint is on the password, which in your case originates from var base64= new com.worklight.customcode.Base64Encoding();.
Because you do not supply the code that you are using in said class, it's difficult to say what the error is, but that is where you should look at for the cause of your error.
You'll need to provide the class's implementation in order to further debug the question.

worklight adapter

I am getting a problem in worklight adapter , In the following http adapter method
,it is showing The mandatory parameter 'action' is missing, returning statusCode as
500 and statusReason as "Internal Server Error". I had given all the user credentials
correctly in adapter xml file, but I don't know why I'm getting this error.
Code:
function actionOnProcessInstance()
{
var param = "/rest/bpm/bfm/v1/process/_PI:9003013d.4387342e.1efe573f.7c20307?action=resume";
var input =
{
method : 'put',
returnedContentType : 'json',
path : param,
};
var response = WL.Server.invokeHttp(input);
return response;
}
In 5.0.5.x, invokeHttp will take any params provided on the path for put and post and place them inside the http body instead of having them remain on the path as query params (as the developer probably intended). This behavior will be updated in an upcoming version but for now there's no way to force these to stay as query params.