Skip to content

Commit 34dcd14

Browse files
impersonate
1 parent cf0394a commit 34dcd14

9 files changed

+328
-12
lines changed

ydb/mvp/oidc_proxy/oidc_client.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ void InitOIDC(NActors::TActorSystem& actorSystem,
1414
)
1515
);
1616

17+
actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(
18+
"/impersonate/start",
19+
actorSystem.Register(new THandlerImpersonateStart(httpProxyId, settings))
20+
)
21+
);
22+
23+
actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(
24+
"/impersonate/stop",
25+
actorSystem.Register(new THandlerImpersonateStop(httpProxyId, settings))
26+
)
27+
);
28+
1729
actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(
1830
"/",
1931
actorSystem.Register(new TProtectedPageHandler(httpProxyId, settings))
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include <library/cpp/json/json_reader.h>
2+
#include <library/cpp/string_utils/base64/base64.h>
3+
#include <ydb/library/actors/http/http.h>
4+
#include <ydb/mvp/core/mvp_log.h>
5+
#include "openid_connect.h"
6+
#include "oidc_session_create.h"
7+
#include "oidc_settings.h"
8+
9+
namespace NMVP {
10+
namespace NOIDC {
11+
12+
THandlerImpersonateStart::THandlerImpersonateStart(const NActors::TActorId& sender,
13+
const NHttp::THttpIncomingRequestPtr& request,
14+
const NActors::TActorId& httpProxyId,
15+
const TOpenIdConnectSettings& settings)
16+
: Sender(sender)
17+
, Request(request)
18+
, HttpProxyId(httpProxyId)
19+
, Settings(settings)
20+
{}
21+
22+
void THandlerImpersonateStart::Bootstrap(const NActors::TActorContext& ctx) {
23+
NHttp::TUrlParameters urlParameters(Request->URL);
24+
TString serviceAccountId = urlParameters["service_accound_id"];
25+
26+
NHttp::THeaders headers(Request->Headers);
27+
LOG_DEBUG_S(ctx, EService::MVP, "Start impersonation process");
28+
NHttp::TCookies cookies(headers.Get("Cookie"));
29+
TString sessionToken = DecodeToken(cookies, CreateNameSessionCookie(Settings.ClientId));
30+
31+
if (sessionToken && serviceAccountId) {
32+
RequestImpersonatedToken(sessionToken, serviceAccountId, ctx);
33+
} else {
34+
NHttp::THeadersBuilder responseHeaders;
35+
responseHeaders.Set("Content-Type", "text/plain");
36+
httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error);
37+
}
38+
}
39+
40+
void THandlerImpersonateStart::RequestImpersonatedToken(const TString sessionToken, const TString serviceAccountId, const NActors::TActorContext& ctx) {
41+
LOG_DEBUG_S(ctx, EService::MVP, "Request impersonated token");
42+
NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetExchangeEndpointURL());
43+
httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded");
44+
45+
TMvpTokenator* tokenator = MVPAppData()->Tokenator;
46+
TString token = "";
47+
if (tokenator) {
48+
token = tokenator->GetToken(Settings.SessionServiceTokenName);
49+
}
50+
httpRequest->Set("Authorization", token); // Bearer included
51+
TStringBuilder body;
52+
body << "grant_type=urn:ietf:params:oauth:grant-type:token-exchange"
53+
<< "&requested_token_type=urn:ietf:params:oauth:token-type:access_token"
54+
<< "&subject_token_type=urn:ietf:params:oauth:token-type:session_token"
55+
<< "&subject_token=" << sessionToken;
56+
httpRequest->Set<&NHttp::THttpRequest::Body>(body);
57+
58+
ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest));
59+
60+
Become(&THandlerSessionServiceCheckNebius::StateExchange);
61+
}
62+
63+
void THandlerImpersonateStart::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) {
64+
NHttp::THttpOutgoingResponsePtr httpResponse;
65+
if (event->Get()->Error.empty() && event->Get()->Response) {
66+
NHttp::THttpIncomingResponsePtr response = event->Get()->Response;
67+
LOG_DEBUG_S(ctx, EService::MVP, "Incoming response from authorization server: " << response->Status);
68+
if (response->Status == "200") {
69+
TStringBuf jsonError;
70+
NJson::TJsonValue jsonValue;
71+
NJson::TJsonReaderConfig jsonConfig;
72+
if (NJson::ReadJsonTree(response->Body, &jsonConfig, &jsonValue)) {
73+
const NJson::TJsonValue* jsonAccessToken;
74+
if (jsonValue.GetValuePointer("access_token", &jsonAccessToken)) {
75+
TString sessionToken = jsonAccessToken->GetStringRobust();
76+
ProcessSessionToken(sessionToken, ctx);
77+
return;
78+
} else {
79+
jsonError = "Wrong OIDC provider response: access_token not found";
80+
}
81+
} else {
82+
jsonError = "Wrong OIDC response";
83+
}
84+
NHttp::THeadersBuilder responseHeaders;
85+
responseHeaders.Set("Content-Type", "text/plain");
86+
httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, jsonError);
87+
} else {
88+
NHttp::THeadersBuilder responseHeaders;
89+
responseHeaders.Parse(response->Headers);
90+
httpResponse = Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body);
91+
}
92+
} else {
93+
NHttp::THeadersBuilder responseHeaders;
94+
responseHeaders.Set("Content-Type", "text/plain");
95+
httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error);
96+
}
97+
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse));
98+
Die(ctx);
99+
}
100+
101+
TString THandlerImpersonateStart::ChangeSameSiteFieldInSessionCookie(const TString& cookie) {
102+
const static TStringBuf SameSiteParameter {"SameSite=Lax"};
103+
size_t n = cookie.find(SameSiteParameter);
104+
if (n == TString::npos) {
105+
return cookie;
106+
}
107+
TStringBuilder cookieBuilder;
108+
cookieBuilder << cookie.substr(0, n);
109+
cookieBuilder << "SameSite=None";
110+
cookieBuilder << cookie.substr(n + SameSiteParameter.size());
111+
return cookieBuilder;
112+
}
113+
114+
void THandlerImpersonateStart::RetryRequestToProtectedResourceAndDie(const NActors::TActorContext& ctx) {
115+
NHttp::THeadersBuilder responseHeaders;
116+
RetryRequestToProtectedResourceAndDie(&responseHeaders, ctx);
117+
}
118+
119+
void THandlerImpersonateStart::RetryRequestToProtectedResourceAndDie(NHttp::THeadersBuilder* responseHeaders, const NActors::TActorContext& ctx) {
120+
SetCORS(Request, responseHeaders);
121+
responseHeaders->Set("Location", Context.GetRequestedAddress());
122+
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("302", "Found", *responseHeaders)));
123+
Die(ctx);
124+
}
125+
126+
void THandlerImpersonateStart::SendUnknownErrorResponseAndDie(const NActors::TActorContext& ctx) {
127+
NHttp::THeadersBuilder responseHeaders;
128+
responseHeaders.Set("Content-Type", "text/html");
129+
SetCORS(Request, &responseHeaders);
130+
const static TStringBuf BAD_REQUEST_HTML_PAGE = "<html>"
131+
"<head>"
132+
"<title>"
133+
"400 Bad Request"
134+
"</title>"
135+
"</head>"
136+
"<body bgcolor=\"white\">"
137+
"<center>"
138+
"<h1>"
139+
"Unknown error has occurred. Please open the page again"
140+
"</h1>"
141+
"</center>"
142+
"</body>"
143+
"</html>";
144+
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(Request->CreateResponse("400", "Bad Request", responseHeaders, BAD_REQUEST_HTML_PAGE)));
145+
Die(ctx);
146+
}
147+
148+
} // NOIDC
149+
} // NMVP
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
#include <util/generic/string.h>
4+
#include <util/generic/strbuf.h>
5+
#include <ydb/library/actors/core/actor_bootstrapped.h>
6+
#include <ydb/library/actors/core/actorid.h>
7+
#include <ydb/library/actors/http/http.h>
8+
#include <ydb/library/actors/http/http_proxy.h>
9+
#include "oidc_settings.h"
10+
#include "context.h"
11+
12+
namespace NMVP {
13+
namespace NOIDC {
14+
15+
class THandlerImpersonateStart : public NActors::TActorBootstrapped<THandlerImpersonateStart> {
16+
private:
17+
using TBase = NActors::TActorBootstrapped<THandlerImpersonateStart>;
18+
19+
protected:
20+
const NActors::TActorId Sender;
21+
const NHttp::THttpIncomingRequestPtr Request;
22+
NActors::TActorId HttpProxyId;
23+
const TOpenIdConnectSettings Settings;
24+
TContext Context;
25+
26+
public:
27+
THandlerImpersonateStart(const NActors::TActorId& sender,
28+
const NHttp::THttpIncomingRequestPtr& request,
29+
const NActors::TActorId& httpProxyId,
30+
const TOpenIdConnectSettings& settings);
31+
32+
virtual void RequestSessionToken(const TString&, const NActors::TActorContext&) = 0;
33+
virtual void ProcessSessionToken(const TString& accessToken, const NActors::TActorContext&) = 0;
34+
35+
void Bootstrap(const NActors::TActorContext& ctx);
36+
void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx);
37+
38+
protected:
39+
TString ChangeSameSiteFieldInSessionCookie(const TString& cookie);
40+
void RetryRequestToProtectedResourceAndDie(const NActors::TActorContext& ctx);
41+
void RetryRequestToProtectedResourceAndDie(NHttp::THeadersBuilder* responseHeaders, const NActors::TActorContext& ctx);
42+
43+
private:
44+
void SendUnknownErrorResponseAndDie(const NActors::TActorContext& ctx);
45+
};
46+
47+
} // NOIDC
48+
} // NMVP
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <library/cpp/json/json_reader.h>
2+
#include <library/cpp/string_utils/base64/base64.h>
3+
#include <ydb/library/actors/http/http.h>
4+
#include <ydb/mvp/core/mvp_log.h>
5+
#include "openid_connect.h"
6+
#include "oidc_session_create.h"
7+
#include "oidc_settings.h"
8+
9+
namespace NMVP {
10+
namespace NOIDC {
11+
12+
THandlerImpersonateStop::THandlerSessionCreate(const NActors::TActorId& sender,
13+
const NHttp::THttpIncomingRequestPtr& request,
14+
const NActors::TActorId& httpProxyId,
15+
const TOpenIdConnectSettings& settings)
16+
: Sender(sender)
17+
, Request(request)
18+
, HttpProxyId(httpProxyId)
19+
, Settings(settings)
20+
{}
21+
22+
void THandlerImpersonateStop::Bootstrap(const NActors::TActorContext& ctx) {
23+
NHttp::THeadersBuilder responseHeaders;responseHeaders
24+
SetCORS(Request, &responseHeaders);
25+
responseHeaders.Set("Set-Cookie", CreateSecureCookie(Settings.ClientId, sessionToken));
26+
27+
NHttp::THttpOutgoingResponsePtr httpResponse;
28+
httpResponse = Request->CreateResponse("200", "OK", responseHeaders);
29+
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse));
30+
Die(ctx);
31+
}
32+
33+
} // NOIDC
34+
} // NMVP
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#pragma once
2+
3+
#include <util/generic/string.h>
4+
#include <util/generic/strbuf.h>
5+
#include <ydb/library/actors/core/actor_bootstrapped.h>
6+
#include <ydb/library/actors/core/actorid.h>
7+
#include <ydb/library/actors/http/http.h>
8+
#include <ydb/library/actors/http/http_proxy.h>
9+
#include "oidc_settings.h"
10+
#include "context.h"
11+
12+
namespace NMVP {
13+
namespace NOIDC {
14+
15+
class THandlerImpersonateStop : public NActors::TActorBootstrapped<THandlerImpersonateStop> {
16+
private:
17+
using TBase = NActors::TActorBootstrapped<THandlerImpersonateStop>;
18+
19+
protected:
20+
const NActors::TActorId Sender;
21+
const NHttp::THttpIncomingRequestPtr Request;
22+
NActors::TActorId HttpProxyId;
23+
const TOpenIdConnectSettings Settings;
24+
TContext Context;
25+
26+
public:
27+
THandlerImpersonateStop(const NActors::TActorId& sender,
28+
const NHttp::THttpIncomingRequestPtr& request,
29+
const NActors::TActorId& httpProxyId,
30+
const TOpenIdConnectSettings& settings);
31+
32+
void Bootstrap(const NActors::TActorContext& ctx);
33+
};
34+
35+
} // NOIDC
36+
} // NMVP

ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ THandlerSessionServiceCheckNebius::THandlerSessionServiceCheckNebius(const NActo
1919
: THandlerSessionServiceCheck(sender, request, httpProxyId, settings)
2020
{}
2121

22+
TString DecodeToken(NHttp::TCookies& cookies, const TString& name) {
23+
TString token;
24+
try {
25+
Base64StrictDecode(cookies.Get(name), value);
26+
} catch (std::exception& e) {
27+
LOG_DEBUG("Base64Decode " << name << " cookie: " << e.what());
28+
token.clear();
29+
}
30+
return token;
31+
}
32+
2233
void THandlerSessionServiceCheckNebius::StartOidcProcess(const NActors::TActorContext& ctx) {
2334
NHttp::THeaders headers(Request->Headers);
2435
LOG_DEBUG_S(ctx, EService::MVP, "Start OIDC process");
@@ -30,18 +41,15 @@ void THandlerSessionServiceCheckNebius::StartOidcProcess(const NActors::TActorCo
3041
LOG_DEBUG_S(ctx, EService::MVP, "Using session cookie (" << sessionCookieName << ": " << NKikimr::MaskTicket(sessionCookieValue) << ")");
3142
}
3243

33-
34-
TString sessionToken;
35-
try {
36-
Base64StrictDecode(sessionCookieValue, sessionToken);
37-
} catch (std::exception& e) {
38-
LOG_DEBUG_S(ctx, EService::MVP, "Base64Decode session cookie: " << e.what());
39-
sessionToken.clear();
40-
}
41-
44+
TString sessionToken = DecodeToken(cookies, CreateNameSessionCookie(Settings.ClientId));
4245
if (sessionToken) {
43-
ExchangeSessionToken(sessionToken, ctx);
44-
} else {
46+
TString impersonatedToken = DecodeToken(cookies, CreateNameImpersonatedCookie(Settings.ClientId));
47+
if (impersonatedToken) {
48+
ExchangeImpersonatedToken(impersonatedToken, sessionToken, ctx);
49+
} else {
50+
ExchangeSessionToken(sessionToken, ctx);
51+
}
52+
} else{
4553
RequestAuthorizationCode(ctx);
4654
}
4755
}
@@ -105,6 +113,31 @@ void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessi
105113
Become(&THandlerSessionServiceCheckNebius::StateExchange);
106114
}
107115

