Imagine a web application with routes that need to check whether the user is allowed to access a given resource before proceeding. The "is authenticated" check relies on a database call.
In each route, I may have:
authorizeOwnership(req, res)
.then(function() {
// do stuff
res.send(200, "Yay");
});
I want the authorizeOwnership()
function to handle 403 (access denied) and 500 (e.g. database query error) responses so that each route doesn't need to do so explicitly.
I have a function that can query the database to check for ownership.
function confirmOwnership(resourceId, userId) {
// SequelizeJS... returns a bluebird promise
return Resource.find({
where: {id: resourceId, userId: userId}
})
.then(function(resource) {
if(!resource) {
return null; // no match for this resource id + user id
} else {
return resource;
}
});
}
This is then used in authorizeOwnership
:
function authorizeOwnership(req, res) {
var rid = parseInt(req.params.rid, 10),
userId = parseInt(req.authInfo.userid, 10);
return new Promise(function(resolve, reject) {
confirmOwnership(rid, userId)
.then(function(resource) {
if(resource === null) {
res.send(403, "Forbidden");
// Note: we don't resolve; outer handler will not be called
} else {
resolve(resource);
}
})
.catch(function(err) {
console.log(err);
res.send(500, "Server error");
// Note: we don't resolve; outer handler will not be called
});
});
}
In this scenario, I deliberately don't call reject()
or resolve()
in some of the code paths. If I do, then my "outer" route logic (the code that's calling authorizeOwnership()
) gets more complicated because it has to handle errors (either with a .catch()
or with a null
check in .then()
).
Two things make me a bit nervous though:
Is it OK for the promise returned by
authorizeOwnership()
to never be resolved in error scenarios? Will it cause a delay or a memory leak?Is it logically sound to resolve
confirmOwnership()
with null to say "no matching resource found" and then treat that as an error inauthorizeOwnership()
? My first attempt rejected the promise inconfirmOwnership()
when there was no matching resource, but this made things more complicated because it is difficult to distinguish between this (the 403 case) and an actual error (the 500 case).