Skip to content

Commit

Permalink
Http authorization support (#5982)
Browse files Browse the repository at this point in the history
  • Loading branch information
UgnineSirdis authored Jul 1, 2024
1 parent e791eb9 commit 6576464
Show file tree
Hide file tree
Showing 16 changed files with 539 additions and 73 deletions.
248 changes: 246 additions & 2 deletions ydb/core/grpc_services/base/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ using TYdbIssueMessageType = Ydb::Issue::IssueMessage;
std::pair<TString, TString> SplitPath(const TMaybe<TString>& database, const TString& path);
std::pair<TString, TString> SplitPath(const TString& path);

inline TActorId CreateGRpcRequestProxyId(int n = 0) {
if (n == 0) {
const auto actorId = TActorId(0, "GRpcReqProxy");
return actorId;
}

const auto actorId = TActorId(0, TStringBuilder() << "GRpcReqPro" << n);
return actorId;
}

struct TRpcServices {
enum EServiceId {
EvMakeDirectory = EventSpaceBegin(TKikimrEvents::ES_GRPC_CALLS),
Expand Down Expand Up @@ -224,7 +234,9 @@ struct TRpcServices {
EvAcquireYndxRateLimiterResource,
EvGrpcRuntimeRequest,
EvNodeCheckRequest,
EvStreamWriteRefreshToken // internal call, pair to EvRefreshToken
EvStreamWriteRefreshToken, // internal call, pair to EvRefreshToken
EvRequestAuthAndCheck, // performs authorization and runs GrpcRequestCheckActor
EvRequestAuthAndCheckResult,
// !!! DO NOT ADD NEW REQUEST !!!
};

Expand Down Expand Up @@ -365,7 +377,7 @@ class IRequestProxyCtx
virtual ~IRequestProxyCtx() = default;

// auth
virtual const TMaybe<TString> GetYdbToken() const = 0;
virtual const TMaybe<TString> GetYdbToken() const = 0;
virtual void UpdateAuthState(NYdbGrpc::TAuthState::EAuthState state) = 0;
virtual void SetInternalToken(const TIntrusiveConstPtr<NACLib::TUserToken>& token) = 0;
virtual const NYdbGrpc::TAuthState& GetAuthState() const = 0;
Expand Down Expand Up @@ -1553,5 +1565,237 @@ class TGRpcRequestValidationWrapper
bool RlAllowed;
};

class TEvRequestAuthAndCheckResult : public TEventLocal<TEvRequestAuthAndCheckResult, TRpcServices::EvRequestAuthAndCheckResult> {
public:
TEvRequestAuthAndCheckResult(Ydb::StatusIds::StatusCode status, const NYql::TIssues& issues)
: Status(status)
, Issues(issues)
{}

TEvRequestAuthAndCheckResult(Ydb::StatusIds::StatusCode status, const NYql::TIssue& issue)
: Status(status)
{
Issues.AddIssue(issue);
}

TEvRequestAuthAndCheckResult(Ydb::StatusIds::StatusCode status, const TString& error)
: Status(status)
{
Issues.AddIssue(error);
}

TEvRequestAuthAndCheckResult(const TString& database, const TMaybe<TString>& ydbToken, const TIntrusiveConstPtr<NACLib::TUserToken>& userToken)
: Database(database)
, YdbToken(ydbToken)
, UserToken(userToken)
{}

Ydb::StatusIds::StatusCode Status = Ydb::StatusIds::SUCCESS;
NYql::TIssues Issues;
TString Database;
TMaybe<TString> YdbToken;
TIntrusiveConstPtr<NACLib::TUserToken> UserToken;
};

class TEvRequestAuthAndCheck
: public IRequestProxyCtx
, public TEventLocal<TEvRequestAuthAndCheck, TRpcServices::EvRequestAuthAndCheck> {
public:
TEvRequestAuthAndCheck(const TString& database, const TMaybe<TString>& ydbToken, NActors::TActorId sender)
: Database(database)
, YdbToken(ydbToken)
, Sender(sender)
, AuthState(true)
{}

// IRequestProxyCtx
const TMaybe<TString> GetYdbToken() const override {
return YdbToken;
}

void UpdateAuthState(NYdbGrpc::TAuthState::EAuthState state) override {
AuthState.State = state;
}

void SetInternalToken(const TIntrusiveConstPtr<NACLib::TUserToken>& token) override {
UserToken = token;
}

const NYdbGrpc::TAuthState& GetAuthState() const override {
return AuthState;
}

void ReplyUnauthenticated(const TString& msg = "") override {
if (msg) {
IssueManager.RaiseIssue(NYql::TIssue{msg});
}
ReplyWithYdbStatus(Ydb::StatusIds::UNAUTHORIZED);
}

void ReplyWithYdbStatus(Ydb::StatusIds::StatusCode status) override {
const NActors::TActorContext& ctx = NActors::TActivationContext::AsActorContext();
if (status == Ydb::StatusIds::SUCCESS) {
ctx.Send(Sender,
new TEvRequestAuthAndCheckResult(
Database,
YdbToken,
UserToken
)
);
} else {
ctx.Send(Sender,
new TEvRequestAuthAndCheckResult(
status,
IssueManager.GetIssues()
)
);
}
}

void RaiseIssue(const NYql::TIssue& issue) override {
IssueManager.RaiseIssue(issue);
}

void RaiseIssues(const NYql::TIssues& issues) override {
IssueManager.RaiseIssues(issues);
}

TVector<TStringBuf> FindClientCertPropertyValues() const override {
return {};
}

void StartTracing(NWilson::TSpan&& span) override {
Span = std::move(span);
}
void FinishSpan() override {
Span.End();
}

bool* IsTracingDecided() override {
return nullptr;
}

bool Validate(TString& /*error*/) override {
return true;
}

void SetCounters(IGRpcProxyCounters::TPtr counters) override {
Counters = std::move(counters);
}

IGRpcProxyCounters::TPtr GetCounters() const override {
return Counters;
}

void UseDatabase(const TString& database) override {
Database = database;
}

void SetRespHook(TRespHook&& /*hook*/) override {
}

void SetRlPath(TMaybe<NRpcService::TRlPath>&& path) override {
RlPath = std::move(path);
}

TRateLimiterMode GetRlMode() const override {
return TRateLimiterMode::Rps;
}

bool TryCustomAttributeProcess(const TSchemeBoardEvents::TDescribeSchemeResult& /*schemeData*/, ICheckerIface* /*iface*/) override {
return false;
}

void Pass(const IFacilityProvider& /*facility*/) override {
ReplyWithYdbStatus(Ydb::StatusIds::SUCCESS);
}

void SetAuditLogHook(TAuditLogHook&& /*hook*/) override {
}

void SetDiskQuotaExceeded(bool /*disk*/) override {
}

void AddAuditLogPart(const TStringBuf& name, const TString& value) override {
AuditLogParts.emplace_back(name, value);
}

const TAuditLogParts& GetAuditLogParts() const override {
return AuditLogParts;
}

TMaybe<TString> GetTraceId() const override {
return {};
}

NWilson::TTraceId GetWilsonTraceId() const override {
return Span.GetTraceId();
}

const TMaybe<TString> GetDatabaseName() const override {
return Database ? TMaybe<TString>(Database) : Nothing();
}

const TIntrusiveConstPtr<NACLib::TUserToken>& GetInternalToken() const override {
return UserToken;
}

const TString& GetSerializedToken() const override {
if (UserToken) {
return UserToken->GetSerializedToken();
}

return EmptySerializedTokenMessage;
}

bool IsClientLost() const override {
return false;
}

const TMaybe<TString> GetPeerMetaValues(const TString&) const override {
return {};
}

TString GetPeerName() const override {
return {};
}

const TString& GetRequestName() const override {
static TString str = "request auth and check internal request";
return str;
}

TMaybe<NRpcService::TRlPath> GetRlPath() const override {
return RlPath;
}

TInstant GetDeadline() const override {
return deadline;
}


TMaybe<TString> GetSdkBuildInfo() const {
return {};
}

TMaybe<TString> GetGrpcUserAgent() const {
return {};
}

TString Database;
TMaybe<TString> YdbToken;
NActors::TActorId Sender;
NYdbGrpc::TAuthState AuthState;
NWilson::TSpan Span;
IGRpcProxyCounters::TPtr Counters;
TMaybe<NRpcService::TRlPath> RlPath;
TAuditLogParts AuditLogParts;
NYql::TIssueManager IssueManager;
TIntrusiveConstPtr<NACLib::TUserToken> UserToken;
TInstant deadline = TInstant::Now() + TDuration::Seconds(10);

inline static const TString EmptySerializedTokenMessage;
};

} // namespace NGRpcService
} // namespace NKikimr
34 changes: 32 additions & 2 deletions ydb/core/grpc_services/grpc_request_check_actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ bool TGRpcRequestProxyHandleMethods::ValidateAndReplyOnError(TCtx* ctx) {
}
}

