aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxenoxeno <xeno@ydb.tech>2023-09-15 16:04:28 +0300
committerxenoxeno <xeno@ydb.tech>2023-09-15 16:22:01 +0300
commitf08fad547ba421f6693a02af6f34deb0b5ae6e11 (patch)
treeee023b3c9a42aa36cf7bef1fe2e7a7aa2c6d72be
parenta74a9c76aa1222d0f420396c2f5b10c90389f5ca (diff)
downloadydb-f08fad547ba421f6693a02af6f34deb0b5ae6e11.tar.gz
switch web login service to http handlers to avoid promise lock-out KIKIMR-19359
-rw-r--r--library/cpp/actors/http/http.cpp2
-rw-r--r--library/cpp/actors/http/http.h2
-rw-r--r--ydb/core/driver_lib/run/run.cpp6
-rw-r--r--ydb/core/mon/async_http_mon.cpp18
-rw-r--r--ydb/core/mon/async_http_mon.h2
-rw-r--r--ydb/core/mon/mon.h1
-rw-r--r--ydb/core/mon/sync_http_mon.cpp4
-rw-r--r--ydb/core/mon/sync_http_mon.h1
-rw-r--r--ydb/core/security/login_page.cpp363
-rw-r--r--ydb/core/security/login_page.h8
-rw-r--r--ydb/core/tx/tx_proxy/proxy_impl.cpp2
11 files changed, 187 insertions, 222 deletions
diff --git a/library/cpp/actors/http/http.cpp b/library/cpp/actors/http/http.cpp
index 3f3687ccef..22d5409343 100644
--- a/library/cpp/actors/http/http.cpp
+++ b/library/cpp/actors/http/http.cpp
@@ -770,7 +770,7 @@ size_t THeaders::Parse(TStringBuf headers) {
return headers.begin() - start;
}
-TStringBuf THeaders::operator [](TStringBuf name) const {
+const TStringBuf THeaders::operator [](TStringBuf name) const {
return Get(name);
}
diff --git a/library/cpp/actors/http/http.h b/library/cpp/actors/http/http.h
index c859c21a6d..2711b307c8 100644
--- a/library/cpp/actors/http/http.h
+++ b/library/cpp/actors/http/http.h
@@ -109,7 +109,7 @@ struct THeaders {
THeaders() = default;
THeaders(TStringBuf headers);
THeaders(const THeaders&) = delete;
- TStringBuf operator [](TStringBuf name) const;
+ const TStringBuf operator [](TStringBuf name) const;
bool Has(TStringBuf name) const;
TStringBuf Get(TStringBuf name) const; // raw
size_t Parse(TStringBuf headers);
diff --git a/ydb/core/driver_lib/run/run.cpp b/ydb/core/driver_lib/run/run.cpp
index eac36b8893..cb4c6de3db 100644
--- a/ydb/core/driver_lib/run/run.cpp
+++ b/ydb/core/driver_lib/run/run.cpp
@@ -460,8 +460,8 @@ void TKikimrRunner::InitializeMonitoring(const TKikimrRunConfig& runConfig, bool
void TKikimrRunner::InitializeMonitoringLogin(const TKikimrRunConfig&)
{
if (Monitoring) {
- Monitoring->Register(CreateLoginPage(ActorSystem.Get()));
- Monitoring->Register(CreateLogoutPage(ActorSystem.Get()));
+ Monitoring->RegisterHandler("/login", MakeWebLoginServiceId());
+ Monitoring->RegisterHandler("/logout", MakeWebLoginServiceId());
}
}
@@ -1215,6 +1215,8 @@ void TKikimrRunner::InitializeActorSystem(
serviceInitializers->InitializeServices(setup.Get(), AppData.Get());
if (Monitoring) {
+ setup->LocalServices.emplace_back(MakeWebLoginServiceId(), TActorSetupCmd(CreateWebLoginService(),
+ TMailboxType::HTSwap, AppData->UserPoolId));
setup->LocalServices.emplace_back(NCrossRef::MakeCrossRefActorId(), TActorSetupCmd(NCrossRef::CreateCrossRefActor(),
TMailboxType::HTSwap, AppData->SystemPoolId));
setup->LocalServices.emplace_back(MakeMonVDiskStreamId(), TActorSetupCmd(CreateMonVDiskStreamActor(),
diff --git a/ydb/core/mon/async_http_mon.cpp b/ydb/core/mon/async_http_mon.cpp
index 220ff17217..a74e579a2a 100644
--- a/ydb/core/mon/async_http_mon.cpp
+++ b/ydb/core/mon/async_http_mon.cpp
@@ -731,7 +731,11 @@ void TAsyncHttpMon::Start(TActorSystem* actorSystem) {
ActorSystem->Send(HttpProxyActorId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/", HttpMonServiceActorId));
ActorSystem->Send(HttpProxyActorId, new NHttp::TEvHttpProxy::TEvRegisterHandler("/node", NodeProxyServiceActorId));
for (auto& pageInfo : ActorMonPages) {
- RegisterActorMonPage(pageInfo);
+ if (pageInfo.Page) {
+ RegisterActorMonPage(pageInfo);
+ } else if (pageInfo.Handler) {
+ ActorSystem->Send(HttpProxyActorId, new NHttp::TEvHttpProxy::TEvRegisterHandler(pageInfo.Path, pageInfo.Handler));
+ }
}
ActorMonPages.clear();
}
@@ -817,6 +821,18 @@ NMonitoring::IMonPage* TAsyncHttpMon::RegisterCountersPage(const TString& path,
return page;
}
+void TAsyncHttpMon::RegisterHandler(const TString& path, const TActorId& handler) {
+ if (ActorSystem) {
+ ActorSystem->Send(HttpProxyActorId, new NHttp::TEvHttpProxy::TEvRegisterHandler(path, handler));
+ } else {
+ TGuard<TMutex> g(Mutex);
+ ActorMonPages.emplace_back(TActorMonPageInfo{
+ .Handler = handler,
+ .Path = path,
+ });
+ }
+}
+
NMonitoring::IMonPage* TAsyncHttpMon::FindPage(const TString& relPath) {
return IndexMonPage->FindPage(relPath);
}
diff --git a/ydb/core/mon/async_http_mon.h b/ydb/core/mon/async_http_mon.h
index 3bed45c106..a6f7a2e448 100644
--- a/ydb/core/mon/async_http_mon.h
+++ b/ydb/core/mon/async_http_mon.h
@@ -25,6 +25,7 @@ public:
NMonitoring::IMonPage* RegisterActorPage(TRegisterActorPageFields fields) override;
NMonitoring::IMonPage* RegisterCountersPage(const TString& path, const TString& title, TIntrusivePtr<::NMonitoring::TDynamicCounters> counters) override;
NMonitoring::IMonPage* FindPage(const TString& relPath) override;
+ void RegisterHandler(const TString& path, const TActorId& handler) override;
protected:
TConfig Config;
@@ -36,6 +37,7 @@ protected:
struct TActorMonPageInfo {
NMonitoring::TMonPagePtr Page;
+ TActorId Handler;
TString Path;
};
diff --git a/ydb/core/mon/mon.h b/ydb/core/mon/mon.h
index ab69f0c3f8..f4314ec6b0 100644
--- a/ydb/core/mon/mon.h
+++ b/ydb/core/mon/mon.h
@@ -55,6 +55,7 @@ public:
const TString& title, bool preTag, TActorSystem* actorSystem, const TActorId& actorId, bool useAuth = true, bool sortPages = true);
virtual NMonitoring::IMonPage* RegisterCountersPage(const TString& path, const TString& title, TIntrusivePtr<::NMonitoring::TDynamicCounters> counters) = 0;
virtual NMonitoring::IMonPage* FindPage(const TString& relPath) = 0;
+ virtual void RegisterHandler(const TString& path, const TActorId& handler) = 0;
};
} // NActors
diff --git a/ydb/core/mon/sync_http_mon.cpp b/ydb/core/mon/sync_http_mon.cpp
index 8506344933..e8a5040bb7 100644
--- a/ydb/core/mon/sync_http_mon.cpp
+++ b/ydb/core/mon/sync_http_mon.cpp
@@ -108,4 +108,8 @@ namespace NActors {
IMonPage* TSyncHttpMon::FindPage(const TString& relPath) {
return TBase::FindPage(relPath);
}
+
+ void TSyncHttpMon::RegisterHandler(const TString& path, const TActorId& handler) {
+ ALOG_ERROR(NActorsServices::HTTP, "Cannot register actor handler " << handler << " in sync mon for " << path);
+ }
} // NActors
diff --git a/ydb/core/mon/sync_http_mon.h b/ydb/core/mon/sync_http_mon.h
index 9a61980c77..be8b97a431 100644
--- a/ydb/core/mon/sync_http_mon.h
+++ b/ydb/core/mon/sync_http_mon.h
@@ -24,6 +24,7 @@ public:
NMonitoring::IMonPage* RegisterCountersPage(const TString& path, const TString& title, TIntrusivePtr<::NMonitoring::TDynamicCounters> counters) override;
void OutputIndexPage(IOutputStream& out) override;
NMonitoring::IMonPage* FindPage(const TString& relPath) override;
+ void RegisterHandler(const TString& path, const TActorId& handler) override;
protected:
typedef NMonitoring::TMonService2 TBase;
diff --git a/ydb/core/security/login_page.cpp b/ydb/core/security/login_page.cpp
index d4fa195dd9..f3ada553e5 100644
--- a/ydb/core/security/login_page.cpp
+++ b/ydb/core/security/login_page.cpp
@@ -1,6 +1,7 @@
#include "login_page.h"
#include "login_shared_func.h"
+#include <library/cpp/actors/http/http_proxy.h>
#include <library/cpp/json/json_value.h>
#include <library/cpp/json/json_reader.h>
#include <library/cpp/json/json_writer.h>
@@ -24,22 +25,18 @@ using THttpResponsePtr = THolder<NMon::IEvHttpInfoRes>;
class TLoginRequest : public NActors::TActorBootstrapped<TLoginRequest> {
public:
+ using TBase = NActors::TActorBootstrapped<TLoginRequest>;
+
static constexpr NKikimrServices::TActivity::EType ActorActivityType() {
return NKikimrServices::TActivity::ACTORLIB_COMMON;
}
- TLoginRequest(IMonHttpRequest& request, NThreading::TPromise<THttpResponsePtr> result)
- : Request(request)
- , Result(result)
+ TLoginRequest(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr& ev)
+ : Sender(ev->Sender)
+ , Request(ev->Get()->Request)
{
}
- ~TLoginRequest() {
- if (!Result.HasValue()) {
- Result.SetValue(nullptr);
- }
- }
-
STATEFN(StateWork) {
switch (ev->GetTypeRewrite()) {
hFunc(TEvents::TEvPoisonPill, HandlePoisonPill);
@@ -51,52 +48,27 @@ public:
}
}
- static TString GetMethod(HTTP_METHOD method) {
- switch (method) {
- case HTTP_METHOD_UNDEFINED: return "UNDEFINED";
- case HTTP_METHOD_OPTIONS: return "OPTIONS";
- case HTTP_METHOD_GET: return "GET";
- case HTTP_METHOD_HEAD: return "HEAD";
- case HTTP_METHOD_POST: return "POST";
- case HTTP_METHOD_PUT: return "PUT";
- case HTTP_METHOD_DELETE: return "DELETE";
- case HTTP_METHOD_TRACE: return "TRACE";
- case HTTP_METHOD_CONNECT: return "CONNECT";
- case HTTP_METHOD_EXTENSION: return "EXTENSION";
- default: return "UNKNOWN";
- }
- }
-
void Bootstrap() {
- LOG_WARN_S(*TlsActivationContext, NActorsServices::HTTP,
- Request.GetRemoteAddr()
- << " " << GetMethod(Request.GetMethod())
- << " " << Request.GetUri());
+ ALOG_WARN(NActorsServices::HTTP, Request->Address << " " << Request->Method << " " << Request->URL);
- if (Request.GetMethod() == HTTP_METHOD_OPTIONS) {
+ if (Request->Method == "OPTIONS") {
return ReplyOptionsAndPassAway();
}
- if (Request.GetMethod() != HTTP_METHOD_POST) {
- return ReplyErrorAndPassAway("400 Bad Request", "Invalid method");
+ if (Request->Method != "POST") {
+ return ReplyErrorAndPassAway("400", "Bad Request", "Invalid method");
}
- if (Request.GetHeader("Content-Type").Before(';') != "application/json") {
- return ReplyErrorAndPassAway("400 Bad Request", "Invalid Content-Type");
+ NHttp::THeaders headers(Request->Headers);
+
+ if (headers.Get("Content-Type").Before(';') != "application/json") {
+ return ReplyErrorAndPassAway("400", "Bad Request", "Invalid Content-Type");
}
NJson::TJsonValue postData;
- if (!NJson::ReadJsonTree(Request.GetPostContent(), &postData)) {
- return ReplyErrorAndPassAway("400 Bad Request", "Invalid JSON data");
- }
-
- if (postData.Has("database")) {
- Database = postData["database"].GetStringRobust();
- } else {
- TDomainsInfo* domainsInfo = AppData()->DomainsInfo.Get();
- const TDomainsInfo::TDomain& domain = *domainsInfo->Domains.begin()->second.Get();
- Database = "/" + domain.Name;
+ if (!NJson::ReadJsonTree(Request->Body, &postData)) {
+ return ReplyErrorAndPassAway("400", "Bad Request", "Invalid JSON data");
}
TString login;
@@ -105,20 +77,43 @@ public:
if (postData.GetValuePointer("user", &jsonUser)) {
login = jsonUser->GetStringRobust();
} else {
- return ReplyErrorAndPassAway("400 Bad Request", "User must be specified");
+ return ReplyErrorAndPassAway("400", "Bad Request", "User must be specified");
}
NJson::TJsonValue* jsonPassword;
if (postData.GetValuePointer("password", &jsonPassword)) {
password = jsonPassword->GetStringRobust();
} else {
- return ReplyErrorAndPassAway("400 Bad Request", "Password must be specified");
+ return ReplyErrorAndPassAway("400", "Bad Request", "Password must be specified");
+ }
+
+ if (postData.Has("database")) {
+ Database = postData["database"].GetStringRobust();
}
AuthCredentials = PrepareCredentials(login, password, AppData()->AuthConfig);
- auto sendParameters = GetSendParameters(AuthCredentials, Database);
- Send(sendParameters.Recipient, sendParameters.Event.Release());
+ if (AuthCredentials.AuthType == TAuthCredentials::EAuthType::Ldap) {
+ ALOG_DEBUG(NActorsServices::HTTP, "Login: Requesting LDAP provider for user " << AuthCredentials.Login);
+ Send(MakeLdapAuthProviderID(), new TEvLdapAuthProvider::TEvAuthenticateRequest(AuthCredentials.Login, AuthCredentials.Password));
+ } else {
+ TDomainsInfo* domainsInfo = AppData()->DomainsInfo.Get();
+ const TDomainsInfo::TDomain& domain = *domainsInfo->Domains.begin()->second.Get();
+ TString rootDatabase = "/" + domain.Name;
+ ui64 rootSchemeShardTabletId = domain.SchemeRoot;
+ if (true /*!Database.empty() && Database != rootDatabase*/) {
+ Database = rootDatabase;
+ auto dest(MakeSchemeCacheID());
+ auto actr = TlsActivationContext->ExecutorThread.ActorSystem->LookupLocalService(dest);
+
+ ALOG_DEBUG(NActorsServices::HTTP, "Login: Requesting schemecache (" << dest << " -> " << actr << ") for database " << Database);
+
+ Send(dest, new TEvTxProxySchemeCache::TEvNavigateKeySet(CreateNavigateKeySetRequest(Database).Release()));
+ } else {
+ Database = rootDatabase;
+ RequestSchemeShard(rootSchemeShardTabletId);
+ }
+ }
Become(&TThis::StateWork, Timeout, new TEvents::TEvWakeup());
}
@@ -128,39 +123,45 @@ public:
return clientConfig;
}
+ void RequestSchemeShard(ui64 schemeShardTabletId) {
+ ALOG_DEBUG(NActorsServices::HTTP, "Login: Requesting schemeshard " << schemeShardTabletId << " for user " << AuthCredentials.Login);
+ IActor* pipe = NTabletPipe::CreateClient(SelfId(), schemeShardTabletId, GetPipeClientConfig());
+ PipeClient = RegisterWithSameMailbox(pipe);
+ THolder<TEvSchemeShard::TEvLogin> request = MakeHolder<TEvSchemeShard::TEvLogin>();
+ request.Get()->Record = CreateLoginRequest(AuthCredentials, AppData()->AuthConfig);
+ NTabletPipe::SendData(SelfId(), PipeClient, request.Release());
+ }
+
void HandleNavigate(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
const NSchemeCache::TSchemeCacheNavigate* response = ev->Get()->Request.Get();
if (response->ResultSet.size() == 1) {
if (response->ResultSet.front().Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok) {
const NSchemeCache::TSchemeCacheNavigate::TEntry& entry = response->ResultSet.front();
ui64 schemeShardTabletId = entry.DomainInfo->ExtractSchemeShard();
- IActor* pipe = NTabletPipe::CreateClient(SelfId(), schemeShardTabletId, GetPipeClientConfig());
- TActorId pipeClient = RegisterWithSameMailbox(pipe);
- THolder<TEvSchemeShard::TEvLogin> request = MakeHolder<TEvSchemeShard::TEvLogin>();
- request.Get()->Record = CreateLoginRequest(AuthCredentials, AppData()->AuthConfig);
- NTabletPipe::SendData(SelfId(), pipeClient, request.Release());
+ RequestSchemeShard(schemeShardTabletId);
return;
} else {
- ReplyErrorAndPassAway("503 Service Unavailable", TStringBuilder()
+ ReplyErrorAndPassAway("503", "Service Unavailable", TStringBuilder()
<< "Status " << static_cast<int>(response->ResultSet.front().Status));
}
} else {
- ReplyErrorAndPassAway("503 Service Unavailable", "Scheme error");
+ ReplyErrorAndPassAway("503", "Service Unavailable", "Scheme error");
}
}
void Handle(TEvLdapAuthProvider::TEvAuthenticateResponse::TPtr& ev) {
TEvLdapAuthProvider::TEvAuthenticateResponse* response = ev->Get();
if (response->Status == TEvLdapAuthProvider::EStatus::SUCCESS) {
+ ALOG_DEBUG(NActorsServices::HTTP, "Login: Requesting schemecache for database " << Database);
Send(MakeSchemeCacheID(), new TEvTxProxySchemeCache::TEvNavigateKeySet(CreateNavigateKeySetRequest(Database).Release()));
} else {
- ReplyErrorAndPassAway("403 Forbidden", response->Error.Message);
+ ReplyErrorAndPassAway("403", "Forbidden", response->Error.Message);
}
}
void HandleResult(TEvSchemeShard::TEvLoginResult::TPtr& ev) {
if (ev->Get()->Record.GetError()) {
- ReplyErrorAndPassAway("403 Forbidden", ev->Get()->Record.GetError());
+ ReplyErrorAndPassAway("403", "Forbidden", ev->Get()->Record.GetError());
} else {
ReplyCookieAndPassAway(ev->Get()->Record.GetToken());
}
@@ -168,7 +169,7 @@ public:
void HandleConnect(TEvTabletPipe::TEvClientConnected::TPtr& ev) {
if (ev->Get()->Status != NKikimrProto::OK) {
- ReplyErrorAndPassAway("503 Service Unavailable", "SchemeShard is not available");
+ ReplyErrorAndPassAway("503", "Service Unavailable", "SchemeShard is not available");
}
}
@@ -177,159 +178,92 @@ public:
}
void HandleTimeout() {
- ReplyErrorAndPassAway("504 Gateway Timeout", "Timeout");
+ ReplyErrorAndPassAway("504", "Gateway Timeout", "Timeout");
}
void ReplyOptionsAndPassAway() {
- Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(
- "HTTP/1.1 204 No Content\r\n"
- "Allow: OPTIONS, POST\r\n"
- "Connection: Keep-Alive\r\n\r\n", 0, NMon::IEvHttpInfoRes::EContentType::Custom));
+ NHttp::THeadersBuilder headers;
+ headers.Set("Allow", "OPTIONS, POST");
+ Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("204", "No Content", headers)));
PassAway();
}
- TString GetCORS() {
+ void SetCORS(NHttp::THeadersBuilder& headers) {
TStringBuilder res;
- TString origin = TString(Request.GetHeader("Origin"));
+ TString origin = TString(NHttp::THeaders(Request->Headers)["Origin"]);
if (origin.empty()) {
origin = "*";
}
- res << "Access-Control-Allow-Origin: " << origin << "\r\n";
- res << "Access-Control-Allow-Credentials: true\r\n";
- res << "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept\r\n";
- res << "Access-Control-Allow-Methods: OPTIONS, GET, POST\r\n";
- return res;
+ headers.Set("Access-Control-Allow-Origin", origin);
+ headers.Set("Access-Control-Allow-Credentials", "true");
+ headers.Set("Access-Control-Allow-Headers", "Content-Type,Authorization,Origin,Accept");
+ headers.Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
}
void ReplyCookieAndPassAway(const TString& cookie) {
- TStringStream response;
+ ALOG_DEBUG(NActorsServices::HTTP, "Login success for " << AuthCredentials.Login);
+ NHttp::THeadersBuilder headers;
+ SetCORS(headers);
TDuration maxAge = (ToInstant(NLogin::TLoginProvider::GetTokenExpiresAt(cookie)) - TInstant::Now());
- response << "HTTP/1.1 200 OK\r\n";
- response << "Set-Cookie: ydb_session_id=" << cookie << "; Max-Age=" << maxAge.Seconds() << "\r\n";
- response << GetCORS();
- response << "\r\n";
- Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom));
+ headers.Set("Set-Cookie", TStringBuilder() << "ydb_session_id=" << cookie << "; Max-Age=" << maxAge.Seconds());
+ Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("200", "OK", headers)));
PassAway();
}
- void ReplyErrorAndPassAway(const TString& status, const TString& error) {
+ void ReplyErrorAndPassAway(const TString& status, const TString& message, const TString& error) {
+ ALOG_ERROR(NActorsServices::HTTP, "Login: " << error);
+ NHttp::THeadersBuilder headers;
+ SetCORS(headers);
+ headers.Set("Content-Type", "application/json");
NJson::TJsonValue body;
body["error"] = error;
- TStringStream response;
- TString responseBody = NJson::WriteJson(body, false);
- response << "HTTP/1.1 " << status << "\r\n";
- response << "Content-Type: application/json\r\n";
- response << "Content-Length: " << responseBody.Size() << "\r\n";
- response << GetCORS();
- response << "\r\n";
- response << responseBody;
- Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom));
+ Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse(status, message, headers, NJson::WriteJson(body, false))));
PassAway();
}
+ void PassAway() override {
+ if (PipeClient) {
+ NTabletPipe::CloseClient(TBase::SelfId(), PipeClient);
+ }
+ TBase::PassAway();
+ }
+
protected:
- IMonHttpRequest& Request;
- NThreading::TPromise<THttpResponsePtr> Result;
+ TActorId Sender;
+ NHttp::THttpIncomingRequestPtr Request;
TDuration Timeout = TDuration::Seconds(60);
TString Database;
-
-private:
+ TActorId PipeClient;
TAuthCredentials AuthCredentials;
};
-class TLoginMonPage: public IMonPage {
-public:
- TLoginMonPage(TActorSystem* actorSystem, const TString& path)
- : IMonPage(path, {})
- , ActorSystem(actorSystem)
- {
- }
-
- void Output(IMonHttpRequest &request) override {
- auto promise = NThreading::NewPromise<THttpResponsePtr>();
- auto future = promise.GetFuture();
-
- ActorSystem->Register(new TLoginRequest(request, promise));
-
- THttpResponsePtr result = future.ExtractValue(TDuration::Max());
-
- if (result) {
- Output(request, *result);
- }
- }
-
-private:
- void Output(IMonHttpRequest& request, const NMon::IEvHttpInfoRes& result) const {
- result.Output(request.Output());
- }
-
-private:
- TActorSystem* ActorSystem;
-};
-
class TLogoutRequest : public NActors::TActorBootstrapped<TLogoutRequest> {
public:
static constexpr NKikimrServices::TActivity::EType ActorActivityType() {
return NKikimrServices::TActivity::ACTORLIB_COMMON;
}
- TLogoutRequest(IMonHttpRequest& request, NThreading::TPromise<THttpResponsePtr> result)
- : Request(request)
- , Result(result)
+ TLogoutRequest(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr& ev)
+ : Sender(ev->Sender)
+ , Request(ev->Get()->Request)
{
}
- ~TLogoutRequest() {
- if (!Result.HasValue()) {
- Result.SetValue(nullptr);
- }
- }
-
STATEFN(StateWork) {
switch (ev->GetTypeRewrite()) {
hFunc(TEvents::TEvPoisonPill, HandlePoisonPill);
- cFunc(TEvents::TSystem::Wakeup, HandleTimeout);
- }
- }
-
- static TString GetMethod(HTTP_METHOD method) {
- switch (method) {
- case HTTP_METHOD_UNDEFINED: return "UNDEFINED";
- case HTTP_METHOD_OPTIONS: return "OPTIONS";
- case HTTP_METHOD_GET: return "GET";
- case HTTP_METHOD_HEAD: return "HEAD";
- case HTTP_METHOD_POST: return "POST";
- case HTTP_METHOD_PUT: return "PUT";
- case HTTP_METHOD_DELETE: return "DELETE";
- case HTTP_METHOD_TRACE: return "TRACE";
- case HTTP_METHOD_CONNECT: return "CONNECT";
- case HTTP_METHOD_EXTENSION: return "EXTENSION";
- default: return "UNKNOWN";
}
}
void Bootstrap() {
- LOG_WARN_S(*TlsActivationContext, NActorsServices::HTTP,
- Request.GetRemoteAddr()
- << " " << GetMethod(Request.GetMethod())
- << " " << Request.GetUri());
+ ALOG_WARN(NActorsServices::HTTP, Request->Address << " " << Request->Method << " " << Request->URL);
- if (Request.GetMethod() == HTTP_METHOD_OPTIONS) {
+ if (Request->Method == "OPTIONS") {
return ReplyOptionsAndPassAway();
}
- if (Request.GetMethod() != HTTP_METHOD_POST) {
- return ReplyErrorAndPassAway("400 Bad Request", "Invalid method");
- }
-
- if (Request.GetHeader("Content-Type").Before(';') != "application/json") {
- return ReplyErrorAndPassAway("400 Bad Request", "Invalid Content-Type");
- }
-
- NJson::TJsonValue postData;
-
- if (!NJson::ReadJsonTree(Request.GetPostContent(), &postData)) {
- return ReplyErrorAndPassAway("400 Bad Request", "Invalid JSON data");
+ if (Request->Method != "POST") {
+ return ReplyErrorAndPassAway("400", "Bad Request", "Invalid method");
}
ReplyDeleteCookieAndPassAway();
@@ -339,87 +273,88 @@ public:
PassAway();
}
- void HandleTimeout() {
- ReplyErrorAndPassAway("504 Gateway Timeout", "Timeout");
- }
-
void ReplyOptionsAndPassAway() {
- Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(
- "HTTP/1.1 204 No Content\r\n"
- "Allow: OPTIONS, POST\r\n"
- "Connection: Keep-Alive\r\n\r\n", 0, NMon::IEvHttpInfoRes::EContentType::Custom));
+ NHttp::THeadersBuilder headers;
+ headers.Set("Allow", "OPTIONS, POST");
+ Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("204", "No Content", headers)));
PassAway();
}
+ void SetCORS(NHttp::THeadersBuilder& headers) {
+ TStringBuilder res;
+ TString origin = TString(NHttp::THeaders(Request->Headers)["Origin"]);
+ if (origin.empty()) {
+ origin = "*";
+ }
+ headers.Set("Access-Control-Allow-Origin", origin);
+ headers.Set("Access-Control-Allow-Credentials", "true");
+ headers.Set("Access-Control-Allow-Headers", "Content-Type,Authorization,Origin,Accept");
+ headers.Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST");
+ }
+
void ReplyDeleteCookieAndPassAway() {
- TStringStream response;
- response << "HTTP/1.1 200 OK\r\n";
- response << "Set-Cookie: ydb_session_id=; Max-Age=0\r\n";
- response << "\r\n";
- Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom));
+ ALOG_DEBUG(NActorsServices::HTTP, "Logout success");
+ NHttp::THeadersBuilder headers;
+ SetCORS(headers);
+ headers.Set("Set-Cookie", "ydb_session_id=; Max-Age=0");
+ Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("200", "OK", headers)));
PassAway();
}
- void ReplyErrorAndPassAway(const TString& status, const TString& error) {
+ void ReplyErrorAndPassAway(const TString& status, const TString& message, const TString& error) {
+ ALOG_ERROR(NActorsServices::HTTP, "Logout: " << error);
+ NHttp::THeadersBuilder headers;
+ SetCORS(headers);
+ headers.Set("Content-Type", "application/json");
NJson::TJsonValue body;
body["error"] = error;
- TStringStream response;
- TString responseBody = NJson::WriteJson(body, false);
- response << "HTTP/1.1 " << status << "\r\n";
- response << "Content-Type: application/json\r\n";
- response << "Content-Length: " << responseBody.Size() << "\r\n";
- response << "\r\n";
- response << responseBody;
- Result.SetValue(MakeHolder<NMon::TEvHttpInfoRes>(response.Str(), 0, NMon::IEvHttpInfoRes::EContentType::Custom));
+ Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse(status, message, headers, NJson::WriteJson(body, false))));
PassAway();
}
protected:
- IMonHttpRequest& Request;
- NThreading::TPromise<THttpResponsePtr> Result;
- TDuration Timeout = TDuration::Seconds(60);
+ TActorId Sender;
+ NHttp::THttpIncomingRequestPtr Request;
};
-class TLogoutMonPage: public IMonPage {
+class TLoginService : public TActor<TLoginService> {
public:
- TLogoutMonPage(TActorSystem* actorSystem, const TString& path)
- : IMonPage(path, {})
- , ActorSystem(actorSystem)
- {
- }
-
- void Output(IMonHttpRequest &request) override {
- auto promise = NThreading::NewPromise<THttpResponsePtr>();
- auto future = promise.GetFuture();
-
- ActorSystem->Register(new TLogoutRequest(request, promise));
+ TLoginService()
+ : TActor(&TLoginService::Work)
+ {}
- THttpResponsePtr result = future.ExtractValue(TDuration::Max());
+ static constexpr NKikimrServices::TActivity::EType ActorActivityType() {
+ return NKikimrServices::TActivity::ACTORLIB_COMMON;
+ }
- if (result) {
- Output(request, *result);
+ STATEFN(Work) {
+ switch (ev->GetTypeRewrite()) {
+ hFunc(TEvents::TEvPoisonPill, HandlePoisonPill);
+ hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, HandleRequest);
}
}
-private:
- void Output(IMonHttpRequest& request, const NMon::IEvHttpInfoRes& result) const {
- result.Output(request.Output());
+ void HandlePoisonPill(TEvents::TEvPoisonPill::TPtr&) {
+ PassAway();
}
-private:
- TActorSystem* ActorSystem;
+ void HandleRequest(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr& ev) {
+ if (ev->Get()->Request->URL == "/login") {
+ Register(new TLoginRequest(ev));
+ } else if (ev->Get()->Request->URL == "/logout") {
+ Register(new TLogoutRequest(ev));
+ } else {
+ Send(ev->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(ev->Get()->Request->CreateResponseNotFound()));
+ }
+ }
};
}
namespace NKikimr {
-IMonPage* CreateLoginPage(TActorSystem* actorSystem, const TString& path) {
- return new TLoginMonPage(actorSystem, path);
-}
-
-IMonPage* CreateLogoutPage(TActorSystem* actorSystem, const TString& path) {
- return new TLogoutMonPage(actorSystem, path);
+NActors::IActor* CreateWebLoginService() {
+ return new TLoginService();
}
}
diff --git a/ydb/core/security/login_page.h b/ydb/core/security/login_page.h
index fcf80e8c99..f1ef0a8008 100644
--- a/ydb/core/security/login_page.h
+++ b/ydb/core/security/login_page.h
@@ -3,7 +3,11 @@
namespace NKikimr {
-NMonitoring::IMonPage* CreateLoginPage(NActors::TActorSystem* actorSystem, const TString& path = "login");
-NMonitoring::IMonPage* CreateLogoutPage(NActors::TActorSystem* actorSystem, const TString& path = "logout");
+inline NActors::TActorId MakeWebLoginServiceId() {
+ const char name[12] = "webloginsvc";
+ return NActors::TActorId(0, TStringBuf(name, 12));
+}
+
+NActors::IActor* CreateWebLoginService();
}
diff --git a/ydb/core/tx/tx_proxy/proxy_impl.cpp b/ydb/core/tx/tx_proxy/proxy_impl.cpp
index 1bef920e11..28c7a2b50d 100644
--- a/ydb/core/tx/tx_proxy/proxy_impl.cpp
+++ b/ydb/core/tx/tx_proxy/proxy_impl.cpp
@@ -450,7 +450,7 @@ public:
Become(&TThis::StateWork);
LOG_DEBUG_S(ctx, NKikimrServices::TX_PROXY,
"actor# " << SelfId() <<
- " Become StateWork");
+ " Become StateWork (SchemeCache " << Services.SchemeCache << ")");
}
static constexpr NKikimrServices::TActivity::EType ActorActivityType() {