116+
void THandlerSessionServiceCheckNebius::ExchangeImpersonatedToken(const TString sessionToken, const TString impersonatedToken, const NActors::TActorContext& ctx) {
117+
LOG_DEBUG_S(ctx, EService::MVP, "Exchange session token");
118+
NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetExchangeEndpointURL());
119+
httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded");
120+
121+
TMvpTokenator* tokenator = MVPAppData()->Tokenator;
122+
TString token = "";
123+
if (tokenator) {
124+
token = tokenator->GetToken(Settings.SessionServiceTokenName);
125+
}
126+
httpRequest->Set("Authorization", token); // Bearer included
127+
TStringBuilder body;
128+
body << "grant_type=urn:ietf:params:oauth:grant-type:impersonation"
129+
<< "&requested_token_type=urn:ietf:params:oauth:token-type:access_token"
130+
<< "&subject_token_type=urn:ietf:params:oauth:token-type:jwt"
131+
<< "&subject_token=" << impersonatedToken
132+
<< "&actor_token=" << sessionToken
133+
<< "&actor_token_type=urn:ietf:params:oauth:token-type:session_token";
134+
httpRequest->Set<&NHttp::THttpRequest::Body>(body);
135+
136+
ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest));
137+
138+
Become(&THandlerSessionServiceCheckNebius::StateExchange);
139+
}
140+
108141
void THandlerSessionServiceCheckNebius::RequestAuthorizationCode(const NActors::TActorContext& ctx) {
109142
LOG_DEBUG_S(ctx, EService::MVP, "Request authorization code");
110143
NHttp::THttpOutgoingResponsePtr httpResponse = GetHttpOutgoingResponsePtr(Request, Settings);

ydb/mvp/oidc_proxy/oidc_session_create.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ void THandlerSessionCreate::Bootstrap(const NActors::TActorContext& ctx) {
5656
SendUnknownErrorResponseAndDie(ctx);
5757
}
5858
}
59-
6059
}
6160

