Skip to content

Commit 2341a2a

Browse files
committed
Create class TContext
1 parent dbba0ea commit 2341a2a

14 files changed

+347
-165
lines changed

ydb/mvp/oidc_proxy/context.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include <util/generic/string.h>
2+
#include <util/random/random.h>
3+
#include <util/string/builder.h>
4+
#include <library/cpp/string_utils/base64/base64.h>
5+
#include <ydb/library/actors/http/http.h>
6+
#include "openid_connect.h"
7+
#include "context.h"
8+
9+
namespace NMVP {
10+
namespace NOIDC {
11+
12+
TContext::TContext(const TString& state, const TString& requestedAddress, bool isAjaxRequest)
13+
: State(state)
14+
, IsAjaxRequest(isAjaxRequest)
15+
, RequestedAddress(requestedAddress)
16+
{}
17+
18+
TContext::TContext(const NHttp::THttpIncomingRequestPtr& request)
19+
: State(GenerateState())
20+
, IsAjaxRequest(DetectAjaxRequest(request))
21+
, RequestedAddress(GetRequestedUrl(request, IsAjaxRequest))
22+
{}
23+
24+
TString TContext::GetState() const {
25+
return State;
26+
}
27+
28+
bool TContext::GetIsAjaxRequest() const {
29+
return IsAjaxRequest;
30+
}
31+
32+
TString TContext::GetRequestedAddress() const {
33+
return RequestedAddress;
34+
}
35+
36+
TString TContext::CreateYdbOidcCookie(const TString& secret) const {
37+
static constexpr size_t COOKIE_MAX_AGE_SEC = 420;
38+
return TStringBuilder() << CreateNameYdbOidcCookie(secret, State) << "="
39+
<< GenerateCookie(secret) << ";"
40+
" Path=" << GetAuthCallbackUrl() << ";"
41+
" Max-Age=" << COOKIE_MAX_AGE_SEC << ";"
42+
" SameSite=None; Secure";
43+
}
44+
45+
TString TContext::GenerateCookie(const TString& secret) const {
46+
const TDuration StateLifeTime = TDuration::Minutes(10);
47+
TInstant expirationTime = TInstant::Now() + StateLifeTime;
48+
TStringBuilder stateStruct;
49+
stateStruct << "{\"state\":\"" << State
50+
<< "\",\"requested_address\":\"" << RequestedAddress
51+
<< "\",\"expiration_time\":" << ToString(expirationTime.TimeT())
52+
<< ",\"ajax_request\":" << (IsAjaxRequest ? "true" : "false") << "}";
53+
TString digest = HmacSHA256(secret, stateStruct);
54+
TString cookieStruct {"{\"state_struct\":\"" + Base64Encode(stateStruct) + "\",\"digest\":\"" + Base64Encode(digest) + "\"}"};
55+
return Base64Encode(cookieStruct);
56+
}
57+
58+
TString TContext::GenerateState() {
59+
TStringBuilder sb;
60+
static constexpr size_t CHAR_NUMBER = 15;
61+
for (size_t i{0}; i < CHAR_NUMBER; i++) {
62+
sb << RandomNumber<char>();
63+
}
64+
return Base64EncodeUrlNoPadding(sb);
65+
}
66+
67+
bool TContext::DetectAjaxRequest(const NHttp::THttpIncomingRequestPtr& request) {
68+
static const THashMap<TStringBuf, TStringBuf> expectedHeaders {
69+
{"Accept", "application/json"}
70+
};
71+
NHttp::THeaders headers(request->Headers);
72+
for (const auto& el : expectedHeaders) {
73+
TStringBuf headerValue = headers.Get(el.first);
74+
if (!headerValue || headerValue.find(el.second) == TStringBuf::npos) {
75+
return false;
76+
}
77+
}
78+
return true;
79+
}
80+
81+
TStringBuf TContext::GetRequestedUrl(const NHttp::THttpIncomingRequestPtr& request, bool isAjaxRequest) {
82+
NHttp::THeaders headers(request->Headers);
83+
TStringBuf requestedUrl = headers.Get("Referer");
84+
if (!isAjaxRequest || requestedUrl.empty()) {
85+
return request->URL;
86+
}
87+
return requestedUrl;
88+
}
89+
90+
} // NOIDC
91+
} // NMVP

ydb/mvp/oidc_proxy/context.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#pragma once
2+
3+
#include <util/generic/string.h>
4+
#include <util/generic/ptr.h>
5+
6+
namespace NHttp {
7+
8+
class THttpIncomingRequest;
9+
using THttpIncomingRequestPtr = TIntrusivePtr<THttpIncomingRequest>;
10+
11+
}
12+
13+
namespace NMVP {
14+
namespace NOIDC {
15+
16+
class TContext {
17+
private:
18+
TString State;
19+
bool IsAjaxRequest = false;
20+
TString RequestedAddress;
21+
22+
public:
23+
TContext(const TString& state = "", const TString& requestedAddress = "", bool isAjaxRequest = false);
24+
TContext(const NHttp::THttpIncomingRequestPtr& request);
25+
26+
TString GetState() const;
27+
bool GetIsAjaxRequest() const;
28+
TString GetRequestedAddress() const;
29+
30+
TString CreateYdbOidcCookie(const TString& secret) const;
31+
32+
private:
33+
static TString GenerateState();
34+
static bool DetectAjaxRequest(const NHttp::THttpIncomingRequestPtr& request);
35+
static TStringBuf GetRequestedUrl(const NHttp::THttpIncomingRequestPtr& request, bool isAjaxRequest);
36+
37+
TString GenerateCookie(const TString& secret) const;
38+
};
39+
40+
} // NOIDC
41+
} // NMVP