inline const TVector<TEvTicketParser::TEvAuthorizeTicket::TEntry>& GetEntriesForAuthAndCheckRequest(TEvRequestAuthAndCheck::TPtr& ev) {
if (ev->Get()->YdbToken && ev->Get()->YdbToken->StartsWith("Bearer")) {
if (AppData()->AuthConfig.GetUseAccessService()
&& (AppData()->DomainsConfig.GetSecurityConfig().ViewerAllowedSIDsSize() > 0 || AppData()->DomainsConfig.GetSecurityConfig().MonitoringAllowedSIDsSize() > 0)) {
static TVector<NKikimr::TEvTicketParser::TEvAuthorizeTicket::TEntry> entries = {
{NKikimr::TEvTicketParser::TEvAuthorizeTicket::ToPermissions({"ydb.developerApi.get", "ydb.developerApi.update"}), {{"gizmo_id", "gizmo"}}}
};
return entries;
}
}
static TVector<NKikimr::TEvTicketParser::TEvAuthorizeTicket::TEntry> emptyEntries = {};
return emptyEntries;
}

template <typename TEvent>
class TGrpcRequestCheckActor
: public TGRpcRequestProxyHandleMethods
Expand Down Expand Up @@ -73,7 +87,8 @@ class TGrpcRequestCheckActor
}