6261
void THandlerSessionCreate::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) {

ydb/mvp/oidc_proxy/oidc_settings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace NOIDC {
99
struct TOpenIdConnectSettings {
1010
static const inline TString YDB_OIDC_COOKIE = "ydb_oidc_cookie";
1111
static const inline TString SESSION_COOKIE = "session_cookie";
12+
static const inline TString IMPERSONATED_COOKIE = "impersonated_cookie";
1213

1314
static const inline TString DEFAULT_CLIENT_ID = "yc.oauth.ydb-viewer";
1415
static const inline TString DEFAULT_AUTH_URL_PATH = "/oauth/authorize";

ydb/mvp/oidc_proxy/openid_connect.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ TString CreateNameSessionCookie(TStringBuf key) {
111111
return "__Host_" + TOpenIdConnectSettings::SESSION_COOKIE + "_" + HexEncode(key);
112112
}
113113

114+
TString CreateNameImpersonatedCookie(TStringBuf key) {
115+
return "__Host_" + TOpenIdConnectSettings::IMPERSONATED_COOKIE + "_" + HexEncode(key);
116+
}
117+
114118
const TString& GetAuthCallbackUrl() {
115119
static const TString callbackUrl = "/auth/callback";
116120
return callbackUrl;

0 commit comments

Comments
 (0)