Skip to content

Commit

Permalink
[irods#7576] Allow proxied groupadmin to use user administration api
Browse files Browse the repository at this point in the history
This PR patches add_user(...), remove_group(...),
add_user_to_group(...), and remove_user_from_group(...) to use
the rxUserAdmin(...) endpoint for groupadmins.
  • Loading branch information
MartinFlores751 authored and alanking committed Apr 22, 2024
1 parent cd6b746 commit e4b4e8a
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 2 deletions.
5 changes: 5 additions & 0 deletions lib/administration/user/include/irods/user_administration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,30 @@
#undef NAMESPACE_IMPL
#undef RxComm
#undef rxGeneralAdmin
#undef rxUserAdmin

#ifdef IRODS_USER_ADMINISTRATION_ENABLE_SERVER_SIDE_API
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
# define NAMESPACE_IMPL server
# define RxComm RsComm
# define rxGeneralAdmin rsGeneralAdmin
# define rxUserAdmin rsUserAdmin
// NOLINTEND(cppcoreguidelines-macro-usage)

# include "irods/rsGeneralAdmin.hpp"
# include "irods/rsUserAdmin.hpp"

struct RsComm;
#else
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
# define NAMESPACE_IMPL client
# define RxComm RcComm
# define rxGeneralAdmin rcGeneralAdmin
# define rxUserAdmin rcUserAdmin
// NOLINTEND(cppcoreguidelines-macro-usage)

# include "irods/generalAdmin.h"
# include "irods/userAdmin.h"

struct RcComm;
#endif // IRODS_USER_ADMINISTRATION_ENABLE_SERVER_SIDE_API
Expand Down
72 changes: 70 additions & 2 deletions lib/administration/user/src/user_administration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ namespace irods::experimental::administration::NAMESPACE_IMPL
zone = get_local_zone(_comm);
}

const auto current_user{user{_comm.clientUser.userName, _comm.clientUser.rodsZone}};
const auto user_type{type(_comm, current_user)};

if (user_type == irods::experimental::administration::user_type::groupadmin) {
// clang-format off
userAdminInp_t input{.arg0 = "mkuser",
.arg1 = const_cast<char*>(name.data()),
.arg3 = const_cast<char*>(zone.data())};
// clang-format on
if (const auto ec{rxUserAdmin(&_comm, &input)}; ec != 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
THROW(ec, fmt::format("Failed to add user [{}].", name));
}
return;
}

GeneralAdminInput input{};
input.arg0 = "add";
input.arg1 = "user";
Expand Down Expand Up @@ -87,6 +103,22 @@ namespace irods::experimental::administration::NAMESPACE_IMPL
{
const auto zone = get_local_zone(_comm);

const auto current_user{user{_comm.clientUser.userName, _comm.clientUser.rodsZone}};
const auto user_type{type(_comm, current_user)};

if (user_type == irods::experimental::administration::user_type::groupadmin) {
userAdminInp_t input{.arg0 = "mkgroup",
.arg1 = const_cast<char*>(_group.name.data()),
.arg2 = "rodsgroup",
.arg3 = const_cast<char*>(zone.data())};

if (const auto ec{rxUserAdmin(&_comm, &input)}; ec != 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
THROW(ec, fmt::format("Failed to add group [{}].", _group.name));
}
return;
}

GeneralAdminInput input{};
input.arg0 = "add";
input.arg1 = "user";
Expand Down Expand Up @@ -122,6 +154,25 @@ namespace irods::experimental::administration::NAMESPACE_IMPL

auto add_user_to_group(RxComm& _comm, const group& _group, const user& _user) -> void
{
const auto name{local_unique_name(_comm, _user)};
const auto current_user{user{_comm.clientUser.userName, _comm.clientUser.rodsZone}};
const auto user_type{type(_comm, current_user)};

if (user_type == irods::experimental::administration::user_type::groupadmin) {
userAdminInp_t input{.arg0 = "modify",
.arg1 = "group",
.arg2 = const_cast<char*>(_group.name.data()),
.arg3 = "add",
.arg4 = const_cast<char*>(_user.name.data()),
.arg5 = const_cast<char*>(_user.zone.data())};

if (const auto ec{rxUserAdmin(&_comm, &input)}; ec != 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
THROW(ec, fmt::format("Failed to add user [{}] to group [{}].", name, _group.name));
}
return;
}

GeneralAdminInput input{};
input.arg0 = "modify";
input.arg1 = "group";
Expand All @@ -131,14 +182,32 @@ namespace irods::experimental::administration::NAMESPACE_IMPL
input.arg5 = _user.zone.data();

if (const auto ec = rxGeneralAdmin(&_comm, &input); ec != 0) {
const auto name = local_unique_name(_comm, _user);
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
THROW(ec, fmt::format("Failed to add user [{}] to group [{}].", name, _group.name));
}
} // add_user_to_group

auto remove_user_from_group(RxComm& _comm, const group& _group, const user& _user) -> void
{
const auto name{local_unique_name(_comm, _user)};
const auto current_user{user{_comm.clientUser.userName, _comm.clientUser.rodsZone}};
const auto user_type{type(_comm, current_user)};

if (user_type == irods::experimental::administration::user_type::groupadmin) {
userAdminInp_t input{.arg0 = "modify",
.arg1 = "group",
.arg2 = const_cast<char*>(_group.name.data()),
.arg3 = "remove",
.arg4 = const_cast<char*>(_user.name.data()),
.arg5 = const_cast<char*>(_user.zone.data())};

if (const auto ec{rxUserAdmin(&_comm, &input)}; ec != 0) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
THROW(ec, fmt::format("Failed to remove user [{}] from group [{}].", name, _group.name));
}
return;
}

GeneralAdminInput input{};
input.arg0 = "modify";
input.arg1 = "group";
Expand All @@ -148,7 +217,6 @@ namespace irods::experimental::administration::NAMESPACE_IMPL
input.arg5 = _user.zone.data();

if (const auto ec = rxGeneralAdmin(&_comm, &input); ec != 0) {
const auto name = local_unique_name(_comm, _user);
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
THROW(ec, fmt::format("Failed to remove user [{}] from group [{}].", name, _group.name));
}
Expand Down
51 changes: 51 additions & 0 deletions unit_tests/src/test_user_administration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ TEST_CASE("user group administration")

irods::experimental::client_connection conn;

// Create a shared, generic test user
adm::user generic_user{"lagging-train"};
REQUIRE_NOTHROW(adm::client::add_user(conn, generic_user));
irods::at_scope_exit generic_user_cleanup{
[&conn, &generic_user] { CHECK_NOTHROW(adm::client::remove_user(conn, generic_user)); }};

SECTION("local user operations")
{
adm::user test_user{"unit_test_test_user"};
Expand Down Expand Up @@ -223,4 +229,49 @@ TEST_CASE("user group administration")
irods::experimental::defer_authentication, env.rodsHost, env.rodsPort, {test_user.name, env.rodsZone}};
CHECK(clientLoginWithPassword(static_cast<RcComm*>(user_conn), test_user_prop.value.data()) == 0);
}

