From 177979468e6096e6d9e45495020e5f6faddce8c0 Mon Sep 17 00:00:00 2001 From: huerni <47264950+huerni@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:12:41 +0800 Subject: [PATCH] Separate Authentication and Modification in AccountManager (#321) * feat: separate adduser addaccount and addqos * feat: separate AM add partition and qos * feat: Standardize item in proto and rename it to modifyField * fix: CheckAddUserAllowedQos check qos already in user allowed * feat: modifyUser overwrite * refactor: Replace pointer parameters with references * feat: modifyuser delete * feat: delete user and fix delete user in default account(bug 179) * feat: deleteAccount deleteQos * feat: modifyAccount * fix: Cannot modify partition and qos simultaneously * feat: blockuser blockaccount * feat: refactor QueryUser QueryAccount QueryQos * feat: refactor modifyUser * feat: refactor blockaccount blockuser * fix: modifyUserdefaultqos * feat: CheckPartitionIsAllowed CheckQosIsAllowed * fix: fix modify adminLevel, new_level must <= op_level * fix: modify qos modifyField error * fix: adduser Check whether the account exists * fix: addcount opuser existed and account empty bug * fix: CheckOpUserHasPermissionToAccount * fix: simultaneous modification conflict * feat: Return front-end error code demo. * feat:fix CheckSetUserAllowedQos No result returned issue. * feat: update err code * feat: Error Code Specification * merge Add display of coordinator field (#180) * feat: To upgrade to C++23, use std::expected and std::unreachable. * feat: update errCode * delete tl expected head file * fix: errCode * fix: add ERR_PARENT_ALLOWED_ err code * fix: ERR_USER_EMPTY_PARTITION * fix: set account qos err code * refactor * refactor * refactor * refactor * refactor * refactor * refactor * refactor: unused-parameters * refactor: CraneExpected to CraneExpected * refactor * refactor Signed-off-by: RileyW * refactor * refactor: CheckIfUserHasPemOnUser CheckIfUserHasPemOnUser CheckIfUserHasPemOnUser CheckIfUserHasPemOnUser * refactor: HasPermissionToUser * Refactor. Signed-off-by: RileyW * fix: double locking * refactor * refactor * fix: AddAccount * Update PublicDefs.proto Remove useless changes * fix: The default QoS setting was missed when adding the account. * refactor * Reformat cmake. Signed-off-by: RileyW --------- Signed-off-by: RileyW Co-authored-by: RileyW --- CMakeLists.txt | 58 +- dependencies/cmake/grpc/CMakeLists.txt | 4 +- dependencies/cmake/libcgroup/CMakeLists.txt | 8 +- dependencies/cmake/mimalloc/CMakeLists.txt | 20 +- protos/Crane.proto | 128 +- protos/PublicDefs.proto | 111 +- src/CraneCtld/AccountManager.cpp | 3180 ++++++++++--------- src/CraneCtld/AccountManager.h | 323 +- src/CraneCtld/CtldGrpcServer.cpp | 779 ++--- src/CraneCtld/CtldGrpcServer.h | 69 +- src/CraneCtld/CtldPreCompiledHeader.h | 1 + src/CraneCtld/TaskScheduler.cpp | 8 +- 12 files changed, 2502 insertions(+), 2187 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0338c2a16..9a981c68e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,53 +37,53 @@ else () ) endif () -if(VERSION_CONTENT STREQUAL "") +if (VERSION_CONTENT STREQUAL "") # Fallback version set(VERSION_CONTENT "Unknown") set(CMAKE_PROJECT_VERSION "0.0.0") -endif() +endif () project(Crane VERSION ${CMAKE_PROJECT_VERSION} LANGUAGES C CXX) -# check and set compiler +# check and set compiler set(REQUIRED_GNU_VERSION 13.0.0) set(REQUIRED_CLANG_VERSION 19.0.0) set(REQUIRED_BPF_CLANG_VERSION 17.0.0) set(CLANG_VERSION "0.0.0") -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL REQUIRED_GNU_VERSION) - if(CRANE_ENABLE_CGROUP_V2) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL REQUIRED_GNU_VERSION) + if (CRANE_ENABLE_CGROUP_V2) message(STATUS "Enabling Cgroup V2 will build cgroup_dev_bpf_object, which requires Clang ${REQUIRED_BPF_CLANG_VERSION}+. Use GNU ${CMAKE_CXX_COMPILER_VERSION} for other modules. ") find_program(CLANG_EXECUTABLE NAMES clang) - if(CLANG_EXECUTABLE) + if (CLANG_EXECUTABLE) execute_process( - COMMAND ${CLANG_EXECUTABLE} --version - OUTPUT_VARIABLE CLANG_VERSION_OUTPUT - OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND ${CLANG_EXECUTABLE} --version + OUTPUT_VARIABLE CLANG_VERSION_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE ) string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" CLANG_VERSION ${CLANG_VERSION_OUTPUT}) - if(CLANG_VERSION VERSION_GREATER_EQUAL REQUIRED_BPF_CLANG_VERSION) + if (CLANG_VERSION VERSION_GREATER_EQUAL REQUIRED_BPF_CLANG_VERSION) set(ENABLE_BPF ON) message(STATUS "Found Clang at ${CLANG_EXECUTABLE} with version ${CLANG_VERSION}; using this version for the cgroup_dev_bpf_object module.") - else() + else () message(FATAL_ERROR "Clang found at ${CLANG_EXECUTABLE} is version ${CLANG_VERSION}, but version ${REQUIRED_BPF_CLANG_VERSION} or higher is required for device management on Cgroup V2. You may use Cgroup V1 instead.") - endif() - else() + endif () + else () message(FATAL_ERROR "Clang ${REQUIRED_BPF_CLANG_VERSION} or higher is required for device management on Cgroup V2. You can use Cgroup V1.") - endif() - endif() -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL REQUIRED_CLANG_VERSION) - if(CRANE_ENABLE_CGROUP_V2) + endif () + endif () +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL REQUIRED_CLANG_VERSION) + if (CRANE_ENABLE_CGROUP_V2) set(ENABLE_BPF ON) - endif() + endif () message(STATUS "Using Clang for all module.") - else() + else () message(FATAL_ERROR "Clang ${REQUIRED_CLANG_VERSION} or higher is required.") - endif() -else() + endif () +else () message(FATAL_ERROR "Neither GNU ${REQUIRED_GNU_VERSION}+ nor Clang ${REQUIRED_CLANG_VERSION}+ found. Stop compiling crane") -endif() +endif () # Options start here ---------------------------------------------------------------------------- @@ -115,9 +115,7 @@ option(CRANE_MIN_LOG_LEVEL "Set the minimal log level (INFO/DEBUG/TRACE)" OFF) option(CRANE_USE_MIMALLOC "Override malloc using mimalloc" OFF) # Options end here ------------------------------------------------------------------------------- - - -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(CRANE_ENABLE_TESTS ON) @@ -173,8 +171,8 @@ endif () # 1. devtoolset-11-libasan-devel.x86_64 # 2. devtoolset-11-libtsan-devel.x86_64 if (CRANE_ADDRESS_SANITIZER AND CRANE_THREAD_SANITIZER) - message(FATAL_ERROR "CRANE_ADDRESS_SANITIZER and CRANE_THREAD_SANITIZER cannot be enabled at the same time.") -endif() + message(FATAL_ERROR "CRANE_ADDRESS_SANITIZER and CRANE_THREAD_SANITIZER cannot be enabled at the same time.") +endif () if (${CRANE_ADDRESS_SANITIZER}) message(STATUS "address_sanitizer is enabled") @@ -340,10 +338,10 @@ set(_PROTOBUF_PROTOC $) set(_GRPC_GRPCPP grpc++) set(_GRPC_CPP_PLUGIN_EXECUTABLE $) -if(ENABLE_BPF) +if (ENABLE_BPF) find_package(PkgConfig REQUIRED) pkg_check_modules(libbpf REQUIRED IMPORTED_TARGET libbpf>=1.4.6) -endif() +endif () # @formatter:off add_definitions(-DCRANE_BUILD_DIRECTORY=\("${CMAKE_BINARY_DIR}"\)) # @formatter:on diff --git a/dependencies/cmake/grpc/CMakeLists.txt b/dependencies/cmake/grpc/CMakeLists.txt index 169fc8ad0..48415d51a 100644 --- a/dependencies/cmake/grpc/CMakeLists.txt +++ b/dependencies/cmake/grpc/CMakeLists.txt @@ -11,7 +11,7 @@ else () set(C_ARES_SRC_URL "https://github.com/c-ares/c-ares/releases/download/cares-1_18_1/c-ares-1.18.1.tar.gz") set(PROTOBUF_SRC_URL "https://github.com/protocolbuffers/protobuf/releases/download/v27.2/protobuf-27.2.tar.gz") set(RE2_SRC_URL "https://github.com/google/re2/archive/refs/tags/2022-06-01.tar.gz") - set(GRPC_SRC_URL "https://github.com/grpc/grpc/archive/refs/tags/v1.65.2.tar.gz") + set(GRPC_SRC_URL "https://github.com/grpc/grpc/archive/refs/tags/v1.67.0.tar.gz") endif () set(ABSL_PROPAGATE_CXX_STD ON) @@ -110,7 +110,7 @@ FetchContent_Declare(grpc OVERRIDE_FIND_PACKAGE URL ${GRPC_SRC_URL} - URL_HASH SHA256=0ff2e0a6abf195cf23b4ce808570bcbb2ff4b5bee453af0b45afd496e661f2c0 + URL_HASH SHA256=af0638f73e4452e22e295f8b3f452518234254104713a08497f3d3aaa76733ad INACTIVITY_TIMEOUT 5 ) FetchContent_MakeAvailable(grpc) diff --git a/dependencies/cmake/libcgroup/CMakeLists.txt b/dependencies/cmake/libcgroup/CMakeLists.txt index ac7f7af17..98524db9d 100644 --- a/dependencies/cmake/libcgroup/CMakeLists.txt +++ b/dependencies/cmake/libcgroup/CMakeLists.txt @@ -6,12 +6,12 @@ if (${CRANE_USE_SYSTEM_LIBCGROUP}) if (libcgroup_FOUND) message(STATUS "Found libcgroup ${libcgroup_VERSION} in system using pkg-config.") - if(CRANE_ENABLE_CGROUP_V2 AND libcgroup_VERSION VERSION_LESS "3.1.0") + if (CRANE_ENABLE_CGROUP_V2 AND libcgroup_VERSION VERSION_LESS "3.1.0") message(FATAL_ERROR "libcgroup version must be higher than 3.1.0 when CRANE_ENABLE_CGROUP_V2 is set.") - endif() - else() + endif () + else () message(FATAL_ERROR "libcgroup in system is not found using pkg-config.") - endif() + endif () add_library(libcgroup INTERFACE) target_link_libraries(libcgroup INTERFACE PkgConfig::libcgroup) diff --git a/dependencies/cmake/mimalloc/CMakeLists.txt b/dependencies/cmake/mimalloc/CMakeLists.txt index 8ab86e2c5..e888e2a66 100644 --- a/dependencies/cmake/mimalloc/CMakeLists.txt +++ b/dependencies/cmake/mimalloc/CMakeLists.txt @@ -1,9 +1,9 @@ include(FetchContent) FetchContent_Declare( - mimalloc - GIT_REPOSITORY https://github.com/microsoft/mimalloc.git - GIT_TAG v2.1.7 - GIT_SHALLOW TRUE + mimalloc + GIT_REPOSITORY https://github.com/microsoft/mimalloc.git + GIT_TAG v2.1.7 + GIT_SHALLOW TRUE ) # If MI_OVERRIDE is ON then operator new and delete will be override not only malloc and free # ref: https://github.com/microsoft/mimalloc/issues/535 @@ -13,19 +13,19 @@ if (BUILD_SHARED_LIBS) set(MI_BUILD_SHARED ON) else () set(MI_BUILD_SHARED OFF) -endif() +endif () -if(CRANE_ADDRESS_SANITIZER) +if (CRANE_ADDRESS_SANITIZER) set(MI_TRACK_ASAN ON) message(STATUS "Set ASAN enable in mimalloc") -endif() +endif () FetchContent_MakeAvailable(mimalloc) if (BUILD_SHARED_LIBS) - add_library(dev_mimalloc ALIAS mimalloc) + add_library(dev_mimalloc ALIAS mimalloc) else () # When building with static library, make sure that link it as the first object file. # ref: https://github.com/microsoft/mimalloc?tab=readme-ov-file#static-override - add_library(dev_mimalloc ALIAS mimalloc-static) -endif() + add_library(dev_mimalloc ALIAS mimalloc-static) +endif () diff --git a/protos/Crane.proto b/protos/Crane.proto index b693630d2..edf0b87c3 100644 --- a/protos/Crane.proto +++ b/protos/Crane.proto @@ -242,7 +242,7 @@ message AddAccountRequest { message AddAccountReply { bool ok = 1; - string reason = 2; + ErrCode reason = 2; } message AddUserRequest { @@ -252,7 +252,7 @@ message AddUserRequest { message AddUserReply { bool ok = 1; - string reason = 2; + ErrCode reason = 2; } message AddQosRequest { @@ -262,57 +262,113 @@ message AddQosRequest { message AddQosReply { bool ok = 1; - string reason = 2; + ErrCode reason = 2; } -message DeleteEntityRequest { +message DeleteAccountRequest { uint32 uid = 1; - EntityType entity_type = 2; - string name = 3; - string account = 4; + string name = 2; } -message DeleteEntityReply { +message DeleteAccountReply { bool ok = 1; - string reason = 2; + ErrCode reason = 2; } -message ModifyEntityRequest { +message DeleteUserRequest { uint32 uid = 1; + string name = 2; + string account = 3; +} - enum OperatorType { - Overwrite = 0; - Add = 1; - Delete = 2; - } +message DeleteUserReply { + bool ok = 1; + ErrCode reason = 2; +} + +message DeleteQosRequest { + uint32 uid = 1; + string name = 2; +} + +message DeleteQosReply { + bool ok = 1; + ErrCode reason = 2; +} + +message ModifyAccountRequest { + uint32 uid = 1; + ModifyField modify_field = 2; //modify item field + string value = 3; //new value + string name = 4; + OperationType type = 5; + bool force = 6; +} + +message ModifyAccountReply { + bool ok = 1; + ErrCode reason = 2; +} - string item = 2; //modify item field +message ModifyUserRequest { + uint32 uid = 1; + ModifyField modify_field = 2; //modify item field string value = 3; //new value string name = 4; string partition = 5; - OperatorType type = 6; - EntityType entity_type = 7; + OperationType type = 6; string account = 8; bool force = 9; } -message ModifyEntityReply { +message ModifyUserReply { bool ok = 1; - string reason = 2; + ErrCode reason = 2; } -message QueryEntityInfoRequest { +message ModifyQosRequest { uint32 uid = 1; - EntityType entity_type = 2; - string name = 3; - string account = 4; + ModifyField modify_field = 2; //modify item field + string value = 3; //new value + string name = 4; } -message QueryEntityInfoReply { +message ModifyQosReply { bool ok = 1; - string reason = 2; - repeated UserInfo user_list = 3; - repeated AccountInfo account_list = 4; + ErrCode reason = 2; +} + +message QueryAccountInfoRequest { + uint32 uid = 1; + string name = 2; +} + +message QueryAccountInfoReply { + bool ok = 1; + ErrCode reason = 2; + repeated AccountInfo account_list = 3; +} + +message QueryUserInfoRequest { + uint32 uid = 1; + string name = 2; + string account = 3; +} + +message QueryUserInfoReply { + bool ok = 1; + ErrCode reason = 2; + repeated UserInfo user_list = 3; +} + +message QueryQosInfoRequest { + uint32 uid = 1; + string name = 2; +} + +message QueryQosInfoReply { + bool ok = 1; + ErrCode reason = 2; repeated QosInfo qos_list = 5; } @@ -326,7 +382,7 @@ message BlockAccountOrUserRequest { message BlockAccountOrUserReply { bool ok = 1; - string reason = 2; + ErrCode reason = 2; } message MigrateSshProcToCgroupRequest { @@ -719,10 +775,18 @@ service CraneCtld { rpc AddUser(AddUserRequest) returns (AddUserReply); rpc AddQos(AddQosRequest) returns (AddQosReply); - rpc DeleteEntity(DeleteEntityRequest) returns (DeleteEntityReply); + rpc DeleteAccount(DeleteAccountRequest) returns (DeleteAccountReply); + rpc DeleteUser(DeleteUserRequest) returns (DeleteUserReply); + rpc DeleteQos(DeleteQosRequest) returns (DeleteQosReply); + + rpc QueryAccountInfo(QueryAccountInfoRequest) returns (QueryAccountInfoReply); + rpc QueryUserInfo(QueryUserInfoRequest) returns (QueryUserInfoReply); + rpc QueryQosInfo(QueryQosInfoRequest) returns (QueryQosInfoReply); - rpc QueryEntityInfo(QueryEntityInfoRequest) returns (QueryEntityInfoReply); - rpc ModifyEntity(ModifyEntityRequest) returns (ModifyEntityReply); + rpc ModifyAccount(ModifyAccountRequest) returns (ModifyAccountReply); + rpc ModifyUser(ModifyUserRequest) returns (ModifyUserReply); + rpc ModifyQos(ModifyQosRequest) returns (ModifyQosReply); + rpc BlockAccountOrUser(BlockAccountOrUserRequest) returns (BlockAccountOrUserReply); /* RPCs called from cinfo */ diff --git a/protos/PublicDefs.proto b/protos/PublicDefs.proto index 3e3e8be27..8c328d860 100644 --- a/protos/PublicDefs.proto +++ b/protos/PublicDefs.proto @@ -38,25 +38,25 @@ message AllocatableResource { uint64 memory_sw_limit_bytes = 3; } -message TypeCountMap{ - map type_count_map = 1; +message TypeCountMap { + map type_count_map = 1; uint64 total = 2; } -message DeviceMap{ +message DeviceMap { map name_type_map = 1; } -message Slots{ +message Slots { repeated string slots = 1; } -message DeviceTypeSlotsMap{ +message DeviceTypeSlotsMap { map type_slots_map = 1; } -message DedicatedResourceInNode{ - map name_type_map = 1; +message DedicatedResourceInNode { + map name_type_map = 1; } message ResourceInNode { @@ -65,7 +65,7 @@ message ResourceInNode { } message ResourceV2 { - map each_node_res = 1; + map each_node_res = 1; } message ResourceView { @@ -218,7 +218,7 @@ message BatchTaskAdditionalMeta { string error_file_pattern = 4; } -message InteractiveTaskAdditionalMeta{ +message InteractiveTaskAdditionalMeta { string cfored_name = 1; string sh_script = 2; string term_env = 3; @@ -316,10 +316,95 @@ message TrimmedPartitionInfo { repeated TrimmedCranedInfo craned_lists = 3; } +enum ErrCode { + SUCCESS = 0; // Success + + ERR_INVALID_UID = 10001; + ERR_INVALID_OP_USER = 10002; + ERR_INVALID_USER = 10003; + ERR_PERMISSION_USER = 10004; + ERR_USER_DUPLICATE_ACCOUNT = 10005; + ERR_USER_ALLOWED_ACCOUNT = 10006; + ERR_INVALID_ADMIN_LEVEL = 10007; + ERR_USER_ACCOUNT_MISMATCH = 10008; + ERR_NO_ACCOUNT_SPECIFIED = 10009; + + ERR_INVALID_ACCOUNT = 10010; + ERR_DUPLICATE_ACCOUNT = 10011; + ERR_INVALID_PARENTACCOUNT = 10012; + ERR_DELETE_ACCOUNT = 10013; + + ERR_INVALID_PARTITION = 10014; + ERR_ALLOWED_PARTITION = 10015; + ERR_DUPLICATE_PARTITION = 10016; + ERR_PARENT_ALLOWED_PARTITION = 10017; + ERR_USER_EMPTY_PARTITION = 10018; + ERR_CHILD_HAS_PARTITION = 10019; + + ERR_INVALID_QOS = 10020; + ERR_DB_DUPLICATE_QOS = 10021; + ERR_DELETE_QOS = 10022; + ERR_CONVERT_TO_INTERGER = 10023; + ERR_TIME_LIMIT = 10024; + ERR_ALLOWED_QOS = 10025; + ERR_DUPLICATE_QOS = 10026; + ERR_PARENT_ALLOWED_QOS = 10027; + ERR_SET_ALLOWED_QOS = 10028; + ERR_ALLOWED_DEFAULT_QOS = 10029; + ERR_DUPLICATE_DEFAULT_QOS = 10030; + ERR_CHILD_HAS_DEFAULT_QOS = 10031; + ERR_SET_ACCOUNT_QOS = 10032; + ERR_SET_DEFAULT_QOS = 10033; + ERR_IS_DEFAULT_QOS = 10034; + + ERR_UPDATE_DATABASE = 10035; + + ERR_GENERIC_FAILURE = 10100; + ERR_NO_RESOURCE = 10101; + ERR_NON_EXISTENT = 10102; + ERR_INVALID_NODE_NUM = 10103; + ERR_SYSTEM_ERR = 10104; + ERR_EXISTING_TASK = 10105; + ERR_INVALID_PARAM = 10106; + ERR_STOP = 10107; + ERR_PERMISSION_DENIED = 10108; + ERR_CONNECTION_TIMEOUT = 10109; + ERR_CONNECTION_ABORTED = 10110; + ERR_RPC_FAILURE = 10111; + ERR_TOKEN_REQUEST_FAILURE = 10112; + ERR_STREAM_BROKEN = 10113; + ERR_INVALID_STUB = 10114; + ERR_CGROUP = 10115; + ERR_PROTOBUF = 10116; + ERR_LIB_EVENT = 10117; + ERR_NO_AVAIL_NODE = 10118; +} + enum EntityType { Account = 0; User = 1; - Qos = 2; +} + +enum OperationType { + Overwrite = 0; + Add = 1; + Delete = 2; +} + +enum ModifyField { + // user and account + Partition = 0; + Qos = 1; + DefaultQos = 2; + // account and qos + Description = 3; + // user + AdminLevel = 4; + // qos + Priority = 5; + MaxJobsPerUser = 6; + MaxCpusPerUser = 7; + MaxTimeLimitPerTask = 8; } message AccountInfo { @@ -345,13 +430,13 @@ message AccountInfo { // The c++ code and database representation use a Map to contain // in ONE UserInfo message all the information belonging to different accounts. message UserInfo { - enum AdminLevel{ + enum AdminLevel { None = 0; Operator = 1; Admin = 2; Root = 3; }; - message AllowedPartitionQos{ + message AllowedPartitionQos { string partition_name = 1; repeated string qos_list = 2; string default_qos = 3; @@ -392,4 +477,4 @@ message CranedRemoteMeta { string craned_version = 3; google.protobuf.Timestamp craned_start_time = 4; google.protobuf.Timestamp system_boot_time = 5; -} \ No newline at end of file +} diff --git a/src/CraneCtld/AccountManager.cpp b/src/CraneCtld/AccountManager.cpp index 5a1f9612d..d2e466ce6 100644 --- a/src/CraneCtld/AccountManager.cpp +++ b/src/CraneCtld/AccountManager.cpp @@ -18,459 +18,225 @@ #include "AccountManager.h" +#include + +#include "CtldPublicDefs.h" #include "crane/PasswordEntry.h" +#include "protos/PublicDefs.pb.h" +#include "range/v3/algorithm/contains.hpp" namespace Ctld { AccountManager::AccountManager() { InitDataMap_(); } -AccountManager::Result AccountManager::AddUser(User&& new_user) { - // When you add a new user, you can only associate it with the default account - if (new_user.account_to_attrs_map.size() != 1 || - new_user.account_to_attrs_map.begin()->first != - new_user.default_account) { - CRANE_ERROR("The added user does not comply with system rules"); - return Result{false, "Crane system error"}; - } +AccountManager::CraneExpected AccountManager::AddUser( + uint32_t uid, const User& new_user) { + CraneExpected result; util::write_lock_guard user_guard(m_rw_user_mutex_); util::write_lock_guard account_guard(m_rw_account_mutex_); - if (new_user.default_account.empty()) { - // User must specify an account - return Result{false, fmt::format("Please specify the user's account")}; - } + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); - bool add_coordinator = false; - if (!new_user.coordinator_accounts.empty()) { - add_coordinator = true; - } + result = CheckIfUserHasHigherPrivThan_(*op_user, new_user.admin_level); + if (!result) return result; + + // User must specify an account + if (new_user.default_account.empty()) + return std::unexpected(CraneErrCode::ERR_NO_ACCOUNT_SPECIFIED); - std::string object_account = new_user.default_account; - const std::string name = new_user.name; + const std::string& object_account = new_user.default_account; + const std::string& name = new_user.name; // Avoid duplicate insertion - const User* find_user = GetUserInfoNoLock_(name); - User res_user; - if (find_user && !find_user->deleted) { - if (find_user->account_to_attrs_map.contains(object_account)) { - return Result{false, - fmt::format("The user '{}' already have account '{}'", name, - object_account)}; - } else { - // Add account to user's map - res_user = *find_user; - res_user.account_to_attrs_map[object_account] = - new_user.account_to_attrs_map[object_account]; - if (add_coordinator) { - res_user.coordinator_accounts.push_back(object_account); - } - } - } else { - res_user = std::move(new_user); + const User* stale_user = GetUserInfoNoLock_(name); + if (stale_user && !stale_user->deleted) { + if (stale_user->account_to_attrs_map.contains(object_account)) + return std::unexpected(CraneErrCode::ERR_USER_DUPLICATE_ACCOUNT); } // Check whether the account exists - const Account* find_account = GetExistedAccountInfoNoLock_(object_account); - if (!find_account) { - return Result{false, fmt::format("unknown account '{}'", object_account)}; - } + const Account* account = GetExistedAccountInfoNoLock_(object_account); + if (!account) return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); - const std::list& parent_allowed_partition = - find_account->allowed_partition; - if (!res_user.account_to_attrs_map[object_account] - .allowed_partition_qos_map.empty()) { - // Check if user's allowed partition is a subset of parent's allowed - // partition - for (auto&& [partition, qos] : res_user.account_to_attrs_map[object_account] - .allowed_partition_qos_map) { - if (std::find(parent_allowed_partition.begin(), - parent_allowed_partition.end(), - partition) != parent_allowed_partition.end()) { - qos.first = find_account->default_qos; - qos.second = find_account->allowed_qos_list; - } else { - return Result{ - false, fmt::format("Partition '{}' is not allowed in account '{}'", - partition, find_account->name)}; - } - } - } else { - // Inherit - for (const auto& partition : parent_allowed_partition) { - res_user.account_to_attrs_map[object_account] - .allowed_partition_qos_map[partition] = - std::pair>{ - find_account->default_qos, - std::list{find_account->allowed_qos_list}}; - } + // Check if user's allowed partition is a subset of parent's allowed + // partition + for (const auto& [partition, qos] : + new_user.account_to_attrs_map.at(object_account) + .allowed_partition_qos_map) { + result = CheckPartitionIsAllowedNoLock_(account, partition, false, true); + if (!result) return result; } - res_user.account_to_attrs_map[object_account].blocked = false; - - mongocxx::client_session::with_transaction_cb callback = - [&](mongocxx::client_session* session) { - // Update the user's account - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$addToSet", object_account, "users", - name); - if (add_coordinator) { - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$addToSet", object_account, - "coordinators", name); - } - - if (find_user) { - // There is a same user but was deleted or user would like to add user - // to a new account,here will overwrite it with the same name - g_db_client->UpdateUser(res_user); - g_db_client->UpdateEntityOne(MongodbClient::EntityType::USER, "$set", - name, "creation_time", - ToUnixSeconds(absl::Now())); - } else { - // Insert the new user - g_db_client->InsertUser(res_user); - } - }; - if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; - } + return AddUser_(new_user, account, stale_user); +} - m_account_map_[object_account]->users.emplace_back(name); - if (add_coordinator) { - m_account_map_[object_account]->coordinators.emplace_back(name); +AccountManager::CraneExpected AccountManager::AddAccount( + uint32_t uid, const Account& new_account) { + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + util::read_lock_guard account_guard(m_rw_account_mutex_); + CraneExpected result; + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + // When creating an account without a parent, the permission level of + // op_user must be Operator or higher. + if (new_account.parent_account.empty()) + result = CheckIfUserHasHigherPrivThan_(*op_user, User::None); + else + result = CheckIfUserHasPermOnAccountNoLock_( + *op_user, new_account.parent_account, false); + if (!result) return result; } - m_user_map_[name] = std::make_unique(std::move(res_user)); - return Result{true}; -} - -AccountManager::Result AccountManager::AddAccount(Account&& new_account) { util::write_lock_guard account_guard(m_rw_account_mutex_); util::write_lock_guard qos_guard(m_rw_qos_mutex_); - const std::string name = new_account.name; - + const std::string& name = new_account.name; // Avoid duplicate insertion - const Account* find_account = GetAccountInfoNoLock_(name); - if (find_account && !find_account->deleted) { - return Result{ - false, - fmt::format("The account '{}' already exists in the database", name)}; - } - - for (const auto& qos : new_account.allowed_qos_list) { - const Qos* find_qos = GetExistedQosInfoNoLock_(qos); - if (!find_qos) { - return Result{false, fmt::format("Qos '{}' does not exist", qos)}; - } - } + const Account* stale_account = GetAccountInfoNoLock_(name); + if (stale_account && !stale_account->deleted) + return std::unexpected(CraneErrCode::ERR_DUPLICATE_ACCOUNT); + const Account* find_parent = nullptr; if (!new_account.parent_account.empty()) { // Check whether the account's parent account exists - const Account* find_parent = - GetExistedAccountInfoNoLock_(new_account.parent_account); - if (!find_parent) { - return Result{ - false, - fmt::format("The parent account '{}' doesn't exist in the database", - new_account.parent_account)}; - } + find_parent = GetExistedAccountInfoNoLock_(new_account.parent_account); + if (!find_parent) + return std::unexpected(CraneErrCode::ERR_INVALID_PARENTACCOUNT); - if (new_account.allowed_partition.empty()) { - // Inherit - new_account.allowed_partition = - std::list{find_parent->allowed_partition}; - } else { - // check allowed partition authority - for (const auto& par : new_account.allowed_partition) { - if (std::find(find_parent->allowed_partition.begin(), - find_parent->allowed_partition.end(), par) == - find_parent->allowed_partition.end()) { // not find - return Result{ - false, - fmt::format( - "Parent account '{}' does not have access to partition '{}'", - new_account.parent_account, par)}; - } - } + // check allowed partition authority + for (const auto& par : new_account.allowed_partition) { + if (!ranges::contains(find_parent->allowed_partition, par)) // not find + return std::unexpected(CraneErrCode::ERR_PARENT_ALLOWED_PARTITION); } - if (new_account.allowed_qos_list.empty()) { - // Inherit - new_account.allowed_qos_list = - std::list{find_parent->allowed_qos_list}; - } else { - // check allowed qos list authority - for (const auto& qos : new_account.allowed_qos_list) { - if (std::find(find_parent->allowed_qos_list.begin(), - find_parent->allowed_qos_list.end(), - qos) == - find_parent->allowed_qos_list.end()) { // not find - return Result{ - false, fmt::format( - "Parent account '{}' does not have access to qos '{}'", - new_account.parent_account, qos)}; - } - } + // check allowed qos list authority + for (const auto& qos : new_account.allowed_qos_list) { + if (!ranges::contains(find_parent->allowed_qos_list, qos)) // not find + return std::unexpected(CraneErrCode::ERR_PARENT_ALLOWED_QOS); } } else { // No parent account // Check whether partitions exists for (const auto& p : new_account.allowed_partition) { if (!g_config.Partitions.contains(p)) { - return Result{false, fmt::format("Partition '{}' does not exist", p)}; + return std::unexpected(CraneErrCode::ERR_INVALID_PARTITION); } } - } - if (new_account.default_qos.empty()) { - if (!new_account.allowed_qos_list.empty()) - new_account.default_qos = new_account.allowed_qos_list.front(); - } else { - if (std::find(new_account.allowed_qos_list.begin(), - new_account.allowed_qos_list.end(), - new_account.default_qos) == - new_account.allowed_qos_list.end()) - return Result{ - false, - fmt::format("default qos '{}' not included in allowed qos list", - new_account.default_qos)}; + for (const auto& qos : new_account.allowed_qos_list) { + const Qos* find_qos = GetExistedQosInfoNoLock_(qos); + if (!find_qos) return std::unexpected(CraneErrCode::ERR_INVALID_QOS); + } } - mongocxx::client_session::with_transaction_cb callback = - [&](mongocxx::client_session* session) { - if (!new_account.parent_account.empty()) { - // update the parent account's child_account_list - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$addToSet", new_account.parent_account, - "child_accounts", name); - } - - if (find_account) { - // There is a same account but was deleted,here will delete the - // original account and overwrite it with the same name - g_db_client->UpdateAccount(new_account); - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$set", name, "creation_time", - ToUnixSeconds(absl::Now())); - } else { - // Insert the new account - g_db_client->InsertAccount(new_account); - } - for (const auto& qos : new_account.allowed_qos_list) { - IncQosReferenceCountInDb_(qos, 1); - } - }; - - if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; - } - if (!new_account.parent_account.empty()) { - m_account_map_[new_account.parent_account]->child_accounts.emplace_back( - name); - } - for (const auto& qos : new_account.allowed_qos_list) { - m_qos_map_[qos]->reference_count++; + // Check if default_qos is in allowed_qos_list. + if (!new_account.default_qos.empty()) { + if (!ranges::contains(new_account.allowed_qos_list, + new_account.default_qos)) + return std::unexpected(CraneErrCode::ERR_ALLOWED_DEFAULT_QOS); } - m_account_map_[name] = std::make_unique(std::move(new_account)); - return Result{true}; + return AddAccount_(new_account, find_parent, stale_account); } -AccountManager::Result AccountManager::AddQos(const Qos& new_qos) { - util::write_lock_guard qos_guard(m_rw_qos_mutex_); +AccountManager::CraneExpected AccountManager::AddQos(uint32_t uid, + const Qos& new_qos) { + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); - const Qos* find_qos = GetQosInfoNoLock_(new_qos.name); - if (find_qos && !find_qos->deleted) { - return Result{false, fmt::format("Qos '{}' already exists in the database", - new_qos.name)}; + auto result = CheckIfUserHasHigherPrivThan_(*op_user, User::None); + if (!result) return result; } - if (find_qos) { - // There is a same qos but was deleted,here will delete the original - // qos and overwrite it with the same name - mongocxx::client_session::with_transaction_cb callback = - [&](mongocxx::client_session* session) { - g_db_client->UpdateQos(new_qos); - g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", - new_qos.name, "creation_time", - ToUnixSeconds(absl::Now())); - }; + util::write_lock_guard qos_guard(m_rw_qos_mutex_); - if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Can't update the deleted qos to database"}; - } - } else { - // Insert the new qos - if (!g_db_client->InsertQos(new_qos)) { - return Result{false, "Can't insert the new qos to database"}; - } - } + const Qos* find_qos = GetQosInfoNoLock_(new_qos.name); - m_qos_map_[new_qos.name] = std::make_unique(new_qos); + // Avoid duplicate insertion + if (find_qos && !find_qos->deleted) + return std::unexpected(CraneErrCode::ERR_DB_DUPLICATE_QOS); - return Result{true}; + return AddQos_(new_qos, find_qos); } -AccountManager::Result AccountManager::DeleteUser(const std::string& name, - const std::string& account) { - if (!account.empty()) { - return RemoveUserFromAccount(name, account); - } - +AccountManager::CraneExpected AccountManager::DeleteUser( + uint32_t uid, const std::string& name, const std::string& account) { util::write_lock_guard user_guard(m_rw_user_mutex_); util::write_lock_guard account_guard(m_rw_account_mutex_); - const User* user = GetExistedUserInfoNoLock_(name); - if (!user) { - return Result{false, - fmt::format("User '{}' doesn't exist in the database", name)}; - } - - mongocxx::client_session::with_transaction_cb callback = - [&](mongocxx::client_session* session) { - // delete form the parent accounts' users list - for (const auto& kv : user->account_to_attrs_map) { - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$pull", kv.first, - /*account name*/ "users", name); - } - for (const std::string& coordinatorAccount : - user->coordinator_accounts) { - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$pull", coordinatorAccount, - /*account name*/ "coordinators", name); - } + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); - // Delete the user - g_db_client->UpdateEntityOne(MongodbClient::EntityType::USER, "$set", - name, "deleted", true); - }; + const User* user = GetExistedUserInfoNoLock_(name); + if (!user) return std::unexpected(CraneErrCode::ERR_INVALID_USER); - if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; - } + auto result = CheckIfUserHasHigherPrivThan_(*op_user, user->admin_level); + if (!result) return result; - for (const auto& kv : user->account_to_attrs_map) { - m_account_map_[kv.first]->users.remove(name); - m_account_map_[kv.first]->coordinators.remove( - name); // No inspection required - } - m_user_map_[name]->deleted = true; + // The provided account is invalid. + if (!account.empty() && !user->account_to_attrs_map.contains(account)) + return std::unexpected(CraneErrCode::ERR_USER_ACCOUNT_MISMATCH); - return Result{true}; + return DeleteUser_(*user, account); } -AccountManager::Result AccountManager::RemoveUserFromAccount( - const std::string& name, const std::string& account) { - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::write_lock_guard account_guard(m_rw_account_mutex_); - - const User* user = GetExistedUserInfoNoLock_(name); - if (!user) { - return Result{false, fmt::format("Unknown user '{}'", name)}; - } - if (!user->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; - } - - mongocxx::client_session::with_transaction_cb callback = - [&](mongocxx::client_session* session) { - // delete form the parent accounts' users list - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$pull", account, /*account name*/ "users", - name); - if (std::find(user->coordinator_accounts.begin(), - user->coordinator_accounts.end(), - account) != user->coordinator_accounts.end()) { - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$pull", account, - /*account name*/ "coordinators", name); - } +AccountManager::CraneExpected AccountManager::DeleteAccount( + uint32_t uid, const std::string& name) { + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + util::read_lock_guard account_guard(m_rw_account_mutex_); - // Delete the account from user account_map - g_db_client->UpdateEntityOne(MongodbClient::EntityType::USER, "$unset", - name, "account_to_attrs_map." + account, - std::string("")); - }; + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); - if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; + auto result = CheckIfUserHasPermOnAccountNoLock_(*op_user, name, false); + if (!result) return result; } - m_account_map_[account]->users.remove(name); - m_account_map_[account]->coordinators.remove(name); // No inspection required - m_user_map_[name]->account_to_attrs_map.erase(account); - - return Result{true}; -} - -AccountManager::Result AccountManager::DeleteAccount(const std::string& name) { util::write_lock_guard account_guard(m_rw_account_mutex_); util::write_lock_guard qos_guard(m_rw_qos_mutex_); - const Account* account = GetExistedAccountInfoNoLock_(name); - - if (!account) { - return Result{ - false, fmt::format("Account '{}' doesn't exist in the database", name)}; - } - if (!account->child_accounts.empty() || !account->users.empty()) { - return Result{false, "This account has child account or users"}; - } + const Account* account = GetExistedAccountInfoNoLock_(name); + if (!account) return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); - mongocxx::client_session::with_transaction_cb callback = - [&](mongocxx::client_session* session) { - if (!account->parent_account.empty()) { - // delete form the parent account's child account list - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$pull", account->parent_account, - "child_accounts", name); - } - // Delete the account - g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", - name, "deleted", true); - for (const auto& qos : account->allowed_qos_list) { - IncQosReferenceCountInDb_(qos, -1); - } - }; + // Cannot delete because there are child nodes. + if (!account->child_accounts.empty() || !account->users.empty()) + return std::unexpected(CraneErrCode::ERR_DELETE_ACCOUNT); - if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; - } + return DeleteAccount_(*account); +} - if (!account->parent_account.empty()) { - m_account_map_[account->parent_account]->child_accounts.remove(name); - } - m_account_map_[name]->deleted = true; +AccountManager::CraneExpected AccountManager::DeleteQos( + uint32_t uid, const std::string& name) { + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); - for (const auto& qos : account->allowed_qos_list) { - m_qos_map_[qos]->reference_count--; + auto result = CheckIfUserHasHigherPrivThan_(*op_user, User::None); + if (!result) return result; } - return Result{true}; -} - -AccountManager::Result AccountManager::DeleteQos(const std::string& name) { util::write_lock_guard qos_guard(m_rw_qos_mutex_); - const Qos* qos = GetExistedQosInfoNoLock_(name); - if (!qos) { - return Result{false, fmt::format("Qos '{}' not exists in database", name)}; - } else if (qos->reference_count != 0) { - return Result{false, - fmt::format("There still has {} references to this qos", - qos->reference_count)}; - } + const Qos* qos = GetExistedQosInfoNoLock_(name); + if (!qos) return std::unexpected(CraneErrCode::ERR_INVALID_QOS); - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", - name, "deleted", true)) { - return Result{false, fmt::format("Delete qos '{}' failed", name)}; - } - m_qos_map_[name]->deleted = true; + // Cannot delete because the QoS is still in use. + if (qos->reference_count != 0) + return std::unexpected(CraneErrCode::ERR_DELETE_QOS); - return Result{true}; + return DeleteQos_(name); } AccountManager::UserMutexSharedPtr AccountManager::GetExistedUserInfo( @@ -481,9 +247,9 @@ AccountManager::UserMutexSharedPtr AccountManager::GetExistedUserInfo( if (!user) { m_rw_user_mutex_.unlock_shared(); return UserMutexSharedPtr{nullptr}; - } else { - return UserMutexSharedPtr{user, &m_rw_user_mutex_}; } + + return UserMutexSharedPtr{user, &m_rw_user_mutex_}; } AccountManager::UserMapMutexSharedPtr AccountManager::GetAllUserInfo() { @@ -492,9 +258,9 @@ AccountManager::UserMapMutexSharedPtr AccountManager::GetAllUserInfo() { if (m_user_map_.empty()) { m_rw_user_mutex_.unlock_shared(); return UserMapMutexSharedPtr{nullptr}; - } else { - return UserMapMutexSharedPtr{&m_user_map_, &m_rw_user_mutex_}; } + + return UserMapMutexSharedPtr{&m_user_map_, &m_rw_user_mutex_}; } AccountManager::AccountMutexSharedPtr AccountManager::GetExistedAccountInfo( @@ -505,9 +271,9 @@ AccountManager::AccountMutexSharedPtr AccountManager::GetExistedAccountInfo( if (!account) { m_rw_account_mutex_.unlock_shared(); return AccountMutexSharedPtr{nullptr}; - } else { - return AccountMutexSharedPtr{account, &m_rw_account_mutex_}; } + + return AccountMutexSharedPtr{account, &m_rw_account_mutex_}; } AccountManager::AccountMapMutexSharedPtr AccountManager::GetAllAccountInfo() { @@ -516,9 +282,9 @@ AccountManager::AccountMapMutexSharedPtr AccountManager::GetAllAccountInfo() { if (m_account_map_.empty()) { m_rw_account_mutex_.unlock_shared(); return AccountMapMutexSharedPtr{nullptr}; - } else { - return AccountMapMutexSharedPtr{&m_account_map_, &m_rw_account_mutex_}; } + + return AccountMapMutexSharedPtr{&m_account_map_, &m_rw_account_mutex_}; } AccountManager::QosMutexSharedPtr AccountManager::GetExistedQosInfo( @@ -529,9 +295,9 @@ AccountManager::QosMutexSharedPtr AccountManager::GetExistedQosInfo( if (!qos) { m_rw_qos_mutex_.unlock_shared(); return QosMutexSharedPtr{nullptr}; - } else { - return QosMutexSharedPtr{qos, &m_rw_qos_mutex_}; } + + return QosMutexSharedPtr{qos, &m_rw_qos_mutex_}; } AccountManager::QosMapMutexSharedPtr AccountManager::GetAllQosInfo() { @@ -540,229 +306,617 @@ AccountManager::QosMapMutexSharedPtr AccountManager::GetAllQosInfo() { if (m_qos_map_.empty()) { m_rw_qos_mutex_.unlock_shared(); return QosMapMutexSharedPtr{nullptr}; - } else { - return QosMapMutexSharedPtr{&m_qos_map_, &m_rw_qos_mutex_}; } + + return QosMapMutexSharedPtr{&m_qos_map_, &m_rw_qos_mutex_}; } -AccountManager::Result AccountManager::ModifyUser( - const crane::grpc::ModifyEntityRequest_OperatorType& operatorType, - const std::string& name, const std::string& partition, std::string account, - const std::string& item, const std::string& value, bool force) { - if (account.empty()) { - auto p = GetExistedUserInfo(name); - if (!p) { - return Result{false, fmt::format("Unknown user '{}'", name)}; +AccountManager::CraneExpected AccountManager::QueryUserInfo( + uint32_t uid, const std::string& name, + std::unordered_map* res_user_map) { + util::read_lock_guard user_guard(m_rw_user_mutex_); + CraneExpected result{}; + + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + + if (name.empty()) { // Query all users that can be queried. + // Operators and above can query all users. + if (CheckIfUserHasHigherPrivThan_(*op_user, User::None)) { + // The rules for querying user information are the same as those for + // querying accounts + for (const auto& [user_name, user] : m_user_map_) { + if (user->deleted) continue; + res_user_map->try_emplace(user->uid, *user); + } + } else { + util::read_lock_guard account_guard(m_rw_account_mutex_); + std::queue queue; + for (const auto& [acct, item] : op_user->account_to_attrs_map) { + queue.push(acct); + while (!queue.empty()) { + std::string father = queue.front(); + for (const auto& user : m_account_map_.at(father)->users) { + res_user_map->try_emplace(m_user_map_.at(user)->uid, + *(m_user_map_.at(user))); + } + queue.pop(); + for (const auto& child : m_account_map_.at(father)->child_accounts) { + queue.push(child); + } + } + } } - account = p->default_account; + } else { // Query the specified user information. + util::read_lock_guard account_guard(m_rw_account_mutex_); + const User* user = GetExistedUserInfoNoLock_(name); + result = CheckIfUserHasPermOnUserNoLock_(*op_user, user, true); + if (!result) return result; + res_user_map->try_emplace(user->uid, *user); } - switch (operatorType) { - case crane::grpc::ModifyEntityRequest_OperatorType_Add: - if (item == "allowed_partition") { - return AddUserAllowedPartition_(name, account, value); - } else if (item == "allowed_qos_list") { - return AddUserAllowedQos_(name, value, account, partition); - } else { - return Result{false, fmt::format("Field '{}' can't be added", item)}; - } + return result; +} - case crane::grpc::ModifyEntityRequest_OperatorType_Overwrite: - if (item == "admin_level") { - return SetUserAdminLevel_(name, value); - } else if (item == "default_qos") { - return SetUserDefaultQos_(name, value, account, partition); - } else if (item == "allowed_partition") { - return SetUserAllowedPartition_(name, account, value); - } else if (item == "allowed_qos_list") { - return SetUserAllowedQos_(name, account, partition, value, force); - } else { - return Result{false, fmt::format("Field '{}' can't be set", item)}; +AccountManager::CraneExpected AccountManager::QueryAccountInfo( + uint32_t uid, const std::string& name, + std::unordered_map* res_account_map) { + User res_user; + CraneExpected result{}; + + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + util::read_lock_guard account_guard(m_rw_account_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + if (!name.empty()) { + result = CheckIfUserHasPermOnAccountNoLock_(*op_user, name, true); + if (!result) return result; } + res_user = *op_user; + } - case crane::grpc::ModifyEntityRequest_OperatorType_Delete: - if (item == "allowed_partition") { - return DeleteUserAllowedPartition_(name, account, value); - } else if (item == "allowed_qos_list") { - return DeleteUserAllowedQos_(name, value, account, partition, force); + util::read_lock_guard account_guard(m_rw_account_mutex_); + if (name.empty()) { + if (CheckIfUserHasHigherPrivThan_(res_user, User::None)) { + // If an administrator user queries account information, all + // accounts are returned, variable user_account not used + for (const auto& [name, account] : m_account_map_) { + if (account->deleted) continue; + res_account_map->try_emplace(account->name, *account); + } } else { - return Result{false, fmt::format("Field '{}' can't be deleted", item)}; - } + // Otherwise, only all sub-accounts under your own accounts will be + // returned + std::queue queue; + for (const auto& [acct, item] : res_user.account_to_attrs_map) { + // Z->A->B--->C->E + // |->D |->F + // If we query account C, [Z,A,B,C,E,F] is included. + std::string p_name = m_account_map_.at(acct)->parent_account; + while (!p_name.empty()) { + res_account_map->try_emplace(p_name, *(m_account_map_.at(p_name))); + p_name = m_account_map_.at(p_name)->parent_account; + } - default: - return Result{false, fmt::format("Unknown field '{}'", item)}; + queue.push(acct); + while (!queue.empty()) { + std::string father = queue.front(); + const auto& account_content = m_account_map_.at(father); + res_account_map->try_emplace(account_content->name, + *(account_content)); + queue.pop(); + for (const auto& child : account_content->child_accounts) { + queue.push(child); + } + } + } + } + } else { + const Account* account = GetAccountInfoNoLock_(name); + if (!account) return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); + res_account_map->try_emplace(name, *account); } + + return result; } -AccountManager::Result AccountManager::ModifyAccount( - const crane::grpc::ModifyEntityRequest_OperatorType& operatorType, - const std::string& name, const std::string& item, const std::string& value, - bool force) { - switch (operatorType) { - case crane::grpc::ModifyEntityRequest_OperatorType_Add: - if (item == "allowed_partition") { - return AddAccountAllowedPartition_(name, value); - } else if (item == "allowed_qos_list") { - return AddAccountAllowedQos_(name, value); - } else { - return Result{false, fmt::format("Field '{}' can't be added", item)}; - } +AccountManager::CraneExpected AccountManager::QueryQosInfo( + uint32_t uid, const std::string& name, + std::unordered_map* res_qos_map) { + User res_user; + CraneExpected result{}; + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + res_user = *op_user; + } - case crane::grpc::ModifyEntityRequest_OperatorType_Overwrite: - if (item == "description") { - return SetAccountDescription_(name, value); - } else if (item == "allowed_partition") { - return SetAccountAllowedPartition_(name, value, force); - } else if (item == "allowed_qos_list") { - return SetAccountAllowedQos_(name, value, force); - } else if (item == "default_qos") { - return SetAccountDefaultQos_(name, value); + util::read_lock_guard qos_guard(m_rw_qos_mutex_); + if (name.empty()) { + if (CheckIfUserHasHigherPrivThan_(res_user, User::None)) { + for (const auto& [name, qos] : m_qos_map_) { + if (qos->deleted) continue; + res_qos_map->try_emplace(name, *qos); + } } else { - return Result{false, fmt::format("Field '{}' can't be set", item)}; + for (const auto& [acct, item] : res_user.account_to_attrs_map) { + for (const auto& [part, part_qos_map] : + item.allowed_partition_qos_map) { + for (const auto& qos : part_qos_map.second) { + res_qos_map->try_emplace(qos, *(m_qos_map_.at(qos))); + } + } + } } - - case crane::grpc::ModifyEntityRequest_OperatorType_Delete: - if (item == "allowed_partition") { - return DeleteAccountAllowedPartition_(name, value, force); - } else if (item == "allowed_qos_list") { - return DeleteAccountAllowedQos_(name, value, force); - } else { - return Result{false, fmt::format("Field '{}' can't be deleted", item)}; + } else { + const Qos* qos = GetExistedQosInfoNoLock_(name); + if (!qos) return std::unexpected(CraneErrCode::ERR_INVALID_QOS); + + if (res_user.admin_level < User::Operator) { + bool found = false; + for (const auto& [acct, item] : res_user.account_to_attrs_map) { + for (const auto& [part, part_qos_map] : + item.allowed_partition_qos_map) { + for (const auto& part_qos : part_qos_map.second) { + if (part_qos == name) found = true; + } + } + } + if (!found) return std::unexpected(CraneErrCode::ERR_ALLOWED_QOS); } - - default: - return Result{true}; + res_qos_map->try_emplace(name, *qos); } + + return result; } -AccountManager::Result AccountManager::ModifyQos(const std::string& name, - const std::string& item, - const std::string& value) { - bool value_is_number{false}; - int64_t value_number; - if (item != Qos::FieldStringOfDescription()) { - bool ok = util::ConvertStringToInt64(value, &value_number); - if (!ok) return Result{false, "Failed to convert value to integer"}; +AccountManager::CraneExpected AccountManager::ModifyAdminLevel( + uint32_t uid, const std::string& name, const std::string& value) { + util::write_lock_guard user_guard(m_rw_user_mutex_); - value_is_number = true; + const User* user = GetExistedUserInfoNoLock_(name); + if (!user) return std::unexpected(CraneErrCode::ERR_INVALID_USER); - if (item == Qos::FieldStringOfMaxTimeLimitPerTask() && - !CheckIfTimeLimitSecIsValid(value_number)) - return Result{false, "Invalid time limit value"}; + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + + auto result = CheckIfUserHasHigherPrivThan_(*op_user, user->admin_level); + if (!result) return std::unexpected(CraneErrCode::ERR_PERMISSION_USER); + + User::AdminLevel new_level; + if (value == "none") + new_level = User::None; + else if (value == "operator") + new_level = User::Operator; + else if (value == "admin") + new_level = User::Admin; + else + return std::unexpected(CraneErrCode::ERR_INVALID_ADMIN_LEVEL); + + if (op_user->admin_level <= new_level) + return std::unexpected(CraneErrCode::ERR_PERMISSION_USER); + + if (new_level == user->admin_level) return {}; + + return SetUserAdminLevel_(name, new_level); +} + +AccountManager::CraneExpected AccountManager::ModifyUserDefaultQos( + uint32_t uid, const std::string& name, const std::string& partition, + const std::string& account, const std::string& value) { + util::write_lock_guard user_guard(m_rw_user_mutex_); + + const User* p_target_user = GetExistedUserInfoNoLock_(name); + CraneExpected result{}; + // Account might be empty. In that case, ues user->default_account. + std::string actual_account = account; + { + util::read_lock_guard account_guard(m_rw_account_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + result = CheckIfUserHasPermOnUserOfAccountNoLock_(*op_user, p_target_user, + &actual_account, false); + if (!result) return result; + } + + result = CheckSetUserDefaultQosNoLock_(*p_target_user, actual_account, + partition, value); + if (!result) return result; + + return SetUserDefaultQos_(*p_target_user, actual_account, partition, value); +} + +AccountManager::CraneExpected AccountManager::ModifyUserAllowedPartition( + crane::grpc::OperationType operation_type, uint32_t uid, + const std::string& name, const std::string& account, + const std::string& value) { + util::write_lock_guard user_guard(m_rw_user_mutex_); + util::read_lock_guard account_guard(m_rw_account_mutex_); + + const User* p = GetExistedUserInfoNoLock_(name); + CraneExpected result{}; + std::string actual_account = account; + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + result = CheckIfUserHasPermOnUserOfAccountNoLock_(*op_user, p, + &actual_account, false); + if (!result) return result; + + const Account* account_ptr = GetExistedAccountInfoNoLock_(actual_account); + + switch (operation_type) { + case crane::grpc::OperationType::Add: + result = CheckAddUserAllowedPartitionNoLock_(p, account_ptr, value); + return !result ? result : AddUserAllowedPartition_(*p, *account_ptr, value); + case crane::grpc::OperationType::Overwrite: + result = CheckSetUserAllowedPartitionNoLock_(account_ptr, value); + return !result ? result : SetUserAllowedPartition_(*p, *account_ptr, value); + default: + std::unreachable(); + } + + return result; +} + +AccountManager::CraneExpected AccountManager::ModifyUserAllowedQos( + crane::grpc::OperationType operation_type, uint32_t uid, + const std::string& name, const std::string& partition, + const std::string& account, const std::string& value, bool force) { + util::write_lock_guard user_guard(m_rw_user_mutex_); + util::read_lock_guard account_guard(m_rw_account_mutex_); + util::read_lock_guard qos_guard(m_rw_qos_mutex_); + + const User* p = GetExistedUserInfoNoLock_(name); + CraneExpected result{}; + std::string actual_account = account; + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + result = CheckIfUserHasPermOnUserOfAccountNoLock_(*op_user, p, + &actual_account, false); + if (!result) return result; + + const Account* account_ptr = GetExistedAccountInfoNoLock_(actual_account); + + switch (operation_type) { + case crane::grpc::OperationType::Add: + result = CheckAddUserAllowedQosNoLock_(p, account_ptr, partition, value); + return !result ? result + : AddUserAllowedQos_(*p, *account_ptr, partition, value); + case crane::grpc::OperationType::Overwrite: + result = + CheckSetUserAllowedQosNoLock_(p, account_ptr, partition, value, force); + return !result + ? result + : SetUserAllowedQos_(*p, *account_ptr, partition, value, force); + default: + std::unreachable(); + } + + return result; +} + +AccountManager::CraneExpected AccountManager::DeleteUserAllowedPartition( + uint32_t uid, const std::string& name, const std::string& account, + const std::string& value) { + util::write_lock_guard user_guard(m_rw_user_mutex_); + + const User* p = GetExistedUserInfoNoLock_(name); + CraneExpected result; + // Account might be empty. In that case, ues user->default_account. + std::string actual_account = account; + { + util::read_lock_guard account_guard(m_rw_account_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + result = CheckIfUserHasPermOnUserOfAccountNoLock_(*op_user, p, + &actual_account, false); + if (!result) return result; + } + + result = CheckDeleteUserAllowedPartitionNoLock_(*p, actual_account, value); + if (!result) return result; + + return DeleteUserAllowedPartition_(*p, actual_account, value); +} + +AccountManager::CraneExpected AccountManager::DeleteUserAllowedQos( + uint32_t uid, const std::string& name, const std::string& partition, + const std::string& account, const std::string& value, bool force) { + util::write_lock_guard user_guard(m_rw_user_mutex_); + + const User* p = GetExistedUserInfoNoLock_(name); + CraneExpected result; + std::string actual_account = account; + { + util::read_lock_guard account_guard(m_rw_account_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + result = CheckIfUserHasPermOnUserOfAccountNoLock_(*op_user, p, + &actual_account, false); + if (!result) return result; + } + + result = CheckDeleteUserAllowedQosNoLock_(*p, actual_account, partition, + value, force); + if (!result) return result; + + return DeleteUserAllowedQos_(*p, value, actual_account, partition, force); +} + +AccountManager::CraneExpected AccountManager::ModifyAccount( + crane::grpc::OperationType operation_type, uint32_t uid, + const std::string& name, crane::grpc::ModifyField modify_field, + const std::string& value, bool force) { + CraneExpected result{}; + + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + util::read_lock_guard account_guard(m_rw_account_mutex_); + + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + + result = CheckIfUserHasPermOnAccountNoLock_(*op_user, name, false); + if (!result) return result; + } + + switch (operation_type) { + case crane::grpc::OperationType::Add: { + util::write_lock_guard account_guard(m_rw_account_mutex_); + const Account* account = GetExistedAccountInfoNoLock_(name); + switch (modify_field) { + case crane::grpc::ModifyField::Partition: { + result = CheckAddAccountAllowedPartitionNoLock_(account, value); + return !result ? result : AddAccountAllowedPartition_(name, value); + } + + case crane::grpc::ModifyField::Qos: { + util::write_lock_guard qos_guard(m_rw_qos_mutex_); + result = CheckAddAccountAllowedQosNoLock_(account, value); + return !result ? result : AddAccountAllowedQos_(*account, value); + } + + default: + std::unreachable(); + } + } + + case crane::grpc::OperationType::Overwrite: + switch (modify_field) { + case crane::grpc::ModifyField::Description: { + util::write_lock_guard account_guard(m_rw_account_mutex_); + const Account* account = GetExistedAccountInfoNoLock_(name); + result = CheckSetAccountDescriptionNoLock_(account); + return !result ? result : SetAccountDescription_(name, value); + } + case crane::grpc::ModifyField::Partition: { + util::write_lock_guard user_guard(m_rw_user_mutex_); + util::write_lock_guard account_guard(m_rw_account_mutex_); + const Account* account = GetExistedAccountInfoNoLock_(name); + result = CheckSetAccountAllowedPartitionNoLock_(account, value, force); + return !result ? result : SetAccountAllowedPartition_(*account, value); + } + + case crane::grpc::ModifyField::Qos: { + util::write_lock_guard user_guard(m_rw_user_mutex_); + util::write_lock_guard account_guard(m_rw_account_mutex_); + util::write_lock_guard qos_guard(m_rw_qos_mutex_); + const Account* account = GetExistedAccountInfoNoLock_(name); + result = CheckSetAccountAllowedQosNoLock_(account, value, force); + return !result ? result : SetAccountAllowedQos_(*account, value); + } + case crane::grpc::ModifyField::DefaultQos: { + util::write_lock_guard account_guard(m_rw_account_mutex_); + const Account* account = GetExistedAccountInfoNoLock_(name); + result = CheckSetAccountDefaultQosNoLock_(account, value); + return !result ? result : SetAccountDefaultQos_(*account, value); + } + + default: + std::unreachable(); + } + + case crane::grpc::OperationType::Delete: + switch (modify_field) { + case crane::grpc::ModifyField::Partition: { + util::write_lock_guard user_guard(m_rw_user_mutex_); + util::write_lock_guard account_guard(m_rw_account_mutex_); + const Account* account = GetExistedAccountInfoNoLock_(name); + result = CheckDeleteAccountAllowedPartitionNoLock_(account, value, force); + return !result ? result : DeleteAccountAllowedPartition_(*account, value); + } + + case crane::grpc::ModifyField::Qos: { + util::write_lock_guard user_guard(m_rw_user_mutex_); + util::write_lock_guard account_guard(m_rw_account_mutex_); + util::write_lock_guard qos_guard(m_rw_qos_mutex_); + const Account* account = GetExistedAccountInfoNoLock_(name); + result = CheckDeleteAccountAllowedQosNoLock_(account, value, force); + return !result ? result : DeleteAccountAllowedQos_(*account, value); + } + + default: + std::unreachable(); + } + + default: + std::unreachable(); + } + + return result; +} + +AccountManager::CraneExpected AccountManager::ModifyQos( + uint32_t uid, const std::string& name, + crane::grpc::ModifyField modify_field, const std::string& value) { + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + + auto result = CheckIfUserHasHigherPrivThan_(*op_user, User::None); + if (!result) return result; } util::write_lock_guard qos_guard(m_rw_qos_mutex_); const Qos* p = GetExistedQosInfoNoLock_(name); - if (!p) { - return Result{false, fmt::format("Qos '{}' not existed in database", name)}; + if (!p) return std::unexpected(CraneErrCode::ERR_INVALID_QOS); + + std::string item = ""; + switch (modify_field) { + case crane::grpc::ModifyField::Description: + item = "description"; + break; + case crane::grpc::ModifyField::Priority: + item = "priority"; + break; + case crane::grpc::ModifyField::MaxJobsPerUser: + item = "max_jobs_per_user"; + break; + case crane::grpc::ModifyField::MaxCpusPerUser: + item = "max_cpus_per_user"; + break; + case crane::grpc::ModifyField::MaxTimeLimitPerTask: + item = "max_time_limit_per_task"; + break; + default: + std::unreachable(); + } + + bool value_is_number{false}; + int64_t value_number; + if (item != Qos::FieldStringOfDescription()) { + bool ok = util::ConvertStringToInt64(value, &value_number); + if (!ok) return std::unexpected(CraneErrCode::ERR_CONVERT_TO_INTERGER); + + value_is_number = true; + + if (item == Qos::FieldStringOfMaxTimeLimitPerTask() && + !CheckIfTimeLimitSecIsValid(value_number)) + return std::unexpected(CraneErrCode::ERR_TIME_LIMIT); } + mongocxx::client_session::with_transaction_cb callback; if (item == "description") { - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", - name, item, value)) { - return Result{false, "Fail to update the database"}; - } + // Update to database + callback = [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", name, + item, value); + }; + } else { /* uint32 Type Stores data based on long(int64_t) */ - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", - name, item, value_number)) { - return Result{false, "Fail to update the database"}; - } + callback = [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", name, + item, value_number); + }; } - // To avoid frequently judging item, obtain the modified qos of the Mongodb + if (!g_db_client->CommitTransaction(callback)) + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); + + // To avoid frequently judging item, obtain the modified qos of the + // Mongodb Qos qos; g_db_client->SelectQos("name", name, &qos); *m_qos_map_[name] = std::move(qos); - return Result{true}; + return {}; } -AccountManager::Result AccountManager::BlockAccount(const std::string& name, - bool block) { - util::write_lock_guard account_guard(m_rw_account_mutex_); +AccountManager::CraneExpected AccountManager::BlockAccount( + uint32_t uid, const std::string& name, bool block) { + { + util::read_lock_guard user_guard(m_rw_user_mutex_); + util::read_lock_guard account_guard(m_rw_account_mutex_); - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; - } + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); - if (account->blocked == block) { - return Result{true}; + auto result = CheckIfUserHasPermOnAccountNoLock_(*op_user, name, false); + if (!result) return result; } - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", - name, "blocked", block)) { - return Result{false, "Can't update the database"}; - } - m_account_map_[name]->blocked = block; + util::write_lock_guard account_guard(m_rw_account_mutex_); + + const Account* account = GetExistedAccountInfoNoLock_(name); + if (!account) return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); + + if (account->blocked == block) return {}; - return Result{true}; + return BlockAccount_(name, block); } -AccountManager::Result AccountManager::BlockUser(const std::string& name, - const std::string& account, - bool block) { +AccountManager::CraneExpected AccountManager::BlockUser( + uint32_t uid, const std::string& name, const std::string& account, + bool block) { util::write_lock_guard user_guard(m_rw_user_mutex_); const User* user = GetExistedUserInfoNoLock_(name); - if (!user) { - return Result{false, fmt::format("Unknown user '{}'", name)}; - } - if (!user->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; - } - - if (user->account_to_attrs_map.at(account).blocked == block) { - return Result{true}; + std::string actual_account = account; + { + util::read_lock_guard account_guard(m_rw_account_mutex_); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); + auto result = CheckIfUserHasPermOnUserOfAccountNoLock_( + *op_user, user, &actual_account, false); + if (!result) return result; } - if (!g_db_client->UpdateEntityOne( - MongodbClient::EntityType::USER, "$set", name, - "account_to_attrs_map." + account + ".blocked", block)) { - return Result{false, "Can't update the database"}; - } - m_user_map_[name]->account_to_attrs_map[account].blocked = block; + if (user->account_to_attrs_map.at(actual_account).blocked == block) return {}; - return Result{true}; + return BlockUser_(name, actual_account, block); } bool AccountManager::CheckUserPermissionToPartition( const std::string& name, const std::string& account, const std::string& partition) { UserMutexSharedPtr user_share_ptr = GetExistedUserInfo(name); - if (!user_share_ptr) { - return false; - } + if (!user_share_ptr) return false; if (user_share_ptr->uid == 0 || user_share_ptr->account_to_attrs_map.at(account) .allowed_partition_qos_map.contains(partition)) { return true; } + return false; } -result::result AccountManager::CheckEnableState( - const std::string& account, const std::string& user) { +result::result AccountManager::CheckIfUserOfAccountIsEnabled( + const std::string& user, const std::string& account) { util::read_lock_guard user_guard(m_rw_user_mutex_); util::read_lock_guard account_guard(m_rw_account_mutex_); - std::string p_str = account; - const Account* p_account; + std::string account_name = account; + do { - p_account = GetExistedAccountInfoNoLock_(p_str); - if (p_account->blocked) { + const Account* account_ptr = GetExistedAccountInfoNoLock_(account_name); + if (account_ptr->blocked) { return result::fail( - fmt::format("Ancestor account '{}' is blocked", p_account->name)); + fmt::format("Ancestor account '{}' is blocked", account_ptr->name)); } - p_str = p_account->parent_account; - } while (!p_str.empty()); + account_name = account_ptr->parent_account; + } while (!account_name.empty()); - const User* p_user = GetExistedUserInfoNoLock_(user); - if (p_user->account_to_attrs_map.at(account).blocked) { - return result::fail(fmt::format("User '{}' is blocked", p_user->name)); + const User* user_ptr = GetExistedUserInfoNoLock_(user); + if (user_ptr->account_to_attrs_map.at(account).blocked) { + return result::fail(fmt::format("User '{}' is blocked", user_ptr->name)); } return {}; } @@ -794,13 +948,10 @@ result::result AccountManager::CheckAndApplyQosLimitOnTask( task->Username(), task->partition_id)); } else { // Check whether task.qos in the qos list - if (std::find(partition_it->second.second.begin(), - partition_it->second.second.end(), - task->qos) == partition_it->second.second.end()) { + if (!ranges::contains(partition_it->second.second, task->qos)) return result::fail(fmt::format( "The qos '{}' you set is not in partition's allowed qos list", task->qos)); - } } } else { if (task->qos.empty()) { @@ -826,863 +977,1172 @@ result::result AccountManager::CheckAndApplyQosLimitOnTask( return {}; } -AccountManager::Result AccountManager::FindUserLevelAccountsOfUid( - uint32_t uid, User::AdminLevel* level, std::list* accounts) { - PasswordEntry entry(uid); - if (!entry.Valid()) { - return Result{false, fmt::format("Uid {} not found.", uid)}; - } - - UserMutexSharedPtr ptr = GetExistedUserInfo(entry.Username()); - if (!ptr) { - return Result{false, fmt::format("User {} is not a user of Crane.", - entry.Username())}; - } - if (level != nullptr) *level = ptr->admin_level; - if (accounts != nullptr) { - for (const auto& [acct, item] : ptr->account_to_attrs_map) { - accounts->emplace_back(acct); - } - } - - return Result{true}; -} - result::result AccountManager::CheckUidIsAdmin( uint32_t uid) { - PasswordEntry entry(uid); - if (!entry.Valid()) { - return result::failure(fmt::format("Uid {} not found.", uid)); - } - util::read_lock_guard user_guard(m_rw_user_mutex_); - const User* ptr = GetExistedUserInfoNoLock_(entry.Username()); - if (!ptr) { - return result::failure( - fmt::format("User {} is not a user of Crane.", entry.Username())); + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) { + return result::failure("User is not a user of Crane."); } + const User* user_ptr = user_result.value(); - if (ptr->admin_level >= User::Operator) return {}; + if (user_ptr->admin_level >= User::Operator) return {}; - return result::failure( - fmt::format("User {} has insufficient privilege.", entry.Username())); + return result::failure("User has insufficient privilege."); } -AccountManager::Result AccountManager::HasPermissionToAccount( - uint32_t uid, const std::string& account, bool read_only_priv, - User::AdminLevel* level_of_uid) { - PasswordEntry entry(uid); - if (!entry.Valid()) { - return Result{false, fmt::format("Uid {} not existed", uid)}; - } - +AccountManager::CraneExpected AccountManager::CheckIfUidHasPermOnUser( + uint32_t uid, const std::string& username, bool read_only_priv) { util::read_lock_guard user_guard(m_rw_user_mutex_); util::read_lock_guard account_guard(m_rw_account_mutex_); - const User* user = GetExistedUserInfoNoLock_(entry.Username()); - if (!user) { - return Result{false, fmt::format("User '{}' is not a user of Crane", - entry.Username())}; - } + auto user_result = GetUserInfoByUidNoLock_(uid); + if (!user_result) return std::unexpected(user_result.error()); + const User* op_user = user_result.value(); - if (level_of_uid != nullptr) *level_of_uid = user->admin_level; + const User* user = GetExistedUserInfoNoLock_(username); - if (user->admin_level == User::None) { - if (account.empty()) - return Result{false, fmt::format("No account is specified for user {}", - entry.Username())}; + return CheckIfUserHasPermOnUserNoLock_(*op_user, user, read_only_priv); +} - if (read_only_priv) { - // In current implementation, if a user is the coordinator of an - // account, it must exist in user->account_to_attrs_map. - // This is guaranteed by the procedure of adding coordinator, where the - // coordinator is specified only when adding a user to an account. - // Thus, user->account_to_attrs_map must cover all the accounts he - // coordinates, and it's ok to skip checking user->coordinator_accounts. - for (const auto& [acc, item] : user->account_to_attrs_map) - if (acc == account || PaternityTestNoLock_(acc, account)) - return Result{true}; - } else { - for (const auto& acc : user->coordinator_accounts) - if (acc == account || PaternityTestNoLock_(acc, account)) - return Result{true}; - } +AccountManager::CraneExpected +AccountManager::CheckAddUserAllowedPartitionNoLock_( + const User* user, const Account* account, const std::string& partition) { + auto result = CheckPartitionIsAllowedNoLock_(account, partition, false, true); + if (!result) return result; - return Result{ - false, - fmt::format("User {} is not allowed to access" - "account {} out of the subtree of his permitted accounts.", - entry.Username(), account)}; + const std::string& account_name = account->name; + if (user->account_to_attrs_map.at(account_name) + .allowed_partition_qos_map.contains(partition)) { + return std::unexpected(CraneErrCode::ERR_DUPLICATE_PARTITION); } - return Result{true}; + return {}; } -AccountManager::Result AccountManager::HasPermissionToUser( - uint32_t uid, const std::string& target_user, bool read_only_priv, - User::AdminLevel* level_of_uid) { - PasswordEntry source_user_entry(uid); - if (!source_user_entry.Valid()) { - return Result{false, fmt::format("Uid {} not existed", uid)}; - } - - util::read_lock_guard user_guard(m_rw_user_mutex_); - util::read_lock_guard account_guard(m_rw_account_mutex_); - - const User* source_user_ptr = - GetExistedUserInfoNoLock_(source_user_entry.Username()); - if (!source_user_ptr) - return Result{false, fmt::format("User {} is not a user of Crane", - source_user_entry.Username())}; +AccountManager::CraneExpected +AccountManager::CheckSetUserAllowedPartitionNoLock_( + const Account* account, const std::string& partition) { + auto result = CheckPartitionIsAllowedNoLock_(account, partition, false, true); + if (!result) return result; - const User* target_user_ptr = GetExistedUserInfoNoLock_(target_user); - if (!target_user_ptr) - return Result{false, - fmt::format("User {} is not a user of Crane", target_user)}; - - if (level_of_uid != nullptr) *level_of_uid = source_user_ptr->admin_level; - - if (source_user_ptr->admin_level != User::None || - target_user == source_user_entry.Username()) - return Result{true}; - - std::vector accounts_under_permission_vec; - if (read_only_priv) - for (const auto& [acc, acct_item] : source_user_ptr->account_to_attrs_map) - accounts_under_permission_vec.emplace_back(acc); - else - for (const auto& acc : source_user_ptr->coordinator_accounts) - accounts_under_permission_vec.emplace_back(acc); + return {}; +} - for (const auto& [target_user_acc, item] : - target_user_ptr->account_to_attrs_map) { - for (const auto& acc_under_perm : accounts_under_permission_vec) - if (acc_under_perm == target_user_acc || - PaternityTestNoLock_(acc_under_perm, target_user_acc)) - return Result{true}; +AccountManager::CraneExpected +AccountManager::CheckAddUserAllowedQosNoLock_(const User* user, + const Account* account, + const std::string& partition, + const std::string& qos_str) { + auto result = CheckQosIsAllowedNoLock_(account, qos_str, false, true); + if (!result) return result; + const std::string& account_name = account->name; + // check if add item already the user's allowed qos + const auto& attrs_in_account_map = + user->account_to_attrs_map.at(account_name); + if (partition.empty()) { + // When the user has no partition, QoS cannot be added. + if (attrs_in_account_map.allowed_partition_qos_map.empty()) + return std::unexpected(CraneErrCode::ERR_USER_EMPTY_PARTITION); + + bool is_allowed = false; + for (const auto& [par, pair] : + attrs_in_account_map.allowed_partition_qos_map) { + const std::list& list = pair.second; + if (!ranges::contains(list, qos_str)) { + is_allowed = true; + break; + } + } + if (!is_allowed) return std::unexpected(CraneErrCode::ERR_DUPLICATE_QOS); + } else { + auto iter = attrs_in_account_map.allowed_partition_qos_map.find(partition); + if (iter == attrs_in_account_map.allowed_partition_qos_map.end()) + return std::unexpected(CraneErrCode::ERR_ALLOWED_PARTITION); + const std::list& list = iter->second.second; + if (ranges::contains(list, qos_str)) + return std::unexpected(CraneErrCode::ERR_DUPLICATE_QOS); } - return Result{false, fmt::format("User {} is not permitted to access user {} " - "out of subtrees of his permitted accounts", - source_user_entry.Username(), target_user)}; + return {}; } -void AccountManager::InitDataMap_() { - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::write_lock_guard account_guard(m_rw_account_mutex_); - util::write_lock_guard qos_guard(m_rw_qos_mutex_); - - std::list user_list; - g_db_client->SelectAllUser(&user_list); - for (auto& user : user_list) { - m_user_map_[user.name] = std::make_unique(user); - } +AccountManager::CraneExpected +AccountManager::CheckSetUserAllowedQosNoLock_(const User* user, + const Account* account, + const std::string& partition, + const std::string& qos_str, + bool force) { + auto result = CheckQosIsAllowedNoLock_(account, qos_str, false, true); + if (!result) return result; + const std::string& account_name = account->name; + std::vector qos_vec = + absl::StrSplit(qos_str, ',', absl::SkipEmpty()); - std::list account_list; - g_db_client->SelectAllAccount(&account_list); - for (auto& account : account_list) { - m_account_map_[account.name] = std::make_unique(account); + std::unordered_map>> + cache_allowed_partition_qos_map; + const auto& attrs_in_account_map = + user->account_to_attrs_map.at(account_name); + if (partition.empty()) { + cache_allowed_partition_qos_map = + attrs_in_account_map.allowed_partition_qos_map; + } else { + auto iter = attrs_in_account_map.allowed_partition_qos_map.find(partition); + if (iter == attrs_in_account_map.allowed_partition_qos_map.end()) { + return std::unexpected(CraneErrCode::ERR_ALLOWED_PARTITION); + } + cache_allowed_partition_qos_map.insert({iter->first, iter->second}); } - std::list qos_list; - g_db_client->SelectAllQos(&qos_list); - for (auto& qos : qos_list) { - m_qos_map_[qos.name] = std::make_unique(qos); + for (const auto& [par, pair] : cache_allowed_partition_qos_map) { + if (!ranges::contains(qos_vec, pair.first)) { + if (!force && !pair.first.empty()) + return std::unexpected(CraneErrCode::ERR_SET_ALLOWED_QOS); + } } + return {}; } -/** - * - * @param name - * @note copy user info from m_user_map_ - * @return bool - */ -const User* AccountManager::GetUserInfoNoLock_(const std::string& name) { - auto find_res = m_user_map_.find(name); - if (find_res == m_user_map_.end()) { - return nullptr; - } else { - return find_res->second.get(); - } -} +AccountManager::CraneExpected +AccountManager::CheckSetUserDefaultQosNoLock_(const User& user, + const std::string& account, + const std::string& partition, + const std::string& qos) { + const auto& attrs_in_account = user.account_to_attrs_map.at(account); + if (partition.empty()) { + bool is_allowed = false; + for (const auto& [par, pair] : attrs_in_account.allowed_partition_qos_map) { + if (ranges::contains(pair.second, qos) && qos != pair.first) { + is_allowed = true; + break; + } + } -/* - * Get the user info form mongodb and deletion flag marked false - */ -const User* AccountManager::GetExistedUserInfoNoLock_(const std::string& name) { - const User* user = GetUserInfoNoLock_(name); - if (user && !user->deleted) { - return user; + if (!is_allowed) return std::unexpected(CraneErrCode::ERR_SET_DEFAULT_QOS); } else { - return nullptr; - } -} + auto iter = attrs_in_account.allowed_partition_qos_map.find(partition); -const Account* AccountManager::GetAccountInfoNoLock_(const std::string& name) { - auto find_res = m_account_map_.find(name); - if (find_res == m_account_map_.end()) { - return nullptr; - } else { - return find_res->second.get(); - } -} + if (iter == attrs_in_account.allowed_partition_qos_map.end()) + return std::unexpected(CraneErrCode::ERR_ALLOWED_PARTITION); -const Account* AccountManager::GetExistedAccountInfoNoLock_( - const std::string& name) { - const Account* account = GetAccountInfoNoLock_(name); - if (account && !account->deleted) { - return account; - } else { - return nullptr; + if (!ranges::contains(iter->second.second, qos)) + return std::unexpected(CraneErrCode::ERR_ALLOWED_QOS); + + if (iter->second.first == qos) + return std::unexpected(CraneErrCode::ERR_DUPLICATE_DEFAULT_QOS); } + + return {}; } -const Qos* AccountManager::GetQosInfoNoLock_(const std::string& name) { - auto find_res = m_qos_map_.find(name); - if (find_res == m_qos_map_.end()) { - return nullptr; - } else { - return find_res->second.get(); - } +AccountManager::CraneExpected +AccountManager::CheckDeleteUserAllowedPartitionNoLock_( + const User& user, const std::string& account, + const std::string& partition) { + if (!user.account_to_attrs_map.at(account).allowed_partition_qos_map.contains( + partition)) + return std::unexpected(CraneErrCode::ERR_ALLOWED_PARTITION); + + return {}; } -const Qos* AccountManager::GetExistedQosInfoNoLock_(const std::string& name) { - const Qos* qos = GetQosInfoNoLock_(name); - if (qos && !qos->deleted) { - return qos; +AccountManager::CraneExpected +AccountManager::CheckDeleteUserAllowedQosNoLock_(const User& user, + const std::string& account, + const std::string& partition, + const std::string& qos, + bool force) { + const auto& attrs_in_account = user.account_to_attrs_map.at(account); + if (partition.empty()) { + bool is_allowed = false; + for (const auto& [par, pair] : attrs_in_account.allowed_partition_qos_map) { + if (ranges::contains(pair.second, qos)) { + is_allowed = true; + if (pair.first == qos && !force) + return std::unexpected(CraneErrCode::ERR_IS_DEFAULT_QOS); + } + if (!is_allowed) return std::unexpected(CraneErrCode::ERR_ALLOWED_QOS); + } } else { - return nullptr; + // Delete the qos of a specified partition + auto iter = attrs_in_account.allowed_partition_qos_map.find(partition); + + if (iter == attrs_in_account.allowed_partition_qos_map.end()) + return std::unexpected(CraneErrCode::ERR_ALLOWED_PARTITION); + + if (!ranges::contains(iter->second.second, qos)) + return std::unexpected(CraneErrCode::ERR_ALLOWED_QOS); + + if (qos == iter->second.first && !force) + return std::unexpected(CraneErrCode::ERR_IS_DEFAULT_QOS); } + + return {}; } -bool AccountManager::IncQosReferenceCountInDb_(const std::string& name, - int num) { - return g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$inc", - name, "reference_count", num); +AccountManager::CraneExpected +AccountManager::CheckAddAccountAllowedPartitionNoLock_( + const Account* account, const std::string& partition) { + auto result = CheckPartitionIsAllowedNoLock_(account, partition, true, false); + if (!result) return result; + + if (ranges::contains(account->allowed_partition, partition)) + return std::unexpected(CraneErrCode::ERR_DUPLICATE_PARTITION); + + return {}; } -AccountManager::Result AccountManager::AddUserAllowedPartition_( - const std::string& name, const std::string& account, - const std::string& partition) { - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::read_lock_guard account_guard(m_rw_account_mutex_); +AccountManager::CraneExpected +AccountManager::CheckAddAccountAllowedQosNoLock_(const Account* account, + const std::string& qos) { + auto result = CheckQosIsAllowedNoLock_(account, qos, true, false); + if (!result) return result; - const User* p = GetExistedUserInfoNoLock_(name); - if (!p) { - return Result{false, fmt::format("Unknown user '{}'", name)}; + if (ranges::contains(account->allowed_qos_list, qos)) + return std::unexpected(CraneErrCode::ERR_DUPLICATE_QOS); + + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckSetAccountDescriptionNoLock_(const Account* account) { + if (!account) return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); + + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckSetAccountAllowedPartitionNoLock_( + const Account* account, const std::string& partitions, bool force) { + auto result = + CheckPartitionIsAllowedNoLock_(account, partitions, true, false); + if (!result) return result; + + std::vector partition_vec = + absl::StrSplit(partitions, ',', absl::SkipEmpty()); + + for (const auto& par : account->allowed_partition) { + if (!ranges::contains(partition_vec, par)) { + if (!force && IsAllowedPartitionOfAnyNodeNoLock_(account, par)) + return std::unexpected(CraneErrCode::ERR_CHILD_HAS_PARTITION); + } } - User user(*p); - if (!p->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckSetAccountAllowedQosNoLock_(const Account* account, + const std::string& qos_list, + bool force) { + auto result = CheckQosIsAllowedNoLock_(account, qos_list, true, false); + if (!result) return result; + + std::vector qos_vec = + absl::StrSplit(qos_list, ',', absl::SkipEmpty()); + + for (const auto& qos : account->allowed_qos_list) { + if (!ranges::contains(qos_vec, qos)) { + if (!force && IsDefaultQosOfAnyNodeNoLock_(account, qos)) + return std::unexpected(CraneErrCode::ERR_SET_ACCOUNT_QOS); + } } - const Account* account_ptr = GetExistedAccountInfoNoLock_(account); + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckSetAccountDefaultQosNoLock_(const Account* account, + const std::string& qos) { + auto result = CheckQosIsAllowedNoLock_(account, qos, false, false); + if (!result) return result; + + if (account->default_qos == qos) + return std::unexpected(CraneErrCode::ERR_DUPLICATE_DEFAULT_QOS); + + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckDeleteAccountAllowedPartitionNoLock_( + const Account* account, const std::string& partition, bool force) { + auto result = + CheckPartitionIsAllowedNoLock_(account, partition, false, false); + if (!result) return result; + + if (!force && IsAllowedPartitionOfAnyNodeNoLock_(account, partition)) + return std::unexpected(CraneErrCode::ERR_CHILD_HAS_PARTITION); + + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckDeleteAccountAllowedQosNoLock_(const Account* account, + const std::string& qos, + bool force) { + auto result = CheckQosIsAllowedNoLock_(account, qos, false, false); + if (!result) return result; + + if (!force && account->default_qos == qos) + return std::unexpected(CraneErrCode::ERR_IS_DEFAULT_QOS); + + if (!force && IsDefaultQosOfAnyNodeNoLock_(account, qos)) + return std::unexpected(CraneErrCode::ERR_CHILD_HAS_DEFAULT_QOS); - // check if new partition existed - if (!g_config.Partitions.contains(partition)) { - return Result{false, fmt::format("Partition '{}' not existed", partition)}; + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckIfUserHasHigherPrivThan_(const User& op_user, + User::AdminLevel admin_level) { + if (op_user.admin_level <= admin_level) + return std::unexpected(CraneErrCode::ERR_PERMISSION_USER); + + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckIfUserHasPermOnAccountNoLock_(const User& op_user, + const std::string& account, + bool read_only_priv) { + if (account.empty()) { + return std::unexpected(CraneErrCode::ERR_PERMISSION_USER); + } + + const Account* account_ptr = GetExistedAccountInfoNoLock_(account); + if (!account_ptr) { + return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); } - // check if account has access to new partition - if (std::find(account_ptr->allowed_partition.begin(), - account_ptr->allowed_partition.end(), - partition) == account_ptr->allowed_partition.end()) { - return Result{false, fmt::format("User '{}''s account '{}' is not allowed " - "to use the partition '{}'", - name, user.default_account, partition)}; + + if (op_user.admin_level == User::None) { + if (read_only_priv) { + // In current implementation, if a user is the coordinator of an + // account, it must exist in user->account_to_attrs_map. + // This is guaranteed by the procedure of adding coordinator, where + // the coordinator is specified only when adding a user to an account. + // Thus, user->account_to_attrs_map must cover all the accounts he + // coordinates, and it's ok to skip checking + // user->coordinator_accounts. + for (const auto& [acc, item] : op_user.account_to_attrs_map) + if (acc == account || PaternityTestNoLock_(acc, account)) return {}; + } else { + for (const auto& acc : op_user.coordinator_accounts) + if (acc == account || PaternityTestNoLock_(acc, account)) return {}; + } + + return std::unexpected(CraneErrCode::ERR_USER_ALLOWED_ACCOUNT); } - // check if add item already the user's allowed partition - if (user.account_to_attrs_map[account].allowed_partition_qos_map.contains( - partition)) { - return Result{ - false, fmt::format("The partition '{}' is already in " - "user '{}''s allowed partition under account '{}'", - partition, name, account)}; + return {}; +} + +AccountManager::CraneExpected +AccountManager::CheckIfUserHasPermOnUserOfAccountNoLock_(const User& op_user, + const User* user, + std::string* account, + bool read_only_priv) { + if (!user) return std::unexpected(CraneErrCode::ERR_INVALID_USER); + + if (account->empty()) *account = user->default_account; + + if (!user->account_to_attrs_map.contains(*account)) + return std::unexpected(CraneErrCode::ERR_USER_ACCOUNT_MISMATCH); + + if (op_user.admin_level < user->admin_level) + return std::unexpected(CraneErrCode::ERR_PERMISSION_USER); + + return CheckIfUserHasPermOnAccountNoLock_(op_user, *account, read_only_priv); +} + +AccountManager::CraneExpected +AccountManager::CheckIfUserHasPermOnUserNoLock_(const User& op_user, + const User* user, + bool read_only_priv) { + if (!user) return std::unexpected(CraneErrCode::ERR_INVALID_USER); + + if (CheckIfUserHasHigherPrivThan_(op_user, user->admin_level) || + op_user.name == user->name) + return {}; + + for (const auto& [acct, item] : user->account_to_attrs_map) { + if (CheckIfUserHasPermOnAccountNoLock_(op_user, acct, read_only_priv)) + return {}; } - // Update the map - user.account_to_attrs_map[account].allowed_partition_qos_map[partition] = - std::pair>{ - account_ptr->default_qos, - std::list{account_ptr->allowed_qos_list}}; + return std::unexpected(CraneErrCode::ERR_PERMISSION_USER); +} - // Update to database - if (!g_db_client->UpdateUser(user)) { - return Result{false, "Fail to update data in database"}; +AccountManager::CraneExpected +AccountManager::CheckPartitionIsAllowedNoLock_(const Account* account, + const std::string& partition, + bool check_parent, + bool is_user) { + if (!account) return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); + + std::vector partition_vec = + absl::StrSplit(partition, ',', absl::SkipEmpty()); + + for (const auto& part : partition_vec) { + // check if new partition existed + if (!g_config.Partitions.contains(part)) + return std::unexpected(CraneErrCode::ERR_INVALID_PARTITION); + + if (!check_parent) { + // check if account has access to new partition + if (!ranges::contains(account->allowed_partition, part)) { + if (is_user) + return std::unexpected(CraneErrCode::ERR_PARENT_ALLOWED_PARTITION); + return std::unexpected(CraneErrCode::ERR_ALLOWED_PARTITION); + } + } else { + // Check if parent account has access to the partition + if (!account->parent_account.empty()) { + const Account* parent = + GetExistedAccountInfoNoLock_(account->parent_account); + if (!ranges::contains(parent->allowed_partition, part)) { + return std::unexpected(CraneErrCode::ERR_PARENT_ALLOWED_PARTITION); + } + } + } } - m_user_map_[name]->account_to_attrs_map[account].allowed_partition_qos_map = - user.account_to_attrs_map[account].allowed_partition_qos_map; + return {}; +} + +AccountManager::CraneExpected AccountManager::CheckQosIsAllowedNoLock_( + const Account* account, const std::string& qos_str, bool check_parent, + bool is_user) { + if (!account) return std::unexpected(CraneErrCode::ERR_INVALID_ACCOUNT); + + std::vector qos_vec = + absl::StrSplit(qos_str, ',', absl::SkipEmpty()); + + for (const auto& qos : qos_vec) { + // check if the qos existed + if (!GetExistedQosInfoNoLock_(qos)) + return std::unexpected(CraneErrCode::ERR_INVALID_QOS); + + if (!check_parent) { + // check if account has access to new qos + if (!ranges::contains(account->allowed_qos_list, qos)) { + if (is_user) + return std::unexpected(CraneErrCode::ERR_PARENT_ALLOWED_QOS); + return std::unexpected(CraneErrCode::ERR_ALLOWED_QOS); + } + } else { + // Check if parent account has access to the qos + if (!account->parent_account.empty()) { + const Account* parent = + GetExistedAccountInfoNoLock_(account->parent_account); + for (const auto& qos : qos_vec) { + if (!ranges::contains(parent->allowed_qos_list, qos)) + return std::unexpected(CraneErrCode::ERR_PARENT_ALLOWED_QOS); + } + } + } + } - return Result{true}; + return {}; } -AccountManager::Result AccountManager::AddUserAllowedQos_( - const std::string& name, const std::string& qos, const std::string& account, - const std::string& partition) { +void AccountManager::InitDataMap_() { util::write_lock_guard user_guard(m_rw_user_mutex_); - util::read_lock_guard account_guard(m_rw_account_mutex_); - util::read_lock_guard qos_guard(m_rw_qos_mutex_); + util::write_lock_guard account_guard(m_rw_account_mutex_); + util::write_lock_guard qos_guard(m_rw_qos_mutex_); - const User* p = GetExistedUserInfoNoLock_(name); - if (!p) { - return Result{false, fmt::format("Unknown user '{}'", name)}; + std::list user_list; + g_db_client->SelectAllUser(&user_list); + for (auto& user : user_list) { + m_user_map_[user.name] = std::make_unique(user); } - User user(*p); - if (!p->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; + + std::list account_list; + g_db_client->SelectAllAccount(&account_list); + for (auto& account : account_list) { + m_account_map_[account.name] = std::make_unique(account); } - const Account* account_ptr = GetExistedAccountInfoNoLock_(account); + std::list qos_list; + g_db_client->SelectAllQos(&qos_list); + for (auto& qos : qos_list) { + m_qos_map_[qos.name] = std::make_unique(qos); + } +} + +AccountManager::CraneExpected +AccountManager::GetUserInfoByUidNoLock_(uint32_t uid) { + PasswordEntry entry(uid); - // check if qos existed - if (!GetExistedQosInfoNoLock_(qos)) { - return Result{false, fmt::format("Qos '{}' not existed", qos)}; + if (!entry.Valid()) { + CRANE_ERROR("Uid {} not existed", uid); + return std::unexpected(CraneErrCode::ERR_INVALID_UID); } - // check if account has access to new qos - if (std::find(account_ptr->allowed_qos_list.begin(), - account_ptr->allowed_qos_list.end(), - qos) == account_ptr->allowed_qos_list.end()) { - return Result{ - false, - fmt::format("Sorry, account '{}' is not allowed to use the qos '{}'", - account, qos)}; + const User* user = GetExistedUserInfoNoLock_(entry.Username()); + + if (!user) { + CRANE_ERROR("User '{}' is not a user of Crane", entry.Username()); + return std::unexpected(CraneErrCode::ERR_INVALID_OP_USER); } - // check if add item already the user's allowed qos - if (partition.empty()) { - // add to all partition - int change_num = 0; - for (auto& [par, pair] : - user.account_to_attrs_map[account].allowed_partition_qos_map) { - std::list& list = pair.second; - if (std::find(list.begin(), list.end(), qos) == list.end()) { - if (pair.first.empty()) { - pair.first = qos; - } - list.emplace_back(qos); - change_num++; - } - } + return user; +} + +/* + * Get the user info form mongodb + */ +const User* AccountManager::GetUserInfoNoLock_(const std::string& name) { + auto find_res = m_user_map_.find(name); + if (find_res == m_user_map_.end()) return nullptr; + return find_res->second.get(); +} + +/* + * Get the user info form mongodb and deletion flag marked false + */ +const User* AccountManager::GetExistedUserInfoNoLock_(const std::string& name) { + const User* user = GetUserInfoNoLock_(name); + if (user && !user->deleted) return user; + return nullptr; +} + +const Account* AccountManager::GetAccountInfoNoLock_(const std::string& name) { + auto find_res = m_account_map_.find(name); + if (find_res == m_account_map_.end()) return nullptr; + + return find_res->second.get(); +} + +const Account* AccountManager::GetExistedAccountInfoNoLock_( + const std::string& name) { + const Account* account = GetAccountInfoNoLock_(name); + if (account && !account->deleted) return account; + return nullptr; +} + +const Qos* AccountManager::GetQosInfoNoLock_(const std::string& name) { + auto find_res = m_qos_map_.find(name); + if (find_res == m_qos_map_.end()) return nullptr; - if (change_num == 0) { - return Result{false, fmt::format("Qos '{}' is already in user '{}''s " - "allowed qos of all partition", - qos, name)}; + return find_res->second.get(); +} + +const Qos* AccountManager::GetExistedQosInfoNoLock_(const std::string& name) { + const Qos* qos = GetQosInfoNoLock_(name); + if (qos && !qos->deleted) return qos; + return nullptr; +} + +bool AccountManager::IncQosReferenceCountInDb_(const std::string& name, + int num) { + return g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$inc", + name, "reference_count", num); +} + +AccountManager::CraneExpected AccountManager::AddUser_( + const User& user, const Account* account, const User* stale_user) { + const std::string& object_account = user.default_account; + const std::string& name = user.name; + + bool add_coordinator = false; + if (!user.coordinator_accounts.empty()) add_coordinator = true; + + User res_user; + if (stale_user && !stale_user->deleted) { + res_user = *stale_user; + res_user.account_to_attrs_map[object_account] = + user.account_to_attrs_map.at(object_account); + if (add_coordinator) + res_user.coordinator_accounts.push_back(object_account); + } else { + res_user = user; + } + + const std::list& parent_allowed_partition = + account->allowed_partition; + if (!res_user.account_to_attrs_map[object_account] + .allowed_partition_qos_map.empty()) { + for (auto&& [partition, qos] : res_user.account_to_attrs_map[object_account] + .allowed_partition_qos_map) { + qos.first = account->default_qos; + qos.second = account->allowed_qos_list; } } else { - // add to exacted partition - auto iter = - user.account_to_attrs_map[account].allowed_partition_qos_map.find( - partition); - if (iter == - user.account_to_attrs_map[account].allowed_partition_qos_map.end()) { - return Result{ - false, - fmt::format("Partition '{}' is not in user '{}''s allowed partition", - partition, name)}; + // Inherit + for (const auto& partition : parent_allowed_partition) { + res_user.account_to_attrs_map[object_account] + .allowed_partition_qos_map[partition] = + std::pair>{ + account->default_qos, + std::list{account->allowed_qos_list}}; } + } + res_user.account_to_attrs_map[object_account].blocked = false; - std::list& list = iter->second.second; - if (std::find(list.begin(), list.end(), qos) != list.end()) { - return Result{false, fmt::format("Qos '{}' is already in user '{}''s " - "allowed qos of partition '{}'", - qos, name, partition)}; + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + // Update the user's account + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$addToSet", object_account, "users", + name); + if (add_coordinator) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$addToSet", object_account, + "coordinators", name); + } + + if (stale_user) { + // There is a same user but was deleted or user would like to add + // user to a new account,here will overwrite it with the same name + g_db_client->UpdateUser(res_user); + g_db_client->UpdateEntityOne(MongodbClient::EntityType::USER, "$set", + name, "creation_time", + ToUnixSeconds(absl::Now())); + } else { + // Insert the new user + g_db_client->InsertUser(res_user); + } + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); + } + + m_account_map_[object_account]->users.emplace_back(name); + if (add_coordinator) { + m_account_map_[object_account]->coordinators.emplace_back(name); + } + m_user_map_[name] = std::make_unique(std::move(res_user)); + + return {}; +} + +AccountManager::CraneExpected AccountManager::AddAccount_( + const Account& account, const Account* parent, + const Account* stale_account) { + const std::string& name = account.name; + + Account res_account(account); + if (parent != nullptr) { + if (res_account.allowed_partition.empty()) { + // Inherit + res_account.allowed_partition = + std::list{parent->allowed_partition}; } - if (iter->second.first.empty()) { - iter->second.first = qos; + + if (res_account.allowed_qos_list.empty()) { + // Inherit + res_account.allowed_qos_list = + std::list{parent->allowed_qos_list}; } - list.push_back(qos); } - // Update to database - if (!g_db_client->UpdateUser(user)) { - return Result{false, "Fail to update data in database"}; + if (res_account.default_qos.empty()) { + if (!res_account.allowed_qos_list.empty()) + res_account.default_qos = res_account.allowed_qos_list.front(); } - m_user_map_[name]->account_to_attrs_map[account].allowed_partition_qos_map = - user.account_to_attrs_map[account].allowed_partition_qos_map; + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + if (!res_account.parent_account.empty()) { + // update the parent account's child_account_list + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$addToSet", res_account.parent_account, + "child_accounts", name); + } + + if (stale_account) { + // There is a same account but was deleted,here will delete the + // original account and overwrite it with the same name + g_db_client->UpdateAccount(res_account); + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$set", name, "creation_time", + ToUnixSeconds(absl::Now())); + } else { + // Insert the new account + g_db_client->InsertAccount(res_account); + } + for (const auto& qos : res_account.allowed_qos_list) { + IncQosReferenceCountInDb_(qos, 1); + } + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); + } + if (!res_account.parent_account.empty()) { + m_account_map_[res_account.parent_account]->child_accounts.emplace_back( + name); + } + for (const auto& qos : res_account.allowed_qos_list) { + m_qos_map_[qos]->reference_count++; + } + m_account_map_[name] = std::make_unique(std::move(res_account)); - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetUserAdminLevel_( - const std::string& name, const std::string& level) { - util::write_lock_guard user_guard(m_rw_user_mutex_); +AccountManager::CraneExpected AccountManager::AddQos_( + const Qos& qos, const Qos* stale_qos) { + if (stale_qos) { + // There is a same qos but was deleted,here will delete the original + // qos and overwrite it with the same name + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateQos(qos); + g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", + qos.name, "creation_time", + ToUnixSeconds(absl::Now())); + }; - const User* user = GetExistedUserInfoNoLock_(name); - if (!user) { - return Result{false, fmt::format("Unknown user '{}'", name)}; + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); + } + } else { + // Insert the new qos + if (!g_db_client->InsertQos(qos)) + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - User::AdminLevel new_level; - if (level == "none") { - new_level = User::None; - } else if (level == "operator") { - new_level = User::Operator; - } else if (level == "admin") { - new_level = User::Admin; + m_qos_map_[qos.name] = std::make_unique(qos); + + return {}; +} + +AccountManager::CraneExpected AccountManager::DeleteUser_( + const User& user, const std::string& account) { + const std::string& name = user.name; + + std::list remove_accounts; + std::list remove_coordinator_accounts; + + User res_user(user); + + if (account.empty()) { + // Delete the user + for (const auto& kv : user.account_to_attrs_map) { + remove_accounts.emplace_back(kv.first); + } + for (const auto& coordinatorAccount : user.coordinator_accounts) { + remove_coordinator_accounts.emplace_back(coordinatorAccount); + } + res_user.deleted = true; } else { - return Result{false, fmt::format("Unknown admin level '{}'", level)}; + // Remove user from account + remove_accounts.emplace_back(account); + if (ranges::contains(user.coordinator_accounts, account)) + remove_coordinator_accounts.emplace_back(account); + + res_user.account_to_attrs_map.erase(account); + if (res_user.default_account == account) { + if (res_user.account_to_attrs_map.empty()) + res_user.deleted = true; + else + res_user.default_account = res_user.account_to_attrs_map.begin()->first; + } } - if (new_level == user->admin_level) { - return Result{false, - fmt::format("User '{}' is already a {} role", name, level)}; + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + // delete form the parent accounts' users list + for (const auto& remove_account : remove_accounts) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$pull", remove_account, + /*account name*/ "users", name); + } + for (const auto& coordinatorAccount : remove_coordinator_accounts) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$pull", coordinatorAccount, + /*account name*/ "coordinators", name); + } + + g_db_client->UpdateUser(res_user); + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - // Update to database - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::USER, "$set", - name, "admin_level", - static_cast(new_level))) { - return Result{false, "Fail to update data in database"}; + for (auto& remove_account : remove_accounts) { + m_account_map_[remove_account]->users.remove(name); + } + for (auto& coordinatorAccount : remove_coordinator_accounts) { + m_account_map_[coordinatorAccount]->coordinators.remove(name); } - m_user_map_[name]->admin_level = new_level; + m_user_map_[name] = std::make_unique(std::move(res_user)); - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetUserDefaultQos_( - const std::string& name, const std::string& qos, const std::string& account, - const std::string& partition) { - util::write_lock_guard user_guard(m_rw_user_mutex_); +AccountManager::CraneExpected AccountManager::DeleteAccount_( + const Account& account) { + const std::string& name = account.name; - const User* p = GetExistedUserInfoNoLock_(name); - if (!p) { - return Result{false, fmt::format("Unknown user '{}'", name)}; + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + if (!account.parent_account.empty()) { + // delete form the parent account's child account list + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$pull", account.parent_account, + "child_accounts", name); + } + // Delete the account + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", + name, "deleted", true); + for (const auto& qos : account.allowed_qos_list) { + IncQosReferenceCountInDb_(qos, -1); + } + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - User user(*p); - if (!p->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; + + if (!account.parent_account.empty()) { + m_account_map_[account.parent_account]->child_accounts.remove(name); + } + m_account_map_[name]->deleted = true; + + for (const auto& qos : account.allowed_qos_list) { + m_qos_map_[qos]->reference_count--; + } + + return {}; +} + +AccountManager::CraneExpected AccountManager::DeleteQos_( + const std::string& name) { + if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::QOS, "$set", + name, "deleted", true)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); + } + m_qos_map_[name]->deleted = true; + + return {}; +} + +AccountManager::CraneExpected AccountManager::AddUserAllowedPartition_( + const User& user, const Account& account, const std::string& partition) { + const std::string& name = user.name; + const std::string& account_name = account.name; + + User res_user(user); + + // Update the map + res_user.account_to_attrs_map[account_name] + .allowed_partition_qos_map[partition] = + std::pair>{ + account.default_qos, + std::list{account.allowed_qos_list}}; + + // Update to database + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateUser(res_user); + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } + m_user_map_[name] + ->account_to_attrs_map[account_name] + .allowed_partition_qos_map = + res_user.account_to_attrs_map[account_name].allowed_partition_qos_map; + + return {}; +} + +AccountManager::CraneExpected AccountManager::AddUserAllowedQos_( + const User& user, const Account& account, const std::string& partition, + const std::string& qos) { + const std::string& name = user.name; + const std::string& account_name = account.name; + + User res_user(user); + if (partition.empty()) { - bool is_changed = false; - for (auto& [par, pair] : - user.account_to_attrs_map[account].allowed_partition_qos_map) { - if (std::find(pair.second.begin(), pair.second.end(), qos) != - pair.second.end() && - qos != pair.first) { - is_changed = true; - pair.first = qos; + // add to all partition + for (auto& [par, pair] : res_user.account_to_attrs_map[account_name] + .allowed_partition_qos_map) { + std::list& list = pair.second; + if (!ranges::contains(list, qos)) { + if (pair.first.empty()) { + pair.first = qos; + } + list.emplace_back(qos); } } - - if (!is_changed) { - return Result{false, fmt::format("Qos '{}' not in allowed qos list " - "or is already the default qos", - qos)}; - } } else { - auto iter = - user.account_to_attrs_map[account].allowed_partition_qos_map.find( - partition); - - if (std::find(iter->second.second.begin(), iter->second.second.end(), - qos) == iter->second.second.end()) { - return Result{false, - fmt::format("Qos '{}' not in allowed qos list", qos)}; + // add to exacted partition + auto iter = res_user.account_to_attrs_map[account_name] + .allowed_partition_qos_map.find(partition); + std::list& list = iter->second.second; + if (iter->second.first.empty()) { + iter->second.first = qos; } + list.push_back(qos); + } - if (iter->second.first == qos) { - return Result{false, - fmt::format("Qos '{}' is already the default qos", qos)}; - } - iter->second.first = qos; + // Update to database + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateUser(res_user); + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } + m_user_map_[name] + ->account_to_attrs_map[account_name] + .allowed_partition_qos_map = + res_user.account_to_attrs_map[account_name].allowed_partition_qos_map; + + return {}; +} + +AccountManager::CraneExpected AccountManager::SetUserAdminLevel_( + const std::string& name, User::AdminLevel new_level) { // Update to database - if (!g_db_client->UpdateUser(user)) { - return Result{false, "Fail to update data in database"}; + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::USER, "$set", + name, "admin_level", + static_cast(new_level)); + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - m_user_map_[name]->account_to_attrs_map[account].allowed_partition_qos_map = - user.account_to_attrs_map[account].allowed_partition_qos_map; + m_user_map_[name]->admin_level = new_level; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetUserAllowedPartition_( - const std::string& name, const std::string& account, - const std::string& partitions) { - std::vector partition_vec = - absl::StrSplit(partitions, ',', absl::SkipEmpty()); +AccountManager::CraneExpected AccountManager::SetUserDefaultQos_( + const User& user, const std::string& account, const std::string& partition, + const std::string& qos) { + const std::string& name = user.name; - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::read_lock_guard account_guard(m_rw_account_mutex_); + User res_user(user); - const User* p = GetExistedUserInfoNoLock_(name); - if (!p) { - return Result{false, fmt::format("Unknown user '{}'", name)}; + if (partition.empty()) { + for (auto& [par, pair] : + res_user.account_to_attrs_map[account].allowed_partition_qos_map) { + if (ranges::contains(pair.second, qos) && qos != pair.first) + pair.first = qos; + } + } else { + auto iter = + res_user.account_to_attrs_map[account].allowed_partition_qos_map.find( + partition); + + iter->second.first = qos; } - User user(*p); - if (!p->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; + // Update to database + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateUser(res_user); + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - const Account* account_ptr = GetExistedAccountInfoNoLock_(account); + m_user_map_[name]->account_to_attrs_map[account].allowed_partition_qos_map = + res_user.account_to_attrs_map[account].allowed_partition_qos_map; - for (const auto& par : partition_vec) { - // check if partition existed - if (!g_config.Partitions.contains(par)) { - return Result{false, fmt::format("Partition '{}' not existed", par)}; - } - // check if account has access to new partition - if (std::find(account_ptr->allowed_partition.begin(), - account_ptr->allowed_partition.end(), - par) == account_ptr->allowed_partition.end()) { - return Result{false, - fmt::format("User '{}''s account '{}' is not allowed " - "to use the partition '{}'", - name, user.default_account, par)}; - } - } + return {}; +} + +AccountManager::CraneExpected AccountManager::SetUserAllowedPartition_( + const User& user, const Account& account, const std::string& partitions) { + const std::string& name = user.name; + const std::string& account_name = account.name; + + std::vector partition_vec = + absl::StrSplit(partitions, ',', absl::SkipEmpty()); + User res_user(user); // Update the map - user.account_to_attrs_map[account] + res_user.account_to_attrs_map[account_name] .allowed_partition_qos_map.clear(); // clear the partitions for (const auto& par : partition_vec) { - user.account_to_attrs_map[account].allowed_partition_qos_map[par] = + res_user.account_to_attrs_map[account_name].allowed_partition_qos_map[par] = std::pair>{ - account_ptr->default_qos, - std::list{account_ptr->allowed_qos_list}}; + account.default_qos, + std::list{account.allowed_qos_list}}; } + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateUser(res_user); + }; + // Update to database - if (!g_db_client->UpdateUser(user)) { - return Result{false, "Fail to update data in database"}; + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - m_user_map_[name]->account_to_attrs_map[account].allowed_partition_qos_map = - user.account_to_attrs_map[account].allowed_partition_qos_map; + m_user_map_[name] + ->account_to_attrs_map[account_name] + .allowed_partition_qos_map = + res_user.account_to_attrs_map[account_name].allowed_partition_qos_map; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetUserAllowedQos_( - const std::string& name, const std::string& account, - const std::string& partition, const std::string& qos_list_str, bool force) { +AccountManager::CraneExpected AccountManager::SetUserAllowedQos_( + const User& user, const Account& account, const std::string& partition, + const std::string& qos_list_str, bool force) { + const std::string& name = user.name; + const std::string& account_name = account.name; + std::vector qos_vec = absl::StrSplit(qos_list_str, ',', absl::SkipEmpty()); - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::read_lock_guard account_guard(m_rw_account_mutex_); - util::read_lock_guard qos_guard(m_rw_qos_mutex_); - - const User* p = GetExistedUserInfoNoLock_(name); - if (!p) { - return Result{false, fmt::format("Unknown user '{}'", name)}; - } - if (!p->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; - } - User user(*p); - - const Account* account_ptr = GetExistedAccountInfoNoLock_(account); - - for (const auto& qos : qos_vec) { - // check if qos existed - if (!GetExistedQosInfoNoLock_(qos)) { - return Result{false, fmt::format("Qos '{}' not existed", qos)}; - } - // check if account has access to new qos - if (std::find(account_ptr->allowed_qos_list.begin(), - account_ptr->allowed_qos_list.end(), - qos) == account_ptr->allowed_qos_list.end()) { - return Result{ - false, - fmt::format("Sorry, account '{}' is not allowed to use the qos '{}'", - account, qos)}; - } - } - + User res_user(user); if (partition.empty()) { // Set the qos of all partition - for (auto& [par, pair] : - user.account_to_attrs_map[account].allowed_partition_qos_map) { - if (std::find(qos_vec.begin(), qos_vec.end(), pair.first) == - qos_vec.end()) { - if (!force && !pair.first.empty()) { - return Result{ - false, - fmt::format("Qos '{}' is default qos of partition '{}',but not " - "found in new qos list.Ignoring this constraint with " - "forced operation, the default qos is randomly " - "replaced with one of the items in the new qos list", - pair.first, par)}; - } else { - pair.first = qos_vec.empty() ? "" : qos_vec.front(); - } - } + for (auto& [par, pair] : res_user.account_to_attrs_map[account_name] + .allowed_partition_qos_map) { + if (!ranges::contains(qos_vec, pair.first)) + pair.first = qos_vec.empty() ? "" : qos_vec.front(); pair.second.assign(qos_vec.begin(), qos_vec.end()); } } else { // Set the qos of a specified partition - auto iter = - user.account_to_attrs_map[account].allowed_partition_qos_map.find( - partition); - if (iter == - user.account_to_attrs_map[account].allowed_partition_qos_map.end()) { - return Result{false, - fmt::format("Partition '{}' not in allowed partition list", - partition)}; - } + auto iter = res_user.account_to_attrs_map[account_name] + .allowed_partition_qos_map.find(partition); + + if (!ranges::contains(qos_vec, iter->second.first)) + iter->second.first = qos_vec.empty() ? "" : qos_vec.front(); - if (std::find(qos_vec.begin(), qos_vec.end(), iter->second.first) == - qos_vec.end()) { - if (!force && !iter->second.first.empty()) { - return Result{ - false, - fmt::format("Qos '{}' is default qos of partition '{}',but not " - "found in new qos list.Ignoring this constraint with " - "forced operation, the default qos is randomly " - "replaced with one of the items in the new qos list", - iter->second.first, partition)}; - } else { - iter->second.first = qos_vec.empty() ? "" : qos_vec.front(); - } - } iter->second.second.assign(qos_vec.begin(), qos_vec.end()); } + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateUser(res_user); + }; + // Update to database - if (!g_db_client->UpdateUser(user)) { - return Result{false, "Fail to update data in database"}; + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - m_user_map_[name]->account_to_attrs_map[account].allowed_partition_qos_map = - user.account_to_attrs_map[account].allowed_partition_qos_map; + m_user_map_[name] + ->account_to_attrs_map[account_name] + .allowed_partition_qos_map = + res_user.account_to_attrs_map[account_name].allowed_partition_qos_map; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::DeleteUserAllowedPartition_( - const std::string& name, const std::string& account, +AccountManager::CraneExpected AccountManager::DeleteUserAllowedPartition_( + const User& user, const std::string& account, const std::string& partition) { - util::write_lock_guard user_guard(m_rw_user_mutex_); - - const User* user = GetExistedUserInfoNoLock_(name); - if (!user) { - return Result{false, fmt::format("Unknown user '{}'", name)}; - } - if (!user->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; - } - - if (!user->account_to_attrs_map.at(account) - .allowed_partition_qos_map.contains(partition)) { - return Result{ - false, - fmt::format( - "Partition '{}' is not in user '{}''s allowed partition list", - partition, name)}; - } + const std::string& name = user.name; // Update to database - if (!g_db_client->UpdateEntityOne( - Ctld::MongodbClient::EntityType::USER, "$unset", name, - "account_to_attrs_map." + account + ".allowed_partition_qos_map." + - partition, - std::string(""))) { - return Result{false, "Fail to update data in database"}; + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne( + Ctld::MongodbClient::EntityType::USER, "$unset", name, + "account_to_attrs_map." + account + ".allowed_partition_qos_map." + + partition, + std::string("")); + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } m_user_map_[name] ->account_to_attrs_map[account] .allowed_partition_qos_map.erase(partition); - return Result{true}; + return {}; } -AccountManager::Result AccountManager::DeleteUserAllowedQos_( - const std::string& name, const std::string& qos, const std::string& account, +AccountManager::CraneExpected AccountManager::DeleteUserAllowedQos_( + const User& user, const std::string& qos, const std::string& account, const std::string& partition, bool force) { - util::write_lock_guard user_guard(m_rw_user_mutex_); + const std::string& name = user.name; - const User* p = GetExistedUserInfoNoLock_(name); - if (!p) { - return Result{false, fmt::format("Unknown user '{}'", name)}; - } - if (!p->account_to_attrs_map.contains(account)) { - return Result{false, fmt::format("User '{}' doesn't belong to account '{}'", - name, account)}; - } - User user(*p); + User res_user(user); if (partition.empty()) { // Delete the qos of all partition - int change_num = 0; for (auto& [par, pair] : - user.account_to_attrs_map[account].allowed_partition_qos_map) { - if (std::find(pair.second.begin(), pair.second.end(), qos) != - pair.second.end()) { - change_num++; + res_user.account_to_attrs_map[account].allowed_partition_qos_map) { + if (ranges::contains(pair.second, qos)) { pair.second.remove(qos); - if (pair.first == qos) { - if (!force) { - return Result{ - false, fmt::format( - "Qos '{}' is default qos of partition '{}'.Ignoring " - "this constraint with forced deletion, the deleted " - "default qos is randomly replaced with one of the " - "remaining items in the qos list", - qos, par)}; - } else { - pair.first = pair.second.empty() ? "" : pair.second.front(); - } + pair.first = pair.second.empty() ? "" : pair.second.front(); } } } - - if (change_num == 0) { - return Result{ - false, fmt::format( - "Qos '{}' not in allowed qos list of all partition", qos)}; - } } else { // Delete the qos of a specified partition auto iter = - user.account_to_attrs_map[account].allowed_partition_qos_map.find( + res_user.account_to_attrs_map[account].allowed_partition_qos_map.find( partition); - if (iter == - user.account_to_attrs_map[account].allowed_partition_qos_map.end()) { - return Result{false, - fmt::format("Partition '{}' not in allowed partition list", - partition)}; - } - - if (std::find(iter->second.second.begin(), iter->second.second.end(), - qos) == iter->second.second.end()) { - return Result{ - false, - fmt::format("Qos '{}' not in allowed qos list of partition '{}'", qos, - partition)}; - } iter->second.second.remove(qos); if (qos == iter->second.first) { - if (!force) - return Result{ - false, - fmt::format("Qos '{}' is default qos of partition '{}'.Ignoring " - "this constraint with forced deletion, the deleted " - "default qos is randomly replaced with one of the " - "remaining items in the qos list", - qos, partition)}; - else - iter->second.first = - iter->second.second.empty() ? "" : iter->second.second.front(); + iter->second.first = + iter->second.second.empty() ? "" : iter->second.second.front(); } } // Update to database - if (!g_db_client->UpdateUser(user)) { - return Result{false, "Fail to update data in database"}; + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateUser(res_user); + }; + + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } m_user_map_[name]->account_to_attrs_map[account].allowed_partition_qos_map = - user.account_to_attrs_map[account].allowed_partition_qos_map; + res_user.account_to_attrs_map[account].allowed_partition_qos_map; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::AddAccountAllowedPartition_( +AccountManager::CraneExpected AccountManager::AddAccountAllowedPartition_( const std::string& name, const std::string& partition) { - util::write_lock_guard account_guard(m_rw_account_mutex_); - - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; - } - - // check if the partition existed - if (g_config.Partitions.find(partition) == g_config.Partitions.end()) { - return Result{false, fmt::format("Partition '{}' not existed", partition)}; - } - // Check if parent account has access to the partition - if (!account->parent_account.empty()) { - const Account* parent = - GetExistedAccountInfoNoLock_(account->parent_account); - if (std::find(parent->allowed_partition.begin(), - parent->allowed_partition.end(), - partition) == parent->allowed_partition.end()) { - return Result{false, fmt::format("Parent account '{}' does not " - "have access to partition '{}'", - account->parent_account, partition)}; - } - } - - if (std::find(account->allowed_partition.begin(), - account->allowed_partition.end(), - partition) != account->allowed_partition.end()) { - return Result{ - false, - fmt::format("Partition '{}' is already in allowed partition list", - partition)}; - } + // Update to database + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, + "$addToSet", name, "allowed_partition", + partition); + }; - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, - "$addToSet", name, "allowed_partition", - partition)) { - return Result{false, "Can't update the database"}; + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } m_account_map_[name]->allowed_partition.emplace_back(partition); - return Result{true}; + return {}; } -AccountManager::Result AccountManager::AddAccountAllowedQos_( - const std::string& name, const std::string& qos) { - util::write_lock_guard account_guard(m_rw_account_mutex_); - util::write_lock_guard qos_guard(m_rw_qos_mutex_); - - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; - } - - // check if the qos existed - if (!GetExistedQosInfoNoLock_(qos)) { - return Result{false, fmt::format("Qos '{}' not existed", qos)}; - } - - // Check if parent account has access to the qos - if (!account->parent_account.empty()) { - const Account* parent = - GetExistedAccountInfoNoLock_(account->parent_account); - - if (std::find(parent->allowed_qos_list.begin(), - parent->allowed_qos_list.end(), - qos) == parent->allowed_qos_list.end()) { - return Result{ - false, - fmt::format("Parent account '{}' does not have access to qos '{}'", - account->parent_account, qos)}; - } - } - - if (std::find(account->allowed_qos_list.begin(), - account->allowed_qos_list.end(), - qos) != account->allowed_qos_list.end()) { - return Result{false, - fmt::format("Qos '{}' is already in allowed qos list", qos)}; - } +AccountManager::CraneExpected AccountManager::AddAccountAllowedQos_( + const Account& account, const std::string& qos) { + const std::string& name = account.name; mongocxx::client_session::with_transaction_cb callback = [&](mongocxx::client_session* session) { - if (account->default_qos.empty()) { + if (account.default_qos.empty()) { g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", name, "default_qos", qos); } @@ -1694,133 +2154,77 @@ AccountManager::Result AccountManager::AddAccountAllowedQos_( // Update to database if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - if (account->default_qos.empty()) { + if (account.default_qos.empty()) { m_account_map_[name]->default_qos = qos; } m_account_map_[name]->allowed_qos_list.emplace_back(qos); m_qos_map_[qos]->reference_count++; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetAccountDescription_( +AccountManager::CraneExpected AccountManager::SetAccountDescription_( const std::string& name, const std::string& description) { - util::write_lock_guard account_guard(m_rw_account_mutex_); - - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; - } - - if (description == account->description) { - return Result{false, "Description content not change"}; - } + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", + name, "description", description); + }; - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", - name, "description", description)) { - return Result{false, "Can't update the database"}; + // Update to database + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } m_account_map_[name]->description = description; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetAccountDefaultQos_( - const std::string& name, const std::string& qos) { - util::write_lock_guard account_guard(m_rw_account_mutex_); - - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; - } - - if (account->default_qos == qos) { - return Result{false, - fmt::format("Qos '{}' is already the default qos", qos)}; - } +AccountManager::CraneExpected AccountManager::SetAccountDefaultQos_( + const Account& account, const std::string& qos) { + const std::string& name = account.name; - if (std::find(account->allowed_qos_list.begin(), - account->allowed_qos_list.end(), - qos) == account->allowed_qos_list.end()) { - return Result{false, fmt::format("Qos '{}' not in allowed qos list", qos)}; - } + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", + name, "default_qos", qos); + }; - if (!g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", - name, "default_qos", qos)) { - return Result{false, "Can't update the database"}; + // Update to database + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } m_account_map_[name]->default_qos = qos; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetAccountAllowedPartition_( - const std::string& name, const std::string& partitions, bool force) { +AccountManager::CraneExpected AccountManager::SetAccountAllowedPartition_( + const Account& account, const std::string& partitions) { + const std::string& name = account.name; + std::vector partition_vec = absl::StrSplit(partitions, ',', absl::SkipEmpty()); - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::write_lock_guard account_guard(m_rw_account_mutex_); - - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; - } - // check if the partition existed - for (const auto& p : partition_vec) { - if (!g_config.Partitions.contains(p)) { - return Result{false, fmt::format("Partition '{}' not existed", p)}; - } - } - - // Check if parent account has access to the partition - if (!account->parent_account.empty()) { - const Account* parent = - GetExistedAccountInfoNoLock_(account->parent_account); - for (const auto& p : partition_vec) { - if (std::find(parent->allowed_partition.begin(), - parent->allowed_partition.end(), - p) == parent->allowed_partition.end()) { - return Result{false, fmt::format("Parent account '{}' does not " - "have access to partition '{}'", - account->parent_account, p)}; - } - } - } - std::list deleted_partition; - for (const auto& par : account->allowed_partition) { - if (std::find(partition_vec.begin(), partition_vec.end(), par) == - partition_vec.end()) { - if (!force && IsAllowedPartitionOfAnyNodeNoLock_(account, par)) { - return Result{ - false, - fmt::format("partition '{}' in allowed partition list before is " - "used by some descendant node of the account " - "'{}'.Ignoring this constraint with forced operation", - par, name)}; - } + for (const auto& par : account.allowed_partition) { + if (!ranges::contains(partition_vec, par)) deleted_partition.emplace_back(par); - } } int add_num = 0; for (const auto& par : partition_vec) { - if (std::find(account->allowed_partition.begin(), - account->allowed_partition.end(), - par) == account->allowed_partition.end()) { - add_num++; - } + if (!ranges::contains(account.allowed_partition, par)) add_num++; } mongocxx::client_session::with_transaction_cb callback = [&](mongocxx::client_session* session) { for (const auto& par : deleted_partition) { - DeleteAccountAllowedPartitionFromDBNoLock_(account->name, par); + DeleteAccountAllowedPartitionFromDBNoLock_(account.name, par); } if (add_num > 0) { @@ -1831,83 +2235,41 @@ AccountManager::Result AccountManager::SetAccountAllowedPartition_( }; if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } for (const auto& par : deleted_partition) { - DeleteAccountAllowedPartitionFromMapNoLock_(account->name, par); + DeleteAccountAllowedPartitionFromMapNoLock_(account.name, par); } m_account_map_[name]->allowed_partition.assign(partition_vec.begin(), partition_vec.end()); - return Result{true}; + return {}; } -AccountManager::Result AccountManager::SetAccountAllowedQos_( - const std::string& name, const std::string& qos_list_str, bool force) { +AccountManager::CraneExpected AccountManager::SetAccountAllowedQos_( + const Account& account, const std::string& qos_list_str) { + const std::string& name = account.name; + std::vector qos_vec = absl::StrSplit(qos_list_str, ',', absl::SkipEmpty()); - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::write_lock_guard account_guard(m_rw_account_mutex_); - util::write_lock_guard qos_guard(m_rw_qos_mutex_); - - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; - } - // check if the qos existed - for (const auto& qos : qos_vec) { - if (!GetExistedQosInfoNoLock_(qos)) { - return Result{false, fmt::format("Qos '{}' not existed", qos)}; - } - } - - // Check if parent account has access to the qos - if (!account->parent_account.empty()) { - const Account* parent = - GetExistedAccountInfoNoLock_(account->parent_account); - for (const auto& qos : qos_vec) { - if (std::find(parent->allowed_qos_list.begin(), - parent->allowed_qos_list.end(), - qos) == parent->allowed_qos_list.end()) { - return Result{ - false, - fmt::format("Parent account '{}' does not have access to qos '{}'", - account->parent_account, qos)}; - } - } - } - std::list deleted_qos; - for (const auto& qos : account->allowed_qos_list) { - if (std::find(qos_vec.begin(), qos_vec.end(), qos) == qos_vec.end()) { - if (!force && IsDefaultQosOfAnyNodeNoLock_(account, qos)) { - return Result{ - false, - fmt::format("partition '{}' in allowed partition list before is " - "used by some descendant node of the account '{}' or " - "itself.Ignoring this constraint with forced operation", - qos, name)}; - } - deleted_qos.emplace_back(qos); - } + for (const auto& qos : account.allowed_qos_list) { + if (!ranges::contains(qos_vec, qos)) deleted_qos.emplace_back(qos); } std::list add_qos; for (const auto& qos : qos_vec) { - if (std::find(account->allowed_qos_list.begin(), - account->allowed_qos_list.end(), - qos) == account->allowed_qos_list.end()) { + if (!ranges::contains(account.allowed_qos_list, qos)) add_qos.emplace_back(qos); - } } std::list change_num; mongocxx::client_session::with_transaction_cb callback = [&](mongocxx::client_session* session) { for (const auto& qos : deleted_qos) { - int num = DeleteAccountAllowedQosFromDBNoLock_(account->name, qos); + int num = DeleteAccountAllowedQosFromDBNoLock_(account.name, qos); IncQosReferenceCountInDb_(qos, -num); change_num.emplace_back(num); } @@ -1931,17 +2293,17 @@ AccountManager::Result AccountManager::SetAccountAllowedQos_( }; if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } for (const auto& qos : deleted_qos) { - DeleteAccountAllowedQosFromMapNoLock_(account->name, qos); + DeleteAccountAllowedQosFromMapNoLock_(account.name, qos); m_qos_map_[qos]->reference_count -= change_num.front(); change_num.pop_front(); } if (!add_qos.empty()) { - if (account->default_qos.empty()) { + if (account.default_qos.empty()) { m_account_map_[name]->default_qos = qos_vec.front(); } m_account_map_[name]->allowed_qos_list.assign(qos_vec.begin(), @@ -1951,95 +2313,77 @@ AccountManager::Result AccountManager::SetAccountAllowedQos_( } } - return Result{true}; + return {}; } -AccountManager::Result AccountManager::DeleteAccountAllowedPartition_( - const std::string& name, const std::string& partition, bool force) { - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::write_lock_guard account_guard(m_rw_account_mutex_); +AccountManager::CraneExpected +AccountManager::DeleteAccountAllowedPartition_(const Account& account, + const std::string& partition) { + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + DeleteAccountAllowedPartitionFromDBNoLock_(account.name, partition); + }; - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - if (std::find(account->allowed_partition.begin(), - account->allowed_partition.end(), - partition) == account->allowed_partition.end()) { - return Result{ - false, - fmt::format( - "Partition '{}' not in allowed partition list of account '{}'", - partition, name)}; - } + DeleteAccountAllowedPartitionFromMapNoLock_(account.name, partition); - if (!force && IsAllowedPartitionOfAnyNodeNoLock_(account, partition)) { - return Result{ - false, fmt::format( - "partition '{}' is used by some descendant node of the " - "account '{}'.Ignoring this constraint with forced deletion", - partition, name)}; - } + return {}; +} +AccountManager::CraneExpected AccountManager::DeleteAccountAllowedQos_( + const Account& account, const std::string& qos) { + int change_num; mongocxx::client_session::with_transaction_cb callback = [&](mongocxx::client_session* session) { - DeleteAccountAllowedPartitionFromDBNoLock_(account->name, partition); + change_num = DeleteAccountAllowedQosFromDBNoLock_(account.name, qos); + IncQosReferenceCountInDb_(qos, -change_num); }; if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - DeleteAccountAllowedPartitionFromMapNoLock_(account->name, partition); + DeleteAccountAllowedQosFromMapNoLock_(account.name, qos); + m_qos_map_[qos]->reference_count -= change_num; - return Result{true}; + return {}; } -AccountManager::Result AccountManager::DeleteAccountAllowedQos_( - const std::string& name, const std::string& qos, bool force) { - util::write_lock_guard user_guard(m_rw_user_mutex_); - util::write_lock_guard account_guard(m_rw_account_mutex_); - util::write_lock_guard qos_guard(m_rw_qos_mutex_); +AccountManager::CraneExpected AccountManager::BlockUser_( + const std::string& name, const std::string& account, bool block) { + mongocxx::client_session::with_transaction_cb callback = + [&](mongocxx::client_session* session) { + g_db_client->UpdateEntityOne( + MongodbClient::EntityType::USER, "$set", name, + "account_to_attrs_map." + account + ".blocked", block); + }; - const Account* account = GetExistedAccountInfoNoLock_(name); - if (!account) { - return Result{false, fmt::format("Unknown account '{}'", name)}; + if (!g_db_client->CommitTransaction(callback)) { + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } - if (std::find(account->allowed_qos_list.begin(), - account->allowed_qos_list.end(), - qos) == account->allowed_qos_list.end()) { - return Result{ - false, fmt::format("Qos '{}' is not in account '{}''s allowed qos list", - qos, name)}; - } + m_user_map_[name]->account_to_attrs_map[account].blocked = block; - if (!force && IsDefaultQosOfAnyNodeNoLock_(account, qos)) { - return Result{ - false, - fmt::format("Someone is using qos '{}' as default qos.Ignoring this " - "constraint with forced deletion, the deleted default " - "qos is randomly replaced with one of the remaining " - "items in the qos list", - qos)}; - } + return {}; +} - int change_num; +AccountManager::CraneExpected AccountManager::BlockAccount_( + const std::string& name, bool block) { mongocxx::client_session::with_transaction_cb callback = [&](mongocxx::client_session* session) { - change_num = DeleteAccountAllowedQosFromDBNoLock_(account->name, qos); - IncQosReferenceCountInDb_(qos, -change_num); + g_db_client->UpdateEntityOne(MongodbClient::EntityType::ACCOUNT, "$set", + name, "blocked", block); }; if (!g_db_client->CommitTransaction(callback)) { - return Result{false, "Fail to update data in database"}; + return std::unexpected(CraneErrCode::ERR_UPDATE_DATABASE); } + m_account_map_[name]->blocked = block; - DeleteAccountAllowedQosFromMapNoLock_(account->name, qos); - m_qos_map_[qos]->reference_count -= change_num; - - return Result{true}; + return {}; } /** @@ -2050,9 +2394,7 @@ AccountManager::Result AccountManager::DeleteAccountAllowedQos_( */ bool AccountManager::IsAllowedPartitionOfAnyNodeNoLock_( const Account* account, const std::string& partition, int depth) { - if (depth > 0 && std::find(account->allowed_partition.begin(), - account->allowed_partition.end(), - partition) != account->allowed_partition.end()) { + if (depth > 0 && ranges::contains(account->allowed_partition, partition)) { return true; } for (const auto& child : account->child_accounts) { @@ -2113,26 +2455,19 @@ bool AccountManager::IsDefaultQosOfAnyPartitionNoLock_(const User* user, /** * @note need read lock(m_rw_user_mutex_ && m_rw_account_mutex_) - * @param name - * @param qos - * @return */ int AccountManager::DeleteAccountAllowedQosFromDBNoLock_( const std::string& name, const std::string& qos) { const Account* account = GetExistedAccountInfoNoLock_(name); if (!account) { CRANE_ERROR( - "Operating on a non-existent account '{}', please check this item in " - "database and restart cranectld", + "Operating on a non-existent account '{}', please check this item " + "in database and restart cranectld", name); return false; } - auto iter = std::find(account->allowed_qos_list.begin(), - account->allowed_qos_list.end(), qos); - if (iter == account->allowed_qos_list.end()) { - return false; - } + if (!ranges::contains(account->allowed_qos_list, qos)) return false; int change_num = 1; for (const auto& child : account->child_accounts) { @@ -2156,7 +2491,8 @@ int AccountManager::DeleteAccountAllowedQosFromDBNoLock_( } /** - * @note need write lock(m_rw_account_mutex_) and write lock(m_rw_user_mutex_) + * @note need write lock(m_rw_account_mutex_) and write + * lock(m_rw_user_mutex_) * @param name * @param qos * @return @@ -2166,17 +2502,13 @@ bool AccountManager::DeleteAccountAllowedQosFromMapNoLock_( const Account* account = GetExistedAccountInfoNoLock_(name); if (!account) { CRANE_ERROR( - "Operating on a non-existent account '{}', please check this item in " - "database and restart cranectld", + "Operating on a non-existent account '{}', please check this item " + "in database and restart cranectld", name); return false; } - if (std::find(account->allowed_qos_list.begin(), - account->allowed_qos_list.end(), - qos) == account->allowed_qos_list.end()) { - return false; - } + if (!ranges::contains(account->allowed_qos_list, qos)) return false; for (const auto& child : account->child_accounts) { DeleteAccountAllowedQosFromMapNoLock_(child, qos); @@ -2220,10 +2552,7 @@ bool AccountManager::DeleteUserAllowedQosOfAllPartitionFromDBNoLock_( for (auto& [par, pair] : user.account_to_attrs_map[account].allowed_partition_qos_map) { - auto iter = std::find(pair.second.begin(), pair.second.end(), qos); - if (iter != pair.second.end()) { - pair.second.remove(qos); - } + if (ranges::contains(pair.second, qos)) pair.second.remove(qos); if (pair.first == qos) { pair.first = pair.second.empty() ? "" : pair.second.front(); @@ -2282,17 +2611,13 @@ bool AccountManager::DeleteAccountAllowedPartitionFromDBNoLock_( const Account* account = GetExistedAccountInfoNoLock_(name); if (!account) { CRANE_ERROR( - "Operating on a non-existent account '{}', please check this item in " - "database and restart cranectld", + "Operating on a non-existent account '{}', please check this item " + "in database and restart cranectld", name); return false; } - auto iter = std::find(account->allowed_partition.begin(), - account->allowed_partition.end(), partition); - if (iter == account->allowed_partition.end()) { - return false; - } + if (!ranges::contains(account->allowed_partition, partition)) return false; for (const auto& child : account->child_accounts) { DeleteAccountAllowedPartitionFromDBNoLock_(child, partition); @@ -2312,7 +2637,8 @@ bool AccountManager::DeleteAccountAllowedPartitionFromDBNoLock_( } /** - * @note need write lock(m_rw_account_mutex_) and write lock(m_rw_user_mutex_) + * @note need write lock(m_rw_account_mutex_) and write + * lock(m_rw_user_mutex_) * @param name * @param partition * @return @@ -2322,17 +2648,13 @@ bool AccountManager::DeleteAccountAllowedPartitionFromMapNoLock_( const Account* account = GetExistedAccountInfoNoLock_(name); if (!account) { CRANE_ERROR( - "Operating on a non-existent account '{}', please check this item in " - "database and restart cranectld", + "Operating on a non-existent account '{}', please check this item " + "in database and restart cranectld", name); return false; } - if (std::find(account->allowed_partition.begin(), - account->allowed_partition.end(), - partition) == account->allowed_partition.end()) { - return false; - } + if (!ranges::contains(account->allowed_partition, partition)) return false; for (const auto& child : account->child_accounts) { DeleteAccountAllowedPartitionFromMapNoLock_(child, partition); diff --git a/src/CraneCtld/AccountManager.h b/src/CraneCtld/AccountManager.h index 2d5fc746a..62d251a9d 100644 --- a/src/CraneCtld/AccountManager.h +++ b/src/CraneCtld/AccountManager.h @@ -40,29 +40,39 @@ class AccountManager { using QosMapMutexSharedPtr = util::ScopeConstSharedPtr< std::unordered_map>, util::rw_mutex>; - struct Result { - bool ok{false}; - std::string reason; - }; + using CraneErrCode = crane::grpc::ErrCode; + + template + using CraneExpected = std::expected; AccountManager(); ~AccountManager() = default; - Result AddUser(User&& new_user); + CraneExpected AddUser(uint32_t uid, const User& new_user); + + CraneExpected AddAccount(uint32_t uid, const Account& new_account); - Result AddAccount(Account&& new_account); + CraneExpected AddQos(uint32_t uid, const Qos& new_qos); - Result AddQos(const Qos& new_qos); + CraneExpected DeleteUser(uint32_t uid, const std::string& name, + const std::string& account); - Result DeleteUser(const std::string& name, const std::string& account); + CraneExpected DeleteAccount(uint32_t uid, const std::string& name); - Result RemoveUserFromAccount(const std::string& name, - const std::string& account); + CraneExpected DeleteQos(uint32_t uid, const std::string& name); - Result DeleteAccount(const std::string& name); + CraneExpected QueryUserInfo( + uint32_t uid, const std::string& name, + std::unordered_map* res_user_map); - Result DeleteQos(const std::string& name); + CraneExpected QueryAccountInfo( + uint32_t uid, const std::string& name, + std::unordered_map* res_account_map); + + CraneExpected QueryQosInfo( + uint32_t uid, const std::string& name, + std::unordered_map* res_qos_map); UserMutexSharedPtr GetExistedUserInfo(const std::string& name); UserMapMutexSharedPtr GetAllUserInfo(); @@ -73,74 +83,69 @@ class AccountManager { QosMutexSharedPtr GetExistedQosInfo(const std::string& name); QosMapMutexSharedPtr GetAllQosInfo(); - Result ModifyUser( - const crane::grpc::ModifyEntityRequest_OperatorType& operatorType, + /* --------------------------------------------------------------------------- + * ModifyUser-related functions + * --------------------------------------------------------------------------- + */ + CraneExpected ModifyAdminLevel(uint32_t uid, const std::string& name, + const std::string& value); + CraneExpected ModifyUserDefaultQos(uint32_t uid, + const std::string& name, + const std::string& partition, + const std::string& account, + const std::string& value); + CraneExpected ModifyUserAllowedPartition( + crane::grpc::OperationType operation_type, uint32_t uid, + const std::string& name, const std::string& account, + const std::string& value); + CraneExpected ModifyUserAllowedQos( + crane::grpc::OperationType operation_type, uint32_t uid, const std::string& name, const std::string& partition, - std::string account, const std::string& item, const std::string& value, - bool force); - Result ModifyAccount( - const crane::grpc::ModifyEntityRequest_OperatorType& operatorType, - const std::string& name, const std::string& item, - const std::string& value, bool force); - - Result ModifyQos(const std::string& name, const std::string& item, - const std::string& value); - - Result BlockAccount(const std::string& name, bool block); - - Result BlockUser(const std::string& name, const std::string& account, - bool block); + const std::string& account, const std::string& value, bool force); + CraneExpected DeleteUserAllowedPartition(uint32_t uid, + const std::string& name, + const std::string& account, + const std::string& value); + CraneExpected DeleteUserAllowedQos( + uint32_t uid, const std::string& name, const std::string& partition, + const std::string& account, const std::string& value, bool force); + + CraneExpected ModifyAccount(crane::grpc::OperationType operation_type, + uint32_t uid, const std::string& name, + crane::grpc::ModifyField modify_field, + const std::string& value, bool force); + + CraneExpected ModifyQos(uint32_t uid, const std::string& name, + crane::grpc::ModifyField modify_field, + const std::string& value); + + CraneExpected BlockAccount(uint32_t uid, const std::string& name, + bool block); + + CraneExpected BlockUser(uint32_t uid, const std::string& name, + const std::string& account, bool block); bool CheckUserPermissionToPartition(const std::string& name, const std::string& account, const std::string& partition); - result::result CheckEnableState(const std::string& account, - const std::string& user); + result::result CheckIfUserOfAccountIsEnabled( + const std::string& user, const std::string& account); result::result CheckAndApplyQosLimitOnTask( const std::string& user, const std::string& account, TaskInCtld* task); - Result FindUserLevelAccountsOfUid(uint32_t uid, User::AdminLevel* level, - std::list* accounts); - result::result CheckUidIsAdmin(uint32_t uid); - /** - * @param[in] uid is system uid of user. - * @param[in] account is the target that uid wants to query or modify. - * If its value is an empty string, the permission check fails of course, - * but level_of_uid will be filled and thus the function serves as a - * query function for user level. - * @param[in] read_only_priv specifies the permission type. - * If true, the function checks if uid has read/query only permission - * to specified account and the check will pass - * if the list of accounts the uid belongs to, - * which includes all the accounts this uid coordinates - * (guaranteed by account rule), - * contains any account which is is the parent of target account - * (including itself). - * If false, the function checks if uid has read/query only permission - * to specified account and the check will pass - * if the list of accounts coordinated by uid, - * contains any account which is is the parent of target account - * (including itself). - * @param[out] level_of_uid will be written with the user level of uid - * when function returns if both uid and user information exist. - * @return True if both uid and corresponding user exists and the permission - * check is passed, otherwise False. - */ - AccountManager::Result HasPermissionToAccount( - uint32_t uid, const std::string& account, bool read_only_priv, - User::AdminLevel* level_of_uid = nullptr); - - AccountManager::Result HasPermissionToUser( - uint32_t uid, const std::string& target_user, bool read_only_priv, - User::AdminLevel* level_of_uid = nullptr); + CraneExpected CheckIfUidHasPermOnUser(uint32_t uid, + const std::string& username, + bool read_only_priv); private: void InitDataMap_(); + CraneExpected GetUserInfoByUidNoLock_(uint32_t uid); + const User* GetUserInfoNoLock_(const std::string& name); const User* GetExistedUserInfoNoLock_(const std::string& name); @@ -150,51 +155,159 @@ class AccountManager { const Qos* GetQosInfoNoLock_(const std::string& name); const Qos* GetExistedQosInfoNoLock_(const std::string& name); + /* --------------------------------------------------------------------------- + * ModifyUser-related functions(no lock) + * --------------------------------------------------------------------------- + */ + CraneExpected CheckAddUserAllowedPartitionNoLock_( + const User* user, const Account* account, const std::string& partition); + CraneExpected CheckSetUserAllowedPartitionNoLock_( + const Account* account, const std::string& partition); + CraneExpected CheckAddUserAllowedQosNoLock_( + const User* user, const Account* account, const std::string& partition, + const std::string& qos_str); + CraneExpected CheckSetUserAllowedQosNoLock_( + const User* user, const Account* account, const std::string& partition, + const std::string& qos_str, bool force); + CraneExpected CheckSetUserDefaultQosNoLock_( + const User& user, const std::string& account, + const std::string& partition, const std::string& qos); + CraneExpected CheckDeleteUserAllowedPartitionNoLock_( + const User& user, const std::string& account, + const std::string& partition); + CraneExpected CheckDeleteUserAllowedQosNoLock_( + const User& user, const std::string& account, + const std::string& partition, const std::string& qos, bool force); + + /* --------------------------------------------------------------------------- + * ModifyAccount-related functions(no lock) + * --------------------------------------------------------------------------- + */ + CraneExpected CheckAddAccountAllowedPartitionNoLock_( + const Account* account, const std::string& partition); + CraneExpected CheckAddAccountAllowedQosNoLock_(const Account* account, + const std::string& qos); + CraneExpected CheckSetAccountDescriptionNoLock_(const Account* account); + CraneExpected CheckSetAccountAllowedPartitionNoLock_( + const Account* account, const std::string& partitions, bool force); + CraneExpected CheckSetAccountAllowedQosNoLock_( + const Account* account, const std::string& qos_list, bool force); + CraneExpected CheckSetAccountDefaultQosNoLock_(const Account* account, + const std::string& qos); + CraneExpected CheckDeleteAccountAllowedPartitionNoLock_( + const Account* account, const std::string& partition, bool force); + CraneExpected CheckDeleteAccountAllowedQosNoLock_( + const Account* account, const std::string& qos, bool force); + + // Compare the user's permission levels for operations. + CraneExpected CheckIfUserHasHigherPrivThan_( + const User& op_user, User::AdminLevel admin_level); + + // Determine if the operating user has permissions for the account, + // e.g. admin or coordinator + CraneExpected CheckIfUserHasPermOnAccountNoLock_( + const User& op_user, const std::string& account, bool read_only_priv); + + /** + * Check whether the operating user has permissions to access the target user. + * Permissions are granted if any of the following three conditions are met: + * 1. The operating user is the same as the target user. + * 2. The operating user's level is higher than the target user's level. + * 3. The operating user is the coordinator of the target user's account. + * If the read_only_priv is true, it means the operating user is the + * coordinator of any target user's account. + */ + CraneExpected CheckIfUserHasPermOnUserNoLock_(const User& op_user, + const User* user, + bool read_only_priv); + + // Determine if the operating user has permissions for a specific account of a + // particular user. + // 1. The operating user's permissions are greater than the target user's. + // 2. The operating user is the coordinator of the account. + CraneExpected CheckIfUserHasPermOnUserOfAccountNoLock_( + const User& op_user, const User* user, std::string* account, + bool read_only_priv); + + CraneExpected CheckPartitionIsAllowedNoLock_( + const Account* account, const std::string& partition, bool check_parent, + bool is_user); + + CraneExpected CheckQosIsAllowedNoLock_(const Account* account, + const std::string& qos_str, + bool check_parent, bool is_user); + bool IncQosReferenceCountInDb_(const std::string& name, int num); - Result AddUserAllowedPartition_(const std::string& name, - const std::string& account, - const std::string& partition); - Result AddUserAllowedQos_(const std::string& name, const std::string& qos, - const std::string& account, - const std::string& partition); - - Result SetUserAdminLevel_(const std::string& name, const std::string& level); - Result SetUserDefaultQos_(const std::string& name, const std::string& qos, - const std::string& account, - const std::string& partition); - Result SetUserAllowedPartition_(const std::string& name, - const std::string& account, - const std::string& partitions); - Result SetUserAllowedQos_(const std::string& name, const std::string& account, - const std::string& partition, - const std::string& qos_list_str, bool force); - - Result DeleteUserAllowedPartition_(const std::string& name, - const std::string& account, - const std::string& partition); - Result DeleteUserAllowedQos_(const std::string& name, const std::string& qos, - const std::string& account, - const std::string& partition, bool force); - - Result AddAccountAllowedPartition_(const std::string& name, - const std::string& partition); - AccountManager::Result AddAccountAllowedQos_(const std::string& name, + CraneExpected AddUser_(const User& user, const Account* account, + const User* stale_user); + + CraneExpected AddAccount_(const Account& account, const Account* parent, + const Account* stale_account); + + CraneExpected AddQos_(const Qos& qos, const Qos* stale_qos); + + CraneExpected DeleteUser_(const User& user, const std::string& account); + + CraneExpected DeleteAccount_(const Account& account); + + CraneExpected DeleteQos_(const std::string& name); + + CraneExpected AddUserAllowedPartition_(const User& user, + const Account& account, + const std::string& partition); + CraneExpected AddUserAllowedQos_(const User& user, + const Account& account, + const std::string& partition, + const std::string& qos); + + CraneExpected SetUserAdminLevel_(const std::string& name, + User::AdminLevel new_level); + CraneExpected SetUserDefaultQos_(const User& user, + const std::string& account, + const std::string& partition, + const std::string& qos); + CraneExpected SetUserAllowedPartition_(const User& user, + const Account& account, + const std::string& partitions); + CraneExpected SetUserAllowedQos_(const User& user, + const Account& account, + const std::string& partition, + const std::string& qos_list_str, + bool force); + + CraneExpected DeleteUserAllowedPartition_(const User& user, + const std::string& account, + const std::string& partition); + CraneExpected DeleteUserAllowedQos_(const User& user, + const std::string& qos, + const std::string& account, + const std::string& partition, + bool force); + + CraneExpected AddAccountAllowedPartition_(const std::string& name, + const std::string& partition); + CraneExpected AddAccountAllowedQos_(const Account& account, + const std::string& qos); + + CraneExpected SetAccountDescription_(const std::string& name, + const std::string& description); + CraneExpected SetAccountDefaultQos_(const Account& account, + const std::string& qos); + CraneExpected SetAccountAllowedPartition_( + const Account& account, const std::string& partitions); + CraneExpected SetAccountAllowedQos_(const Account& account, + const std::string& qos_list_str); + + CraneExpected DeleteAccountAllowedPartition_( + const Account& account, const std::string& partition); + CraneExpected DeleteAccountAllowedQos_(const Account& account, const std::string& qos); - Result SetAccountDescription_(const std::string& name, - const std::string& description); - Result SetAccountDefaultQos_(const std::string& name, const std::string& qos); - Result SetAccountAllowedPartition_(const std::string& name, - const std::string& partitions, bool force); - Result SetAccountAllowedQos_(const std::string& name, - const std::string& qos_list_str, bool force); - - Result DeleteAccountAllowedPartition_(const std::string& name, - const std::string& partition, - bool force); - Result DeleteAccountAllowedQos_(const std::string& name, - const std::string& qos, bool force); + CraneExpected BlockUser_(const std::string& name, + const std::string& account, bool block); + + CraneExpected BlockAccount_(const std::string& name, bool block); bool IsAllowedPartitionOfAnyNodeNoLock_(const Account* account, const std::string& partition, diff --git a/src/CraneCtld/CtldGrpcServer.cpp b/src/CraneCtld/CtldGrpcServer.cpp index 817dff62f..724fcf2b3 100644 --- a/src/CraneCtld/CtldGrpcServer.cpp +++ b/src/CraneCtld/CtldGrpcServer.cpp @@ -286,15 +286,6 @@ grpc::Status CraneCtldServiceImpl::QueryTasksInfo( grpc::Status CraneCtldServiceImpl::AddAccount( grpc::ServerContext *context, const crane::grpc::AddAccountRequest *request, crane::grpc::AddAccountReply *response) { - AccountManager::Result judge_res = g_account_manager->HasPermissionToAccount( - request->uid(), request->account().parent_account(), false); - - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } - Account account; const crane::grpc::AccountInfo *account_info = &request->account(); @@ -309,13 +300,12 @@ grpc::Status CraneCtldServiceImpl::AddAccount( account.allowed_qos_list.emplace_back(qos); } - AccountManager::Result result = - g_account_manager->AddAccount(std::move(account)); - if (result.ok) { + auto result = g_account_manager->AddAccount(request->uid(), account); + if (result) { response->set_ok(true); } else { response->set_ok(false); - response->set_reason(result.reason); + response->set_reason(result.error()); } return grpc::Status::OK; @@ -324,25 +314,6 @@ grpc::Status CraneCtldServiceImpl::AddAccount( grpc::Status CraneCtldServiceImpl::AddUser( grpc::ServerContext *context, const crane::grpc::AddUserRequest *request, crane::grpc::AddUserReply *response) { - User::AdminLevel user_level; - AccountManager::Result judge_res = g_account_manager->HasPermissionToAccount( - request->uid(), request->user().account(), false, &user_level); - - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } - - if (static_cast(request->user().admin_level()) >= - static_cast(user_level)) { - response->set_ok(false); - response->set_reason( - "Permission error : You cannot add a user with the same or greater " - "permissions as yourself"); - return grpc::Status::OK; - } - User user; const crane::grpc::UserInfo *user_info = &request->user(); @@ -369,12 +340,13 @@ grpc::Status CraneCtldServiceImpl::AddUser( .allowed_partition_qos_map[apq.partition_name()]; } - AccountManager::Result result = g_account_manager->AddUser(std::move(user)); - if (result.ok) { + AccountManager::CraneExpected result = + g_account_manager->AddUser(request->uid(), user); + if (result) { response->set_ok(true); } else { response->set_ok(false); - response->set_reason(result.reason); + response->set_reason(result.error()); } return grpc::Status::OK; @@ -383,14 +355,6 @@ grpc::Status CraneCtldServiceImpl::AddUser( grpc::Status CraneCtldServiceImpl::AddQos( grpc::ServerContext *context, const crane::grpc::AddQosRequest *request, crane::grpc::AddQosReply *response) { - auto judge_res = g_account_manager->CheckUidIsAdmin(request->uid()); - - if (judge_res.has_error()) { - response->set_ok(false); - response->set_reason(judge_res.error()); - return grpc::Status::OK; - } - Qos qos; const crane::grpc::QosInfo *qos_info = &request->qos(); @@ -404,549 +368,292 @@ grpc::Status CraneCtldServiceImpl::AddQos( int64_t sec = qos_info->max_time_limit_per_task(); if (!CheckIfTimeLimitSecIsValid(sec)) { response->set_ok(false); - response->set_reason(fmt::format("Time limit should be in [{}, {}] seconds", - kTaskMinTimeLimitSec, - kTaskMaxTimeLimitSec)); + response->set_reason(AccountManager::CraneErrCode::ERR_TIME_LIMIT); return grpc::Status::OK; } qos.max_time_limit_per_task = absl::Seconds(sec); - AccountManager::Result result = g_account_manager->AddQos(qos); - if (result.ok) { + auto result = g_account_manager->AddQos(request->uid(), qos); + if (result) { response->set_ok(true); } else { response->set_ok(false); - response->set_reason(result.reason); + response->set_reason(result.error()); } return grpc::Status::OK; } -grpc::Status CraneCtldServiceImpl::ModifyEntity( +grpc::Status CraneCtldServiceImpl::ModifyAccount( grpc::ServerContext *context, - const crane::grpc::ModifyEntityRequest *request, - crane::grpc::ModifyEntityReply *response) { - AccountManager::Result judge_res; - AccountManager::Result modify_res; + const crane::grpc::ModifyAccountRequest *request, + crane::grpc::ModifyAccountReply *response) { + auto modify_res = g_account_manager->ModifyAccount( + request->type(), request->uid(), request->name(), request->modify_field(), + request->value(), request->force()); - switch (request->entity_type()) { - case crane::grpc::Account: - judge_res = g_account_manager->HasPermissionToAccount( - request->uid(), request->name(), false); + if (modify_res) { + response->set_ok(true); + } else { + response->set_ok(false); + response->set_reason(modify_res.error()); + } - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } - modify_res = g_account_manager->ModifyAccount( - request->type(), request->name(), request->item(), request->value(), - request->force()); - break; - case crane::grpc::User: { - AccountManager::UserMutexSharedPtr modifier_shared_ptr = - g_account_manager->GetExistedUserInfo(request->name()); - User::AdminLevel user_level; - judge_res = g_account_manager->HasPermissionToUser( - request->uid(), request->name(), false, &user_level); - - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } + return grpc::Status::OK; +} - if (modifier_shared_ptr->admin_level >= user_level) { - response->set_ok(false); - response->set_reason( - "Permission error : You cannot modify a user with the same or " - "greater permissions as yourself"); - return grpc::Status::OK; +grpc::Status CraneCtldServiceImpl::ModifyUser( + grpc::ServerContext *context, const crane::grpc::ModifyUserRequest *request, + crane::grpc::ModifyUserReply *response) { + AccountManager::CraneExpected modify_res; + + if (request->type() == crane::grpc::OperationType::Delete) { + switch (request->modify_field()) { + case crane::grpc::ModifyField::Partition: + modify_res = g_account_manager->DeleteUserAllowedPartition( + request->uid(), request->name(), request->account(), + request->value()); + break; + case crane::grpc::ModifyField::Qos: + modify_res = g_account_manager->DeleteUserAllowedQos( + request->uid(), request->name(), request->partition(), + request->account(), request->value(), request->force()); + break; + default: + std::unreachable(); } - if (request->item() == "admin_level") { - User::AdminLevel new_level; - if (request->value() == "none") { - new_level = User::None; - } else if (request->value() == "operator") { - new_level = User::Operator; - } else if (request->value() == "admin") { - new_level = User::Admin; - } else { - response->set_ok(false); - response->set_reason( - fmt::format("Unknown admin level '{}'", request->value())); - return grpc::Status::OK; - } - if (new_level > user_level) { - response->set_ok(false); - response->set_reason( - "Permission error : You cannot modify a user's permissions to " - "which greater than your own permissions"); - return grpc::Status::OK; - } + } else { + switch (request->modify_field()) { + case crane::grpc::ModifyField::AdminLevel: + modify_res = g_account_manager->ModifyAdminLevel( + request->uid(), request->name(), request->value()); + break; + case crane::grpc::ModifyField::Partition: + modify_res = g_account_manager->ModifyUserAllowedPartition( + request->type(), request->uid(), request->name(), request->account(), + request->value()); + break; + case crane::grpc::ModifyField::Qos: + modify_res = g_account_manager->ModifyUserAllowedQos( + request->type(), request->uid(), request->name(), + request->partition(), request->account(), request->value(), + request->force()); + break; + case crane::grpc::ModifyField::DefaultQos: + modify_res = g_account_manager->ModifyUserDefaultQos( + request->uid(), request->name(), request->partition(), + request->account(), request->value()); + break; + default: + std::unreachable(); } } - modify_res = g_account_manager->ModifyUser( - request->type(), request->name(), request->partition(), - request->account(), request->item(), request->value(), - request->force()); - break; - - case crane::grpc::Qos: { - auto res = g_account_manager->CheckUidIsAdmin(request->uid()); + if (modify_res) { + response->set_ok(true); + } else { + response->set_ok(false); + response->set_reason(modify_res.error()); + } - if (res.has_error()) { - response->set_ok(false); - response->set_reason(res.error()); - return grpc::Status::OK; - } + return grpc::Status::OK; +} - modify_res = g_account_manager->ModifyQos(request->name(), request->item(), - request->value()); - } break; +grpc::Status CraneCtldServiceImpl::ModifyQos( + grpc::ServerContext *context, const crane::grpc::ModifyQosRequest *request, + crane::grpc::ModifyQosReply *response) { + auto modify_res = + g_account_manager->ModifyQos(request->uid(), request->name(), + request->modify_field(), request->value()); - default: - break; - } - if (modify_res.ok) { + if (modify_res) { response->set_ok(true); } else { response->set_ok(false); - response->set_reason(modify_res.reason); + response->set_reason(modify_res.error()); } + return grpc::Status::OK; } -grpc::Status CraneCtldServiceImpl::QueryEntityInfo( +grpc::Status CraneCtldServiceImpl::QueryAccountInfo( grpc::ServerContext *context, - const crane::grpc::QueryEntityInfoRequest *request, - crane::grpc::QueryEntityInfoReply *response) { - User::AdminLevel user_level; - std::list user_accounts; + const crane::grpc::QueryAccountInfoRequest *request, + crane::grpc::QueryAccountInfoReply *response) { std::unordered_map res_account_map; - std::unordered_map res_user_map; - std::unordered_map res_qos_map; - - AccountManager::Result find_res = - g_account_manager->FindUserLevelAccountsOfUid(request->uid(), &user_level, - &user_accounts); - if (!find_res.ok) { + auto modify_res = g_account_manager->QueryAccountInfo( + request->uid(), request->name(), &res_account_map); + if (modify_res) { + response->set_ok(true); + } else { response->set_ok(false); - response->set_reason(find_res.reason); - return grpc::Status::OK; + response->set_reason(modify_res.error()); } - switch (request->entity_type()) { - case crane::grpc::Account: - if (request->name().empty()) { - AccountManager::AccountMapMutexSharedPtr account_map_shared_ptr = - g_account_manager->GetAllAccountInfo(); - if (account_map_shared_ptr) { - if (user_level != User::None) { - // If an administrator user queries account information, all - // accounts are returned, variable user_account not used - for (const auto &[name, account] : *account_map_shared_ptr) { - if (account->deleted) { - continue; - } - res_account_map.try_emplace(account->name, *account); - } - } else { - // Otherwise, only all sub-accounts under your own accounts will be - // returned - std::queue queue; - for (const auto &acct : user_accounts) { - // Z->A->B--->C->E - // |->D |->F - // If we query account C, [Z,A,B,C,E,F] is included. - std::string p_name = - account_map_shared_ptr->at(acct)->parent_account; - while (!p_name.empty()) { - res_account_map.try_emplace( - p_name, *(account_map_shared_ptr->at(p_name))); - p_name = account_map_shared_ptr->at(p_name)->parent_account; - } + for (const auto &it : res_account_map) { + const auto &account = it.second; + // put the account info into grpc element + auto *account_info = response->mutable_account_list()->Add(); + account_info->set_name(account.name); + account_info->set_description(account.description); - queue.push(acct); - while (!queue.empty()) { - std::string father = queue.front(); - res_account_map.try_emplace( - account_map_shared_ptr->at(father)->name, - *(account_map_shared_ptr->at(father))); - queue.pop(); - for (const auto &child : - account_map_shared_ptr->at(father)->child_accounts) { - queue.push(child); - } - } - } - } - response->set_ok(true); - } else { - response->set_ok(false); - response->set_reason("Can't find any account!"); - return grpc::Status::OK; - } - } else { - // Query an account - Account temp; - { - AccountManager::AccountMutexSharedPtr account_shared_ptr = - g_account_manager->GetExistedAccountInfo(request->name()); - if (!account_shared_ptr) { - response->set_ok(false); - response->set_reason( - fmt::format("Can't find account {}!", request->name())); - return grpc::Status::OK; - } - temp = *account_shared_ptr; - } - - AccountManager::Result judge_res = - g_account_manager->HasPermissionToAccount(request->uid(), - request->name(), true); - - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } - - res_account_map.emplace(temp.name, std::move(temp)); - response->set_ok(true); + auto *user_list = account_info->mutable_users(); + for (auto &&user : account.users) { + user_list->Add()->assign(user); } - for (const auto &it : res_account_map) { - const auto &account = it.second; - // put the account info into grpc element - auto *account_info = response->mutable_account_list()->Add(); - account_info->set_name(account.name); - account_info->set_description(account.description); - - auto *user_list = account_info->mutable_users(); - for (auto &&user : account.users) { - user_list->Add()->assign(user); - } - - auto *child_list = account_info->mutable_child_accounts(); - for (auto &&child : account.child_accounts) { - child_list->Add()->assign(child); - } - account_info->set_parent_account(account.parent_account); - - auto *partition_list = account_info->mutable_allowed_partitions(); - for (auto &&partition : account.allowed_partition) { - partition_list->Add()->assign(partition); - } - account_info->set_default_qos(account.default_qos); - account_info->set_blocked(account.blocked); - - auto *allowed_qos_list = account_info->mutable_allowed_qos_list(); - for (auto &&qos : account.allowed_qos_list) { - allowed_qos_list->Add()->assign(qos); - } - - auto *coordinators = account_info->mutable_coordinators(); - for (auto &&coord : account.coordinators) { - coordinators->Add()->assign(coord); - } + auto *child_list = account_info->mutable_child_accounts(); + for (auto &&child : account.child_accounts) { + child_list->Add()->assign(child); } - break; - case crane::grpc::User: - if (request->name().empty()) { - AccountManager::UserMapMutexSharedPtr user_map_shared_ptr = - g_account_manager->GetAllUserInfo(); - - if (user_map_shared_ptr) { - if (user_level != User::None) { - // The rules for querying user information are the same as those for - // querying accounts - for (const auto &[user_name, user] : *user_map_shared_ptr) { - if (user->deleted) { - continue; - } - res_user_map.try_emplace(user->uid, *user); - } - } else { - AccountManager::AccountMapMutexSharedPtr account_map_shared_ptr = - g_account_manager->GetAllAccountInfo(); - - std::queue queue; - for (const auto &acct : user_accounts) { - queue.push(acct); - while (!queue.empty()) { - std::string father = queue.front(); - for (const auto &user : - account_map_shared_ptr->at(father)->users) { - res_user_map.try_emplace(user_map_shared_ptr->at(user)->uid, - *(user_map_shared_ptr->at(user))); - } - queue.pop(); - for (const auto &child : - account_map_shared_ptr->at(father)->child_accounts) { - queue.push(child); - } - } - } - } - response->set_ok(true); - } else { - response->set_ok(false); - response->set_reason("Can't find any user!"); - return grpc::Status::OK; - } + account_info->set_parent_account(account.parent_account); - } else { - AccountManager::UserMutexSharedPtr user_shared_ptr = - g_account_manager->GetExistedUserInfo(request->name()); - if (user_shared_ptr) { - AccountManager::Result judge_res = - g_account_manager->HasPermissionToUser(request->uid(), - request->name(), true); - - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } - - res_user_map.try_emplace(user_shared_ptr->uid, *user_shared_ptr); - response->set_ok(true); - } else { - response->set_ok(false); - response->set_reason( - fmt::format("Can't find user {}", request->name())); - return grpc::Status::OK; - } + auto *partition_list = account_info->mutable_allowed_partitions(); + for (auto &&partition : account.allowed_partition) { + partition_list->Add()->assign(partition); } + account_info->set_default_qos(account.default_qos); + account_info->set_blocked(account.blocked); - for (const auto &it : res_user_map) { - const auto &user = it.second; - for (const auto &[account, item] : user.account_to_attrs_map) { - if (!request->account().empty() && account != request->account()) { - continue; - } - auto *user_info = response->mutable_user_list()->Add(); - user_info->set_name(user.name); - user_info->set_uid(user.uid); - if (account == user.default_account) { - user_info->set_account(account + '*'); - } else { - user_info->set_account(account); - } - user_info->set_admin_level( - (crane::grpc::UserInfo_AdminLevel)user.admin_level); - user_info->set_blocked(item.blocked); - - auto *partition_qos_list = - user_info->mutable_allowed_partition_qos_list(); - for (const auto &[par_name, pair] : item.allowed_partition_qos_map) { - auto *partition_qos = partition_qos_list->Add(); - partition_qos->set_partition_name(par_name); - partition_qos->set_default_qos(pair.first); - - auto *qos_list = partition_qos->mutable_qos_list(); - for (const auto &qos : pair.second) { - qos_list->Add()->assign(qos); - } - } - - auto *coordinated_accounts = user_info->mutable_coordinator_accounts(); - for (auto &&coord : user.coordinator_accounts) { - coordinated_accounts->Add()->assign(coord); - } - } + auto *allowed_qos_list = account_info->mutable_allowed_qos_list(); + for (const auto &qos : account.allowed_qos_list) { + allowed_qos_list->Add()->assign(qos); } - break; - case crane::grpc::Qos: { - PasswordEntry entry(request->uid()); - AccountManager::UserMutexSharedPtr user_shared_ptr = - g_account_manager->GetExistedUserInfo(entry.Username()); - if (!user_shared_ptr) { - response->set_ok(false); - response->set_reason( - fmt::format("User {} is not a user of Crane.", entry.Username())); - return grpc::Status::OK; + auto *coordinators = account_info->mutable_coordinators(); + for (auto &&coord : account.coordinators) { + coordinators->Add()->assign(coord); } + } - if (request->name().empty()) { - AccountManager::QosMapMutexSharedPtr qos_map_shared_ptr = - g_account_manager->GetAllQosInfo(); + return grpc::Status::OK; +} - if (qos_map_shared_ptr) { - if (user_level != User::None) { - for (const auto &[name, qos] : *qos_map_shared_ptr) { - if (qos->deleted) continue; - res_qos_map[name] = *qos; - } - } else { - for (const auto &[acct, item] : - user_shared_ptr->account_to_attrs_map) { - for (const auto &[part, part_qos_map] : - item.allowed_partition_qos_map) { - for (const auto &qos : part_qos_map.second) { - res_qos_map[qos] = *(qos_map_shared_ptr->at(qos)); - } - } - } - } - } else { - response->set_ok(false); - response->set_reason("Can't find any QOS!"); - return grpc::Status::OK; +grpc::Status CraneCtldServiceImpl::QueryUserInfo( + grpc::ServerContext *context, + const crane::grpc::QueryUserInfoRequest *request, + crane::grpc::QueryUserInfoReply *response) { + std::unordered_map res_user_map; + auto modify_res = g_account_manager->QueryUserInfo( + request->uid(), request->name(), &res_user_map); + if (modify_res) { + response->set_ok(true); + } else { + response->set_ok(false); + response->set_reason(modify_res.error()); + } + + for (const auto &it : res_user_map) { + const auto &user = it.second; + for (const auto &[account, item] : user.account_to_attrs_map) { + if (!request->account().empty() && account != request->account()) { + continue; } - } else { - AccountManager::QosMutexSharedPtr qos_shared_ptr = - g_account_manager->GetExistedQosInfo(request->name()); - if (!qos_shared_ptr) { - response->set_ok(false); - response->set_reason( - fmt::format("Can't find QOS {}!", request->name())); - return grpc::Status::OK; + auto *user_info = response->mutable_user_list()->Add(); + user_info->set_name(user.name); + user_info->set_uid(user.uid); + if (account == user.default_account) { + user_info->set_account(account + '*'); + } else { + user_info->set_account(account); } - - if (user_level == User::None) { - bool found = false; - for (const auto &[acct, item] : user_shared_ptr->account_to_attrs_map) { - for (const auto &[part, part_qos_map] : - item.allowed_partition_qos_map) { - for (const auto &qos : part_qos_map.second) { - if (qos == request->name()) found = true; - } - } - } - - if (!found) { - response->set_ok(false); - response->set_reason( - fmt::format("User {} is not allowed to access qos {} which is " - "not in allowed qos list", - entry.Username(), request->name())); - return grpc::Status::OK; + user_info->set_admin_level( + (crane::grpc::UserInfo_AdminLevel)user.admin_level); + user_info->set_blocked(item.blocked); + + auto *partition_qos_list = + user_info->mutable_allowed_partition_qos_list(); + for (const auto &[par_name, pair] : item.allowed_partition_qos_map) { + auto *partition_qos = partition_qos_list->Add(); + partition_qos->set_partition_name(par_name); + partition_qos->set_default_qos(pair.first); + + auto *qos_list = partition_qos->mutable_qos_list(); + for (const auto &qos : pair.second) { + qos_list->Add()->assign(qos); } } - res_qos_map[request->name()] = *qos_shared_ptr; - } - - response->set_ok(true); - auto *list = response->mutable_qos_list(); - for (const auto &[name, qos] : res_qos_map) { - auto *qos_info = list->Add(); - qos_info->set_name(qos.name); - qos_info->set_description(qos.description); - qos_info->set_priority(qos.priority); - qos_info->set_max_jobs_per_user(qos.max_jobs_per_user); - qos_info->set_max_cpus_per_user(qos.max_cpus_per_user); - qos_info->set_max_time_limit_per_task( - absl::ToInt64Seconds(qos.max_time_limit_per_task)); + auto *coordinated_accounts = user_info->mutable_coordinator_accounts(); + for (auto &&coord : user.coordinator_accounts) { + coordinated_accounts->Add()->assign(coord); + } } } - default: - break; - } + return grpc::Status::OK; } -grpc::Status CraneCtldServiceImpl::DeleteEntity( +grpc::Status CraneCtldServiceImpl::QueryQosInfo( grpc::ServerContext *context, - const crane::grpc::DeleteEntityRequest *request, - crane::grpc::DeleteEntityReply *response) { - User::AdminLevel user_level; - AccountManager::Result res; + const crane::grpc::QueryQosInfoRequest *request, + crane::grpc::QueryQosInfoReply *response) { + std::unordered_map res_qos_map; - switch (request->entity_type()) { - case crane::grpc::User: { - AccountManager::UserMutexSharedPtr deleter_shared_ptr = - g_account_manager->GetExistedUserInfo(request->name()); - if (!deleter_shared_ptr) { - response->set_ok(false); - response->set_reason( - fmt::format("User '{}' is not a crane user", request->name())); - return grpc::Status::OK; - } + auto modify_res = g_account_manager->QueryQosInfo( + request->uid(), request->name(), &res_qos_map); + if (modify_res) { + response->set_ok(true); + } else { + response->set_ok(false); + response->set_reason(modify_res.error()); + } - if (request->account().empty()) { - // Remove user from all of it's accounts - AccountManager::Result judge_res = - g_account_manager->HasPermissionToAccount( - request->uid(), deleter_shared_ptr->default_account, false, - &user_level); - if (user_level == User::None) { - if (deleter_shared_ptr->account_to_attrs_map.size() != 1) { - response->set_ok(false); - response->set_reason( - "Permission error : You can't remove user form more than one " - "account at a time"); - return grpc::Status::OK; - } else { - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } - } - } + auto *list = response->mutable_qos_list(); + for (const auto &[name, qos] : res_qos_map) { + auto *qos_info = list->Add(); + qos_info->set_name(qos.name); + qos_info->set_description(qos.description); + qos_info->set_priority(qos.priority); + qos_info->set_max_jobs_per_user(qos.max_jobs_per_user); + qos_info->set_max_cpus_per_user(qos.max_cpus_per_user); + qos_info->set_max_time_limit_per_task( + absl::ToInt64Seconds(qos.max_time_limit_per_task)); + } - } else { - // Remove user from specific account - AccountManager::Result judge_res = - g_account_manager->HasPermissionToAccount( - request->uid(), request->account(), false, &user_level); - - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } - } + return grpc::Status::OK; +} - if (user_level <= deleter_shared_ptr->admin_level) { - response->set_ok(false); - response->set_reason( - "Permission error : You cannot delete a user with the same or " - "greater permissions as yourself"); - return grpc::Status::OK; - } +grpc::Status CraneCtldServiceImpl::DeleteAccount( + grpc::ServerContext *context, + const crane::grpc::DeleteAccountRequest *request, + crane::grpc::DeleteAccountReply *response) { + auto res = g_account_manager->DeleteAccount(request->uid(), request->name()); + if (res) { + response->set_ok(true); + } else { + response->set_ok(false); + response->set_reason(res.error()); } - res = g_account_manager->DeleteUser(request->name(), request->account()); - break; - case crane::grpc::Account: { - AccountManager::Result judge_res = - g_account_manager->HasPermissionToAccount(request->uid(), - request->name(), false); + return grpc::Status::OK; +} - if (!judge_res.ok) { - response->set_ok(false); - response->set_reason(judge_res.reason); - return grpc::Status::OK; - } +grpc::Status CraneCtldServiceImpl::DeleteUser( + grpc::ServerContext *context, const crane::grpc::DeleteUserRequest *request, + crane::grpc::DeleteUserReply *response) { + auto res = g_account_manager->DeleteUser(request->uid(), request->name(), + request->account()); + if (res) { + response->set_ok(true); + } else { + response->set_ok(false); + response->set_reason(res.error()); } - res = g_account_manager->DeleteAccount(request->name()); - break; - case crane::grpc::Qos: { - auto judge_res = g_account_manager->CheckUidIsAdmin(request->uid()); - if (judge_res.has_error()) { - response->set_ok(false); - response->set_reason(judge_res.error()); - return grpc::Status::OK; - } - } - res = g_account_manager->DeleteQos(request->name()); - break; - default: - break; - } + return grpc::Status::OK; +} - if (res.ok) { +grpc::Status CraneCtldServiceImpl::DeleteQos( + grpc::ServerContext *context, const crane::grpc::DeleteQosRequest *request, + crane::grpc::DeleteQosReply *response) { + auto res = g_account_manager->DeleteQos(request->uid(), request->name()); + if (res) { response->set_ok(true); } else { response->set_ok(false); - response->set_reason(res.reason); + response->set_reason(res.error()); } + return grpc::Status::OK; } @@ -954,39 +661,28 @@ grpc::Status CraneCtldServiceImpl::BlockAccountOrUser( grpc::ServerContext *context, const crane::grpc::BlockAccountOrUserRequest *request, crane::grpc::BlockAccountOrUserReply *response) { - AccountManager::Result res; + AccountManager::CraneExpected res; switch (request->entity_type()) { case crane::grpc::Account: - res = g_account_manager->HasPermissionToAccount(request->uid(), - request->name(), false); - - if (!res.ok) { - response->set_ok(false); - response->set_reason(res.reason); - return grpc::Status::OK; - } - res = g_account_manager->BlockAccount(request->name(), request->block()); - response->set_ok(res.ok); - response->set_reason(res.reason); + res = g_account_manager->BlockAccount(request->uid(), request->name(), + request->block()); break; case crane::grpc::User: - res = g_account_manager->HasPermissionToUser(request->uid(), - request->name(), false); - - if (!res.ok) { - response->set_ok(false); - response->set_reason(res.reason); - return grpc::Status::OK; - } - res = g_account_manager->BlockUser(request->name(), request->account(), - request->block()); - response->set_ok(res.ok); - response->set_reason(res.reason); + res = g_account_manager->BlockUser(request->uid(), request->name(), + request->account(), request->block()); break; default: - break; + std::unreachable(); } + + if (res) { + response->set_ok(true); + } else { + response->set_ok(false); + response->set_reason(res.error()); + } + return grpc::Status::OK; } @@ -1267,7 +963,8 @@ CtldServer::SubmitTaskToScheduler(std::unique_ptr task) { } auto enable_res = - g_account_manager->CheckEnableState(task->account, task->Username()); + g_account_manager->CheckIfUserOfAccountIsEnabled( + task->Username(), task->account); if (enable_res.has_error()) { return result::fail(enable_res.error()); } diff --git a/src/CraneCtld/CtldGrpcServer.h b/src/CraneCtld/CtldGrpcServer.h index 4d29d78b5..2099602c2 100644 --- a/src/CraneCtld/CtldGrpcServer.h +++ b/src/CraneCtld/CtldGrpcServer.h @@ -44,9 +44,8 @@ class CforedStreamWriter { crane::grpc::StreamCforedRequest> *stream) : m_stream_(stream), m_valid_(true) {} - bool WriteTaskIdReply( - pid_t calloc_pid, - result::result res) { + bool WriteTaskIdReply(pid_t calloc_pid, + result::result res) { LockGuard guard(&m_stream_mtx_); if (!m_valid_) return false; @@ -66,8 +65,11 @@ class CforedStreamWriter { return m_stream_->Write(reply); } - bool WriteTaskResAllocReply(task_id_t task_id, - result::result>, std::string> res) { + bool WriteTaskResAllocReply( + task_id_t task_id, + result::result>, + std::string> + res) { LockGuard guard(&m_stream_mtx_); if (!m_valid_) return false; @@ -78,8 +80,12 @@ class CforedStreamWriter { if (res.has_value()) { task_res_alloc_reply->set_ok(true); - task_res_alloc_reply->set_allocated_craned_regex(std::move(res.value().first)); - std::ranges::for_each(res.value().second,[&task_res_alloc_reply](const auto& craned_id){task_res_alloc_reply->add_craned_ids(craned_id);}); + task_res_alloc_reply->set_allocated_craned_regex( + std::move(res.value().first)); + std::ranges::for_each(res.value().second, + [&task_res_alloc_reply](const auto &craned_id) { + task_res_alloc_reply->add_craned_ids(craned_id); + }); } else { task_res_alloc_reply->set_ok(false); task_res_alloc_reply->set_failure_reason(std::move(res.error())); @@ -91,7 +97,8 @@ class CforedStreamWriter { bool WriteTaskCompletionAckReply(task_id_t task_id) { LockGuard guard(&m_stream_mtx_); if (!m_valid_) return false; - CRANE_TRACE("Sending TaskCompletionAckReply to cfored of task id {}",task_id); + CRANE_TRACE("Sending TaskCompletionAckReply to cfored of task id {}", + task_id); StreamCtldReply reply; reply.set_type(StreamCtldReply::TASK_COMPLETION_ACK_REPLY); @@ -234,18 +241,46 @@ class CraneCtldServiceImpl final : public crane::grpc::CraneCtld::Service { const crane::grpc::AddQosRequest *request, crane::grpc::AddQosReply *response) override; - grpc::Status ModifyEntity(grpc::ServerContext *context, - const crane::grpc::ModifyEntityRequest *request, - crane::grpc::ModifyEntityReply *response) override; + grpc::Status ModifyAccount( + grpc::ServerContext *context, + const crane::grpc::ModifyAccountRequest *request, + crane::grpc::ModifyAccountReply *response) override; + + grpc::Status ModifyUser(grpc::ServerContext *context, + const crane::grpc::ModifyUserRequest *request, + crane::grpc::ModifyUserReply *response) override; + + grpc::Status ModifyQos(grpc::ServerContext *context, + const crane::grpc::ModifyQosRequest *request, + crane::grpc::ModifyQosReply *response) override; + + grpc::Status QueryAccountInfo( + grpc::ServerContext *context, + const crane::grpc::QueryAccountInfoRequest *request, + crane::grpc::QueryAccountInfoReply *response) override; - grpc::Status QueryEntityInfo( + grpc::Status QueryUserInfo( grpc::ServerContext *context, - const crane::grpc::QueryEntityInfoRequest *request, - crane::grpc::QueryEntityInfoReply *response) override; + const crane::grpc::QueryUserInfoRequest *request, + crane::grpc::QueryUserInfoReply *response) override; + + grpc::Status QueryQosInfo(grpc::ServerContext *context, + const crane::grpc::QueryQosInfoRequest *request, + crane::grpc::QueryQosInfoReply *response) override; + + grpc::Status DeleteAccount( + grpc::ServerContext *context, + const crane::grpc::DeleteAccountRequest *request, + crane::grpc::DeleteAccountReply *response) override; + + grpc::Status DeleteUser(grpc::ServerContext *context, + const crane::grpc::DeleteUserRequest *request, + crane::grpc::DeleteUserReply *response) override; + + grpc::Status DeleteQos(grpc::ServerContext *context, + const crane::grpc::DeleteQosRequest *request, + crane::grpc::DeleteQosReply *response) override; - grpc::Status DeleteEntity(grpc::ServerContext *context, - const crane::grpc::DeleteEntityRequest *request, - crane::grpc::DeleteEntityReply *response) override; grpc::Status BlockAccountOrUser( grpc::ServerContext *context, const crane::grpc::BlockAccountOrUserRequest *request, diff --git a/src/CraneCtld/CtldPreCompiledHeader.h b/src/CraneCtld/CtldPreCompiledHeader.h index 5bf3cf263..4560c705b 100644 --- a/src/CraneCtld/CtldPreCompiledHeader.h +++ b/src/CraneCtld/CtldPreCompiledHeader.h @@ -48,6 +48,7 @@ #include #include #include +#include // backward-cpp #include diff --git a/src/CraneCtld/TaskScheduler.cpp b/src/CraneCtld/TaskScheduler.cpp index 61ec39645..58efa3541 100644 --- a/src/CraneCtld/TaskScheduler.cpp +++ b/src/CraneCtld/TaskScheduler.cpp @@ -1251,9 +1251,9 @@ crane::grpc::CancelTaskReply TaskScheduler::CancelPendingOrRunningTask( CRANE_ASSERT(it != m_pending_task_map_.end()); TaskInCtld* task = it->second.get(); - auto result = g_account_manager->HasPermissionToUser( + auto result = g_account_manager->CheckIfUidHasPermOnUser( operator_uid, task->Username(), false); - if (!result.ok) { + if (!result) { reply.add_not_cancelled_tasks(task_id); reply.add_not_cancelled_reasons("Permission Denied."); } else { @@ -1273,9 +1273,9 @@ crane::grpc::CancelTaskReply TaskScheduler::CancelPendingOrRunningTask( CRANE_TRACE("Cancelling running task #{}", task_id); - auto result = g_account_manager->HasPermissionToUser( + auto result = g_account_manager->CheckIfUidHasPermOnUser( operator_uid, task->Username(), false); - if (!result.ok) { + if (!result) { reply.add_not_cancelled_tasks(task_id); reply.add_not_cancelled_reasons("Permission Denied."); } else {