Feature Authorization. Generalize authorization object class.

This commit is contained in:
lganzzzo 2019-08-30 04:19:14 +03:00
parent 73b92b38f0
commit e62fd32f9e
12 changed files with 112 additions and 129 deletions

View File

@ -261,7 +261,7 @@ if(!__param_str_val_##NAME){ \
error->putHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE, "Basic realm=\"API\""); \
return error; \
} \
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = authorize(__param_str_val_##NAME); \
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = ApiController::handleAuthorization(__param_str_val_##NAME); \
if(__param_aosp_val_##NAME.get() == nullptr) { \
auto error = ApiController::handleError(Status::CODE_401, "Unauthorized"); \
error->putHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE, "Basic realm=\"API\""); \
@ -280,7 +280,7 @@ if(!__param_str_val_##NAME){ \
error->putHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE, "Basic realm=\"" REALM "\""); \
return error; \
} \
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = authorize(__param_str_val_##NAME); \
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = ApiController::handleAuthorization(__param_str_val_##NAME); \
if(__param_aosp_val_##NAME.get() == nullptr) { \
auto error = ApiController::handleError(Status::CODE_401, "Unauthorized"); \
error->putHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE, "Basic realm=\"" REALM "\""); \
@ -298,7 +298,7 @@ if(!__param_str_val_##NAME){ \
error->putHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE, SCHEME " realm=\"" REALM "\""); \
return error; \
} \
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = authorize(__param_str_val_##NAME); \
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> __param_aosp_val_##NAME = ApiController::handleAuthorization(__param_str_val_##NAME); \
if(__param_aosp_val_##NAME.get() == nullptr) { \
auto error = ApiController::handleError(Status::CODE_401, "Unauthorized"); \
error->putHeader(oatpp::web::protocol::http::Header::WWW_AUTHENTICATE, SCHEME " realm=\"" REALM "\""); \

View File

@ -48,6 +48,7 @@ std::shared_ptr<ApiController::Endpoint::Info> ApiController::getEndpointInfo(co
return m_endpointInfo[endpointName];
}
// TODO - refactor this method
std::shared_ptr<ApiController::OutgoingResponse> ApiController::handleError(const Status& status, const oatpp::String& message) const {
if(m_errorHandler) {
return m_errorHandler->handleError(status, message);
@ -56,11 +57,12 @@ std::shared_ptr<ApiController::OutgoingResponse> ApiController::handleError(cons
return handler::DefaultErrorHandler::handleDefaultError(status, message);
}
std::shared_ptr<handler::AuthorizationObject> ApiController::authorize(const String &authHeader) const {
std::shared_ptr<handler::AuthorizationObject> ApiController::handleAuthorization(const String &authHeader) const {
if(m_authorizationHandler) {
return m_authorizationHandler->handleAuthorization(authHeader);
}
return handler::DefaultAuthorizationHandler::defaultAuthorizationObject(authHeader);
// If Authorization is not setup on the server then it's 500
throw oatpp::web::protocol::http::HttpError(Status::CODE_500, "Authorization is not setup.");
}
void ApiController::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){

View File

@ -268,7 +268,7 @@ public:
* Do not use it directly. This method is under discussion.
* Currently returns AuthorizationObject created by AuthorizationHandler or return DefaultAuthrorizationObject by DefaultAuthorizationHandler if AuthorizationHandler is null
*/
std::shared_ptr<handler::AuthorizationObject> authorize(const String &authHeader) const;
std::shared_ptr<handler::AuthorizationObject> handleAuthorization(const String &authHeader) const;
/**
* [under discussion]

View File

@ -30,11 +30,7 @@
namespace oatpp { namespace web { namespace server { namespace handler {
std::shared_ptr<handler::AuthorizationObject> DefaultAuthorizationHandler::handleAuthorization(const oatpp::String &header) {
return defaultAuthorizationObject(header);
}
std::shared_ptr<handler::AuthorizationObject> DefaultAuthorizationHandler::defaultAuthorizationObject(const oatpp::String &header) {
std::shared_ptr<handler::AuthorizationObject> BasicAuthorizationHandler::handleAuthorization(const oatpp::String &header) {
if(!header->startsWith("Basic ")) {
return nullptr;
@ -44,10 +40,9 @@ std::shared_ptr<handler::AuthorizationObject> DefaultAuthorizationHandler::defau
parser::Caret caret(auth);
if(caret.findChar(':')) {
auto dto = std::make_shared<handler::DefaultAuthorizationObject>();
dto->user = oatpp::String((const char*)&caret.getData()[0], caret.getPosition(), true /* copy as own data */);
dto->password = oatpp::String((const char*)&caret.getData()[caret.getPosition() + 1], caret.getDataSize() - caret.getPosition() - 1, true /* copy as own data */);
return dto;
oatpp::String userId((const char*)&caret.getData()[0], caret.getPosition(), true /* copy as own data */);
oatpp::String password((const char*)&caret.getData()[caret.getPosition() + 1], caret.getDataSize() - caret.getPosition() - 1, true /* copy as own data */);
return authorize(userId, password);
}
return nullptr;

View File

@ -35,72 +35,60 @@
namespace oatpp { namespace web { namespace server { namespace handler {
/**
* The AuthorizationObject superclass, all AuthorizationObjects have to implement this interface
* The AuthorizationObject superclass, all AuthorizationObjects have to extend this class.
*/
class AuthorizationObject : public oatpp::base::Countable {
public:
oatpp::String user;
oatpp::String password;
protected:
AuthorizationObject() = default;
};
/**
* The default AuthorizationObject as generated by DefaultAuthorizationHandler
*/
class DefaultAuthorizationObject : public AuthorizationObject {
public:
DefaultAuthorizationObject() = default;
};
/**
* Authorization Handler.
* Abstract Authorization Handler.
*/
class AuthorizationHandler {
public:
/**
* Convenience typedef for &l:AuthorizationObject;.
*/
typedef oatpp::web::server::handler::AuthorizationObject AuthorizationObject;
public:
/**
* Default virtual destructor.
*/
virtual ~AuthorizationHandler() = default;
/**
* Implement this method! Return nullptr if authorization should be denied.
* @param header - &id:oatpp::String;.
* @param header - `Authorization` header. &id:oatpp::String;.
* @return - `std::shared_ptr` to &id:oatpp::web::server::handler::AuthorizationObject;.
*/
virtual std::shared_ptr<handler::AuthorizationObject> handleAuthorization(const oatpp::String &header) = 0;
virtual std::shared_ptr<AuthorizationObject> handleAuthorization(const oatpp::String& authorizationHeader) = 0;
};
/**
* Default Authorization Handler.
* AuthorizationHandler for Authorization Type `Basic`. <br>
* See [RFC 7617](https://tools.ietf.org/html/rfc7617). <br>
* Extend this class to implement Basic Authorization.
*/
class DefaultAuthorizationHandler : public oatpp::base::Countable, public AuthorizationHandler {
class BasicAuthorizationHandler : public AuthorizationHandler {
public:
/**
* Constructor.
*/
DefaultAuthorizationHandler() = default;
public:
/**
* Create shared DefaultAuthorizationHandler.
* @return - `std::shared_ptr` to DefaultAuthorizationHandler.
*/
static std::shared_ptr<DefaultAuthorizationHandler> createShared() {
return std::make_shared<DefaultAuthorizationHandler>();
}
/**
* Implementation of &l:AuthorizationHandler::handleAuthorization ();
* @param header - &id:oatpp::String;.
* @return - std::shared_ptr to &id:oatpp::web::server::handler::AuthorizationObject;.
*/
std::shared_ptr<handler::AuthorizationObject> handleAuthorization(const oatpp::String &header) override;
std::shared_ptr<AuthorizationObject> handleAuthorization(const oatpp::String &header) override;
/**
* Static implementation of &l:AuthorizationHandler::handleAuthorization () for convenience usage.
* @param header - &id:oatpp::String;.
* @return - std::shared_ptr to &id:oatpp::web::server::handler::AuthorizationObject;.
* Implement this method! Do the actual authorization here.
* @param userId - user id. &id:oatpp::String;.
* @param password - password. &id:oatpp::String;.
* @return - `std::shared_ptr` to &l:AuthorizationObject;.
*/
static std::shared_ptr<handler::AuthorizationObject> defaultAuthorizationObject(const oatpp::String &header);
virtual std::shared_ptr<AuthorizationObject> authorize(const oatpp::String& userId, const oatpp::String& password) = 0;
};

View File

@ -49,7 +49,7 @@ add_executable(oatppAllTests
oatpp/web/FullTest.hpp
oatpp/web/app/Client.hpp
oatpp/web/app/BearerAuthorizationController.hpp
oatpp/web/app/AuthorizationController.hpp
oatpp/web/app/BasicAuthorizationController.hpp
oatpp/web/app/Controller.hpp
oatpp/web/app/ControllerAsync.hpp
oatpp/web/app/DTOs.hpp

View File

@ -27,7 +27,7 @@
#include "oatpp/web/app/Client.hpp"
#include "oatpp/web/app/Controller.hpp"
#include "oatpp/web/app/AuthorizationController.hpp"
#include "oatpp/web/app/BasicAuthorizationController.hpp"
#include "oatpp/web/app/BearerAuthorizationController.hpp"
#include "oatpp/web/client/HttpRequestExecutor.hpp"
@ -140,7 +140,7 @@ void FullTest::onRun() {
oatpp::test::web::ClientServerTestRunner runner;
runner.addController(app::Controller::createShared());
runner.addController(app::AuthorizationController::createShared());
runner.addController(app::BasicAuthorizationController::createShared());
runner.addController(app::BearerAuthorizationController::createShared());
runner.run([this, &runner] {
@ -229,19 +229,8 @@ void FullTest::onRun() {
OATPP_ASSERT(response->getStatusCode() == 200);
}
{ // test simple authorization header
auto response = client->defAuthorization("foo:bar", connection);
OATPP_ASSERT(response->getStatusCode() == 200);
}
{ // test authorzation of unknown user in endpoint-code
oatpp::String token = "john:doe"; // this time as string to test Macro generation
auto response = client->defAuthorization(token);
OATPP_ASSERT(response->getStatusCode() == 403);
}
{ // test call of an endpoint that requiers authorization headers, but we don't send one
auto response = client->defAuthorizationWithoutHeader();
auto response = client->myAuthorizationWithoutHeader();
OATPP_ASSERT(response->getStatusCode() == 401);
oatpp::String body = response->readBodyToString();
OATPP_ASSERT(body == "server=oatpp/" OATPP_VERSION "\n"
@ -254,11 +243,6 @@ void FullTest::onRun() {
OATPP_ASSERT(header->second.toString()->startsWith("Basic realm="))
}
{ // test custom authorization handler with default authorization object
auto response = client->myDefAuthorization("foo:bar", connection);
OATPP_ASSERT(response->getStatusCode() == 200);
}
{ // test custom authorization handler with custom authorization object
auto response = client->myAuthorization("foo:bar", connection);
OATPP_ASSERT(response->getStatusCode() == 200);

View File

@ -46,74 +46,75 @@ namespace oatpp { namespace test { namespace web { namespace app {
class MyAuthorizationObject : public oatpp::web::server::handler::AuthorizationObject {
public:
oatpp::Int64 id;
MyAuthorizationObject(v_int64 pId, const oatpp::String& pAuthString)
: id(pId)
, authString(pAuthString)
{}
v_int64 id;
oatpp::String authString;
};
class MyAuthorizationHandler : public oatpp::web::server::handler::AuthorizationHandler {
class MyBasicAuthorizationHandler : public oatpp::web::server::handler::BasicAuthorizationHandler {
public:
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> handleAuthorization(const oatpp::String &header) override {
auto def = oatpp::web::server::handler::DefaultAuthorizationHandler::defaultAuthorizationObject(header);
auto my = std::make_shared<MyAuthorizationObject>();
my->user = def->user;
my->password = def->password;
if(my->user != "foo" || my->password != "bar") {
return nullptr;
std::shared_ptr<AuthorizationObject> authorize(const oatpp::String& userId, const oatpp::String& password) override {
if(userId == "foo" && password == "bar") {
return std::make_shared<MyAuthorizationObject>(1337, userId + ":" + password);
}
my->id = 1337;
return my;
return nullptr;
}
};
class AuthorizationController : public oatpp::web::server::api::ApiController {
class BasicAuthorizationController : public oatpp::web::server::api::ApiController {
private:
static constexpr const char* TAG = "test::web::app::AuthorizationController";
static constexpr const char* TAG = "test::web::app::BasicAuthorizationController";
public:
AuthorizationController(const std::shared_ptr<ObjectMapper>& objectMapper)
BasicAuthorizationController(const std::shared_ptr<ObjectMapper>& objectMapper)
: oatpp::web::server::api::ApiController(objectMapper)
{
m_authorizationHandler = std::make_shared<MyAuthorizationHandler>();
m_authorizationHandler = std::make_shared<MyBasicAuthorizationHandler>();
}
public:
static std::shared_ptr<AuthorizationController> createShared(const std::shared_ptr<ObjectMapper>& objectMapper = OATPP_GET_COMPONENT(std::shared_ptr<ObjectMapper>)){
return std::make_shared<AuthorizationController>(objectMapper);
static std::shared_ptr<BasicAuthorizationController> createShared(const std::shared_ptr<ObjectMapper>& objectMapper = OATPP_GET_COMPONENT(std::shared_ptr<ObjectMapper>)){
return std::make_shared<BasicAuthorizationController>(objectMapper);
}
#include OATPP_CODEGEN_BEGIN(ApiController)
ENDPOINT("GET", "mydefauthorization", authorization,
AUTHORIZATION(std::shared_ptr<oatpp::web::server::handler::AuthorizationObject>, authorizationHeader)) {
auto dto = TestDto::createShared();
dto->testValue = authorizationHeader->user + ":" + authorizationHeader->password;
if(dto->testValue == "foo:bar") {
return createDtoResponse(Status::CODE_200, dto);
} else {
return createDtoResponse(Status::CODE_401, dto);
}
}
ENDPOINT("GET", "myauthorization", myauthorization,
AUTHORIZATION(std::shared_ptr<MyAuthorizationObject>, authorizationHeader)) {
AUTHORIZATION(std::shared_ptr<MyAuthorizationObject>, authObject)) {
auto dto = TestDto::createShared();
dto->testValue = authorizationHeader->user + ":" + authorizationHeader->password;
if(dto->testValue == "foo:bar" && authorizationHeader->id == oatpp::Int64(1337)) {
dto->testValue = authObject->authString;
if(dto->testValue == "foo:bar" && authObject->id == 1337) {
return createDtoResponse(Status::CODE_200, dto);
} else {
return createDtoResponse(Status::CODE_401, dto);
}
}
ENDPOINT("GET", "myauthorizationrealm", myauthorizationrealm,
AUTHORIZATION(std::shared_ptr<MyAuthorizationObject>, authorizationHeader, "Test Realm")) {
AUTHORIZATION(std::shared_ptr<MyAuthorizationObject>, authObject, "Test Realm")) {
auto dto = TestDto::createShared();
dto->testValue = authorizationHeader->user + ":" + authorizationHeader->password;
if(dto->testValue == "foo:bar" && authorizationHeader->id == oatpp::Int64(1337)) {
dto->testValue = authObject->authString;
if(dto->testValue == "foo:bar" && authObject->id == 1337) {
return createDtoResponse(Status::CODE_200, dto);
} else {
return createDtoResponse(Status::CODE_401, dto);
}
}
#include OATPP_CODEGEN_END(ApiController)
};

View File

@ -45,12 +45,15 @@
namespace oatpp { namespace test { namespace web { namespace app {
class BearerAuthorizationObject : public oatpp::web::server::handler::AuthorizationObject {
public:
public:
oatpp::String user;
oatpp::String password;
oatpp::String token;
};
class BearerAuthorizationHandler : public oatpp::web::server::handler::AuthorizationHandler {
public:
public:
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> handleAuthorization(const oatpp::String &header) override {
if(!header->startsWith("Bearer ")) {
return nullptr;
@ -69,19 +72,20 @@ class BearerAuthorizationHandler : public oatpp::web::server::handler::Authoriza
return nullptr;
}
};
class BearerAuthorizationController : public oatpp::web::server::api::ApiController {
private:
private:
static constexpr const char* TAG = "test::web::app::BearerAuthorizationController";
public:
public:
BearerAuthorizationController(const std::shared_ptr<ObjectMapper>& objectMapper)
: oatpp::web::server::api::ApiController(objectMapper)
: oatpp::web::server::api::ApiController(objectMapper)
{
m_authorizationHandler = std::make_shared<BearerAuthorizationHandler>();
}
public:
public:
static std::shared_ptr<BearerAuthorizationController> createShared(const std::shared_ptr<ObjectMapper>& objectMapper = OATPP_GET_COMPONENT(std::shared_ptr<ObjectMapper>)){
return std::make_shared<BearerAuthorizationController>(objectMapper);

View File

@ -50,10 +50,8 @@ public:
API_CALL("POST", "echo", echoBody, BODY_STRING(String, body))
API_CALL("GET", "header-value-set", headerValueSet, HEADER(String, valueSet, "X-VALUE-SET"))
API_CALL("GET", "defauthorization", defAuthorization, AUTHORIZATION(String, authorization))
API_CALL("GET", "defauthorization", defAuthorizationWithoutHeader)
API_CALL("GET", "mydefauthorization", myDefAuthorization, AUTHORIZATION(String, authorization))
API_CALL("GET", "myauthorization", myAuthorization, AUTHORIZATION(String, authorization))
API_CALL("GET", "myauthorization", myAuthorizationWithoutHeader)
API_CALL("GET", "myauthorizationrealm", myAuthorizationRealm, AUTHORIZATION(String, authorization));
API_CALL("GET", "bearerauthorization", bearerAuthorization, AUTHORIZATION(String, authorization, "Bearer"))

View File

@ -133,17 +133,6 @@ public:
return createResponse(Status::CODE_200, "");
}
ENDPOINT("GET", "defauthorization", authorization,
AUTHORIZATION(std::shared_ptr<oatpp::web::server::handler::AuthorizationObject>, authorizationHeader)) {
auto dto = TestDto::createShared();
dto->testValue = authorizationHeader->user + ":" + authorizationHeader->password;
if(dto->testValue == "foo:bar") {
return createDtoResponse(Status::CODE_200, dto);
} else {
return createDtoResponse(Status::CODE_403, dto);
}
}
class ReadCallback : public oatpp::data::stream::ReadCallback {
private:
oatpp::String m_text;

View File

@ -29,6 +29,28 @@
namespace oatpp { namespace test { namespace web { namespace server { namespace handler {
namespace {
class MyBasicAuthorizationObject : public oatpp::web::server::handler::AuthorizationObject {
public:
oatpp::String userId;
oatpp::String password;
};
class MyBasicAuthorizationHandler : public oatpp::web::server::handler::BasicAuthorizationHandler {
public:
std::shared_ptr<AuthorizationObject> authorize(const oatpp::String& userId, const oatpp::String& password) {
auto authObject = std::make_shared<MyBasicAuthorizationObject>();
authObject->userId = userId;
authObject->password = password;
return authObject;
}
};
}
void AuthorizationHandlerTest::onRun() {
oatpp::String user = "foo";
@ -36,10 +58,10 @@ void AuthorizationHandlerTest::onRun() {
oatpp::String header = "Basic Zm9vOmJhcg==";
{
std::shared_ptr<oatpp::web::server::handler::DefaultAuthorizationHandler> default_authorization_handler = oatpp::web::server::handler::DefaultAuthorizationHandler::createShared();
std::shared_ptr<oatpp::web::server::handler::AuthorizationObject> auth = default_authorization_handler->handleAuthorization(header);
OATPP_LOGV(TAG, "header=\"%s\" -> user=\"%s\" password=\"%s\"", header->c_str(), auth->user->c_str(), auth->password->c_str());
OATPP_ASSERT(auth->user->equals("foo"));
MyBasicAuthorizationHandler basicAuthHandler;
auto auth = std::static_pointer_cast<MyBasicAuthorizationObject>(basicAuthHandler.handleAuthorization(header));
OATPP_LOGV(TAG, "header=\"%s\" -> user=\"%s\" password=\"%s\"", header->c_str(), auth->userId->c_str(), auth->password->c_str());
OATPP_ASSERT(auth->userId->equals("foo"));
OATPP_ASSERT(auth->password->equals("bar"));
}