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..5da68afe01d9 100644 --- a/ydb/mvp/oidc_proxy/oidc_client.cpp +++ b/ydb/mvp/oidc_proxy/oidc_client.cpp @@ -1,9 +1,12 @@ #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 { + +using namespace NActors; void InitOIDC(NActors::TActorSystem& actorSystem, const NActors::TActorId& httpProxyId, @@ -14,6 +17,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 +38,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..5c3de4b018e0 --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp @@ -0,0 +1,153 @@ +#include +#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(const NActors::TActorContext& ctx) { + 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")); + + TString sessionCookieName = CreateNameSessionCookie(Settings.ClientId); + TStringBuf sessionCookieValue = cookies.Get(sessionCookieName); + if (!sessionCookieValue.Empty()) { + BLOG_D("Using session cookie (" << sessionCookieName << ": " << NKikimr::MaskTicket(sessionCookieValue) << ")"); + } + 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, ctx); +} + +void THandlerImpersonateStart::RequestImpersonatedToken(TString& sessionToken, TString& serviceAccountId, const NActors::TActorContext& ctx) { + 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 + + CGIEscape(sessionToken); + CGIEscape(serviceAccountId); + TStringBuilder body; + body << "session=" << sessionToken + << "&service_account_id=" << serviceAccountId; + httpRequest->Set<&NHttp::THttpRequest::Body>(body); + + ctx.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); + NHttp::THttpOutgoingResponsePtr httpResponse = Request->CreateResponse("200", "OK", responseHeaders); + ReplyAndPassAway(httpResponse); +} + +void THandlerImpersonateStart::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event) { + NHttp::THttpOutgoingResponsePtr httpResponse; + if (event->Get()->Error.empty() && event->Get()->Response) { + NHttp::THttpIncomingResponsePtr response = 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(httpResponse)); + PassAway(); +} + +void THandlerImpersonateStart::ReplyBadRequestAndPassAway(const TString& errorMessage) { + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Set("Content-Type", "text/plain"); + SetCORS(Request, &responseHeaders); + NHttp::THttpOutgoingResponsePtr httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, errorMessage); + ReplyAndPassAway(httpResponse); +} + +TImpersonateStartPageHandler::TImpersonateStartPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings) + : TBase(&TImpersonateStartPageHandler::StateWork) + , HttpProxyId(httpProxyId) + , Settings(settings) +{} + +void TImpersonateStartPageHandler::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { + ctx.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..ed4ff9a831b7 --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.h @@ -0,0 +1,58 @@ +#pragma once + +#include "oidc_settings.h" +#include "context.h" + +namespace NMVP::NOIDC { + +using namespace NActors; + +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(const NActors::TActorContext& ctx); + void RequestImpersonatedToken(TString&, TString&, const NActors::TActorContext&); + 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); + cFunc(TEvents::TEvPoisonPill::EventType, PassAway); + } + } +}; + +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, const NActors::TActorContext& ctx); + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(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..36305292d096 --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.cpp @@ -0,0 +1,46 @@ +#include "openid_connect.h" +#include "oidc_session_create.h" +#include "oidc_impersonate_stop_page_nebius.h" +#include + +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); + + NHttp::THttpOutgoingResponsePtr httpResponse; + httpResponse = Request->CreateResponse("200", "OK", responseHeaders); + ReplyAndPassAway(httpResponse); +} + +void THandlerImpersonateStop::ReplyAndPassAway(NHttp::THttpOutgoingResponsePtr httpResponse) { + Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(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, const NActors::TActorContext& ctx) { + ctx.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..9f7fd296fbf9 --- /dev/null +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h @@ -0,0 +1,48 @@ +#pragma once + +#include "oidc_settings.h" +#include "context.h" + +namespace NMVP::NOIDC { + +using namespace NActors; + +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, const NActors::TActorContext& ctx); + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(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..bbef5c002d16 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); + 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(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::TActorBootstrapped #include "oidc_settings.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { + +using namespace NActors; class TProtectedPageHandler : public NActors::TActor { using TBase = NActors::TActor; @@ -21,9 +22,9 @@ class TProtectedPageHandler : public NActors::TActor { STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(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..e60797c6c70c 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp @@ -1,7 +1,7 @@ #include #include +#include #include -#include #include #include #include @@ -9,8 +9,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, @@ -21,69 +20,64 @@ THandlerSessionServiceCheckNebius::THandlerSessionServiceCheckNebius(const NActo void THandlerSessionServiceCheckNebius::StartOidcProcess(const NActors::TActorContext& ctx) { 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, ctx); + } else { + ExchangeSessionToken(sessionToken, ctx); + } + } 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(httpResponse); + } + + NHttp::THttpIncomingResponsePtr response = 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::THttpOutgoingResponsePtr httpResponse; + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Parse(response->Headers); + httpResponse = Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body); + ReplyAndPassAway(httpResponse); } -void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessionToken, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "Exchange session token"); +void THandlerSessionServiceCheckNebius::SendTokenExchangeRequest(const TStringBuilder& body, const ETokenExchangeType exchangeType, const NActors::TActorContext& ctx) { + tokenExchangeType = exchangeType; NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetExchangeEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); @@ -93,27 +87,59 @@ void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessi token = tokenator->GetToken(Settings.SessionServiceTokenName); } httpRequest->Set("Authorization", token); // Bearer included + httpRequest->Set<&NHttp::THttpRequest::Body>(body); + + ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); + Become(&THandlerSessionServiceCheckNebius::StateExchange); +} + +void THandlerSessionServiceCheckNebius::ExchangeSessionToken(TString& sessionToken, const NActors::TActorContext& ctx) { + BLOG_D("Exchange session token"); + CGIEscape(sessionToken); 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)); + SendTokenExchangeRequest(body, ETokenExchangeType::SessionToken, ctx); +} - Become(&THandlerSessionServiceCheckNebius::StateExchange); +void THandlerSessionServiceCheckNebius::ExchangeImpersonatedToken(TString& sessionToken, TString& impersonatedToken, const NActors::TActorContext& ctx) { + BLOG_D("Exchange impersonated token"); + CGIEscape(sessionToken); + CGIEscape(impersonatedToken); + 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:jwt" + << "&subject_token=" << impersonatedToken + << "&actor_token=" << sessionToken + << "&actor_token_type=urn:ietf:params:oauth:token-type:session_token"; + + SendTokenExchangeRequest(body, ETokenExchangeType::ImpersonatedToken, ctx); +} + +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); + + NHttp::THttpOutgoingResponsePtr httpResponse = Request->CreateResponse("307", "Temporary Redirect", responseHeaders); + ReplyAndPassAway(httpResponse); } -void THandlerSessionServiceCheckNebius::RequestAuthorizationCode(const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "Request authorization code"); +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(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 +150,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..8407f1b92673 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h @@ -2,41 +2,52 @@ #include "oidc_protected_page.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { + +using namespace NActors; 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); + cFunc(TEvents::TEvPoisonPill::EventType, PassAway); } } STFUNC(StateExchange) { switch (ev->GetTypeRewrite()) { - HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleExchange); + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, HandleExchange); + cFunc(TEvents::TEvPoisonPill::EventType, PassAway); } } 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 TStringBuilder& body, const ETokenExchangeType exchangeType, const NActors::TActorContext& ctx); + void ExchangeSessionToken(TString& sessionToken, const NActors::TActorContext& ctx); + void ExchangeImpersonatedToken(TString& sessionToken, TString& impersonatedToken, const NActors::TActorContext& ctx); + 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..591c394a204c 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.h +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_yandex.h @@ -3,8 +3,9 @@ #include "openid_connect.h" #include "oidc_protected_page.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { + +using namespace NActors; class THandlerSessionServiceCheckYandex : public THandlerSessionServiceCheck { private: @@ -19,14 +20,15 @@ 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); + cFunc(TEvents::TEvPoisonPill::EventType, PassAway); } } @@ -35,5 +37,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..4a7d85ccc2fb 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create.cpp +++ b/ydb/mvp/oidc_proxy/oidc_session_create.cpp @@ -6,8 +6,7 @@ #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, @@ -20,6 +19,7 @@ THandlerSessionCreate::THandlerSessionCreate(const NActors::TActorId& sender, {} void THandlerSessionCreate::Bootstrap(const NActors::TActorContext& ctx) { + LOG_DEBUG_S(ctx, NMVP::EService::MVP, "Restore oidc session"); NHttp::TUrlParameters urlParameters(Request->URL); TString code = urlParameters["code"]; TString state = urlParameters["state"]; @@ -35,7 +35,7 @@ void THandlerSessionCreate::Bootstrap(const NActors::TActorContext& ctx) { if (restoreContextResult.IsSuccess()) { if (code.empty()) { LOG_DEBUG_S(ctx, NMVP::EService::MVP, "Restore oidc session failed: receive empty 'code' parameter"); - RetryRequestToProtectedResourceAndDie(ctx); + RetryRequestToProtectedResourceAndDie(); } else { RequestSessionToken(code, ctx); } @@ -43,17 +43,17 @@ void THandlerSessionCreate::Bootstrap(const NActors::TActorContext& ctx) { const auto& restoreSessionStatus = restoreContextResult.Status; LOG_DEBUG_S(ctx, NMVP::EService::MVP, restoreSessionStatus.ErrorMessage); if (restoreSessionStatus.IsErrorRetryable) { - RetryRequestToProtectedResourceAndDie(ctx); + RetryRequestToProtectedResourceAndDie(); } else { - SendUnknownErrorResponseAndDie(ctx); + SendUnknownErrorResponseAndDie(); } } } else { LOG_DEBUG_S(ctx, NMVP::EService::MVP, checkStateResult.ErrorMessage); if (restoreContextResult.IsSuccess() || restoreContextResult.Status.IsErrorRetryable) { - RetryRequestToProtectedResourceAndDie(ctx); + RetryRequestToProtectedResourceAndDie(); } else { - SendUnknownErrorResponseAndDie(ctx); + SendUnknownErrorResponseAndDie(); } } @@ -63,9 +63,9 @@ void THandlerSessionCreate::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse: 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); + 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)) { @@ -75,14 +75,14 @@ void THandlerSessionCreate::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse: ProcessSessionToken(sessionToken, ctx); return; } 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); + httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, errorMessage); } else { NHttp::THeadersBuilder responseHeaders; responseHeaders.Parse(response->Headers); @@ -93,8 +93,7 @@ void THandlerSessionCreate::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse: responseHeaders.Set("Content-Type", "text/plain"); httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error); } - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); - Die(ctx); + ReplyAndPassAway(httpResponse); } TString THandlerSessionCreate::ChangeSameSiteFieldInSessionCookie(const TString& cookie) { @@ -110,19 +109,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 +138,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(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..7d4c0f35c53b 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::TActorBootstrappedSender, 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..b8225f4bd7aa 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_handler.h +++ b/ydb/mvp/oidc_proxy/oidc_session_create_handler.h @@ -5,8 +5,9 @@ #include #include "oidc_settings.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { + +using namespace NActors; class TSessionCreateHandler : public NActors::TActor { using TBase = NActors::TActor; @@ -21,9 +22,9 @@ class TSessionCreateHandler : public NActors::TActor { STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle); + cFunc(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..46cb1e16a9a4 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_nebius.cpp +++ b/ydb/mvp/oidc_proxy/oidc_session_create_nebius.cpp @@ -3,9 +3,9 @@ #include "openid_connect.h" #include "oidc_session_create_nebius.h" #include +#include -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { THandlerSessionCreateNebius::THandlerSessionCreateNebius(const NActors::TActorId& sender, const NHttp::THttpIncomingRequestPtr& request, @@ -14,11 +14,13 @@ 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(TString& code, const NActors::TActorContext& ctx) { TStringBuf host = Request->Host; + CGIEscape(code); + + TStringBuilder body; body << "code=" << code - << "&client_id=" << Settings.ClientId + << "&client_id=" << code << "&grant_type=authorization_code" << "&redirect_uri=" << (Request->Endpoint->Secure ? "https://" : "http://") @@ -34,19 +36,17 @@ void THandlerSessionCreateNebius::RequestSessionToken(const TString& code, const 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(httpResponse); } -} // 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..73977c698a5f 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_nebius.h +++ b/ydb/mvp/oidc_proxy/oidc_session_create_nebius.h @@ -2,8 +2,9 @@ #include "oidc_session_create.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { + +using namespace NActors; class THandlerSessionCreateNebius : public THandlerSessionCreate { private: @@ -15,16 +16,16 @@ class THandlerSessionCreateNebius : public THandlerSessionCreate { const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); - void RequestSessionToken(const TString& code, const NActors::TActorContext& ctx) override; + void RequestSessionToken(TString& code, const NActors::TActorContext& ctx) override; void ProcessSessionToken(const TString& sessionToken, const NActors::TActorContext& ctx) override; private: STFUNC(StateWork) { switch (ev->GetTypeRewrite()) { HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); + cFunc(TEvents::TEvPoisonPill::EventType, PassAway); } } }; -} // 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..ec7fcb73140e 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_yandex.cpp +++ b/ydb/mvp/oidc_proxy/oidc_session_create_yandex.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "oidc_session_create_yandex.h" namespace NMVP { @@ -16,13 +17,17 @@ THandlerSessionCreateYandex::THandlerSessionCreateYandex(const NActors::TActorId : THandlerSessionCreate(sender, request, httpProxyId, settings) {} -void THandlerSessionCreateYandex::RequestSessionToken(const TString& code, const NActors::TActorContext& ctx) { +void THandlerSessionCreateYandex::RequestSessionToken(TString& code, const NActors::TActorContext& ctx) { NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetTokenEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); httpRequest->Set("Authorization", Settings.GetAuthorizationString()); + + CGIEscape(code); TStringBuilder body; - body << "grant_type=authorization_code&code=" << code; + body << "grant_type=authorization_code" + << "&code=" << code; httpRequest->Set<&NHttp::THttpRequest::Body>(body); + ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest)); Become(&THandlerSessionCreateYandex::StateWork); } @@ -55,31 +60,30 @@ 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(httpResponse); } } -} // 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..5f92e5fb8a27 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create_yandex.h +++ b/ydb/mvp/oidc_proxy/oidc_session_create_yandex.h @@ -3,8 +3,9 @@ #include "oidc_session_create.h" #include "openid_connect.h" -namespace NMVP { -namespace NOIDC { +namespace NMVP::NOIDC { + +using namespace NActors; class THandlerSessionCreateYandex : public THandlerSessionCreate { private: @@ -17,20 +18,20 @@ class THandlerSessionCreateYandex : public THandlerSessionCreate { const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings); - void RequestSessionToken(const TString& code, const NActors::TActorContext& ctx) override; + void RequestSessionToken(TString& code, const NActors::TActorContext& ctx) 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); + cFunc(TEvents::TEvPoisonPill::EventType, PassAway); } } }; -} // 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