receiving post variables from an external source in zendframework 2 - zend-framework2

i am working on a zendframework 2 project and want to receive post variables from an external source.
the source where the values will come from is a payment site (i.e world pay and paypal). i.e the return values of a payment confirming that payment has been made.
on the external site, i simply gave the URL of the web page that i want the information to be returned to:
http://example-site.com/payments/payment-made
then in action function on the controller page i did the following;
public function paymentMadeAction()
{
$contents = file_get_contents('php://input'); // read request contents
$data = explode('&', $contents);
if ($contents)
{
foreach($data as &$entry) {
$entry = explode('=', $entry);
$entry[1] = urldecode($entry[1]);
}
unset($entry);
}
print_r($data);
}
nothing happens though. i mean, i tested it but the values are not being received. when i went to the external source to check if my site received the information it confirmed that the information had been successfully sent
is there a special procedure that needs to be followed when receiving information from an external source in zend framework 2
would really appreciate any guidance or advise.
update:
below is a sample of the post variable that should be returned to the site; its a simply http object
POST /fail?installation=XXXXXX&msgType=authResult HTTP/1.0
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Host: www.worldpay.com
Content-Length: 973
User-Agent: WJHRO/1.0 (worldPay Java HTTP Request Object)
region=new+format+region&authAmountString=%26%23163%3B10.00&_SP.charEnc=UTF8&desc=&tel=&address1=new+format+address1)

you are doing it in the absolute WRONG way, the correct way to get post(or query or route ) variables in ZF2 action :
$this->params()->fromPost();
or
$this->request->getPost()->toArray();

Related

FastAPI RedirectResponse gets {"message": "Forbidden"} when redirecting to a different route

