Line data Source code
1 : /**
2 : Copyright (c) 2018-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPDbTransaction.h"
25 :
26 : #include "SPDbAdapter.h"
27 : #include "SPDbScheme.h"
28 : #include "SPDbWorker.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::db {
31 :
32 200 : Transaction::Op Transaction::getTransactionOp(Action a) {
33 200 : switch (a) {
34 50 : case Action::Get: return FieldGet; break;
35 50 : case Action::Set: return FieldSet; break;
36 100 : case Action::Append: return FieldAppend; break;
37 0 : case Action::Remove: return FieldClear; break;
38 0 : case Action::Count: return FieldCount; break;
39 : }
40 0 : return None;
41 : }
42 :
43 8847 : Transaction Transaction::acquire(const Adapter &adapter) {
44 8847 : auto pool = pool::acquire();
45 :
46 8845 : if (auto d = pool::get<Transaction::Data>(pool, adapter.getTransactionKey())) {
47 3225 : auto ret = Transaction(d);
48 3225 : ret.retain();
49 3225 : return ret;
50 : } else {
51 5618 : d = new (pool) Transaction::Data{adapter};
52 5619 : d->role = AccessRoleId::System;
53 5619 : pool::store(pool, d, adapter.getTransactionKey());
54 5614 : auto ret = Transaction(d);
55 5617 : ret.retain();
56 :
57 5617 : if (adapter.getApplicationInterface()) {
58 5621 : adapter.getApplicationInterface()->initTransaction(ret);
59 : }
60 :
61 5623 : return ret;
62 : }
63 :
64 : return Transaction(nullptr);
65 : }
66 :
67 600 : Transaction Transaction::acquireIfExists() {
68 600 : return acquireIfExists(pool::acquire());
69 : }
70 :
71 3525 : Transaction Transaction::acquireIfExists(stappler::memory::pool_t *pool) {
72 3525 : auto stack = pool::get<Stack>(pool, config::STORAGE_TRANSACTION_STACK_KEY);
73 3525 : if (!stack) {
74 475 : return Transaction(nullptr);
75 : }
76 :
77 3050 : return stack->stack.empty() ? Transaction(nullptr) : Transaction(stack->stack.back());
78 : }
79 :
80 38168 : void Transaction::retain() const {
81 38168 : auto p = pool::acquire();
82 38169 : auto stack = pool::get<Stack>(p, config::STORAGE_TRANSACTION_STACK_KEY);
83 38173 : if (!stack) {
84 5598 : stack = new (p) Stack;
85 5593 : pool::store(p, stack, config::STORAGE_TRANSACTION_STACK_KEY);
86 : }
87 :
88 38172 : stack->stack.emplace_back(_data);
89 38168 : }
90 :
91 33521 : void Transaction::release() const {
92 33521 : auto p = pool::acquire();
93 33520 : auto stack = pool::get<Stack>(p, config::STORAGE_TRANSACTION_STACK_KEY);
94 33523 : if (stack) {
95 33523 : auto it = std::find(stack->stack.rbegin(), stack->stack.rend(), _data);
96 33522 : if (it != stack->stack.rend()) {
97 33521 : stack->stack.erase( std::next(it).base() );
98 : }
99 : }
100 33520 : }
101 :
102 5900 : Transaction::Transaction(nullptr_t) : Transaction((Data *)nullptr) { }
103 :
104 17642 : Transaction::Transaction(Data *d) : _data(d) { }
105 :
106 18575 : void Transaction::setRole(AccessRoleId id) const {
107 18575 : _data->role = id;
108 18575 : }
109 8700 : AccessRoleId Transaction::getRole() const {
110 8700 : return _data->role;
111 : }
112 :
113 0 : const Value & Transaction::setValue(const StringView &key, Value &&val) {
114 0 : return _data->data.emplace(key.str<Interface>(), std::move(val)).first->second;
115 : }
116 :
117 0 : const Value &Transaction::getValue(const StringView &key) const {
118 0 : auto it = _data->data.find(key);
119 0 : if (it != _data->data.end()) {
120 0 : return it->second;
121 : }
122 0 : return Value::Null;
123 : }
124 :
125 1925 : Value Transaction::setObject(int64_t id, Value &&val) const {
126 1925 : Value ret;
127 1925 : pool::push(_data->objects.get_allocator());
128 : do {
129 1925 : ret = _data->objects.emplace(id, std::move(val)).first->second;
130 : } while (0);
131 1925 : pool::pop();
132 1925 : return ret;
133 0 : }
134 :
135 75 : Value Transaction::getObject(int64_t id) const {
136 75 : auto it = _data->objects.find(id);
137 75 : if (it != _data->objects.end()) {
138 0 : return it->second;
139 : }
140 75 : return Value::Null;
141 : }
142 :
143 0 : void Transaction::setStatus(int value) { _data->status = value; }
144 0 : int Transaction::getStatus() const { return _data->status; }
145 :
146 0 : void Transaction::setAdapter(const Adapter &a) {
147 0 : _data->adapter = a;
148 0 : }
149 6425 : const Adapter &Transaction::getAdapter() const {
150 6425 : return _data->adapter;
151 : }
152 :
153 15500 : bool Transaction::isInTransaction() const {
154 15500 : return _data->adapter.isInTransaction();
155 : }
156 :
157 0 : TransactionStatus Transaction::getTransactionStatus() const {
158 0 : return _data->adapter.getTransactionStatus();
159 : }
160 :
161 : struct DataHolder {
162 1300 : DataHolder(Transaction::Data *data, Worker &w) : _data(data) {
163 1300 : _tmpRole = data->role;
164 1300 : if (w.isSystem()) {
165 50 : data->role = AccessRoleId::System;
166 : }
167 1300 : }
168 :
169 1300 : ~DataHolder() {
170 1300 : _data->role = _tmpRole;
171 1300 : }
172 :
173 : Transaction::Data *_data = nullptr;
174 : AccessRoleId _tmpRole = AccessRoleId::Nobody;
175 : };
176 :
177 0 : bool Transaction::foreach(Worker &w, const Query &query, const Callback<bool(Value &)> &cb) const {
178 0 : if (!w.scheme().hasAccessControl()) {
179 0 : return _data->adapter.foreach(w, query, cb);
180 : }
181 :
182 0 : DataHolder h(_data, w);
183 :
184 0 : if (!isOpAllowed(w.scheme(), Select)) {
185 0 : return false;
186 : }
187 :
188 0 : auto r = w.scheme().getAccessRole(_data->role);
189 0 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
190 :
191 0 : if ((d && d->onSelect && !d->onSelect(w, query)) || (r && r->onSelect && !r->onSelect(w, query))) {
192 0 : return false;
193 : }
194 :
195 0 : return _data->adapter.foreach(w, query, [&, this] (Value &val) -> bool {
196 0 : if (processReturnObject(w.scheme(), val)) {
197 0 : return cb(val);
198 : }
199 0 : return true;
200 0 : });
201 0 : }
202 :
203 6274 : Value Transaction::select(Worker &w, const Query &query) const {
204 6274 : if (!w.scheme().hasAccessControl()) {
205 5799 : auto val = _data->adapter.select(w, query);
206 5800 : if (val.empty()) {
207 300 : return Value();
208 : }
209 5500 : return val;
210 5800 : }
211 :
212 475 : DataHolder h(_data, w);
213 :
214 475 : if (!isOpAllowed(w.scheme(), Select)) {
215 25 : return Value();
216 : }
217 :
218 450 : auto r = w.scheme().getAccessRole(_data->role);
219 450 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
220 :
221 450 : if ((d && d->onSelect && !d->onSelect(w, query)) || (r && r->onSelect && !r->onSelect(w, query))) {
222 0 : return Value();
223 : }
224 :
225 450 : auto val = _data->adapter.select(w, query);
226 :
227 450 : auto &arr = val.asArray();
228 450 : auto it = arr.begin();
229 925 : while (it != arr.end()) {
230 475 : if (processReturnObject(w.scheme(), *it)) {
231 425 : ++ it;
232 : } else {
233 50 : it = arr.erase(it);
234 : }
235 : }
236 :
237 450 : if (val.empty()) {
238 75 : return Value();
239 : }
240 375 : return val;
241 475 : }
242 :
243 375 : size_t Transaction::count(Worker &w, const Query &q) const {
244 375 : if (!w.scheme().hasAccessControl()) {
245 375 : return _data->adapter.count(w, q);
246 : }
247 :
248 0 : DataHolder h(_data, w);
249 :
250 0 : if (!isOpAllowed(w.scheme(), Count)) {
251 0 : return 0;
252 : }
253 :
254 0 : auto r = w.scheme().getAccessRole(_data->role);
255 0 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
256 :
257 0 : if ((d && d->onCount && !d->onCount(w, q)) || (r && r->onCount && !r->onCount(w, q))) {
258 0 : return 0;
259 : }
260 :
261 0 : return _data->adapter.count(w, q);
262 0 : }
263 :
264 275 : bool Transaction::remove(Worker &w, uint64_t oid) const {
265 275 : if (!w.scheme().hasAccessControl()) {
266 275 : return _data->adapter.remove(w, oid);
267 : }
268 :
269 0 : DataHolder h(_data, w);
270 :
271 0 : if (!isOpAllowed(w.scheme(), Remove)) {
272 0 : return false;
273 : }
274 :
275 0 : auto r = w.scheme().getAccessRole(_data->role);
276 0 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
277 :
278 0 : bool hasR = (r && r->onRemove);
279 0 : bool hasD = (d && d->onRemove);
280 :
281 0 : if (hasR || hasD) {
282 0 : if (auto obj = acquireObject(w.scheme(), oid)) {
283 0 : if ((!hasD || d->onRemove(w, obj)) && (!hasR || r->onRemove(w, obj))) {
284 0 : return _data->adapter.remove(w, oid);
285 : }
286 0 : }
287 0 : return false;
288 : }
289 :
290 0 : return _data->adapter.remove(w, oid);
291 0 : }
292 :
293 3650 : Value Transaction::create(Worker &w, Value &data) const {
294 3650 : if (!w.scheme().hasAccessControl()) {
295 3350 : return _data->adapter.create(w, data);
296 : }
297 :
298 300 : DataHolder h(_data, w);
299 :
300 300 : if (!isOpAllowed(w.scheme(), Create)) {
301 0 : return Value();
302 : }
303 :
304 300 : Value ret;
305 300 : if (perform([&, this] {
306 300 : auto r = w.scheme().getAccessRole(_data->role);
307 300 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
308 :
309 300 : if (data.isArray()) {
310 0 : auto &arr = data.asArray();
311 0 : auto it = arr.begin();
312 0 : while (it != arr.end()) {
313 0 : if ((d && d->onCreate && !d->onCreate(w, *it)) || (r && r->onCreate && !r->onCreate(w, *it))) {
314 0 : it = arr.erase(it);
315 : } else {
316 0 : ++ it;
317 : }
318 : }
319 :
320 0 : if (auto val = _data->adapter.create(w, data)) {
321 0 : auto &arr = val.asArray();
322 0 : auto it = arr.begin();
323 :
324 0 : while (it != arr.end()) {
325 0 : if ((d && d->onCreate && !d->onCreate(w, *it)) || (r && r->onCreate && !r->onCreate(w, *it))) {
326 0 : it = arr.erase(it);
327 : } else {
328 0 : ++ it;
329 : }
330 : }
331 :
332 0 : ret = !arr.empty() ? std::move(val) : Value(true);
333 0 : return true; // if user can not see result - return success but with no object
334 0 : }
335 : } else {
336 300 : if ((d && d->onCreate && !d->onCreate(w, data)) || (r && r->onCreate && !r->onCreate(w, data))) {
337 0 : return false;
338 : }
339 :
340 300 : if (auto val = _data->adapter.create(w, data)) {
341 300 : ret = processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
342 300 : return true; // if user can not see result - return success but with no object
343 300 : }
344 : }
345 0 : return false;
346 : })) {
347 300 : return ret;
348 : }
349 0 : return Value();
350 300 : }
351 :
352 1850 : Value Transaction::save(Worker &w, uint64_t oid, Value &obj, Value &patch, Set<const Field *> &fields) const {
353 1850 : if (!w.scheme().hasAccessControl()) {
354 1825 : return _data->adapter.save(w, oid, obj, patch, fields);
355 : }
356 :
357 25 : DataHolder h(_data, w);
358 :
359 25 : if (!isOpAllowed(w.scheme(), Save)) {
360 0 : return Value();
361 : }
362 :
363 25 : Value ret;
364 25 : if (perform([&, this] {
365 25 : auto r = w.scheme().getAccessRole(_data->role);
366 25 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
367 :
368 25 : bool hasR = (r && r->onSave);
369 25 : bool hasD = (d && d->onSave);
370 :
371 25 : if (hasR || hasD) {
372 0 : if ((hasD && !d->onSave(w, obj, patch, fields)) || (hasR && !r->onSave(w, obj, patch, fields))) {
373 0 : return false;
374 : }
375 :
376 0 : if (auto val = _data->adapter.save(w, oid, obj, patch, fields)) {
377 0 : ret = processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
378 0 : return true;
379 0 : }
380 0 : return false;
381 : }
382 :
383 25 : if (auto val = _data->adapter.save(w, oid, obj, patch, fields)) {
384 25 : ret = processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
385 25 : return true;
386 25 : }
387 0 : return false;
388 : })) {
389 25 : return ret;
390 : }
391 0 : return Value();
392 25 : }
393 :
394 725 : Value Transaction::patch(Worker &w, uint64_t oid, Value &data) const {
395 725 : Value tmp;
396 725 : if (!w.scheme().hasAccessControl()) {
397 850 : return _data->adapter.save(w, oid, tmp, data, Set<const Field *>());
398 : }
399 :
400 300 : DataHolder h(_data, w);
401 :
402 300 : if (!isOpAllowed(w.scheme(), Patch)) {
403 0 : return Value();
404 : }
405 :
406 300 : Value ret;
407 300 : if (perform([&, this] {
408 300 : auto r = w.scheme().getAccessRole(_data->role);
409 300 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
410 300 : if ((d && d->onPatch && !d->onPatch(w, oid, data)) || (r && r->onPatch && !r->onPatch(w, oid, data))) {
411 0 : return false;
412 : }
413 :
414 600 : if (auto val = _data->adapter.save(w, oid, tmp, data, Set<const Field *>())) {
415 300 : ret = processReturnObject(w.scheme(), val) ? std::move(val) : Value(true);
416 300 : return true;
417 300 : }
418 0 : return false;
419 : })) {
420 300 : return ret;
421 : }
422 0 : return Value();
423 725 : }
424 :
425 350 : Value Transaction::field(Action a, Worker &w, uint64_t oid, const Field &f, Value &&patch) const {
426 350 : if (!w.scheme().hasAccessControl()) {
427 200 : return _data->adapter.field(a, w, oid, f, std::move(patch));
428 : }
429 :
430 150 : DataHolder h(_data, w);
431 :
432 150 : if (!isOpAllowed(w.scheme(), getTransactionOp(a), &f)) {
433 0 : return Value();
434 : }
435 :
436 150 : auto r = w.scheme().getAccessRole(_data->role);
437 150 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
438 :
439 150 : if ((r && r->onField) || (d && d->onField) || f.getSlot()->readFilterFn) {
440 0 : if (auto obj = acquireObject(w.scheme(), oid)) {
441 0 : return field(a, w, obj, f, std::move(patch));
442 0 : }
443 0 : return Value();
444 : }
445 :
446 150 : Value ret;
447 150 : if (perform([&, this] {
448 150 : ret = _data->adapter.field(a, w, oid, f, std::move(patch));
449 150 : return true;
450 : })) {
451 150 : if (a != Action::Remove) {
452 150 : if (processReturnField(w.scheme(), Value(oid), f, ret)) {
453 150 : return ret;
454 : }
455 : } else {
456 0 : return ret;
457 : }
458 : }
459 0 : return Value();
460 150 : }
461 425 : Value Transaction::field(Action a, Worker &w, const Value &obj, const Field &f, Value &&patch) const {
462 425 : if (!w.scheme().hasAccessControl()) {
463 375 : return _data->adapter.field(a, w, obj, f, std::move(patch));
464 : }
465 :
466 50 : DataHolder h(_data, w);
467 :
468 50 : if (!isOpAllowed(w.scheme(), getTransactionOp(a), &f)) {
469 0 : return Value();
470 : }
471 :
472 50 : Value ret;
473 50 : if (perform([&, this] {
474 50 : auto r = w.scheme().getAccessRole(_data->role);
475 50 : auto d = w.scheme().getAccessRole(AccessRoleId::Default);
476 :
477 50 : if ((d && d->onField && !d->onField(a, w, obj, f, patch)) || (r && r->onField && !r->onField(a, w, obj, f, patch))) {
478 0 : return false;
479 : }
480 :
481 50 : ret = _data->adapter.field(a, w, obj, f, std::move(patch));
482 50 : return true;
483 : })) {
484 50 : if (a != Action::Remove) {
485 50 : if (processReturnField(w.scheme(), obj, f, ret)) {
486 50 : return ret;
487 : }
488 : } else {
489 0 : return ret;
490 : }
491 : }
492 0 : return Value();
493 50 : }
494 :
495 200 : bool Transaction::removeFromView(const Scheme &scheme, const FieldView &field, uint64_t oid, const Value &obj) const {
496 200 : if (!isOpAllowed(scheme, RemoveFromView)) {
497 0 : return false;
498 : }
499 :
500 200 : return _data->adapter.removeFromView(field, &scheme, oid);
501 : }
502 :
503 125 : bool Transaction::addToView(const Scheme &scheme, const FieldView &field, uint64_t oid, const Value &obj, const Value &viewObj) const {
504 125 : if (!isOpAllowed(scheme, AddToView)) {
505 0 : return false;
506 : }
507 :
508 125 : return _data->adapter.addToView(field, &scheme, oid, viewObj);
509 : }
510 :
511 1025 : int64_t Transaction::getDeltaValue(const Scheme &scheme) {
512 1025 : if (!isOpAllowed(scheme, Delta)) {
513 0 : return false;
514 : }
515 :
516 1025 : return _data->adapter.getDeltaValue(scheme);
517 : }
518 :
519 50 : int64_t Transaction::getDeltaValue(const Scheme &scheme, const FieldView &f, uint64_t id) {
520 50 : if (!isOpAllowed(scheme, DeltaView)) {
521 0 : return false;
522 : }
523 :
524 50 : return _data->adapter.getDeltaValue(scheme, f, id);
525 : }
526 :
527 775 : Vector<int64_t> Transaction::performQueryListForIds(const QueryList &list, size_t count) const {
528 1875 : for (auto &it : list.getItems()) {
529 1100 : if (!isOpAllowed(*it.scheme, Id)) {
530 0 : return Vector<int64_t>();
531 : }
532 : }
533 :
534 775 : return _data->adapter.performQueryListForIds(list, count);
535 : }
536 :
537 1500 : Value Transaction::performQueryList(const QueryList &list, size_t count, bool forUpdate) const {
538 1500 : count = (count == stappler::maxOf<size_t>()) ? list.size() : count;
539 3150 : for (auto &it : list.getItems()) {
540 1650 : if (!isOpAllowed(*it.scheme, Id)) {
541 0 : return Value();
542 : }
543 : }
544 :
545 1500 : if (!isOpAllowed(*list.getScheme(), Select)) {
546 0 : return Value();
547 : }
548 :
549 1500 : Value vals;
550 1500 : auto &t = list.getContinueToken();
551 1500 : if (t && count == list.size()) {
552 375 : if (count > 1) {
553 0 : auto &item = list.getItems().at(list.size() - 2);
554 0 : return performQueryListField(list, *item.field);
555 : } else {
556 375 : auto q = list.getItems().back().query;
557 750 : vals = t.perform(*list.getItems().back().scheme, *this, q,
558 750 : t.hasFlag(ContinueToken::Inverted) ? Ordering::Descending : Ordering::Ascending);
559 375 : }
560 : } else {
561 1125 : vals = _data->adapter.performQueryList(list, count, forUpdate);
562 : }
563 :
564 1500 : if (vals) {
565 1500 : auto &arr = vals.asArray();
566 1500 : auto it = arr.begin();
567 7000 : while (it != arr.end()) {
568 5500 : if (processReturnObject(*list.getScheme(), *it)) {
569 5500 : ++ it;
570 : } else {
571 0 : it = arr.erase(it);
572 : }
573 : }
574 : }
575 1500 : return vals;
576 1500 : }
577 :
578 125 : Value Transaction::performQueryListField(const QueryList &list, const Field &f) const {
579 125 : auto count = list.size();
580 125 : if (f.getType() == Type::View || f.getType() == Type::Set) {
581 75 : count -= 1;
582 : }
583 :
584 325 : for (auto &it : list.getItems()) {
585 200 : if (!isOpAllowed(*it.scheme, Id)) {
586 0 : return Value();
587 : }
588 : }
589 :
590 125 : if (!isOpAllowed(*list.getScheme(), FieldGet, &f)) {
591 0 : return Value();
592 : }
593 :
594 125 : auto ids = performQueryListForIds(list, count);
595 125 : if (ids.size() == 1) {
596 100 : auto id = ids.front();
597 100 : auto scheme = list.getItems().at(count - 1).scheme;
598 100 : if (f.getType() == Type::View || f.getType() == Type::Set) {
599 50 : db::Query q = db::Query::field(id, f.getName(), list.getItems().back().query);
600 50 : Worker w(*scheme, *this);
601 :
602 50 : Value obj(id);
603 50 : auto r = scheme->getAccessRole(_data->role);
604 50 : auto d = scheme->getAccessRole(AccessRoleId::Default);
605 :
606 50 : if ((r && r->onField) || (d && d->onField) || f.getSlot()->readFilterFn) {
607 0 : if ((obj = acquireObject(w.scheme(), id))) {
608 0 : Value tmp;
609 0 : if (!d->onField(Action::Get, w, obj, f, tmp) || !r->onField(Action::Get, w, obj, f, tmp)) {
610 0 : return Value();
611 : }
612 0 : }
613 : }
614 :
615 50 : Value val;
616 50 : if (auto &t = list.getContinueToken()) {
617 0 : val = t.perform(*scheme, *this, q, t.hasFlag(ContinueToken::Inverted) ? Ordering::Descending : Ordering::Ascending);
618 : } else {
619 50 : val = scheme->select(*this, q);
620 : }
621 50 : if (val) {
622 25 : if (!processReturnField(*scheme, obj, f, val)) {
623 0 : return Value();
624 : }
625 : }
626 50 : return val;
627 50 : } else {
628 50 : if (auto obj = acquireObject(*scheme, id)) {
629 50 : return scheme->getProperty(*this, obj, f, list.getItems().at(list.size() - 1).getQueryFields());
630 50 : }
631 : }
632 : }
633 :
634 25 : return Value();
635 125 : }
636 :
637 2550 : void Transaction::scheduleAutoField(const Scheme &scheme, const Field &field, uint64_t id) const {
638 2550 : _data->adapter.scheduleAutoField(scheme, field, id);
639 2550 : }
640 :
641 2625 : bool Transaction::beginTransaction() const {
642 2625 : return _data->adapter.beginTransaction();
643 : }
644 2625 : bool Transaction::endTransaction() const {
645 2625 : if (_data->adapter.endTransaction()) {
646 2625 : if (!_data->adapter.isInTransaction()) {
647 2625 : clearObjectStorage();
648 : }
649 2625 : return true;
650 : }
651 0 : return false;
652 : }
653 0 : void Transaction::cancelTransaction() const {
654 0 : _data->adapter.cancelTransaction();
655 0 : }
656 :
657 2625 : void Transaction::clearObjectStorage() const {
658 2625 : _data->objects.clear();
659 2625 : }
660 :
661 1650 : static bool Transaction_processFields(const Scheme &scheme, const Value &val, Value &obj, const Map<String, Field> &vec) {
662 1650 : if (obj.isDictionary()) {
663 1650 : auto &dict = obj.asDict();
664 1650 : auto it = dict.begin();
665 7900 : while (it != dict.end()) {
666 6250 : auto f_it = vec.find(it->first);
667 6250 : if (f_it != vec.end()) {
668 4950 : auto slot = f_it->second.getSlot();
669 4950 : if (slot->readFilterFn) {
670 0 : if (!slot->readFilterFn(scheme, val, it->second)) {
671 0 : it = dict.erase(it);
672 0 : continue;
673 : }
674 : }
675 :
676 4950 : if (slot->type == Type::Extra) {
677 350 : auto extraSlot = static_cast<const FieldExtra *>(slot);
678 350 : if (!Transaction_processFields(scheme, val, it->second, extraSlot->fields)) {
679 0 : it = dict.erase(it);
680 0 : continue;
681 : }
682 : }
683 : }
684 6250 : ++ it;
685 : }
686 : }
687 :
688 1650 : return true;
689 : }
690 :
691 6600 : bool Transaction::processReturnObject(const Scheme &scheme, Value &val) const {
692 6600 : if (!scheme.hasAccessControl()) {
693 5225 : return true;
694 : }
695 :
696 1375 : auto r = scheme.getAccessRole(_data->role);
697 1375 : auto d = scheme.getAccessRole(AccessRoleId::Default);
698 :
699 0 : if ((d && d->onReturn && !d->onReturn(scheme, val))
700 1375 : || (r && r->onReturn && !r->onReturn(scheme, val))) {
701 75 : return false;
702 : }
703 :
704 1300 : return Transaction_processFields(scheme, val, val, scheme.getFields());
705 : }
706 :
707 225 : bool Transaction::processReturnField(const Scheme &scheme, const Value &obj, const Field &field, Value &val) const {
708 225 : if (!scheme.hasAccessControl()) {
709 25 : return true;
710 : }
711 :
712 200 : auto slot = field.getSlot();
713 200 : if (slot->readFilterFn) {
714 0 : if (obj.isInteger()) {
715 0 : if (auto tmpObj = acquireObject(scheme, obj.getInteger())) {
716 0 : if (!slot->readFilterFn(scheme, tmpObj, val)) {
717 0 : return false;
718 : }
719 : } else {
720 0 : return false;
721 0 : }
722 : } else {
723 0 : if (!slot->readFilterFn(scheme, obj, val)) {
724 0 : return false;
725 : }
726 : }
727 : }
728 :
729 200 : auto r = scheme.getAccessRole(_data->role);
730 200 : auto d = scheme.getAccessRole(AccessRoleId::Default);
731 :
732 0 : if ((d && d->onReturnField && !d->onReturnField(scheme, field, val))
733 200 : || (r && r->onReturnField && !r->onReturnField(scheme, field, val))) {
734 0 : return false;
735 : }
736 :
737 200 : if (field.getType() == Type::Object || field.getType() == Type::Set || field.getType() == Type::View) {
738 0 : if (auto nextScheme = field.getForeignScheme()) {
739 0 : if (val.isDictionary()) {
740 0 : if (!processReturnObject(*nextScheme, val)) {
741 0 : return false;
742 : }
743 0 : } else if (val.isArray()) {
744 0 : auto &arr = val.asArray();
745 0 : auto it = arr.begin();
746 0 : while (it != arr.end()) {
747 0 : if (processReturnObject(*nextScheme, *it)) {
748 0 : ++ it;
749 : } else {
750 0 : it = arr.erase(it);
751 : }
752 : }
753 : }
754 : }
755 : }
756 200 : return true;
757 : }
758 :
759 7275 : bool Transaction::isOpAllowed(const Scheme &scheme, Op op, const Field *f) const {
760 7275 : if (!scheme.hasAccessControl()) {
761 4575 : return true;
762 : }
763 :
764 2700 : if (auto r = scheme.getAccessRole(_data->role)) {
765 2475 : return r->operations.test(stappler::toInt(op));
766 : }
767 225 : switch (op) {
768 0 : case Op::None:
769 : case Op::Max:
770 0 : return false;
771 : break;
772 100 : case Op::Id:
773 : case Op::Select:
774 : case Op::Count:
775 : case Op::Delta:
776 : case Op::DeltaView:
777 : case Op::FieldGet:
778 100 : return true;
779 : break;
780 125 : case Op::Remove:
781 : case Op::Create:
782 : case Op::Save:
783 : case Op::Patch:
784 : case Op::FieldSet:
785 : case Op::FieldAppend:
786 : case Op::FieldClear:
787 : case Op::FieldCount:
788 : case Op::RemoveFromView:
789 : case Op::AddToView:
790 125 : return _data->role == AccessRoleId::Admin || _data->role == AccessRoleId::System;
791 : break;
792 : }
793 0 : return false;
794 : }
795 :
796 5618 : Transaction::Data::Data(const Adapter &adapter, memory::pool_t *p) : adapter(adapter), pool(p) { }
797 :
798 :
799 50 : Value Transaction::acquireObject(const Scheme &scheme, uint64_t oid) const {
800 50 : Value ret;
801 50 : pool::push(_data->objects.get_allocator());
802 : do {
803 50 : auto it = _data->objects.find(oid);
804 50 : if (it == _data->objects.end()) {
805 100 : if (auto obj = Worker(scheme, *this).asSystem().get(oid)) {
806 50 : ret = _data->objects.emplace(oid, std::move(obj)).first->second;
807 50 : }
808 : } else {
809 0 : ret = it->second;
810 : }
811 : } while (0);
812 50 : pool::pop();
813 50 : return ret;
814 0 : }
815 :
816 0 : AccessRole &AccessRole::define(Transaction::Op op) {
817 0 : operations.set(op);
818 0 : return *this;
819 : }
820 175 : AccessRole &AccessRole::define(AccessRoleId id) {
821 175 : users.set(stappler::toInt(id));
822 175 : return *this;
823 : }
824 0 : AccessRole &AccessRole::define() {
825 0 : return *this;
826 : }
827 25 : AccessRole &AccessRole::define(OnSelect &&val) {
828 25 : if (val.get()) {
829 25 : operations.set(Transaction::Select);
830 : }
831 25 : onSelect = std::move(val.get());
832 25 : return *this;
833 : }
834 0 : AccessRole &AccessRole::define(OnCount &&val) {
835 0 : if (val.get()) {
836 0 : operations.set(Transaction::Count);
837 : }
838 0 : onCount = std::move(val.get());
839 0 : return *this;
840 : }
841 75 : AccessRole &AccessRole::define(OnCreate &&val) {
842 75 : if (val.get()) {
843 75 : operations.set(Transaction::Create);
844 : }
845 75 : onCreate = std::move(val.get());
846 75 : return *this;
847 : }
848 0 : AccessRole &AccessRole::define(OnPatch &&val) {
849 0 : if (val.get()) {
850 0 : operations.set(Transaction::Patch);
851 : }
852 0 : onPatch = std::move(val.get());
853 0 : return *this;
854 : }
855 0 : AccessRole &AccessRole::define(OnSave &&val) {
856 0 : if (val.get()) {
857 0 : operations.set(Transaction::Save);
858 : }
859 0 : onSave = std::move(val.get());
860 0 : return *this;
861 : }
862 0 : AccessRole &AccessRole::define(OnRemove &&val) {
863 0 : if (val.get()) {
864 0 : operations.set(Transaction::Remove);
865 : }
866 0 : onRemove = std::move(val.get());
867 0 : return *this;
868 : }
869 0 : AccessRole &AccessRole::define(OnField &&val) {
870 0 : onField = std::move(val.get());
871 0 : return *this;
872 : }
873 50 : AccessRole &AccessRole::define(OnReturn &&val) {
874 50 : onReturn = std::move(val.get());
875 50 : return *this;
876 : }
877 0 : AccessRole &AccessRole::define(OnReturnField &&val) {
878 0 : onReturnField = std::move(val.get());
879 0 : return *this;
880 : }
881 :
882 : }
|