diff --git a/ydb/core/mon/events_internal.h b/ydb/core/mon/events_internal.h new file mode 100644 index 000000000000..fbb68252c510 --- /dev/null +++ b/ydb/core/mon/events_internal.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +namespace NMonitoring::NPrivate { + +struct TEvMon { + enum { + EvBegin = EventSpaceBegin(NActors::TEvents::ES_PRIVATE), + + EvMonitoringRequest = EvBegin, + EvMonitoringResponse, + EvRegisterHandler, + EvMonitoringCancelRequest, + EvCleanupProxy, + + End + }; + + static_assert(End < EventSpaceEnd(NActors::TEvents::ES_PRIVATE), "expect End < EventSpaceEnd(TEvents::ES_PRIVATE)"); + + struct TEvMonitoringRequest : NActors::TEventPB { + TEvMonitoringRequest() = default; + }; + + struct TEvMonitoringResponse : NActors::TEventPB { + TEvMonitoringResponse() = default; + }; + + struct TEvRegisterHandler : NActors::TEventLocal { + NActors::TMon::TRegisterHandlerFields Fields; + + TEvRegisterHandler(const NActors::TMon::TRegisterHandlerFields& fields) + : Fields(fields) + {} + }; + + struct TEvMonitoringCancelRequest : NActors::TEventPB { + TEvMonitoringCancelRequest() = default; + }; + + struct TEvCleanupProxy : NActors::TEventLocal { + TString Address; + + TEvCleanupProxy(const TString& address) + : Address(address) + {} + }; +}; + +} // namespace NMonitoring::NPrivate diff --git a/ydb/core/mon/mon.cpp b/ydb/core/mon/mon.cpp index a633068f0611..ecc307d1f5e1 100644 --- a/ydb/core/mon/mon.cpp +++ b/ydb/core/mon/mon.cpp @@ -1,5 +1,6 @@ #include "mon.h" #include "mon_impl.h" +#include "events_internal.h" #include "counters_adapter_impl.h" #include @@ -32,51 +33,10 @@ namespace NActors { -struct TEvMon { - enum { - EvMonitoringRequest = NActors::NMon::HttpInfo + 10, - EvMonitoringResponse, - EvRegisterHandler, - EvMonitoringCancelRequest, - EvCleanupProxy, - End - }; - - static_assert(EvMonitoringRequest > NMon::End, "expect EvMonitoringRequest > NMon::End"); - static_assert(End < EventSpaceEnd(NActors::TEvents::ES_MON), "expect End < EventSpaceEnd(NActors::TEvents::ES_MON)"); - - struct TEvMonitoringRequest : TEventPB { - TEvMonitoringRequest() = default; - }; - - struct TEvMonitoringResponse : TEventPB { - TEvMonitoringResponse() = default; - }; - - struct TEvRegisterHandler : TEventLocal { - TMon::TRegisterHandlerFields Fields; - - TEvRegisterHandler(const TMon::TRegisterHandlerFields& fields) - : Fields(fields) - {} - }; - - struct TEvMonitoringCancelRequest : TEventPB { - TEvMonitoringCancelRequest() = default; - }; - - struct TEvCleanupProxy : TEventLocal { - TString Address; - - TEvCleanupProxy(const TString& address) - : Address(address) - {} - }; -}; - namespace { using namespace NKikimr; +using namespace NMonitoring::NPrivate; bool HasJsonContent(NHttp::THttpIncomingRequest* request) { if (request->Method == "POST") { diff --git a/ydb/core/mon/mon_ut.cpp b/ydb/core/mon/mon_ut.cpp new file mode 100644 index 000000000000..e3507ba63ec4 --- /dev/null +++ b/ydb/core/mon/mon_ut.cpp @@ -0,0 +1,353 @@ +#include +#include +#include +#include + +#include +#include +#include + +namespace NMonitoring::NTests { + +using namespace NActors; +using namespace NKikimr; +using namespace NKikimr::Tests; + +void GrantConnect(Tests::TClient& client) { + client.CreateUser("/Root", "username", "password"); + client.GrantConnect("username"); + + const auto alterAttrsStatus = client.AlterUserAttributes("/", "Root", { + { "folder_id", "test_folder_id" }, + { "database_id", "test_database_id" }, + }); + UNIT_ASSERT_EQUAL(alterAttrsStatus, NMsgBusProxy::MSTATUS_OK); +} + +struct THttpMonTestEnvOptions { + enum class ERegKind { + None, + ActorPage, + ActorHandler, + MonPage, + }; + + ERegKind RegKind = ERegKind::None; + TVector ActorAllowedSIDs; + TVector TicketParserGroupSIDs = DEFAULT_TICKET_PARSER_GROUPS; + bool UseAuth = true; +}; + +class THttpMonTestEnv { +public: + THttpMonTestEnv(const THttpMonTestEnvOptions& options = {}) + : Port(PortManager.GetPort(2134)) + , GrpcPort(PortManager.GetPort(2135)) + , MonPort(PortManager.GetPort(8765)) + , Settings(Port) + , Options(std::move(options)) + { + Settings.InitKikimrRunConfig() + .SetNodeCount(1) + .SetUseRealThreads(true) + .SetDomainName("Root") + .SetUseSectorMap(true) + .SetMonitoringPortOffset(MonPort, true); + + auto& securityConfig = *Settings.AppConfig->MutableDomainsConfig()->MutableSecurityConfig(); + securityConfig.SetEnforceUserTokenCheckRequirement(true); + securityConfig.MutableMonitoringAllowedSIDs()->Add("ydb.clusters.monitor@as"); + + Settings.CreateTicketParser = [&](const TTicketParserSettings&) -> NActors::IActor* { + return TicketParser = new TFakeTicketParserActor(Options.TicketParserGroupSIDs); + }; + + Server = std::make_unique(Settings); + Server->EnableGRpc(GrpcPort); + Client = std::make_unique(Settings); + Client->InitRootScheme(); + GrantConnect(*Client); + + Runtime = Server->GetRuntime(); + + TMon* mon = Runtime->GetAppData().Mon; + UNIT_ASSERT(mon != nullptr); + + switch (Options.RegKind) { + case THttpMonTestEnvOptions::ERegKind::None: + break; + case THttpMonTestEnvOptions::ERegKind::ActorPage: { + TestActorPage = new TTestActorPage(); + TestActorId = Runtime->Register(TestActorPage); + mon->RegisterActorPage({ + .RelPath = TEST_MON_PATH, + .ActorSystem = Runtime->GetActorSystem(0), + .ActorId = TestActorId, + .UseAuth = Options.UseAuth, + .AllowedSIDs = Options.ActorAllowedSIDs, + }); + break; + } + case THttpMonTestEnvOptions::ERegKind::ActorHandler: { + TestActorHandler = new TTestActorHandler(); + TestActorId = Runtime->Register(TestActorHandler); + mon->RegisterActorHandler({ + .Path = MakeDefaultUrl(), + .Handler = TestActorId, + .UseAuth = Options.UseAuth, + .AllowedSIDs = Options.ActorAllowedSIDs, + }); + break; + } + case THttpMonTestEnvOptions::ERegKind::MonPage: { + mon->Register(new TTestMonPage()); + break; + } + } + + HttpClient = std::make_unique("localhost", MonPort); + } + + TKeepAliveHttpClient::THeaders MakeAuthHeaders(const TString& token = VALID_TOKEN) const { + TKeepAliveHttpClient::THeaders headers; + headers[AUTHORIZATION_HEADER] = token; + return headers; + } + + TString MakeDefaultUrl() const { + TStringBuilder url; + url << "/" << TEST_MON_PATH; + return url; + } + + TFakeTicketParserActor* GetTicketParser() const { + UNIT_ASSERT(TicketParser); + return TicketParser; + } + + TKeepAliveHttpClient& GetHttpClient() const { + return *HttpClient; + } + +private: + TPortManager PortManager; + ui16 Port; + ui16 GrpcPort; + ui16 MonPort; + TServerSettings Settings; + std::unique_ptr Server; + std::unique_ptr Client; + TTestActorRuntime* Runtime = nullptr; + TTestActorPage* TestActorPage = nullptr; + TTestActorHandler* TestActorHandler = nullptr; + TActorId TestActorId; + TFakeTicketParserActor* TicketParser = nullptr; + std::unique_ptr HttpClient; + THttpMonTestEnvOptions Options; +}; + +Y_UNIT_TEST_SUITE(ActorPage) { + Y_UNIT_TEST(HttpOk) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorPage, + .ActorAllowedSIDs = {"valid_group"}, + .TicketParserGroupSIDs = {"valid_group"}, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders()); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_OK); + + const TString response = responseStream.ReadAll(); + UNIT_ASSERT_STRING_CONTAINS(response, TEST_RESPONSE); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 0); + } + + Y_UNIT_TEST(NoValidGroupForbidden) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorPage, + .ActorAllowedSIDs = {"valid_group"}, + .TicketParserGroupSIDs = {"wrong_group"}, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders()); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_FORBIDDEN); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 0); + } + + Y_UNIT_TEST(InvalidTokenForbidden) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorPage, + }); + + TStringStream responseStream; + const TString invalidToken = TString("Bearer invalid"); + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders(invalidToken)); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_FORBIDDEN); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 1); + } + + Y_UNIT_TEST(NoUseAuthOk) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorPage, + .UseAuth = false, + }); + + TStringStream responseStream; + const TString invalidToken = TString("Bearer invalid"); + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders(invalidToken)); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_OK); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 0); + } + + Y_UNIT_TEST(OptionsNoContent) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorPage, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoRequest("OPTIONS", env.MakeDefaultUrl(), "", &responseStream); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_NO_CONTENT); + } +} + +Y_UNIT_TEST_SUITE(ActorHandler) { + Y_UNIT_TEST(HttpOk) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorHandler, + .ActorAllowedSIDs = {"valid_group"}, + .TicketParserGroupSIDs = {"valid_group"}, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders()); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_OK); + + const TString response = responseStream.ReadAll(); + UNIT_ASSERT_STRING_CONTAINS(response, TEST_RESPONSE); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 0); + } + + Y_UNIT_TEST(NoValidGroupForbidden) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorHandler, + .ActorAllowedSIDs = {"valid_group"}, + .TicketParserGroupSIDs = {"wrong_group"}, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders()); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_FORBIDDEN); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 0); + } + + Y_UNIT_TEST(InvalidTokenForbidden) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorHandler, + }); + + TStringStream responseStream; + const TString invalidToken = TString("Bearer invalid"); + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders(invalidToken)); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_FORBIDDEN); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 1); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 1); + } + + Y_UNIT_TEST(NoUseAuthOk) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorHandler, + .UseAuth = false, + }); + + TStringStream responseStream; + const TString invalidToken = TString("Bearer invalid"); + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders(invalidToken)); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_OK); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 0); + } + + Y_UNIT_TEST(OptionsNoContent) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::ActorHandler, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoRequest("OPTIONS", env.MakeDefaultUrl(), "", &responseStream); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_NO_CONTENT); + } +} + +Y_UNIT_TEST_SUITE(MonPage) { + Y_UNIT_TEST(HttpOk) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::MonPage, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoGet(env.MakeDefaultUrl(), &responseStream, env.MakeAuthHeaders()); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_OK); + + const TString response = responseStream.ReadAll(); + UNIT_ASSERT_STRING_CONTAINS(response, TEST_RESPONSE); + + TFakeTicketParserActor* ticketParser = env.GetTicketParser(); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketRequests, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketSuccesses, 0); + UNIT_ASSERT_VALUES_EQUAL(ticketParser->AuthorizeTicketFails, 0); + } + + Y_UNIT_TEST(OptionsNoContent) { + THttpMonTestEnv env({ + .RegKind = THttpMonTestEnvOptions::ERegKind::MonPage, + }); + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoRequest("OPTIONS", env.MakeDefaultUrl(), "", &responseStream); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_NO_CONTENT); + } +} + +Y_UNIT_TEST_SUITE(Other) { + Y_UNIT_TEST(UnknownPathNotFound) { + THttpMonTestEnv env; + + TStringStream responseStream; + const auto status = env.GetHttpClient().DoGet("/wrong_path", &responseStream, env.MakeAuthHeaders()); + UNIT_ASSERT_VALUES_EQUAL(status, HTTP_NOT_FOUND); + } +} + +} // namespace NMonitoring::NTests diff --git a/ydb/core/mon/ut/ya.make b/ydb/core/mon/ut/ya.make new file mode 100644 index 000000000000..ae78f5b6cc7c --- /dev/null +++ b/ydb/core/mon/ut/ya.make @@ -0,0 +1,21 @@ +UNITTEST_FOR(ydb/core/mon) + +FORK_SUBTESTS() + +SIZE(MEDIUM) + +PEERDIR( + ydb/core/mon + ydb/core/mon/ut_utils + ydb/core/testlib/default + ydb/library/aclib + ydb/library/actors/core +) + +SRCS( + mon_ut.cpp +) + +YQL_LAST_ABI_VERSION() + +END() diff --git a/ydb/core/mon/ut_utils/ut_utils.cpp b/ydb/core/mon/ut_utils/ut_utils.cpp new file mode 100644 index 000000000000..1dfe21040048 --- /dev/null +++ b/ydb/core/mon/ut_utils/ut_utils.cpp @@ -0,0 +1,133 @@ +#include "ut_utils.h" + +#include + +namespace NMonitoring::NTests { + +using namespace NActors; +using namespace NKikimr; + +const TString TEST_MON_PATH = "test_mon"; +const TString TEST_RESPONSE = "Test actor"; +const TString AUTHORIZATION_HEADER = "Authorization"; +const TString VALID_TOKEN = "Bearer token"; +const TVector DEFAULT_TICKET_PARSER_GROUPS = {"group_name"}; + +void TTestActorPage::Bootstrap() { + Become(&TTestActorPage::StateWork); +} + +void TTestActorPage::Handle(NMon::TEvHttpInfo::TPtr& ev) { + TStringBuilder body; + body << "