ydb/mvp/oidc_proxy/oidc_protected_page.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ void THandlerSessionServiceCheck::Bootstrap(const NActors::TActorContext& ctx) {
2626
return;
2727
}
2828
NHttp::THeaders headers(Request->Headers);
29-
IsAjaxRequest = DetectAjaxRequest(headers);
3029
TStringBuf authHeader = headers.Get(AUTH_HEADER_NAME);
3130
if (Request->Method == "OPTIONS" || IsAuthorizedRequest(authHeader)) {
3231
ForwardUserRequest(TString(authHeader), ctx);

ydb/mvp/oidc_proxy/oidc_protected_page.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class THandlerSessionServiceCheck : public NActors::TActorBootstrapped<THandlerS
2121
const TOpenIdConnectSettings Settings;
2222
TString ProtectedPageUrl;
2323
TString RequestedPageScheme;
24-
bool IsAjaxRequest = false;
24+
// bool IsAjaxRequest = false;
2525

2626
const static inline TStringBuf IAM_TOKEN_SCHEME = "Bearer ";
2727
const static inline TStringBuf IAM_TOKEN_SCHEME_LOWER = "bearer ";

ydb/mvp/oidc_proxy/oidc_protected_page_nebius.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <ydb/mvp/core/mvp_tokens.h>
66
#include <ydb/mvp/core/mvp_log.h>
77
#include "openid_connect.h"
8+
#include "context.h"
89
#include "oidc_protected_page_nebius.h"
910

1011
namespace NMVP {
@@ -99,7 +100,8 @@ void THandlerSessionServiceCheckNebius::ExchangeSessionToken(const TString sessi
99100

100101
void THandlerSessionServiceCheckNebius::RequestAuthorizationCode(const NActors::TActorContext& ctx) {
101102
LOG_DEBUG_S(ctx, EService::MVP, "Request authorization code");
102-
NHttp::THttpOutgoingResponsePtr httpResponse = GetHttpOutgoingResponsePtr(Request, Settings, IsAjaxRequest);
103+
TContext context(Request);
104+
NHttp::THttpOutgoingResponsePtr httpResponse = GetHttpOutgoingResponsePtr(Request, Settings, context);
103105
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse));
104106
Die(ctx);
105107
}

ydb/mvp/oidc_proxy/oidc_protected_page_yandex.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <ydb/mvp/core/mvp_tokens.h>
33
#include <ydb/mvp/core/appdata.h>
44
#include <ydb/mvp/core/mvp_log.h>
5+
#include "context.h"
56
#include "oidc_protected_page_yandex.h"
67

78
namespace NMVP {
@@ -31,7 +32,8 @@ void THandlerSessionServiceCheckYandex::Handle(TEvPrivate::TEvErrorResponse::TPt
3132
LOG_DEBUG_S(ctx, EService::MVP, "SessionService.Check(): " << event->Get()->Status);
3233
NHttp::THttpOutgoingResponsePtr httpResponse;
3334
if (event->Get()->Status == "400") {
34-
httpResponse = GetHttpOutgoingResponsePtr(Request, Settings, IsAjaxRequest);
35+
TContext context(Request);
36+
httpResponse = GetHttpOutgoingResponsePtr(Request, Settings, context);
3537
} else {
3638
httpResponse = Request->CreateResponse( event->Get()->Status, event->Get()->Message, "text/plain", event->Get()->Details);
3739
}

ydb/mvp/oidc_proxy/oidc_proxy_ut.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "oidc_session_create_handler.h"
1111
#include "oidc_settings.h"
1212
#include "openid_connect.h"
13+
#include "context.h"
1314

1415
using namespace NMVP::NOIDC;
1516

@@ -657,7 +658,8 @@ Y_UNIT_TEST_SUITE(Mvp) {
657658
TStringBuilder request;
658659
request << "GET /auth/callback?code=code_template&state=" << state << " HTTP/1.1\r\n";
659660
request << "Host: " + hostProxy + "\r\n";
660-
request << "Cookie: " << CreateNameYdbOidcCookie(settings.ClientSecret, wrongState) << "=" << GenerateCookie(wrongState, "/requested/page", settings.ClientSecret, redirectStrategy.IsAjaxRequest()) << "\r\n";
661+
TContext context(wrongState, "/requested/page", redirectStrategy.IsAjaxRequest());
662+
request << "Cookie: " << context.CreateYdbOidcCookie(settings.ClientSecret) << "\r\n";
661663
NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest();
662664
EatWholeString(incomingRequest, redirectStrategy.CreateRequest(request));
663665
incomingRequest->Endpoint->Secure = true;
@@ -711,8 +713,8 @@ Y_UNIT_TEST_SUITE(Mvp) {
711713
TStringBuilder request;
712714
request << "GET /auth/callback?code=code_template&state=" << state << " HTTP/1.1\r\n";
713715
request << "Host: oidcproxy.net\r\n";
714-
const TString oidcCookie = CreateNameYdbOidcCookie(settings.ClientSecret, state);
715-
request << "Cookie: " << oidcCookie << "=" << GenerateCookie(state, "/requested/page", settings.ClientSecret, false) << "\r\n\r\n";
716+
TContext context(state, "/requested/page", false);
717+
request << "Cookie: " << context.CreateYdbOidcCookie(settings.ClientSecret) << "\r\n\r\n";
716718
NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest();
717719
EatWholeString(incomingRequest, request);
718720
runtime.Send(new IEventHandle(sessionCreator, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(incomingRequest)));
@@ -761,7 +763,8 @@ Y_UNIT_TEST_SUITE(Mvp) {
761763
TStringBuilder request;
762764
request << "GET /auth/callback?code=code_template&state=" << state << " HTTP/1.1\r\n";
763765
request << "Host: oidcproxy.net\r\n";
764-
request << "Cookie: " << CreateNameYdbOidcCookie(settings.ClientSecret, state) << "=" << GenerateCookie(state, "/requested/page", settings.ClientSecret, redirectStrategy.IsAjaxRequest()) << "\r\n";
766+
TContext context(state, "/requested/page", redirectStrategy.IsAjaxRequest());
767+
request << "Cookie: " << context.CreateYdbOidcCookie(settings.ClientSecret) << "\r\n";
765768
NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest();
766769
EatWholeString(incomingRequest, redirectStrategy.CreateRequest(request));
767770
incomingRequest->Endpoint->Secure = true;
@@ -834,8 +837,8 @@ Y_UNIT_TEST_SUITE(Mvp) {
834837
TStringBuilder request;
835838
request << "GET /callback?code=code_template&state=" << state << " HTTP/1.1\r\n";
836839
request << "Host: oidcproxy.net\r\n";
837-
const TString oidcCookie = CreateNameYdbOidcCookie(settings.ClientSecret, state);
838-
request << "Cookie: " << oidcCookie << "=" << GenerateCookie(state, "/requested/page", settings.ClientSecret, false) << "\r\n\r\n";
840+
TContext context(state, "/requested/page", false);
841+
request << "Cookie: " << context.CreateYdbOidcCookie(settings.ClientSecret) << "\r\n\r\n";
839842
NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest();
840843
EatWholeString(incomingRequest, request);
841844
runtime.Send(new IEventHandle(sessionCreator, edge, new NHttp::TEvHttpProxy::TEvHttpIncomingRequest(incomingRequest)));
@@ -879,14 +882,14 @@ Y_UNIT_TEST_SUITE(Mvp) {
879882
std::unique_ptr<grpc::Server> sessionServer(builder.BuildAndStart());
880883

881884
const NActors::TActorId sessionCreator = runtime.Register(new TSessionCreateHandler(edge, settings));
882-
TStringBuf firstRequestState = "first_request_state";
883-
TStringBuf secondRequestState = "second_request_state";
884-
TString firstCookie {CreateNameYdbOidcCookie(settings.ClientSecret, firstRequestState) + "=" + GenerateCookie(firstRequestState, "/requested/page", settings.ClientSecret, redirectStrategy.IsAjaxRequest())};
885-
TString secondCookie {CreateNameYdbOidcCookie(settings.ClientSecret, secondRequestState) + "=" + GenerateCookie(secondRequestState, "/requested/page", settings.ClientSecret, redirectStrategy.IsAjaxRequest())};
885+
TString firstRequestState = "first_request_state";
886+
TString secondRequestState = "second_request_state";
887+
TContext context1(firstRequestState, "/requested/page", redirectStrategy.IsAjaxRequest());
888+
TContext context2(secondRequestState, "/requested/page", redirectStrategy.IsAjaxRequest());
886889
TStringBuilder request;
887890
request << "GET /auth/callback?code=code_template&state=" << firstRequestState << " HTTP/1.1\r\n";
888891
request << "Host: oidcproxy.net\r\n";
889-
request << "Cookie: " << firstCookie << "; " << secondCookie << "\r\n";
892+
request << "Cookie: " << context1.CreateYdbOidcCookie(settings.ClientSecret) << "; " << context2.CreateYdbOidcCookie(settings.ClientSecret) << "\r\n";
890893
NHttp::THttpIncomingRequestPtr incomingRequest = new NHttp::THttpIncomingRequest();
891894
EatWholeString(incomingRequest, redirectStrategy.CreateRequest(request));
892895
incomingRequest->Endpoint->Secure = true;

0 commit comments

Comments
 (0)