aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/tvmauth/src/user_impl.cpp
diff options
context:
space:
mode:
authorkomels <komels@yandex-team.ru>2022-04-14 13:10:53 +0300
committerkomels <komels@yandex-team.ru>2022-04-14 13:10:53 +0300
commit21c9b0e6b039e9765eb414c406c2b86e8cea6850 (patch)
treef40ebc18ff8958dfbd189954ad024043ca983ea5 /library/cpp/tvmauth/src/user_impl.cpp
parent9a4effa852abe489707139c2b260dccc6f4f9aa9 (diff)
downloadydb-21c9b0e6b039e9765eb414c406c2b86e8cea6850.tar.gz
Final part on compatibility layer: LOGBROKER-7215
ref:777c67aadbf705d19034a09a792b2df61ba53697
Diffstat (limited to 'library/cpp/tvmauth/src/user_impl.cpp')
-rw-r--r--library/cpp/tvmauth/src/user_impl.cpp241
1 files changed, 241 insertions, 0 deletions
diff --git a/library/cpp/tvmauth/src/user_impl.cpp b/library/cpp/tvmauth/src/user_impl.cpp
new file mode 100644
index 0000000000..4fda799aac
--- /dev/null
+++ b/library/cpp/tvmauth/src/user_impl.cpp
@@ -0,0 +1,241 @@
+#include "user_impl.h"
+
+#include "parser.h"
+
+#include <library/cpp/tvmauth/exception.h>
+#include <library/cpp/tvmauth/ticket_status.h>
+
+#include <util/generic/strbuf.h>
+#include <util/string/cast.h>
+#include <util/string/split.h>
+
+#include <algorithm>
+
+namespace NTvmAuth {
+ static const char* EX_MSG = "Method cannot be used in non-valid ticket";
+
+ TStringBuf GetBlackboxEnvAsString(EBlackboxEnv environment) {
+ switch (environment) {
+ case (EBlackboxEnv::Prod):
+ return TStringBuf("Prod");
+ case (EBlackboxEnv::Test):
+ return TStringBuf("Test");
+ case (EBlackboxEnv::ProdYateam):
+ return TStringBuf("ProdYateam");
+ case (EBlackboxEnv::TestYateam):
+ return TStringBuf("TestYateam");
+ case (EBlackboxEnv::Stress):
+ return TStringBuf("Stress");
+ default:
+ throw yexception() << "Unknown environment";
+ }
+ }
+
+ TCheckedUserTicket::TImpl::operator bool() const {
+ return (Status_ == ETicketStatus::Ok);
+ }
+
+ TUid TCheckedUserTicket::TImpl::GetDefaultUid() const {
+ Y_ENSURE_EX(bool(*this), TNotAllowedException() << EX_MSG);
+ return ProtobufTicket_.user().defaultuid();
+ }
+
+ time_t TCheckedUserTicket::TImpl::GetExpirationTime() const {
+ Y_ENSURE_EX(bool(*this), TNotAllowedException() << EX_MSG);
+ return ProtobufTicket_.expirationtime();
+ }
+
+ const TScopes& TCheckedUserTicket::TImpl::GetScopes() const {
+ Y_ENSURE_EX(bool(*this), TNotAllowedException() << EX_MSG);
+ if (CachedScopes_.empty()) {
+ for (const auto& el : ProtobufTicket_.user().scopes()) {
+ CachedScopes_.push_back(el);
+ }
+ }
+ return CachedScopes_;
+ }
+
+ bool TCheckedUserTicket::TImpl::HasScope(TStringBuf scopeName) const {
+ Y_ENSURE_EX(bool(*this), TNotAllowedException() << EX_MSG);
+ return std::binary_search(ProtobufTicket_.user().scopes().begin(), ProtobufTicket_.user().scopes().end(), scopeName);
+ }
+
+ ETicketStatus TCheckedUserTicket::TImpl::GetStatus() const {
+ return Status_;
+ }
+
+ const TUids& TCheckedUserTicket::TImpl::GetUids() const {
+ Y_ENSURE_EX(bool(*this), TNotAllowedException() << EX_MSG);
+ if (CachedUids_.empty()) {
+ for (const auto& user : ProtobufTicket_.user().users()) {
+ CachedUids_.push_back(user.uid());
+ }
+ }
+ return CachedUids_;
+ }
+
+ TString TCheckedUserTicket::TImpl::DebugInfo() const {
+ if (CachedDebugInfo_) {
+ return CachedDebugInfo_;
+ }
+
+ if (Status_ == ETicketStatus::Malformed) {
+ CachedDebugInfo_ = "status=malformed;";
+ return CachedDebugInfo_;
+ }
+
+ TString targetString = "ticket_type=";
+ targetString.reserve(256);
+ if (Status_ == ETicketStatus::InvalidTicketType) {
+ targetString.append("not-user;");
+ CachedDebugInfo_ = targetString;
+ return targetString;
+ }
+
+ targetString.append("user");
+ if (ProtobufTicket_.expirationtime() > 0)
+ targetString.append(";expiration_time=").append(IntToString<10>(ProtobufTicket_.expirationtime()));
+ for (const auto& scope : ProtobufTicket_.user().scopes()) {
+ targetString.append(";scope=").append(scope);
+ }
+
+ if (ProtobufTicket_.user().defaultuid() > 0)
+ targetString.append(";default_uid=").append(IntToString<10>(ProtobufTicket_.user().defaultuid()));
+ for (const auto& user : ProtobufTicket_.user().users()) {
+ targetString.append(";uid=").append(IntToString<10>(user.uid()));
+ }
+
+ targetString.append(";env=");
+ EBlackboxEnv environment = static_cast<EBlackboxEnv>(ProtobufTicket_.user().env());
+ targetString.append(GetBlackboxEnvAsString(environment));
+ targetString.append(";");
+
+ CachedDebugInfo_ = targetString;
+ return targetString;
+ }
+
+ EBlackboxEnv TCheckedUserTicket::TImpl::GetEnv() const {
+ return (EBlackboxEnv)ProtobufTicket_.user().env();
+ }
+
+ void TCheckedUserTicket::TImpl::SetStatus(ETicketStatus status) {
+ Status_ = status;
+ }
+
+ TCheckedUserTicket::TImpl::TImpl(ETicketStatus status, ticket2::Ticket&& protobufTicket)
+ : Status_(status)
+ , ProtobufTicket_(std::move(protobufTicket))
+ {
+ }
+
+ TUserTicketImplPtr TCheckedUserTicket::TImpl::CreateTicketForTests(ETicketStatus status,
+ TUid defaultUid,
+ TScopes scopes,
+ TUids uids,
+ EBlackboxEnv env) {
+ auto prepareCont = [](auto& cont) {
+ std::sort(cont.begin(), cont.end());
+ cont.erase(std::unique(cont.begin(), cont.end()), cont.end());
+ };
+ auto erase = [](auto& cont, auto val) {
+ auto it = std::find(cont.begin(), cont.end(), val);
+ if (it != cont.end()) {
+ cont.erase(it);
+ }
+ };
+
+ prepareCont(scopes);
+ erase(scopes, "");
+
+ uids.push_back(defaultUid);
+ prepareCont(uids);
+ erase(uids, 0);
+ Y_ENSURE(!uids.empty(), "User ticket cannot contain empty uid list");
+
+ ticket2::Ticket proto;
+ for (TUid uid : uids) {
+ proto.mutable_user()->add_users()->set_uid(uid);
+ }
+ proto.mutable_user()->set_defaultuid(defaultUid);
+ proto.mutable_user()->set_entrypoint(100500);
+ for (TStringBuf scope : scopes) {
+ proto.mutable_user()->add_scopes(TString(scope));
+ }
+
+ proto.mutable_user()->set_env((tvm_keys::BbEnvType)env);
+
+ return MakeHolder<TImpl>(status, std::move(proto));
+ }
+
+ TUserContext::TImpl::TImpl(EBlackboxEnv env, TStringBuf tvmKeysResponse)
+ : Env_(env)
+ {
+ ResetKeys(tvmKeysResponse);
+ }
+
+ void TUserContext::TImpl::ResetKeys(TStringBuf tvmKeysResponse) {
+ tvm_keys::Keys protoKeys;
+ if (!protoKeys.ParseFromString(TParserTvmKeys::ParseStrV1(tvmKeysResponse))) {
+ ythrow TMalformedTvmKeysException() << "Malformed TVM keys";
+ }
+
+ NRw::TPublicKeys keys;
+ for (int idx = 0; idx < protoKeys.bb_size(); ++idx) {
+ const tvm_keys::BbKey& k = protoKeys.bb(idx);
+ if (IsAllowed(k.env())) {
+ keys.emplace(k.gen().id(),
+ k.gen().body());
+ }
+ }
+
+ if (keys.empty()) {
+ ythrow TEmptyTvmKeysException() << "Empty TVM keys";
+ }
+
+ Keys_ = std::move(keys);
+ }
+
+ TUserTicketImplPtr TUserContext::TImpl::Check(TStringBuf ticketBody) const {
+ TParserTickets::TRes res = TParserTickets::ParseV3(ticketBody, Keys_, TParserTickets::UserFlag());
+ ETicketStatus status = CheckProtobufUserTicket(res.Ticket);
+
+ if (res.Status != ETicketStatus::Ok && !(res.Status == ETicketStatus::MissingKey && status == ETicketStatus::InvalidBlackboxEnv)) {
+ status = res.Status;
+ }
+ return MakeHolder<TCheckedUserTicket::TImpl>(status, std::move(res.Ticket));
+ }
+
+ ETicketStatus TUserContext::TImpl::CheckProtobufUserTicket(const ticket2::Ticket& ticket) const {
+ if (!ticket.has_user()) {
+ return ETicketStatus::Malformed;
+ }
+ if (!IsAllowed(ticket.user().env())) {
+ return ETicketStatus::InvalidBlackboxEnv;
+ }
+ return ETicketStatus::Ok;
+ }
+
+ const NRw::TPublicKeys& TUserContext::TImpl::GetKeys() const {
+ return Keys_;
+ }
+
+ bool TUserContext::TImpl::IsAllowed(tvm_keys::BbEnvType env) const {
+ if (env == tvm_keys::Prod && (Env_ == EBlackboxEnv::Prod || Env_ == EBlackboxEnv::Stress)) {
+ return true;
+ }
+ if (env == tvm_keys::ProdYateam && Env_ == EBlackboxEnv::ProdYateam) {
+ return true;
+ }
+ if (env == tvm_keys::Test && Env_ == EBlackboxEnv::Test) {
+ return true;
+ }
+ if (env == tvm_keys::TestYateam && Env_ == EBlackboxEnv::TestYateam) {
+ return true;
+ }
+ if (env == tvm_keys::Stress && Env_ == EBlackboxEnv::Stress) {
+ return true;
+ }
+
+ return false;
+ }
+}