Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
authority_checker.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include <fc/scoped_exit.hpp>
9
10#include <boost/range/algorithm/find.hpp>
11#include <boost/algorithm/cxx11/all_of.hpp>
12
13#include <functional>
14
15namespace sysio { namespace chain {
16
17namespace detail {
18 using meta_permission_key = std::tuple<uint32_t, int>;
19 using meta_permission_value = std::function<uint32_t()>;
20 using meta_permission_map = boost::container::flat_multimap<meta_permission_key, meta_permission_value, std::greater<>>;
21
22}
23
34 template<typename PermissionToAuthorityFunc>
36 private:
37 PermissionToAuthorityFunc permission_to_authority;
38 const std::function<void()>& checktime;
39 vector<public_key_type> provided_keys; // Making this a flat_set<public_key_type> causes runtime problems with utilities::filter_data_by_marker for some reason. TODO: Figure out why.
40 flat_set<permission_level> provided_permissions;
41 vector<bool> _used_keys;
42 fc::microseconds provided_delay;
43 uint16_t recursion_depth_limit;
44
45 public:
46 authority_checker( PermissionToAuthorityFunc permission_to_authority,
47 uint16_t recursion_depth_limit,
48 const flat_set<public_key_type>& provided_keys,
49 const flat_set<permission_level>& provided_permissions,
50 fc::microseconds provided_delay,
51 const std::function<void()>& checktime
52 )
53 :permission_to_authority(permission_to_authority)
54 ,checktime( checktime )
55 ,provided_keys(provided_keys.begin(), provided_keys.end())
56 ,provided_permissions(provided_permissions)
57 ,_used_keys(provided_keys.size(), false)
58 ,provided_delay(provided_delay)
59 ,recursion_depth_limit(recursion_depth_limit)
60 {
61 SYS_ASSERT( static_cast<bool>(checktime), authorization_exception, "checktime cannot be empty" );
62 }
63
69
70 typedef map<permission_level, permission_cache_status> permission_cache_type;
71
72 bool satisfied( const permission_level& permission,
73 fc::microseconds override_provided_delay,
74 permission_cache_type* cached_perms = nullptr
75 )
76 {
77 auto delay_reverter = fc::make_scoped_exit( [this, delay = provided_delay] () mutable {
78 provided_delay = delay;
79 });
80
81 provided_delay = override_provided_delay;
82
83 return satisfied( permission, cached_perms );
84 }
85
86 bool satisfied( const permission_level& permission, permission_cache_type* cached_perms = nullptr ) {
87 permission_cache_type cached_permissions;
88
89 if( cached_perms == nullptr )
90 cached_perms = initialize_permission_cache( cached_permissions );
91
92 weight_tally_visitor visitor(*this, *cached_perms, 0);
93 return ( visitor(permission_level_weight{permission, 1}) > 0 );
94 }
95
96 template<typename AuthorityType>
97 bool satisfied( const AuthorityType& authority,
98 fc::microseconds override_provided_delay,
99 permission_cache_type* cached_perms = nullptr
100 )
101 {
102 auto delay_reverter = fc::make_scoped_exit( [this, delay = provided_delay] () mutable {
103 provided_delay = delay;
104 });
105
106 provided_delay = override_provided_delay;
107
108 return satisfied( authority, cached_perms );
109 }
110
111 template<typename AuthorityType>
112 bool satisfied( const AuthorityType& authority, permission_cache_type* cached_perms = nullptr ) {
113 permission_cache_type cached_permissions;
114
115 if( cached_perms == nullptr )
116 cached_perms = initialize_permission_cache( cached_permissions );
117
118 return satisfied( authority, *cached_perms, 0 );
119 }
120
121 bool all_keys_used() const { return boost::algorithm::all_of_equal(_used_keys, true); }
122
123 flat_set<public_key_type> used_keys() const {
124 auto range = filter_data_by_marker(provided_keys, _used_keys, true);
125 return {range.begin(), range.end()};
126 }
127 flat_set<public_key_type> unused_keys() const {
128 auto range = filter_data_by_marker(provided_keys, _used_keys, false);
129 return {range.begin(), range.end()};
130 }
131
132 static std::optional<permission_cache_status>
134 const permission_level& level )
135 {
136 auto itr = permissions.find( level );
137 if( itr != permissions.end() )
138 return itr->second;
139
140 itr = permissions.find( {level.actor, permission_name()} );
141 if( itr != permissions.end() )
142 return itr->second;
143
144 return std::optional<permission_cache_status>();
145 }
146
147 private:
148 permission_cache_type* initialize_permission_cache( permission_cache_type& cached_permissions ) {
149 for( const auto& p : provided_permissions ) {
150 cached_permissions.emplace_hint( cached_permissions.end(), p, permission_satisfied );
151 }
152 return &cached_permissions;
153 }
154
155 template<typename AuthorityType>
156 bool satisfied( const AuthorityType& authority, permission_cache_type& cached_permissions, uint16_t depth ) {
157 // Save the current used keys; if we do not satisfy this authority, the newly used keys aren't actually used
158 auto KeyReverter = fc::make_scoped_exit([this, keys = _used_keys] () mutable {
159 _used_keys = keys;
160 });
161
162 // Sort key permissions and account permissions together into a single set of meta_permissions
163 detail::meta_permission_map permissions;
164
165 weight_tally_visitor visitor(*this, cached_permissions, depth);
166 auto emplace_permission = [&permissions, &visitor](int priority, const auto& mp) {
167 permissions.emplace(
168 std::make_tuple(mp.weight, priority),
169 [&mp, &visitor]() {
170 return visitor(mp);
171 }
172 );
173 };
174
175 permissions.reserve(authority.waits.size() + authority.keys.size() + authority.accounts.size());
176 std::for_each(authority.accounts.begin(), authority.accounts.end(), std::bind(emplace_permission, 1, std::placeholders::_1));
177 std::for_each(authority.keys.begin(), authority.keys.end(), std::bind(emplace_permission, 2, std::placeholders::_1));
178 std::for_each(authority.waits.begin(), authority.waits.end(), std::bind(emplace_permission, 3, std::placeholders::_1));
179
180 // Check all permissions, from highest weight to lowest, seeing if provided authorization factors satisfies them or not
181 for( const auto& p: permissions )
182 // If we've got enough weight, to satisfy the authority, return!
183 if( p.second() >= authority.threshold ) {
184 KeyReverter.cancel();
185 return true;
186 }
187 return false;
188 }
189
190 struct weight_tally_visitor {
191 using result_type = uint32_t;
192
193 authority_checker& checker;
194 permission_cache_type& cached_permissions;
195 uint16_t recursion_depth;
196 uint32_t total_weight = 0;
197
198 weight_tally_visitor(authority_checker& checker, permission_cache_type& cached_permissions, uint16_t recursion_depth)
199 :checker(checker)
200 ,cached_permissions(cached_permissions)
201 ,recursion_depth(recursion_depth)
202 {}
203
204 uint32_t operator()(const wait_weight& permission) {
205 if( checker.provided_delay >= fc::seconds(permission.wait_sec) ) {
206 total_weight += permission.weight;
207 }
208 return total_weight;
209 }
210
211 template<typename KeyWeight, typename = std::enable_if_t<detail::is_any_of_v<KeyWeight, shared_key_weight, key_weight>>>
212 uint32_t operator()(const KeyWeight& permission) {
213 auto itr = boost::find( checker.provided_keys, permission.key );
214 if( itr != checker.provided_keys.end() ) {
215 checker._used_keys[itr - checker.provided_keys.begin()] = true;
216 total_weight += permission.weight;
217 }
218 return total_weight;
219 }
220
221 uint32_t operator()(const permission_level_weight& permission) {
222 auto status = authority_checker::permission_status_in_cache( cached_permissions, permission.permission );
223 if( !status ) {
224 if( recursion_depth < checker.recursion_depth_limit ) {
225 bool r = false;
226 typename permission_cache_type::iterator itr = cached_permissions.end();
227
228 bool propagate_error = false;
229 try {
230 auto&& auth = checker.permission_to_authority( permission.permission );
231 propagate_error = true;
232 auto res = cached_permissions.emplace( permission.permission, being_evaluated );
233 itr = res.first;
234 r = checker.satisfied( std::forward<decltype(auth)>(auth), cached_permissions, recursion_depth + 1 );
235 } catch( const permission_query_exception& ) {
236 if( propagate_error )
237 throw;
238 else
239 return total_weight; // if the permission doesn't exist, continue without it
240 }
241
242 if( r ) {
243 total_weight += permission.weight;
244 itr->second = permission_satisfied;
245 } else {
246 itr->second = permission_unsatisfied;
247 }
248 }
249 } else if( *status == permission_satisfied ) {
250 total_weight += permission.weight;
251 }
252 return total_weight;
253 }
254 };
255
256 };
257
258 template<typename PermissionToAuthorityFunc>
259 auto make_auth_checker( PermissionToAuthorityFunc&& pta,
260 uint16_t recursion_depth_limit,
261 const flat_set<public_key_type>& provided_keys,
262 const flat_set<permission_level>& provided_permissions = flat_set<permission_level>(),
263 fc::microseconds provided_delay = fc::microseconds(0),
264 const std::function<void()>& _checktime = std::function<void()>()
265 )
266 {
267 auto noop_checktime = []() {};
268 const auto& checktime = ( static_cast<bool>(_checktime) ? _checktime : noop_checktime );
269 return authority_checker< PermissionToAuthorityFunc>( std::forward<PermissionToAuthorityFunc>(pta),
270 recursion_depth_limit,
271 provided_keys,
272 provided_permissions,
273 provided_delay,
274 checktime );
275 }
276
277} } // namespace sysio::chain
const mie::Vuint & p
Definition bn.cpp:27
const mie::Vuint & r
Definition bn.cpp:28
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
static std::optional< permission_cache_status > permission_status_in_cache(const permission_cache_type &permissions, const permission_level &level)
authority_checker(PermissionToAuthorityFunc permission_to_authority, uint16_t recursion_depth_limit, const flat_set< public_key_type > &provided_keys, const flat_set< permission_level > &provided_permissions, fc::microseconds provided_delay, const std::function< void()> &checktime)
flat_set< public_key_type > unused_keys() const
bool satisfied(const permission_level &permission, permission_cache_type *cached_perms=nullptr)
bool satisfied(const permission_level &permission, fc::microseconds override_provided_delay, permission_cache_type *cached_perms=nullptr)
bool satisfied(const AuthorityType &authority, permission_cache_type *cached_perms=nullptr)
flat_set< public_key_type > used_keys() const
map< permission_level, permission_cache_status > permission_cache_type
bool satisfied(const AuthorityType &authority, fc::microseconds override_provided_delay, permission_cache_type *cached_perms=nullptr)
void delay(websocketpp::connection_hdl, long duration)
scoped_exit< Callback > make_scoped_exit(Callback &&c)
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
std::tuple< uint32_t, int > meta_permission_key
boost::container::flat_multimap< meta_permission_key, meta_permission_value, std::greater<> > meta_permission_map
std::function< uint32_t()> meta_permission_value
DataRange filter_data_by_marker(DataRange data, MarkerRange markers, const Marker &markerValue)
Return values in DataRange corresponding to matching Markers.
auto make_auth_checker(PermissionToAuthorityFunc &&pta, uint16_t recursion_depth_limit, const flat_set< public_key_type > &provided_keys, const flat_set< permission_level > &provided_permissions=flat_set< permission_level >(), fc::microseconds provided_delay=fc::microseconds(0), const std::function< void()> &_checktime=std::function< void()>())
authority_checker
name permission_name
Definition types.hpp:121
unsigned short uint16_t
Definition stdint.h:125
unsigned int uint32_t
Definition stdint.h:126