SECTION("#7576: ensure no limitations on proxied groupadmin")
{
rodsEnv env{};
_getRodsEnv(env);

REQUIRE_NOTHROW(
adm::client::modify_user(conn, generic_user, adm::user_type_property{adm::user_type::groupadmin}));
REQUIRE(adm::client::type(conn, generic_user) == adm::user_type::groupadmin);

// Connect to iRODS
irods::experimental::client_connection user_conn{env.rodsHost,
env.rodsPort,
{env.rodsUserName, env.rodsZone}, // (proxy) rodsadmin user
{generic_user.name, env.rodsZone}}; // groupadmin user

// Try to add a group as the proxied groupadmin user.
adm::group test_group{"bad_bad_water_group"};
REQUIRE_NOTHROW(adm::client::add_group(user_conn, test_group));
REQUIRE(adm::client::exists(user_conn, test_group));
irods::at_scope_exit group_cleanup{
[&conn, &test_group] { CHECK_NOTHROW(adm::client::remove_group(conn, test_group)); }};

// Add self to group
REQUIRE_NOTHROW(adm::client::add_user_to_group(user_conn, test_group, generic_user));
REQUIRE(adm::client::user_is_member_of_group(user_conn, test_group, generic_user));
irods::at_scope_exit groupadmin_self_add_cleanup{[&user_conn, &test_group, &generic_user] {
CHECK_NOTHROW(adm::client::remove_user_from_group(user_conn, test_group, generic_user));
}};

// Create and remove user using groupadmin
adm::user groupadmin_created_user{"mr_flips"};
REQUIRE_NOTHROW(adm::client::add_user(user_conn, groupadmin_created_user));
REQUIRE(adm::client::exists(user_conn, groupadmin_created_user));
irods::at_scope_exit created_user_cleanup{[&conn, &groupadmin_created_user] {
CHECK_NOTHROW(adm::client::remove_user(conn, groupadmin_created_user));
}};

// Add and remove user from group using groupadmin
REQUIRE_NOTHROW(adm::client::add_user_to_group(user_conn, test_group, groupadmin_created_user));
REQUIRE(adm::client::user_is_member_of_group(user_conn, test_group, groupadmin_created_user));

// Begin cleanup. Previous 'irods::at_scope_exit's will run after the following line
CHECK_NOTHROW(adm::client::remove_user_from_group(user_conn, test_group, groupadmin_created_user));
}
}

0 comments on commit e4b4e8a

Please sign in to comment.