diff options
author | qrort <qrort@yandex-team.com> | 2022-11-30 23:47:12 +0300 |
---|---|---|
committer | qrort <qrort@yandex-team.com> | 2022-11-30 23:47:12 +0300 |
commit | 22f8ae0e3f5d68b92aecccdf96c1d841a0334311 (patch) | |
tree | bffa27765faf54126ad44bcafa89fadecb7a73d7 /library/cpp/tvmauth/client/misc/roles | |
parent | 332b99e2173f0425444abb759eebcb2fafaa9209 (diff) | |
download | ydb-22f8ae0e3f5d68b92aecccdf96c1d841a0334311.tar.gz |
validate canons without yatest_common
Diffstat (limited to 'library/cpp/tvmauth/client/misc/roles')
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/decoder.cpp | 93 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/decoder.h | 32 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/entities_index.cpp | 114 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/entities_index.h | 107 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/parser.cpp | 149 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/parser.h | 36 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/roles.cpp | 101 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/roles.h | 186 | ||||
-rw-r--r-- | library/cpp/tvmauth/client/misc/roles/types.h | 70 |
9 files changed, 888 insertions, 0 deletions
diff --git a/library/cpp/tvmauth/client/misc/roles/decoder.cpp b/library/cpp/tvmauth/client/misc/roles/decoder.cpp new file mode 100644 index 0000000000..6337fb91c2 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/decoder.cpp @@ -0,0 +1,93 @@ +#include "decoder.h" + +#include <library/cpp/tvmauth/client/misc/utils.h> + +#include <library/cpp/openssl/crypto/sha.h> +#include <library/cpp/streams/brotli/brotli.h> +#include <library/cpp/streams/zstd/zstd.h> + +#include <util/generic/yexception.h> +#include <util/stream/zlib.h> +#include <util/string/ascii.h> + +namespace NTvmAuth::NRoles { + TString TDecoder::Decode(const TStringBuf codec, TString&& blob) { + if (codec.empty()) { + return std::move(blob); + } + + const TCodecInfo info = ParseCodec(codec); + TString decoded = DecodeImpl(info.Type, blob); + + VerifySize(decoded, info.Size); + VerifyChecksum(decoded, info.Sha256); + + return decoded; + } + + TDecoder::TCodecInfo TDecoder::ParseCodec(TStringBuf codec) { + const char delim = ':'; + + const TStringBuf version = codec.NextTok(delim); + Y_ENSURE(version == "1", + "unknown codec format version; known: 1; got: " << version); + + TCodecInfo res; + res.Type = codec.NextTok(delim); + Y_ENSURE(res.Type, "codec type is empty"); + + const TStringBuf size = codec.NextTok(delim); + Y_ENSURE(TryIntFromString<10>(size, res.Size), + "decoded blob size is not number"); + + res.Sha256 = codec; + const size_t expectedSha256Size = 2 * NOpenSsl::NSha256::DIGEST_LENGTH; + Y_ENSURE(res.Sha256.size() == expectedSha256Size, + "sha256 of decoded blob has invalid length: expected " + << expectedSha256Size << ", got " << res.Sha256.size()); + + return res; + } + + TString TDecoder::DecodeImpl(TStringBuf codec, const TString& blob) { + if (AsciiEqualsIgnoreCase(codec, "brotli")) { + return DecodeBrolti(blob); + } else if (AsciiEqualsIgnoreCase(codec, "gzip")) { + return DecodeGzip(blob); + } else if (AsciiEqualsIgnoreCase(codec, "zstd")) { + return DecodeZstd(blob); + } + + ythrow yexception() << "unknown codec: '" << codec << "'"; + } + + TString TDecoder::DecodeBrolti(const TString& blob) { + TStringInput in(blob); + return TBrotliDecompress(&in).ReadAll(); + } + + TString TDecoder::DecodeGzip(const TString& blob) { + TStringInput in(blob); + return TZLibDecompress(&in).ReadAll(); + } + + TString TDecoder::DecodeZstd(const TString& blob) { + TStringInput in(blob); + return TZstdDecompress(&in).ReadAll(); + } + + void TDecoder::VerifySize(const TStringBuf decoded, size_t expected) { + Y_ENSURE(expected == decoded.size(), + "Decoded blob has bad size: expected " << expected << ", actual " << decoded.size()); + } + + void TDecoder::VerifyChecksum(const TStringBuf decoded, const TStringBuf expected) { + using namespace NOpenSsl::NSha256; + + const TDigest dig = Calc(decoded); + const TString actual = NUtils::ToHex(TStringBuf((char*)dig.data(), dig.size())); + + Y_ENSURE(AsciiEqualsIgnoreCase(actual, expected), + "Decoded blob has bad sha256: expected=" << expected << ", actual=" << actual); + } +} diff --git a/library/cpp/tvmauth/client/misc/roles/decoder.h b/library/cpp/tvmauth/client/misc/roles/decoder.h new file mode 100644 index 0000000000..de5cdb37e0 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/decoder.h @@ -0,0 +1,32 @@ +#pragma once + +#include <util/generic/string.h> + +namespace NTvmAuth::NRoles { + class TDecoder { + public: + static TString Decode(const TStringBuf codec, TString&& blob); + + public: + struct TCodecInfo { + TStringBuf Type; + size_t Size = 0; + TStringBuf Sha256; + + bool operator==(const TCodecInfo& o) const { + return Type == o.Type && + Size == o.Size && + Sha256 == o.Sha256; + } + }; + + static TCodecInfo ParseCodec(TStringBuf codec); + static TString DecodeImpl(TStringBuf codec, const TString& blob); + static TString DecodeBrolti(const TString& blob); + static TString DecodeGzip(const TString& blob); + static TString DecodeZstd(const TString& blob); + + static void VerifySize(const TStringBuf decoded, size_t expected); + static void VerifyChecksum(const TStringBuf decoded, const TStringBuf expected); + }; +} diff --git a/library/cpp/tvmauth/client/misc/roles/entities_index.cpp b/library/cpp/tvmauth/client/misc/roles/entities_index.cpp new file mode 100644 index 0000000000..c9b72c3a17 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/entities_index.cpp @@ -0,0 +1,114 @@ +#include "entities_index.h" + +#include <util/stream/str.h> + +#include <set> + +namespace NTvmAuth::NRoles { + TEntitiesIndex::TStage::TStage(const std::set<TString>& k) + : Keys_(k.begin(), k.end()) + { + } + + // TODO TStringBuf + bool TEntitiesIndex::TStage::GetNextKeySet(std::vector<TString>& out) { + out.clear(); + out.reserve(Keys_.size()); + + ++Id_; + for (size_t idx = 0; idx < Keys_.size(); ++idx) { + bool need = (Id_ >> idx) & 0x01; + + if (need) { + out.push_back(Keys_[idx]); + } + } + + return !out.empty(); + } + + TEntitiesIndex::TEntitiesIndex(const std::vector<TEntityPtr>& entities) { + const std::set<TString> uniqueKeys = GetUniqueSortedKeys(entities); + Idx_.Entities = entities; + Idx_.SubTree.reserve(uniqueKeys.size() * entities.size()); + + TStage stage(uniqueKeys); + std::vector<TString> keyset; + while (stage.GetNextKeySet(keyset)) { + for (const TEntityPtr& e : entities) { + TSubTree* currentBranch = &Idx_; + + for (const TString& key : keyset) { + auto it = e->find(key); + if (it == e->end()) { + continue; + } + + auto [i, ok] = currentBranch->SubTree.emplace( + TKeyValue{it->first, it->second}, + TSubTree()); + + currentBranch = &i->second; + currentBranch->Entities.push_back(e); + } + } + } + + MakeUnique(Idx_); + } + + std::set<TString> TEntitiesIndex::GetUniqueSortedKeys(const std::vector<TEntityPtr>& entities) { + std::set<TString> res; + + for (const TEntityPtr& e : entities) { + for (const auto& [key, value] : *e) { + res.insert(key); + } + } + + return res; + } + + void TEntitiesIndex::MakeUnique(TSubTree& branch) { + auto& vec = branch.Entities; + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + for (auto& [_, restPart] : branch.SubTree) { + MakeUnique(restPart); + } + } + + static void Print(const TEntitiesIndex::TSubTree& part, IOutputStream& out, size_t offset = 0) { + std::vector<std::pair<TKeyValue, const TEntitiesIndex::TSubTree*>> vec; + vec.reserve(part.SubTree.size()); + + for (const auto& [key, value] : part.SubTree) { + vec.push_back({key, &value}); + } + + std::sort(vec.begin(), vec.end(), [](const auto& l, const auto& r) { + if (l.first.Key < r.first.Key) { + return true; + } + if (l.first.Value < r.first.Value) { + return true; + } + return false; + }); + + for (const auto& [key, value] : vec) { + out << TString(offset, ' ') << "\"" << key.Key << "/" << key.Value << "\"" << Endl; + Print(*value, out, offset + 4); + } + } + + TString TEntitiesIndex::PrintDebugString() const { + TStringStream res; + res << Endl; + + Print(Idx_, res); + + return res.Str(); + } +} diff --git a/library/cpp/tvmauth/client/misc/roles/entities_index.h b/library/cpp/tvmauth/client/misc/roles/entities_index.h new file mode 100644 index 0000000000..bf42750d52 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/entities_index.h @@ -0,0 +1,107 @@ +#pragma once + +#include "types.h" + +#include <library/cpp/tvmauth/client/exception.h> + +#include <set> +#include <vector> + +namespace NTvmAuth::NRoles { + class TEntitiesIndex: TMoveOnly { + public: + struct TSubTree; + using TIdxByAttrs = THashMap<TKeyValue, TSubTree>; + + struct TSubTree { + std::vector<TEntityPtr> Entities; + TIdxByAttrs SubTree; + }; + + class TStage { + public: + TStage(const std::set<TString>& k); + + bool GetNextKeySet(std::vector<TString>& out); + + private: + std::vector<TString> Keys_; + size_t Id_ = 0; + }; + + public: + TEntitiesIndex(const std::vector<TEntityPtr>& entities); + + /** + * Iterators must be to sorted unique key/value + */ + template <typename Iterator> + bool ContainsExactEntity(Iterator begin, Iterator end) const; + + /** + * Iterators must be to sorted unique key/value + */ + template <typename Iterator> + const std::vector<TEntityPtr>& GetEntitiesWithAttrs(Iterator begin, Iterator end) const; + + public: // for tests + static std::set<TString> GetUniqueSortedKeys(const std::vector<TEntityPtr>& entities); + static void MakeUnique(TEntitiesIndex::TSubTree& branch); + + TString PrintDebugString() const; + + private: + template <typename Iterator> + const TSubTree* FindSubtree(Iterator begin, Iterator end, size_t& size) const; + + private: + TSubTree Idx_; + std::vector<TEntityPtr> EmptyResult_; + }; + + template <typename Iterator> + bool TEntitiesIndex::ContainsExactEntity(Iterator begin, Iterator end) const { + size_t size = 0; + const TSubTree* subtree = FindSubtree(begin, end, size); + if (!subtree) { + return false; + } + + auto res = std::find_if( + subtree->Entities.begin(), + subtree->Entities.end(), + [size](const auto& e) { return size == e->size(); }); + return res != subtree->Entities.end(); + } + + template <typename Iterator> + const std::vector<TEntityPtr>& TEntitiesIndex::GetEntitiesWithAttrs(Iterator begin, Iterator end) const { + size_t size = 0; + const TSubTree* subtree = FindSubtree(begin, end, size); + if (!subtree) { + return EmptyResult_; + } + + return subtree->Entities; + } + + template <typename Iterator> + const TEntitiesIndex::TSubTree* TEntitiesIndex::FindSubtree(Iterator begin, + Iterator end, + size_t& size) const { + const TSubTree* subtree = &Idx_; + size = 0; + + for (auto attr = begin; attr != end; ++attr) { + auto it = subtree->SubTree.find(TKeyValueView{attr->first, attr->second}); + if (it == subtree->SubTree.end()) { + return nullptr; + } + + ++size; + subtree = &it->second; + } + + return subtree; + } +} diff --git a/library/cpp/tvmauth/client/misc/roles/parser.cpp b/library/cpp/tvmauth/client/misc/roles/parser.cpp new file mode 100644 index 0000000000..28faf4c057 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/parser.cpp @@ -0,0 +1,149 @@ +#include "parser.h" + +#include <library/cpp/json/json_reader.h> + +#include <util/string/cast.h> + +namespace NTvmAuth::NRoles { + static void GetRequiredValue(const NJson::TJsonValue& doc, + TStringBuf key, + NJson::TJsonValue& obj) { + Y_ENSURE(doc.GetValue(key, &obj), "Missing '" << key << "'"); + } + + static ui64 GetRequiredUInt(const NJson::TJsonValue& doc, + TStringBuf key) { + NJson::TJsonValue obj; + GetRequiredValue(doc, key, obj); + Y_ENSURE(obj.IsUInteger(), "key '" << key << "' must be uint"); + return obj.GetUInteger(); + } + + static bool GetOptionalMap(const NJson::TJsonValue& doc, + TStringBuf key, + NJson::TJsonValue& obj) { + if (!doc.GetValue(key, &obj)) { + return false; + } + + Y_ENSURE(obj.IsMap(), "'" << key << "' must be object"); + return true; + } + + TRolesPtr TParser::Parse(TRawPtr decodedBlob) { + try { + return ParseImpl(decodedBlob); + } catch (const std::exception& e) { + throw yexception() << "Failed to parse roles from tirole: " << e.what() + << ". '" << *decodedBlob << "'"; + } + } + + TRolesPtr TParser::ParseImpl(TRawPtr decodedBlob) { + NJson::TJsonValue doc; + Y_ENSURE(NJson::ReadJsonTree(*decodedBlob, &doc), "Invalid json"); + Y_ENSURE(doc.IsMap(), "Json must be object"); + + TRoles::TTvmConsumers tvm = GetConsumers<TTvmId>(doc, "tvm"); + TRoles::TUserConsumers user = GetConsumers<TUid>(doc, "user"); + + // fetch it last to provide more correct apply instant + TRoles::TMeta meta = GetMeta(doc); + + return std::make_shared<TRoles>( + std::move(meta), + std::move(tvm), + std::move(user), + std::move(decodedBlob)); + } + + TRoles::TMeta TParser::GetMeta(const NJson::TJsonValue& doc) { + TRoles::TMeta res; + + NJson::TJsonValue obj; + GetRequiredValue(doc, "revision", obj); + if (obj.IsString()) { + res.Revision = obj.GetString(); + } else if (obj.IsUInteger()) { + res.Revision = ToString(obj.GetUInteger()); + } else { + ythrow yexception() << "'revision' has unexpected type: " << obj.GetType(); + } + + res.BornTime = TInstant::Seconds(GetRequiredUInt(doc, "born_date")); + + return res; + } + + template <typename Id> + THashMap<Id, TConsumerRolesPtr> TParser::GetConsumers(const NJson::TJsonValue& doc, + TStringBuf type) { + THashMap<Id, TConsumerRolesPtr> res; + + NJson::TJsonValue obj; + if (!GetOptionalMap(doc, type, obj)) { + return res; + } + + for (const auto& [key, value] : obj.GetMap()) { + Y_ENSURE(value.IsMap(), + "roles for consumer must be map: '" << key << "' is " << value.GetType()); + + Id id = 0; + Y_ENSURE(TryIntFromString<10>(key, id), + "id must be valid positive number of proper size for " + << type << ". got '" + << key << "'"); + + Y_ENSURE(res.emplace(id, GetConsumer(value, key)).second, + "consumer duplicate detected: '" << key << "' for " << type); + } + + return res; + } + + TConsumerRolesPtr TParser::GetConsumer(const NJson::TJsonValue& obj, TStringBuf consumer) { + TEntitiesByRoles entities; + + for (const auto& [key, value] : obj.GetMap()) { + Y_ENSURE(value.IsArray(), + "entities for roles must be array: '" << key << "' is " << value.GetType()); + + entities.emplace(key, GetEntities(value, consumer, key)); + } + + return std::make_shared<TConsumerRoles>(std::move(entities)); + } + + TEntitiesPtr TParser::GetEntities(const NJson::TJsonValue& obj, + TStringBuf consumer, + TStringBuf role) { + std::vector<TEntityPtr> entities; + entities.reserve(obj.GetArray().size()); + + for (const NJson::TJsonValue& e : obj.GetArray()) { + Y_ENSURE(e.IsMap(), + "role entity for role must be map: consumer '" + << consumer << "' with role '" << role << "' has " << e.GetType()); + + entities.push_back(GetEntity(e, consumer, role)); + } + + return std::make_shared<TEntities>(TEntities(entities)); + } + + TEntityPtr TParser::GetEntity(const NJson::TJsonValue& obj, TStringBuf consumer, TStringBuf role) { + TEntityPtr res = std::make_shared<TEntity>(); + + for (const auto& [key, value] : obj.GetMap()) { + Y_ENSURE(value.IsString(), + "entity is map (str->str), got value " + << value.GetType() << ". consumer '" + << consumer << "' with role '" << role << "'"); + + res->emplace(key, value.GetString()); + } + + return res; + } +} diff --git a/library/cpp/tvmauth/client/misc/roles/parser.h b/library/cpp/tvmauth/client/misc/roles/parser.h new file mode 100644 index 0000000000..0982ba78c6 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/parser.h @@ -0,0 +1,36 @@ +#pragma once + +#include "roles.h" +#include "types.h" + +namespace NJson { + class TJsonValue; +} + +namespace NTvmAuth::NRoles { + class TParser { + public: + static TRolesPtr Parse(TRawPtr decodedBlob); + + public: + static TRolesPtr ParseImpl(TRawPtr decodedBlob); + static TRoles::TMeta GetMeta(const NJson::TJsonValue& doc); + + template <typename Id> + static THashMap<Id, TConsumerRolesPtr> GetConsumers( + const NJson::TJsonValue& doc, + TStringBuf key); + + static TConsumerRolesPtr GetConsumer( + const NJson::TJsonValue& obj, + TStringBuf consumer); + static TEntitiesPtr GetEntities( + const NJson::TJsonValue& obj, + TStringBuf consumer, + TStringBuf role); + static TEntityPtr GetEntity( + const NJson::TJsonValue& obj, + TStringBuf consumer, + TStringBuf role); + }; +} diff --git a/library/cpp/tvmauth/client/misc/roles/roles.cpp b/library/cpp/tvmauth/client/misc/roles/roles.cpp new file mode 100644 index 0000000000..0761033104 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/roles.cpp @@ -0,0 +1,101 @@ +#include "roles.h" + +#include <library/cpp/tvmauth/checked_service_ticket.h> +#include <library/cpp/tvmauth/checked_user_ticket.h> + +namespace NTvmAuth::NRoles { + TRoles::TRoles(TMeta&& meta, + TTvmConsumers tvm, + TUserConsumers user, + TRawPtr raw) + : Meta_(std::move(meta)) + , TvmIds_(std::move(tvm)) + , Users_(std::move(user)) + , Raw_(std::move(raw)) + { + Y_ENSURE(Raw_); + } + + TConsumerRolesPtr TRoles::GetRolesForService(const TCheckedServiceTicket& t) const { + Y_ENSURE_EX(t, + TIllegalUsage() << "Service ticket must be valid, got: " << t.GetStatus()); + auto it = TvmIds_.find(t.GetSrc()); + return it == TvmIds_.end() ? TConsumerRolesPtr() : it->second; + } + + TConsumerRolesPtr TRoles::GetRolesForUser(const TCheckedUserTicket& t, + std::optional<TUid> selectedUid) const { + Y_ENSURE_EX(t, + TIllegalUsage() << "User ticket must be valid, got: " << t.GetStatus()); + Y_ENSURE_EX(t.GetEnv() == EBlackboxEnv::ProdYateam, + TIllegalUsage() << "User ticket must be from ProdYateam, got from " << t.GetEnv()); + + TUid uid = t.GetDefaultUid(); + if (selectedUid) { + auto it = std::find(t.GetUids().begin(), t.GetUids().end(), *selectedUid); + Y_ENSURE_EX(it != t.GetUids().end(), + TIllegalUsage() << "selectedUid must be in user ticket but it's not: " + << *selectedUid); + uid = *selectedUid; + } + + auto it = Users_.find(uid); + return it == Users_.end() ? TConsumerRolesPtr() : it->second; + } + + const TRoles::TMeta& TRoles::GetMeta() const { + return Meta_; + } + + const TString& TRoles::GetRaw() const { + return *Raw_; + } + + bool TRoles::CheckServiceRole(const TCheckedServiceTicket& t, + const TStringBuf roleName) const { + TConsumerRolesPtr c = GetRolesForService(t); + return c ? c->HasRole(roleName) : false; + } + + bool TRoles::CheckUserRole(const TCheckedUserTicket& t, + const TStringBuf roleName, + std::optional<TUid> selectedUid) const { + TConsumerRolesPtr c = GetRolesForUser(t, selectedUid); + return c ? c->HasRole(roleName) : false; + } + + bool TRoles::CheckServiceRoleForExactEntity(const TCheckedServiceTicket& t, + const TStringBuf roleName, + const TEntity& exactEntity) const { + TConsumerRolesPtr c = GetRolesForService(t); + return c ? c->CheckRoleForExactEntity(roleName, exactEntity) : false; + } + + bool TRoles::CheckUserRoleForExactEntity(const TCheckedUserTicket& t, + const TStringBuf roleName, + const TEntity& exactEntity, + std::optional<TUid> selectedUid) const { + TConsumerRolesPtr c = GetRolesForUser(t, selectedUid); + return c ? c->CheckRoleForExactEntity(roleName, exactEntity) : false; + } + + TConsumerRoles::TConsumerRoles(TEntitiesByRoles roles) + : Roles_(std::move(roles)) + { + } + + bool TConsumerRoles::CheckRoleForExactEntity(const TStringBuf roleName, + const TEntity& exactEntity) const { + auto it = Roles_.find(roleName); + if (it == Roles_.end()) { + return false; + } + + return it->second->Contains(exactEntity); + } + + TEntities::TEntities(TEntitiesIndex idx) + : Idx_(std::move(idx)) + { + } +} diff --git a/library/cpp/tvmauth/client/misc/roles/roles.h b/library/cpp/tvmauth/client/misc/roles/roles.h new file mode 100644 index 0000000000..6d510ee8a1 --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/roles.h @@ -0,0 +1,186 @@ +#pragma once + +#include "entities_index.h" +#include "types.h" + +#include <library/cpp/tvmauth/client/exception.h> + +#include <library/cpp/tvmauth/type.h> + +#include <util/datetime/base.h> +#include <util/generic/array_ref.h> +#include <util/generic/hash.h> + +#include <vector> + +namespace NTvmAuth { + class TCheckedServiceTicket; + class TCheckedUserTicket; +} + +namespace NTvmAuth::NRoles { + class TRoles { + public: + struct TMeta { + TString Revision; + TInstant BornTime; + TInstant Applied = TInstant::Now(); + }; + + using TTvmConsumers = THashMap<TTvmId, TConsumerRolesPtr>; + using TUserConsumers = THashMap<TUid, TConsumerRolesPtr>; + + TRoles(TMeta&& meta, + TTvmConsumers tvm, + TUserConsumers user, + TRawPtr raw); + + /** + * @return ptr to roles. It will be nullptr if there are no roles + */ + TConsumerRolesPtr GetRolesForService(const TCheckedServiceTicket& t) const; + + /** + * @return ptr to roles. It will be nullptr if there are no roles + */ + TConsumerRolesPtr GetRolesForUser(const TCheckedUserTicket& t, + std::optional<TUid> selectedUid = {}) const; + + const TMeta& GetMeta() const; + const TString& GetRaw() const; + + public: // shortcuts + /** + * @brief CheckServiceRole() is shortcut for simple role checking - for any possible entity + */ + bool CheckServiceRole( + const TCheckedServiceTicket& t, + const TStringBuf roleName) const; + + /** + * @brief CheckUserRole() is shortcut for simple role checking - for any possible entity + */ + bool CheckUserRole( + const TCheckedUserTicket& t, + const TStringBuf roleName, + std::optional<TUid> selectedUid = {}) const; + + /** + * @brief CheckServiceRoleForExactEntity() is shortcut for simple role checking for exact entity + */ + bool CheckServiceRoleForExactEntity( + const TCheckedServiceTicket& t, + const TStringBuf roleName, + const TEntity& exactEntity) const; + + /** + * @brief CheckUserRoleForExactEntity() is shortcut for simple role checking for exact entity + */ + bool CheckUserRoleForExactEntity( + const TCheckedUserTicket& t, + const TStringBuf roleName, + const TEntity& exactEntity, + std::optional<TUid> selectedUid = {}) const; + + private: + TMeta Meta_; + TTvmConsumers TvmIds_; + TUserConsumers Users_; + TRawPtr Raw_; + }; + + class TConsumerRoles { + public: + TConsumerRoles(TEntitiesByRoles roles); + + bool HasRole(const TStringBuf roleName) const { + return Roles_.contains(roleName); + } + + const TEntitiesByRoles& GetRoles() const { + return Roles_; + } + + /** + * @return ptr to entries. It will be nullptr if there is no role + */ + TEntitiesPtr GetEntitiesForRole(const TStringBuf roleName) const { + auto it = Roles_.find(roleName); + return it == Roles_.end() ? TEntitiesPtr() : it->second; + } + + /** + * @brief CheckRoleForExactEntity() is shortcut for simple role checking for exact entity + */ + bool CheckRoleForExactEntity(const TStringBuf roleName, + const TEntity& exactEntity) const; + + private: + TEntitiesByRoles Roles_; + }; + + class TEntities { + public: + TEntities(TEntitiesIndex idx); + + /** + * @brief Contains() provides info about entity presence + */ + bool Contains(const TEntity& exactEntity) const { + return Idx_.ContainsExactEntity(exactEntity.begin(), exactEntity.end()); + } + + /** + * @brief The same as Contains() + * It checks span for sorted and unique properties. + */ + template <class StrKey = TString, class StrValue = TString> + bool ContainsSortedUnique( + const TArrayRef<const std::pair<StrKey, StrValue>>& exactEntity) const { + CheckSpan(exactEntity); + return Idx_.ContainsExactEntity(exactEntity.begin(), exactEntity.end()); + } + + /** + * @brief GetEntitiesWithAttrs() collects entities with ALL attributes from `attrs` + */ + template <class StrKey = TString, class StrValue = TString> + const std::vector<TEntityPtr>& GetEntitiesWithAttrs( + const std::map<StrKey, StrValue>& attrs) const { + return Idx_.GetEntitiesWithAttrs(attrs.begin(), attrs.end()); + } + + /** + * @brief The same as GetEntitiesWithAttrs() + * It checks span for sorted and unique properties. + */ + template <class StrKey = TString, class StrValue = TString> + const std::vector<TEntityPtr>& GetEntitiesWithSortedUniqueAttrs( + const TArrayRef<const std::pair<StrKey, StrValue>>& attrs) const { + CheckSpan(attrs); + return Idx_.GetEntitiesWithAttrs(attrs.begin(), attrs.end()); + } + + private: + template <class StrKey, class StrValue> + static void CheckSpan(const TArrayRef<const std::pair<StrKey, StrValue>>& attrs) { + if (attrs.empty()) { + return; + } + + auto prev = attrs.begin(); + for (auto it = prev + 1; it != attrs.end(); ++it) { + Y_ENSURE_EX(prev->first != it->first, + TIllegalUsage() << "attrs are not unique: '" << it->first << "'"); + Y_ENSURE_EX(prev->first < it->first, + TIllegalUsage() << "attrs are not sorted: '" << prev->first + << "' before '" << it->first << "'"); + + prev = it; + } + } + + private: + TEntitiesIndex Idx_; + }; +} diff --git a/library/cpp/tvmauth/client/misc/roles/types.h b/library/cpp/tvmauth/client/misc/roles/types.h new file mode 100644 index 0000000000..de0745e72e --- /dev/null +++ b/library/cpp/tvmauth/client/misc/roles/types.h @@ -0,0 +1,70 @@ +#pragma once + +#include <util/generic/hash_set.h> + +#include <map> +#include <memory> + +namespace NTvmAuth::NRoles { + using TEntity = std::map<TString, TString>; + using TEntityPtr = std::shared_ptr<TEntity>; + + class TEntities; + using TEntitiesPtr = std::shared_ptr<TEntities>; + + using TEntitiesByRoles = THashMap<TString, TEntitiesPtr>; + + class TConsumerRoles; + using TConsumerRolesPtr = std::shared_ptr<TConsumerRoles>; + + class TRoles; + using TRolesPtr = std::shared_ptr<TRoles>; + + using TRawPtr = std::shared_ptr<TString>; + + template <class T> + struct TKeyValueBase { + T Key; + T Value; + + template <typename U> + bool operator==(const TKeyValueBase<U>& o) const { + return Key == o.Key && Value == o.Value; + } + }; + + using TKeyValue = TKeyValueBase<TString>; + using TKeyValueView = TKeyValueBase<TStringBuf>; +} + +// Traits + +template <> +struct THash<NTvmAuth::NRoles::TKeyValue> { + std::size_t operator()(const NTvmAuth::NRoles::TKeyValue& e) const { + return std::hash<std::string_view>()(e.Key) + std::hash<std::string_view>()(e.Value); + } + + std::size_t operator()(const NTvmAuth::NRoles::TKeyValueView& e) const { + return std::hash<std::string_view>()(e.Key) + std::hash<std::string_view>()(e.Value); + } +}; + +template <> +struct TEqualTo<NTvmAuth::NRoles::TKeyValue> { + using is_transparent = std::true_type; + + template <typename T, typename U> + bool operator()(const NTvmAuth::NRoles::TKeyValueBase<T>& l, + const NTvmAuth::NRoles::TKeyValueBase<U>& r) { + return l == r; + } +}; + +inline bool operator<(const NTvmAuth::NRoles::TEntityPtr& l, const NTvmAuth::NRoles::TEntityPtr& r) { + return *l < *r; +} + +inline bool operator==(const NTvmAuth::NRoles::TEntityPtr& l, const NTvmAuth::NRoles::TEntityPtr& r) { + return *l == *r; +} |