1use alloc::{
2 collections::{btree_map::Entry, BTreeMap},
3 vec::Vec,
4};
5use core::fmt::{self, Display, Formatter};
6
7#[cfg(any(feature = "testing", test))]
8use rand::{
9 distributions::{Distribution, Standard},
10 Rng,
11};
12use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
13
14use crate::{bytesrepr, HashAddr, URef, URefAddr};
15pub use private::AccessRights;
16
17pub const ACCESS_RIGHTS_SERIALIZED_LENGTH: usize = 1;
19
20#[allow(clippy::bad_bit_mask)]
22mod private {
23 use bitflags::bitflags;
24 #[cfg(feature = "datasize")]
25 use datasize::DataSize;
26
27 bitflags! {
28 #[allow(clippy::derived_hash_with_manual_eq)]
31 #[cfg_attr(feature = "datasize", derive(DataSize))]
32 pub struct AccessRights: u8 {
33 const NONE = 0;
35 const READ = 0b001;
37 const WRITE = 0b010;
39 const ADD = 0b100;
41 const READ_ADD = Self::READ.bits | Self::ADD.bits;
43 const READ_WRITE = Self::READ.bits | Self::WRITE.bits;
45 const ADD_WRITE = Self::ADD.bits | Self::WRITE.bits;
47 const READ_ADD_WRITE = Self::READ.bits | Self::ADD.bits | Self::WRITE.bits;
49 }
50 }
51}
52
53impl Default for AccessRights {
54 fn default() -> Self {
55 AccessRights::NONE
56 }
57}
58
59impl AccessRights {
60 pub fn is_readable(self) -> bool {
62 self & AccessRights::READ == AccessRights::READ
63 }
64
65 pub fn is_writeable(self) -> bool {
67 self & AccessRights::WRITE == AccessRights::WRITE
68 }
69
70 pub fn is_addable(self) -> bool {
72 self & AccessRights::ADD == AccessRights::ADD
73 }
74
75 pub fn is_none(self) -> bool {
77 self == AccessRights::NONE
78 }
79}
80
81impl Display for AccessRights {
82 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
83 match *self {
84 AccessRights::NONE => write!(f, "NONE"),
85 AccessRights::READ => write!(f, "READ"),
86 AccessRights::WRITE => write!(f, "WRITE"),
87 AccessRights::ADD => write!(f, "ADD"),
88 AccessRights::READ_ADD => write!(f, "READ_ADD"),
89 AccessRights::READ_WRITE => write!(f, "READ_WRITE"),
90 AccessRights::ADD_WRITE => write!(f, "ADD_WRITE"),
91 AccessRights::READ_ADD_WRITE => write!(f, "READ_ADD_WRITE"),
92 _ => write!(f, "UNKNOWN"),
93 }
94 }
95}
96
97impl bytesrepr::ToBytes for AccessRights {
98 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
99 self.bits().to_bytes()
100 }
101
102 fn serialized_length(&self) -> usize {
103 ACCESS_RIGHTS_SERIALIZED_LENGTH
104 }
105
106 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
107 writer.push(self.bits());
108 Ok(())
109 }
110}
111
112impl bytesrepr::FromBytes for AccessRights {
113 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
114 let (id, rem) = u8::from_bytes(bytes)?;
115 match AccessRights::from_bits(id) {
116 Some(rights) => Ok((rights, rem)),
117 None => Err(bytesrepr::Error::Formatting),
118 }
119 }
120}
121
122impl Serialize for AccessRights {
123 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
124 self.bits().serialize(serializer)
125 }
126}
127
128impl<'de> Deserialize<'de> for AccessRights {
129 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
130 let bits = u8::deserialize(deserializer)?;
131 AccessRights::from_bits(bits).ok_or_else(|| SerdeError::custom("invalid bits"))
132 }
133}
134
135#[cfg(any(feature = "testing", test))]
136impl Distribution<AccessRights> for Standard {
137 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> AccessRights {
138 let mut result = AccessRights::NONE;
139 if rng.gen() {
140 result |= AccessRights::READ;
141 }
142 if rng.gen() {
143 result |= AccessRights::WRITE;
144 }
145 if rng.gen() {
146 result |= AccessRights::ADD;
147 }
148 result
149 }
150}
151
152#[derive(Debug, PartialEq, Eq)]
154pub enum GrantedAccess {
155 PreExisting,
157 Granted {
159 uref_addr: URefAddr,
161 newly_granted_access_rights: AccessRights,
163 },
164}
165
166#[derive(Debug, PartialEq, Eq)]
168pub struct ContextAccessRights {
169 hash_addr: HashAddr,
170 access_rights: BTreeMap<URefAddr, AccessRights>,
171}
172
173impl ContextAccessRights {
174 pub fn new<T: IntoIterator<Item = URef>>(hash_addr: HashAddr, uref_iter: T) -> Self {
177 let mut context_access_rights = ContextAccessRights {
178 hash_addr,
179 access_rights: BTreeMap::new(),
180 };
181 context_access_rights.do_extend(uref_iter);
182 context_access_rights
183 }
184
185 pub fn extend_access_rights(&mut self, access_rights: BTreeMap<URefAddr, AccessRights>) {
187 for (uref_addr, access_rights) in access_rights {
188 match self.access_rights.entry(uref_addr) {
189 Entry::Occupied(rights) => {
190 *rights.into_mut() = rights.get().union(access_rights);
191 }
192 Entry::Vacant(rights) => {
193 rights.insert(access_rights);
194 }
195 }
196 }
197 }
198
199 pub fn context_key(&self) -> HashAddr {
201 self.hash_addr
202 }
203
204 pub fn extend(&mut self, urefs: &[URef]) {
206 self.do_extend(urefs.iter().copied())
207 }
208
209 fn do_extend<T: IntoIterator<Item = URef>>(&mut self, uref_iter: T) {
211 for uref in uref_iter {
212 match self.access_rights.entry(uref.addr()) {
213 Entry::Occupied(rights) => {
214 *rights.into_mut() = rights.get().union(uref.access_rights());
215 }
216 Entry::Vacant(rights) => {
217 rights.insert(uref.access_rights());
218 }
219 }
220 }
221 }
222
223 pub fn has_access_rights_to_uref(&self, uref: &URef) -> bool {
225 if let Some(known_rights) = self.access_rights.get(&uref.addr()) {
226 let rights_to_check = uref.access_rights();
227 known_rights.contains(rights_to_check)
228 } else {
229 false
231 }
232 }
233
234 pub fn access_rights(&self) -> &BTreeMap<URefAddr, AccessRights> {
236 &self.access_rights
237 }
238
239 pub fn take_access_rights(self) -> BTreeMap<URefAddr, AccessRights> {
241 self.access_rights
242 }
243
244 pub fn grant_access(&mut self, uref: URef) -> GrantedAccess {
246 match self.access_rights.entry(uref.addr()) {
247 Entry::Occupied(existing_rights) => {
248 let newly_granted_access_rights =
249 uref.access_rights().difference(*existing_rights.get());
250 *existing_rights.into_mut() = existing_rights.get().union(uref.access_rights());
251 if newly_granted_access_rights.is_none() {
252 GrantedAccess::PreExisting
253 } else {
254 GrantedAccess::Granted {
255 uref_addr: uref.addr(),
256 newly_granted_access_rights,
257 }
258 }
259 }
260 Entry::Vacant(rights) => {
261 rights.insert(uref.access_rights());
262 GrantedAccess::Granted {
263 uref_addr: uref.addr(),
264 newly_granted_access_rights: uref.access_rights(),
265 }
266 }
267 }
268 }
269
270 pub fn remove_access(&mut self, uref_addr: URefAddr, access_rights: AccessRights) {
272 if let Some(current_access_rights) = self.access_rights.get_mut(&uref_addr) {
273 current_access_rights.remove(access_rights)
274 }
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use crate::UREF_ADDR_LENGTH;
282
283 const ENTITY_HASH: HashAddr = [1u8; 32];
284 const UREF_ADDRESS: [u8; UREF_ADDR_LENGTH] = [1; UREF_ADDR_LENGTH];
285 const UREF_NO_PERMISSIONS: URef = URef::new(UREF_ADDRESS, AccessRights::empty());
286 const UREF_READ: URef = URef::new(UREF_ADDRESS, AccessRights::READ);
287 const UREF_ADD: URef = URef::new(UREF_ADDRESS, AccessRights::ADD);
288 const UREF_WRITE: URef = URef::new(UREF_ADDRESS, AccessRights::WRITE);
289 const UREF_READ_ADD: URef = URef::new(UREF_ADDRESS, AccessRights::READ_ADD);
290 const UREF_READ_ADD_WRITE: URef = URef::new(UREF_ADDRESS, AccessRights::READ_ADD_WRITE);
291
292 fn test_readable(right: AccessRights, is_true: bool) {
293 assert_eq!(right.is_readable(), is_true)
294 }
295
296 #[test]
297 fn test_is_readable() {
298 test_readable(AccessRights::READ, true);
299 test_readable(AccessRights::READ_ADD, true);
300 test_readable(AccessRights::READ_WRITE, true);
301 test_readable(AccessRights::READ_ADD_WRITE, true);
302 test_readable(AccessRights::ADD, false);
303 test_readable(AccessRights::ADD_WRITE, false);
304 test_readable(AccessRights::WRITE, false);
305 }
306
307 fn test_writable(right: AccessRights, is_true: bool) {
308 assert_eq!(right.is_writeable(), is_true)
309 }
310
311 #[test]
312 fn test_is_writable() {
313 test_writable(AccessRights::WRITE, true);
314 test_writable(AccessRights::READ_WRITE, true);
315 test_writable(AccessRights::ADD_WRITE, true);
316 test_writable(AccessRights::READ, false);
317 test_writable(AccessRights::ADD, false);
318 test_writable(AccessRights::READ_ADD, false);
319 test_writable(AccessRights::READ_ADD_WRITE, true);
320 }
321
322 fn test_addable(right: AccessRights, is_true: bool) {
323 assert_eq!(right.is_addable(), is_true)
324 }
325
326 #[test]
327 fn test_is_addable() {
328 test_addable(AccessRights::ADD, true);
329 test_addable(AccessRights::READ_ADD, true);
330 test_addable(AccessRights::READ_WRITE, false);
331 test_addable(AccessRights::ADD_WRITE, true);
332 test_addable(AccessRights::READ, false);
333 test_addable(AccessRights::WRITE, false);
334 test_addable(AccessRights::READ_ADD_WRITE, true);
335 }
336
337 #[test]
338 fn should_check_has_access_rights_to_uref() {
339 let context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD]);
340 assert!(context_rights.has_access_rights_to_uref(&UREF_READ_ADD));
341 assert!(context_rights.has_access_rights_to_uref(&UREF_READ));
342 assert!(context_rights.has_access_rights_to_uref(&UREF_ADD));
343 assert!(context_rights.has_access_rights_to_uref(&UREF_NO_PERMISSIONS));
344 }
345
346 #[test]
347 fn should_check_does_not_have_access_rights_to_uref() {
348 let context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD]);
349 assert!(!context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE));
350 assert!(!context_rights
351 .has_access_rights_to_uref(&URef::new([2; UREF_ADDR_LENGTH], AccessRights::empty())));
352 }
353
354 #[test]
355 fn should_extend_access_rights() {
356 let mut context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_NO_PERMISSIONS]);
358 let mut expected_rights = BTreeMap::new();
359 expected_rights.insert(UREF_ADDRESS, AccessRights::empty());
360 assert_eq!(context_rights.access_rights, expected_rights);
361
362 context_rights.extend(&[UREF_READ_ADD]);
364 *expected_rights.get_mut(&UREF_ADDRESS).unwrap() = AccessRights::READ_ADD;
365 assert_eq!(context_rights.access_rights, expected_rights);
366
367 context_rights.extend(&[UREF_READ]);
369 assert_eq!(context_rights.access_rights, expected_rights);
370
371 context_rights.extend(&[UREF_WRITE]);
373 *expected_rights.get_mut(&UREF_ADDRESS).unwrap() = AccessRights::READ_ADD_WRITE;
374 assert_eq!(context_rights.access_rights, expected_rights);
375 }
376
377 #[test]
378 fn should_perform_union_of_access_rights_in_new() {
379 let context_rights =
380 ContextAccessRights::new(ENTITY_HASH, vec![UREF_NO_PERMISSIONS, UREF_READ, UREF_ADD]);
381
382 let mut expected_rights = BTreeMap::new();
384 expected_rights.insert(UREF_ADDRESS, AccessRights::READ_ADD);
385 assert_eq!(context_rights.access_rights, expected_rights);
386 }
387
388 #[test]
389 fn should_grant_access_rights() {
390 let mut context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD]);
391 let granted_access = context_rights.grant_access(UREF_READ);
392 assert_eq!(granted_access, GrantedAccess::PreExisting);
393 let granted_access = context_rights.grant_access(UREF_READ_ADD_WRITE);
394 assert_eq!(
395 granted_access,
396 GrantedAccess::Granted {
397 uref_addr: UREF_ADDRESS,
398 newly_granted_access_rights: AccessRights::WRITE
399 }
400 );
401 assert!(context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE));
402 let new_uref = URef::new([3; 32], AccessRights::all());
403 let granted_access = context_rights.grant_access(new_uref);
404 assert_eq!(
405 granted_access,
406 GrantedAccess::Granted {
407 uref_addr: new_uref.addr(),
408 newly_granted_access_rights: AccessRights::all()
409 }
410 );
411 assert!(context_rights.has_access_rights_to_uref(&new_uref));
412 }
413
414 #[test]
415 fn should_remove_access_rights() {
416 let mut context_rights = ContextAccessRights::new(ENTITY_HASH, vec![UREF_READ_ADD_WRITE]);
417 assert!(context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE));
418
419 context_rights.remove_access(UREF_ADDRESS, AccessRights::WRITE);
421 assert!(
422 !context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE),
423 "Write access should have been removed"
424 );
425
426 context_rights.remove_access(UREF_ADDRESS, AccessRights::WRITE);
428 assert!(
429 !context_rights.has_access_rights_to_uref(&UREF_READ_ADD_WRITE),
430 "Write access should not have been granted back"
431 );
432 assert!(
433 context_rights.has_access_rights_to_uref(&UREF_READ_ADD),
434 "Read and add access should be preserved."
435 );
436
437 context_rights.remove_access(UREF_ADDRESS, AccessRights::READ_ADD);
439 assert!(
440 !context_rights.has_access_rights_to_uref(&UREF_READ_ADD),
441 "Read and add access should have been removed"
442 );
443 assert!(
444 context_rights.has_access_rights_to_uref(&UREF_NO_PERMISSIONS),
445 "The access rights should be empty"
446 );
447 }
448}