Please bare with me for a question for which it's nearly impossible to create a reproducible example.
I have an API setup with FastAPI using Docker, Serverless and deployed on AWS API Gateway. All routes discussed are protected with an api-key that is passed into the header (x-api-key).
I'm trying to accomplish a simple redirect from one route to another using fastapi.responses.RedirectResponse. The redirect works perfectly fine locally (though, this is without api-key), and both routes work perfectly fine when deployed on AWS and connected to directly, but something is blocking the redirect from route one (abc/item) to route two (xyz/item) when I deploy to AWS. I'm not sure what could be the issue, because the logs in CloudWatch aren't giving me much to work with.
To illustrate my issue let's say we have route abc/item that looks like this:
#router.get("/abc/item")
async def get_item(item_id: int, request: Request, db: Session = Depends(get_db)):
if False:
redirect_url = f"/xyz/item?item_id={item_id}"
logging.info(f"Redirecting to {redirect_url}")
return RedirectResponse(redirect_url, headers=request.headers)
else:
execution = db.execute(text(items_query))
return convert_to_json(execution)
So, we check if some value is True/False and if it's False we redirect from abc/item to xyz/item using RedirectResponse(). We pass the redirect_url, which is just the xyz/item route including query parameters and we pass request.headers (as suggested here and here), because I figured we need to pass along the x-api-key to the new route. In the second route we again try a query in a different table (other_items) and return some value.
I have also tried passing status_code=status.HTTP_303_SEE_OTHER and status_code=status.HTTP_307_TEMPORARY_REDIRECT to RedirectResponse() as suggested by some tangentially related questions I found on StackOverflow and the FastAPI discussions, but that didn't help either.
#router.get("/xyz/item")
async def get_item(item_id: int, db: Session = Depends(get_db)):
execution = db.execute(text(other_items_query))
return convert_to_json(execution)
Like I said, when deployed I can successfully connect directly to both abc/item and get a return value if True and I can also connect to xyz/item directly and get a correct value from that, but when I pass a value to abc/item that is False (and thus it should redirect) I get {"message": "Forbidden"}.
In case it can be of any help, I try debugging this using a "curl" tool, and the headers I get returned give the following info:
Content-Type: application/json
Content-Length: 23
Connection: keep-alive
Date: Wed, 27 Jul 2022 08:43:06 GMT
x-amzn-RequestId: XXXXXXXXXXXXXXXXXXXX
x-amzn-ErrorType: ForbiddenException
x-amz-apigw-id: XXXXXXXXXXXXXXXX
X-Cache: Error from cloudfront
Via: 1.1 XXXXXXXXXXXXXXXXXXXXXXXXX.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: XXXXX
X-Amz-Cf-Id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
So, this is hinting at a CloudFront error. Unfortunately I don't see anything slightly hinting at this API when I look into my CloudFront dashboard on AWS, there literally is nothing there (I do have permissions to view the contents though...)
The API logs in CloudWatch look like this:
2022-07-27T03:43:06.495-05:00 Redirecting to /xyz/item?item_id=1234...
2022-07-27T03:43:06.495-05:00 [INFO] 2022-07-27T08:43:06.495Z Redirecting to /xyz/item?item_id=1234...
2022-07-27T03:43:06.496-05:00 2022-07-27 08:43:06,496 INFO sqlalchemy.engine.Engine ROLLBACK
2022-07-27T03:43:06.496-05:00 [INFO] 2022-07-27T08:43:06.496Z ROLLBACK
2022-07-27T03:43:06.499-05:00 END RequestId: 6f449762-6a60189e4314
2022-07-27T03:43:06.499-05:00 REPORT RequestId: 6f449762-6a60189e4314 Duration: 85.62 ms Billed Duration: 86 ms Memory Size: 256 MB Max Memory Used: 204 MB
I have been wondering if my issue could be related to something I need to add to somewhere in my serverless.yml, perhaps in the functions: part. That currently looks like this for these two routes:
events:
- http:
path: abc/item
method: get
cors: true
private: true
request:
parameters:
querystrings:
item_id: true
- http:
path: xyz/item
method: get
cors: true
private: true
request:
parameters:
querystrings:
item_id: true
Finally, it's probably good to note that I have added custom middleware to FastAPI to handle the two different database connections I need for connecting to other_items and items tables, though I'm not sure how relevant this is, considering this functions fine when redirecting locally. For this I implemented the solution found here. This custom middleware is the reason for the redirect in the first place (we change connection URI based on route with that middleware), so I figured it's good to share this bit of info as well.
Thanks!
As noted here and here, it is mpossible to redirect to a page with custom headers set. A redirection in the HTTP protocol doesn't support adding any headers to the target location. It is basically just a header in itself and only allows for a URL (a redirect response though could also include body content, if needed—see this answer). When you add the authorization header to the RedirectResponse, you only send that header back to the client.
A suggested here, you could use the set-cookie HTTP response header:
The Set-Cookie HTTP response header is used to send a cookie from the
server to the user agent (client), so that the user agent can send it back to
the server later.
In FastAPI—documentation can be found here and here—this can be done as follows:
from fastapi import Request
from fastapi.responses import RedirectResponse
#app.get("/abc/item")
def get_item(request: Request):
redirect_url = request.url_for('your_endpoints_function_name') #e.g., 'get_item'
response = RedirectResponse(redirect_url)
response.set_cookie(key="fakesession", value="fake-cookie-session-value", httponly=True)
return response
Inside the other endpoint, where you are redirecting the user to, you can extract that cookie to authenticate the user. The cookie can be found in request.cookies—which should return, for example, {'fakesession': 'fake-cookie-session-value-MANUAL'}—and you retrieve it using request.cookies.get('fakesession').
On a different note, request.url_for() function accepts only path parameters, not query parameters (such as item_id in your /abc/item and /xyz/item endpoints). Thus, you can either create the URL in the way you already do, or use the CustomURLProcessor suggested here, here and here, which allows you to pass both path and query parameters.
If the redirection takes place from one domain to another (e.g., from abc.com to xyz.com), please have a look at this answer.