void ProcessCommonAttributes(const TSchemeBoardEvents::TDescribeSchemeResult& schemeData) {
static std::vector<TString> allowedAttributes = {"folder_id", "service_account_id", "database_id", "container_id"};
TVector<TEvTicketParser::TEvAuthorizeTicket::TEntry> entries;
static std::vector<TString> allowedAttributes = {"folder_id", "service_account_id", "database_id"};
TVector<std::pair<TString, TString>> attributes;
attributes.reserve(schemeData.GetPathDescription().UserAttributesSize());
for (const auto& attr : schemeData.GetPathDescription().GetUserAttributes()) {
Expand All @@ -82,7 +97,16 @@ class TGrpcRequestCheckActor
}
}
if (!attributes.empty()) {
SetEntries({{GetPermissions(), attributes}});
entries.emplace_back(GetPermissions(), attributes);
}

if constexpr (std::is_same_v<TEvent, TEvRequestAuthAndCheck>) {
const auto& e = GetEntriesForAuthAndCheckRequest(Request_);
entries.insert(entries.end(), e.begin(), e.end());
}

if (!entries.empty()) {
SetEntries(entries);
}
}

Expand Down Expand Up @@ -464,6 +488,12 @@ class TGrpcRequestCheckActor
ReplyBackAndDie();
}

void HandleAndDie(TEvRequestAuthAndCheck::TPtr& ev) {
GrpcRequestBaseCtx_->FinishSpan();
ev->Get()->ReplyWithYdbStatus(Ydb::StatusIds::SUCCESS);
PassAway();
}

template <typename T>
void HandleAndDie(T& event) {
GrpcRequestBaseCtx_->FinishSpan();
Expand Down
8 changes: 7 additions & 1 deletion ydb/core/grpc_services/grpc_request_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ class TGRpcRequestProxyImpl
NYql::TIssues()});
}

void Handle(TEvRequestAuthAndCheck::TPtr& ev, const TActorContext&) {
ev->Get()->FinishSpan();
ev->Get()->ReplyWithYdbStatus(Ydb::StatusIds::SUCCESS);
}

// returns true and defer event if no updates for given database
// otherwice returns false and leave event untouched
template <typename TEvent>
Expand Down Expand Up @@ -186,7 +191,7 @@ class TGRpcRequestProxyImpl
if (maybeDatabaseName && !maybeDatabaseName.GetRef().empty()) {
databaseName = CanonizePath(maybeDatabaseName.GetRef());
} else {
if (!AllowYdbRequestsWithoutDatabase && DynamicNode) {
if (!AllowYdbRequestsWithoutDatabase && DynamicNode && !std::is_same_v<TEvent, TEvRequestAuthAndCheck>) { // TEvRequestAuthAndCheck is allowed to be processed without database
requestBaseCtx->ReplyUnauthenticated("Requests without specified database are not allowed");
requestBaseCtx->FinishSpan();
return;
Expand Down Expand Up @@ -590,6 +595,7 @@ void TGRpcRequestProxyImpl::StateFunc(TAutoPtr<IEventHandle>& ev) {
HFunc(TEvCoordinationSessionRequest, PreHandle);
HFunc(TEvNodeCheckRequest, PreHandle);
HFunc(TEvProxyRuntimeEvent, PreHandle);
HFunc(TEvRequestAuthAndCheck, PreHandle);

default:
Y_ABORT("Unknown request: %u\n", ev->GetTypeRewrite());
Expand Down
10 changes: 0 additions & 10 deletions ydb/core/grpc_services/grpc_request_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,5 @@ class TGRpcRequestProxy : public TGRpcRequestProxyHandleMethods, public IFacilit
TActorId DiscoveryCacheActorID;
};

inline TActorId CreateGRpcRequestProxyId(int n = 0) {
if (n == 0) {
const auto actorId = TActorId(0, "GRpcReqProxy");
return actorId;
}

const auto actorId = TActorId(0, TStringBuilder() << "GRpcReqPro" << n);
return actorId;
}

} // namespace NGRpcService
} // namespace NKikimr
Loading

0 comments on commit 6576464

Please sign in to comment.