diff --git a/etc/nebula-graphd.conf.default b/etc/nebula-graphd.conf.default index ba394ea1e42..6a6c2bd683f 100644 --- a/etc/nebula-graphd.conf.default +++ b/etc/nebula-graphd.conf.default @@ -54,3 +54,7 @@ --default_charset=utf8 # The defaule collate when a space is created --default_collate=utf8_bin + +########## authorization ########## +# Enable authorization +--enable_authorize=false diff --git a/etc/nebula-graphd.conf.production b/etc/nebula-graphd.conf.production index 9fe056ee6e1..de3625d7098 100644 --- a/etc/nebula-graphd.conf.production +++ b/etc/nebula-graphd.conf.production @@ -52,3 +52,7 @@ --ws_h2_port=13002 --heartbeat_interval_secs=10 + +########## authorization ########## +# Enable authorization +--enable_authorize=false \ No newline at end of file diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 43a4c887506..408b01fb7e7 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -14,4 +14,6 @@ nebula_add_subdirectory(test) nebula_add_subdirectory(charset) nebula_add_subdirectory(algorithm) nebula_add_subdirectory(encryption) +nebula_add_subdirectory(permission) +nebula_add_subdirectory(session) diff --git a/src/common/base/Status.cpp b/src/common/base/Status.cpp index 4c4198b6cb2..c8e7df6c2f9 100644 --- a/src/common/base/Status.cpp +++ b/src/common/base/Status.cpp @@ -33,6 +33,9 @@ std::string Status::toString() const { case kSyntaxError: str = "SyntaxError: "; break; + case kPermissionError: + str = "PermissionError: "; + break; default: snprintf(tmp, sizeof(tmp), "Unknown error(%hu): ", static_cast(code())); str = tmp; diff --git a/src/common/permission/CMakeLists.txt b/src/common/permission/CMakeLists.txt new file mode 100644 index 00000000000..1d2163366a0 --- /dev/null +++ b/src/common/permission/CMakeLists.txt @@ -0,0 +1,4 @@ +nebula_add_library( + permission_obj OBJECT + PermissionManager.cpp +) diff --git a/src/common/permission/PermissionManager.cpp b/src/common/permission/PermissionManager.cpp new file mode 100644 index 00000000000..795b982bfa4 --- /dev/null +++ b/src/common/permission/PermissionManager.cpp @@ -0,0 +1,223 @@ +/* Copyright (c) 2020 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 "permission/PermissionManager.h" + +namespace nebula { +namespace permission { + +// static +bool PermissionManager::canReadSpace(session::Session *session, GraphSpaceID spaceId) { + if (!FLAGS_enable_authorize) { + return true; + } + if (session->isGod()) { + return true; + } + bool havePermission = false; + switch (session->roleWithSpace(spaceId)) { + case session::Role::GOD : + case session::Role::ADMIN : + case session::Role::DBA : + case session::Role::USER : + case session::Role::GUEST : { + havePermission = true; + break; + } + case session::Role::INVALID_ROLE : { + break; + } + } + return havePermission; +} + +// static +bool PermissionManager::canReadSchemaOrData(session::Session *session) { + if (session->space() == -1) { + LOG(ERROR) << "The space name is not set"; + return false; + } + if (session->isGod()) { + return true; + } + bool havePermission = false; + switch (session->roleWithSpace(session->space())) { + case session::Role::GOD : + case session::Role::ADMIN : + case session::Role::DBA : + case session::Role::USER : + case session::Role::GUEST : { + havePermission = true; + break; + } + case session::Role::INVALID_ROLE : { + break; + } + } + return havePermission; +} + +// static +bool PermissionManager::canWriteSpace(session::Session *session) { + return session->isGod(); +} + +// static +bool PermissionManager::canWriteSchema(session::Session *session) { + if (session->space() == -1) { + LOG(ERROR) << "The space name is not set"; + return false; + } + if (session->isGod()) { + return true; + } + bool havePermission = false; + switch (session->roleWithSpace(session->space())) { + case session::Role::GOD : + case session::Role::ADMIN : + case session::Role::DBA : { + havePermission = true; + break; + } + case session::Role::USER : + case session::Role::GUEST : + case session::Role::INVALID_ROLE : { + break; + } + } + return havePermission; +} + +// static +bool PermissionManager::canWriteUser(session::Session *session) { + return session->isGod(); +} + +bool PermissionManager::canWriteRole(session::Session *session, + session::Role targetRole, + GraphSpaceID spaceId, + const std::string& targetUser) { + if (!FLAGS_enable_authorize) { + return true; + } + /** + * Reject grant or revoke to himself. + */ + if (session->user() == targetUser) { + return false; + } + /* + * Reject any user grant or revoke role to GOD + */ + if (targetRole == session::Role::GOD) { + return false; + } + /* + * God user can be grant or revoke any one. + */ + if (session->isGod()) { + return true; + } + /** + * Only allow ADMIN user grant or revoke other user to DBA, USER, GUEST. + */ + auto role = session->roleWithSpace(spaceId); + if (role == session::Role::ADMIN && targetRole != session::Role::ADMIN) { + return true; + } + return false; +} + +// static +bool PermissionManager::canWriteData(session::Session *session) { + if (session->space() == -1) { + LOG(ERROR) << "The space name is not set"; + return false; + } + if (session->isGod()) { + return true; + } + bool havePermission = false; + switch (session->roleWithSpace(session->space())) { + case session::Role::GOD : + case session::Role::ADMIN : + case session::Role::DBA : + case session::Role::USER : { + havePermission = true; + break; + } + case session::Role::GUEST : + case session::Role::INVALID_ROLE : { + break; + } + } + return havePermission; +} + +// static +bool PermissionManager::canShow(session::Session *session, + ShowSentence::ShowType type, + GraphSpaceID targetSpace) { + if (!FLAGS_enable_authorize) { + return true; + } + bool havePermission = false; + switch (type) { + case ShowSentence::ShowType::kShowParts: + case ShowSentence::ShowType::kShowTags: + case ShowSentence::ShowType::kShowEdges: + case ShowSentence::ShowType::kShowTagIndexes: + case ShowSentence::ShowType::kShowEdgeIndexes: + case ShowSentence::ShowType::kShowCreateTag: + case ShowSentence::ShowType::kShowCreateEdge: + case ShowSentence::ShowType::kShowCreateTagIndex: + case ShowSentence::ShowType::kShowCreateEdgeIndex: + case ShowSentence::ShowType::kShowTagIndexStatus: + case ShowSentence::ShowType::kShowEdgeIndexStatus: { + /** + * Above operations can get the space id via session, + * so the permission same with canReadSchemaOrData. + * They've been checked by "USE SPACE", so here skip the check. + */ + havePermission = true; + break; + } + case ShowSentence::ShowType::kShowCharset: + case ShowSentence::ShowType::kShowCollation: + case ShowSentence::ShowType::kShowHosts: { + /** + * all roles can be show for above operations. + */ + havePermission = true; + break; + } + case ShowSentence::ShowType::kShowSpaces: + case ShowSentence::ShowType::kShowCreateSpace: + case ShowSentence::ShowType::kShowRoles: { + /* + * Above operations are special operation. + * can not get the space id via session, + * Permission checking needs to be done in their executor. + */ + havePermission = canReadSpace(session, targetSpace); + break; + } + case ShowSentence::ShowType::kShowUsers: + case ShowSentence::ShowType::kShowSnapshots: { + /** + * Only GOD role can be show. + */ + havePermission = session->isGod(); + break; + } + case ShowSentence::ShowType::kUnknown: + break; + } + return havePermission; +} + +} // namespace permission +} // namespace nebula diff --git a/src/common/permission/PermissionManager.h b/src/common/permission/PermissionManager.h new file mode 100644 index 00000000000..e155e15268c --- /dev/null +++ b/src/common/permission/PermissionManager.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2020 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 COMMON_PERMISSION_PERMISSIONMANAGER_H_ +#define COMMON_PERMISSION_PERMISSIONMANAGER_H_ + +#include "base/Base.h" +#include "session/Session.h" +#include "meta/client/MetaClient.h" +#include "parser/Sentence.h" +#include "parser/UserSentences.h" +#include "parser/AdminSentences.h" +#include "graph/GraphFlags.h" + +namespace nebula { +namespace permission { + +class PermissionManager final { +public: + PermissionManager() = delete; + static bool canReadSpace(session::Session *session, GraphSpaceID spaceId); + static bool canReadSchemaOrData(session::Session *session); + static bool canWriteSpace(session::Session *session); + static bool canWriteSchema(session::Session *session); + static bool canWriteUser(session::Session *session); + static bool canWriteRole(session::Session *session, + session::Role targetRole, + GraphSpaceID spaceId, + const std::string& targetUser); + static bool canWriteData(session::Session *session); + static bool canShow(session::Session *session, + ShowSentence::ShowType type, + GraphSpaceID targetSpace = -1); +}; +} // namespace permission +} // namespace nebula + +#endif // COMMON_PERMISSION_PERMISSIONMANAGER_H_ + diff --git a/src/common/session/CMakeLists.txt b/src/common/session/CMakeLists.txt new file mode 100644 index 00000000000..74348bbc362 --- /dev/null +++ b/src/common/session/CMakeLists.txt @@ -0,0 +1,4 @@ +nebula_add_library( + session_obj OBJECT + Session.cpp +) diff --git a/src/common/session/Session.cpp b/src/common/session/Session.cpp new file mode 100644 index 00000000000..3c81ac5bf45 --- /dev/null +++ b/src/common/session/Session.cpp @@ -0,0 +1,28 @@ +/* Copyright (c) 2020 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 "session/Session.h" + +namespace nebula { +namespace session { + +Session::Session(int64_t id) { + id_ = id; +} + +std::shared_ptr Session::create(int64_t id) { + return std::shared_ptr(new Session(id)); +} + +void Session::charge() { + idleDuration_.reset(); +} + +uint64_t Session::idleSeconds() const { + return idleDuration_.elapsedInSec(); +} +} // namespace session +} // namespace nebula diff --git a/src/common/session/Session.h b/src/common/session/Session.h new file mode 100644 index 00000000000..1c68868dab9 --- /dev/null +++ b/src/common/session/Session.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2020 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 COMMON_SESSION_H_ +#define COMMON_SESSION_H_ + +#include "base/Base.h" +#include "time/Duration.h" +#include "interface/gen-cpp2/common_types.h" + +namespace nebula { +namespace session { + +enum class Role : char { + GOD = 1, + ADMIN = 2, + DBA = 3, + USER = 4, + GUEST = 5, + INVALID_ROLE = 6, +}; + +class Session final { +public: + static std::shared_ptr create(int64_t id); + + int64_t id() const { + return id_; + } + + void setId(int64_t id) { + id_ = id; + } + + GraphSpaceID space() const { + return space_; + } + + void setSpace(const std::string &name, GraphSpaceID space) { + spaceName_ = name; + space_ = space; + } + + const std::string& spaceName() const { + return spaceName_; + } + + const std::string& user() const { + return account_; + } + + void setAccount(std::string account) { + account_ = std::move(account); + } + + std::unordered_map roles() const { + return roles_; + } + + Role roleWithSpace(GraphSpaceID space) const { + auto ret = roles_.find(space); + if (ret == roles_.end()) { + return Role::INVALID_ROLE; + } + return ret->second; + } + + Role toRole(nebula::cpp2::RoleType role) { + switch (role) { + case nebula::cpp2::RoleType::GOD : { + return Role::GOD; + } + case nebula::cpp2::RoleType::ADMIN : { + return Role::ADMIN; + } + case nebula::cpp2::RoleType::DBA : { + return Role::DBA; + } + case nebula::cpp2::RoleType::USER : { + return Role::USER; + } + case nebula::cpp2::RoleType::GUEST : { + return Role::GUEST; + } + } + return Role::INVALID_ROLE; + } + + bool isGod() const { + /** + * Only have one user as GOD, the user name is "root". + */ + return user() == "root"; + } + + void setRole(GraphSpaceID space, Role role) { + roles_.emplace(space, role); + } + + uint64_t idleSeconds() const; + + void charge(); + +private: + Session() = default; + explicit Session(int64_t id); + + +private: + int64_t id_{0}; + GraphSpaceID space_{-1}; + std::string spaceName_; + std::string account_; + time::Duration idleDuration_; + /* + * map + * One user can have roles in multiple spaces + * But a user has only one role in one space + */ + std::unordered_map roles_; +}; + +} // namespace session +} // namespace nebula + +#endif // COMMON_SESSION_H_ diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt index 4ebbe1f8e18..ca2964b49de 100644 --- a/src/daemons/CMakeLists.txt +++ b/src/daemons/CMakeLists.txt @@ -33,6 +33,8 @@ nebula_add_executable( $ $ $ + $ + $ LIBRARIES proxygenhttpserver proxygenlib diff --git a/src/graph/CMakeLists.txt b/src/graph/CMakeLists.txt index e4b501d75de..25ac1d87a3a 100644 --- a/src/graph/CMakeLists.txt +++ b/src/graph/CMakeLists.txt @@ -2,10 +2,11 @@ nebula_add_library( graph_obj OBJECT GraphFlags.cpp GraphService.cpp - ClientSession.cpp SessionManager.cpp + PasswordAuthenticator.cpp ExecutionEngine.cpp ExecutionContext.cpp + PermissionCheck.cpp ExecutionPlan.cpp Executor.cpp TraverseExecutor.cpp diff --git a/src/graph/ClientSession.cpp b/src/graph/ClientSession.cpp deleted file mode 100644 index daeaac35e49..00000000000 --- a/src/graph/ClientSession.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (c) 2018 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 "graph/ClientSession.h" - - -namespace nebula { -namespace graph { - -ClientSession::ClientSession(int64_t id) { - id_ = id; -} - -std::shared_ptr ClientSession::create(int64_t id) { - // return std::make_shared(id); - // `std::make_shared' cannot access ClientSession's construtor - return std::shared_ptr(new ClientSession(id)); -} - -void ClientSession::charge() { - idleDuration_.reset(); -} - -uint64_t ClientSession::idleSeconds() const { - return idleDuration_.elapsedInSec(); -} - -} // namespace graph -} // namespace nebula diff --git a/src/graph/ClientSession.h b/src/graph/ClientSession.h deleted file mode 100644 index 605e4433111..00000000000 --- a/src/graph/ClientSession.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright (c) 2018 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_CLIENTSESSION_H_ -#define GRAPH_CLIENTSESSION_H_ - -#include "base/Base.h" -#include "time/Duration.h" - -/** - * A ClientSession holds the context informations of a session opened by a client. - */ - -namespace nebula { -namespace graph { - -class ClientSession final { -public: - int64_t id() const { - return id_; - } - - void setId(int64_t id) { - id_ = id; - } - - GraphSpaceID space() const { - return space_; - } - - void setSpace(const std::string &name, GraphSpaceID space) { - spaceName_ = name; - space_ = space; - } - - const std::string& spaceName() const { - return spaceName_; - } - - uint64_t idleSeconds() const; - - const std::string& user() const { - return user_; - } - - void setUser(std::string user) { - user_ = std::move(user); - } - - void charge(); - -private: - // ClientSession could only be created via SessionManager - friend class SessionManager; - ClientSession() = default; - explicit ClientSession(int64_t id); - - static std::shared_ptr create(int64_t id); - - -private: - int64_t id_{0}; - GraphSpaceID space_{-1}; - time::Duration idleDuration_; - std::string spaceName_; - std::string user_; -}; - -} // namespace graph -} // namespace nebula - -#endif // GRAPH_CLIENTSESSION_H_ diff --git a/src/graph/DescribeSpaceExecutor.cpp b/src/graph/DescribeSpaceExecutor.cpp index 9870cd549b8..5734b4d98c2 100644 --- a/src/graph/DescribeSpaceExecutor.cpp +++ b/src/graph/DescribeSpaceExecutor.cpp @@ -30,6 +30,17 @@ void DescribeSpaceExecutor::execute() { doError(Status::Error("Space not found")); return; } + /** + * Permission check. + */ + auto spaceId = resp.value().get_space_id(); + + auto *session = ectx()->rctx()->session(); + auto rst = permission::PermissionManager::canReadSpace(session, spaceId); + if (!rst) { + doError(Status::PermissionError("Permission denied")); + return; + } resp_ = std::make_unique(); std::vector header{"ID", @@ -43,7 +54,7 @@ void DescribeSpaceExecutor::execute() { std::vector rows; std::vector row; row.resize(6); - row[0].set_integer(resp.value().get_space_id()); + row[0].set_integer(spaceId); auto properties = resp.value().get_properties(); row[1].set_str(properties.get_space_name()); diff --git a/src/graph/DescribeSpaceExecutor.h b/src/graph/DescribeSpaceExecutor.h index 31e8a37b14f..bea024c2c51 100644 --- a/src/graph/DescribeSpaceExecutor.h +++ b/src/graph/DescribeSpaceExecutor.h @@ -9,6 +9,8 @@ #include "base/Base.h" #include "graph/Executor.h" +#include "graph/GraphFlags.h" +#include "common/permission//PermissionManager.h" namespace nebula { namespace graph { diff --git a/src/graph/ExecutionEngine.cpp b/src/graph/ExecutionEngine.cpp index bbb640cea9f..ff8df3fdc95 100644 --- a/src/graph/ExecutionEngine.cpp +++ b/src/graph/ExecutionEngine.cpp @@ -10,13 +10,12 @@ #include "graph/ExecutionPlan.h" #include "storage/client/StorageClient.h" -DECLARE_string(meta_server_addrs); -DECLARE_bool(local_config); namespace nebula { namespace graph { -ExecutionEngine::ExecutionEngine() { +ExecutionEngine::ExecutionEngine(meta::MetaClient* client) { + metaClient_ = client; } @@ -25,31 +24,13 @@ ExecutionEngine::~ExecutionEngine() { Status ExecutionEngine::init(std::shared_ptr ioExecutor) { - auto addrs = network::NetworkUtils::toHosts(FLAGS_meta_server_addrs); - if (!addrs.ok()) { - return addrs.status(); - } - - meta::MetaClientOptions options; - options.serviceName_ = "graph"; - options.skipConfig_ = FLAGS_local_config; - metaClient_ = std::make_unique(ioExecutor, - std::move(addrs.value()), - options); - // load data try 3 time - bool loadDataOk = metaClient_->waitForMetadReady(3); - if (!loadDataOk) { - // Resort to retrying in the background - LOG(WARNING) << "Failed to synchronously wait for meta service ready"; - } - schemaManager_ = meta::SchemaManager::create(); - schemaManager_->init(metaClient_.get()); + schemaManager_->init(metaClient_); - gflagsManager_ = std::make_unique(metaClient_.get()); + gflagsManager_ = std::make_unique(metaClient_); storage_ = std::make_unique(ioExecutor, - metaClient_.get(), + metaClient_, "graph"); charsetInfo_ = CharsetInfo::instance(); @@ -61,7 +42,7 @@ void ExecutionEngine::execute(RequestContextPtr rctx) { schemaManager_.get(), gflagsManager_.get(), storage_.get(), - metaClient_.get(), + metaClient_, charsetInfo_); // TODO(dutor) add support to plan cache auto plan = new ExecutionPlan(std::move(ectx)); diff --git a/src/graph/ExecutionEngine.h b/src/graph/ExecutionEngine.h index 36833b5b41e..08223e64748 100644 --- a/src/graph/ExecutionEngine.h +++ b/src/graph/ExecutionEngine.h @@ -33,7 +33,7 @@ namespace graph { class ExecutionEngine final : public cpp::NonCopyable, public cpp::NonMovable { public: - ExecutionEngine(); + explicit ExecutionEngine(meta::MetaClient* client); ~ExecutionEngine(); Status init(std::shared_ptr ioExecutor); @@ -45,7 +45,7 @@ class ExecutionEngine final : public cpp::NonCopyable, public cpp::NonMovable { std::unique_ptr schemaManager_; std::unique_ptr gflagsManager_; std::unique_ptr storage_; - std::unique_ptr metaClient_; + meta::MetaClient* metaClient_; CharsetInfo* charsetInfo_{nullptr}; }; diff --git a/src/graph/ExecutionPlan.cpp b/src/graph/ExecutionPlan.cpp index fa2e6a50807..93700967ff6 100644 --- a/src/graph/ExecutionPlan.cpp +++ b/src/graph/ExecutionPlan.cpp @@ -79,6 +79,8 @@ void ExecutionPlan::onError(Status status) { rctx->resp().set_error_code(cpp2::ErrorCode::E_SYNTAX_ERROR); } else if (status.isStatementEmpty()) { rctx->resp().set_error_code(cpp2::ErrorCode::E_STATEMENT_EMTPY); + } else if (status.isPermissionError()) { + rctx->resp().set_error_code(cpp2::ErrorCode::E_BAD_PERMISSION); } else { rctx->resp().set_error_code(cpp2::ErrorCode::E_EXECUTION_ERROR); } diff --git a/src/graph/GraphFlags.cpp b/src/graph/GraphFlags.cpp index db63d4ce332..4f21e668c8f 100644 --- a/src/graph/GraphFlags.cpp +++ b/src/graph/GraphFlags.cpp @@ -5,7 +5,6 @@ */ #include "base/Base.h" -#include "graph/GraphFlags.h" DEFINE_int32(port, 3699, "Nebula Graph daemon's listen port"); DEFINE_int32(client_idle_timeout_secs, 0, @@ -32,3 +31,5 @@ DEFINE_bool(local_config, false, "meta client will not retrieve latest configura DEFINE_string(default_charset, "utf8", "The default charset when a space is created"); DEFINE_string(default_collate, "utf8_bin", "The default collate when a space is created"); + +DEFINE_bool(enable_authorize, false, "Enable authorization, default false"); diff --git a/src/graph/GraphFlags.h b/src/graph/GraphFlags.h index 543888ca1b7..aebf1da8fe0 100644 --- a/src/graph/GraphFlags.h +++ b/src/graph/GraphFlags.h @@ -29,5 +29,6 @@ DECLARE_string(meta_server_addrs); DECLARE_string(default_charset); DECLARE_string(default_collate); +DECLARE_bool(enable_authorize); #endif // GRAPH_GRAPHFLAGS_H_ diff --git a/src/graph/GraphService.cpp b/src/graph/GraphService.cpp index 918a5d1a143..4817159b748 100644 --- a/src/graph/GraphService.cpp +++ b/src/graph/GraphService.cpp @@ -6,18 +6,38 @@ #include "base/Base.h" #include "graph/GraphService.h" -#include "time/Duration.h" #include "graph/RequestContext.h" -#include "graph/SimpleAuthenticator.h" #include "storage/client/StorageClient.h" +#include "graph/GraphFlags.h" +#include "common/encryption/MD5Utils.h" + +DECLARE_string(meta_server_addrs); +DECLARE_bool(local_config); namespace nebula { namespace graph { Status GraphService::init(std::shared_ptr ioExecutor) { + auto addrs = network::NetworkUtils::toHosts(FLAGS_meta_server_addrs); + if (!addrs.ok()) { + return addrs.status(); + } + + meta::MetaClientOptions options; + options.serviceName_ = "graph"; + options.skipConfig_ = FLAGS_local_config; + metaClient_ = std::make_unique(ioExecutor, + std::move(addrs.value()), + options); + // load data try 3 time + bool loadDataOk = metaClient_->waitForMetadReady(3); + if (!loadDataOk) { + // Resort to retrying in the background + LOG(WARNING) << "Failed to synchronously wait for meta service ready"; + } + sessionManager_ = std::make_unique(); - executionEngine_ = std::make_unique(); - authenticator_ = std::make_unique(); + executionEngine_ = std::make_unique(metaClient_.get()); return executionEngine_->init(std::move(ioExecutor)); } @@ -31,22 +51,60 @@ folly::Future GraphService::future_authenticate( RequestContext ctx; auto session = sessionManager_->createSession(); - session->setUser(username); + session->setAccount(username); ctx.setSession(std::move(session)); - if (authenticator_->auth(username, password)) { - ctx.resp().set_error_code(cpp2::ErrorCode::SUCCEEDED); - ctx.resp().set_session_id(ctx.session()->id()); + if (!FLAGS_enable_authorize) { + onHandle(ctx, cpp2::ErrorCode::SUCCEEDED); + } else if (auth(username, password)) { + auto roles = metaClient_->getRolesByUserFromCache(username); + for (const auto& role : roles) { + ctx.session()->setRole(role.get_space_id(), toRole(role.get_role_type())); + } + onHandle(ctx, cpp2::ErrorCode::SUCCEEDED); } else { - sessionManager_->removeSession(ctx.session()->id()); - ctx.resp().set_error_code(cpp2::ErrorCode::E_BAD_USERNAME_PASSWORD); - ctx.resp().set_error_msg(getErrorStr(cpp2::ErrorCode::E_BAD_USERNAME_PASSWORD)); + onHandle(ctx, cpp2::ErrorCode::E_BAD_USERNAME_PASSWORD); } ctx.finish(); return ctx.future(); } +void GraphService::onHandle(RequestContext& ctx, cpp2::ErrorCode code) { + ctx.resp().set_error_code(code); + if (code != cpp2::ErrorCode::SUCCEEDED) { + sessionManager_->removeSession(ctx.session()->id()); + ctx.resp().set_error_msg(getErrorStr(code)); + } else { + ctx.resp().set_session_id(ctx.session()->id()); + } +} + +session::Role GraphService::toRole(nebula::cpp2::RoleType role) { + switch (role) { + case nebula::cpp2::RoleType::GOD : { + return session::Role::GOD; + } + case nebula::cpp2::RoleType::ADMIN : { + return session::Role::ADMIN; + } + case nebula::cpp2::RoleType::DBA : { + return session::Role::DBA; + } + case nebula::cpp2::RoleType::USER : { + return session::Role::USER; + } + case nebula::cpp2::RoleType::GUEST : { + return session::Role::GUEST; + } + } + return session::Role::INVALID_ROLE; +} + +bool GraphService::auth(const std::string& username, const std::string& password) { + auto authenticator = std::make_unique(metaClient_.get()); + return authenticator->auth(username, encryption::MD5Utils::md5Encode(password)); +} void GraphService::signout(int64_t sessionId) { VLOG(2) << "Sign out session " << sessionId; @@ -92,6 +150,10 @@ const char* GraphService::getErrorStr(cpp2::ErrorCode result) { return "The session timed out"; case cpp2::ErrorCode::E_SYNTAX_ERROR: return "Syntax error"; + case cpp2::ErrorCode::E_USER_NOT_FOUND: + return "User not exist"; + case cpp2::ErrorCode::E_BAD_PERMISSION: + return "Permission denied"; /********************** * Unknown error **********************/ diff --git a/src/graph/GraphService.h b/src/graph/GraphService.h index 804e49d284e..eff6ccf2eae 100644 --- a/src/graph/GraphService.h +++ b/src/graph/GraphService.h @@ -9,7 +9,7 @@ #include "base/Base.h" #include "gen-cpp2/GraphService.h" -#include "graph/Authenticator.h" +#include "graph/PasswordAuthenticator.h" #include "graph/ExecutionEngine.h" #include "graph/SessionManager.h" @@ -38,10 +38,17 @@ class GraphService final : public cpp2::GraphServiceSvIf { const char* getErrorStr(cpp2::ErrorCode result); +private: + void onHandle(RequestContext& ctx, cpp2::ErrorCode code); + + session::Role toRole(nebula::cpp2::RoleType role); + + bool auth(const std::string& username, const std::string& password); + private: std::unique_ptr sessionManager_; std::unique_ptr executionEngine_; - std::unique_ptr authenticator_; + std::unique_ptr metaClient_; }; } // namespace graph diff --git a/src/graph/PasswordAuthenticator.cpp b/src/graph/PasswordAuthenticator.cpp new file mode 100644 index 00000000000..1e56041db1e --- /dev/null +++ b/src/graph/PasswordAuthenticator.cpp @@ -0,0 +1,21 @@ +/* Copyright (c) 2020 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 "graph/PasswordAuthenticator.h" + +namespace nebula { +namespace graph { + +PasswordAuthenticator::PasswordAuthenticator(meta::MetaClient* client) { + metaClient_ = client; +} + +bool PasswordAuthenticator::auth(const std::string& user, const std::string& password) { + return metaClient_->authCheckFromCache(user, password); +} + +} // namespace graph +} // namespace nebula diff --git a/src/graph/PasswordAuthenticator.h b/src/graph/PasswordAuthenticator.h new file mode 100644 index 00000000000..2b4de8bac13 --- /dev/null +++ b/src/graph/PasswordAuthenticator.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2018 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_PASSWORDAUTHENTICATOR_H_ +#define GRAPH_PASSWORDAUTHENTICATOR_H_ + +#include "base/Base.h" +#include "graph/Authenticator.h" +#include "meta/client/MetaClient.h" + +namespace nebula { +namespace graph { + +class PasswordAuthenticator final : public Authenticator { +public: + explicit PasswordAuthenticator(meta::MetaClient* client); + + bool auth(const std::string& user, const std::string& password) override; + +private: + meta::MetaClient* metaClient_; +}; + +} // namespace graph +} // namespace nebula + +#endif // GRAPH_PASSWORDAUTHENTICATOR_H_ diff --git a/src/graph/PermissionCheck.cpp b/src/graph/PermissionCheck.cpp new file mode 100644 index 00000000000..3f83af701a5 --- /dev/null +++ b/src/graph/PermissionCheck.cpp @@ -0,0 +1,139 @@ +/* Copyright (c) 2020 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 "graph/PermissionCheck.h" + +namespace nebula { +namespace graph { + +/** + * Read space : kUse, kDescribeSpace + * Write space : kCreateSpace, kDropSpace, kCreateSnapshot, kDropSnapshot + * kBalance, kAdmin, kConfig, kIngest, kDownload + * Read schema : kDescribeTag, kDescribeEdge, + * kDescribeTagIndex, kDescribeEdgeIndex + * Write schema : kCreateTag, kAlterTag, kCreateEdge, + * kAlterEdge, kDropTag, kDropEdge, + * kCreateTagIndex, kCreateEdgeIndex, kDropTagIndex, kDropEdgeIndex, + * Read user : + * Write user : kCreateUser, kDropUser, kAlterUser, + * Write role : kGrant, kRevoke, + * Read data : kGo , kSet, kPipe, kMatch, kAssignment, kLookup, + * kYield, kOrderBy, kFetchVertices, kFind + * kFetchEdges, kFindPath, kLimit, KGroupBy, kReturn + * Write data: kBuildTagIndex, kBuildEdgeIndex, + * kInsertVertex, kUpdateVertex, kInsertEdge, + * kUpdateEdge, kDeleteVertex, kDeleteEdges + * Special operation : kShow, kChangePassword + */ + +// static +bool PermissionCheck::permissionCheck(session::Session *session, Sentence* sentence) { + auto kind = sentence->kind(); + switch (kind) { + case Sentence::Kind::kUnknown : { + return false; + } + case Sentence::Kind::kUse : + case Sentence::Kind::kDescribeSpace : { + /** + * Use space and Describe space are special operations. + * Permission checking needs to be done in their executor. + * skip the check at here. + */ + return true; + } + case Sentence::Kind::kCreateSpace : + case Sentence::Kind::kDropSpace : + case Sentence::Kind::kCreateSnapshot : + case Sentence::Kind::kDropSnapshot : + case Sentence::Kind::kBalance : + case Sentence::Kind::kAdmin : + case Sentence::Kind::kConfig : + case Sentence::Kind::kIngest : + case Sentence::Kind::kDownload : { + return permission::PermissionManager::canWriteSpace(session); + } + case Sentence::Kind::kCreateTag : + case Sentence::Kind::kAlterTag : + case Sentence::Kind::kCreateEdge : + case Sentence::Kind::kAlterEdge : + case Sentence::Kind::kDropTag : + case Sentence::Kind::kDropEdge : + case Sentence::Kind::kCreateTagIndex : + case Sentence::Kind::kCreateEdgeIndex : + case Sentence::Kind::kDropTagIndex : + case Sentence::Kind::kDropEdgeIndex : { + return permission::PermissionManager::canWriteSchema(session); + } + case Sentence::Kind::kCreateUser : + case Sentence::Kind::kDropUser : + case Sentence::Kind::kAlterUser : { + return permission::PermissionManager::canWriteUser(session); + } + case Sentence::Kind::kRevoke : + case Sentence::Kind::kGrant : { + /** + * Use grant and revoke are special operations. + * Because have not found the target space id and target role + * so permission checking needs to be done in their executor. + * skip the check at here. + */ + return true; + } + case Sentence::Kind::kRebuildTagIndex : + case Sentence::Kind::kRebuildEdgeIndex : + case Sentence::Kind::kInsertVertex : + case Sentence::Kind::kUpdateVertex : + case Sentence::Kind::kInsertEdge : + case Sentence::Kind::kUpdateEdge : + case Sentence::Kind::kDeleteVertex : + case Sentence::Kind::kDeleteEdges : { + return permission::PermissionManager::canWriteData(session); + } + case Sentence::Kind::kDescribeTag : + case Sentence::Kind::kDescribeEdge : + case Sentence::Kind::kDescribeTagIndex : + case Sentence::Kind::kDescribeEdgeIndex : + case Sentence::Kind::kGo : + case Sentence::Kind::kSet : + case Sentence::Kind::kPipe : + case Sentence::Kind::kMatch : + case Sentence::Kind::kAssignment : + case Sentence::Kind::kLookup : + case Sentence::Kind::kYield : + case Sentence::Kind::kOrderBy : + case Sentence::Kind::kFetchVertices : + case Sentence::Kind::kFetchEdges : + case Sentence::Kind::kFindPath : + case Sentence::Kind::kLimit : + case Sentence::Kind::KGroupBy : + case Sentence::Kind::kReturn : { + return permission::PermissionManager::canReadSchemaOrData(session); + } + case Sentence::Kind::kShow : { + auto *stc = static_cast(sentence); + if (stc->showType() == ShowSentence::ShowType::kShowSpaces || + stc->showType() == ShowSentence::ShowType::kShowCreateSpace || + stc->showType() == ShowSentence::ShowType::kShowRoles) { + /* + * Above operations are special operation. + * can not get the space id via session, + * Permission checking needs to be done in their executor. + * here skip the permission check. + */ + return true; + } + return permission::PermissionManager::canShow(session, stc->showType()); + } + case Sentence::Kind::kChangePassword : { + return true; + } + } + return false; +} +} // namespace graph +} // namespace nebula diff --git a/src/graph/PermissionCheck.h b/src/graph/PermissionCheck.h new file mode 100644 index 00000000000..3422342502d --- /dev/null +++ b/src/graph/PermissionCheck.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2020 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 "common/permission/PermissionManager.h" +#include "parser/Sentence.h" +#include "parser/TraverseSentences.h" +#include "parser/AdminSentences.h" + +#ifndef GRAPH_PERMISSIONCHECK_H_ +#define GRAPH_PERMISSIONCHECK_H_ + +namespace nebula { +namespace graph { + +class PermissionCheck final { +public: + PermissionCheck() = delete; + + static bool permissionCheck(session::Session *session, Sentence* sentence); +}; +} // namespace graph +} // namespace nebula + +#endif // GRAPH_PERMISSIONCHECK_H_ diff --git a/src/graph/PrivilegeExecutor.cpp b/src/graph/PrivilegeExecutor.cpp index 19ef5bed507..6dbde0a0225 100644 --- a/src/graph/PrivilegeExecutor.cpp +++ b/src/graph/PrivilegeExecutor.cpp @@ -34,6 +34,19 @@ void GrantExecutor::execute() { doError(spaceRet.status()); return; } + /** + * Permission check. + */ + auto *session = ectx()->rctx()->session(); + auto role = session->toRole(PrivilegeUtils::toRoleType(aclItem->getRoleType())); + auto rst = permission::PermissionManager::canWriteRole(session, + role, + spaceRet.value(), + *account); + if (!rst) { + doError(Status::PermissionError("Permission denied")); + return; + } roleItem.set_user(*account); roleItem.set_space_id(spaceRet.value()); @@ -86,6 +99,20 @@ void RevokeExecutor::execute() { return; } + /** + * Permission check. + */ + auto *session = ectx()->rctx()->session(); + auto role = session->toRole(PrivilegeUtils::toRoleType(aclItem->getRoleType())); + auto rst = permission::PermissionManager::canWriteRole(session, + role, + spaceRet.value(), + *account); + if (!rst) { + doError(Status::PermissionError("Permission denied")); + return; + } + nebula::cpp2::RoleItem roleItem; roleItem.set_user(*account); roleItem.set_space_id(spaceRet.value()); diff --git a/src/graph/PrivilegeExecutor.h b/src/graph/PrivilegeExecutor.h index 06b6c4e9467..98048479aa8 100644 --- a/src/graph/PrivilegeExecutor.h +++ b/src/graph/PrivilegeExecutor.h @@ -9,7 +9,7 @@ #include "base/Base.h" #include "graph/Executor.h" #include "meta/SchemaManager.h" -#include "graph/GraphFlags.h" +#include "common/permission/PermissionManager.h" namespace nebula { namespace graph { diff --git a/src/graph/RequestContext.h b/src/graph/RequestContext.h index 4dbc34db646..85bb458adc5 100644 --- a/src/graph/RequestContext.h +++ b/src/graph/RequestContext.h @@ -11,7 +11,7 @@ #include "gen-cpp2/GraphService.h" #include "cpp/helpers.h" #include "time/Duration.h" -#include "graph/ClientSession.h" +#include "common/session/Session.h" /** * RequestContext holds context infos of a specific request from a client. @@ -52,7 +52,7 @@ class RequestContext final : public cpp::NonCopyable, public cpp::NonMovable { return promise_.getFuture(); } - void setSession(std::shared_ptr session) { + void setSession(std::shared_ptr session) { session_ = std::move(session); if (session_ != nullptr) { // keep the session active @@ -60,7 +60,7 @@ class RequestContext final : public cpp::NonCopyable, public cpp::NonMovable { } } - ClientSession* session() const { + session::Session* session() const { return session_.get(); } @@ -85,7 +85,7 @@ class RequestContext final : public cpp::NonCopyable, public cpp::NonMovable { std::string query_; Response resp_; folly::Promise promise_; - std::shared_ptr session_; + std::shared_ptr session_; folly::Executor *runner_{nullptr}; }; diff --git a/src/graph/SequentialExecutor.cpp b/src/graph/SequentialExecutor.cpp index 9071daeef0b..4b33a260c54 100644 --- a/src/graph/SequentialExecutor.cpp +++ b/src/graph/SequentialExecutor.cpp @@ -19,10 +19,21 @@ SequentialExecutor::SequentialExecutor(SequentialSentences *sentences, } + Status SequentialExecutor::prepare() { for (auto i = 0U; i < sentences_->sentences_.size(); i++) { auto *sentence = sentences_->sentences_[i].get(); auto executor = makeExecutor(sentence); + if (FLAGS_enable_authorize) { + auto *session = executor->ectx()->rctx()->session(); + /** + * Skip special operations check at here. they are : + * kUse, kDescribeSpace, kRevoke and kGrant. + */ + if (!PermissionCheck::permissionCheck(session, sentence)) { + return Status::PermissionError("Permission denied"); + } + } if (executor == nullptr) { return Status::Error("The statement has not been implemented"); } diff --git a/src/graph/SequentialExecutor.h b/src/graph/SequentialExecutor.h index 094328aa1b4..8d5055ac44b 100644 --- a/src/graph/SequentialExecutor.h +++ b/src/graph/SequentialExecutor.h @@ -9,7 +9,8 @@ #include "base/Base.h" #include "graph/Executor.h" - +#include "graph/GraphFlags.h" +#include "graph/PermissionCheck.h" namespace nebula { namespace graph { diff --git a/src/graph/SessionManager.cpp b/src/graph/SessionManager.cpp index d1ca38915fb..df629d1b1c4 100644 --- a/src/graph/SessionManager.cpp +++ b/src/graph/SessionManager.cpp @@ -29,7 +29,7 @@ SessionManager::~SessionManager() { } -StatusOr> +StatusOr> SessionManager::findSession(int64_t id) { folly::RWSpinLock::ReadHolder holder(rwlock_); auto iter = activeSessions_.find(id); @@ -40,7 +40,7 @@ SessionManager::findSession(int64_t id) { } -std::shared_ptr SessionManager::createSession() { +std::shared_ptr SessionManager::createSession() { folly::RWSpinLock::WriteHolder holder(rwlock_); auto sid = newSessionId(); while (true) { @@ -51,14 +51,14 @@ std::shared_ptr SessionManager::createSession() { sid = newSessionId(); } DCHECK_NE(sid, 0L); - auto session = ClientSession::create(sid); + auto session = session::Session::create(sid); activeSessions_[sid] = session; session->charge(); return session; } -std::shared_ptr SessionManager::removeSession(int64_t id) { +std::shared_ptr SessionManager::removeSession(int64_t id) { folly::RWSpinLock::WriteHolder holder(rwlock_); auto iter = activeSessions_.find(id); if (iter == activeSessions_.end()) { diff --git a/src/graph/SessionManager.h b/src/graph/SessionManager.h index d2dbe91c1b2..f174b536d9f 100644 --- a/src/graph/SessionManager.h +++ b/src/graph/SessionManager.h @@ -9,8 +9,8 @@ #include "base/Base.h" #include "base/StatusOr.h" -#include "graph/ClientSession.h" #include "thread/GenericWorker.h" +#include "common/session/Session.h" /** * SessionManager manages the client sessions, e.g. create new, find existing and drop expired. @@ -24,7 +24,7 @@ class SessionManager final { SessionManager(); ~SessionManager(); - using SessionPtr = std::shared_ptr; + using SessionPtr = std::shared_ptr; /** * Find an existing session */ diff --git a/src/graph/ShowExecutor.cpp b/src/graph/ShowExecutor.cpp index 3bb517d7657..ddd326a607a 100644 --- a/src/graph/ShowExecutor.cpp +++ b/src/graph/ShowExecutor.cpp @@ -6,8 +6,7 @@ #include "graph/ShowExecutor.h" #include "network/NetworkUtils.h" -#include "common/charset/Charset.h" - +#include "common/permission/PermissionManager.h" namespace nebula { namespace graph { @@ -243,6 +242,13 @@ void ShowExecutor::showSpaces() { resp_->set_column_names(std::move(header)); for (auto &space : retShowSpaces) { + auto canShow = permission::PermissionManager::canShow(ectx()->rctx()->session(), + sentence_->showType(), + space.first); + if (!canShow) { + continue; + } + std::vector row; row.emplace_back(); row.back().set_str(std::move(space.second)); @@ -526,7 +532,13 @@ void ShowExecutor::showCreateSpace() { sentence_->getName()->c_str(), resp.status().toString().c_str())); return; } - + auto canShow = permission::PermissionManager::canShow(ectx()->rctx()->session(), + sentence_->showType(), + resp.value().get_space_id()); + if (!canShow) { + doError(Status::PermissionError()); + return; + } resp_ = std::make_unique(); std::vector header{"Space", "Create Space"}; resp_->set_column_names(std::move(header)); @@ -1071,7 +1083,7 @@ void ShowExecutor::showUsers() { for (auto& user : value) { std::vector row; row.resize(1); - row[0].set_str(user); + row[0].set_str(user.first); rows.emplace_back(); rows.back().set_columns(std::move(row)); } @@ -1090,7 +1102,22 @@ void ShowExecutor::showUsers() { void ShowExecutor::showRoles() { auto *space = sentence_->getName(); - auto future = ectx()->getMetaClient()->listRoles(*space); + auto *mc = ectx()->getMetaClient(); + + auto spaceRet = mc->getSpaceIdByNameFromCache(*space); + if (!spaceRet.ok()) { + doError(spaceRet.status()); + return; + } + auto canShow = permission::PermissionManager::canShow(ectx()->rctx()->session(), + sentence_->showType(), + spaceRet.value()); + if (!canShow) { + doError(Status::PermissionError()); + return; + } + + auto future = ectx()->getMetaClient()->listRoles(spaceRet.value()); auto *runner = ectx()->rctx()->runner(); auto cb = [this] (auto &&resp) { diff --git a/src/graph/SimpleAuthenticator.h b/src/graph/SimpleAuthenticator.h deleted file mode 100644 index 6ded4b242cc..00000000000 --- a/src/graph/SimpleAuthenticator.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (c) 2018 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_SIMPLEAUTHENTICATOR_H_ -#define GRAPH_SIMPLEAUTHENTICATOR_H_ - -#include "base/Base.h" -#include "graph/Authenticator.h" - -namespace nebula { -namespace graph { - -class SimpleAuthenticator final : public Authenticator { -public: - bool MUST_USE_RESULT auth(const std::string &user, - const std::string &password) override { - if (user == "user" && password == "password") { - return true; - } - return false; - } - -private: -}; - -} // namespace graph -} // namespace nebula - -#endif // GRAPH_SIMPLEAUTHENTICATOR_H_ diff --git a/src/graph/UseExecutor.cpp b/src/graph/UseExecutor.cpp index e43aa4f1472..9f2d0b992e0 100644 --- a/src/graph/UseExecutor.cpp +++ b/src/graph/UseExecutor.cpp @@ -32,7 +32,19 @@ void UseExecutor::execute() { } auto spaceId = resp.value().get_space_id(); + + /** + * Permission check. + */ + auto *session = ectx()->rctx()->session(); + auto rst = permission::PermissionManager::canReadSpace(session, spaceId); + if (!rst) { + doError(Status::PermissionError("Permission denied")); + return; + } + ectx()->rctx()->session()->setSpace(*sentence_->space(), spaceId); + FLOG_INFO("Graph space switched to `%s', space id: %d", sentence_->space()->c_str(), spaceId); diff --git a/src/graph/UseExecutor.h b/src/graph/UseExecutor.h index 66abd214b13..2e3491f7868 100644 --- a/src/graph/UseExecutor.h +++ b/src/graph/UseExecutor.h @@ -9,6 +9,8 @@ #include "base/Base.h" #include "graph/Executor.h" +#include "graph/GraphFlags.h" +#include "common/permission/PermissionManager.h" namespace nebula { namespace graph { diff --git a/src/graph/test/CMakeLists.txt b/src/graph/test/CMakeLists.txt index 0ff0a49010e..88c0fb9c633 100644 --- a/src/graph/test/CMakeLists.txt +++ b/src/graph/test/CMakeLists.txt @@ -33,6 +33,8 @@ set(GRAPH_TEST_LIBS $ $ $ + $ + $ ) set(GRAPH_TEST_CLIENT_LIBS @@ -372,3 +374,23 @@ nebula_add_test( wangle gtest ) + +nebula_add_test( + NAME + permission_test + SOURCES + PermissionTest.cpp + OBJECTS + $ + $ + $ + $ + ${GRAPH_TEST_LIBS} + LIBRARIES + ${THRIFT_LIBRARIES} + ${ROCKSDB_LIBRARIES} + proxygenlib + wangle + gtest +) + diff --git a/src/graph/test/PermissionTest.cpp b/src/graph/test/PermissionTest.cpp new file mode 100644 index 00000000000..918abd32d88 --- /dev/null +++ b/src/graph/test/PermissionTest.cpp @@ -0,0 +1,834 @@ +/* Copyright (c) 2020 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 "graph/test/TestEnv.h" +#include "graph/test/TestBase.h" +#include "meta/test/TestUtils.h" + +DECLARE_int32(heartbeat_interval_secs); + +namespace nebula { +namespace graph { + +class PermissionTest : public TestBase { +protected: + void SetUp() override { + TestBase::SetUp(); + // ... + } + + void TearDown() override { + // ... + TestBase::TearDown(); + } +}; + +TEST_F(PermissionTest, SimpleTest) { + FLAGS_heartbeat_interval_secs = 1; + FLAGS_enable_authorize = true; + /* + * test incorrect password. + */ + { + auto client = gEnv->getClient("root", "pwd"); + ASSERT_EQ(nullptr, client); + } + /* + * test incorrect user name. + */ + { + auto client = gEnv->getClient("user", "nebula"); + ASSERT_EQ(nullptr, client); + } + /* + * test root user password and use space. + */ + { + auto client = gEnv->getClient(); + ASSERT_NE(nullptr, client); + cpp2::ExecutionResponse resp; + std::string query = "CREATE SPACE my_space(partition_num=1, replica_factor=1)"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "USE my_space; CREATE TAG person(name string)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "USE my_space"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG person(name string)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + client->disconnect(); + } + /* + * change root password, incorrect password. + */ + { + auto client = gEnv->getClient(); + ASSERT_NE(nullptr, client); + cpp2::ExecutionResponse resp; + std::string query = "CHANGE PASSWORD root FROM \"aa\" TO \"bb\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + client->disconnect(); + } + /* + * change root password, correct password. + */ + { + auto client = gEnv->getClient(); + ASSERT_NE(nullptr, client); + cpp2::ExecutionResponse resp; + std::string query = "CHANGE PASSWORD root FROM \"nebula\" TO \"bb\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + client->disconnect(); + } + /* + * verify password changed + */ + sleep(FLAGS_heartbeat_interval_secs + 1); + { + auto client = gEnv->getClient("root", "nebula"); + ASSERT_EQ(nullptr, client); + } + { + auto client = gEnv->getClient("root", "bb"); + ASSERT_NE(nullptr, client); + cpp2::ExecutionResponse resp; + std::string query = "CHANGE PASSWORD root FROM \"bb\" TO \"nebula\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + client->disconnect(); + } + sleep(FLAGS_heartbeat_interval_secs + 1); +} + +TEST_F(PermissionTest, UserWriteTest) { + FLAGS_heartbeat_interval_secs = 1; + FLAGS_enable_authorize = true; + sleep(FLAGS_heartbeat_interval_secs + 1); + auto client = gEnv->getClient(); + ASSERT_NE(nullptr, client); + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE SPACE space1(partition_num=1, replica_factor=1)"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + sleep(FLAGS_heartbeat_interval_secs + 1); + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE USER admin WITH PASSWORD \"admin\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE ADMIN ON space1 TO admin"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE GOD ON space1 TO admin"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW ROLES IN space1"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_EQ(1, resp.get_rows()->size()); + } + sleep(FLAGS_heartbeat_interval_secs + 1); + { + auto adminClient = gEnv->getClient("admin", "admin"); + ASSERT_NE(nullptr, adminClient); + cpp2::ExecutionResponse resp; + std::string query = "ALTER USER root WITH PASSWORD \"root\""; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + auto adminClient = gEnv->getClient("admin", "admin"); + ASSERT_NE(nullptr, adminClient); + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE ADMIN ON space1 TO admin"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + auto adminClient = gEnv->getClient("admin", "admin"); + ASSERT_NE(nullptr, adminClient); + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE GOD ON space1 TO admin"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + auto adminClient = gEnv->getClient("admin", "admin"); + ASSERT_NE(nullptr, adminClient); + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE GOD ON space1 TO admin"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + /** + * Reject the admin user grant or revoke to himself self + */ + { + auto adminClient = gEnv->getClient("admin", "admin"); + ASSERT_NE(nullptr, adminClient); + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE GUEST ON space1 TO admin"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + auto adminClient = gEnv->getClient("admin", "admin"); + ASSERT_NE(nullptr, adminClient); + cpp2::ExecutionResponse resp; + std::string query = "DROP USER admin"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "DROP USER admin"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } +} + +TEST_F(PermissionTest, SchemaAndDataTest) { + FLAGS_enable_authorize = true; + auto client = gEnv->getClient(); + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE SPACE space2(partition_num=1, replica_factor=1)"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + sleep(FLAGS_heartbeat_interval_secs + 1); + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE USER admin WITH PASSWORD \"admin\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE ADMIN ON space2 TO admin"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE USER dba WITH PASSWORD \"dba\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE DBA ON space2 TO dba"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE USER user WITH PASSWORD \"user\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE USER ON space2 TO user"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE USER guest WITH PASSWORD \"guest\""; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "GRANT ROLE GUEST ON space2 TO guest"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + sleep(FLAGS_heartbeat_interval_secs + 1); + /** + * god write schema test + */ + { + cpp2::ExecutionResponse resp; + std::string query = "USE space2"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG t1(t_c int)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE EDGE e1(e_c int)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG INDEX tid1 ON t1(t_c)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE EDGE INDEX eid1 ON e1(e_c)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE TAG t1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE EDGE e1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE TAG INDEX tid1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE EDGE INDEX eid1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP TAG INDEX tid1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP EDGE INDEX eid1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "ALTER TAG t1 DROP (t_c)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "ALTER EDGE e1 DROP (e_c)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP TAG t1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP EDGE e1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + /** + * admin write schema test + */ + sleep(FLAGS_heartbeat_interval_secs + 1); + auto adminClient = gEnv->getClient("admin", "admin"); + ASSERT_NE(nullptr, adminClient); + { + cpp2::ExecutionResponse resp; + std::string query = "USE space2"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG t1(t_c int)"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE EDGE e1(e_c int)"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG INDEX tid1 ON t1(t_c)"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE EDGE INDEX eid1 ON e1(e_c)"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE TAG t1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE EDGE e1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE TAG INDEX tid1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE EDGE INDEX eid1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP TAG INDEX tid1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP EDGE INDEX eid1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "ALTER TAG t1 DROP (t_c)"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "ALTER EDGE e1 DROP (e_c)"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP TAG t1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP EDGE e1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + + /** + * dba write schema test + */ + sleep(FLAGS_heartbeat_interval_secs + 1); + auto dbaClient = gEnv->getClient("dba", "dba"); + ASSERT_NE(nullptr, dbaClient); + { + cpp2::ExecutionResponse resp; + std::string query = "USE space2"; + auto code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG t1(t_c int)"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE EDGE e1(e_c int)"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG INDEX tid1 ON t1(t_c)"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE EDGE INDEX eid1 ON e1(e_c)"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE TAG t1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE EDGE e1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE TAG INDEX tid1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DESCRIBE EDGE INDEX eid1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP TAG INDEX tid1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP EDGE INDEX eid1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "ALTER TAG t1 DROP (t_c)"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "ALTER EDGE e1 DROP (e_c)"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP TAG t1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "DROP EDGE e1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + + /** + * user write schema test + */ + sleep(FLAGS_heartbeat_interval_secs + 1); + auto userClient = gEnv->getClient("user", "user"); + ASSERT_NE(nullptr, userClient); + { + cpp2::ExecutionResponse resp; + std::string query = "USE space2"; + auto code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG t1(t_c int)"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "CREATE EDGE e1(e_c int)"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "CREATE TAG INDEX tid1 ON t1(t_c)"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "CREATE EDGE INDEX eid1 ON e1(e_c)"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DESCRIBE TAG t1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DESCRIBE EDGE e1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DESCRIBE TAG INDEX tid1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DESCRIBE EDGE INDEX eid1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DROP TAG INDEX tid1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DROP EDGE INDEX eid1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "ALTER TAG t1 DROP (t_c)"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "ALTER EDGE e1 DROP (e_c)"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DROP TAG t1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DROP EDGE e1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + + /** + * guest write schema test + */ + sleep(FLAGS_heartbeat_interval_secs + 1); + auto guestClient = gEnv->getClient("guest", "guest"); + ASSERT_NE(nullptr, guestClient); + { + cpp2::ExecutionResponse resp; + std::string query = "USE space2"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE TAG t1(t_c int)"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "CREATE EDGE e1(e_c int)"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "CREATE TAG INDEX tid1 ON t1(t_c)"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "CREATE EDGE INDEX eid1 ON e1(e_c)"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DESCRIBE TAG t1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DESCRIBE EDGE e1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DESCRIBE TAG INDEX tid1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DESCRIBE EDGE INDEX eid1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + + query = "DROP TAG INDEX tid1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DROP EDGE INDEX eid1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "ALTER TAG t1 DROP (t_c)"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "ALTER EDGE e1 DROP (e_c)"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DROP TAG t1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "DROP EDGE e1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + /** + * god write data test + */ + { + cpp2::ExecutionResponse resp; + auto query = "CREATE TAG t1(t_c int)"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "CREATE EDGE e1(e_c int)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + sleep(FLAGS_heartbeat_interval_secs + 1); + + query = "INSERT VERTEX t1(t_c) VALUES 1:(1)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "INSERT EDGE e1(e_c) VALUES 1 -> 2:(95)"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "GO FROM 1 OVER e1"; + code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + /** + * admin write data test + */ + { + cpp2::ExecutionResponse resp; + auto query = "INSERT VERTEX t1(t_c) VALUES 1:(1)"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "INSERT EDGE e1(e_c) VALUES 1 -> 2:(95)"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "GO FROM 1 OVER e1"; + code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + /** + * dba write data test + */ + { + cpp2::ExecutionResponse resp; + auto query = "INSERT VERTEX t1(t_c) VALUES 1:(1)"; + auto code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "INSERT EDGE e1(e_c) VALUES 1 -> 2:(95)"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "GO FROM 1 OVER e1"; + code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + /** + * user write data test + */ + { + cpp2::ExecutionResponse resp; + auto query = "INSERT VERTEX t1(t_c) VALUES 1:(1)"; + auto code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "INSERT EDGE e1(e_c) VALUES 1 -> 2:(95)"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + + query = "GO FROM 1 OVER e1"; + code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + /** + * guest write data test + */ + { + cpp2::ExecutionResponse resp; + auto query = "INSERT VERTEX t1(t_c) VALUES 1:(1)"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "INSERT EDGE e1(e_c) VALUES 1 -> 2:(95)"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + + query = "GO FROM 1 OVER e1"; + code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + /** + * use space test + */ + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE SPACE space3(partition_num=1, replica_factor=1)"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + sleep(FLAGS_heartbeat_interval_secs + 1); + { + cpp2::ExecutionResponse resp; + std::string query = "USE space3"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "USE space3"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "USE space3"; + auto code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "USE space3"; + auto code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "USE space3"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } +} +TEST_F(PermissionTest, ShowTest) { + auto client = gEnv->getClient(); + auto adminClient = gEnv->getClient("admin", "admin"); + auto dbaClient = gEnv->getClient("dba", "dba"); + auto userClient = gEnv->getClient("user", "user"); + auto guestClient = gEnv->getClient("guest", "guest"); + { + cpp2::ExecutionResponse resp; + std::string query = "CREATE SPACE space4(partition_num=1, replica_factor=1)"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + sleep(FLAGS_heartbeat_interval_secs + 1); + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW SPACES"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_EQ(5, resp.get_rows()->size()); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW SPACES"; + auto code = adminClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_EQ(1, resp.get_rows()->size()); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW SPACES"; + auto code = dbaClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_EQ(1, resp.get_rows()->size()); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW SPACES"; + auto code = userClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_EQ(1, resp.get_rows()->size()); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW SPACES"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + ASSERT_EQ(1, resp.get_rows()->size()); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW ROLES IN space1"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW ROLES IN space2"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW ROLES IN space1"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW CREATE SPACE space1"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW CREATE SPACE space1"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "SHOW CREATE SPACE space2"; + auto code = guestClient->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } +} +} // namespace graph +} // namespace nebula diff --git a/src/graph/test/TestEnv.cpp b/src/graph/test/TestEnv.cpp index a2408f4a605..4a978294876 100644 --- a/src/graph/test/TestEnv.cpp +++ b/src/graph/test/TestEnv.cpp @@ -8,6 +8,7 @@ #include "graph/test/TestEnv.h" #include "meta/test/TestUtils.h" #include "storage/test/TestUtils.h" +#include "meta/RootUserMan.h" DECLARE_int32(heartbeat_interval_secs); DECLARE_string(meta_server_addrs); @@ -48,6 +49,10 @@ void TestEnv::SetUp() { } auto& localhost = hostRet.value(); + if (!nebula::meta::RootUserMan::initRootUser(metaServer_->kvStore_.get())) { + LOG(ERROR) << "Init root user failed"; + } + meta::MetaClientOptions options; options.localHost_ = localhost; options.clusterId_ = kClusterId; @@ -94,9 +99,10 @@ uint16_t TestEnv::storageServerPort() const { return storageServer_->port_; } -std::unique_ptr TestEnv::getClient() const { +std::unique_ptr TestEnv::getClient(const std::string& user, + const std::string& password) const { auto client = std::make_unique("127.0.0.1", graphServerPort()); - if (cpp2::ErrorCode::SUCCEEDED != client->connect("user", "password")) { + if (cpp2::ErrorCode::SUCCEEDED != client->connect(user, password)) { return nullptr; } return client; diff --git a/src/graph/test/TestEnv.h b/src/graph/test/TestEnv.h index c28ec6216c2..66c1ca55501 100644 --- a/src/graph/test/TestEnv.h +++ b/src/graph/test/TestEnv.h @@ -13,6 +13,7 @@ #include "test/ServerContext.h" #include #include "TestUtils.h" +#include "graph/GraphFlags.h" namespace nebula { namespace graph { @@ -31,7 +32,8 @@ class TestEnv : public ::testing::Environment { uint16_t metaServerPort() const; uint16_t storageServerPort() const; - std::unique_ptr getClient() const; + std::unique_ptr getClient(const std::string& user = "root", + const std::string& password = "nebula") const; meta::ClientBasedGflagsManager* gflagsManager(); diff --git a/src/graph/test/UserTest.cpp b/src/graph/test/UserTest.cpp index 7447f1068b3..29a69712dda 100644 --- a/src/graph/test/UserTest.cpp +++ b/src/graph/test/UserTest.cpp @@ -8,7 +8,7 @@ #include "graph/test/TestEnv.h" #include "graph/test/TestBase.h" -DECLARE_uint32(raft_heartbeat_interval_secs); +DECLARE_int32(heartbeat_interval_secs); namespace nebula { namespace graph { @@ -60,12 +60,12 @@ TEST_F(UserTest, CreateUser) { std::string query = "SHOW USERS"; auto code = client->execute(query, resp); ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); - ASSERT_EQ(2, resp.get_rows()->size()); + ASSERT_EQ(3, resp.get_rows()->size()); decltype(resp.column_names) colNames = {"Account"}; ASSERT_TRUE(verifyColNames(resp, colNames)); std::vector> - rows = { {"user1"}, {"user2"}, }; + rows = {{"root"}, {"user1"}, {"user2"}, }; ASSERT_TRUE(verifyResult(resp, rows)); } } @@ -119,7 +119,7 @@ TEST_F(UserTest, DropUser) { std::string query = "SHOW USERS"; auto code = client->execute(query, resp); ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); - ASSERT_EQ(2, resp.get_rows()->size()); + ASSERT_EQ(3, resp.get_rows()->size()); } } @@ -164,7 +164,7 @@ TEST_F(UserTest, GrantRevoke) { auto code = client->execute(cmd, resp); ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); } - sleep(FLAGS_raft_heartbeat_interval_secs); + sleep(FLAGS_heartbeat_interval_secs + 1); // must set the space if is not god role. expect fail. { cpp2::ExecutionResponse resp; @@ -175,15 +175,15 @@ TEST_F(UserTest, GrantRevoke) { // user not exist. expect fail. { cpp2::ExecutionResponse resp; - std::string cmd = "GRANT DBA ON user_space TO user"; + std::string cmd = "GRANT DBA ON user_space TO user"; auto code = client->execute(cmd, resp); ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); } { cpp2::ExecutionResponse resp; - std::string cmd = "GRANT GOD ON user_space TO user1"; + std::string cmd = "GRANT GOD ON user_space TO user1"; auto code = client->execute(cmd, resp); - ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + ASSERT_EQ(cpp2::ErrorCode::E_BAD_PERMISSION, code); } // space not exists. expect fail. { @@ -197,7 +197,7 @@ TEST_F(UserTest, GrantRevoke) { std::string query = "SHOW USERS"; auto code = client->execute(query, resp); ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); - ASSERT_EQ(2, resp.get_rows()->size()); + ASSERT_EQ(3, resp.get_rows()->size()); } { cpp2::ExecutionResponse resp; diff --git a/src/interface/common.thrift b/src/interface/common.thrift index 9156d6bff2b..8fe8bbff948 100644 --- a/src/interface/common.thrift +++ b/src/interface/common.thrift @@ -112,13 +112,13 @@ struct Pair { } /** -** GOD is A global senior administrator.like root of Linux systems. -** ADMIN is an administrator for a given Graph Space. -** DBA is an schema 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. -**/ + ** GOD is A global senior administrator.like root of Linux systems. + ** ADMIN is an administrator for a given Graph Space. + ** DBA is an schema 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, diff --git a/src/interface/graph.thrift b/src/interface/graph.thrift index cd55f7d62e0..85f5b307ea9 100644 --- a/src/interface/graph.thrift +++ b/src/interface/graph.thrift @@ -29,6 +29,11 @@ enum ErrorCode { E_EXECUTION_ERROR = -8, // Nothing is executed When command is comment E_STATEMENT_EMTPY = -9, + + // User and permission error + E_USER_NOT_FOUND = -10, + E_BAD_PERMISSION = -11, + } (cpp.enum_strict) diff --git a/src/interface/meta.thrift b/src/interface/meta.thrift index 8a6c696a453..67ba0864710 100644 --- a/src/interface/meta.thrift +++ b/src/interface/meta.thrift @@ -524,23 +524,23 @@ struct RevokeRoleReq { 1: common.RoleItem role_item, } -struct AuthCheckReq { - 1: string account, - 2: string encoded_pwd, -} - struct ListUsersReq { } struct ListUsersResp { - 1: ErrorCode code, + 1: ErrorCode code, // Valid if ret equals E_LEADER_CHANGED. - 2: common.HostAddr leader, - 3: list users, + 2: common.HostAddr leader, + // map + 3: map users, } struct ListRolesReq { - 1: string space, + 1: common.GraphSpaceID space_id, +} + +struct GetUserRolesReq { + 1: string account, } struct ListRolesResp { @@ -737,7 +737,7 @@ service MetaService { ExecResp revokeRole(1: RevokeRoleReq req); ListUsersResp listUsers(1: ListUsersReq req); ListRolesResp listRoles(1: ListRolesReq req); - ExecResp authCheck(1: AuthCheckReq req); + ListRolesResp getUserRoles(1: GetUserRolesReq req); ExecResp changePassword(1: ChangePasswordReq req); HBResp heartBeat(1: HBReq req); diff --git a/src/meta/MetaServiceHandler.cpp b/src/meta/MetaServiceHandler.cpp index 3d15cbe7a33..13293c16709 100644 --- a/src/meta/MetaServiceHandler.cpp +++ b/src/meta/MetaServiceHandler.cpp @@ -332,9 +332,9 @@ MetaServiceHandler::future_changePassword(const cpp2::ChangePasswordReq& req) { RETURN_FUTURE(processor); } -folly::Future -MetaServiceHandler::future_authCheck(const cpp2::AuthCheckReq& req) { - auto* processor = AuthCheckProcessor::instance(kvstore_); +folly::Future +MetaServiceHandler::future_getUserRoles(const cpp2::GetUserRolesReq& req) { + auto* processor = GetUserRolesProcessor::instance(kvstore_); RETURN_FUTURE(processor); } diff --git a/src/meta/MetaServiceHandler.h b/src/meta/MetaServiceHandler.h index 990893d0aeb..aa7a774cd15 100644 --- a/src/meta/MetaServiceHandler.h +++ b/src/meta/MetaServiceHandler.h @@ -169,8 +169,8 @@ class MetaServiceHandler final : public cpp2::MetaServiceSvIf { folly::Future future_changePassword(const cpp2::ChangePasswordReq& req) override; - folly::Future - future_authCheck(const cpp2::AuthCheckReq& req) override; + folly::Future + future_getUserRoles(const cpp2::GetUserRolesReq& req) override; /** * HeartBeat diff --git a/src/meta/MetaServiceUtils.cpp b/src/meta/MetaServiceUtils.cpp index db480225c5f..8351f146bdb 100644 --- a/src/meta/MetaServiceUtils.cpp +++ b/src/meta/MetaServiceUtils.cpp @@ -531,10 +531,24 @@ std::string MetaServiceUtils::userKey(const std::string& account) { return key; } +std::string MetaServiceUtils::userVal(const std::string& val) { + std::string key; + auto pwdLen = val.size(); + key.reserve(sizeof(int64_t) + pwdLen); + key.append(reinterpret_cast(&pwdLen), sizeof(size_t)) + .append(val); + return key; +} + std::string MetaServiceUtils::parseUser(folly::StringPiece key) { return key.subpiece(kUsersTable.size(), key.size() - kUsersTable.size()).str(); } +std::string MetaServiceUtils::parseUserPwd(folly::StringPiece val) { + auto len = *reinterpret_cast(val.data()); + return val.subpiece(sizeof(size_t), len).str(); +} + std::string MetaServiceUtils::roleKey(GraphSpaceID spaceId, const std::string& account) { std::string key; key.reserve(kRolesTable.size() + sizeof(GraphSpaceID) + account.size()); @@ -556,6 +570,10 @@ std::string MetaServiceUtils::parseRoleUser(folly::StringPiece key) { return key.subpiece(offset, key.size() - offset).str(); } +GraphSpaceID MetaServiceUtils::parseRoleSpace(folly::StringPiece key) { + return *reinterpret_cast(key.data() + kRolesTable.size()); +} + std::string MetaServiceUtils::rolesPrefix() { return kRolesTable; } diff --git a/src/meta/MetaServiceUtils.h b/src/meta/MetaServiceUtils.h index 8e21d39bbfe..dd81f957637 100644 --- a/src/meta/MetaServiceUtils.h +++ b/src/meta/MetaServiceUtils.h @@ -145,14 +145,20 @@ class MetaServiceUtils final { static std::string userKey(const std::string& account); + static std::string userVal(const std::string& val); + static std::string parseUser(folly::StringPiece key); + static std::string parseUserPwd(folly::StringPiece val); + static std::string roleKey(GraphSpaceID spaceId, const std::string& account); static std::string roleVal(nebula::cpp2::RoleType roleType); static std::string parseRoleUser(folly::StringPiece key); + static GraphSpaceID parseRoleSpace(folly::StringPiece key); + static std::string rolesPrefix(); static std::string roleSpacePrefix(GraphSpaceID spaceId); diff --git a/src/meta/RootUserMan.h b/src/meta/RootUserMan.h index e88fe6d7688..23dba19d68c 100644 --- a/src/meta/RootUserMan.h +++ b/src/meta/RootUserMan.h @@ -35,12 +35,14 @@ class RootUserMan { static bool initRootUser(kvstore::KVStore* kv) { LOG(INFO) << "Init root user"; + auto encodedPwd = encryption::MD5Utils::md5Encode("nebula"); auto userKey = MetaServiceUtils::userKey("root"); + auto userVal = MetaServiceUtils::userVal(std::move(encodedPwd)); auto roleKey = MetaServiceUtils::roleKey(kDefaultSpaceId, "root"); auto roleVal = MetaServiceUtils::roleVal(nebula::cpp2::RoleType::GOD); std::vector data; - data.emplace_back(std::move(userKey), encryption::MD5Utils::md5Encode("nebula")); + data.emplace_back(std::move(userKey), std::move(userVal)); data.emplace_back(std::move(roleKey), std::move(roleVal)); bool ret = true; folly::Baton baton; diff --git a/src/meta/client/MetaClient.cpp b/src/meta/client/MetaClient.cpp index 56f187c2048..37bb43fddfd 100644 --- a/src/meta/client/MetaClient.cpp +++ b/src/meta/client/MetaClient.cpp @@ -132,12 +132,42 @@ void MetaClient::heartBeatThreadFunc() { } } +bool MetaClient::loadUsersAndRoles() { + auto userRoleRet = listUsers().get(); + if (!userRoleRet.ok()) { + LOG(ERROR) << "List users failed, status:" << userRoleRet.status(); + return false; + } + decltype(userRolesMap_) userRolesMap; + decltype(userPasswordMap_) userPasswordMap; + for (auto& user : userRoleRet.value()) { + auto rolesRet = getUserRoles(user.first).get(); + if (!rolesRet.ok()) { + LOG(ERROR) << "List role by user failed, user : " << user.first; + return false; + } + userRolesMap[user.first] = rolesRet.value(); + userPasswordMap[user.first] = user.second; + } + { + folly::RWSpinLock::WriteHolder holder(localCacheLock_); + userRolesMap_ = std::move(userRolesMap); + userPasswordMap_ = std::move(userPasswordMap); + } + return true; +} + bool MetaClient::loadData() { if (ioThreadPool_->numThreads() <= 0) { LOG(ERROR) << "The threads number in ioThreadPool should be greater than 0"; return false; } + if (!loadUsersAndRoles()) { + LOG(ERROR) << "Load roles Failed"; + return false; + } + auto ret = listSpaces().get(); if (!ret.ok()) { LOG(ERROR) << "List space failed, status:" << ret.status(); @@ -1622,6 +1652,23 @@ const std::vector& MetaClient::getAddresses() { return addrs_; } +std::vector +MetaClient::getRolesByUserFromCache(const std::string& user) { + auto iter = userRolesMap_.find(user); + if (iter == userRolesMap_.end()) { + return std::vector(0); + } + return iter->second; +} + +bool MetaClient::authCheckFromCache(const std::string& account, const std::string& password) { + auto iter = userPasswordMap_.find(account); + if (iter == userPasswordMap_.end()) { + return false; + } + return iter->second == password; +} + StatusOr MetaClient::getLatestTagVersionFromCache(const GraphSpaceID& space, const TagID& tagId) { if (!ready_) { @@ -1772,10 +1819,10 @@ MetaClient::revokeFromUser(nebula::cpp2::RoleItem roleItem) { return future; } -folly::Future>> +folly::Future>> MetaClient::listUsers() { cpp2::ListUsersReq req; - folly::Promise>> promise; + folly::Promise>> promise; auto future = promise.getFuture(); getResponse(std::move(req), [] (auto client, auto request) { return client->future_listUsers(request); @@ -1786,9 +1833,9 @@ MetaClient::listUsers() { } folly::Future>> -MetaClient::listRoles(std::string space) { +MetaClient::listRoles(GraphSpaceID space) { cpp2::ListRolesReq req; - req.set_space(std::move(space)); + req.set_space_id(std::move(space)); folly::Promise>> promise; auto future = promise.getFuture(); getResponse(std::move(req), [] (auto client, auto request) { @@ -1817,18 +1864,17 @@ MetaClient::changePassword(std::string account, return future; } -folly::Future> -MetaClient::authCheck(std::string account, std::string password) { - cpp2::AuthCheckReq req; +folly::Future>> +MetaClient::getUserRoles(std::string account) { + cpp2::GetUserRolesReq req; req.set_account(std::move(account)); - req.set_encoded_pwd(std::move(password)); - folly::Promise> promise; + folly::Promise>> promise; auto future = promise.getFuture(); getResponse(std::move(req), [] (auto client, auto request) { - return client->future_authCheck(request); - }, [] (cpp2::ExecResp&& resp) -> bool { - return resp.code == cpp2::ErrorCode::SUCCEEDED; - }, std::move(promise), true); + return client->future_getUserRoles(request); + }, [] (cpp2::ListRolesResp&& resp) -> decltype(auto) { + return std::move(resp).get_roles(); + }, std::move(promise)); return future; } diff --git a/src/meta/client/MetaClient.h b/src/meta/client/MetaClient.h index a8c5e741c28..e5cecb74278 100644 --- a/src/meta/client/MetaClient.h +++ b/src/meta/client/MetaClient.h @@ -83,6 +83,11 @@ using LeaderMap = std::unordered_map, HostA using IndexStatus = std::tuple; +// get user roles by account +using UserRolesMap = std::unordered_map>; +// get user password by account +using UserPasswordMap = std::unordered_map; + struct ConfigItem { ConfigItem() {} @@ -349,17 +354,17 @@ class MetaClient { folly::Future> revokeFromUser(nebula::cpp2::RoleItem roleItem); - folly::Future>> + folly::Future>> listUsers(); folly::Future>> - listRoles(std::string space); + listRoles(GraphSpaceID space); folly::Future> changePassword(std::string account, std::string newPwd, std::string oldPwd); - folly::Future> - authCheck(std::string account, std::string password); + folly::Future>> + getUserRoles(std::string account); // Operations for admin folly::Future> @@ -472,6 +477,10 @@ class MetaClient { EdgeType edgeType, const std::string& field); + std::vector getRolesByUserFromCache(const std::string& user); + + bool authCheckFromCache(const std::string& account, const std::string& password); + Status refreshCache(); StatusOr loadLeader(); @@ -496,6 +505,8 @@ class MetaClient { SpaceNewestEdgeVerMap &newestEdgeVerMap, SpaceAllEdgeMap &allEdgemap); + bool loadUsersAndRoles(); + bool loadIndexes(GraphSpaceID spaceId, std::shared_ptr cache); @@ -577,6 +588,9 @@ class MetaClient { SpaceNewestEdgeVerMap spaceNewestEdgeVerMap_; SpaceAllEdgeMap spaceAllEdgeMap_; + UserRolesMap userRolesMap_; + UserPasswordMap userPasswordMap_; + NameIndexMap tagNameIndexMap_; NameIndexMap edgeNameIndexMap_; diff --git a/src/meta/processors/BaseProcessor.inl b/src/meta/processors/BaseProcessor.inl index c72cd7cb35e..48992baccaa 100644 --- a/src/meta/processors/BaseProcessor.inl +++ b/src/meta/processors/BaseProcessor.inl @@ -331,7 +331,7 @@ template bool BaseProcessor::checkPassword(const std::string& account, const std::string& password) { auto userKey = MetaServiceUtils::userKey(account); auto ret = doGet(userKey); - return ret.value() == password; + return MetaServiceUtils::parseUserPwd(ret.value()) == password; } template diff --git a/src/meta/processors/usersMan/AuthenticationProcessor.cpp b/src/meta/processors/usersMan/AuthenticationProcessor.cpp index d056bdd7b31..33815ed7406 100644 --- a/src/meta/processors/usersMan/AuthenticationProcessor.cpp +++ b/src/meta/processors/usersMan/AuthenticationProcessor.cpp @@ -20,7 +20,7 @@ void CreateUserProcessor::process(const cpp2::CreateUserReq& req) { code = cpp2::ErrorCode::SUCCEEDED; } else { LOG(ERROR) << "Create User Failed : User " << account - << " have existed!"; + << " already existed!"; code = cpp2::ErrorCode::E_EXISTED; } handleErrorCode(code); @@ -29,9 +29,10 @@ void CreateUserProcessor::process(const cpp2::CreateUserReq& req) { } std::vector data; - data.emplace_back(MetaServiceUtils::userKey(account), password); + data.emplace_back(MetaServiceUtils::userKey(account), + MetaServiceUtils::userVal(password)); handleErrorCode(cpp2::ErrorCode::SUCCEEDED); - doPut(std::move(data)); + doSyncPutAndUpdate(std::move(data)); } @@ -40,6 +41,7 @@ void AlterUserProcessor::process(const cpp2::AlterUserReq& req) { const auto& account = req.get_account(); const auto& password = req.get_encoded_pwd(); auto userKey = MetaServiceUtils::userKey(account); + auto userVal = MetaServiceUtils::userVal(password); std::string val; auto result = kvstore_->get(kDefaultSpaceId, kDefaultPartId, userKey, &val); if (result != kvstore::ResultCode::SUCCEEDED) { @@ -48,9 +50,9 @@ void AlterUserProcessor::process(const cpp2::AlterUserReq& req) { return; } std::vector data; - data.emplace_back(std::move(userKey), password); + data.emplace_back(std::move(userKey), std::move(userVal)); handleErrorCode(cpp2::ErrorCode::SUCCEEDED); - doPut(std::move(data)); + doSyncPutAndUpdate(std::move(data)); } @@ -88,7 +90,7 @@ void DropUserProcessor::process(const cpp2::DropUserReq& req) { handleErrorCode(cpp2::ErrorCode::SUCCEEDED); LOG(INFO) << "Drop User " << req.get_account(); - doMultiRemove(std::move(keys)); + doSyncMultiRemoveAndUpdate({std::move(keys)}); } @@ -108,7 +110,7 @@ void GrantProcessor::process(const cpp2::GrantRoleReq& req) { data.emplace_back(MetaServiceUtils::roleKey(spaceId, roleItem.get_user()), MetaServiceUtils::roleVal(roleItem.get_role_type())); handleErrorCode(cpp2::ErrorCode::SUCCEEDED); - doPut(std::move(data)); + doSyncPutAndUpdate(std::move(data)); } @@ -139,7 +141,7 @@ void RevokeProcessor::process(const cpp2::RevokeRoleReq& req) { return; } handleErrorCode(cpp2::ErrorCode::SUCCEEDED); - doRemove(roleKey); + doSyncMultiRemoveAndUpdate({std::move(roleKey)}); } @@ -159,17 +161,11 @@ void ChangePasswordProcessor::process(const cpp2::ChangePasswordReq& req) { } auto userKey = MetaServiceUtils::userKey(req.get_account()); - std::string val; - auto result = kvstore_->get(kDefaultSpaceId, kDefaultPartId, userKey, &val); - if (result != kvstore::ResultCode::SUCCEEDED) { - handleErrorCode(cpp2::ErrorCode::E_NOT_FOUND); - onFinished(); - return; - } + auto userVal = MetaServiceUtils::userVal(req.get_new_encoded_pwd()); std::vector data; - data.emplace_back(std::move(userKey), req.get_new_encoded_pwd()); + data.emplace_back(std::move(userKey), std::move(userVal)); handleErrorCode(cpp2::ErrorCode::SUCCEEDED); - doPut(std::move(data)); + doSyncPutAndUpdate(std::move(data)); } @@ -187,8 +183,9 @@ void ListUsersProcessor::process(const cpp2::ListUsersReq& req) { } decltype(resp_.users) users; while (iter->valid()) { - auto user = MetaServiceUtils::parseUser(iter->key()); - users.emplace_back(std::move(user)); + auto account = MetaServiceUtils::parseUser(iter->key()); + auto password = MetaServiceUtils::parseUserPwd(iter->val()); + users.emplace(std::pair(std::move(account), std::move(password))); iter->next(); } resp_.set_users(users); @@ -198,19 +195,14 @@ void ListUsersProcessor::process(const cpp2::ListUsersReq& req) { void ListRolesProcessor::process(const cpp2::ListRolesReq& req) { - auto spaceRet = getSpaceId(req.get_space()); - if (!spaceRet.ok()) { - LOG(ERROR) << "space dose not found"; - handleErrorCode(MetaCommon::to(spaceRet.status())); - onFinished(); - return; - } + auto spaceId = req.get_space_id(); + CHECK_SPACE_ID_AND_RETURN(spaceId); folly::SharedMutex::ReadHolder rHolder(LockUtils::userLock()); - auto prefix = MetaServiceUtils::roleSpacePrefix(spaceRet.value()); + 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 " << spaceRet.value(); + LOG(ERROR) << "Can't find any roles by space id " << spaceId; handleErrorCode(cpp2::ErrorCode::E_NOT_FOUND); onFinished(); return; @@ -222,7 +214,7 @@ void ListRolesProcessor::process(const cpp2::ListRolesReq& req) { auto val = iter->val(); nebula::cpp2::RoleItem role; role.set_user(std::move(account)); - role.set_space_id(spaceRet.value()); + role.set_space_id(spaceId); role.set_role_type(*reinterpret_cast(val.begin())); roles.emplace_back(role); iter->next(); @@ -232,22 +224,36 @@ void ListRolesProcessor::process(const cpp2::ListRolesReq& req) { onFinished(); } -void AuthCheckProcessor::process(const cpp2::AuthCheckReq& req) { +void GetUserRolesProcessor::process(const cpp2::GetUserRolesReq& req) { folly::SharedMutex::WriteHolder wHolder(LockUtils::userLock()); - auto userRet = userExist(req.get_account()); - if (!userRet.ok()) { - handleErrorCode(MetaCommon::to(userRet)); + auto prefix = MetaServiceUtils::rolesPrefix(); + 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 user " << req.get_account(); + handleErrorCode(cpp2::ErrorCode::E_NOT_FOUND); onFinished(); return; } - if (!checkPassword(req.get_account(), req.get_encoded_pwd())) { - handleErrorCode(cpp2::ErrorCode::E_INVALID_PASSWORD); - onFinished(); - return; + decltype(resp_.roles) roles; + while (iter->valid()) { + auto account = MetaServiceUtils::parseRoleUser(iter->key()); + auto spaceId = MetaServiceUtils::parseRoleSpace(iter->key()); + if (account == req.get_account()) { + auto val = iter->val(); + nebula::cpp2::RoleItem role; + role.set_user(std::move(account)); + role.set_space_id(spaceId); + role.set_role_type(*reinterpret_cast(val.begin())); + roles.emplace_back(role); + } + iter->next(); } + resp_.set_roles(roles); handleErrorCode(cpp2::ErrorCode::SUCCEEDED); onFinished(); } + } // namespace meta } // namespace nebula diff --git a/src/meta/processors/usersMan/AuthenticationProcessor.h b/src/meta/processors/usersMan/AuthenticationProcessor.h index 8370d441bec..9d738124b86 100644 --- a/src/meta/processors/usersMan/AuthenticationProcessor.h +++ b/src/meta/processors/usersMan/AuthenticationProcessor.h @@ -122,17 +122,17 @@ class ListRolesProcessor : public BaseProcessor { : BaseProcessor(kvstore) {} }; -class AuthCheckProcessor : public BaseProcessor { +class GetUserRolesProcessor : public BaseProcessor { public: - static AuthCheckProcessor* instance(kvstore::KVStore* kvstore) { - return new AuthCheckProcessor(kvstore); + static GetUserRolesProcessor* instance(kvstore::KVStore* kvstore) { + return new GetUserRolesProcessor(kvstore); } - void process(const cpp2::AuthCheckReq& req); + void process(const cpp2::GetUserRolesReq& req); private: - explicit AuthCheckProcessor(kvstore::KVStore* kvstore) - : BaseProcessor(kvstore) {} + explicit GetUserRolesProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} }; } // namespace meta } // namespace nebula diff --git a/src/meta/test/AuthProcessorTest.cpp b/src/meta/test/AuthProcessorTest.cpp index 536d0bf1efa..1bb75cde76e 100644 --- a/src/meta/test/AuthProcessorTest.cpp +++ b/src/meta/test/AuthProcessorTest.cpp @@ -64,39 +64,6 @@ TEST(AuthProcessorTest, CreateUserTest) { auto resp = std::move(f).get(); ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); } - // authCheck - { - cpp2::AuthCheckReq req; - req.set_account("user1"); - req.set_encoded_pwd("password"); - auto* processor = AuthCheckProcessor::instance(kv.get()); - auto f = processor->getFuture(); - processor->process(req); - auto resp = std::move(f).get(); - ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); - } - // authCheck, user not exists. - { - cpp2::AuthCheckReq req; - req.set_account("user4"); - req.set_encoded_pwd("password"); - auto* processor = AuthCheckProcessor::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()); - } - // authCheck, password invalid. - { - cpp2::AuthCheckReq req; - req.set_account("user1"); - req.set_encoded_pwd("passwordd"); - auto* processor = AuthCheckProcessor::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()); - } } TEST(AuthProcessorTest, AlterUserTest) { @@ -125,16 +92,6 @@ TEST(AuthProcessorTest, AlterUserTest) { auto resp = std::move(f).get(); ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); } - { - cpp2::AuthCheckReq req; - req.set_account("user1"); - req.set_encoded_pwd("password_1"); - auto* processor = AuthCheckProcessor::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 user not exists { cpp2::AlterUserReq req; @@ -355,7 +312,7 @@ TEST(AuthProcessorTest, GrantRevokeTest) { // list roles. { cpp2::ListRolesReq req; - req.set_space("space1"); + req.set_space_id(space1); auto* processor = ListRolesProcessor::instance(kv.get()); auto f = processor->getFuture(); processor->process(req); @@ -376,7 +333,7 @@ TEST(AuthProcessorTest, GrantRevokeTest) { // list roles. { cpp2::ListRolesReq req; - req.set_space("space2"); + req.set_space_id(space2); auto* processor = ListRolesProcessor::instance(kv.get()); auto f = processor->getFuture(); processor->process(req); @@ -460,7 +417,7 @@ TEST(AuthProcessorTest, GrantRevokeTest) { // list roles. { cpp2::ListRolesReq req; - req.set_space("space1"); + req.set_space_id(space1); auto* processor = ListRolesProcessor::instance(kv.get()); auto f = processor->getFuture(); processor->process(req); @@ -477,7 +434,7 @@ TEST(AuthProcessorTest, GrantRevokeTest) { // list roles. { cpp2::ListRolesReq req; - req.set_space("space2"); + req.set_space_id(space2); auto* processor = ListRolesProcessor::instance(kv.get()); auto f = processor->getFuture(); processor->process(req); @@ -528,7 +485,7 @@ TEST(AuthProcessorTest, GrantRevokeTest) { // list roles. { cpp2::ListRolesReq req; - req.set_space("space2"); + req.set_space_id(space2); auto* processor = ListRolesProcessor::instance(kv.get()); auto f = processor->getFuture(); processor->process(req); @@ -552,7 +509,7 @@ TEST(AuthProcessorTest, GrantRevokeTest) { } { cpp2::ListRolesReq req; - req.set_space("space1"); + req.set_space_id(space1); auto* processor = ListRolesProcessor::instance(kv.get()); auto f = processor->getFuture(); processor->process(req); @@ -646,17 +603,6 @@ TEST(AuthProcessorTest, ChangePasswordTest) { auto resp = std::move(f).get(); ASSERT_EQ(cpp2::ErrorCode::E_INVALID_PASSWORD, resp.get_code()); } - // authCheck - { - cpp2::AuthCheckReq req; - req.set_account("user1"); - req.set_encoded_pwd("pwd1"); - auto* processor = AuthCheckProcessor::instance(kv.get()); - auto f = processor->getFuture(); - processor->process(req); - auto resp = std::move(f).get(); - ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.get_code()); - } } } // namespace meta diff --git a/src/parser/Sentence.h b/src/parser/Sentence.h index ad40b7a7661..02e7146e272 100644 --- a/src/parser/Sentence.h +++ b/src/parser/Sentence.h @@ -47,7 +47,6 @@ class Sentence { kShow, kDeleteVertex, kDeleteEdges, - kFind, kLookup, kCreateSpace, kDropSpace,