Saturn API not responding to GET when using acceptJson

The F# Saturn web framework fails on retrieving a value for GET method when acceptJson is a part of pipeline.
Below a sample code that I run to reproduce the issue:
let api = pipeline {
plug acceptJson
set_header "x-pipeline-type" "Api"
}
let apiRouter = router {
not_found_handler (setStatusCode 404 >=> text "Api 404")
pipe_through api
get "/test" (text "Hello world")
}
let appRouter = router {
forward "/api" apiRouter
}
appRouter is then added in the use_router section of the application code.
When I'm sending the request with a header Content-Type:application/json the response is "404 not found". But if I remove plug acceptJson from the api pipeline definition I get a correct response.
How to make Saturn work with the plug acceptJson?
I suspect 3615 is right - this seems similar to a problem I just solved yesterday after beating my head against the wall for a week. A request to my app (just a straight app from "dotnew new Saturn") from my browser was accepted. But a request to the same url from a test method returned a 404. It boiled down to the fact that the test request was missing an "Accept" header. When I added "Accept text/html", it worked. What I deduced from that is that, if the app can't find a content type that will be accepted according to the request, then it will report a 404. Your situation is the same - you're trying to return Json, but the request didn't include an "Accept application/json", so it can't find any page that the request would accept.
Of course, I could be wrong.

BigCommerce oAuth auth token request always returning 401

I can not figure out what I'm doing wrong. I'm developing an App for BigCommerce and can not get the simple oAuth exchange to work correctly.
The initial get request is being made to https://www.my-app.com/oauth/bigcommerce/auth. This is the code in the controller for that request. It's a Laravel 5.6 app:
use Illuminate\Http\Request;
use Bigcommerce\Api\Client as Bigcommerce;
class BigcommerceOAuthController extends Controller
{
public function auth(Request $request)
{
$object = new \stdClass();
$object->client_id = 'my-client-id';
$object->client_secret = 'my-client-secret';
$object->redirect_uri = 'https://my-app.com/oauth/bigcommerce/auth';
$object->code = $request->get('code');
$object->context = $request->get('context');
$object->scope = $request->get('scope');
$authTokenResponse = Bigcommerce::getAuthToken($object);
$storeHash = str_replace('stores/', '', $request->get('context'));
Bigcommerce::configure(array(
'client_id' => 'my-client-id',
'auth_token' => $authTokenResponse->access_token,
'store_hash' => $storeHash
));
echo "<pre>";
print_r($authTokenResponse);
print_r(Bigcommerce::getTime());
echo "</pre>";
}
}
Every time I try to install my draft app from the BigCommerce control panel, I get an error because $authTokenResponse is not an object. When I debug further into the Bigcommerce\Api\Connection class, I can see that the response from the server is empty, and the status is a 401, which means "Unauthorized".
I can't figure out why I am getting this error. As far as I can see, I'm doing everything right. I've tried urlencoding the string retrieved from $request->get('scope'), since that string becomes unencoded by Laravel, but that didn't seem to help.
I am also confused how this is even supposed to work at all. In the BigCommerce docs, they show this example POST request, which uses application/x-www-form-urlencoded Content-Type and passes the request body as a url encoded string:
POST /oauth2/token HTTP/1.1 Host: login.bigcommerce.com Content-Type:
application/x-www-form-urlencoded Content-Length: 186
client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&code=qr6h3thvbvag2ffq&scope=store_v2_orders&grant_type=authorization_code&redirect_uri=https://app.example.com/oauth&context=stores/{STORE_HASH}
However, if you inspect what's going on in the Connection class, you can see that the Content-Type is being set to application/x-www-form-urlencoded as the docs say, but the request body is being passed in as a json string, not a url string. Shouldn't the request be a url encoded string as the docs suggest?
A couple of things here to check:
Do you have a public URL where you can receive the Auth Callback?
If so, did the store owner registered the app successfully? https://developer.bigcommerce.com/api/registration
When you have the client_id and secret_id. You should have all of the details needed to send a POST request to the BC Auth Token Service at https://login.bigcommerce.com/oauth2/token
The content uses URL encode Make sure to URL encode your content. Be careful of of the encoding of & and = signs when those are actually being used as separators.
More details can be found in this post:
Can BigCommerce Private Apps use OAuth

