From f12608fc885327307d9fe6a00ca927988a4891bc Mon Sep 17 00:00:00 2001 From: StekPerepolnen Date: Fri, 8 Nov 2024 15:35:53 +0000 Subject: [PATCH] impersonate --- ydb/mvp/oidc_proxy/mvp.cpp | 1 + ydb/mvp/oidc_proxy/oidc_client.cpp | 24 +- .../oidc_impersonate_start_page_nebius.cpp | 138 ++++----- .../oidc_impersonate_start_page_nebius.h | 45 +-- .../oidc_impersonate_stop_page_nebius.cpp | 31 +- .../oidc_impersonate_stop_page_nebius.h | 29 +- .../oidc_proxy/oidc_protected_page_nebius.cpp | 54 ++-- .../oidc_proxy/oidc_protected_page_nebius.h | 13 +- ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp | 271 ++++++++++++++++++ ydb/mvp/oidc_proxy/oidc_session_create.cpp | 2 + ydb/mvp/oidc_proxy/oidc_settings.cpp | 4 + ydb/mvp/oidc_proxy/oidc_settings.h | 3 + ydb/mvp/oidc_proxy/openid_connect.cpp | 6 + ydb/mvp/oidc_proxy/openid_connect.h | 2 + ydb/mvp/oidc_proxy/ya.make | 2 + 15 files changed, 492 insertions(+), 133 deletions(-) diff --git a/ydb/mvp/oidc_proxy/mvp.cpp b/ydb/mvp/oidc_proxy/mvp.cpp index c66759d46f06..2ec089051388 100644 --- a/ydb/mvp/oidc_proxy/mvp.cpp +++ b/ydb/mvp/oidc_proxy/mvp.cpp @@ -233,6 +233,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; diff --git a/ydb/mvp/oidc_proxy/oidc_client.cpp b/ydb/mvp/oidc_proxy/oidc_client.cpp index 34e0bd8f2549..825e1e71259d 100644 --- a/ydb/mvp/oidc_proxy/oidc_client.cpp +++ b/ydb/mvp/oidc_proxy/oidc_client.cpp @@ -1,6 +1,8 @@ #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 { @@ -14,17 +16,19 @@ void InitOIDC(NActors::TActorSystem& actorSystem, ) ); - actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler( - "/impersonate/start", - actorSystem.Register(new THandlerImpersonateStart(httpProxyId, settings)) - ) - ); + 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 THandlerImpersonateStop(httpProxyId, settings)) - ) - ); + actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler( + "/impersonate/stop", + actorSystem.Register(new TImpersonateStopPageHandler(httpProxyId, settings)) + ) + ); + } actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler( "/", diff --git a/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp index da800a8cdf05..f7ce19f399db 100644 --- a/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp @@ -1,45 +1,73 @@ -#include #include #include +#include #include +#include #include "openid_connect.h" #include "oidc_session_create.h" -#include "oidc_settings.h" +#include "oidc_impersonate_start_page_nebius.h" namespace NMVP { namespace NOIDC { THandlerImpersonateStart::THandlerImpersonateStart(const NActors::TActorId& sender, - const NHttp::THttpIncomingRequestPtr& request, - const NActors::TActorId& httpProxyId, - const TOpenIdConnectSettings& settings) + const NHttp::THttpIncomingRequestPtr& request, + const NActors::TActorId& httpProxyId, + const TOpenIdConnectSettings& settings) : Sender(sender) , Request(request) , HttpProxyId(httpProxyId) , Settings(settings) {} +TString THandlerImpersonateStart::DecodeToken(const TStringBuf& cookie, const NActors::TActorContext& ctx) { + TString token; + try { + Base64StrictDecode(cookie, token); + } catch (std::exception& e) { + LOG_DEBUG_S(ctx, EService::MVP, "Base64Decode " << cookie << " cookie: " << e.what()); + token.clear(); + } + return token; +} + void THandlerImpersonateStart::Bootstrap(const NActors::TActorContext& ctx) { + LOG_DEBUG_S(ctx, EService::MVP, "Start impersonation process"); NHttp::TUrlParameters urlParameters(Request->URL); - TString serviceAccountId = urlParameters["service_accound_id"]; + TString serviceAccountId = urlParameters["service_account_id"]; NHttp::THeaders headers(Request->Headers); - LOG_DEBUG_S(ctx, EService::MVP, "Start impersonation process"); NHttp::TCookies cookies(headers.Get("Cookie")); - TString sessionToken = DecodeToken(cookies, CreateNameSessionCookie(Settings.ClientId)); - if (sessionToken && serviceAccountId) { - RequestImpersonatedToken(sessionToken, serviceAccountId, ctx); - } else { + 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 = DecodeToken(sessionCookieValue, ctx); + + TString jsonError; + if (sessionToken.empty()) { + jsonError = "Wrong impersonate parameter: session cookie not found"; + } else if (serviceAccountId.empty()) { + jsonError = "Wrong impersonate parameter: account_id not found"; + } + + if (jsonError) { NHttp::THeadersBuilder responseHeaders; responseHeaders.Set("Content-Type", "text/plain"); - httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error); + NHttp::THttpOutgoingResponsePtr httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, jsonError); + ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); + Die(ctx); + } else { + RequestImpersonatedToken(sessionToken, serviceAccountId, ctx); } } -void THandlerImpersonateStart::RequestImpersonatedToken(const TString sessionToken, const TString serviceAccountId, const NActors::TActorContext& ctx) { +void THandlerImpersonateStart::RequestImpersonatedToken(const TString& sessionToken, const TString& serviceAccountId, const NActors::TActorContext& ctx) { LOG_DEBUG_S(ctx, EService::MVP, "Request impersonated token"); - NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetExchangeEndpointURL()); + NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetImpersonateEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); TMvpTokenator* tokenator = MVPAppData()->Tokenator; @@ -48,16 +76,27 @@ void THandlerImpersonateStart::RequestImpersonatedToken(const TString sessionTok 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; + 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, const NActors::TActorContext& ctx) { + TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId); + TString impersonatedCookieValue = Base64Encode(impersonatedToken); + LOG_DEBUG_S(ctx, EService::MVP, "Set impersonated cookie: (" << impersonatedCookieName << ": " << NKikimr::MaskTicket(impersonatedCookieValue) << ")"); - Become(&THandlerSessionServiceCheckNebius::StateExchange); + NHttp::THeadersBuilder responseHeaders; + responseHeaders.Set("Set-Cookie", CreateSecureCookie(impersonatedCookieName, impersonatedCookieValue)); + NHttp::THttpOutgoingResponsePtr httpResponse; + httpResponse = Request->CreateResponse("200", "OK", responseHeaders); + ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse)); + Die(ctx); } void THandlerImpersonateStart::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { @@ -70,13 +109,13 @@ void THandlerImpersonateStart::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRespon 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); + const NJson::TJsonValue* jsonImpersonatedToken; + if (jsonValue.GetValuePointer("impersonation", &jsonImpersonatedToken)) { + TString impersonatedToken = jsonImpersonatedToken->GetStringRobust(); + ProcessImpersonatedToken(impersonatedToken, ctx); return; } else { - jsonError = "Wrong OIDC provider response: access_token not found"; + jsonError = "Wrong OIDC provider response: impersonated token not found"; } } else { jsonError = "Wrong OIDC response"; @@ -98,51 +137,14 @@ void THandlerImpersonateStart::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRespon Die(ctx); } -TString THandlerImpersonateStart::ChangeSameSiteFieldInSessionCookie(const TString& cookie) { - const static TStringBuf SameSiteParameter {"SameSite=Lax"}; - size_t n = cookie.find(SameSiteParameter); - if (n == TString::npos) { - return cookie; - } - TStringBuilder cookieBuilder; - cookieBuilder << cookie.substr(0, n); - cookieBuilder << "SameSite=None"; - cookieBuilder << cookie.substr(n + SameSiteParameter.size()); - return cookieBuilder; -} - -void THandlerImpersonateStart::RetryRequestToProtectedResourceAndDie(const NActors::TActorContext& ctx) { - NHttp::THeadersBuilder responseHeaders; - RetryRequestToProtectedResourceAndDie(&responseHeaders, ctx); -} - -void THandlerImpersonateStart::RetryRequestToProtectedResourceAndDie(NHttp::THeadersBuilder* responseHeaders, const NActors::TActorContext& ctx) { - SetCORS(Request, responseHeaders); - responseHeaders->Set("Location", Context.GetRequestedAddress()); - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("302", "Found", *responseHeaders))); - Die(ctx); -} +TImpersonateStartPageHandler::TImpersonateStartPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings) + : TBase(&TImpersonateStartPageHandler::StateWork) + , HttpProxyId(httpProxyId) + , Settings(settings) +{} -void THandlerImpersonateStart::SendUnknownErrorResponseAndDie(const NActors::TActorContext& ctx) { - NHttp::THeadersBuilder responseHeaders; - responseHeaders.Set("Content-Type", "text/html"); - SetCORS(Request, &responseHeaders); - const static TStringBuf BAD_REQUEST_HTML_PAGE = "" - "" - "" - "400 Bad Request" - "" - "" - "" - "
" - "

