| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
This repository implements a C++ framework to facilitate the creation of REST API.
This library is designed to be installed by making use of Conan package manager. So, you just need to add the following requirement into your Conan recipe:
def requirements(self):
self.requires("RESTAPICore/1.0.0@systelab/stable")Version number of this code snipped is set just as an example. Replace it for the desired package version to retrieve.
As this package is not available on the conan-center, you will also need to configure a remote repository before installing dependencies:
conan remote add systelab-public https://systelab.jfrog.io/artifactory/api/conan/cpp-conan-production-localSee Conan documentation for further details on how to integrate this package with your build system.
See BUILD.md document for details.
#include "RESTAPICore/Endpoint/IEndpoint.h"
class YourEndpoint : public systelab::rest_api_core::IEndpoint
{
public:
YourEndpoint() = default;
std::unique_ptr<systelab::web_server::Reply>
execute(const systelab::rest_api_core::EndpointRequestData&) override
{
// Process given systelab::rest_api_core::EndpointRequestData
// and generate a systelab::web_server::Reply
return reply;
}
};#include "RESTAPICore/Router/Router.h"
#include "RESTAPICore/Router/RoutesFactory.h"
class RESTAPIWebService : public systelab::web_server::IWebService
{
public:
RESTAPIWebService()
{
std::string jwtKey = "HereGoesYourJWTSecretKey";
auto routesFactory = std::make_unique<systelab::rest_api_core::RoutesFactory>(jwtKey);
m_router = std::make_unique<systelab::rest_api_core::Router>();
m_router->addRoute(routesFactory.buildRoute("GET", "/rest/api/yourendpoint", {},
[]() { return std::make_unique<YourEndpoint>() }) );
// Register more routes here
}
std::unique_ptr<systelab::web_server::Reply>
process(const systelab::web_server::Request& request) const override
{
return m_router->process(request);
}
private:
std::unique_ptr<systelab::rest_api_core::Router> m_router;
};Thus, when the web service receives a GET HTTP request with "/rest/api/yourendpoint" URI, it redirects this request to the YourEndpointclass implemented previously.
router->addRoute(routesFactory.buildRoute("POST", "/rest/api/yourendpoint", {},
[]() { return std::make_unique<YourPostEndpoint>() });
router->addRoute(routesFactory.buildRoute("PUT", "/rest/api/yourendpoint", {},
[]() { return std::make_unique<YourPutEndpoint>() });
router->addRoute(routesFactory.buildRoute("DELETE", "/rest/api/yourendpoint", {},
[]() { return std::make_unique<YourDeleteEndpoint>() });
router->addRoute(routesFactory.buildRoute("GET", "/rest/api/anotherendpoint", {},
[]() { return std::make_unique<AnotherGetEndpoint>() });Routes with parameters can be registered by using an specific syntax on associated URIs:
// Route with an string parameter named 'id'
router->addRoute(routesFactory.buildRoute("GET", "/rest/api/yourendpoint/:id", {},
[]() { return std::make_unique<YourGetIdEndpoint>() });
// Route with a numeric parameter named 'number'
router->addRoute(routesFactory.buildRoute("GET", "/rest/api/anotherendpoint/+number", {},
[]() { return std::make_unique<AnotherGetIdEndpoint>() });
// Route with multiple parameters
router->addRoute(routesFactory.buildRoute("GET", "/rest/api/yourendpoint/+id1/:id2", {},
[]() { return std::make_unique<YourGetMultipleParamsEndpoint>() });When a request matches a registered route with parameters, the associated endpoint is called with a systelab::rest_api_core::EndpointRequestData object that contains these parameters properly parsed for easy usage:
std::unique_ptr<systelab::web_server::Reply>
YourGetMultipleParamsEndpoint::execute(const systelab::rest_api_core::EndpointRequestData& requestData)
{
unsigned int id1 = requestData.getParameters().getNumericParameter("id1");
std::string id2 = requestData.getParameters().getStringParameter("id2");
...
}When working with a Bearer Authentication scheme, the library automatically decodes the tokens contained on Authorization header of HTTP requests. It uses the key provided on the RoutesFactory constructor to verify that token signature is correct. Then, claims contained on decoded JSON Web Tokens are provided to the matching endpoint as part of the systelab::rest_api_core::EndpointRequestData object:
std::unique_ptr<systelab::web_server::Reply>
YourEndpoint::execute(const systelab::rest_api_core::EndpointRequestData& requestData)
{
auto& authorizationClaims = requestData.getAuthorizationClaims();
std::vector<std::string> claimNames = authorizationClaims.getClaimNames();
for (auto name : claimNames)
{
std::string claimValue = authorizationClaims.getClaim(name);
...
}
...
}Routes can be configured to allow access only to users that have a certain role. This can be achieved through route access validators, a middleware that is executed after matching a request with a route, but before dispatching it to the endpoint. So, user role validation can be set up as follows:
#include "RESTAPICore/RouteAccess/IUserRoleService.h"
class YourUserRoleService : public systelab::rest_api_core::IUserRoleService
{
public:
YourUserRoleService() = default;
std::vector<std::string> getUserRoles(const std::string& username) override
{
...
}
};
class RESTAPIWebService : public systelab::web_server::IWebService
{
public:
...
private:
...
YourUserRoleService m_userRoleService;
};#include "RESTAPICore/RouteAccess/UserRoleRouteAccessValidator.h"
// Route that only allows access to 'Admin' users
m_router->addRoute(routesFactory.buildRoute("GET", "/rest/api/yourendpoint",
{ [this](){ return std::make_unique<UserRoleRouteAccessValidator>({"Admin"}, m_userRoleService) } },
[]() { return std::make_unique<YourEndpoint>() }) );
// Route that allows access to 'Admin' and 'Basic' users
m_router->addRoute(routesFactory.buildRoute("GET", "/rest/api/anotherendpoint",
{ [this](){ return std::make_unique<UserRoleRouteAccessValidator>({"Admin", "Basic"}, m_userRoleService) } },
[]() { return std::make_unique<AnotherEndpoint>() }) );When the web service receives an HTTP request that matches any of these routes, before redirecting the request to the endpoint, it will check if the request has an authorization claim for the subject ("sub"). If so, this claim will be used to retrieve the associated user roles and then, these roles will be compared against the allowed ones for the route. If user is allowed, then the request will be dispatched to the endpoint. Otherwise, a forbidden reply will be returned.
Another built-in route access validator (class TokenExpirationAccessValidator) can be used to check if the token contained on the Authorization header of the requests has expired or not. This verification is based on the "Issued at" (iat) authorization claim and the current time. Thus, when current time is greater than iat plus a configured expiration time (in seconds), a forbidden reply will be returned.
#include "RESTAPICore/RouteAccess/TokenExpirationAccessValidator.h"
// Route that does not allow using tokens generated more than 10 min (600 seconds) ago
m_router->addRoute(routesFactory.buildRoute("GET", "/rest/api/yourendpoint",
{ [this](){ return std::make_unique<TokenExpirationAccessValidator>(m_timeAdapter, 600) } },
[]() { return std::make_unique<YourEndpoint>() }) );The m_timeAdapter member is an instance of the systelab::time::ITimeAdapter class, which provides a method to query the current time of the system. See documentation of cpp-time-adapter repository for further details about it.
This library can be extended by implementing any other kind of route access validation through the IRouteAccessValidator interface. The hasAccess() method can make use of any data contained on the EndpointRequestData argument to determine if the request has access to the route or not and return a boolean accordingly.
#include "RESTAPICore/RouteAccess/IRouteAccessValidator.h"
class MyCustomRouteAccessValidator : public systelab::rest_api_core::IRouteAccessValidator
{
MyCustomRouteAccessValidator(... ) // Inject any object required
bool hasAccess(EndpointRequestData&) const override
{
// Use EndpointRequestData or injected objects to return if request has access to the route
}
};| Back | FazBrowse Home | New Git URL |