Setting content type/ encoding in Jersey REST Client

HI I have been trying to call REST POST API using jersey REST Client. The API is docs is
URL:
METHOD: POST
Header Info:-
X-GWS-APP-NAME: XYZ
Accept: application/json or application/xml
My Sample Jersey client code is
Client client = Client.create();
WebResource resource=client.resource(URL);
resource.accept(javax.ws.rs.core.MediaType.APPLICATION_XML);
resource.type(javax.ws.rs.core.MediaType.APPLICATION_XML);
resource.type("charset=utf-8");
ClientResponse response = resource.post(ClientResponse.class,myReqObj);
I have been trying this code variation since last 1 week and it is not working. Any help in this regard is highly appreciated.
The tricky part is that the WebResource methods follows the Builder design pattern so it returns a Builder object which you need to preserve and carry on as you call further methods to set the full context of the request.
When you do resource.accept, it returns something you don't store, so it's lost when you do resource.type and therefore only your last call takes effect.
You'd typically set all the criterias in one line, but you could also save the output in a local variable.
ClientResponse response = client.resource(URL)
.accept(MediaType.APPLICATION_XML)
.type(MediaType.APPLICATION_XML)
.post(ClientResponse.class,myReqObj);
I do like that.
Response response = webTarget.request(MediaType.APPLICATION_JSON_TYPE)
.accept(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(a, "application/json; charset=UTF-8"));
here, 'a' is account class instance which like
#XmlRootElement
public class account {
...
...
}

XMLHttpRequest Post Method - Headers are stopping function

I'm trying to send an XMLHttpRequest object to my Rails server but the headers are causing my function to stop. Here are my observations:
When I comment out the 3 lines of code that set the headers, then xhr.readyState will eventually equal 4 (alert boxes within the anonymous function fire off).
If any one of the 3 header lines are uncommented, then the xhr object never changes state (none of the alert boxes ever fire off).
function saveUserProfile(){
var user_email = $('#userEmail_box').val();
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if (xhr.readyState==4 && xhr.status==200)
{
alert("Yes: " + xhr.readyState);
}
alert("No: " + xhr.readyState);
}
var method = 'POST';
var params = 'userEmail=user_email';
var url = 'http://localhost:3000/xhr_requests.json';
var async = true;
//Need to send proper header information with POST request
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.open(method, url, async);
xhr.send(params);
}
My three questions are:
What do I need to change in the code above in order to send data through the POST method?
I'm under the impression that the POST method requires some headers to be sent but am not clear about which ones though "xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');" seems to be one that is often mentioned in references. Can somebody help me understand a) why headers need to be sent and b) which ones need to be sent?
I'm using a rails server and am developing locally. Ultimately, this code will be executed on the client side of a mobile device which will go to a hosted rails server for passing and receiving data. Are there limitations with using the POST method with a rails server? Keep in mind that I plan to use JSON when sending information to the client from the server.
Thanks!
UPDATE: The headers should come AFTER the opening the xhr request but BEFORE sending it:
xhr.open(method, url, async);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('Content-length', params.length);
xhr.setRequestHeader('Connection', 'close');
xhr.send(params);
Hope this post saves somebody else 4 hours.
Does your web page with the JavaScript code also live on localhost:3000? If not, this is considered a cross-domain request, and your server will need to return special headers. So you have two options:
1) Host the web page on the same domain as the server, which will make this a same-domain request.
2) Have your server return the appropriate CORS headers. You can learn more about CORS here: http://www.html5rocks.com/en/tutorials/cors/

Resources