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/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt index b601b86ab53..d03edaa3b8b 100644 --- a/src/daemons/CMakeLists.txt +++ b/src/daemons/CMakeLists.txt @@ -87,6 +87,7 @@ add_executable( $ $ $ + $ ) nebula_link_libraries( nebula-metad 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 4ec08c21af7..768523374c1 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) @@ -43,10 +46,19 @@ enum AlterSchemaOp { } (cpp.enum_strict) +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 { @@ -89,6 +101,22 @@ struct EdgeItem { 4: common.Schema schema, } + + +struct UserItem { + 1: string account, + 2: string first_name, + 3: string last_name, + 4: string email, + 5: string phone, +} + +struct RoleItem { + 1: string account, + 2: string space, + 3: RoleType RoleType, +} + struct ExecResp { 1: ErrorCode code, // For custom kv operations, it is useless. @@ -300,6 +328,63 @@ 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: UserItem user_item, +} + +struct ListUsersReq { +} + +struct ListUsersResp { + 1: list users, +} + +struct ListRolesReq { + 1: string space, +} + +struct ListRolesResp { + 1: 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); @@ -332,5 +417,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 a9b29c2b574..0cf9ae74d23 100644 --- a/src/meta/CMakeLists.txt +++ b/src/meta/CMakeLists.txt @@ -36,6 +36,7 @@ add_library( processors/customKV/RemoveRangeProcessor.cpp processors/customKV/ScanProcessor.cpp processors/admin/HBProcessor.cpp + processors/usersMan/AuthenticationProcessor.cpp ) add_dependencies( meta_service_handler @@ -45,6 +46,7 @@ add_dependencies( kvstore_obj thread_obj stats_obj + dataman_obj ) add_library( 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 2f4cee941f0..60613a1c4fb 100644 --- a/src/meta/MetaServiceUtils.cpp +++ b/src/meta/MetaServiceUtils.cpp @@ -5,6 +5,9 @@ */ #include "meta/MetaServiceUtils.h" +#include "dataman/RowWriter.h" +#include "dataman/RowUpdater.h" +#include "dataman/RowReader.h" #include #include @@ -17,6 +20,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 = "__user__"; // NOLINT +const std::string kRolesTable = "__role__"; // NOLINT std::string MetaServiceUtils::spaceKey(GraphSpaceID spaceId) { std::string key; @@ -302,5 +307,129 @@ 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, + std::shared_ptr schema) { + auto len = password.size(); + std::string val; + RowWriter writer(std::move(schema)); + writer << userItem.get_account() + << userItem.get_first_name() + << userItem.get_last_name() + << userItem.get_email() + << userItem.get_phone(); + std::string encoded(writer.encode()); + val.reserve(sizeof(int32_t) + len + encoded.size()); + val.append(reinterpret_cast(&len), sizeof(int32_t)); + val.append(password); + val.append(encoded); + 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, + std::shared_ptr schema) { + auto reader = RowReader::getRowReader(MetaServiceUtils::userItemVal(val), schema); + RowUpdater updater(move(reader), schema); + + if (user.__isset.first_name) { + updater.setString(GLOBAL_USER_ITEM_FIRSTNAME, user.first_name); + } + if (user.__isset.last_name) { + updater.setString(GLOBAL_USER_ITEM_LASTNAME, user.get_last_name()); + } + if (user.__isset.email) { + updater.setString(GLOBAL_USER_ITEM_EMAIL, user.get_email()); + } + if (user.__isset.phone) { + updater.setString(GLOBAL_USER_ITEM_PHONE, user.get_phone()); + } + auto newVal = updater.encode(); + auto len = sizeof(int32_t) + *reinterpret_cast(val.begin()); + std::string userVal; + userVal.reserve(len + newVal.size()); + userVal.append(val.subpiece(0, len).str()); + userVal.append(newVal); + return userVal; +} + +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, + std::shared_ptr schema) { + auto reader = RowReader::getRowReader(userItemVal(val), schema); + folly::StringPiece accont, first, last, email, phone; + reader->getString(GLOBAL_USER_ITEM_ACCOUNT, accont); + reader->getString(GLOBAL_USER_ITEM_FIRSTNAME, first); + reader->getString(GLOBAL_USER_ITEM_LASTNAME, last); + reader->getString(GLOBAL_USER_ITEM_EMAIL, email); + reader->getString(GLOBAL_USER_ITEM_PHONE, phone); + cpp2::UserItem userItem(apache::thrift::FragileConstructor::FRAGILE, + accont.str(), first.str(), last.str(), email.str(), phone.str()); + return userItem; +} + +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::parseUserId(folly::StringPiece val) { + return *reinterpret_cast(val.begin() + + kRolesTable.size() + + sizeof(GraphSpaceID)); +} + } // namespace meta } // namespace nebula diff --git a/src/meta/MetaServiceUtils.h b/src/meta/MetaServiceUtils.h index cfff8e7b88c..59ff3bc02d8 100644 --- a/src/meta/MetaServiceUtils.h +++ b/src/meta/MetaServiceUtils.h @@ -9,6 +9,7 @@ #include "base/Base.h" #include "interface/gen-cpp2/meta_types.h" +#include "SchemaProviderIf.h" namespace nebula { namespace meta { @@ -17,8 +18,16 @@ enum class EntryType : int8_t { SPACE = 0x01, TAG = 0x02, EDGE = 0x03, + USER = 0x04, }; +#define GLOBAL_USER_SCHEMA_TAG "global_user_schema_tag" +#define GLOBAL_USER_ITEM_ACCOUNT "account" +#define GLOBAL_USER_ITEM_FIRSTNAME "first-name" +#define GLOBAL_USER_ITEM_LASTNAME "last-name" +#define GLOBAL_USER_ITEM_EMAIL "email-addr" +#define GLOBAL_USER_ITEM_PHONE "phone-num" + class MetaServiceUtils final { public: MetaServiceUtils() = delete; @@ -86,6 +95,32 @@ 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, + std::shared_ptr schema); + + static folly::StringPiece userItemVal(folly::StringPiece rawVal); + + static std::string replaceUserVal(const cpp2::UserItem& user, folly::StringPiece rawVal, + std::shared_ptr schema); + + 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, + std::shared_ptr schema); + + static std::string roleSpacePrefix(GraphSpaceID spaceId); + + static UserID parseUserId(folly::StringPiece val); }; } // namespace meta diff --git a/src/meta/processors/BaseProcessor.h b/src/meta/processors/BaseProcessor.h index e54383ac21e..bc8dcd6b6fe 100644 --- a/src/meta/processors/BaseProcessor.h +++ b/src/meta/processors/BaseProcessor.h @@ -16,6 +16,7 @@ #include "kvstore/KVStore.h" #include "meta/MetaServiceUtils.h" #include "meta/common/MetaCommon.h" +#include "meta/NebulaSchemaProvider.h" #include "network/NetworkUtils.h" namespace nebula { @@ -36,6 +37,7 @@ GENERATE_LOCK(space); GENERATE_LOCK(id); GENERATE_LOCK(tag); GENERATE_LOCK(edge); +GENERATE_LOCK(user); #undef GENERATE_LOCK }; @@ -116,6 +118,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; } @@ -200,6 +205,14 @@ class BaseProcessor { */ StatusOr getEdgeType(GraphSpaceID spaceId, const std::string& name); + StatusOr getUserId(const std::string& account); + + StatusOr> getUserSchema(); + + 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 a40c2606fd2..622330a4428 100644 --- a/src/meta/processors/BaseProcessor.inl +++ b/src/meta/processors/BaseProcessor.inl @@ -204,5 +204,76 @@ StatusOr BaseProcessor::getEdgeType(GraphSpaceID spaceId, const 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 +StatusOr> BaseProcessor::getUserSchema() { + auto ret = getTagId(0, GLOBAL_USER_SCHEMA_TAG); + if (!ret.ok()) { + return Status::TagNotFound(folly::stringPrintf("Tag %s not found", GLOBAL_USER_SCHEMA_TAG)); + } + + std::unique_ptr iter; + auto tagPrefix = MetaServiceUtils::schemaTagPrefix(0, ret.value()); + auto code = kvstore_->prefix(kDefaultSpaceId_, kDefaultPartId_, tagPrefix, &iter); + if (code != kvstore::ResultCode::SUCCEEDED || !iter->valid()) { + return Status:: TagNotFound(folly::stringPrintf("Tag %s not found", + GLOBAL_USER_SCHEMA_TAG)); + } + + // Get last version of tag + auto version = MetaServiceUtils::parseTagVersion(iter->key()); + auto schema = MetaServiceUtils::parseSchema(iter->val()); + auto& columns = schema.get_columns(); + + std::shared_ptr schemaProv(new NebulaSchemaProvider(version)); + for (auto& column : columns) { + schemaProv->addField(column.get_name(), + const_cast(column.get_type())); + } + + return schemaProv; +} + +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 schema = getUserSchema(); + if (!schema.ok()) { + return Status::TagNotFound("Global user tag not found."); + } + + auto user = MetaServiceUtils::parseUserItem(value, schema.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 36c7f5483a4..d8dd99c5cab 100644 --- a/src/meta/processors/partsMan/DropSpaceProcessor.cpp +++ b/src/meta/processors/partsMan/DropSpaceProcessor.cpp @@ -41,6 +41,16 @@ 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. + auto rolePrefix = MetaServiceUtils::roleSpacePrefix(spaceRet.value()); + std::unique_ptr roleIter; + auto roleRet = kvstore_->prefix(kDefaultSpaceId_, kDefaultPartId_, rolePrefix, &roleIter); + if (roleRet == kvstore::ResultCode::SUCCEEDED) { + while (roleIter->valid()) { + deleteKeys.emplace_back(roleIter->key()); + roleIter->next(); + } + } // 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..96a49bb349e --- /dev/null +++ b/src/meta/processors/usersMan/AuthenticationProcessor.cpp @@ -0,0 +1,310 @@ +/* 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; + auto schema = getUserSchema(); + if (!schema.ok()) { + resp_.set_code(to(ret.status())); + onFinished(); + return; + } + data.emplace_back(MetaServiceUtils::userKey(userId), + MetaServiceUtils::userVal(req.get_encoded_pwd(), user, schema.value())); + 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; + } + + auto schema = getUserSchema(); + if (!schema.ok()) { + resp_.set_code(to(ret.status())); + onFinished(); + return; + } + std::vector data; + data.emplace_back(std::move(userKey), + MetaServiceUtils::replaceUserVal(user, val, schema.value())); + 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; + std::string prefix = "__roles__"; + auto userRet = kvstore_->prefix(kDefaultSpaceId_, kDefaultPartId_, prefix, &iter); + if (userRet == kvstore::ResultCode::SUCCEEDED) { + while (iter->valid()) { + auto key = iter->key(); + auto userId = MetaServiceUtils::parseUserId(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) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + const auto& roleItem = req.get_role_item(); + auto spaceRet = getSpaceId(roleItem.get_space()); + if (!spaceRet.ok()) { + resp_.set_code(to(spaceRet.status())); + onFinished(); + return;; + } + auto userRet = getUserId(roleItem.get_account()); + if (!userRet.ok()) { + resp_.set_code(to(userRet.status())); + onFinished(); + return; + } + std::vector data; + data.emplace_back(MetaServiceUtils::roleKey(spaceRet.value(), userRet.value()), + MetaServiceUtils::roleVal(roleItem.get_RoleType())); + resp_.set_code(cpp2::ErrorCode::SUCCEEDED); + doPut(std::move(data)); +} + + +void RevokeProcessor::process(const cpp2::RevokeRoleReq& req) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); + const auto& roleItem = req.get_role_item(); + auto spaceRet = getSpaceId(roleItem.get_space()); + if (!spaceRet.ok()) { + resp_.set_code(to(spaceRet.status())); + onFinished(); + return;; + } + auto userRet = getUserId(roleItem.get_account()); + if (!userRet.ok()) { + resp_.set_code(to(userRet.status())); + onFinished(); + return; + } + auto roleKey = MetaServiceUtils::roleKey(spaceRet.value(), userRet.value()); + resp_.set_id(to(userRet.value(), 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."; + onFinished(); + return; + } + auto userKey = MetaServiceUtils::userKey(userRet.value()); + std::string val; + auto result = kvstore_->get(kDefaultSpaceId_, kDefaultPartId_, userKey, &val); + if (result != kvstore::ResultCode::SUCCEEDED) { + onFinished(); + return; + } + auto schema = getUserSchema(); + if (!schema.ok()) { + LOG(ERROR) << "Global schema " << GLOBAL_USER_SCHEMA_TAG << " not found."; + onFinished(); + return; + } + decltype(resp_.user_item) user = MetaServiceUtils::parseUserItem(val, schema.value()); + resp_.set_user_item(user); + onFinished(); +} + + +void ListUsersProcessor::process(const cpp2::ListUsersReq& req) { + UNUSED(req); + folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); + std::unique_ptr iter; + std::string prefix = "__user__"; + auto ret = kvstore_->prefix(kDefaultSpaceId_, kDefaultPartId_, prefix, &iter); + if (ret != kvstore::ResultCode::SUCCEEDED) { + LOG(ERROR) << "Can't find any users."; + onFinished(); + return; + } + auto schema = getUserSchema(); + if (!schema.ok()) { + LOG(ERROR) << "Global schema " << GLOBAL_USER_SCHEMA_TAG << " not found."; + onFinished(); + return; + } + decltype(resp_.users) users; + while (iter->valid()) { + cpp2::UserItem user = MetaServiceUtils::parseUserItem(iter->val(), schema.value()); + users.emplace_back(std::move(user)); + iter->next(); + } + resp_.set_users(users); + 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) { + folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); + auto spaceRet = getSpaceId(req.get_space()); + if (!spaceRet.ok()) { + onFinished(); + return;; + } + auto prefix = MetaServiceUtils::roleSpacePrefix(spaceRet.value()); + 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 " << req.get_space(); + onFinished(); + return; + } + + decltype(resp_.roles) roles; + while (iter->valid()) { + auto userId = MetaServiceUtils::parseUserId(iter->key()); + auto val = iter->val(); + auto account = getUserAccount(userId); + if (!account.ok()) { + LOG(ERROR) << "Get user account failling by id : " << userId; + onFinished(); + return; + } + cpp2::RoleItem role(apache::thrift::FragileConstructor::FRAGILE, + account.value(), ""/*space name can be ignore at here*/, + *reinterpret_cast(val.begin())); + roles.emplace_back(std::move(role)); + iter->next(); + } + resp_.set_roles(roles); + 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..b15b889e22b --- /dev/null +++ b/src/meta/test/AuthProcessorTest.cpp @@ -0,0 +1,319 @@ +/* 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())); + TestUtils::mockUserTag(kv.get()); + // Simple test + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + "first name", "last name" , + "email@email.com", "+00-00000000"); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + /** + * 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", "", "" , "", ""); + ASSERT_EQ(cpp2::ErrorCode::E_EXISTED, code); + + code = TestUtils::createUser(kv.get(), true, "user1", "pwd", "", "" , "", ""); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); +} + + +TEST(AuthProcessorTest, AlterUserTest) { + fs::TempDir rootPath("/tmp/AlterUserTest.XXXXXX"); + std::unique_ptr kv(TestUtils::initKV(rootPath.path())); + TestUtils::mockUserTag(kv.get()); + // Setup + { + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + "first name", "last name" , + "email@email.com", "+00-00000000"); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + // Alter a few attributes + { + cpp2::AlterUserReq req; + decltype(req.user_item) newUser; + newUser.set_account("user1"); + newUser.set_first_name("user name"); + 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_first_name("new first name"); + newUser.set_last_name(""); + newUser.set_email("aaaa@ccccc.com"); + newUser.set_phone("1234567890"); + 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_first_name("new first name"); + user.set_last_name(""); + user.set_email("aaaa@ccccc.com"); + user.set_phone("1234567890"); + ASSERT_EQ(user, resp.get_user_item()); + } +} + +TEST(AuthProcessorTest, DropUserTest) { + fs::TempDir rootPath("/tmp/DropUserTest.XXXXXX"); + std::unique_ptr kv(TestUtils::initKV(rootPath.path())); + TestUtils::mockUserTag(kv.get()); + // Setup + { + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + "first name", "last name" , + "email@email.com", "+00-00000000"); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + // 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())); + TestUtils::mockUserTag(kv.get()); + // Setup + { + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + "first name", "last name" , + "email@email.com", "+00-00000000"); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + // 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())); + TestUtils::mockUserTag(kv.get()); + // Setup + { + auto code = TestUtils::createUser(kv.get(), false, "user1", "pwd", + "first name", "last name", + "email@email.com", "+00-00000000"); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + // grant test : space does not exist + { + cpp2::GrantRoleReq req; + decltype(req.role_item) role(FRAGILE, "user1", "space1", 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, "user1", "space1", 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 + { + 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); + } + // grant test + { + cpp2::GrantRoleReq req; + decltype(req.role_item) role(FRAGILE, "user1", "test_space", 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, "test_space"); + 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, "user1", "", cpp2::RoleType::USER); + ASSERT_EQ(rolesList, resp.get_roles()); + } + // revoke test + { + cpp2::RevokeRoleReq req; + decltype(req.role_item) role(FRAGILE, "user1", "test_space", 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; + std::string prefix = "__acl__"; + auto code = kv.get()->prefix(0, 0, prefix, &iter); + ASSERT_EQ(kvstore::ResultCode::SUCCEEDED, code); + ASSERT_FALSE(iter->valid()); + } +} +} // 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 6cf3965f648..912e1ac8d89 100644 --- a/src/meta/test/CMakeLists.txt +++ b/src/meta/test/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable( $ $ $ + $ ) nebula_link_libraries( meta_utils_test @@ -33,6 +34,7 @@ add_executable( $ $ $ + $ ) nebula_link_libraries( processor_test @@ -58,6 +60,7 @@ add_executable( $ $ $ + $ ) nebula_link_libraries( hb_processor_test @@ -84,6 +87,7 @@ add_executable( $ $ $ + $ ) nebula_link_libraries( meta_client_test @@ -130,6 +134,7 @@ add_executable( $ $ $ + $ ) nebula_link_libraries( meta_http_test @@ -141,3 +146,30 @@ 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 47ec58437f3..fe895d9091d 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 kvstore::KVStore* initKV(const char* rootPath) { @@ -177,6 +178,55 @@ class TestUtils { << ", path " << dataPath; return sc; } + + static void mockUserTag(kvstore::KVStore* kv, SchemaVer version = 0) { + std::vector tag; + SchemaVer ver = version; + TagID tagId = 1; + nebula::cpp2::Schema srcsch; + auto type = nebula::cpp2::ValueType(FRAGILE, SupportedType::STRING, nullptr, nullptr); + srcsch.columns.emplace_back(FRAGILE, GLOBAL_USER_ITEM_ACCOUNT, type); + srcsch.columns.emplace_back(FRAGILE, GLOBAL_USER_ITEM_FIRSTNAME, type); + srcsch.columns.emplace_back(FRAGILE, GLOBAL_USER_ITEM_LASTNAME, type); + srcsch.columns.emplace_back(FRAGILE, GLOBAL_USER_ITEM_EMAIL, type); + srcsch.columns.emplace_back(FRAGILE, GLOBAL_USER_ITEM_PHONE, type); + + auto tagIdVal = std::string(reinterpret_cast(&tagId), sizeof(tagId)); + tag.emplace_back(MetaServiceUtils::indexTagKey(0, GLOBAL_USER_SCHEMA_TAG), tagIdVal); + tag.emplace_back(MetaServiceUtils::schemaTagKey(0, tagId, ver), + MetaServiceUtils::schemaTagVal(GLOBAL_USER_SCHEMA_TAG, srcsch)); + + kv->asyncMultiPut(0, 0, std::move(tag), + [] (kvstore::ResultCode code, HostAddr leader) { + ASSERT_EQ(kvstore::ResultCode::SUCCEEDED, code); + UNUSED(leader); + }); + } + + static cpp2::ErrorCode createUser(kvstore::KVStore* kv, + bool missingOk, + folly::StringPiece account, + folly::StringPiece password, + folly::StringPiece first, + folly::StringPiece last, + folly::StringPiece email, + folly::StringPiece phone) { + cpp2::CreateUserReq req; + req.set_missing_ok(missingOk); + req.set_encoded_pwd(password.str()); + decltype(req.user) user(FRAGILE, + account.str(), + first.str(), + last.str(), + email.str(), + phone.str()); + req.set_user(std::move(user)); + auto* processor = CreateUserProcessor::instance(kv); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + return resp.get_code(); + } }; } // namespace meta