" << TEST_RESPONSE << "

"; + Send(ev->Sender, new NMon::TEvHttpInfoRes(body)); +} + +void TTestActorHandler::Bootstrap() { + Become(&TTestActorHandler::StateWork); +} + +void TTestActorHandler::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr& ev) { + TStringBuilder body; + body << "

" << TEST_RESPONSE << "

"; + + TStringBuilder response; + response << "HTTP/1.1 200 OK\r\n" + << "Content-Type: text/html\r\n" + << "Content-Length: " << body.size() << "\r\n" + << "Connection: Close\r\n\r\n" + << body; + + Send(ev->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse( + ev->Get()->Request->CreateResponseString(response))); +} + +TTestMonPage::TTestMonPage() + : NMonitoring::IMonPage(TEST_MON_PATH, TString("Test Page")) +{ +} + +void TTestMonPage::Output(NMonitoring::IMonHttpRequest& request) { + const TStringBuf pathInfo = request.GetPathInfo(); + if (!pathInfo.empty() && pathInfo != TStringBuf("/")) { + request.Output() << NMonitoring::HTTPNOTFOUND; + return; + } + + auto& out = request.Output(); + out << NMonitoring::HTTPOKHTML; + out << "

" << TEST_RESPONSE << "

"; +} + +TFakeTicketParserActor::TFakeTicketParserActor(TVector groupSIDs) + : TActor(&TFakeTicketParserActor::StateFunc) + , GroupSIDs(std::move(groupSIDs)) +{} + +void TFakeTicketParserActor::Handle(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev) { + LOG_INFO_S(*TlsActivationContext, NKikimrServices::TICKET_PARSER, "Ticket parser: got TEvAuthorizeTicket event: " << ev->Get()->Ticket << " " << ev->Get()->Database << " " << ev->Get()->Entries.size()); + ++AuthorizeTicketRequests; + + if (ev->Get()->Database != "/Root") { + Fail(ev, TStringBuilder() << "Incorrect database " << ev->Get()->Database); + return; + } + + if (ev->Get()->Ticket != VALID_TOKEN) { + Fail(ev, TStringBuilder() << "Incorrect token " << ev->Get()->Ticket); + return; + } + + bool databaseIdFound = false; + bool folderIdFound = false; + for (const TEvTicketParser::TEvAuthorizeTicket::TEntry& entry : ev->Get()->Entries) { + for (const std::pair& attr : entry.Attributes) { + if (attr.first == "database_id") { + databaseIdFound = true; + if (attr.second != "test_database_id") { + Fail(ev, TStringBuilder() << "Incorrect database_id " << attr.second); + return; + } + } else if (attr.first == "folder_id") { + folderIdFound = true; + if (attr.second != "test_folder_id") { + Fail(ev, TStringBuilder() << "Incorrect folder_id " << attr.second); + return; + } + } + } + } + if (!databaseIdFound) { + Fail(ev, "database_id not found"); + return; + } + if (!folderIdFound) { + Fail(ev, "folder_id not found"); + return; + } + + Success(ev); +} + +void TFakeTicketParserActor::Fail(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev, const TString& message) { + ++AuthorizeTicketFails; + TEvTicketParser::TError err; + err.Retryable = false; + err.Message = message ? message : "Test error"; + LOG_INFO_S(*TlsActivationContext, NKikimrServices::TICKET_PARSER, + "Send TEvAuthorizeTicketResult: " << err.Message); + Send(ev->Sender, new TEvTicketParser::TEvAuthorizeTicketResult(ev->Get()->Ticket, err)); +} + +void TFakeTicketParserActor::Success(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev) { + ++AuthorizeTicketSuccesses; + NACLib::TUserToken::TUserTokenInitFields args; + args.UserSID = "username"; + args.GroupSIDs = GroupSIDs; + TIntrusivePtr userToken = MakeIntrusive(args); + LOG_INFO_S(*TlsActivationContext, NKikimrServices::TICKET_PARSER, + "Send TEvAuthorizeTicketResult success"); + Send(ev->Sender, new TEvTicketParser::TEvAuthorizeTicketResult(ev->Get()->Ticket, userToken)); +} + +} // namespace NMonitoring::NTests diff --git a/ydb/core/mon/ut_utils/ut_utils.h b/ydb/core/mon/ut_utils/ut_utils.h new file mode 100644 index 000000000000..85042b041155 --- /dev/null +++ b/ydb/core/mon/ut_utils/ut_utils.h @@ -0,0 +1,74 @@ +#include +#include + +#include +#include +#include +#include + +namespace NMonitoring::NTests { + +using namespace NActors; +using namespace NKikimr; + +extern const TString TEST_MON_PATH; +extern const TString TEST_RESPONSE; +extern const TString AUTHORIZATION_HEADER; +extern const TString VALID_TOKEN; +extern const TVector DEFAULT_TICKET_PARSER_GROUPS; + +class TTestActorPage : public TActorBootstrapped { +public: + void Bootstrap(); + +private: + void Handle(NMon::TEvHttpInfo::TPtr& ev); + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NMon::TEvHttpInfo, Handle); + } + } +}; + +class TTestActorHandler : public TActorBootstrapped { +public: + void Bootstrap(); + +private: + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr& ev); + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + } + } +}; + +class TTestMonPage : public NMonitoring::IMonPage { +public: + TTestMonPage(); + void Output(NMonitoring::IMonHttpRequest& request) override; +}; + +struct TFakeTicketParserActor : public TActor { + TFakeTicketParserActor(TVector groupSIDs); + void Handle(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev); + void Fail(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev, const TString& message); + void Success(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev); + + size_t AuthorizeTicketRequests = 0; + size_t AuthorizeTicketSuccesses = 0; + size_t AuthorizeTicketFails = 0; + TVector GroupSIDs; + + STFUNC(StateFunc) { + switch (ev->GetTypeRewrite()) { + hFunc(TEvTicketParser::TEvAuthorizeTicket, Handle); + default: + break; + } + } +}; + +} // namespace NMonitoring::NTests diff --git a/ydb/core/mon/ut_utils/ya.make b/ydb/core/mon/ut_utils/ya.make new file mode 100644 index 000000000000..2801f4b860b3 --- /dev/null +++ b/ydb/core/mon/ut_utils/ya.make @@ -0,0 +1,14 @@ +LIBRARY() + +SRCS( + ut_utils.cpp +) + +PEERDIR( + ydb/core/protos + ydb/library/aclib +) + +YQL_LAST_ABI_VERSION() + +END() diff --git a/ydb/core/mon/ya.make b/ydb/core/mon/ya.make index 68afd7f3e087..74ba81f1d26d 100644 --- a/ydb/core/mon/ya.make +++ b/ydb/core/mon/ya.make @@ -1,6 +1,7 @@ LIBRARY() SRCS( + events_internal.h mon.cpp mon.h crossref.cpp @@ -25,3 +26,12 @@ PEERDIR( ) END() + +RECURSE( + audit + ut_utils +) + +RECURSE_FOR_TESTS( + ut +) diff --git a/ydb/core/viewer/ut/ya.make b/ydb/core/viewer/ut/ya.make index 66c01bab212d..868cdfd33cd8 100644 --- a/ydb/core/viewer/ut/ya.make +++ b/ydb/core/viewer/ut/ya.make @@ -16,6 +16,8 @@ SRCS( ) PEERDIR( + ydb/core/mon + ydb/core/mon/ut_utils library/cpp/http/misc library/cpp/http/simple ydb/core/testlib/default diff --git a/ydb/core/viewer/viewer_ut.cpp b/ydb/core/viewer/viewer_ut.cpp index 496fcb2d6746..db07bb01e8d9 100644 --- a/ydb/core/viewer/viewer_ut.cpp +++ b/ydb/core/viewer/viewer_ut.cpp @@ -1,4 +1,6 @@ #include "ut/ut_utils.h" +#include + #include #include #include @@ -35,6 +37,7 @@ using namespace NKikimrWhiteboard; using namespace NSchemeShard; using namespace Tests; using namespace NMonitoring; +using namespace NMonitoring::NTests; using namespace NKikimr::NViewerTests; using TNavigate = NSchemeCache::TSchemeCacheNavigate; @@ -420,90 +423,9 @@ Y_UNIT_TEST_SUITE(Viewer) { #endif } - struct TFakeTicketParserActor : public TActor { - TFakeTicketParserActor() - : TActor(&TFakeTicketParserActor::StFunc) - {} - - STFUNC(StFunc) { - switch (ev->GetTypeRewrite()) { - hFunc(TEvTicketParser::TEvAuthorizeTicket, Handle); - default: - break; - } - } - - void Handle(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev) { - LOG_INFO_S(*TlsActivationContext, NKikimrServices::TICKET_PARSER, "Ticket parser: got TEvAuthorizeTicket event: " << ev->Get()->Ticket << " " << ev->Get()->Database << " " << ev->Get()->Entries.size()); - ++AuthorizeTicketRequests; - - if (ev->Get()->Database != "/Root") { - Fail(ev, TStringBuilder() << "Incorrect database " << ev->Get()->Database); - return; - } - - if (ev->Get()->Ticket != "test_ydb_token") { - Fail(ev, TStringBuilder() << "Incorrect token " << ev->Get()->Ticket); - return; - } - - bool databaseIdFound = false; - bool folderIdFound = false; - for (const TEvTicketParser::TEvAuthorizeTicket::TEntry& entry : ev->Get()->Entries) { - for (const std::pair& attr : entry.Attributes) { - if (attr.first == "database_id") { - databaseIdFound = true; - if (attr.second != "test_database_id") { - Fail(ev, TStringBuilder() << "Incorrect database_id " << attr.second); - return; - } - } else if (attr.first == "folder_id") { - folderIdFound = true; - if (attr.second != "test_folder_id") { - Fail(ev, TStringBuilder() << "Incorrect folder_id " << attr.second); - return; - } - } - } - } - if (!databaseIdFound) { - Fail(ev, "database_id not found"); - return; - } - if (!folderIdFound) { - Fail(ev, "folder_id not found"); - return; - } - - Success(ev); - } - - void Fail(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev, const TString& message) { - ++AuthorizeTicketFails; - TEvTicketParser::TError err; - err.Retryable = false; - err.Message = message ? message : "Test error"; - LOG_INFO_S(*TlsActivationContext, NKikimrServices::TICKET_PARSER, "Send TEvAuthorizeTicketResult: " << err.Message); - Send(ev->Sender, new TEvTicketParser::TEvAuthorizeTicketResult(ev->Get()->Ticket, err)); - } - - void Success(TEvTicketParser::TEvAuthorizeTicket::TPtr& ev) { - ++AuthorizeTicketSuccesses; - NACLib::TUserToken::TUserTokenInitFields args; - args.UserSID = "username"; - args.GroupSIDs.push_back("group_name"); - TIntrusivePtr userToken = MakeIntrusive(args); - LOG_INFO_S(*TlsActivationContext, NKikimrServices::TICKET_PARSER, "Send TEvAuthorizeTicketResult success"); - Send(ev->Sender, new TEvTicketParser::TEvAuthorizeTicketResult(ev->Get()->Ticket, userToken)); - } - - size_t AuthorizeTicketRequests = 0; - size_t AuthorizeTicketSuccesses = 0; - size_t AuthorizeTicketFails = 0; - }; IActor* CreateFakeTicketParser(const TTicketParserSettings&) { - return new TFakeTicketParserActor(); + return new TFakeTicketParserActor({"group_name"}); } struct TPostQueryArguments { @@ -537,7 +459,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoPost("/viewer/query?timeout=600000", NJson::WriteJson(jsonRequest, false), &responseStream, headers); UNIT_ASSERT_EQUAL(statusCode, HTTP_OK); return NJson::ReadJsonTree(&responseStream, /* throwOnError = */ true); @@ -1676,7 +1598,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; TString requestBody = R"json({ "query": "SELECT cast('311111111113.222222223' as Double);", "database": "/Root", @@ -1720,8 +1642,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TFakeTicketParserActor* ticketParser = nullptr; settings.CreateTicketParser = [&](const TTicketParserSettings&) -> IActor* { - ticketParser = new TFakeTicketParserActor(); - return ticketParser; + return ticketParser = new TFakeTicketParserActor({"group_name"}); }; TServer server(settings); @@ -1739,7 +1660,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; TString requestBody = R"json({ "query": "SELECT 42;", "database": "/Root", @@ -1802,7 +1723,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoPost(TStringBuilder() << "/query/script/execute?timeout=600000" << "&database=%2FRoot", requestBody.Str(), &responseStream, headers); @@ -1816,7 +1737,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; id = std::regex_replace(id.c_str(), std::regex("/"), "%2F"); const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoGet(TStringBuilder() << "/operation/get?timeout=600000&id=" << id @@ -1832,7 +1753,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoGet(TStringBuilder() << "/operation/list?timeout=600000&kind=scriptexec" << "&database=%2FRoot", &responseStream, headers); @@ -1847,7 +1768,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; id = std::regex_replace(id.c_str(), std::regex("/"), "%2F"); const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoGet(TStringBuilder() << "/query/script/fetch?timeout=600000&operation_id=" << id @@ -1965,7 +1886,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoPost("/viewer/plan2svg", tinyPlan, &responseStream, headers); const TString response = responseStream.ReadAll(); UNIT_ASSERT_EQUAL_C(statusCode, HTTP_OK, statusCode << ": " << response); @@ -2019,7 +1940,7 @@ Y_UNIT_TEST_SUITE(Viewer) { TStringStream responseStream; TKeepAliveHttpClient::THeaders headers; headers["Content-Type"] = "application/json"; - headers["Authorization"] = "test_ydb_token"; + headers["Authorization"] = VALID_TOKEN; const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoPost("/viewer/plan2svg", brokenPlan, &responseStream, headers); const TString response = responseStream.ReadAll(); UNIT_ASSERT_EQUAL_C(statusCode, HTTP_BAD_REQUEST, statusCode << ": " << response);