How to skip controller actions in case of authorization is not succeed in ZF2 - rest

I am implementing REST API on ZF2. Now I need to check authorization token on Module.php and return with error if authorization failed. But I did not know how to return response from Module.php.
I wrote code to check authorization in DISPATCH Event of onBootstrap. Now how to return error from Module.php without accessing controllers if authorization failed. As only exit function/call make possible to return without accessing controller. But In that case I did not getting any response. Using json_encode(array) is not look like a standard as I am already enabled ViewJsonStrategy and using JsonModel in controllers.

You can shortcircuit the event by having your listener return a response, eg...
public function onBootstrap(EventInterface $e)
{
$eventManager = $e->getApplication()->getEventManager();
// attach dispatch listener
$eventManager->attach('dispatch', function($e) {
// do your auth checks...
if (!$allowed) {
// get response from event
$response = $e->getResponse();
// set status 403 (forbidden)
$response->setStatusCode(403);
// shortcircuit by returning response
return $response;
}
});
}

Related

check if api endpoint exits before send request - axios

I have axios request in my vue application:
async get_banner(id:number) : Promise<any> {
return global.axios.get(`${process.env.VUE_APP_DOMAIN}/banners/${id}`)
}
it works while banner/${id} response exits, but I have situation when I should disable banner in my admin panel so api endpoint becomes empty. (not exits) so I am getting Request failed with status code 404 in my vue app console.
question is how to prevent error and know if url exits or not? what is best practice to do this?
You can't tell whether an API exists or not without trying it (or relying on another API to get status of the former API)
It's usually just a manner of handling the response properly. Usually this would look something like this...
getTheBanner(id){
this.errorMessage = null; // reset message on request init
get_banner(id)
.then(r => {
// handle success
this.results = r;
})
.catch(e => {
// handle error
if (e.status === 404){
// set error message
this.errorMessage = "Invalid banner Id";
}
})
}
then in your template you could have something like this
<div v-if="errorMessage" class="alert danger">{errorMessage}</div>
Explaination:
Yes, you're absolutely right. This is the default behavior of strapi. Whenever the response is empty it throws a 404 error. This is basically because the findOne method in service returns null to the controller and when the controller sends this to the boom module it returns a 404 Not Found error to the front end.
Solution:
Just override the find one method in the controller to return an empty object {} when the response is null.
Implementation
// Path - yourproject/api/banner/controllers/banner.js
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
/**
* Retrieve a record.
*
* #return {Object}
*/
async findOne(ctx) {
const { id } = ctx.params;
const entity = await strapi.services.restaurant.findOne({ id });
// in case no entity is found, just return emtpy object here.
if(!entity) return {};
return sanitizeEntity(entity, { model: strapi.models.restaurant });
},
};
Side Note:
There's no need to make any changes to the browser side axios implementation. You should always handle such cases in controller rather the client side.
Reference:
Backend Customizations

Nuxt Axios Module read status code

I'm calling a Rest API that returns at least 2 success status codes .
A normal 200 OK and a 202 Accepted status code.
Both return a Content in the body.
If I execute in postman my calls I might get something like
Status code: 202 Accepted. With Body "Queued" or some other values
or
Status code: 200 OK. With Body "ValueOfSomeToken"
Making the call with axios in my nuxt app:
this.$axios.$get('/Controller/?id=1')
.then((response)=>{
if(response=='Queued'){
//Do something
}
else if (response=='Expired'){
//Do something
}
else{
//Do something
}
})
.catch((error)=>{
console.log(error);
});
..works, but I actually would like to get the status code (because 202 has other values for the body responses)
I have no idea how to read the status codes.
I tried using (response,code) =>... but code is then nothing.
You can use non $-prefixed functions like this.$axios.get() instead of this.$axios.$get() to get the full response
// Normal usage with axios
let { data } = await $axios.get('...'));
// Fetch Style
let data = await $axios.$get('...');
(source)
You can extract the status codes from the response object in axios
if you print the response object (as shown in below image) you can see the all the objects inside the response object. One among them is status object
response.status will give you the status code that is sent from the server
axios.get("http://localhost:3000/testing").then((response)=>{
console.log("response ",response);
if(response.status == 200){
//do something
}
else if(response.status == 202){
//do something
}
else if(response.status == 301){
//do something
}
}).catch((err)=>{
console.log("err11 ",err);
})
In the server side, you can explicitly send any status code using res.status() method, for more details refer this documentation
app.get('/testing',(req, res)=> {
res.status(202).send({"res" : "hi"});
});
Update:
By default, #nuxtjs/axios returns response.data in the .then((response))
The $axios.onResponse event will have access to the complete response object.
You need to setup an interceptor to intercept the $axios.onResponse event and modify the response object
Under plugin directory create a plugin, plugin/axios.js
Update the plugins section plugins : ['~/plugins/axios']
in nuxt.config.js
export default function ({ $axios, redirect }) {
$axios.onResponse(res=>{
console.log("onResponse ", res);
res.data.status = res.status;
return res;
})
}
In the res object in this interceptor you will have the all the values (as it is shown in my first screenshot). But this res object is not returned as it is, only res.data is returned to our program.
We can update contents inside res.data and then return the res object as shown in my program res.data.status = res.status; .
Now when axios returns res.data we will have access to res.data.status value in response object in the .then((response)) promise
You can access the status using response.status inside this.$axios
this.$axios.$get("url").then((response) =>{
console.log("status ",response.status);
}).catch((err) => {
console.log("res err ",err);
});