" - "Unknown error has occurred. Please open the page again" - "

" - "
" - "" - ""; - ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("400", "Bad Request", responseHeaders, BAD_REQUEST_HTML_PAGE))); - Die(ctx); +void TImpersonateStartPageHandler::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) { + ctx.Register(new THandlerImpersonateStart(event->Sender, event->Get()->Request, HttpProxyId, Settings)); } } // 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 index 938e622d2455..6fca9da17bf7 100644 --- a/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.h +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.h @@ -1,11 +1,5 @@ #pragma once -#include -#include -#include -#include -#include -#include #include "oidc_settings.h" #include "context.h" @@ -24,24 +18,39 @@ class THandlerImpersonateStart : public NActors::TActorBootstrappedGetTypeRewrite()) { + HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); + } + } +}; -private: - void SendUnknownErrorResponseAndDie(const NActors::TActorContext& ctx); +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); + } + } }; } // 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 index b46792a49acc..1bc13778506e 100644 --- a/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.cpp +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.cpp @@ -1,18 +1,14 @@ -#include -#include -#include -#include #include "openid_connect.h" #include "oidc_session_create.h" -#include "oidc_settings.h" +#include "oidc_impersonate_stop_page_nebius.h" namespace NMVP { namespace NOIDC { -THandlerImpersonateStop::THandlerSessionCreate(const NActors::TActorId& sender, - const NHttp::THttpIncomingRequestPtr& request, - const NActors::TActorId& httpProxyId, - const TOpenIdConnectSettings& settings) +THandlerImpersonateStop::THandlerImpersonateStop(const NActors::TActorId& sender, + const NHttp::THttpIncomingRequestPtr& request, + const NActors::TActorId& httpProxyId, + const TOpenIdConnectSettings& settings) : Sender(sender) , Request(request) , HttpProxyId(httpProxyId) @@ -20,9 +16,12 @@ THandlerImpersonateStop::THandlerSessionCreate(const NActors::TActorId& sender, {} void THandlerImpersonateStop::Bootstrap(const NActors::TActorContext& ctx) { - NHttp::THeadersBuilder responseHeaders;responseHeaders + TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId); + LOG_DEBUG_S(ctx, EService::MVP, "Clear impersonated cookie: (" << impersonatedCookieName << ")"); + + NHttp::THeadersBuilder responseHeaders; SetCORS(Request, &responseHeaders); - responseHeaders.Set("Set-Cookie", CreateSecureCookie(Settings.ClientId, sessionToken)); + responseHeaders.Set("Set-Cookie", ClearSecureCookie(impersonatedCookieName)); NHttp::THttpOutgoingResponsePtr httpResponse; httpResponse = Request->CreateResponse("200", "OK", responseHeaders); @@ -30,5 +29,15 @@ void THandlerImpersonateStop::Bootstrap(const NActors::TActorContext& ctx) { Die(ctx); } +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)); +} + } // NOIDC } // NMVP diff --git a/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h index 195391977722..3cbe84b6fd7f 100644 --- a/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h +++ b/ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h @@ -1,11 +1,5 @@ #pragma once -#include -#include -#include -#include -#include -#include #include "oidc_settings.h" #include "context.h" @@ -25,12 +19,29 @@ class THandlerImpersonateStop : public NActors::TActorBootstrapped { + 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); + } + } +}; + } // NOIDC } // NMVP diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp index 89bd55694434..c7ed5f5908f1 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp @@ -8,7 +8,6 @@ #include "openid_connect.h" #include "context.h" #include "oidc_protected_page_nebius.h" - namespace NMVP { namespace NOIDC { @@ -19,12 +18,20 @@ THandlerSessionServiceCheckNebius::THandlerSessionServiceCheckNebius(const NActo : THandlerSessionServiceCheck(sender, request, httpProxyId, settings) {} -TString DecodeToken(NHttp::TCookies& cookies, const TString& name) { +TStringBuf THandlerSessionServiceCheckNebius::GetCookie(const NHttp::TCookies& cookies, const TString& cookieName, const NActors::TActorContext& ctx) { + TStringBuf cookieValue = cookies.Get(cookieName); + if (!cookieValue.Empty()) { + LOG_DEBUG_S(ctx, EService::MVP, "Using cookie (" << cookieName << ": " << NKikimr::MaskTicket(cookieValue) << ")"); + } + return cookieValue; +} + +TString THandlerSessionServiceCheckNebius::DecodeToken(const TStringBuf& cookie, const NActors::TActorContext& ctx) { TString token; try { - Base64StrictDecode(cookies.Get(name), value); + Base64StrictDecode(cookie, token); } catch (std::exception& e) { - LOG_DEBUG("Base64Decode " << name << " cookie: " << e.what()); + LOG_DEBUG_S(ctx, EService::MVP, "Base64Decode " << cookie << " cookie: " << e.what()); token.clear(); } return token; @@ -35,17 +42,14 @@ void THandlerSessionServiceCheckNebius::StartOidcProcess(const NActors::TActorCo LOG_DEBUG_S(ctx, EService::MVP, "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) << ")"); - } + TStringBuf sessionCookieValue = GetCookie(cookies, CreateNameSessionCookie(Settings.ClientId), ctx); + TStringBuf impersonatedCookieValue = GetCookie(cookies, CreateNameImpersonatedCookie(Settings.ClientId), ctx); - TString sessionToken = DecodeToken(cookies, CreateNameSessionCookie(Settings.ClientId)); + TString sessionToken = DecodeToken(sessionCookieValue, ctx); if (sessionToken) { - TString impersonatedToken = DecodeToken(cookies, CreateNameImpersonatedCookie(Settings.ClientId)); + TString impersonatedToken = DecodeToken(impersonatedCookieValue, ctx); if (impersonatedToken) { - ExchangeImpersonatedToken(impersonatedToken, sessionToken, ctx); + ExchangeImpersonatedToken(sessionToken, impersonatedToken, ctx); } else { ExchangeSessionToken(sessionToken, ctx); } @@ -64,7 +68,7 @@ void THandlerSessionServiceCheckNebius:: HandleExchange(NHttp::TEvHttpProxy::TEv Die(ctx); } else { NHttp::THttpIncomingResponsePtr response = event->Get()->Response; - LOG_DEBUG_S(ctx, EService::MVP, "Getting access token: " << response->Status); + LOG_DEBUG_S(ctx, EService::MVP, "Getting access token: " << response->Status << " " << response->Message); if (response->Status == "200") { TString iamToken; static const NJson::TJsonReaderConfig JsonConfig; @@ -77,7 +81,12 @@ void THandlerSessionServiceCheckNebius:: HandleExchange(NHttp::TEvHttpProxy::TEv return; } } else if (response->Status == "400" || response->Status == "401") { - RequestAuthorizationCode(ctx); + LOG_DEBUG_S(ctx, EService::MVP, "Getting access token: " << response->Body); + if (tokenExchangeType == ETokenExchangeType::ImpersonatedToken) { + ClearImpersonatedCookie(ctx); + } else { + RequestAuthorizationCode(ctx); + } return; } // don't know what to do, just forward response @@ -92,6 +101,7 @@ void THandlerSessionServiceCheckNebius:: HandleExchange(NHttp::TEvHttpProxy::TEv void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessionToken, const NActors::TActorContext& ctx) { LOG_DEBUG_S(ctx, EService::MVP, "Exchange session token"); + tokenExchangeType = ETokenExchangeType::SessionToken; NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetExchangeEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); @@ -114,7 +124,8 @@ void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessi } void THandlerSessionServiceCheckNebius::ExchangeImpersonatedToken(const TString sessionToken, const TString impersonatedToken, const NActors::TActorContext& ctx) { - LOG_DEBUG_S(ctx, EService::MVP, "Exchange session token"); + LOG_DEBUG_S(ctx, EService::MVP, "Exchange impersonated token"); + tokenExchangeType = ETokenExchangeType::ImpersonatedToken; NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetExchangeEndpointURL()); httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded"); @@ -125,7 +136,7 @@ void THandlerSessionServiceCheckNebius::ExchangeImpersonatedToken(const TString } httpRequest->Set("Authorization", token); // Bearer included TStringBuilder body; - body << "grant_type=urn:ietf:params:oauth:grant-type:impersonation" + 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 @@ -138,6 +149,17 @@ void THandlerSessionServiceCheckNebius::ExchangeImpersonatedToken(const TString Become(&THandlerSessionServiceCheckNebius::StateExchange); } +void THandlerSessionServiceCheckNebius::ClearImpersonatedCookie(const NActors::TActorContext& ctx) { + TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId); + LOG_DEBUG_S(ctx, EService::MVP, "Clear impersonated cookie (" << impersonatedCookieName << ") and retry"); + NHttp::THeadersBuilder responseHeaders; + SetCORS(Request, &responseHeaders); + responseHeaders.Set("Set-Cookie", ClearSecureCookie(impersonatedCookieName)); + responseHeaders.Set("Location", Request->URL); + ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("307", "Temporary Redirect", responseHeaders))); + Die(ctx); +} + void THandlerSessionServiceCheckNebius::RequestAuthorizationCode(const NActors::TActorContext& ctx) { LOG_DEBUG_S(ctx, EService::MVP, "Request authorization code"); NHttp::THttpOutgoingResponsePtr httpResponse = GetHttpOutgoingResponsePtr(Request, Settings); diff --git a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h index c54aeb700d4d..ed8c04e6d839 100644 --- a/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h +++ b/ydb/mvp/oidc_proxy/oidc_protected_page_nebius.h @@ -9,12 +9,21 @@ 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); - + TStringBuf GetCookie(const NHttp::TCookies& cookies, const TString& cookieName, const NActors::TActorContext& ctx); + TString DecodeToken(const TStringBuf& cookie, const NActors::TActorContext& ctx); void StartOidcProcess(const NActors::TActorContext& ctx) override; void HandleExchange(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx); @@ -33,6 +42,8 @@ class THandlerSessionServiceCheckNebius : public THandlerSessionServiceCheck { private: void ExchangeSessionToken(const TString sessionToken, const NActors::TActorContext& ctx); + void ExchangeImpersonatedToken(const TString sessionToken, const TString impersonatedToken, const NActors::TActorContext& ctx); + void ClearImpersonatedCookie(const NActors::TActorContext& ctx); void RequestAuthorizationCode(const NActors::TActorContext& ctx); void ForwardUserRequest(TStringBuf authHeader, const NActors::TActorContext& ctx, bool secure = false) override; bool NeedSendSecureHttpRequest(const NHttp::THttpIncomingResponsePtr& response) const override; diff --git a/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp b/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp index 68ff53dccb2f..53c27dd80358 100644 --- a/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp +++ b/ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp @@ -8,6 +8,8 @@ #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" @@ -1126,4 +1128,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 048967282f3f..9f5670538579 100644 --- a/ydb/mvp/oidc_proxy/oidc_session_create.cpp +++ b/ydb/mvp/oidc_proxy/oidc_session_create.cpp @@ -20,6 +20,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"]; @@ -56,6 +57,7 @@ void THandlerSessionCreate::Bootstrap(const NActors::TActorContext& ctx) { SendUnknownErrorResponseAndDie(ctx); } } + } void THandlerSessionCreate::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) { diff --git a/ydb/mvp/oidc_proxy/oidc_settings.cpp b/ydb/mvp/oidc_proxy/oidc_settings.cpp index 49ec00fa467a..81c8645d36ce 100644 --- a/ydb/mvp/oidc_proxy/oidc_settings.cpp +++ b/ydb/mvp/oidc_proxy/oidc_settings.cpp @@ -21,5 +21,9 @@ TString TOpenIdConnectSettings::GetExchangeEndpointURL() const { return AuthorizationServerAddress + ExchangeUrlPath; } +TString TOpenIdConnectSettings::GetImpersonateEndpointURL() const { + return AuthorizationServerAddress + ImpersonateUrlPath; +} + } // NOIDC } // NMVP diff --git a/ydb/mvp/oidc_proxy/oidc_settings.h b/ydb/mvp/oidc_proxy/oidc_settings.h index b420ffb3012f..878f3d1ba68c 100644 --- a/ydb/mvp/oidc_proxy/oidc_settings.h +++ b/ydb/mvp/oidc_proxy/oidc_settings.h @@ -15,6 +15,7 @@ struct TOpenIdConnectSettings { 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; @@ -27,11 +28,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 diff --git a/ydb/mvp/oidc_proxy/openid_connect.cpp b/ydb/mvp/oidc_proxy/openid_connect.cpp index aa364fd96ca1..1ee181a4d5e8 100644 --- a/ydb/mvp/oidc_proxy/openid_connect.cpp +++ b/ydb/mvp/oidc_proxy/openid_connect.cpp @@ -127,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: "; diff --git a/ydb/mvp/oidc_proxy/openid_connect.h b/ydb/mvp/oidc_proxy/openid_connect.h index 4cf7cf30196d..a8093a639726 100644 --- a/ydb/mvp/oidc_proxy/openid_connect.h +++ b/ydb/mvp/oidc_proxy/openid_connect.h @@ -45,8 +45,10 @@ 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); 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