diff --git a/ydb/mvp/oidc_proxy/context.cpp b/ydb/mvp/oidc_proxy/context.cpp index fb5ebae81342..f348216a4ea5 100644 --- a/ydb/mvp/oidc_proxy/context.cpp +++ b/ydb/mvp/oidc_proxy/context.cpp @@ -7,8 +7,7 @@ #include "oidc_settings.h" #include "context.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { TContext::TContext(const TInitializer& initializer) : State(initializer.State) @@ -94,5 +93,4 @@ TStringBuf TContext::GetRequestedUrl(const NHttp::THttpIncomingRequestPtr& reque return requestedUrl; } -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/context.h b/ydb/mvp/oidc_proxy/context.h index 93962816f5a2..1719e3063ef1 100644 --- a/ydb/mvp/oidc_proxy/context.h +++ b/ydb/mvp/oidc_proxy/context.h @@ -10,8 +10,7 @@ using THttpIncomingRequestPtr = TIntrusivePtr; } -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class TContext { public: @@ -45,5 +44,4 @@ class TContext { TString GenerateCookie(const TString& key) const; }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/mvp.cpp b/ydb/mvp/oidc_proxy/mvp.cpp index c66759d46f06..fb5171b87c0a 100644 --- a/ydb/mvp/oidc_proxy/mvp.cpp +++ b/ydb/mvp/oidc_proxy/mvp.cpp @@ -28,8 +28,7 @@ NActors::IActor* CreateMemProfiler(); -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { namespace { @@ -233,6 +232,7 @@ void TMVP::TryGetOidcOptionsFromConfig(const YAML::Node& config) { OpenIdConnectSettings.AuthUrlPath = oidc["auth_url_path"].as(OpenIdConnectSettings.DEFAULT_AUTH_URL_PATH); OpenIdConnectSettings.TokenUrlPath = oidc["token_url_path"].as(OpenIdConnectSettings.DEFAULT_TOKEN_URL_PATH); OpenIdConnectSettings.ExchangeUrlPath = oidc["exchange_url_path"].as(OpenIdConnectSettings.DEFAULT_EXCHANGE_URL_PATH); + OpenIdConnectSettings.ImpersonateUrlPath = oidc["impersonate_url_path"].as(OpenIdConnectSettings.DEFAULT_IMPERSONATE_URL_PATH); Cout << "Started processing allowed_proxy_hosts..." << Endl; for (const std::string& host : oidc["allowed_proxy_hosts"].as>()) { Cout << host << " added to allowed_proxy_hosts" << Endl; @@ -417,5 +417,4 @@ THolder TMVP::BuildActorSystemSetup(int argc, char** TAtomic TMVP::Quit = false; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/mvp.h b/ydb/mvp/oidc_proxy/mvp.h index 6e9294cac9ea..57508de5e8d3 100644 --- a/ydb/mvp/oidc_proxy/mvp.h +++ b/ydb/mvp/oidc_proxy/mvp.h @@ -12,8 +12,7 @@ #include #include "oidc_settings.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { const TString& GetEServiceName(NActors::NLog::EComponent component); @@ -72,5 +71,4 @@ class TMVP { int Shutdown(); }; -} // namespace NOIDC -} // namespace NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_client.cpp b/ydb/mvp/oidc_proxy/oidc_client.cpp index 29568624e335..0277b80ebda4 100644 --- a/ydb/mvp/oidc_proxy/oidc_client.cpp +++ b/ydb/mvp/oidc_proxy/oidc_client.cpp @@ -1,9 +1,10 @@ #include "oidc_client.h" #include "oidc_protected_page_handler.h" #include "oidc_session_create_handler.h" +#include "oidc_impersonate_start_page_nebius.h" +#include "oidc_impersonate_stop_page_nebius.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { void InitOIDC(NActors::TActorSystem& actorSystem, const NActors::TActorId& httpProxyId, @@ -14,6 +15,20 @@ void InitOIDC(NActors::TActorSystem& actorSystem, ) ); + if (settings.AccessServiceType == NMvp::nebius_v1) { + actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler( + "/impersonate/start", + actorSystem.Register(new TImpersonateStartPageHandler(httpProxyId, settings)) + ) + ); + + actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler( + "/impersonate/stop", + actorSystem.Register(new TImpersonateStopPageHandler(httpProxyId, settings)) + ) + ); + } + actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler( "/", actorSystem.Register(new TProtectedPageHandler(httpProxyId, settings)) @@ -21,5 +36,4 @@ void InitOIDC(NActors::TActorSystem& actorSystem, ); } -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_client.h b/ydb/mvp/oidc_proxy/oidc_client.h index 47961f548ff8..884739632dfd 100644 --- a/ydb/mvp/oidc_proxy/oidc_client.h +++ b/ydb/mvp/oidc_proxy/oidc_client.h @@ -5,12 +5,10 @@ class TActorSystem; struct TActorId; } // NActors -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { struct TOpenIdConnectSettings; void InitOIDC(NActors::TActorSystem& actorSystem, const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp new file mode 100644 index 000000000000..d23265864d12 --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include "openid_connect.h" +#include "oidc_session_create.h" +#include "oidc_impersonate_start_page_nebius.h" + +namespace NMVP::NOIDC { + +THandlerImpersonateStart::THandlerImpersonateStart(const NActors::TActorId& sender, + const NHttp::THttpIncomingRequestPtr& request, + const NActors::TActorId& httpProxyId, + const TOpenIdConnectSettings& settings) + : Sender(sender) + , Request(request) + , HttpProxyId(httpProxyId) + , Settings(settings) +{} + +void THandlerImpersonateStart::Bootstrap() { + BLOG_D("Start impersonation process"); + + NHttp::TUrlParameters urlParameters(Request->URL); + TString serviceAccountId = urlParameters["service_account_id"]; + + NHttp::THeaders headers(Request->Headers); + NHttp::TCookies cookies(headers.Get("Cookie")); + + TStringBuf sessionCookieValue = GetCookie(cookies, CreateNameSessionCookie(Settings.ClientId)); + TString sessionToken = DecodeToken(sessionCookieValue); + TStringBuf impersonatedCookieValue = GetCookie(cookies, CreateNameImpersonatedCookie(Settings.ClientId)); + + if (sessionToken.empty()) { + return ReplyBadRequestAndPassAway("Wrong impersonate parameter: session cookie not found"); + } + if (!impersonatedCookieValue.empty()) { + return ReplyBadRequestAndPassAway("Wrong impersonate parameter: impersonated cookie already exists"); + } + if (serviceAccountId.empty()) { + return ReplyBadRequestAndPassAway("Wrong impersonate parameter: service_account_id not found"); + } + + RequestImpersonatedToken(sessionToken, serviceAccountId); +} + +void THandlerImpersonateStart::RequestImpersonatedToken(TString& sessionToken, TString& serviceAccountId) { + BLOG_D("Request impersonated token"); + NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetImpersonateEndpointURL()); + httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); + + TMvpTokenator* tokenator = MVPAppData()->Tokenator; + TString token = ""; + if (tokenator) { + token = tokenator->GetToken(Settings.SessionServiceTokenName); + } + httpRequest->Set("Authorization", token); // Bearer included + + TCgiParameters params; + params.emplace("session", sessionToken); + params.emplace("service_account_id", serviceAccountId); + httpRequest->Set<&NHttp::THttpRequest::Body>(params()); + + Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); + Become(&THandlerImpersonateStart::StateWork); +} + +void THandlerImpersonateStart::ProcessImpersonatedToken(const TString& impersonatedToken) { + TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId); + TString impersonatedCookieValue = Base64Encode(impersonatedToken); + BLOG_D("Set impersonated cookie: (" << impersonatedCookieName << ": " << NKikimr::MaskTicket(impersonatedCookieValue) << ")"); + + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Set("Set-Cookie", CreateSecureCookie(impersonatedCookieName, impersonatedCookieValue)); + SetCORS(Request, &responseHeaders); + ReplyAndPassAway(Request->CreateResponse("200", "OK", responseHeaders)); +} + +void THandlerImpersonateStart::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event) { + if (event->Get()->Error.empty() && event->Get()->Response) { + NHttp::THttpIncomingResponsePtr response = std::move(event->Get()->Response); + BLOG_D("Incoming response from authorization server: " << response->Status); + if (response->Status == "200") { + TStringBuf errorMessage; + NJson::TJsonValue jsonValue; + NJson::TJsonReaderConfig jsonConfig; + if (NJson::ReadJsonTree(response->Body, &jsonConfig, &jsonValue)) { + const NJson::TJsonValue* jsonImpersonatedToken; + if (jsonValue.GetValuePointer("impersonation", &jsonImpersonatedToken)) { + TString impersonatedToken = jsonImpersonatedToken->GetStringRobust(); + ProcessImpersonatedToken(impersonatedToken); + return; + } else { + errorMessage = "Wrong OIDC provider response: impersonated token not found"; + } + } else { + errorMessage = "Wrong OIDC response"; + } + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Set("Content-Type", "text/plain"); + SetCORS(Request, &responseHeaders); + return ReplyAndPassAway(Request->CreateResponse("400", "Bad Request", responseHeaders, errorMessage)); + } else { + NHttp::THeadersBuilder responseHeaders; + NHttp::THeaders headers(response->Headers); + if (headers.Has("Content-Type")) { + responseHeaders.Set("Content-Type", headers.Get("Content-Type")); + } + SetCORS(Request, &responseHeaders); + return ReplyAndPassAway(Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body)); + } + } else { + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Set("Content-Type", "text/plain"); + SetCORS(Request, &responseHeaders); + return ReplyAndPassAway(Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error)); + } +} + +void THandlerImpersonateStart::ReplyAndPassAway(NHttp::THttpOutgoingResponsePtr httpResponse) { + Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(std::move(httpResponse))); + PassAway(); +} + +void THandlerImpersonateStart::ReplyBadRequestAndPassAway(const TString& errorMessage) { + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Set("Content-Type", "text/plain"); + SetCORS(Request, &responseHeaders); + ReplyAndPassAway(Request->CreateResponse("400", "Bad Request", responseHeaders, errorMessage)); +} + +TImpersonateStartPageHandler::TImpersonateStartPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings) + : TBase(&TImpersonateStartPageHandler::StateWork) + , HttpProxyId(httpProxyId) + , Settings(settings) +{} + +void TImpersonateStartPageHandler::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event) { + Register(new THandlerImpersonateStart(event->Sender, event->Get()->Request, HttpProxyId, Settings)); +} + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.h b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.h new file mode 100644 index 000000000000..2c9c904d5a26 --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.h @@ -0,0 +1,56 @@ +#pragma once + +#include "oidc_settings.h" +#include "context.h" +#include + +namespace NMVP::NOIDC { + +class THandlerImpersonateStart : public NActors::TActorBootstrapped { +private: + using TBase = NActors::TActorBootstrapped; + +protected: + const NActors::TActorId Sender; + const NHttp::THttpIncomingRequestPtr Request; + const NActors::TActorId HttpProxyId; + const TOpenIdConnectSettings Settings; + +public: + THandlerImpersonateStart(const NActors::TActorId& sender, + const NHttp::THttpIncomingRequestPtr& request, + const NActors::TActorId& httpProxyId, + const TOpenIdConnectSettings& settings); + void Bootstrap(); + void RequestImpersonatedToken(TString&, TString&); + void ProcessImpersonatedToken(const TString& impersonatedToken); + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event); + void ReplyAndPassAway(NHttp::THttpOutgoingResponsePtr httpResponse); + void ReplyBadRequestAndPassAway(const TString& errorMessage); + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); + } + } +}; + +class TImpersonateStartPageHandler : public NActors::TActor { + using TBase = NActors::TActor; + + const NActors::TActorId HttpProxyId; + const TOpenIdConnectSettings Settings; + +public: + TImpersonateStartPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event); + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(NActors::TEvents::TEvPoisonPill::EventType, PassAway); + } + } +}; + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.cpp b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.cpp new file mode 100644 index 000000000000..214b9b07dcf8 --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.cpp @@ -0,0 +1,43 @@ +#include "openid_connect.h" +#include "oidc_session_create.h" +#include "oidc_impersonate_stop_page_nebius.h" + +namespace NMVP::NOIDC { + +THandlerImpersonateStop::THandlerImpersonateStop(const NActors::TActorId& sender, + const NHttp::THttpIncomingRequestPtr& request, + const NActors::TActorId& httpProxyId, + const TOpenIdConnectSettings& settings) + : Sender(sender) + , Request(request) + , HttpProxyId(httpProxyId) + , Settings(settings) +{} + +void THandlerImpersonateStop::Bootstrap() { + TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId); + BLOG_D("Clear impersonated cookie: (" << impersonatedCookieName << ")"); + + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Set("Set-Cookie", ClearSecureCookie(impersonatedCookieName)); + SetCORS(Request, &responseHeaders); + + ReplyAndPassAway(Request->CreateResponse("200", "OK", responseHeaders)); +} + +void THandlerImpersonateStop::ReplyAndPassAway(NHttp::THttpOutgoingResponsePtr httpResponse) { + Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(std::move(httpResponse))); + PassAway(); +} + +TImpersonateStopPageHandler::TImpersonateStopPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings) + : TBase(&TImpersonateStopPageHandler::StateWork) + , HttpProxyId(httpProxyId) + , Settings(settings) +{} + +void TImpersonateStopPageHandler::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event) { + Register(new THandlerImpersonateStop(event->Sender, event->Get()->Request, HttpProxyId, Settings)); +} + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h new file mode 100644 index 000000000000..f298ef7147ad --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h @@ -0,0 +1,47 @@ +#pragma once + +#include "oidc_settings.h" +#include "context.h" +#include + +namespace NMVP::NOIDC { + +class THandlerImpersonateStop : public NActors::TActorBootstrapped { +private: + using TBase = NActors::TActorBootstrapped; + +protected: + const NActors::TActorId Sender; + const NHttp::THttpIncomingRequestPtr Request; + NActors::TActorId HttpProxyId; + const TOpenIdConnectSettings Settings; + +public: + THandlerImpersonateStop(const NActors::TActorId& sender, + const NHttp::THttpIncomingRequestPtr& request, + const NActors::TActorId& httpProxyId, + const TOpenIdConnectSettings& settings); + + void Bootstrap(); + void ReplyAndPassAway(NHttp::THttpOutgoingResponsePtr httpResponse); +}; + +class TImpersonateStopPageHandler : public NActors::TActor { + using TBase = NActors::TActor; + + const NActors::TActorId HttpProxyId; + const TOpenIdConnectSettings Settings; + +public: + TImpersonateStopPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event); + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(NActors::TEvents::TEvPoisonPill::EventType, PassAway); + } + } +}; + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page.cpp b/ydb/mvp/oidc_proxy/oidc_protected_page.cpp index 52d7e6fbacd6..41bea328e805 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page.cpp +++ b/ydb/mvp/oidc_proxy/oidc_protected_page.cpp @@ -5,8 +5,7 @@ #include "openid_connect.h" #include "oidc_protected_page.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { THandlerSessionServiceCheck::THandlerSessionServiceCheck(const NActors::TActorId& sender, const NHttp::THttpIncomingRequestPtr& request, @@ -21,43 +20,37 @@ THandlerSessionServiceCheck::THandlerSessionServiceCheck(const NActors::TActorId void THandlerSessionServiceCheck::Bootstrap(const NActors::TActorContext& ctx) { if (!CheckRequestedHost()) { - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(CreateResponseForbiddenHost())); - Die(ctx); - return; + return ReplyAndPassAway(CreateResponseForbiddenHost()); } NHttp::THeaders headers(Request->Headers); TStringBuf authHeader = headers.Get(AUTH_HEADER_NAME); if (Request->Method == "OPTIONS" || IsAuthorizedRequest(authHeader)) { - ForwardUserRequest(TString(authHeader), ctx); + ForwardUserRequest(TString(authHeader)); } else { StartOidcProcess(ctx); } } -void THandlerSessionServiceCheck::HandleProxy(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { - NHttp::THttpOutgoingResponsePtr httpResponse; +void THandlerSessionServiceCheck::HandleProxy(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event) { if (event->Get()->Response != nullptr) { - NHttp::THttpIncomingResponsePtr response = event->Get()->Response; - LOG_DEBUG_S(ctx, EService::MVP, "Incoming response for protected resource: " << response->Status); + NHttp::THttpIncomingResponsePtr response = std::move(event->Get()->Response); + BLOG_D("Incoming response for protected resource: " << response->Status); if (NeedSendSecureHttpRequest(response)) { - SendSecureHttpRequest(response, ctx); - return; + return SendSecureHttpRequest(response); } NHttp::THeadersBuilder headers = GetResponseHeaders(response); TStringBuf contentType = headers.Get("Content-Type").NextTok(';'); if (contentType == "text/html") { TString newBody = FixReferenceInHtml(response->Body, response->GetRequest()->Host); - httpResponse = Request->CreateResponse( response->Status, response->Message, headers, newBody); + return ReplyAndPassAway(Request->CreateResponse(response->Status, response->Message, headers, newBody)); } else { - httpResponse = Request->CreateResponse( response->Status, response->Message, headers, response->Body); + return ReplyAndPassAway(Request->CreateResponse(response->Status, response->Message, headers, response->Body)); } } else { static constexpr size_t MAX_LOGGED_SIZE = 1024; - LOG_DEBUG_S(ctx, EService::MVP, "Can not process request to protected resource:\n" << event->Get()->Request->GetObfuscatedData().substr(0, MAX_LOGGED_SIZE)); - httpResponse = CreateResponseForNotExistingResponseFromProtectedResource(event->Get()->GetError()); + BLOG_D("Can not process request to protected resource:\n" << event->Get()->Request->GetObfuscatedData().substr(0, MAX_LOGGED_SIZE)); + return ReplyAndPassAway(CreateResponseForNotExistingResponseFromProtectedResource(event->Get()->GetError())); } - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); } bool THandlerSessionServiceCheck::CheckRequestedHost() { @@ -86,8 +79,8 @@ bool THandlerSessionServiceCheck::IsAuthorizedRequest(TStringBuf authHeader) { return to_lower(ToString(authHeader)).StartsWith(IAM_TOKEN_SCHEME_LOWER); } -void THandlerSessionServiceCheck::ForwardUserRequest(TStringBuf authHeader, const NActors::TActorContext& ctx, bool secure) { - LOG_DEBUG_S(ctx, EService::MVP, "Forward user request bypass OIDC"); +void THandlerSessionServiceCheck::ForwardUserRequest(TStringBuf authHeader, bool secure) { + BLOG_D("Forward user request bypass OIDC"); NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequest(Request->Method, ProtectedPageUrl); ForwardRequestHeaders(httpRequest); if (!authHeader.empty()) { @@ -99,7 +92,7 @@ void THandlerSessionServiceCheck::ForwardUserRequest(TStringBuf authHeader, cons if (RequestedPageScheme.empty()) { httpRequest->Secure = secure; } - ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); + Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); } TString THandlerSessionServiceCheck::FixReferenceInHtml(TStringBuf html, TStringBuf host, TStringBuf findStr) { @@ -180,11 +173,11 @@ NHttp::THeadersBuilder THandlerSessionServiceCheck::GetResponseHeaders(const NHt return resultHeaders; } -void THandlerSessionServiceCheck::SendSecureHttpRequest(const NHttp::THttpIncomingResponsePtr& response, const NActors::TActorContext& ctx) { +void THandlerSessionServiceCheck::SendSecureHttpRequest(const NHttp::THttpIncomingResponsePtr& response) { NHttp::THttpOutgoingRequestPtr request = response->GetRequest(); - LOG_DEBUG_S(ctx, EService::MVP, "Try to send request to HTTPS port"); + BLOG_D("Try to send request to HTTPS port"); NHttp::THeadersBuilder headers {request->Headers}; - ForwardUserRequest(headers.Get(AUTH_HEADER_NAME), ctx, true); + ForwardUserRequest(headers.Get(AUTH_HEADER_NAME), true); } TString THandlerSessionServiceCheck::GetFixedLocationHeader(TStringBuf location) { @@ -233,5 +226,9 @@ NHttp::THttpOutgoingResponsePtr THandlerSessionServiceCheck::CreateResponseForNo return Request->CreateResponse("400", "Bad Request", headers, html); } -} // NOIDC -} // NMVP +void THandlerSessionServiceCheck::ReplyAndPassAway(NHttp::THttpOutgoingResponsePtr httpResponse) { + Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(std::move(httpResponse))); + PassAway(); +} + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page.h b/ydb/mvp/oidc_proxy/oidc_protected_page.h index 9bf7b4fce9a5..6b18aa253689 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page.h +++ b/ydb/mvp/oidc_proxy/oidc_protected_page.h @@ -8,8 +8,7 @@ #include #include "oidc_settings.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class THandlerSessionServiceCheck : public NActors::TActorBootstrapped { protected: @@ -17,9 +16,9 @@ class THandlerSessionServiceCheck : public NActors::TActorBootstrappedSender, event->Get()->Request, HttpProxyId, Settings)); + Register(new THandlerSessionServiceCheckYandex(event->Sender, event->Get()->Request, HttpProxyId, Settings)); break; case NMvp::nebius_v1: - ctx.Register(new THandlerSessionServiceCheckNebius(event->Sender, event->Get()->Request, HttpProxyId, Settings)); + Register(new THandlerSessionServiceCheckNebius(event->Sender, event->Get()->Request, HttpProxyId, Settings)); break; } } -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page_handler.h b/ydb/mvp/oidc_proxy/oidc_protected_page_handler.h index 97e79b395e84..39ca40c5a598 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_handler.h +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_handler.h @@ -1,12 +1,12 @@ #pragma once +#include "oidc_settings.h" #include +#include #include #include -#include "oidc_settings.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class TProtectedPageHandler : public NActors::TActor { using TBase = NActors::TActor; @@ -16,14 +16,14 @@ class TProtectedPageHandler : public NActors::TActor { public: TProtectedPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx); + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event); STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(NActors::TEvents::TEvPoisonPill::EventType, PassAway); } } }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp index b4ea27d14749..449afb3abbe1 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp @@ -1,7 +1,5 @@ #include -#include #include -#include #include #include #include @@ -9,8 +7,7 @@ #include "context.h" #include "oidc_protected_page_nebius.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { THandlerSessionServiceCheckNebius::THandlerSessionServiceCheckNebius(const NActors::TActorId& sender, const NHttp::THttpIncomingRequestPtr& request, @@ -19,71 +16,63 @@ THandlerSessionServiceCheckNebius::THandlerSessionServiceCheckNebius(const NActo : THandlerSessionServiceCheck(sender, request, httpProxyId, settings) {} -void THandlerSessionServiceCheckNebius::StartOidcProcess(const NActors::TActorContext& ctx) { +void THandlerSessionServiceCheckNebius::StartOidcProcess(const NActors::TActorContext&) { NHttp::THeaders headers(Request->Headers); - LOG_DEBUG_S(ctx, EService::MVP, "Start OIDC process"); + BLOG_D("Start OIDC process"); NHttp::TCookies cookies(headers.Get("Cookie")); - TString sessionCookieName = CreateNameSessionCookie(Settings.ClientId); - TStringBuf sessionCookieValue = cookies.Get(sessionCookieName); - if (!sessionCookieValue.Empty()) { - LOG_DEBUG_S(ctx, EService::MVP, "Using session cookie (" << sessionCookieName << ": " << NKikimr::MaskTicket(sessionCookieValue) << ")"); - } - - - TString sessionToken; - try { - Base64StrictDecode(sessionCookieValue, sessionToken); - } catch (std::exception& e) { - LOG_DEBUG_S(ctx, EService::MVP, "Base64Decode session cookie: " << e.what()); - sessionToken.clear(); - } + TStringBuf sessionCookieValue = GetCookie(cookies, CreateNameSessionCookie(Settings.ClientId)); + TStringBuf impersonatedCookieValue = GetCookie(cookies, CreateNameImpersonatedCookie(Settings.ClientId)); + TString sessionToken = DecodeToken(sessionCookieValue); if (sessionToken) { - ExchangeSessionToken(sessionToken, ctx); - } else { - RequestAuthorizationCode(ctx); + TString impersonatedToken = DecodeToken(impersonatedCookieValue); + if (impersonatedToken) { + ExchangeImpersonatedToken(sessionToken, impersonatedToken); + } else { + ExchangeSessionToken(sessionToken); + } + } else{ + RequestAuthorizationCode(); } } -void THandlerSessionServiceCheckNebius:: HandleExchange(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { +void THandlerSessionServiceCheckNebius::HandleExchange(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event) { if (!event->Get()->Response) { - LOG_DEBUG_S(ctx, EService::MVP, "Getting access token: Bad Request"); + BLOG_D("Getting access token: Bad Request"); NHttp::THeadersBuilder responseHeaders; responseHeaders.Set("Content-Type", "text/plain"); - NHttp::THttpOutgoingResponsePtr httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error); - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); - } else { - NHttp::THttpIncomingResponsePtr response = event->Get()->Response; - LOG_DEBUG_S(ctx, EService::MVP, "Getting access token: " << response->Status); - if (response->Status == "200") { - TString iamToken; - static const NJson::TJsonReaderConfig JsonConfig; - NJson::TJsonValue requestData; - bool success = NJson::ReadJsonTree(response->Body, &JsonConfig, &requestData); - if (success) { - iamToken = requestData["access_token"].GetStringSafe({}); - const TString authHeader = IAM_TOKEN_SCHEME + iamToken; - ForwardUserRequest(authHeader, ctx); - return; - } - } else if (response->Status == "400" || response->Status == "401") { - RequestAuthorizationCode(ctx); - return; + return ReplyAndPassAway(Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error)); + } + + NHttp::THttpIncomingResponsePtr response = std::move(event->Get()->Response); + BLOG_D("Getting access token: " << response->Status << " " << response->Message); + if (response->Status == "200") { + TString iamToken; + static const NJson::TJsonReaderConfig JsonConfig; + NJson::TJsonValue requestData; + bool success = NJson::ReadJsonTree(response->Body, &JsonConfig, &requestData); + if (success) { + iamToken = requestData["access_token"].GetStringSafe({}); + const TString authHeader = IAM_TOKEN_SCHEME + iamToken; + return ForwardUserRequest(authHeader); + } + } else if (response->Status == "400" || response->Status == "401") { + BLOG_D("Getting access token: " << response->Body); + if (tokenExchangeType == ETokenExchangeType::ImpersonatedToken) { + return ClearImpersonatedCookie(); + } else { + return RequestAuthorizationCode(); } - // don't know what to do, just forward response - NHttp::THttpOutgoingResponsePtr httpResponse; - NHttp::THeadersBuilder responseHeaders; - responseHeaders.Parse(response->Headers); - httpResponse = Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body); - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); } + // don't know what to do, just forward response + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Parse(response->Headers); + ReplyAndPassAway(Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body)); } -void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessionToken, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "Exchange session token"); +void THandlerSessionServiceCheckNebius::SendTokenExchangeRequest(const TCgiParameters& params, const ETokenExchangeType exchangeType) { + tokenExchangeType = exchangeType; NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetExchangeEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); @@ -93,27 +82,54 @@ void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessi token = tokenator->GetToken(Settings.SessionServiceTokenName); } httpRequest->Set("Authorization", token); // Bearer included - TStringBuilder body; - body << "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" - << "&requested_token_type=urn:ietf:params:oauth:token-type:access_token" - << "&subject_token_type=urn:ietf:params:oauth:token-type:session_token" - << "&subject_token=" << sessionToken; - httpRequest->Set<&NHttp::THttpRequest::Body>(body); - - ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); + httpRequest->Set<&NHttp::THttpRequest::Body>(params()); + Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); Become(&THandlerSessionServiceCheckNebius::StateExchange); } -void THandlerSessionServiceCheckNebius::RequestAuthorizationCode(const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "Request authorization code"); +void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString& sessionToken) { + BLOG_D("Exchange session token"); + TCgiParameters params; + params.emplace("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"); + params.emplace("requested_token_type", "urn:ietf:params:oauth:token-type:access_token"); + params.emplace("subject_token_type", "urn:ietf:params:oauth:token-type:session_token"); + params.emplace("subject_token", sessionToken); + + SendTokenExchangeRequest(params, ETokenExchangeType::SessionToken); +} + +void THandlerSessionServiceCheckNebius::ExchangeImpersonatedToken(const TString& sessionToken, const TString& impersonatedToken) { + BLOG_D("Exchange impersonated token"); + TCgiParameters params; + params.emplace("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"); + params.emplace("requested_token_type", "urn:ietf:params:oauth:token-type:access_token"); + params.emplace("subject_token_type", "urn:ietf:params:oauth:token-type:jwt"); + params.emplace("subject_token", impersonatedToken); + params.emplace("actor_token", sessionToken); + params.emplace("actor_token_type", "urn:ietf:params:oauth:token-type:session_token"); + + SendTokenExchangeRequest(params, ETokenExchangeType::ImpersonatedToken); +} + +void THandlerSessionServiceCheckNebius::ClearImpersonatedCookie() { + TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId); + BLOG_D("Clear impersonated cookie (" << impersonatedCookieName << ") and retry"); + NHttp::THeadersBuilder responseHeaders; + SetCORS(Request, &responseHeaders); + responseHeaders.Set("Set-Cookie", ClearSecureCookie(impersonatedCookieName)); + responseHeaders.Set("Location", Request->URL); + ReplyAndPassAway(Request->CreateResponse("307", "Temporary Redirect", responseHeaders)); +} + +void THandlerSessionServiceCheckNebius::RequestAuthorizationCode() { + BLOG_D("Request authorization code"); NHttp::THttpOutgoingResponsePtr httpResponse = GetHttpOutgoingResponsePtr(Request, Settings); - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); + ReplyAndPassAway(std::move(httpResponse)); } -void THandlerSessionServiceCheckNebius::ForwardUserRequest(TStringBuf authHeader, const NActors::TActorContext& ctx, bool secure) { - THandlerSessionServiceCheck::ForwardUserRequest(authHeader, ctx, secure); +void THandlerSessionServiceCheckNebius::ForwardUserRequest(TStringBuf authHeader, bool secure) { + THandlerSessionServiceCheck::ForwardUserRequest(authHeader, secure); Become(&THandlerSessionServiceCheckNebius::StateWork); } @@ -124,5 +140,4 @@ bool THandlerSessionServiceCheckNebius::NeedSendSecureHttpRequest(const NHttp::T return false; } -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h index c54aeb700d4d..035ba94db989 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h @@ -2,41 +2,48 @@ #include "oidc_protected_page.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class THandlerSessionServiceCheckNebius : public THandlerSessionServiceCheck { private: using TBase = THandlerSessionServiceCheck; +protected: + enum class ETokenExchangeType { + SessionToken, + ImpersonatedToken + }; + + ETokenExchangeType tokenExchangeType = ETokenExchangeType::SessionToken; + public: THandlerSessionServiceCheckNebius(const NActors::TActorId& sender, const NHttp::THttpIncomingRequestPtr& request, const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); - void StartOidcProcess(const NActors::TActorContext& ctx) override; - void HandleExchange(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx); + void HandleExchange(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event); STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleProxy); + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleProxy); } } STFUNC(StateExchange) { switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleExchange); + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleExchange); } } private: - - void ExchangeSessionToken(const TString sessionToken, const NActors::TActorContext& ctx); - void RequestAuthorizationCode(const NActors::TActorContext& ctx); - void ForwardUserRequest(TStringBuf authHeader, const NActors::TActorContext& ctx, bool secure = false) override; + void SendTokenExchangeRequest(const TCgiParameters& body, const ETokenExchangeType exchangeType); + void ExchangeSessionToken(const TString& sessionToken); + void ExchangeImpersonatedToken(const TString& sessionToken, const TString& impersonatedToken); + void ClearImpersonatedCookie(); + void RequestAuthorizationCode(); + void ForwardUserRequest(TStringBuf authHeader, bool secure = false) override; bool NeedSendSecureHttpRequest(const NHttp::THttpIncomingResponsePtr& response) const override; }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.cpp b/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.cpp index 041fe927cf9b..6f317e081a49 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.cpp +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.cpp @@ -21,24 +21,22 @@ void THandlerSessionServiceCheckYandex::Bootstrap(const NActors::TActorContext& Become(&THandlerSessionServiceCheckYandex::StateWork); } -void THandlerSessionServiceCheckYandex::Handle(TEvPrivate::TEvCheckSessionResponse::TPtr event, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "SessionService.Check(): OK"); +void THandlerSessionServiceCheckYandex::Handle(TEvPrivate::TEvCheckSessionResponse::TPtr event) { + BLOG_D("SessionService.Check(): OK"); auto response = event->Get()->Response; const auto& iamToken = response.iam_token(); const TString authHeader = IAM_TOKEN_SCHEME + iamToken.iam_token(); - ForwardUserRequest(authHeader, ctx); + ForwardUserRequest(authHeader); } -void THandlerSessionServiceCheckYandex::Handle(TEvPrivate::TEvErrorResponse::TPtr event, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "SessionService.Check(): " << event->Get()->Status); +void THandlerSessionServiceCheckYandex::Handle(TEvPrivate::TEvErrorResponse::TPtr event) { + BLOG_D("SessionService.Check(): " << event->Get()->Status); NHttp::THttpOutgoingResponsePtr httpResponse; if (event->Get()->Status == "400") { - httpResponse = GetHttpOutgoingResponsePtr(Request, Settings); + return ReplyAndPassAway(GetHttpOutgoingResponsePtr(Request, Settings)); } else { - httpResponse = Request->CreateResponse( event->Get()->Status, event->Get()->Message, "text/plain", event->Get()->Details); + return ReplyAndPassAway(Request->CreateResponse( event->Get()->Status, event->Get()->Message, "text/plain", event->Get()->Details)); } - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); } void THandlerSessionServiceCheckYandex::StartOidcProcess(const NActors::TActorContext& ctx) { diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.h b/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.h index 9f4f2f5f8ed4..f117106403cf 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.h +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.h @@ -3,8 +3,7 @@ #include "openid_connect.h" #include "oidc_protected_page.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class THandlerSessionServiceCheckYandex : public THandlerSessionServiceCheck { private: @@ -19,14 +18,14 @@ class THandlerSessionServiceCheckYandex : public THandlerSessionServiceCheck { void Bootstrap(const NActors::TActorContext& ctx) override; - void Handle(TEvPrivate::TEvCheckSessionResponse::TPtr event, const NActors::TActorContext& ctx); - void Handle(TEvPrivate::TEvErrorResponse::TPtr event, const NActors::TActorContext& ctx); + void Handle(TEvPrivate::TEvCheckSessionResponse::TPtr event); + void Handle(TEvPrivate::TEvErrorResponse::TPtr event); STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleProxy); - HFunc(TEvPrivate::TEvCheckSessionResponse, Handle); - HFunc(TEvPrivate::TEvErrorResponse, Handle); + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleProxy); + hFunc(TEvPrivate::TEvCheckSessionResponse, Handle); + hFunc(TEvPrivate::TEvErrorResponse, Handle); } } @@ -35,5 +34,4 @@ class THandlerSessionServiceCheckYandex : public THandlerSessionServiceCheck { bool NeedSendSecureHttpRequest(const NHttp::THttpIncomingResponsePtr& response) const override; }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp b/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp index 68ff53dccb2f..47c5443bc1f1 100644 --- a/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp +++ b/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp @@ -1,16 +1,18 @@ -#include -#include -#include -#include -#include -#include -#include -#include #include "oidc_protected_page_handler.h" #include "oidc_session_create_handler.h" +#include "oidc_impersonate_start_page_nebius.h" +#include "oidc_impersonate_stop_page_nebius.h" #include "oidc_settings.h" #include "openid_connect.h" #include "context.h" +#include +#include +#include +#include +#include +#include +#include +#include using namespace NMVP::NOIDC; @@ -631,7 +633,7 @@ Y_UNIT_TEST_SUITE(Mvp) { const NActors::TActorId sessionCreator = runtime.Register(new TSessionCreateHandler(edge, settings)); incomingRequest = new NHttp::THttpIncomingRequest(); TStringBuilder request; - request << "GET /auth/callback?code=code_template&state=" << state << " HTTP/1.1\r\n"; + request << "GET /auth/callback?code=code_template#&state=" << state << " HTTP/1.1\r\n"; request << "Host: " + hostProxy + "\r\n"; request << "Cookie: " << setCookie.NextTok(";") << "\r\n"; EatWholeString(incomingRequest, redirectStrategy.CreateRequest(request)); @@ -639,7 +641,7 @@ Y_UNIT_TEST_SUITE(Mvp) { auto outgoingRequestEv = runtime.GrabEdgeEvent(handle); const TStringBuf& body = outgoingRequestEv->Request->Body; - UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template"); + UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template%23"); UNIT_ASSERT_STRING_CONTAINS(body, "grant_type=authorization_code"); const TString authorizationServerResponse = R"___({"access_token":"access_token_value","token_type":"bearer","expires_in":43199,"scope":"openid","id_token":"id_token_value"})___"; @@ -722,7 +724,7 @@ Y_UNIT_TEST_SUITE(Mvp) { } const TString hostProxy = "oidcproxy.net"; TStringBuilder request; - request << "GET /auth/callback?code=code_template&state=" << wrongState << " HTTP/1.1\r\n"; + request << "GET /auth/callback?code=code_template#&state=" << wrongState << " HTTP/1.1\r\n"; request << "Host: " + hostProxy + "\r\n"; TString cookie = context.CreateYdbOidcCookie(settings.ClientSecret); TStringBuf cookieBuf(cookie); @@ -776,7 +778,7 @@ Y_UNIT_TEST_SUITE(Mvp) { TContext context({.State = "test_state", .RequestedAddress = "/requested/page", .AjaxRequest = false}); TStringBuilder request; - request << "GET /auth/callback?code=code_template&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; + request << "GET /auth/callback?code=code_template#&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; request << "Host: oidcproxy.net\r\n"; TString cookie = context.CreateYdbOidcCookie(settings.ClientSecret); TStringBuf cookieBuf(cookie); @@ -791,7 +793,7 @@ Y_UNIT_TEST_SUITE(Mvp) { TAutoPtr handle; auto outgoingRequestEv = runtime.GrabEdgeEvent(handle); const TStringBuf& body = outgoingRequestEv->Request->Body; - UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template"); + UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template%23"); UNIT_ASSERT_STRING_CONTAINS(body, "grant_type=authorization_code"); const TString authorizationServerResponse = R"___({"access_token":"access_token_value","token_type":"bearer","expires_in":43199,"scope":"openid","id_token":"id_token_value"})___"; @@ -830,7 +832,7 @@ Y_UNIT_TEST_SUITE(Mvp) { const NActors::TActorId sessionCreator = runtime.Register(new TSessionCreateHandler(edge, settings)); TContext context({.State = "test_state", .RequestedAddress = "/requested/page", .AjaxRequest = redirectStrategy.IsAjaxRequest()}); TStringBuilder request; - request << "GET /auth/callback?code=code_template&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; + request << "GET /auth/callback?code=code_template#&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; request << "Host: oidcproxy.net\r\n"; TString cookie = context.CreateYdbOidcCookie(settings.ClientSecret); TStringBuf cookieBuf(cookie); @@ -846,7 +848,7 @@ Y_UNIT_TEST_SUITE(Mvp) { TAutoPtr handle; auto outgoingRequestEv = runtime.GrabEdgeEvent(handle); const TStringBuf& body = outgoingRequestEv->Request->Body; - UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template"); + UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template%23"); UNIT_ASSERT_STRING_CONTAINS(body, "grant_type=authorization_code"); const TString authorizationServerResponse = R"___({"access_token":"invalid_access_token","token_type":"bearer","expires_in":43199,"scope":"openid","id_token":"id_token_value"})___"; @@ -897,7 +899,7 @@ Y_UNIT_TEST_SUITE(Mvp) { TContext context({.State = "test_state", .RequestedAddress = "/requested/page", .AjaxRequest = false}); TStringBuilder request; - request << "GET /callback?code=code_template&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; + request << "GET /callback?code=code_template#&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; request << "Host: oidcproxy.net\r\n"; TString cookie = context.CreateYdbOidcCookie(settings.ClientSecret); TStringBuf cookieBuf(cookie); @@ -912,7 +914,8 @@ Y_UNIT_TEST_SUITE(Mvp) { TAutoPtr handle; auto outgoingRequestEv = runtime.GrabEdgeEvent(handle); const TStringBuf& body = outgoingRequestEv->Request->Body; - UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template"); + + UNIT_ASSERT_STRING_CONTAINS(body, "code=code_template%23"); UNIT_ASSERT_STRING_CONTAINS(body, "grant_type=authorization_code"); const TString authorizationServerResponse = R"___({"access_token":"access_token_value","token_type":"bearer","expires_in":43199,"scope":"openid","id_token":"id_token_value"})___"; @@ -1059,7 +1062,7 @@ Y_UNIT_TEST_SUITE(Mvp) { TContext context({.State = "good_state", .RequestedAddress = "/requested/page", .AjaxRequest = false}); const TString hostProxy = "oidcproxy.net"; TStringBuilder request; - request << "GET /auth/callback?code=code_template&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; + request << "GET /auth/callback?code=code_template#&state=" << context.GetState(settings.ClientSecret) << " HTTP/1.1\r\n"; request << "Host: " + hostProxy + "\r\n"; NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest(); EatWholeString(incomingRequest, request); @@ -1103,7 +1106,7 @@ Y_UNIT_TEST_SUITE(Mvp) { } const TString hostProxy = "oidcproxy.net"; TStringBuilder request; - request << "GET /auth/callback?code=code_template&state=" << wrongState << " HTTP/1.1\r\n"; + request << "GET /auth/callback?code=code_template#&state=" << wrongState << " HTTP/1.1\r\n"; request << "Host: " + hostProxy + "\r\n"; TString cookie = context.CreateYdbOidcCookie(settings.ClientSecret); TStringBuf cookieBuf(cookie); @@ -1126,4 +1129,273 @@ Y_UNIT_TEST_SUITE(Mvp) { UNIT_ASSERT_STRINGS_EQUAL(outgoingResponseEv->Response->Status, "400"); UNIT_ASSERT_STRING_CONTAINS(outgoingResponseEv->Response->Body, "Unknown error has occurred. Please open the page again"); } + + Y_UNIT_TEST(OidcImpersonationStartFlow) { + TPortManager tp; + ui16 sessionServicePort = tp.GetPort(8655); + TMvpTestRuntime runtime; + runtime.Initialize(); + + TOpenIdConnectSettings settings { + .ClientId = "client_id", + .SessionServiceEndpoint = "localhost:" + ToString(sessionServicePort), + .AuthorizationServerAddress = "https://auth.test.net", + .ClientSecret = "0123456789abcdef", + .AccessServiceType = NMvp::nebius_v1 + }; + + const NActors::TActorId edge = runtime.AllocateEdgeActor(); + + TSessionServiceMock sessionServiceMock; + sessionServiceMock.AllowedAccessTokens.insert("valid_access_token"); + grpc::ServerBuilder builder; + builder.AddListeningPort(settings.SessionServiceEndpoint, grpc::InsecureServerCredentials()).RegisterService(&sessionServiceMock); + std::unique_ptr sessionServer(builder.BuildAndStart()); + + const NActors::TActorId impersonateStart = runtime.Register(new TImpersonateStartPageHandler(edge, settings)); + const TString hostProxy = "oidcproxy.net"; + TStringBuilder request; + request << "GET /impersonate/start?service_account_id=serviceaccount-e0tydb-dev HTTP/1.1\r\n" + << "Host: " + hostProxy + "\r\n" + << "Cookie: " << CreateNameSessionCookie(settings.ClientId) + "=" + Base64Encode("session_cookie") + "\r\n\r\n"; + + NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest(); + EatWholeString(incomingRequest, request); + incomingRequest->Endpoint->Secure = true; + runtime.Send(new IEventHandle(impersonateStart, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(incomingRequest))); + + TAutoPtr handle; + auto outgoingRequestEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->Host, "auth.test.net"); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->URL, "/oauth2/impersonation/impersonate"); + UNIT_ASSERT_EQUAL(outgoingRequestEv->Request->Secure, true); + NHttp::THttpIncomingResponsePtr incomingResponse = new NHttp::THttpIncomingResponse(outgoingRequestEv->Request); + TString okResponseBody {"{\"impersonation\": \"impersonation_token\"}"}; + EatWholeString(incomingResponse, "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "Content-Length: " + ToString(okResponseBody.size()) + "\r\n\r\n" + okResponseBody); + runtime.Send(new IEventHandle(handle->Sender, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(outgoingRequestEv->Request, incomingResponse))); + + NHttp::TEvHttpProxy::TEvHttpOutgoingResponse* outgoingResponseEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingResponseEv->Response->Status, "200"); + const NHttp::THeaders impersonatePageHeaders(outgoingResponseEv->Response->Headers); + UNIT_ASSERT(impersonatePageHeaders.Has("Set-Cookie")); + TStringBuf impersonatedCookie = impersonatePageHeaders.Get("Set-Cookie"); + TString expectedCookie = CreateSecureCookie(CreateNameImpersonatedCookie(settings.ClientId), Base64Encode("impersonation_token")); + UNIT_ASSERT_STRINGS_EQUAL(impersonatedCookie, expectedCookie); + } + + Y_UNIT_TEST(OidcImpersonationStartNeedServiceAccountId) { + TPortManager tp; + ui16 sessionServicePort = tp.GetPort(8655); + TMvpTestRuntime runtime; + runtime.Initialize(); + + TOpenIdConnectSettings settings { + .ClientId = "client_id", + .SessionServiceEndpoint = "localhost:" + ToString(sessionServicePort), + .AuthorizationServerAddress = "https://auth.test.net", + .ClientSecret = "0123456789abcdef", + .AccessServiceType = NMvp::nebius_v1 + }; + + const NActors::TActorId edge = runtime.AllocateEdgeActor(); + + TSessionServiceMock sessionServiceMock; + sessionServiceMock.AllowedAccessTokens.insert("valid_access_token"); + grpc::ServerBuilder builder; + builder.AddListeningPort(settings.SessionServiceEndpoint, grpc::InsecureServerCredentials()).RegisterService(&sessionServiceMock); + std::unique_ptr sessionServer(builder.BuildAndStart()); + + const NActors::TActorId impersonateStart = runtime.Register(new TImpersonateStartPageHandler(edge, settings)); + const TString hostProxy = "oidcproxy.net"; + TStringBuilder request; + request << "GET /impersonate/start HTTP/1.1\r\n" + << "Host: " + hostProxy + "\r\n" + << "Cookie: " << CreateNameSessionCookie(settings.ClientId) + "=" + Base64Encode("session_cookie") + "\r\n\r\n"; + + NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest(); + EatWholeString(incomingRequest, request); + incomingRequest->Endpoint->Secure = true; + runtime.Send(new IEventHandle(impersonateStart, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(incomingRequest))); + + TAutoPtr handle; + NHttp::TEvHttpProxy::TEvHttpOutgoingResponse* outgoingResponseEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingResponseEv->Response->Status, "400"); + const NHttp::THeaders impersonatePageHeaders(outgoingResponseEv->Response->Headers); + UNIT_ASSERT(!impersonatePageHeaders.Has("Set-Cookie")); + } + + Y_UNIT_TEST(OidcImpersonationStopFlow) { + TPortManager tp; + ui16 sessionServicePort = tp.GetPort(8655); + TMvpTestRuntime runtime; + runtime.Initialize(); + + TOpenIdConnectSettings settings { + .ClientId = "client_id", + .SessionServiceEndpoint = "localhost:" + ToString(sessionServicePort), + .AuthorizationServerAddress = "https://auth.test.net", + .ClientSecret = "0123456789abcdef", + .AccessServiceType = NMvp::nebius_v1 + }; + + const NActors::TActorId edge = runtime.AllocateEdgeActor(); + + TSessionServiceMock sessionServiceMock; + sessionServiceMock.AllowedAccessTokens.insert("valid_access_token"); + grpc::ServerBuilder builder; + builder.AddListeningPort(settings.SessionServiceEndpoint, grpc::InsecureServerCredentials()).RegisterService(&sessionServiceMock); + std::unique_ptr sessionServer(builder.BuildAndStart()); + + const NActors::TActorId impersonateStop = runtime.Register(new TImpersonateStopPageHandler(edge, settings)); + const TString hostProxy = "oidcproxy.net"; + TStringBuilder request; + request << "GET /impersonate/start HTTP/1.1\r\n" + << "Host: " + hostProxy + "\r\n" + << "Cookie: " << CreateNameImpersonatedCookie(settings.ClientId) + "=" + Base64Encode("impersonated_cookie") + "\r\n\r\n"; + + NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest(); + EatWholeString(incomingRequest, request); + incomingRequest->Endpoint->Secure = true; + runtime.Send(new IEventHandle(impersonateStop, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(incomingRequest))); + + TAutoPtr handle; + NHttp::TEvHttpProxy::TEvHttpOutgoingResponse* outgoingResponseEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingResponseEv->Response->Status, "200"); + const NHttp::THeaders impersonatePageHeaders(outgoingResponseEv->Response->Headers); + UNIT_ASSERT(impersonatePageHeaders.Has("Set-Cookie")); + TStringBuf impersonatedCookie = impersonatePageHeaders.Get("Set-Cookie"); + TString expectedCookie = ClearSecureCookie(CreateNameImpersonatedCookie(settings.ClientId)); + UNIT_ASSERT_STRINGS_EQUAL(impersonatedCookie, expectedCookie); + } + + Y_UNIT_TEST(OidcImpersonatedAccessToProtectedResource) { + TPortManager tp; + ui16 sessionServicePort = tp.GetPort(8655); + TMvpTestRuntime runtime; + runtime.Initialize(); + + const TString allowedProxyHost {"ydb.viewer.page"}; + TOpenIdConnectSettings settings { + .ClientId = "client_id", + .SessionServiceEndpoint = "localhost:" + ToString(sessionServicePort), + .AuthorizationServerAddress = "https://auth.test.net", + .ClientSecret = "0123456789abcdef", + .AllowedProxyHosts = {allowedProxyHost}, + .AccessServiceType = NMvp::nebius_v1 + }; + + const NActors::TActorId edge = runtime.AllocateEdgeActor(); + TSessionServiceMock sessionServiceMock; + sessionServiceMock.AllowedAccessTokens.insert("valid_access_token"); + grpc::ServerBuilder builder; + builder.AddListeningPort(settings.SessionServiceEndpoint, grpc::InsecureServerCredentials()).RegisterService(&sessionServiceMock); + std::unique_ptr sessionServer(builder.BuildAndStart()); + + const NActors::TActorId impersonateStart = runtime.Register(new TProtectedPageHandler(edge, settings)); + const TString hostProxy = "oidcproxy.net"; + const TString protectedPage = "/" + allowedProxyHost + "/counters"; + TStringBuilder request; + request << "GET " << protectedPage << " HTTP/1.1\r\n" + << "Host: " << hostProxy << "\r\n" + << "Referer: https://" << hostProxy << protectedPage << "\r\n" + << "Cookie: " << CreateNameSessionCookie(settings.ClientId) << "=" << Base64Encode("session_cookie") << "; " + << CreateNameImpersonatedCookie(settings.ClientId) << "=" << Base64Encode("impersonated_cookie") << "\r\n\r\n"; + NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest(); + EatWholeString(incomingRequest, request); + incomingRequest->Endpoint->Secure = true; + runtime.Send(new IEventHandle(impersonateStart, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(incomingRequest))); + + TAutoPtr handle; + auto outgoingRequestEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->Host, "auth.test.net"); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->URL, "/oauth2/session/exchange"); + + UNIT_ASSERT_EQUAL(outgoingRequestEv->Request->Secure, true); + NHttp::THttpIncomingResponsePtr incomingResponse = new NHttp::THttpIncomingResponse(outgoingRequestEv->Request); + TString okResponseBody {"{\"access_token\": \"access_token\"}"}; + EatWholeString(incomingResponse, "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "Content-Length: " + ToString(okResponseBody.size()) + "\r\n\r\n" + okResponseBody); + runtime.Send(new IEventHandle(handle->Sender, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(outgoingRequestEv->Request, incomingResponse))); + + outgoingRequestEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->Host, allowedProxyHost); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->URL, "/counters"); + UNIT_ASSERT_STRING_CONTAINS(outgoingRequestEv->Request->Headers, "Authorization: Bearer access_token"); + UNIT_ASSERT_EQUAL(outgoingRequestEv->Request->Secure, false); + incomingResponse = new NHttp::THttpIncomingResponse(outgoingRequestEv->Request); + okResponseBody = "this is test"; + EatWholeString(incomingResponse, "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "Content-Length: " + ToString(okResponseBody.size()) + "\r\n\r\n" + okResponseBody); + + runtime.Send(new IEventHandle(handle->Sender, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(outgoingRequestEv->Request, incomingResponse))); + auto outgoingResponseEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingResponseEv->Response->Status, "200"); + UNIT_ASSERT_STRINGS_EQUAL(outgoingResponseEv->Response->Body, "this is test"); + } + + Y_UNIT_TEST(OidcImpersonatedAccessNotAuthorized) { + TPortManager tp; + ui16 sessionServicePort = tp.GetPort(8655); + TMvpTestRuntime runtime; + runtime.Initialize(); + + const TString allowedProxyHost {"ydb.viewer.page"}; + TOpenIdConnectSettings settings { + .ClientId = "client_id", + .SessionServiceEndpoint = "localhost:" + ToString(sessionServicePort), + .AuthorizationServerAddress = "https://auth.test.net", + .ClientSecret = "0123456789abcdef", + .AllowedProxyHosts = {allowedProxyHost}, + .AccessServiceType = NMvp::nebius_v1 + }; + + const NActors::TActorId edge = runtime.AllocateEdgeActor(); + TSessionServiceMock sessionServiceMock; + sessionServiceMock.AllowedAccessTokens.insert("valid_access_token"); + grpc::ServerBuilder builder; + builder.AddListeningPort(settings.SessionServiceEndpoint, grpc::InsecureServerCredentials()).RegisterService(&sessionServiceMock); + std::unique_ptr sessionServer(builder.BuildAndStart()); + + const NActors::TActorId impersonateStart = runtime.Register(new TProtectedPageHandler(edge, settings)); + const TString hostProxy = "oidcproxy.net"; + const TString protectedPage = "/" + allowedProxyHost + "/counters"; + TStringBuilder request; + request << "GET " << protectedPage << " HTTP/1.1\r\n" + << "Host: " << hostProxy << "\r\n" + << "Referer: https://" << hostProxy << protectedPage << "\r\n" + << "Cookie: " << CreateNameSessionCookie(settings.ClientId) << "=" << Base64Encode("session_cookie") << "; " + << CreateNameImpersonatedCookie(settings.ClientId) << "=" << Base64Encode("impersonated_cookie") << "\r\n\r\n"; + NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest(); + EatWholeString(incomingRequest, request); + incomingRequest->Endpoint->Secure = true; + runtime.Send(new IEventHandle(impersonateStart, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(incomingRequest))); + + TAutoPtr handle; + auto outgoingRequestEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->Host, "auth.test.net"); + UNIT_ASSERT_STRINGS_EQUAL(outgoingRequestEv->Request->URL, "/oauth2/session/exchange"); + + UNIT_ASSERT_EQUAL(outgoingRequestEv->Request->Secure, true); + NHttp::THttpIncomingResponsePtr incomingResponse = new NHttp::THttpIncomingResponse(outgoingRequestEv->Request); + TString okResponseBody {"{\"error\": \"bad_token\"}"}; + EatWholeString(incomingResponse, "HTTP/1.1 401 OK\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "Content-Length: " + ToString(okResponseBody.size()) + "\r\n\r\n" + okResponseBody); + runtime.Send(new IEventHandle(handle->Sender, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingResponse(outgoingRequestEv->Request, incomingResponse))); + + auto outgoingResponseEv = runtime.GrabEdgeEvent(handle); + UNIT_ASSERT_STRINGS_EQUAL(outgoingResponseEv->Response->Status, "307"); + const NHttp::THeaders headers(outgoingResponseEv->Response->Headers); + UNIT_ASSERT(headers.Has("Location")); + TStringBuf location = headers.Get("Location"); + UNIT_ASSERT_STRINGS_EQUAL(location, protectedPage); + } } diff --git a/ydb/mvp/oidc_proxy/oidc_session_create.cpp b/ydb/mvp/oidc_proxy/oidc_session_create.cpp index 26cd6f3396fc..2f2695143fdf 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create.cpp +++ b/ydb/mvp/oidc_proxy/oidc_session_create.cpp @@ -1,13 +1,11 @@ #include -#include #include #include #include "openid_connect.h" #include "oidc_session_create.h" #include "oidc_settings.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { THandlerSessionCreate::THandlerSessionCreate(const NActors::TActorId& sender, const NHttp::THttpIncomingRequestPtr& request, @@ -19,7 +17,8 @@ THandlerSessionCreate::THandlerSessionCreate(const NActors::TActorId& sender, , Settings(settings) {} -void THandlerSessionCreate::Bootstrap(const NActors::TActorContext& ctx) { +void THandlerSessionCreate::Bootstrap() { + BLOG_D("Restore oidc session"); NHttp::TUrlParameters urlParameters(Request->URL); TString code = urlParameters["code"]; TString state = urlParameters["state"]; @@ -34,67 +33,63 @@ void THandlerSessionCreate::Bootstrap(const NActors::TActorContext& ctx) { if (checkStateResult.IsSuccess()) { if (restoreContextResult.IsSuccess()) { if (code.empty()) { - LOG_DEBUG_S(ctx, NMVP::EService::MVP, "Restore oidc session failed: receive empty 'code' parameter"); - RetryRequestToProtectedResourceAndDie(ctx); + BLOG_D("Restore oidc session failed: receive empty 'code' parameter"); + RetryRequestToProtectedResourceAndDie(); } else { - RequestSessionToken(code, ctx); + RequestSessionToken(code); } } else { const auto& restoreSessionStatus = restoreContextResult.Status; - LOG_DEBUG_S(ctx, NMVP::EService::MVP, restoreSessionStatus.ErrorMessage); + BLOG_D(restoreSessionStatus.ErrorMessage); if (restoreSessionStatus.IsErrorRetryable) { - RetryRequestToProtectedResourceAndDie(ctx); + RetryRequestToProtectedResourceAndDie(); } else { - SendUnknownErrorResponseAndDie(ctx); + SendUnknownErrorResponseAndDie(); } } } else { - LOG_DEBUG_S(ctx, NMVP::EService::MVP, checkStateResult.ErrorMessage); + BLOG_D(checkStateResult.ErrorMessage); if (restoreContextResult.IsSuccess() || restoreContextResult.Status.IsErrorRetryable) { - RetryRequestToProtectedResourceAndDie(ctx); + RetryRequestToProtectedResourceAndDie(); } else { - SendUnknownErrorResponseAndDie(ctx); + SendUnknownErrorResponseAndDie(); } } } void THandlerSessionCreate::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { - NHttp::THttpOutgoingResponsePtr httpResponse; if (event->Get()->Error.empty() && event->Get()->Response) { - NHttp::THttpIncomingResponsePtr response = event->Get()->Response; - LOG_DEBUG_S(ctx, EService::MVP, "Incoming response from authorization server: " << response->Status); + NHttp::THttpIncomingResponsePtr response = std::move(event->Get()->Response); + BLOG_D("Incoming response from authorization server: " << response->Status); if (response->Status == "200") { - TStringBuf jsonError; + TStringBuf errorMessage; NJson::TJsonValue jsonValue; NJson::TJsonReaderConfig jsonConfig; if (NJson::ReadJsonTree(response->Body, &jsonConfig, &jsonValue)) { const NJson::TJsonValue* jsonAccessToken; if (jsonValue.GetValuePointer("access_token", &jsonAccessToken)) { TString sessionToken = jsonAccessToken->GetStringRobust(); - ProcessSessionToken(sessionToken, ctx); - return; + return ProcessSessionToken(sessionToken, ctx); } else { - jsonError = "Wrong OIDC provider response: access_token not found"; + errorMessage = "Wrong OIDC provider response: access_token not found"; } } else { - jsonError = "Wrong OIDC response"; + errorMessage = "Wrong OIDC response"; } NHttp::THeadersBuilder responseHeaders; responseHeaders.Set("Content-Type", "text/plain"); - httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, jsonError); + return ReplyAndPassAway(Request->CreateResponse("400", "Bad Request", responseHeaders, errorMessage)); } else { NHttp::THeadersBuilder responseHeaders; responseHeaders.Parse(response->Headers); - httpResponse = Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body); + return ReplyAndPassAway(Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body)); } } else { NHttp::THeadersBuilder responseHeaders; responseHeaders.Set("Content-Type", "text/plain"); - httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error); + return ReplyAndPassAway(Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error)); } - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); } TString THandlerSessionCreate::ChangeSameSiteFieldInSessionCookie(const TString& cookie) { @@ -110,19 +105,18 @@ TString THandlerSessionCreate::ChangeSameSiteFieldInSessionCookie(const TString& return cookieBuilder; } -void THandlerSessionCreate::RetryRequestToProtectedResourceAndDie(const NActors::TActorContext& ctx) { +void THandlerSessionCreate::RetryRequestToProtectedResourceAndDie() { NHttp::THeadersBuilder responseHeaders; - RetryRequestToProtectedResourceAndDie(&responseHeaders, ctx); + RetryRequestToProtectedResourceAndDie(&responseHeaders); } -void THandlerSessionCreate::RetryRequestToProtectedResourceAndDie(NHttp::THeadersBuilder* responseHeaders, const NActors::TActorContext& ctx) { +void THandlerSessionCreate::RetryRequestToProtectedResourceAndDie(NHttp::THeadersBuilder* responseHeaders) { SetCORS(Request, responseHeaders); responseHeaders->Set("Location", Context.GetRequestedAddress()); - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("302", "Found", *responseHeaders))); - Die(ctx); + ReplyAndPassAway(Request->CreateResponse("302", "Found", *responseHeaders)); } -void THandlerSessionCreate::SendUnknownErrorResponseAndDie(const NActors::TActorContext& ctx) { +void THandlerSessionCreate::SendUnknownErrorResponseAndDie() { NHttp::THeadersBuilder responseHeaders; responseHeaders.Set("Content-Type", "text/html"); SetCORS(Request, &responseHeaders); @@ -140,9 +134,12 @@ void THandlerSessionCreate::SendUnknownErrorResponseAndDie(const NActors::TActor "" "" ""; - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("400", "Bad Request", responseHeaders, BAD_REQUEST_HTML_PAGE))); - Die(ctx); + ReplyAndPassAway(Request->CreateResponse("400", "Bad Request", responseHeaders, BAD_REQUEST_HTML_PAGE)); } -} // NOIDC -} // NMVP +void THandlerSessionCreate::ReplyAndPassAway(NHttp::THttpOutgoingResponsePtr httpResponse) { + Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(std::move(httpResponse))); + PassAway(); +} + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_session_create.h b/ydb/mvp/oidc_proxy/oidc_session_create.h index b8ef1c183e7a..b45f96a36ccf 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create.h +++ b/ydb/mvp/oidc_proxy/oidc_session_create.h @@ -9,8 +9,7 @@ #include "oidc_settings.h" #include "context.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class THandlerSessionCreate : public NActors::TActorBootstrapped { private: @@ -19,7 +18,7 @@ class THandlerSessionCreate : public NActors::TActorBootstrappedGet()->Request; if (request->Method == "GET") { switch (Settings.AccessServiceType) { case NMvp::yandex_v2: - ctx.Register(new THandlerSessionCreateYandex(event->Sender, request, HttpProxyId, Settings)); + Register(new THandlerSessionCreateYandex(event->Sender, request, HttpProxyId, Settings)); return; case NMvp::nebius_v1: - ctx.Register(new THandlerSessionCreateNebius(event->Sender, request, HttpProxyId, Settings)); + Register(new THandlerSessionCreateNebius(event->Sender, request, HttpProxyId, Settings)); return; } } auto response = request->CreateResponseBadRequest(); - ctx.Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); + Send(event->Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(response)); } -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_session_create_handler.h b/ydb/mvp/oidc_proxy/oidc_session_create_handler.h index 232968656583..8fdd2cb0958a 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_handler.h +++ b/ydb/mvp/oidc_proxy/oidc_session_create_handler.h @@ -1,12 +1,12 @@ #pragma once #include +#include #include #include #include "oidc_settings.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class TSessionCreateHandler : public NActors::TActor { using TBase = NActors::TActor; @@ -16,14 +16,14 @@ class TSessionCreateHandler : public NActors::TActor { public: TSessionCreateHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx); + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event); STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(NActors::TEvents::TEvPoisonPill::EventType, PassAway); } } }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_session_create_nebius.cpp b/ydb/mvp/oidc_proxy/oidc_session_create_nebius.cpp index 964a602baacf..5f8fdc66a72e 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_nebius.cpp +++ b/ydb/mvp/oidc_proxy/oidc_session_create_nebius.cpp @@ -4,8 +4,7 @@ #include "oidc_session_create_nebius.h" #include -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { THandlerSessionCreateNebius::THandlerSessionCreateNebius(const NActors::TActorId& sender, const NHttp::THttpIncomingRequestPtr& request, @@ -14,39 +13,35 @@ THandlerSessionCreateNebius::THandlerSessionCreateNebius(const NActors::TActorId : THandlerSessionCreate(sender, request, httpProxyId, settings) {} -void THandlerSessionCreateNebius::RequestSessionToken(const TString& code, const NActors::TActorContext& ctx) { - TStringBuilder body; +void THandlerSessionCreateNebius::RequestSessionToken(const TString& code) { TStringBuf host = Request->Host; - body << "code=" << code - << "&client_id=" << Settings.ClientId - << "&grant_type=authorization_code" - << "&redirect_uri=" - << (Request->Endpoint->Secure ? "https://" : "http://") - << host - << GetAuthCallbackUrl(); + + TCgiParameters params; + params.emplace("code", code); + params.emplace("client_id", code); + params.emplace("grant_type", "authorization_code"); + params.emplace("redirect_uri", TStringBuilder() << (Request->Endpoint->Secure ? "https://" : "http://") + << host + << GetAuthCallbackUrl()); NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetTokenEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); httpRequest->Set("Authorization", Settings.GetAuthorizationString()); - httpRequest->Set<&NHttp::THttpRequest::Body>(body); + httpRequest->Set<&NHttp::THttpRequest::Body>(params()); - ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); + Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); Become(&THandlerSessionCreateNebius::StateWork); } -void THandlerSessionCreateNebius::ProcessSessionToken(const TString& sessionToken, const NActors::TActorContext& ctx) { +void THandlerSessionCreateNebius::ProcessSessionToken(const TString& sessionToken, const NActors::TActorContext& ) { TString sessionCookieName = CreateNameSessionCookie(Settings.ClientId); TString sessionCookieValue = Base64Encode(sessionToken); - LOG_DEBUG_S(ctx, EService::MVP, "Set session cookie: (" << sessionCookieName << ": " << NKikimr::MaskTicket(sessionCookieValue) << ")"); + BLOG_D("Set session cookie: (" << sessionCookieName << ": " << NKikimr::MaskTicket(sessionCookieValue) << ")"); NHttp::THeadersBuilder responseHeaders; responseHeaders.Set("Set-Cookie", CreateSecureCookie(sessionCookieName, sessionCookieValue)); responseHeaders.Set("Location", Context.GetRequestedAddress()); - NHttp::THttpOutgoingResponsePtr httpResponse; - httpResponse = Request->CreateResponse("302", "Cookie set", responseHeaders); - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); + ReplyAndPassAway(Request->CreateResponse("302", "Cookie set", responseHeaders)); } -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_session_create_nebius.h b/ydb/mvp/oidc_proxy/oidc_session_create_nebius.h index cbe8ca386017..7dcd8a1197b3 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_nebius.h +++ b/ydb/mvp/oidc_proxy/oidc_session_create_nebius.h @@ -2,8 +2,7 @@ #include "oidc_session_create.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class THandlerSessionCreateNebius : public THandlerSessionCreate { private: @@ -15,7 +14,7 @@ class THandlerSessionCreateNebius : public THandlerSessionCreate { const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); - void RequestSessionToken(const TString& code, const NActors::TActorContext& ctx) override; + void RequestSessionToken(const TString& code) override; void ProcessSessionToken(const TString& sessionToken, const NActors::TActorContext& ctx) override; private: @@ -26,5 +25,4 @@ class THandlerSessionCreateNebius : public THandlerSessionCreate { } }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_session_create_yandex.cpp b/ydb/mvp/oidc_proxy/oidc_session_create_yandex.cpp index 8a6291209b2a..6a3c45420ddf 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_yandex.cpp +++ b/ydb/mvp/oidc_proxy/oidc_session_create_yandex.cpp @@ -16,14 +16,17 @@ THandlerSessionCreateYandex::THandlerSessionCreateYandex(const NActors::TActorId : THandlerSessionCreate(sender, request, httpProxyId, settings) {} -void THandlerSessionCreateYandex::RequestSessionToken(const TString& code, const NActors::TActorContext& ctx) { +void THandlerSessionCreateYandex::RequestSessionToken(const TString& code) { NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetTokenEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); httpRequest->Set("Authorization", Settings.GetAuthorizationString()); - TStringBuilder body; - body << "grant_type=authorization_code&code=" << code; - httpRequest->Set<&NHttp::THttpRequest::Body>(body); - ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); + + TCgiParameters params; + params.emplace("grant_type", "authorization_code"); + params.emplace("code", code); + httpRequest->Set<&NHttp::THttpRequest::Body>(params()); + + Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); Become(&THandlerSessionCreateYandex::StateWork); } @@ -55,31 +58,29 @@ void THandlerSessionCreateYandex::ProcessSessionToken(const TString& sessionToke connection->DoRequest(requestCreate, std::move(responseCb), &yandex::cloud::priv::oauth::v1::SessionService::Stub::AsyncCreate, meta); } -void THandlerSessionCreateYandex::HandleCreateSession(TEvPrivate::TEvCreateSessionResponse::TPtr event, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "SessionService.Create(): OK"); +void THandlerSessionCreateYandex::HandleCreateSession(TEvPrivate::TEvCreateSessionResponse::TPtr event) { + BLOG_D("SessionService.Create(): OK"); auto response = event->Get()->Response; NHttp::THeadersBuilder responseHeaders; for (const auto& cookie : response.Getset_cookie_header()) { responseHeaders.Set("Set-Cookie", ChangeSameSiteFieldInSessionCookie(cookie)); } - RetryRequestToProtectedResourceAndDie(&responseHeaders, ctx); + RetryRequestToProtectedResourceAndDie(&responseHeaders); } -void THandlerSessionCreateYandex::HandleError(TEvPrivate::TEvErrorResponse::TPtr event, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "SessionService.Create(): " << event->Get()->Status); +void THandlerSessionCreateYandex::HandleError(TEvPrivate::TEvErrorResponse::TPtr event) { + BLOG_D("SessionService.Create(): " << event->Get()->Status); if (event->Get()->Status == "400") { - RetryRequestToProtectedResourceAndDie(ctx); + RetryRequestToProtectedResourceAndDie(); } else { NHttp::THeadersBuilder responseHeaders; responseHeaders.Set("Content-Type", "text/plain"); SetCORS(Request, &responseHeaders); - NHttp::THttpOutgoingResponsePtr httpResponse = Request->CreateResponse( event->Get()->Status, event->Get()->Message, responseHeaders, event->Get()->Details); - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); + ReplyAndPassAway(Request->CreateResponse( event->Get()->Status, event->Get()->Message, responseHeaders, event->Get()->Details)); } } -} // NOIDC +} // NMVP template<> TString SecureShortDebugString(const yandex::cloud::priv::oauth::v1::CreateSessionRequest& request) { diff --git a/ydb/mvp/oidc_proxy/oidc_session_create_yandex.h b/ydb/mvp/oidc_proxy/oidc_session_create_yandex.h index 961fdfb79ca1..8114c29f78be 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_yandex.h +++ b/ydb/mvp/oidc_proxy/oidc_session_create_yandex.h @@ -3,8 +3,7 @@ #include "oidc_session_create.h" #include "openid_connect.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { class THandlerSessionCreateYandex : public THandlerSessionCreate { private: @@ -17,20 +16,19 @@ class THandlerSessionCreateYandex : public THandlerSessionCreate { const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); - void RequestSessionToken(const TString& code, const NActors::TActorContext& ctx) override; + void RequestSessionToken(const TString& code) override; void ProcessSessionToken(const TString& sessionToken, const NActors::TActorContext& ctx) override; - void HandleCreateSession(TEvPrivate::TEvCreateSessionResponse::TPtr event, const NActors::TActorContext& ctx); - void HandleError(TEvPrivate::TEvErrorResponse::TPtr event, const NActors::TActorContext& ctx); + void HandleCreateSession(TEvPrivate::TEvCreateSessionResponse::TPtr event); + void HandleError(TEvPrivate::TEvErrorResponse::TPtr event); private: STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); - HFunc(TEvPrivate::TEvCreateSessionResponse, HandleCreateSession); - HFunc(TEvPrivate::TEvErrorResponse, HandleError); + hFunc(TEvPrivate::TEvCreateSessionResponse, HandleCreateSession); + hFunc(TEvPrivate::TEvErrorResponse, HandleError); } } }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_settings.cpp b/ydb/mvp/oidc_proxy/oidc_settings.cpp index 49ec00fa467a..4df4563b98da 100644 --- a/ydb/mvp/oidc_proxy/oidc_settings.cpp +++ b/ydb/mvp/oidc_proxy/oidc_settings.cpp @@ -1,9 +1,8 @@ -#include -#include #include "oidc_settings.h" +#include +#include -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { TString TOpenIdConnectSettings::GetAuthorizationString() const { return "Basic " + Base64Encode(ClientId + ":" + ClientSecret); @@ -21,5 +20,8 @@ TString TOpenIdConnectSettings::GetExchangeEndpointURL() const { return AuthorizationServerAddress + ExchangeUrlPath; } -} // NOIDC -} // NMVP +TString TOpenIdConnectSettings::GetImpersonateEndpointURL() const { + return AuthorizationServerAddress + ImpersonateUrlPath; +} + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/oidc_settings.h b/ydb/mvp/oidc_proxy/oidc_settings.h index 0b90caf435b9..87a90067c76e 100644 --- a/ydb/mvp/oidc_proxy/oidc_settings.h +++ b/ydb/mvp/oidc_proxy/oidc_settings.h @@ -1,19 +1,20 @@ #pragma once -#include #include +#include -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { struct TOpenIdConnectSettings { static const inline TString YDB_OIDC_COOKIE = "ydb_oidc_cookie"; static const inline TString SESSION_COOKIE = "session_cookie"; + static const inline TString IMPERSONATED_COOKIE = "impersonated_cookie"; static const inline TString DEFAULT_CLIENT_ID = "yc.oauth.ydb-viewer"; static const inline TString DEFAULT_AUTH_URL_PATH = "/oauth/authorize"; static const inline TString DEFAULT_TOKEN_URL_PATH = "/oauth/token"; static const inline TString DEFAULT_EXCHANGE_URL_PATH = "/oauth2/session/exchange"; + static const inline TString DEFAULT_IMPERSONATE_URL_PATH = "/oauth2/impersonation/impersonate"; TString ClientId = DEFAULT_CLIENT_ID; TString SessionServiceEndpoint; @@ -26,12 +27,13 @@ struct TOpenIdConnectSettings { TString AuthUrlPath = DEFAULT_AUTH_URL_PATH; TString TokenUrlPath = DEFAULT_TOKEN_URL_PATH; TString ExchangeUrlPath = DEFAULT_EXCHANGE_URL_PATH; + TString ImpersonateUrlPath = DEFAULT_IMPERSONATE_URL_PATH; TString GetAuthorizationString() const; TString GetAuthEndpointURL() const; TString GetTokenEndpointURL() const; TString GetExchangeEndpointURL() const; + TString GetImpersonateEndpointURL() const; }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/openid_connect.cpp b/ydb/mvp/oidc_proxy/openid_connect.cpp index 498f44a17351..bfc93e2c188e 100644 --- a/ydb/mvp/oidc_proxy/openid_connect.cpp +++ b/ydb/mvp/oidc_proxy/openid_connect.cpp @@ -1,17 +1,17 @@ -#include -#include -#include +#include "context.h" +#include "openid_connect.h" +#include "oidc_settings.h" +#include #include #include #include #include #include -#include "context.h" -#include "openid_connect.h" -#include "oidc_settings.h" +#include +#include +#include -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { namespace { @@ -111,6 +111,10 @@ TString CreateNameSessionCookie(TStringBuf key) { return "__Host_" + TOpenIdConnectSettings::SESSION_COOKIE + "_" + HexEncode(key); } +TString CreateNameImpersonatedCookie(TStringBuf key) { + return "__Host_" + TOpenIdConnectSettings::IMPERSONATED_COOKIE + "_" + HexEncode(key); +} + const TString& GetAuthCallbackUrl() { static const TString callbackUrl = "/auth/callback"; return callbackUrl; @@ -123,6 +127,12 @@ TString CreateSecureCookie(const TString& name, const TString& value) { return cookieBuilder; } +TString ClearSecureCookie(const TString& name) { + TStringBuilder cookieBuilder; + cookieBuilder << name << "=; Path=/; Secure; HttpOnly; SameSite=None; Partitioned; Max-Age=0"; + return cookieBuilder; +} + TRestoreOidcContextResult RestoreOidcContext(const NHttp::TCookies& cookies, const TString& key) { TStringBuilder errorMessage; errorMessage << "Restore oidc context failed: "; @@ -227,5 +237,23 @@ TCheckStateResult CheckState(const TString& state, const TString& key) { return TCheckStateResult(); } -} // NOIDC -} // NMVP +TString DecodeToken(const TStringBuf& cookie) { + TString token; + try { + Base64StrictDecode(cookie, token); + } catch (std::exception& e) { + BLOG_D("Base64Decode " << NKikimr::MaskTicket(cookie) << " cookie: " << e.what()); + token.clear(); + } + return token; +} + +TStringBuf GetCookie(const NHttp::TCookies& cookies, const TString& cookieName) { + TStringBuf cookieValue = cookies.Get(cookieName); + if (!cookieValue.Empty()) { + BLOG_D("Using cookie (" << cookieName << ": " << NKikimr::MaskTicket(cookieValue) << ")"); + } + return cookieValue; +} + +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/openid_connect.h b/ydb/mvp/oidc_proxy/openid_connect.h index 4cf7cf30196d..76b639115098 100644 --- a/ydb/mvp/oidc_proxy/openid_connect.h +++ b/ydb/mvp/oidc_proxy/openid_connect.h @@ -1,17 +1,15 @@ #pragma once -#include -#include -#include +#include "context.h" #include #include #include #include #include -#include "context.h" - +#include +#include +#include -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { struct TOpenIdConnectSettings; @@ -45,11 +43,15 @@ void SetHeader(NYdbGrpc::TCallMeta& meta, const TString& name, const TString& va NHttp::THttpOutgoingResponsePtr GetHttpOutgoingResponsePtr(const NHttp::THttpIncomingRequestPtr& request, const TOpenIdConnectSettings& settings); TString CreateNameYdbOidcCookie(TStringBuf key, TStringBuf state); TString CreateNameSessionCookie(TStringBuf key); +TString CreateNameImpersonatedCookie(TStringBuf key); const TString& GetAuthCallbackUrl(); TString CreateSecureCookie(const TString& name, const TString& value); +TString ClearSecureCookie(const TString& name); void SetCORS(const NHttp::THttpIncomingRequestPtr& request, NHttp::THeadersBuilder* const headers); TRestoreOidcContextResult RestoreOidcContext(const NHttp::TCookies& cookies, const TString& key); TCheckStateResult CheckState(const TString& state, const TString& key); +TString DecodeToken(const TStringBuf& cookie); +TStringBuf GetCookie(const NHttp::TCookies& cookies, const TString& cookieName); template std::unique_ptr> CreateGRpcServiceConnection(const TString& endpoint) { @@ -143,5 +145,4 @@ struct TEvPrivate { }; }; -} // NOIDC -} // NMVP +} // NMVP::NOIDC diff --git a/ydb/mvp/oidc_proxy/ya.make b/ydb/mvp/oidc_proxy/ya.make index 4158e8f78783..61b522f75634 100644 --- a/ydb/mvp/oidc_proxy/ya.make +++ b/ydb/mvp/oidc_proxy/ya.make @@ -10,6 +10,8 @@ SRCS( oidc_client.cpp openid_connect.cpp oidc_settings.cpp + oidc_impersonate_start_page_nebius.cpp + oidc_impersonate_stop_page_nebius.cpp oidc_protected_page_handler.cpp oidc_protected_page_yandex.cpp oidc_protected_page_nebius.cpp