aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/tvmauth/client/misc/roles/parser.cpp
blob: 28faf4c05705312fbdaec94ba6de221776a6b9c8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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;
    }
}