From c946713e634078ce2902db77cd54dd6e0b98b828 Mon Sep 17 00:00:00 2001 From: boshengchen Date: Wed, 19 Jun 2019 15:20:21 +0800 Subject: [PATCH] User manager in meta (#400) * User manager of Meta Server * 1, Changed user data transport protocol from dataman to thrift. 2, Removed user property email and phone. 3, Added new user property lock status * 1,Refactor user processors code structure. 2,Remove user properties first name and last name. 3, Add new user properties of resource limit. 4, Changed related thrift structure from 'name' to 'id', 5,Add some comments for role. * To improve the drop space , on't delete useless role data * Rebased and added (raftex_obj raftex_thrift_obj wal_obj time_obj) to authentication_test module * Rebased and resolved some conflicts * Address laura-ding's comment * Resolved code alignment --- src/common/base/Status.h | 2 + src/common/base/ThriftTypes.h | 1 + src/graph/PermissionManager.h | 47 +++ src/interface/common.thrift | 2 + src/interface/meta.thrift | 117 +++++++ src/meta/CMakeLists.txt | 1 + src/meta/MetaServiceHandler.cpp | 61 ++++ src/meta/MetaServiceHandler.h | 33 ++ src/meta/MetaServiceUtils.cpp | 122 +++++++ src/meta/MetaServiceUtils.h | 28 ++ src/meta/processors/BaseProcessor.h | 23 ++ src/meta/processors/BaseProcessor.inl | 49 +++ .../partsMan/DropSpaceProcessor.cpp | 2 + .../usersMan/AuthenticationProcessor.cpp | 271 +++++++++++++++ .../usersMan/AuthenticationProcessor.h | 155 +++++++++ src/meta/test/AuthProcessorTest.cpp | 326 ++++++++++++++++++ src/meta/test/CMakeLists.txt | 30 ++ src/meta/test/TestUtils.h | 34 +- 18 files changed, 1303 insertions(+), 1 deletion(-) create mode 100644 src/graph/PermissionManager.h create mode 100644 src/meta/processors/usersMan/AuthenticationProcessor.cpp create mode 100644 src/meta/processors/usersMan/AuthenticationProcessor.h create mode 100644 src/meta/test/AuthProcessorTest.cpp diff --git a/src/common/base/Status.h b/src/common/base/Status.h index e7c4491a11c..a08f065c1cf 100644 --- a/src/common/base/Status.h +++ b/src/common/base/Status.h @@ -102,6 +102,7 @@ class Status final { STATUS_GENERATOR(HostNotFound); STATUS_GENERATOR(TagNotFound); STATUS_GENERATOR(EdgeNotFound); + STATUS_GENERATOR(UserNotFound); #undef STATUS_GENERATOR @@ -128,6 +129,7 @@ class Status final { kHostNotFound = 405, kTagNotFound = 406, kEdgeNotFound = 407, + kUserNotFound = 408, }; Code code() const { diff --git a/src/common/base/ThriftTypes.h b/src/common/base/ThriftTypes.h index daa553b29d9..f193278353f 100644 --- a/src/common/base/ThriftTypes.h +++ b/src/common/base/ThriftTypes.h @@ -25,6 +25,7 @@ using EdgeType = int32_t; using EdgeRanking = int64_t; using EdgeVersion = int64_t; using SchemaVer = int64_t; +using UserID = int32_t; } // namespace nebula #endif // COMMON_BASE_THRIFTTYPES_H_ diff --git a/src/graph/PermissionManager.h b/src/graph/PermissionManager.h new file mode 100644 index 00000000000..5fd18579572 --- /dev/null +++ b/src/graph/PermissionManager.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2019 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + + +#ifndef GRAPH_PERMISSIONMANAGER_H +#define GRAPH_PERMISSIONMANAGER_H + +// Operation and permission define: +// Operation | GOD | ADMIN | USER | GUEST +// ---------------- | ------------- | ------------- | ------------- | ------------- +// kGo | Y | Y | Y | Y +// kSet | Y | Y | Y | Y +// kPipe | Y | Y | Y | Y +// kUse | Y | Y | Y | Y +// kMatch | Y | Y | Y | Y +// kAssignment | Y | Y | Y | Y +// kCreateTag | Y | Y | | +// kAlterTag | Y | Y | | +// kCreateEdge | Y | Y | | +// kAlterEdge | Y | Y | | +// kDescribeTag | Y | Y | Y | Y +// kDescribeEdge | Y | Y | Y | Y +// kRemoveTag | Y | Y | | +// kRemoveEdge | Y | Y | | +// kInsertVertex | Y | Y | Y | +// kInsertEdge | Y | Y | Y | +// kShow | Y | Y | Y | Y +// kDeleteVertex | Y | Y | Y | +// kDeleteEdge | Y | Y | Y | +// kFind | Y | Y | Y | Y +// kAddHosts | Y | | | +// kRemoveHosts | Y | | | +// kCreateSpace | Y | | | +// kDropSpace | Y | Y | | +// kYield | Y | Y | Y | Y +// kCreateUser | Y | | | +// kDropUser | Y | | | +// kAlterUser | Y | Y | Y | Y +// kGrant | Y | Y | | +// kRevoke | Y | Y | | +// kChangePassword | Y | Y | Y | Y + + +#endif // GRAPH_PERMISSIONMANAGER_H diff --git a/src/interface/common.thrift b/src/interface/common.thrift index 696bb726ca1..0472528e897 100644 --- a/src/interface/common.thrift +++ b/src/interface/common.thrift @@ -23,6 +23,8 @@ typedef i32 (cpp.type = "nebula::Port") Port typedef i64 (cpp.type = "nebula::SchemaVer") SchemaVer +typedef i32 (cpp.type = "nebula::UserID") UserID + // These are all data types supported in the graph properties enum SupportedType { UNKNOWN = 0, diff --git a/src/interface/meta.thrift b/src/interface/meta.thrift index a980f44148d..f42c6d85edc 100644 --- a/src/interface/meta.thrift +++ b/src/interface/meta.thrift @@ -31,6 +31,9 @@ enum ErrorCode { E_STORE_FAILURE = -31, E_STORE_SEGMENT_ILLEGAL = -32, + E_INVALID_PASSWORD = -41, + E_INPROPER_ROLE = -42, + E_UNKNOWN = -99, } (cpp.enum_strict) @@ -42,11 +45,27 @@ enum AlterSchemaOp { UNKNOWN = 0x04, } (cpp.enum_strict) +/** +** GOD is A global senior administrator.like root of Linux systems. +** ADMIN is an administrator for a given Graph Space. +** USER is a normal user for a given Graph Space. A User can access (read and write) the data in the Graph Space. +** GUEST is a read-only role for a given Graph Space. A Guest cannot modify the data in the Graph Space. +** Refer to header file src/graph/PermissionManager.h for details. +**/ + +enum RoleType { + GOD = 0x01, + ADMIN = 0x02, + USER = 0x03, + GUEST = 0x04, +} (cpp.enum_strict) + union ID { 1: common.GraphSpaceID space_id, 2: common.TagID tag_id, 3: common.EdgeType edge_type, + 4: common.UserID user_id, } struct IdName { @@ -100,6 +119,26 @@ struct HostItem { 2: HostStatus status, } +struct UserItem { + 1: string account; + // Disable user if lock status is true. + 2: bool is_lock, + // The number of queries an account can issue per hour + 3: i32 max_queries_per_hour, + // The number of updates an account can issue per hour + 4: i32 max_updates_per_hour, + // The number of times an account can connect to the server per hour + 5: i32 max_connections_per_hour, + // The number of simultaneous connections to the server by an account + 6: i32 max_user_connections, +} + +struct RoleItem { + 1: common.UserID user_id, + 2: common.GraphSpaceID space_id, + 3: RoleType role_type, +} + struct ExecResp { 1: ErrorCode code, // For custom kv operations, it is useless. @@ -311,6 +350,72 @@ struct HBReq { 1: common.HostAddr host, } +struct CreateUserReq { + 1: UserItem user, + 2: string encoded_pwd, + 3: bool missing_ok, +} + +struct DropUserReq { + 1: string account, + 2: bool missing_ok, +} + +struct AlterUserReq { + 1: UserItem user_item, +} + +struct GrantRoleReq { + 1: RoleItem role_item, +} + +struct RevokeRoleReq { + 1: RoleItem role_item, +} + +struct GetUserReq { + 1: string account, +} + +struct GetUserResp { + 1: ErrorCode code, + // Valid if ret equals E_LEADER_CHANGED. + 2: common.HostAddr leader, + 3: UserItem user_item, +} + +struct ListUsersReq { +} + +struct ListUsersResp { + 1: ErrorCode code, + // Valid if ret equals E_LEADER_CHANGED. + 2: common.HostAddr leader, + 3: map(cpp.template = "std::unordered_map") users, +} + +struct ListRolesReq { + 1: common.GraphSpaceID space_id, +} + +struct ListRolesResp { + 1: ErrorCode code, + // Valid if ret equals E_LEADER_CHANGED. + 2: common.HostAddr leader, + 3: list roles, +} + +struct ChangePasswordReq { + 1: string account, + 2: string new_encoded_pwd, + 3: string old_encoded_pwd, +} + +struct CheckPasswordReq { + 1: string account, + 2: string encoded_pwd, +} + service MetaService { ExecResp createSpace(1: CreateSpaceReq req); ExecResp dropSpace(1: DropSpaceReq req); @@ -343,5 +448,17 @@ service MetaService { ScanResp scan(1: ScanReq req); HBResp heartBeat(1: HBReq req); + + ExecResp createUser(1: CreateUserReq req); + ExecResp dropUser(1: DropUserReq req); + ExecResp alterUser(1: AlterUserReq req); + ExecResp grantRole(1: GrantRoleReq req); + ExecResp revokeRole(1: RevokeRoleReq req); + GetUserResp getUser(1: GetUserReq req); + ListUsersResp listUsers(1: ListUsersReq req); + ListRolesResp listRoles(1: ListRolesReq req); + ExecResp changePassword(1: ChangePasswordReq req); + ExecResp checkPassword(1: CheckPasswordReq req); + } diff --git a/src/meta/CMakeLists.txt b/src/meta/CMakeLists.txt index 6f0e1fcf61f..7d35f54134f 100644 --- a/src/meta/CMakeLists.txt +++ b/src/meta/CMakeLists.txt @@ -37,6 +37,7 @@ add_library( processors/customKV/RemoveRangeProcessor.cpp processors/customKV/ScanProcessor.cpp processors/admin/HBProcessor.cpp + processors/usersMan/AuthenticationProcessor.cpp ) add_dependencies( meta_service_handler diff --git a/src/meta/MetaServiceHandler.cpp b/src/meta/MetaServiceHandler.cpp index 8eea9834799..c3cde610ef0 100644 --- a/src/meta/MetaServiceHandler.cpp +++ b/src/meta/MetaServiceHandler.cpp @@ -31,6 +31,7 @@ #include "meta/processors/customKV/RemoveProcessor.h" #include "meta/processors/customKV/RemoveRangeProcessor.h" #include "meta/processors/admin/HBProcessor.h" +#include "meta/processors/usersMan/AuthenticationProcessor.h" #define RETURN_FUTURE(processor) \ auto f = processor->getFuture(); \ @@ -190,5 +191,65 @@ MetaServiceHandler::future_heartBeat(const cpp2::HBReq& req) { RETURN_FUTURE(processor); } +folly::Future +MetaServiceHandler::future_createUser(const cpp2::CreateUserReq& req) { + auto* processor = CreateUserProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_dropUser(const cpp2::DropUserReq& req) { + auto* processor = DropUserProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_alterUser(const cpp2::AlterUserReq& req) { + auto* processor = AlterUserProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_grantRole(const cpp2::GrantRoleReq& req) { + auto* processor = GrantProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_revokeRole(const cpp2::RevokeRoleReq& req) { + auto* processor = RevokeProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_getUser(const cpp2::GetUserReq& req) { + auto* processor = GetUserProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_listUsers(const cpp2::ListUsersReq& req) { + auto* processor = ListUsersProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_listRoles(const cpp2::ListRolesReq& req) { + auto* processor = ListRolesProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_changePassword(const cpp2::ChangePasswordReq& req) { + auto* processor = ChangePasswordProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future +MetaServiceHandler::future_checkPassword(const cpp2::CheckPasswordReq& req) { + auto* processor = CheckPasswordProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + } // namespace meta } // namespace nebula diff --git a/src/meta/MetaServiceHandler.h b/src/meta/MetaServiceHandler.h index 9f7b0130d74..b679da93340 100644 --- a/src/meta/MetaServiceHandler.h +++ b/src/meta/MetaServiceHandler.h @@ -107,6 +107,39 @@ class MetaServiceHandler final : public cpp2::MetaServiceSvIf { folly::Future future_heartBeat(const cpp2::HBReq& req) override; + /** + * User manager + **/ + folly::Future + future_createUser(const cpp2::CreateUserReq& req) override; + + folly::Future + future_dropUser(const cpp2::DropUserReq& req) override; + + folly::Future + future_alterUser(const cpp2::AlterUserReq& req) override; + + folly::Future + future_grantRole(const cpp2::GrantRoleReq& req) override; + + folly::Future + future_revokeRole(const cpp2::RevokeRoleReq& req) override; + + folly::Future + future_getUser(const cpp2::GetUserReq& req) override; + + folly::Future + future_listUsers(const cpp2::ListUsersReq& req) override; + + folly::Future + future_listRoles(const cpp2::ListRolesReq& req) override; + + folly::Future + future_changePassword(const cpp2::ChangePasswordReq& req) override; + + folly::Future + future_checkPassword(const cpp2::CheckPasswordReq& req) override; + private: kvstore::KVStore* kvstore_ = nullptr; }; diff --git a/src/meta/MetaServiceUtils.cpp b/src/meta/MetaServiceUtils.cpp index c242f61df80..2ed8292d164 100644 --- a/src/meta/MetaServiceUtils.cpp +++ b/src/meta/MetaServiceUtils.cpp @@ -17,6 +17,8 @@ const std::string kHostsTable = "__hosts__"; // NOLINT const std::string kTagsTable = "__tags__"; // NOLINT const std::string kEdgesTable = "__edges__"; // NOLINT const std::string kIndexTable = "__index__"; // NOLINT +const std::string kUsersTable = "__users__"; // NOLINT +const std::string kRolesTable = "__roles__"; // NOLINT const std::string kHostOnline = "Online"; // NOLINT const std::string kHostOffline = "Offline"; // NOLINT @@ -309,5 +311,125 @@ cpp2::ErrorCode MetaServiceUtils::alterColumnDefs(std::vector(&type), sizeof(type)); + key.append(account); + return key; +} + +std::string MetaServiceUtils::userKey(UserID userId) { + std::string key; + key.reserve(64); + key.append(kUsersTable.data(), kUsersTable.size()); + key.append(reinterpret_cast(&userId), sizeof(userId)); + return key; +} + +std::string MetaServiceUtils::userVal(const std::string& password, + const cpp2::UserItem& userItem) { + auto len = password.size(); + std::string val, userVal; + apache::thrift::CompactSerializer::serialize(userItem, &userVal); + val.reserve(sizeof(int32_t) + len + userVal.size()); + val.append(reinterpret_cast(&len), sizeof(int32_t)); + val.append(password); + val.append(userVal); + return val; +} + +folly::StringPiece MetaServiceUtils::userItemVal(folly::StringPiece rawVal) { + auto offset = sizeof(int32_t) + *reinterpret_cast(rawVal.begin()); + return rawVal.subpiece(offset, rawVal.size() - offset); +} + +std::string MetaServiceUtils::replaceUserVal(const cpp2::UserItem& user, folly::StringPiece val) { + cpp2:: UserItem oldUser; + apache::thrift::CompactSerializer::deserialize(userItemVal(val), oldUser); + if (user.__isset.is_lock) { + oldUser.set_is_lock(user.get_is_lock()); + } + if (user.__isset.max_queries_per_hour) { + oldUser.set_max_queries_per_hour(user.get_max_queries_per_hour()); + } + if (user.__isset.max_updates_per_hour) { + oldUser.set_max_updates_per_hour(user.get_max_updates_per_hour()); + } + if (user.__isset.max_connections_per_hour) { + oldUser.set_max_connections_per_hour(user.get_max_connections_per_hour()); + } + if (user.__isset.max_user_connections) { + oldUser.set_max_user_connections(user.get_max_user_connections()); + } + + std::string newVal, userVal; + apache::thrift::CompactSerializer::serialize(oldUser, &userVal); + auto len = sizeof(int32_t) + *reinterpret_cast(val.begin()); + newVal.reserve(len + userVal.size()); + newVal.append(val.subpiece(0, len).str()); + newVal.append(userVal); + return newVal; +} + +std::string MetaServiceUtils::roleKey(GraphSpaceID spaceId, UserID userId) { + std::string key; + key.reserve(64); + key.append(kRolesTable.data(), kRolesTable.size()); + key.append(reinterpret_cast(&spaceId), sizeof(GraphSpaceID)); + key.append(reinterpret_cast(&userId), sizeof(UserID)); + return key; +} + +std::string MetaServiceUtils::roleVal(cpp2::RoleType roleType) { + std::string val; + val.reserve(64); + val.append(reinterpret_cast(&roleType), sizeof(roleType)); + return val; +} + +std::string MetaServiceUtils::changePassword(folly::StringPiece val, folly::StringPiece newPwd) { + auto pwdLen = newPwd.size(); + auto len = sizeof(int32_t) + *reinterpret_cast(val.begin()); + auto userVal = val.subpiece(len, val.size() - len); + std::string newVal; + newVal.reserve(sizeof(int32_t) + pwdLen+ userVal.size()); + newVal.append(reinterpret_cast(&pwdLen), sizeof(int32_t)); + newVal.append(newPwd.str()); + newVal.append(userVal.str()); + return newVal; +} + +cpp2::UserItem MetaServiceUtils::parseUserItem(folly::StringPiece val) { + cpp2:: UserItem user; + apache::thrift::CompactSerializer::deserialize(userItemVal(val), user); + return user; +} + +std::string MetaServiceUtils::rolesPrefix() { + return kRolesTable; +} + +std::string MetaServiceUtils::roleSpacePrefix(GraphSpaceID spaceId) { + std::string key; + key.reserve(64); + key.append(kRolesTable.data(), kRolesTable.size()); + key.append(reinterpret_cast(&spaceId), sizeof(GraphSpaceID)); + return key; +} + +UserID MetaServiceUtils::parseRoleUserId(folly::StringPiece val) { + return *reinterpret_cast(val.begin() + + kRolesTable.size() + + sizeof(GraphSpaceID)); +} + +UserID MetaServiceUtils::parseUserId(folly::StringPiece val) { + return *reinterpret_cast(val.begin() + + kUsersTable.size()); +} + } // namespace meta } // namespace nebula diff --git a/src/meta/MetaServiceUtils.h b/src/meta/MetaServiceUtils.h index c39bd6bd87e..fc1bf664770 100644 --- a/src/meta/MetaServiceUtils.h +++ b/src/meta/MetaServiceUtils.h @@ -17,6 +17,7 @@ enum class EntryType : int8_t { SPACE = 0x01, TAG = 0x02, EDGE = 0x03, + USER = 0x04, }; class MetaServiceUtils final { @@ -88,6 +89,33 @@ class MetaServiceUtils final { static cpp2::ErrorCode alterColumnDefs(std::vector& cols, const nebula::cpp2::ColumnDef col, const cpp2::AlterSchemaOp op); + + static std::string indexUserKey(const std::string& account); + + static std::string userKey(UserID userId); + + static std::string userVal(const std::string& password, + const cpp2::UserItem& userItem); + + static folly::StringPiece userItemVal(folly::StringPiece rawVal); + + static std::string replaceUserVal(const cpp2::UserItem& user, folly::StringPiece rawVal); + + static std::string roleKey(GraphSpaceID spaceId, UserID userId); + + static std::string roleVal(cpp2::RoleType roleType); + + static std::string changePassword(folly::StringPiece val, folly::StringPiece newPwd); + + static cpp2::UserItem parseUserItem(folly::StringPiece val); + + static std::string rolesPrefix(); + + static std::string roleSpacePrefix(GraphSpaceID spaceId); + + static UserID parseRoleUserId(folly::StringPiece val); + + static UserID parseUserId(folly::StringPiece val); }; } // namespace meta diff --git a/src/meta/processors/BaseProcessor.h b/src/meta/processors/BaseProcessor.h index 3dcccc83f9b..0835663112b 100644 --- a/src/meta/processors/BaseProcessor.h +++ b/src/meta/processors/BaseProcessor.h @@ -39,6 +39,7 @@ GENERATE_LOCK(space); GENERATE_LOCK(id); GENERATE_LOCK(tag); GENERATE_LOCK(edge); +GENERATE_LOCK(user); #undef GENERATE_LOCK }; @@ -50,6 +51,13 @@ GENERATE_LOCK(edge); return; \ } +#define CHECK_USER_ID_AND_RETURN(userID) \ + if (userExist(userID) == Status::UserNotFound()) { \ + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); \ + onFinished(); \ + return; \ + } + /** * Check segemnt is consist of numbers and letters and should not empty. * */ @@ -99,6 +107,7 @@ class BaseProcessor { case Status::kSpaceNotFound: case Status::kHostNotFound: case Status::kTagNotFound: + case Status::kUserNotFound: return cpp2::ErrorCode::E_NOT_FOUND; default: return cpp2::ErrorCode::E_UNKNOWN; @@ -119,6 +128,9 @@ class BaseProcessor { case EntryType::EDGE: thriftID.set_edge_type(static_cast(id)); break; + case EntryType::USER: + thriftID.set_user_id(static_cast(id)); + break; } return thriftID; } @@ -183,6 +195,11 @@ class BaseProcessor { * */ Status spaceExist(GraphSpaceID spaceId); + /** + * Check userId exist or not. + **/ + Status userExist(UserID userId); + /** * Check host has been registered or not. * */ @@ -203,6 +220,12 @@ class BaseProcessor { */ StatusOr getEdgeType(GraphSpaceID spaceId, const std::string& name); + StatusOr getUserId(const std::string& account); + + bool checkPassword(UserID userId, const std::string& password); + + StatusOr getUserAccount(UserID userId); + protected: kvstore::KVStore* kvstore_ = nullptr; RESP resp_; diff --git a/src/meta/processors/BaseProcessor.inl b/src/meta/processors/BaseProcessor.inl index 5f671200792..84597159c8b 100644 --- a/src/meta/processors/BaseProcessor.inl +++ b/src/meta/processors/BaseProcessor.inl @@ -178,6 +178,18 @@ Status BaseProcessor::spaceExist(GraphSpaceID spaceId) { } +template +Status BaseProcessor::userExist(UserID spaceId) { + folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); + auto userKey = MetaServiceUtils::userKey(spaceId); + std::string val; + auto ret = kvstore_->get(kDefaultSpaceId, kDefaultPartId, userKey, &val); + if (ret == kvstore::ResultCode::SUCCEEDED) { + return Status::OK(); + } + return Status::UserNotFound(); +} + template Status BaseProcessor::hostExist(const std::string& hostKey) { std::string val; @@ -224,5 +236,42 @@ StatusOr BaseProcessor::getEdgeType(GraphSpaceID spaceId, return Status::EdgeNotFound(folly::stringPrintf("Edge %s not found", name.c_str())); } +template +StatusOr BaseProcessor::getUserId(const std::string& account) { + auto indexKey = MetaServiceUtils::indexUserKey(account); + std::string val; + auto ret = kvstore_->get(kDefaultSpaceId, kDefaultPartId, indexKey, &val); + if (ret == kvstore::ResultCode::SUCCEEDED) { + return *reinterpret_cast(val.c_str()); + } + return Status::UserNotFound(folly::stringPrintf("User %s not found", account.c_str())); +} + +template +bool BaseProcessor::checkPassword(UserID userId, const std::string& password) { + auto userKey = MetaServiceUtils::userKey(userId); + std::string val; + auto ret = kvstore_->get(kDefaultSpaceId, kDefaultPartId, userKey, &val); + if (ret == kvstore::ResultCode::SUCCEEDED) { + auto len = *reinterpret_cast(val.data()); + return password == val.substr(sizeof(int32_t), len); + } + return false; +} + +template +StatusOr BaseProcessor::getUserAccount(UserID userId) { + auto key = MetaServiceUtils::userKey(userId); + std::string value; + auto code = kvstore_->get(kDefaultSpaceId, kDefaultPartId, + key, &value); + if (code != kvstore::ResultCode::SUCCEEDED) { + return Status::UserNotFound(folly::stringPrintf("User not found by id %d", userId)); + } + + auto user = MetaServiceUtils::parseUserItem(value); + return user.get_account(); +} + } // namespace meta } // namespace nebula diff --git a/src/meta/processors/partsMan/DropSpaceProcessor.cpp b/src/meta/processors/partsMan/DropSpaceProcessor.cpp index 692de6b6128..e707cea249d 100644 --- a/src/meta/processors/partsMan/DropSpaceProcessor.cpp +++ b/src/meta/processors/partsMan/DropSpaceProcessor.cpp @@ -41,6 +41,8 @@ void DropSpaceProcessor::process(const cpp2::DropSpaceReq& req) { deleteKeys.emplace_back(MetaServiceUtils::indexSpaceKey(req.get_space_name())); deleteKeys.emplace_back(MetaServiceUtils::spaceKey(spaceId)); + // delete related role data. + // TODO(boshengchen) delete related role data under the space // TODO(YT) delete Tag/Edge under the space doMultiRemove(std::move(deleteKeys)); // TODO(YT) delete part files of the space diff --git a/src/meta/processors/usersMan/AuthenticationProcessor.cpp b/src/meta/processors/usersMan/AuthenticationProcessor.cpp new file mode 100644 index 00000000000..bc9cefb0269 --- /dev/null +++ b/src/meta/processors/usersMan/AuthenticationProcessor.cpp @@ -0,0 +1,271 @@ +/* Copyright (c) 2019 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "meta/processors/usersMan/AuthenticationProcessor.h" + +namespace nebula { +namespace meta { + +void CreateUserProcessor::process(const cpp2::CreateUserReq& req) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + const auto& user = req.get_user(); + auto ret = getUserId(user.get_account()); + if (ret.ok()) { + if (req.get_missing_ok()) { + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + } else { + LOG(ERROR) << "Create User Failed :" << user.get_account() << " have existed"; + resp_.set_code(cpp2::ErrorCode::E_EXISTED); + } + resp_.set_id(to(ret.value(), EntryType::USER)); + onFinished(); + return; + } + + UserID userId = autoIncrementId(); + std::vector data; + data.emplace_back(MetaServiceUtils::indexUserKey(user.get_account()), + std::string(reinterpret_cast(&userId), sizeof(userId))); + LOG(INFO) << "Create User " << user.get_account() << ", userId " << userId; + data.emplace_back(MetaServiceUtils::userKey(userId), + MetaServiceUtils::userVal(req.get_encoded_pwd(), user)); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + resp_.set_id(to(userId, EntryType::USER)); + doPut(std::move(data)); +} + + +void AlterUserProcessor::process(const cpp2::AlterUserReq& req) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + const auto& user = req.get_user_item(); + auto ret = getUserId(user.get_account()); + if (!ret.ok()) { + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + UserID userId = ret.value(); + auto userKey = MetaServiceUtils::userKey(userId); + std::string val; + auto result = kvstore_->get(kDefaultSpaceId, kDefaultPartId, userKey, &val); + if (result != kvstore::ResultCode::SUCCEEDED) { + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + std::vector data; + data.emplace_back(std::move(userKey), + MetaServiceUtils::replaceUserVal(user, val)); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + resp_.set_id(to(userId, EntryType::USER)); + doPut(std::move(data)); +} + + +void DropUserProcessor::process(const cpp2::DropUserReq& req) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + auto ret = getUserId(req.get_account()); + if (!ret.ok()) { + if (req.get_missing_ok()) { + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + } else { + LOG(ERROR) << "Drop User Failed :" << req.get_account() << " not found."; + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + } + onFinished(); + return; + } + std::vector keys; + keys.emplace_back(MetaServiceUtils::indexUserKey(req.get_account())); + keys.emplace_back(MetaServiceUtils::userKey(ret.value())); + + // Collect related roles by user. + std::unique_ptr iter; + auto prefix = MetaServiceUtils::rolesPrefix(); + auto userRet = kvstore_->prefix(kDefaultSpaceId, kDefaultPartId, prefix, &iter); + if (userRet == kvstore::ResultCode::SUCCEEDED) { + while (iter->valid()) { + auto key = iter->key(); + auto userId = MetaServiceUtils::parseRoleUserId(key); + if (userId == ret.value()) { + keys.emplace_back(std::move(key)); + } + iter->next(); + } + } + + resp_.set_id(to(ret.value(), EntryType::USER)); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + LOG(INFO) << "Drop User " << req.get_account(); + doMultiRemove(std::move(keys)); +} + + +void GrantProcessor::process(const cpp2::GrantRoleReq& req) { + const auto& roleItem = req.get_role_item(); + CHECK_SPACE_ID_AND_RETURN(roleItem.get_space_id()); + CHECK_USER_ID_AND_RETURN(roleItem.get_user_id()); + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + std::vector data; + data.emplace_back(MetaServiceUtils::roleKey(roleItem.get_space_id(), roleItem.get_user_id()), + MetaServiceUtils::roleVal(roleItem.get_role_type())); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + doPut(std::move(data)); +} + + +void RevokeProcessor::process(const cpp2::RevokeRoleReq& req) { + const auto& roleItem = req.get_role_item(); + CHECK_SPACE_ID_AND_RETURN(roleItem.get_space_id()); + CHECK_USER_ID_AND_RETURN(roleItem.get_user_id()); + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + auto roleKey = MetaServiceUtils::roleKey(roleItem.get_space_id(), roleItem.get_user_id()); + resp_.set_id(to(roleItem.get_user_id(), EntryType::USER)); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + doRemove(std::move(roleKey)); +} + + +void ChangePasswordProcessor::process(const cpp2::ChangePasswordReq& req) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + auto userRet = getUserId(req.get_account()); + if (!userRet.ok()) { + resp_.set_code(to(userRet.status())); + onFinished(); + return; + } + + // If the user role is god, the option old_encoded_pwd will not be set. + if (req.__isset.old_encoded_pwd) { + if (!checkPassword(userRet.value(), req.get_old_encoded_pwd())) { + resp_.set_code(cpp2::ErrorCode::E_INVALID_PASSWORD); + onFinished(); + return; + } + } + + auto userKey = MetaServiceUtils::userKey(userRet.value()); + std::string val; + auto result = kvstore_->get(kDefaultSpaceId, kDefaultPartId, userKey, &val); + if (result != kvstore::ResultCode::SUCCEEDED) { + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + std::vector data; + data.emplace_back(std::move(userKey), + MetaServiceUtils::changePassword(val, req.get_new_encoded_pwd())); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + doPut(std::move(data)); +} + + +void GetUserProcessor::process(const cpp2::GetUserReq& req) { + folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); + auto userRet = getUserId(req.get_account()); + if (!userRet.ok()) { + LOG(ERROR) << "User " << req.get_account() << " not found."; + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + auto userKey = MetaServiceUtils::userKey(userRet.value()); + std::string val; + auto result = kvstore_->get(kDefaultSpaceId, kDefaultPartId, userKey, &val); + if (result != kvstore::ResultCode::SUCCEEDED) { + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + decltype(resp_.user_item) user = MetaServiceUtils::parseUserItem(val); + resp_.set_user_item(user); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + onFinished(); +} + + +void ListUsersProcessor::process(const cpp2::ListUsersReq& req) { + UNUSED(req); + folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); + std::unique_ptr iter; + std::string prefix = "__users__"; + auto ret = kvstore_->prefix(kDefaultSpaceId, kDefaultPartId, prefix, &iter); + if (ret != kvstore::ResultCode::SUCCEEDED) { + LOG(ERROR) << "Can't find any users."; + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + decltype(resp_.users) users; + while (iter->valid()) { + cpp2::UserItem user = MetaServiceUtils::parseUserItem(iter->val()); + auto userId = MetaServiceUtils::parseUserId(iter->key()); + users.emplace(userId, std::move(user)); + iter->next(); + } + resp_.set_users(users); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + onFinished(); +} + + +void CheckPasswordProcessor::process(const cpp2::CheckPasswordReq& req) { + folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); + auto userRet = getUserId(req.get_account()); + if (!userRet.ok()) { + resp_.set_code(to(userRet.status())); + onFinished(); + return; + } + + if (!checkPassword(userRet.value(), req.get_encoded_pwd())) { + resp_.set_code(cpp2::ErrorCode::E_INVALID_PASSWORD); + onFinished(); + return; + } + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + resp_.set_id(to(userRet.value(), EntryType::USER)); + onFinished(); +} + + +void ListRolesProcessor::process(const cpp2::ListRolesReq& req) { + auto spaceId = req.get_space_id(); + CHECK_SPACE_ID_AND_RETURN(spaceId); + folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); + auto prefix = MetaServiceUtils::roleSpacePrefix(spaceId); + std::unique_ptr iter; + auto ret = kvstore_->prefix(kDefaultSpaceId, kDefaultPartId, prefix, &iter); + if (ret != kvstore::ResultCode::SUCCEEDED) { + LOG(ERROR) << "Can't find any roles by space id " << spaceId; + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + + decltype(resp_.roles) roles; + while (iter->valid()) { + auto userId = MetaServiceUtils::parseRoleUserId(iter->key()); + auto val = iter->val(); + auto account = getUserAccount(userId); + if (!account.ok()) { + LOG(ERROR) << "Get user account failling by id : " << userId; + resp_.set_code(cpp2::ErrorCode::E_NOT_FOUND); + onFinished(); + return; + } + cpp2::RoleItem role(apache::thrift::FragileConstructor::FRAGILE, + userId, spaceId, + *reinterpret_cast(val.begin())); + roles.emplace_back(std::move(role)); + iter->next(); + } + resp_.set_roles(roles); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + onFinished(); +} +} // namespace meta +} // namespace nebula diff --git a/src/meta/processors/usersMan/AuthenticationProcessor.h b/src/meta/processors/usersMan/AuthenticationProcessor.h new file mode 100644 index 00000000000..9a5d9adf025 --- /dev/null +++ b/src/meta/processors/usersMan/AuthenticationProcessor.h @@ -0,0 +1,155 @@ +/* Copyright (c) 2019 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef META_AUTHENTICATIONPROCESSOR_H +#define META_AUTHENTICATIONPROCESSOR_H + +#include "meta/processors/BaseProcessor.h" + +namespace nebula { +namespace meta { + +class CreateUserProcessor : public BaseProcessor { +public: + static CreateUserProcessor* instance(kvstore::KVStore* kvstore) { + return new CreateUserProcessor(kvstore); + } + + void process(const cpp2::CreateUserReq& req); + +private: + explicit CreateUserProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class AlterUserProcessor : public BaseProcessor { +public: + static AlterUserProcessor* instance(kvstore::KVStore* kvstore) { + return new AlterUserProcessor(kvstore); + } + + void process(const cpp2::AlterUserReq& req); + +private: + explicit AlterUserProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class DropUserProcessor : public BaseProcessor { +public: + static DropUserProcessor* instance(kvstore::KVStore* kvstore) { + return new DropUserProcessor(kvstore); + } + + void process(const cpp2::DropUserReq& req); + +private: + explicit DropUserProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class GrantProcessor : public BaseProcessor { +public: + static GrantProcessor* instance(kvstore::KVStore* kvstore) { + return new GrantProcessor(kvstore); + } + + void process(const cpp2::GrantRoleReq& req); + +private: + explicit GrantProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class RevokeProcessor : public BaseProcessor { +public: + static RevokeProcessor* instance(kvstore::KVStore* kvstore) { + return new RevokeProcessor(kvstore); + } + + void process(const cpp2::RevokeRoleReq& req); + +private: + explicit RevokeProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class ChangePasswordProcessor : public BaseProcessor { +public: + static ChangePasswordProcessor* instance(kvstore::KVStore* kvstore) { + return new ChangePasswordProcessor(kvstore); + } + + void process(const cpp2::ChangePasswordReq& req); + +private: + explicit ChangePasswordProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class GetUserProcessor : public BaseProcessor { +public: + static GetUserProcessor* instance(kvstore::KVStore* kvstore) { + return new GetUserProcessor(kvstore); + } + + void process(const cpp2::GetUserReq& req); + +private: + explicit GetUserProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class ListUsersProcessor : public BaseProcessor { +public: + static ListUsersProcessor* instance(kvstore::KVStore* kvstore) { + return new ListUsersProcessor(kvstore); + } + + void process(const cpp2::ListUsersReq& req); + +private: + explicit ListUsersProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class CheckPasswordProcessor : public BaseProcessor { +public: + static CheckPasswordProcessor* instance(kvstore::KVStore* kvstore) { + return new CheckPasswordProcessor(kvstore); + } + + void process(const cpp2::CheckPasswordReq& req); + +private: + explicit CheckPasswordProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + + +class ListRolesProcessor : public BaseProcessor { +public: + static ListRolesProcessor* instance(kvstore::KVStore* kvstore) { + return new ListRolesProcessor(kvstore); + } + + void process(const cpp2::ListRolesReq& req); + +private: + explicit ListRolesProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; +} // namespace meta +} // namespace nebula +#endif // META_AUTHENTICATIONPROCESSOR_H diff --git a/src/meta/test/AuthProcessorTest.cpp b/src/meta/test/AuthProcessorTest.cpp new file mode 100644 index 00000000000..92cd67dd3ea --- /dev/null +++ b/src/meta/test/AuthProcessorTest.cpp @@ -0,0 +1,326 @@ +/* Copyright (c) 2019 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "base/Base.h" +#include +#include +#include +#include "fs/TempDir.h" +#include "meta/test/TestUtils.h" +#include "meta/processors/usersMan/AuthenticationProcessor.h" +#include "meta/processors/partsMan/CreateSpaceProcessor.h" + +namespace nebula { +namespace meta { + +using apache::thrift::FragileConstructor::FRAGILE; + +TEST(AuthProcessorTest, CreateUserTest) { + fs::TempDir rootPath("/tmp/CreateUserTest.XXXXXX"); + std::unique_ptr kv(TestUtils::initKV(rootPath.path())); + // Simple test + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + false, 0, 0, 0, 0); + ASSERT_TRUE(code.ok()); + + /** + * missing_ok should be turn on when "IF NOT EXISTS" is set. + * the result will be SUCCEEDED whether the user exists or not. + * + * missing_ok should be turn off when without "IF NOT EXISTS". + * the result will be EXISTED if the user exists. + **/ + + code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + false, 0, 0, 0, 0); + ASSERT_FALSE(code.ok()); + + code = TestUtils::createUser(kv.get(), true, "user1", "pwd", + false, 0, 0, 0, 0); + ASSERT_TRUE(code.ok()); +} + + +TEST(AuthProcessorTest, AlterUserTest) { + fs::TempDir rootPath("/tmp/AlterUserTest.XXXXXX"); + std::unique_ptr kv(TestUtils::initKV(rootPath.path())); + // Setup + { + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + false, 1, 2, 3, 4); + ASSERT_TRUE(code.ok()); + } + // Alter a few attributes + { + cpp2::AlterUserReq req; + decltype(req.user_item) newUser; + newUser.set_account("user1"); + newUser.set_is_lock(true); + req.set_user_item(std::move(newUser)); + auto* processor = AlterUserProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + // Test user does not exist + { + cpp2::AlterUserReq req; + decltype(req.user_item) newUser; + newUser.set_account("user"); + req.set_user_item(std::move(newUser)); + auto* processor = AlterUserProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::E_NOT_FOUND, resp.get_code()); + } + // Alter all attributes + { + cpp2::AlterUserReq req; + decltype(req.user_item) newUser; + newUser.set_account("user1"); + newUser.set_max_queries_per_hour(10); + newUser.set_max_updates_per_hour(20); + newUser.set_max_connections_per_hour(30); + newUser.set_max_user_connections(40); + newUser.set_is_lock(false); + req.set_user_item(std::move(newUser)); + auto* processor = AlterUserProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + // Test GetUser + { + cpp2::GetUserReq req(FRAGILE, "user1"); + auto* processor = GetUserProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + decltype(resp.user_item) user; + user.set_account("user1"); + user.set_max_queries_per_hour(10); + user.set_max_updates_per_hour(20); + user.set_max_connections_per_hour(30); + user.set_max_user_connections(40); + user.set_is_lock(false); + ASSERT_EQ(user, resp.get_user_item()); + } +} + +TEST(AuthProcessorTest, DropUserTest) { + fs::TempDir rootPath("/tmp/DropUserTest.XXXXXX"); + std::unique_ptr kv(TestUtils::initKV(rootPath.path())); + // Setup + { + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + false, 1, 2, 3, 4); + ASSERT_TRUE(code.ok()); + } + // Simple drop. + { + cpp2::DropUserReq req(FRAGILE, "user1", false); + auto* processor = DropUserProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + + /** + * missing_ok should be turn on when "IF EXISTS" is set. + * the result will be SUCCEEDED whether the user exists or not. + * + * missing_ok should be turn off when without "IF EXISTS" . + * the result will be NOT_FOUND if the user does not exist. + **/ + // missing_ok = false , And user does net exist. + { + cpp2::DropUserReq req(FRAGILE, "user", false); + auto* processor = DropUserProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::E_NOT_FOUND, resp.get_code()); + } + // missing_ok = true , And user does net exist. + { + cpp2::DropUserReq req(FRAGILE, "user", true); + auto* processor = DropUserProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } +} + +TEST(AuthProcessorTest, PasswordTest) { + fs::TempDir rootPath("/tmp/PasswordTest.XXXXXX"); + std::unique_ptr kv(TestUtils::initKV(rootPath.path())); + // Setup + { + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + false, 4, 3, 2, 1); + ASSERT_TRUE(code.ok()); + } + // verify password. + { + cpp2::CheckPasswordReq req(FRAGILE, "user1", "pwd"); + auto* processor = CheckPasswordProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + // verify password. + { + cpp2::CheckPasswordReq req(FRAGILE, "user1", "pwd1"); + auto* processor = CheckPasswordProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::E_INVALID_PASSWORD, resp.get_code()); + } + // Change password for god role. + { + cpp2::ChangePasswordReq req; + req.set_account("user1"); + req.set_new_encoded_pwd("password"); + auto* processor = ChangePasswordProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + // If the user role is not GOD, the old password must be set. + { + cpp2::ChangePasswordReq req; + req.set_account("user1"); + req.set_new_encoded_pwd("password"); + req.set_old_encoded_pwd("password1"); + auto* processor = ChangePasswordProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::E_INVALID_PASSWORD, resp.get_code()); + } + // verify password. + { + cpp2::CheckPasswordReq req(FRAGILE, "user1", "password"); + auto* processor = CheckPasswordProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } +} + +TEST(AuthProcessorTest, GrantRevokeTest) { + fs::TempDir rootPath("/tmp/GrantRevokeTest.XXXXXX"); + std::unique_ptr kv(TestUtils::initKV(rootPath.path())); + auto ret = TestUtils::createUser(kv.get(), false, "user1", "pwd", + false, 1, 2, 3, 4); + ASSERT_TRUE(ret.ok()); + auto userId = ret.value(); + + // grant test : space does not exist + { + cpp2::GrantRoleReq req; + decltype(req.role_item) role(FRAGILE, userId, 100, cpp2::RoleType::USER); + req.set_role_item(std::move(role)); + auto* processor = GrantProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::E_NOT_FOUND, resp.get_code()); + } + + // revoke test : space does not exist + { + cpp2::RevokeRoleReq req; + decltype(req.role_item) role(FRAGILE, userId, 100, cpp2::RoleType::USER); + req.set_role_item(std::move(role)); + auto* processor = RevokeProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::E_NOT_FOUND, resp.get_code()); + } + // setup space + GraphSpaceID spaceId; + { + TestUtils::createSomeHosts(kv.get()); + cpp2::CreateSpaceReq req(FRAGILE, + cpp2::SpaceProperties(FRAGILE, "test_space", 1, 1)); + auto* processor = CreateSpaceProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.code); + spaceId = resp.get_id().get_space_id(); + } + // grant test + { + cpp2::GrantRoleReq req; + decltype(req.role_item) role(FRAGILE, userId, spaceId, cpp2::RoleType::USER); + req.set_role_item(std::move(role)); + auto* processor = GrantProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + // List acl by space name. + { + cpp2::ListRolesReq req(FRAGILE, spaceId); + auto* processor = ListRolesProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + decltype(resp.roles) rolesList; + rolesList.emplace_back(FRAGILE, userId, spaceId, cpp2::RoleType::USER); + ASSERT_EQ(rolesList, resp.get_roles()); + } + // revoke test + { + cpp2::RevokeRoleReq req; + decltype(req.role_item) role(FRAGILE, userId, spaceId, cpp2::RoleType::USER); + req.set_role_item(std::move(role)); + auto* processor = RevokeProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + std::unique_ptr iter; + auto prefix = MetaServiceUtils::rolesPrefix(); + auto code = kv.get()->prefix(0, 0, prefix, &iter); + ASSERT_EQ(kvstore::ResultCode::SUCCEEDED, code); + ASSERT_FALSE(iter->valid()); + } + // List Users + { + cpp2::ListUsersReq req; + auto* processor = ListUsersProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + decltype(resp.users) users; + users.emplace(userId, cpp2::UserItem(FRAGILE, "user1", false, 1, 2, 3, 4)); + ASSERT_EQ(users, resp.get_users()); + } +} +} // namespace meta +} // namespace nebula + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + folly::init(&argc, &argv, true); + google::SetStderrLogging(google::INFO); + return RUN_ALL_TESTS(); +} diff --git a/src/meta/test/CMakeLists.txt b/src/meta/test/CMakeLists.txt index 6b1dd211b61..77061e4f336 100644 --- a/src/meta/test/CMakeLists.txt +++ b/src/meta/test/CMakeLists.txt @@ -171,3 +171,33 @@ nebula_link_libraries( gtest ) nebula_add_test(meta_http_test) +add_executable( + authentication_test + AuthProcessorTest.cpp + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) +nebula_link_libraries( + authentication_test + proxygenhttpserver + proxygenlib + ${ROCKSDB_LIBRARIES} + ${THRIFT_LIBRARIES} + wangle + gtest +) +nebula_add_test(authentication_test) + diff --git a/src/meta/test/TestUtils.h b/src/meta/test/TestUtils.h index 945f3769e87..f524d6ea491 100644 --- a/src/meta/test/TestUtils.h +++ b/src/meta/test/TestUtils.h @@ -16,6 +16,7 @@ #include "meta/processors/partsMan/ListHostsProcessor.h" #include "meta/MetaServiceHandler.h" #include +#include "meta/processors/usersMan/AuthenticationProcessor.h" #include "interface/gen-cpp2/common_types.h" #include "time/TimeUtils.h" #include "meta/ActiveHostsMan.h" @@ -26,7 +27,7 @@ namespace nebula { namespace meta { using nebula::cpp2::SupportedType; - +using apache::thrift::FragileConstructor::FRAGILE; class TestUtils { public: static std::unique_ptr initKV(const char* rootPath) { @@ -184,6 +185,37 @@ class TestUtils { return sc; } + + static StatusOr createUser(kvstore::KVStore* kv, + bool missingOk, + folly::StringPiece account, + folly::StringPiece password, + bool isLock, + int32_t maxQueries, + int32_t maxUpdates, + int32_t maxConnections, + int32_t maxConnectors) { + cpp2::CreateUserReq req; + req.set_missing_ok(missingOk); + req.set_encoded_pwd(password.str()); + decltype(req.user) user(FRAGILE, + account.str(), + isLock, + maxQueries, + maxUpdates, + maxConnections, + maxConnectors); + req.set_user(std::move(user)); + auto* processor = CreateUserProcessor::instance(kv); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + if (resp.get_code() == cpp2::ErrorCode::SUCCEEDED) { + return resp.get_id().get_user_id(); + } else { + return Status::Error("Create user fail"); + } + } }; } // namespace meta