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_command.hpp"
7 :
8 : #include <numeric>
9 :
10 : #include <boost/format.hpp>
11 : #include "backend/protobuf/permissions.hpp"
12 : #include "cryptography/public_key.hpp"
13 : #include "interfaces/common_objects/account.hpp"
14 : #include "interfaces/common_objects/account_asset.hpp"
15 : #include "interfaces/common_objects/asset.hpp"
16 : #include "interfaces/common_objects/domain.hpp"
17 : #include "interfaces/common_objects/peer.hpp"
18 :
19 : namespace iroha {
20 : namespace ametsuchi {
21 :
22 : template <typename Function>
23 : WsvCommandResult execute(soci::statement &st, Function &&error) {
24 27 : st.define_and_bind();
25 : try {
26 27 : st.execute(true);
27 25 : return {};
28 2 : } catch (const std::exception &e) {
29 2 : return expected::makeError(error());
30 2 : }
31 27 : }
32 :
33 : PostgresWsvCommand::PostgresWsvCommand(soci::session &sql) : sql_(sql) {}
34 :
35 : WsvCommandResult PostgresWsvCommand::insertRole(
36 : const shared_model::interface::types::RoleIdType &role_name) {
37 27 : soci::statement st = sql_.prepare
38 27 : << "INSERT INTO role(role_id) VALUES (:role_id)";
39 27 : st.exchange(soci::use(role_name));
40 : auto msg = [&] {
41 27 : return (boost::format("failed to insert role: '%s'") % role_name).str();
42 0 : };
43 27 : return execute(st, msg);
44 27 : }
45 :
46 : WsvCommandResult PostgresWsvCommand::insertAccountRole(
47 : const shared_model::interface::types::AccountIdType &account_id,
48 : const shared_model::interface::types::RoleIdType &role_name) {
49 6 : soci::statement st = sql_.prepare
50 6 : << "INSERT INTO account_has_roles(account_id, role_id) VALUES "
51 : "(:account_id, :role_id)";
52 6 : st.exchange(soci::use(account_id));
53 6 : st.exchange(soci::use(role_name));
54 :
55 : auto msg = [&] {
56 2 : return (boost::format("failed to insert account role, account: '%s', "
57 : "role name: '%s'")
58 6 : % account_id % role_name)
59 2 : .str();
60 0 : };
61 6 : return execute(st, msg);
62 6 : }
63 :
64 : WsvCommandResult PostgresWsvCommand::deleteAccountRole(
65 : const shared_model::interface::types::AccountIdType &account_id,
66 : const shared_model::interface::types::RoleIdType &role_name) {
67 3 : soci::statement st = sql_.prepare
68 3 : << "DELETE FROM account_has_roles WHERE account_id=:account_id "
69 : "AND role_id=:role_id";
70 3 : st.exchange(soci::use(account_id));
71 3 : st.exchange(soci::use(role_name));
72 :
73 : auto msg = [&] {
74 0 : return (boost::format(
75 : "failed to delete account role, account id: '%s', "
76 : "role name: '%s'")
77 3 : % account_id % role_name)
78 0 : .str();
79 0 : };
80 3 : return execute(st, msg);
81 3 : }
82 :
83 : WsvCommandResult PostgresWsvCommand::insertRolePermissions(
84 : const shared_model::interface::types::RoleIdType &role_id,
85 : const shared_model::interface::RolePermissionSet &permissions) {
86 2 : auto perm_str = permissions.toBitstring();
87 2 : soci::statement st = sql_.prepare
88 2 : << "INSERT INTO role_has_permissions(role_id, permission) VALUES "
89 : "(:id, :perm)";
90 2 : st.exchange(soci::use(role_id));
91 2 : st.exchange(soci::use(perm_str));
92 :
93 : auto msg = [&] {
94 1 : const auto &str =
95 2 : shared_model::proto::permissions::toString(permissions);
96 1 : std::string perm_debug_str = std::accumulate(
97 1 : str.begin(),
98 1 : str.end(),
99 1 : std::string(),
100 : [](auto acc, const auto &elem) { return acc + " " + elem; });
101 1 : return (boost::format("failed to insert role permissions, role "
102 : "id: '%s', permissions: [%s]")
103 2 : % role_id % perm_debug_str)
104 1 : .str();
105 1 : };
106 2 : return execute(st, msg);
107 2 : }
108 :
109 : WsvCommandResult PostgresWsvCommand::insertAccountGrantablePermission(
110 : const shared_model::interface::types::AccountIdType
111 : &permittee_account_id,
112 : const shared_model::interface::types::AccountIdType &account_id,
113 : shared_model::interface::permissions::Grantable permission) {
114 : const auto perm_str =
115 3 : shared_model::interface::GrantablePermissionSet({permission})
116 3 : .toBitstring();
117 3 : soci::statement st = sql_.prepare
118 3 : << "INSERT INTO account_has_grantable_permissions as "
119 : "has_perm(permittee_account_id, account_id, permission) VALUES "
120 : "(:permittee_account_id, :account_id, :perm) ON CONFLICT "
121 : "(permittee_account_id, account_id) DO UPDATE SET "
122 : // SELECT will end up with a error, if the permission exists
123 : "permission=(SELECT has_perm.permission | :perm WHERE "
124 : "(has_perm.permission & :perm) <> :perm);";
125 3 : st.exchange(soci::use(permittee_account_id, "permittee_account_id"));
126 3 : st.exchange(soci::use(account_id, "account_id"));
127 3 : st.exchange(soci::use(perm_str, "perm"));
128 :
129 : auto msg = [&] {
130 2 : return (boost::format("failed to insert account grantable permission, "
131 : "permittee account id: '%s', "
132 : "account id: '%s', "
133 : "permission: '%s'")
134 3 : % permittee_account_id
135 3 : % account_id
136 : // TODO(@l4l) 26/06/18 need to be simplified at IR-1479
137 2 : % shared_model::proto::permissions::toString(permission))
138 2 : .str();
139 0 : };
140 3 : return execute(st, msg);
141 3 : }
142 :
143 : WsvCommandResult PostgresWsvCommand::deleteAccountGrantablePermission(
144 : const shared_model::interface::types::AccountIdType
145 : &permittee_account_id,
146 : const shared_model::interface::types::AccountIdType &account_id,
147 : shared_model::interface::permissions::Grantable permission) {
148 1 : const auto perm_str = shared_model::interface::GrantablePermissionSet()
149 1 : .set()
150 1 : .unset(permission)
151 1 : .toBitstring();
152 1 : soci::statement st = sql_.prepare
153 1 : << "UPDATE account_has_grantable_permissions as has_perm SET "
154 : // SELECT will end up with a error, if the permission doesn't
155 : // exists
156 : "permission=(SELECT has_perm.permission & :perm WHERE "
157 : "has_perm.permission & :perm = :perm) WHERE "
158 : "permittee_account_id=:permittee_account_id AND "
159 : "account_id=:account_id;";
160 :
161 1 : st.exchange(soci::use(permittee_account_id, "permittee_account_id"));
162 1 : st.exchange(soci::use(account_id, "account_id"));
163 1 : st.exchange(soci::use(perm_str, "perm"));
164 :
165 : auto msg = [&] {
166 0 : return (boost::format("failed to delete account grantable permission, "
167 : "permittee account id: '%s', "
168 : "account id: '%s', "
169 : "permission id: '%s'")
170 1 : % permittee_account_id % account_id
171 0 : % shared_model::proto::permissions::toString(permission))
172 0 : .str();
173 0 : };
174 1 : return execute(st, msg);
175 1 : }
176 :
177 : WsvCommandResult PostgresWsvCommand::insertAccount(
178 : const shared_model::interface::Account &account) {
179 23 : soci::statement st = sql_.prepare
180 23 : << "INSERT INTO account(account_id, domain_id, quorum,"
181 : "data) VALUES (:id, :domain_id, :quorum, :data)";
182 23 : uint32_t quorum = account.quorum();
183 23 : st.exchange(soci::use(account.accountId()));
184 23 : st.exchange(soci::use(account.domainId()));
185 23 : st.exchange(soci::use(quorum));
186 23 : st.exchange(soci::use(account.jsonData()));
187 :
188 : auto msg = [&] {
189 0 : return (boost::format("failed to insert account, "
190 : "account id: '%s', "
191 : "domain id: '%s', "
192 : "quorum: '%d', "
193 : "json_data: %s")
194 23 : % account.accountId() % account.domainId() % account.quorum()
195 0 : % account.jsonData())
196 0 : .str();
197 0 : };
198 23 : return execute(st, msg);
199 23 : }
200 :
201 : WsvCommandResult PostgresWsvCommand::insertAsset(
202 : const shared_model::interface::Asset &asset) {
203 0 : auto precision = asset.precision();
204 0 : soci::statement st = sql_.prepare
205 0 : << "INSERT INTO asset(asset_id, domain_id, \"precision\", data) "
206 : "VALUES (:id, :domain_id, :precision, NULL)";
207 0 : st.exchange(soci::use(asset.assetId()));
208 0 : st.exchange(soci::use(asset.domainId()));
209 0 : st.exchange(soci::use(precision));
210 :
211 : auto msg = [&] {
212 0 : return (boost::format("failed to insert asset, asset id: '%s', "
213 : "domain id: '%s', precision: %d")
214 0 : % asset.assetId() % asset.domainId() % asset.precision())
215 0 : .str();
216 0 : };
217 0 : return execute(st, msg);
218 0 : }
219 :
220 : WsvCommandResult PostgresWsvCommand::upsertAccountAsset(
221 : const shared_model::interface::AccountAsset &asset) {
222 0 : auto balance = asset.balance().toStringRepr();
223 0 : soci::statement st = sql_.prepare
224 0 : << "INSERT INTO account_has_asset(account_id, asset_id, amount) "
225 : "VALUES (:account_id, :asset_id, :amount) ON CONFLICT "
226 : "(account_id, asset_id) DO UPDATE SET "
227 : "amount = EXCLUDED.amount";
228 :
229 0 : st.exchange(soci::use(asset.accountId()));
230 0 : st.exchange(soci::use(asset.assetId()));
231 0 : st.exchange(soci::use(balance));
232 :
233 : auto msg = [&] {
234 0 : return (boost::format("failed to upsert account, account id: '%s', "
235 : "asset id: '%s', balance: %s")
236 0 : % asset.accountId() % asset.assetId()
237 0 : % asset.balance().toString())
238 0 : .str();
239 0 : };
240 0 : return execute(st, msg);
241 0 : }
242 :
243 : WsvCommandResult PostgresWsvCommand::insertSignatory(
244 : const shared_model::interface::types::PubkeyType &signatory) {
245 0 : soci::statement st = sql_.prepare
246 0 : << "INSERT INTO signatory(public_key) VALUES (:pk) ON CONFLICT DO "
247 : "NOTHING;";
248 0 : st.exchange(soci::use(signatory.hex()));
249 :
250 : auto msg = [&] {
251 0 : return (boost::format(
252 : "failed to insert signatory, signatory hex string: '%s'")
253 0 : % signatory.hex())
254 0 : .str();
255 0 : };
256 0 : return execute(st, msg);
257 0 : }
258 :
259 : WsvCommandResult PostgresWsvCommand::insertAccountSignatory(
260 : const shared_model::interface::types::AccountIdType &account_id,
261 : const shared_model::interface::types::PubkeyType &signatory) {
262 0 : soci::statement st = sql_.prepare
263 0 : << "INSERT INTO account_has_signatory(account_id, public_key) "
264 : "VALUES (:account_id, :pk)";
265 0 : st.exchange(soci::use(account_id));
266 0 : st.exchange(soci::use(signatory.hex()));
267 :
268 : auto msg = [&] {
269 0 : return (boost::format("failed to insert account signatory, account id: "
270 : "'%s', signatory hex string: '%s")
271 0 : % account_id % signatory.hex())
272 0 : .str();
273 0 : };
274 0 : return execute(st, msg);
275 0 : }
276 :
277 : WsvCommandResult PostgresWsvCommand::deleteAccountSignatory(
278 : const shared_model::interface::types::AccountIdType &account_id,
279 : const shared_model::interface::types::PubkeyType &signatory) {
280 0 : soci::statement st = sql_.prepare
281 0 : << "DELETE FROM account_has_signatory WHERE account_id = "
282 : ":account_id AND public_key = :pk";
283 0 : st.exchange(soci::use(account_id));
284 0 : st.exchange(soci::use(signatory.hex()));
285 :
286 : auto msg = [&] {
287 0 : return (boost::format("failed to delete account signatory, account id: "
288 : "'%s', signatory hex string: '%s'")
289 0 : % account_id % signatory.hex())
290 0 : .str();
291 0 : };
292 0 : return execute(st, msg);
293 0 : }
294 :
295 : WsvCommandResult PostgresWsvCommand::deleteSignatory(
296 : const shared_model::interface::types::PubkeyType &signatory) {
297 0 : soci::statement st = sql_.prepare
298 0 : << "DELETE FROM signatory WHERE public_key = :pk AND NOT EXISTS "
299 : "(SELECT 1 FROM account_has_signatory "
300 : "WHERE public_key = :pk) AND NOT EXISTS (SELECT 1 FROM peer "
301 : "WHERE public_key = :pk)";
302 0 : st.exchange(soci::use(signatory.hex(), "pk"));
303 :
304 : auto msg = [&] {
305 0 : return (boost::format(
306 : "failed to delete signatory, signatory hex string: '%s'")
307 0 : % signatory.hex())
308 0 : .str();
309 0 : };
310 0 : return execute(st, msg);
311 0 : }
312 :
313 : WsvCommandResult PostgresWsvCommand::insertPeer(
314 : const shared_model::interface::Peer &peer) {
315 1 : soci::statement st = sql_.prepare
316 1 : << "INSERT INTO peer(public_key, address) VALUES (:pk, :address)";
317 1 : st.exchange(soci::use(peer.pubkey().hex()));
318 1 : st.exchange(soci::use(peer.address()));
319 :
320 : auto msg = [&] {
321 0 : return (boost::format(
322 : "failed to insert peer, public key: '%s', address: '%s'")
323 1 : % peer.pubkey().hex() % peer.address())
324 0 : .str();
325 0 : };
326 1 : return execute(st, msg);
327 1 : }
328 :
329 : WsvCommandResult PostgresWsvCommand::deletePeer(
330 : const shared_model::interface::Peer &peer) {
331 1 : soci::statement st = sql_.prepare
332 1 : << "DELETE FROM peer WHERE public_key = :pk AND address = :address";
333 1 : st.exchange(soci::use(peer.pubkey().hex()));
334 1 : st.exchange(soci::use(peer.address()));
335 :
336 : auto msg = [&] {
337 0 : return (boost::format(
338 : "failed to delete peer, public key: '%s', address: '%s'")
339 1 : % peer.pubkey().hex() % peer.address())
340 0 : .str();
341 0 : };
342 1 : return execute(st, msg);
343 1 : }
344 :
345 : WsvCommandResult PostgresWsvCommand::insertDomain(
346 : const shared_model::interface::Domain &domain) {
347 21 : soci::statement st = sql_.prepare
348 21 : << "INSERT INTO domain(domain_id, default_role) VALUES (:id, "
349 : ":role)";
350 21 : st.exchange(soci::use(domain.domainId()));
351 21 : st.exchange(soci::use(domain.defaultRole()));
352 :
353 : auto msg = [&] {
354 0 : return (boost::format("failed to insert domain, domain id: '%s', "
355 : "default role: '%s'")
356 21 : % domain.domainId() % domain.defaultRole())
357 0 : .str();
358 0 : };
359 21 : return execute(st, msg);
360 21 : }
361 :
362 : WsvCommandResult PostgresWsvCommand::updateAccount(
363 : const shared_model::interface::Account &account) {
364 0 : soci::statement st = sql_.prepare
365 0 : << "UPDATE account SET quorum=:quorum WHERE account_id=:account_id";
366 0 : uint32_t quorum = account.quorum();
367 0 : st.exchange(soci::use(quorum));
368 0 : st.exchange(soci::use(account.accountId()));
369 :
370 : auto msg = [&] {
371 0 : return (boost::format(
372 : "failed to update account, account id: '%s', quorum: '%s'")
373 0 : % account.accountId() % account.quorum())
374 0 : .str();
375 0 : };
376 0 : return execute(st, msg);
377 0 : }
378 :
379 : WsvCommandResult PostgresWsvCommand::setAccountKV(
380 : const shared_model::interface::types::AccountIdType &account_id,
381 : const shared_model::interface::types::AccountIdType &creator_account_id,
382 : const std::string &key,
383 : const std::string &val) {
384 13 : soci::statement st = sql_.prepare
385 13 : << "UPDATE account SET data = jsonb_set("
386 : "CASE WHEN data ?:creator_account_id THEN data ELSE "
387 : "jsonb_set(data, :json, :empty_json) END, "
388 : " :filled_json, :val) WHERE account_id=:account_id";
389 13 : std::string json = "{" + creator_account_id + "}";
390 13 : std::string empty_json = "{}";
391 13 : std::string filled_json = "{" + creator_account_id + ", " + key + "}";
392 13 : std::string value = "\"" + val + "\"";
393 13 : st.exchange(soci::use(creator_account_id));
394 13 : st.exchange(soci::use(json));
395 13 : st.exchange(soci::use(empty_json));
396 13 : st.exchange(soci::use(filled_json));
397 13 : st.exchange(soci::use(value));
398 13 : st.exchange(soci::use(account_id));
399 :
400 : auto msg = [&] {
401 0 : return (boost::format(
402 : "failed to set account key-value, account id: '%s', "
403 : "creator account id: '%s',\n key: '%s', value: '%s'")
404 13 : % account_id % creator_account_id % key % val)
405 0 : .str();
406 0 : };
407 :
408 13 : return execute(st, msg);
409 13 : }
410 : } // namespace ametsuchi
411 : } // namespace iroha
|