How to throw custom error message from API Gateway custom authorizer

Here in the blue print says, API gateway will respond with 401: Unauthorized.
I wrote the same raise Exception('Unauthorized') in my lambda and was able to test it from Lambda Console. But in POSTMAN, I'm receiving status 500
with body:
{
message: null`
}
I want to add custom error messages such as "Invalid signature", "TokenExpired", etc., Any documentation or guidance would be appreciated.
This is totally possible but the docs are so bad and confusing.
Here's how you do it:
There is an object called $context.authorizer that you have access to in your gateway responses template. You can read more about it here: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
Here is an examample of populating this authorizer object from your authorizer lambda like so:
// A simple TOKEN authorizer example to demonstrate how to use an authorization token
// to allow or deny a request. In this example, the caller named 'user' is allowed to invoke
// a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke
// the request if the token value is 'deny'. If the token value is 'Unauthorized', the function
// returns the 'Unauthorized' error with an HTTP status code of 401. For any other token value,
// the authorizer returns an 'Invalid token' error.
exports.handler = function(event, context, callback) {
var token = event.authorizationToken;
switch (token.toLowerCase()) {
case 'allow':
callback(null, generatePolicy('user', 'Allow', event.methodArn));
break;
case 'deny':
callback(null, generatePolicy('user', 'Deny', event.methodArn));
break;
case 'unauthorized':
callback("Unauthorized"); // Return a 401 Unauthorized response
break;
default:
callback("Error: Invalid token");
}
};
var generatePolicy = function(principalId, effect, resource) {
var authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
// Optional output with custom properties of the String, Number or Boolean type.
authResponse.context = {
"stringKey": "stringval custom anything can go here",
"numberKey": 123,
"booleanKey": true,
};
return authResponse;
}
They key here is adding this part:
// Optional output with custom properties of the String, Number or Boolean type.
authResponse.context = {
"stringKey": "stringval custom anything can go here",
"numberKey": 123,
"booleanKey": true,
};
This will become available on $context.authorizer
I then set the body mapping template in gateway responses tab like this:
{"message":"$context.authorizer.stringKey"}
NOTE: it must be quoted!
finally - after sending a request in postman with Authorization token set to deny I now get back a payload from postman that looks like this:
{
"message": "stringval custom anything can go here"
}
I used #maxwell solution, using custom resource ResponseTemplates. For deny response see below:
{
"success":false,
"message":"Custom Deny Message"
}
You can check this out here: https://github.com/SeptiyanAndika/serverless-custom-authorizer
I'm not sure what is causing the 500 message: null response. Possibly misconfiguration of the Lambda function permissions.
To customize the Unauthorized error response, you'll set up a Gateway Response for the UNAUTHORIZED error type. You can configure response headers and payload here.
Maxwell is mostly correct. I tried his implementation and noticed that his message should go from :
{"message":"$context.authorizer.stringKey"}
to
{"message":"$context.authorizer.context.stringKey"}
As noted by Connor far as I can see, the answer to the specific question - which mentions 401 related errors - is NO.
You can produce a generic 401 Unauthorized but you cannot alter the error message.
That is you can customise the 403 Forbidden (DENY) messages but not the 401's.
Note that I've used the NodeJS Lambda custom authorizers but not the Python version referenced in the question.
With my testing what i observed is , You cannot customize message when you throw exception from the lambda,
You can have customized messages when you return DENY Policy message from the authorizer
Here is how i am returning custom message when i DENY from the Authorizer, it in the detail field of
authResponse.context returned from custom Authorizer
you can also update status code to 401 instead of 403 .
This can be easily achieved by using the context.fail() function.
Example:
const customAuthorizer: Handler = (event, context: Context, callback: Callback) => {
authenticate(event)
.then((res) => {
// result should be as described in AWS docs
callback(null, res);
})
.catch((err) => {
context.fail("Unauthorized");
});
}
This will return a 401 response with following body.
{
"message": "Unauthorized"
}
This can also be achieved by throwing an error:
throw new Error('Unauthorized');

what are the differences beetween notifyUrl response and completePurchase response?

i'm try to use omnipay/paypal, i use this code for returnUrl page:
public function completePayment(Request $request)
{
//return 'pagina dopo acquisto';
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('blastor_89-facilitator_api1.msn.com');
$gateway->setPassword('BEWB2BEW9CHCV3EQ');
$gateway->setSignature('AFcWxV21C7fd0v3bYYYRCpSSRl31AC5Dp4AnVYBnMIkNFxSQTj8h.lqD');
$gateway->setTestMode(true);
$params = session()->get('params');
$response = $gateway->completePurchase($params)->send();
$paypalResponse = $response->getData();
//$this->store($paypalResponse);
if(isset($paypalResponse['PAYMENTINFO_0_ACK']) && $paypalResponse['PAYMENTINFO_0_ACK'] === 'Success') {
// here you process the response. Save to database ...
}
else {
// Failed transaction ...
}
}
it response with $paypalResponse, while if i use notifyUrl page what do notifyUrl response? what are the differences?
These two responses are completely different although they contain the same basic idea.
The complete purchase response has the data from your call to completePurchase(). The notify response has data that is fed into your application from a notify (PayPal IPN) call. I suggest that you dump the data from each one to take a look.

Interrupt FuelPHP REST controller flow and display response

I am using FuelPHP's rest controller.
I am trying to break the flow and display my response after encountering an error.
Here is my basic flow needed:
When any of the methods are called I run a "validate" function, which validates parameters and other business logic.
If the "validate" function determines something is off, I want to stop the entire script and display the errors I have complied so far.
I have tried the following in my "validate" function but it simply exits the validate function... then continues to the initial method being requested. How do I stop the script immediately and display the contents of this response?
return $this->response( array(
'error_count' => 2,
'error' => $this->data['errors'] //an array of error messages/codes
) );
That is very bad practice. If you exit you not only abort the current controller, but also the rest of the framework flow.
Just validate in the action:
// do your validation, set a response and return if it failed
if ( ! $valid)
{
$this->response( array(
'error_count' => 2,
'error' => $this->data['errors'] //an array of error messages/codes
), 400); //400 is an HTTP status code
return;
}
Or if you want to do central validation (as opposed to in the controller action), use the router() method:
public function router($resource, $arguments)
{
if ($this->valid_request($resource))
{
return parent::router($resource, $arguments);
}
}
protected function valid_request($resource)
{
// do your validation here, $resource tells you what was called
// set $this->response like above if validation failed, and return false
// if valid, return true
}
I am new to FuelPHP so if this method is bad practice, let me know.
If you want your REST controller to break the flow at some other point than when the requested method returns something, use this code. You can change the $this->response array to return whatever you want. The main part of the script is the $this->response->send() method and exit method.
$this->response( array(
'error_count' => 2,
'error' => $this->data['errors'] //an array of error messages/codes
), 400); //400 is an HTTP status code
//The send method sends the response body to the output buffer (i.e. it is echo'd out).
//pass it TRUE to send any defined HTTP headers before sending the response body.
$this->response->send(true);
//kill the entire script so nothing is processed past this point.
exit;
For more information on the send method, check out the FuelPHP documentation for the response class.