Line data Source code
1 : /**
2 : * Copyright Soramitsu Co., Ltd. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #include "ametsuchi/impl/postgres_wsv_query.hpp"
7 :
8 : #include <soci/boost-tuple.h>
9 : #include "ametsuchi/impl/soci_utils.hpp"
10 : #include "backend/protobuf/permissions.hpp"
11 : #include "common/result.hpp"
12 : #include "cryptography/public_key.hpp"
13 :
14 : namespace iroha {
15 : namespace ametsuchi {
16 :
17 : using shared_model::interface::types::AccountDetailKeyType;
18 : using shared_model::interface::types::AccountIdType;
19 : using shared_model::interface::types::AddressType;
20 : using shared_model::interface::types::AssetIdType;
21 : using shared_model::interface::types::DetailType;
22 : using shared_model::interface::types::DomainIdType;
23 : using shared_model::interface::types::JsonType;
24 : using shared_model::interface::types::PrecisionType;
25 : using shared_model::interface::types::PubkeyType;
26 : using shared_model::interface::types::QuorumType;
27 : using shared_model::interface::types::RoleIdType;
28 :
29 : PostgresWsvQuery::PostgresWsvQuery(
30 : soci::session &sql,
31 : std::shared_ptr<shared_model::interface::CommonObjectsFactory> factory)
32 638 : : sql_(sql), factory_(factory), log_(logger::log("PostgresWsvQuery")) {}
33 :
34 : PostgresWsvQuery::PostgresWsvQuery(
35 : std::unique_ptr<soci::session> sql,
36 : std::shared_ptr<shared_model::interface::CommonObjectsFactory> factory)
37 2065 : : psql_(std::move(sql)),
38 2065 : sql_(*psql_),
39 2065 : factory_(factory),
40 2065 : log_(logger::log("PostgresWsvQuery")) {}
41 :
42 : template <typename T>
43 : boost::optional<std::shared_ptr<T>> PostgresWsvQuery::fromResult(
44 : shared_model::interface::CommonObjectsFactory::FactoryResult<
45 : std::unique_ptr<T>> &&result) {
46 3908 : return result.match(
47 : [](iroha::expected::Value<std::unique_ptr<T>> &v) {
48 3908 : return boost::make_optional(std::shared_ptr<T>(std::move(v.value)));
49 0 : },
50 : [&](iroha::expected::Error<std::string> &e)
51 : -> boost::optional<std::shared_ptr<T>> {
52 0 : log_->error(e.error);
53 0 : return boost::none;
54 : });
55 : }
56 :
57 : template <typename T, typename F>
58 : auto PostgresWsvQuery::execute(F &&f) -> boost::optional<soci::rowset<T>> {
59 : try {
60 1928 : return soci::rowset<T>{std::forward<F>(f)()};
61 0 : } catch (const std::exception &e) {
62 0 : log_->error("Failed to execute query: {}", e.what());
63 0 : return boost::none;
64 0 : }
65 1928 : }
66 :
67 : bool PostgresWsvQuery::hasAccountGrantablePermission(
68 : const AccountIdType &permitee_account_id,
69 : const AccountIdType &account_id,
70 : shared_model::interface::permissions::Grantable permission) {
71 : const auto perm_str =
72 11 : shared_model::interface::GrantablePermissionSet({permission})
73 11 : .toBitstring();
74 : using T = boost::tuple<int>;
75 : auto result = execute<T>([&] {
76 11 : return (sql_.prepare
77 11 : << "SELECT count(*) FROM "
78 : "account_has_grantable_permissions WHERE "
79 : "permittee_account_id = :permittee_account_id AND "
80 : "account_id = "
81 : ":account_id "
82 : " AND permission & :permission = :permission ",
83 11 : soci::use(permitee_account_id, "permittee_account_id"),
84 11 : soci::use(account_id, "account_id"),
85 11 : soci::use(perm_str, "permission"));
86 0 : });
87 :
88 11 : return flatMapValue(
89 : result,
90 : [](auto &count) { return boost::make_optional(count == 1); })
91 11 : .value_or(false);
92 : }
93 :
94 : boost::optional<std::vector<RoleIdType>> PostgresWsvQuery::getAccountRoles(
95 : const AccountIdType &account_id) {
96 : using T = boost::tuple<RoleIdType>;
97 : auto result = execute<T>([&] {
98 12 : return (sql_.prepare << "SELECT role_id FROM account_has_roles WHERE "
99 12 : "account_id = :account_id",
100 12 : soci::use(account_id));
101 0 : });
102 :
103 12 : return mapValues<std::vector<RoleIdType>>(
104 : result, [&](auto &role_id) { return role_id; });
105 12 : }
106 :
107 : boost::optional<shared_model::interface::RolePermissionSet>
108 : PostgresWsvQuery::getRolePermissions(const RoleIdType &role_name) {
109 : using T = boost::tuple<std::string>;
110 : auto result = execute<T>([&] {
111 4 : return (sql_.prepare
112 : << "SELECT permission FROM role_has_permissions WHERE "
113 4 : "role_id = :role_name",
114 4 : soci::use(role_name));
115 0 : });
116 :
117 : return result | [&](auto &&st)
118 : -> boost::optional<
119 : shared_model::interface::RolePermissionSet> {
120 4 : auto range = boost::make_iterator_range(st);
121 :
122 4 : if (range.empty()) {
123 2 : return shared_model::interface::RolePermissionSet{};
124 : }
125 :
126 : return apply(range.front(), [](auto &permission) {
127 2 : return shared_model::interface::RolePermissionSet(permission);
128 : });
129 4 : };
130 4 : }
131 :
132 : boost::optional<std::vector<RoleIdType>> PostgresWsvQuery::getRoles() {
133 : using T = boost::tuple<RoleIdType>;
134 2 : auto result = execute<T>(
135 : [&] { return (sql_.prepare << "SELECT role_id FROM role"); });
136 :
137 2 : return mapValues<std::vector<RoleIdType>>(
138 : result, [&](auto &role_id) { return role_id; });
139 2 : }
140 :
141 : boost::optional<std::shared_ptr<shared_model::interface::Account>>
142 : PostgresWsvQuery::getAccount(const AccountIdType &account_id) {
143 : using T = boost::tuple<DomainIdType, QuorumType, JsonType>;
144 : auto result = execute<T>([&] {
145 19 : return (sql_.prepare << "SELECT domain_id, quorum, data "
146 : "FROM account WHERE account_id = "
147 19 : ":account_id",
148 19 : soci::use(account_id, "account_id"));
149 0 : });
150 :
151 19 : return flatMapValue(
152 : result, [&](auto &domain_id, auto quorum, auto &data) {
153 16 : return this->fromResult(
154 19 : factory_->createAccount(account_id, domain_id, quorum, data));
155 0 : });
156 19 : }
157 :
158 : boost::optional<std::string> PostgresWsvQuery::getAccountDetail(
159 : const std::string &account_id,
160 : const AccountDetailKeyType &key,
161 : const AccountIdType &writer) {
162 : using T = boost::tuple<DetailType>;
163 11 : boost::optional<soci::rowset<T>> result;
164 :
165 11 : if (key.empty() and writer.empty()) {
166 : // retrieve all values for a specified account
167 8 : std::string empty_json = "{}";
168 : result = execute<T>([&] {
169 8 : return (sql_.prepare
170 8 : << "SELECT data#>>:empty_json FROM account WHERE "
171 : "account_id = "
172 8 : ":account_id;",
173 8 : soci::use(empty_json),
174 8 : soci::use(account_id));
175 0 : });
176 8 : } else if (not key.empty() and not writer.empty()) {
177 : // retrieve values for the account, under the key and added by the
178 : // writer
179 1 : std::string filled_json = "{\"" + writer + "\"" + ", \"" + key + "\"}";
180 : result = execute<T>([&] {
181 1 : return (sql_.prepare
182 1 : << "SELECT json_build_object(:writer::text, "
183 : "json_build_object(:key::text, (SELECT data #>> "
184 : ":filled_json "
185 1 : "FROM account WHERE account_id = :account_id)));",
186 1 : soci::use(writer),
187 1 : soci::use(key),
188 1 : soci::use(filled_json),
189 1 : soci::use(account_id));
190 0 : });
191 2 : } else if (not writer.empty()) {
192 : // retrieve values added by the writer under all keys
193 : result = execute<T>([&] {
194 1 : return (
195 1 : sql_.prepare
196 1 : << "SELECT json_build_object(:writer::text, (SELECT data -> "
197 1 : ":writer FROM account WHERE account_id = :account_id));",
198 1 : soci::use(writer, "writer"),
199 1 : soci::use(account_id, "account_id"));
200 0 : });
201 1 : } else {
202 : // retrieve values from all writers under the key
203 : result = execute<T>([&] {
204 1 : return (
205 1 : sql_.prepare
206 1 : << "SELECT json_object_agg(key, value) AS json FROM (SELECT "
207 : "json_build_object(kv.key, json_build_object(:key::text, "
208 : "kv.value -> :key)) FROM jsonb_each((SELECT data FROM "
209 : "account "
210 : "WHERE account_id = :account_id)) kv WHERE kv.value ? "
211 : ":key) "
212 : "AS "
213 1 : "jsons, json_each(json_build_object);",
214 1 : soci::use(key, "key"),
215 1 : soci::use(account_id, "account_id"));
216 0 : });
217 : }
218 :
219 11 : return flatMapValue(
220 : result, [&](auto &json) { return boost::make_optional(json); });
221 11 : }
222 :
223 : boost::optional<std::vector<PubkeyType>> PostgresWsvQuery::getSignatories(
224 : const AccountIdType &account_id) {
225 : using T = boost::tuple<std::string>;
226 : auto result = execute<T>([&] {
227 146 : return (sql_.prepare
228 146 : << "SELECT public_key FROM account_has_signatory WHERE "
229 146 : "account_id = :account_id",
230 146 : soci::use(account_id));
231 0 : });
232 :
233 : return mapValues<std::vector<PubkeyType>>(result, [&](auto &public_key) {
234 182 : return shared_model::crypto::PublicKey{
235 182 : shared_model::crypto::Blob::fromHexString(public_key)};
236 0 : });
237 146 : }
238 :
239 : boost::optional<std::shared_ptr<shared_model::interface::Asset>>
240 : PostgresWsvQuery::getAsset(const AssetIdType &asset_id) {
241 : using T = boost::tuple<DomainIdType, PrecisionType>;
242 : auto result = execute<T>([&] {
243 3 : return (
244 3 : sql_.prepare
245 3 : << "SELECT domain_id, precision FROM asset WHERE asset_id = "
246 3 : ":asset_id",
247 3 : soci::use(asset_id));
248 0 : });
249 :
250 : return flatMapValue(result, [&](auto &domain_id, auto precision) {
251 1 : return this->fromResult(
252 3 : factory_->createAsset(asset_id, domain_id, precision));
253 0 : });
254 3 : }
255 :
256 : boost::optional<
257 : std::vector<std::shared_ptr<shared_model::interface::AccountAsset>>>
258 : PostgresWsvQuery::getAccountAssets(const AccountIdType &account_id) {
259 : using T = boost::tuple<AssetIdType, std::string>;
260 : auto result = execute<T>([&] {
261 0 : return (sql_.prepare
262 0 : << "SELECT asset_id, amount FROM account_has_asset "
263 0 : "WHERE account_id = :account_id",
264 0 : soci::use(account_id));
265 0 : });
266 :
267 0 : return flatMapValues<
268 : std::vector<std::shared_ptr<shared_model::interface::AccountAsset>>>(
269 : result, [&](auto &asset_id, auto &amount) {
270 0 : return this->fromResult(factory_->createAccountAsset(
271 0 : account_id, asset_id, shared_model::interface::Amount(amount)));
272 0 : });
273 0 : }
274 :
275 : boost::optional<std::shared_ptr<shared_model::interface::AccountAsset>>
276 : PostgresWsvQuery::getAccountAsset(const AccountIdType &account_id,
277 : const AssetIdType &asset_id) {
278 : using T = boost::tuple<std::string>;
279 : auto result = execute<T>([&] {
280 29 : return (
281 29 : sql_.prepare
282 29 : << "SELECT amount FROM account_has_asset WHERE account_id = "
283 29 : ":account_id AND asset_id = :asset_id",
284 29 : soci::use(account_id),
285 29 : soci::use(asset_id));
286 0 : });
287 :
288 : return flatMapValue(result, [&](auto &amount) {
289 29 : return this->fromResult(factory_->createAccountAsset(
290 29 : account_id, asset_id, shared_model::interface::Amount(amount)));
291 0 : });
292 29 : }
293 :
294 : boost::optional<std::shared_ptr<shared_model::interface::Domain>>
295 : PostgresWsvQuery::getDomain(const DomainIdType &domain_id) {
296 : using T = boost::tuple<RoleIdType>;
297 : auto result = execute<T>([&] {
298 6 : return (sql_.prepare << "SELECT default_role FROM domain "
299 6 : "WHERE domain_id = :id LIMIT 1",
300 6 : soci::use(domain_id));
301 0 : });
302 :
303 : return flatMapValue(result, [&](auto &default_role) {
304 3 : return this->fromResult(
305 6 : factory_->createDomain(domain_id, default_role));
306 0 : });
307 6 : }
308 :
309 : boost::optional<std::vector<std::shared_ptr<shared_model::interface::Peer>>>
310 : PostgresWsvQuery::getPeers() {
311 : using T = boost::tuple<std::string, AddressType>;
312 : auto result = execute<T>([&] {
313 1928 : return (sql_.prepare << "SELECT public_key, address FROM peer");
314 : });
315 :
316 1928 : return flatMapValues<
317 : std::vector<std::shared_ptr<shared_model::interface::Peer>>>(
318 : result, [&](auto &public_key, auto &address) {
319 3908 : return this->fromResult(factory_->createPeer(
320 3908 : address,
321 3908 : shared_model::crypto::PublicKey{
322 3908 : shared_model::crypto::Blob::fromHexString(public_key)}));
323 0 : });
324 1928 : }
325 : } // namespace ametsuchi
326 : } // namespace iroha
|