Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,7 @@ If an HTTP status code of 200 is returned, the body of the response will contain
{
"api_version": "string",
"build": "string",
"irods_server_version": "string", // Included for authenticated requests.
"irods_zone": "string",
"max_number_of_parallel_write_streams": 0,
"max_number_of_rows_per_catalog_query": 0,
Expand Down
3 changes: 3 additions & 0 deletions core/include/irods/private/http_api/globals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ namespace irods::http::globals

auto set_user_mapping_lib(boost::dll::shared_library _lib) -> void;
auto user_mapping_lib() -> boost::dll::shared_library&;

auto set_irods_server_version(std::string _version) -> void;
auto get_irods_server_version() -> const std::string&;
} // namespace irods::http::globals

#endif // IRODS_HTTP_API_GLOBALS_HPP
13 changes: 13 additions & 0 deletions core/src/globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ namespace

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
boost::dll::shared_library g_user_map_lib;

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::string g_irods_server_version;
} // anonymous namespace

namespace irods::http::globals
Expand Down Expand Up @@ -108,4 +111,14 @@ namespace irods::http::globals
{
return g_user_map_lib;
} // user_mapping_lib

auto set_irods_server_version(std::string _version) -> void
{
g_irods_server_version = std::move(_version);
} // set_irods_server_version

auto get_irods_server_version() -> const std::string&
{
return g_irods_server_version;
} // get_irods_server_version
} // namespace irods::http::globals
30 changes: 30 additions & 0 deletions core/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "irods/private/http_api/process_stash.hpp"
#include "irods/private/http_api/version.hpp"

#include <irods/client_connection.hpp>
#include <irods/connection_pool.hpp>
#include <irods/fully_qualified_username.hpp>
#include <irods/irods_configuration_keywords.hpp>
Expand Down Expand Up @@ -1075,10 +1076,39 @@ auto main(int _argc, char* _argv[]) -> int

std::unique_ptr<irods::connection_pool> conn_pool;

// Initialize the connection pool and cache the version of the iRODS server.
if (!config.at(json::json_pointer{"/irods_client/enable_4_2_compatibility"}).get<bool>()) {
logging::trace("Initializing iRODS connection pool.");
conn_pool = init_irods_connection_pool(config);
irods::http::globals::set_connection_pool(*conn_pool);

// Cache the version of the connected iRODS server. The /info endpoint includes
// the iRODS server version in the response for authenticated HTTP requests.
// This value MUST NOT influence the behavior of the HTTP API. It is purely for
// the user/application interacting with the HTTP API.
auto conn = conn_pool->get_connection();
irods::http::globals::set_irods_server_version(static_cast<RcComm&>(conn).svrVersion->relVersion);
}
else {
// The admin has decided that no connection pool will be used. Connect to the server
// using the admin credentials. There's no need to authenticate since we're only interested
// in the version of the connected iRODS server.
const auto& host = config.at(json::json_pointer{"/irods_client/host"}).get_ref<const std::string&>();
const auto port = config.at(json::json_pointer{"/irods_client/port"}).get<int>();
const auto& zone = config.at(json::json_pointer{"/irods_client/zone"}).get_ref<const std::string&>();
const auto& username = config.at(json::json_pointer{"/irods_client/proxy_admin_account/username"})
.get_ref<const std::string&>();
const irods::experimental::client_connection conn{
irods::experimental::defer_authentication,
host,
port,
irods::experimental::fully_qualified_username{username, zone}};

// Cache the version of the connected iRODS server. The /info endpoint includes
// the iRODS server version in the response for authenticated HTTP requests.
// This value MUST NOT influence the behavior of the HTTP API. It is purely for
// the user/application interacting with the HTTP API.
irods::http::globals::set_irods_server_version(static_cast<RcComm&>(conn).svrVersion->relVersion);
}

// The io_context is required for all I/O.
Expand Down
10 changes: 8 additions & 2 deletions endpoints/information/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,23 @@ namespace irods::http::handler
res.keep_alive(_req.keep_alive());

// clang-format off
res.body() = json{
json server_info{
{"api_version", irods::http::version::api_version},
{"build", irods::http::version::sha},
{"irods_zone", irods_client_config.at("zone")},
{"max_number_of_parallel_write_streams", irods_client_config.at("max_number_of_parallel_write_streams")},
{"max_number_of_rows_per_catalog_query", irods_client_config.at("max_number_of_rows_per_catalog_query")},
{"max_size_of_request_body_in_bytes", http_server_config.at(json::json_pointer{"/requests/max_size_of_request_body_in_bytes"})},
{"openid_connect_enabled", http_server_config.contains(json::json_pointer{"/authentication/openid_connect"})}
}.dump();
};
// clang-format on

// Include the version of the iRODS server if this is an authenticated request.
if (auto result = irods::http::resolve_client_identity(_req); !result.response) {
server_info["irods_server_version"] = globals::get_irods_server_version();
}

res.body() = server_info.dump();
res.prepare_payload();

return _sess_ptr->send(std::move(res));
Expand Down
26 changes: 25 additions & 1 deletion test/test_irods_http_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4041,7 +4041,11 @@ class test_information_endpoint(unittest.TestCase):

@classmethod
def setUpClass(cls):
setup_class(cls, {'endpoint_name': 'info', 'init_rodsadmin': False})
setup_class(cls, {'endpoint_name': 'info'})

@classmethod
def tearDownClass(cls):
tear_down_class(cls)

def setUp(self):
self.assertFalse(self._class_init_error, 'Class initialization failed. Cannot continue.')
Expand All @@ -4060,6 +4064,26 @@ def test_expected_properties_exist_in_json_structure(self):
self.assertIn('max_size_of_request_body_in_bytes', info)
self.assertIn('openid_connect_enabled', info)

# This property should not be available because the request is not from
# an authenticated user.
self.assertNotIn('irods_server_version', info)

def test_irods_server_version_is_included_in_json_structure_for_authenticated_requests(self):
rodsuser_headers = {'Authorization': f'Bearer {self.rodsuser_bearer_token}'}
r = requests.get(self.url_endpoint, headers=rodsuser_headers)
self.logger.debug(r.content)
self.assertEqual(r.status_code, 200)

info = r.json()
self.assertIn('api_version', info)
self.assertIn('build', info)
self.assertIn('irods_server_version', info)
self.assertIn('irods_zone', info)
self.assertIn('max_number_of_parallel_write_streams', info)
self.assertIn('max_number_of_rows_per_catalog_query', info)
self.assertIn('max_size_of_request_body_in_bytes', info)
self.assertIn('openid_connect_enabled', info)

def test_server_reports_error_when_http_method_is_not_supported(self):
do_test_server_reports_error_when_http_method_is_not_supported(self)

Expand Down
Loading