Commit d7d4c49c authored by Sophie Wenzel-Teuber's avatar Sophie Wenzel-Teuber
Browse files

Add the option to send the payload unsigned to the authorisation

Changelog:
* in authorisation check if body is existent and signed
* add string `UNSIGNED_PAYLOAD` to the canonical request if requested
* check in put_request_handler if authorisation is waitig for the body or not
parent ac693d84
Pipeline #1790 passed with stages
in 7 minutes and 14 seconds
......@@ -15,7 +15,7 @@ The implementation of this class follows the guidelines from [https://docs.aws.a
| Name | Description |
| ---- | ---- |
| [search_for_user](#fiphoboserver-s3_utilities-S3_authorisation-search_for_user) | checks whether the user id saved in [m_user_identifier][fiphoboserver-s3_utilities-S3_authorisation-m_user_identifier] exists in the user database |
| [is_chunked](#fiphoboserver-s3_utilities-S3_authorisation-is_chunked) | check if this message has a content body |
| [has_signed_payload](#fiphoboserver-s3_utilities-S3_authorisation-has_signed_payload) | check if this message has a signed content body |
| [print_info](#fiphoboserver-s3_utilities-S3_authorisation-print_info) | print current state of member variables |
......@@ -59,6 +59,7 @@ The implementation of this class follows the guidelines from [https://docs.aws.a
| [authorise](#fiphoboserver-s3_utilities-S3_authorisation-authorise) | main method to run the authorisation algorithm |
| [add_chunk](#fiphoboserver-s3_utilities-S3_authorisation-add_chunk) | add a chunk of data to the payload |
| [is_valid](#fiphoboserver-s3_utilities-S3_authorisation-is_valid) | checks if the authorisation was successful |
| [get_status](#fiphoboserver-s3_utilities-S3_authorisation-get_status) | returns the current status of the authorisation |
| [get_error](#fiphoboserver-s3_utilities-S3_authorisation-get_error) | returns the internal error |
......@@ -92,9 +93,9 @@ Sets
[Go to Top](#fiphoboserver-s3_utilities-S3_authorisation)
### <a name='fiphoboserver-s3_utilities-S3_authorisation-is_chunked' /> private bool fiphoboserver::s3_utilities::S3_authorisation::is_chunked (const S3_header &headers) const
### <a name='fiphoboserver-s3_utilities-S3_authorisation-has_signed_payload' /> private bool fiphoboserver::s3_utilities::S3_authorisation::has_signed_payload (const S3_header &headers) const
check if this message has a content body
check if this message has a signed content body
......@@ -107,7 +108,7 @@ check if this message has a content body
#### Returns:
| Type | Description |
| ---- | ---- |
| bool | true if the message contains a body, false otherwise |
| bool | true if the message contains a signed body, false if it doesn't have any body of if it is unsigned |
......@@ -680,6 +681,36 @@ checks if the authorisation was successful
#### Qualifiers:
* const
* inline
[Go to Top](#fiphoboserver-s3_utilities-S3_authorisation)
### <a name='fiphoboserver-s3_utilities-S3_authorisation-get_status' /> public [Authorisation_status][fiphoboserver-s3_utilities-Authorisation_status] fiphoboserver::s3_utilities::S3_authorisation::get_status () const
returns the current status of the authorisation
#### Returns:
| Type | Description |
| ---- | ---- |
| [Authorisation_status][fiphoboserver-s3_utilities-Authorisation_status] | the current status of the authorisaton |
#### Qualifiers:
* const
* inline
......
......@@ -30,8 +30,10 @@ void PutRequestHandler::onRequest(
// m_s3_header.print_all_headers();
#ifndef NO_AUTHORISATION
if (m_auth.authorise(m_s3_header)
!= s3_utilities::Authorisation_status::waiting_for_payload) {
s3_utilities::Authorisation_status auth_status =
m_auth.authorise(m_s3_header);
if (auth_status != s3_utilities::Authorisation_status::waiting_for_payload
&& auth_status != s3_utilities::Authorisation_status::valid) {
s3_utilities::s3_error_info auth_error = m_auth.get_error();
proxygen::ResponseBuilder(downstream_)
......@@ -93,10 +95,13 @@ void PutRequestHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept
try {
#ifndef NO_AUTHORISATION
// Add body as string to authentication object as payload
std::string body_as_string = std::string(
reinterpret_cast<const char*>(body->data()), body->length());
m_auth.add_chunk(body_as_string);
if (m_auth.get_status()
== s3_utilities::Authorisation_status::waiting_for_payload) {
// Add body as string to authentication object as payload
std::string body_as_string = std::string(
reinterpret_cast<const char*>(body->data()), body->length());
m_auth.add_chunk(body_as_string);
}
#endif
/* Hand message body over to stream for PUT operation */
......@@ -124,17 +129,22 @@ void PutRequestHandler::onEOM() noexcept
}
#ifndef NO_AUTHORISATION
if (m_auth.authorise(m_s3_header)
!= s3_utilities::Authorisation_status::valid) {
// TODO: Delete object from phobos or revert put! (Issue #45)
s3_utilities::s3_error_info auth_error = m_auth.get_error();
proxygen::ResponseBuilder(downstream_)
.status(
auth_error.https_error_code, auth_error.https_error_identifier)
.body(auth_error.get_xml(m_s3_header.get_key()))
.sendWithEOM();
return;
if (m_auth.get_status()
== s3_utilities::Authorisation_status::waiting_for_payload) {
// was waiting for payload, now I can try to authorise again
if (m_auth.authorise(m_s3_header)
!= s3_utilities::Authorisation_status::valid) {
// TODO: Delete object from phobos or revert put! (Issue #45)
s3_utilities::s3_error_info auth_error = m_auth.get_error();
proxygen::ResponseBuilder(downstream_)
.status(
auth_error.https_error_code,
auth_error.https_error_identifier)
.body(auth_error.get_xml(m_s3_header.get_key()))
.sendWithEOM();
return;
}
}
#endif
try {
......
......@@ -22,7 +22,7 @@ Authorisation_status S3_authorisation::authorise(const S3_header& headers)
return m_status;
}
if (is_chunked(headers)) {
if (has_signed_payload(headers)) {
m_status = Authorisation_status::waiting_for_payload;
}
else {
......@@ -197,13 +197,18 @@ bool S3_authorisation::search_for_user()
return false;
}
bool S3_authorisation::is_chunked(const S3_header& headers) const
bool S3_authorisation::has_signed_payload(const S3_header& headers) const
{
std::string content_length = headers.get_header_by_name("Content-Length");
std::string payload_sha256 =
headers.get_header_by_name("x-amz-content-sha256");
if (content_length.empty()) {
return false;
}
if (payload_sha256.empty() || payload_sha256 == "UNSIGNED-PAYLOAD") {
return false;
}
return true;
}
......@@ -297,7 +302,14 @@ std::string S3_authorisation::create_canonical_request(
}
result_stream << '\n';
result_stream << openssl_sha256(m_payload);
std::string payload_sha256 =
headers.get_header_by_name("x-amz-content-sha256");
if (payload_sha256 == "UNSIGNED-PAYLOAD") {
result_stream << "UNSIGNED-PAYLOAD";
}
else {
result_stream << openssl_sha256(m_payload);
}
return result_stream.str();
}
......
......@@ -60,6 +60,13 @@ class S3_authorisation {
///
bool is_valid() const { return m_status == Authorisation_status::valid; }
///
/// @brief returns the current status of the authorisation
///
/// @return the current status of the authorisaton
///
Authorisation_status get_status() const { return m_status; }
///
/// @brief returns the internal error
///
......@@ -82,13 +89,14 @@ class S3_authorisation {
///
bool search_for_user();
///
/// @brief check if this message has a content body
/// @brief check if this message has a signed content body
///
/// @param headers the headers of the message to check
///
/// @returns true if the message contains a body, false otherwise
/// @returns true if the message contains a signed body, false if it doesn't
/// have any body of if it is unsigned
///
bool is_chunked(const S3_header& headers) const;
bool has_signed_payload(const S3_header& headers) const;
///
/// @brief print current state of member variables
......
......@@ -7,7 +7,7 @@
namespace fiphoboserver {
namespace s3_utilities {
SCENARIO("S3 header authorisation", "[authorisation]")
SCENARIO("S3 default header authorisation", "[authorisation]")
{
// Those are the examples from
// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
......@@ -194,6 +194,74 @@ SCENARIO("S3 header authorisation", "[authorisation]")
}
}
SCENARIO("S3 unsigned-payload header authorisation", "[authorisation]")
{
// The headers are a copy of what the s3 copytool sends
std::unique_ptr<proxygen::HTTPMessage> message =
std::make_unique<proxygen::HTTPMessage>();
proxygen::HTTPHeaders& headers = message->getHeaders();
S3_header s3_header;
S3_authorisation auth;
GIVEN("A PUT request with unsigned payload")
{
message->setMethod(proxygen::HTTPMethod::PUT);
message->setURL("/lustre_hsm_4/0000000200000401_00000004_00000000.0");
headers.add("Content-Type", "application/x-lz4");
headers.add("Host", "192.168.2.117:11000");
headers.add("x-amz-content-sha256", "UNSIGNED-PAYLOAD");
headers.add("x-amz-date", "20200612T095534Z");
headers.add("x-amz-meta-chunksize", "104857600");
headers.add("x-amz-meta-gid", "0");
headers.add(
"x-amz-meta-path", "/mnt/lustre/.lustre/fid/0x200000401:0x4:0x0");
headers.add("x-amz-meta-stripecount", "1");
headers.add("x-amz-meta-stripesize", "1048576");
headers.add("x-amz-meta-totallength", "50");
headers.add("x-amz-meta-uid", "0");
headers.add("Content-Length", "52");
WHEN("The authentication header is valid")
{
headers.add(
"Authorization",
"AWS4-HMAC-SHA256 Credential=OPEN_KEY/20200612/us-east-1/s3/aws4_request,SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-chunksize;x-amz-meta-gid;x-amz-meta-path;x-amz-meta-stripecount;x-amz-meta-stripesize;x-amz-meta-totallength;x-amz-meta-uid,Signature=81506ba791e192d332f39c8d6cc6031794de31bead366f55e75cff6cc3e41e8d");
s3_header.set_headers(std::move(message));
THEN("The authentication is valid")
{
REQUIRE(
auth.authorise(s3_header) == Authorisation_status::valid);
auth.add_chunk("Text that should not matter");
REQUIRE(
auth.authorise(s3_header) == Authorisation_status::valid);
}
}
WHEN("The authentication header is not valid")
{
// missing x-amz-meta-gid:
headers.add(
"Authorization",
"AWS4-HMAC-SHA256 Credential=OPEN_KEY/20200612/us-east-1/s3/aws4_request,SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-meta-chunksize;x-amz-meta-path;x-amz-meta-stripecount;x-amz-meta-stripesize;x-amz-meta-totallength;x-amz-meta-uid,Signature=81506ba791e192d332f39c8d6cc6031794de31bead366f55e75cff6cc3e41e8d");
s3_header.set_headers(std::move(message));
THEN("The authentication is not valid")
{
REQUIRE(
auth.authorise(s3_header) != Authorisation_status::valid);
}
}
}
}
} // namespace s3_utilities
} // namespace fiphoboserver
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment