1
use std::{
2
    borrow::Cow,
3
    collections::HashMap,
4
    convert::TryFrom,
5
    fmt::{Display, Formatter, Write},
6
    hash::Hash,
7
};
8

            
9
use serde::{Deserialize, Serialize};
10

            
11
use super::{Action, ActionName};
12

            
13
/// A statement of permissions. A statement describes whether one or more
14
/// `actions` should be `allowed` to be taken against `resources`.
15
#[derive(Clone, Debug, Serialize, Deserialize)]
16
#[must_use]
17
pub struct Statement {
18
    /// The list of resources this statement applies to.
19
    pub resources: Vec<ResourceName<'static>>,
20
    /// The list of actions this statement applies to.
21
    pub actions: Option<ActionNameList>,
22
    /// Any configured values for these resources.
23
    pub configuration: Option<HashMap<String, Configuration>>,
24
}
25

            
26
impl Statement {
27
    /// Returns a statement that allows [`ActionNameList::All`] against
28
    /// [`ResourceName::any()`].
29
    pub fn allow_all_for_any_resource() -> Self {
30
        Self::for_any().allowing_all()
31
    }
32

            
33
    /// Returns an empty statement for a resource named `name`.
34
15
    pub fn for_resource(name: impl Into<ResourceName<'static>>) -> Self {
35
15
        Self {
36
15
            resources: vec![name.into()],
37
15
            actions: None,
38
15
            configuration: None,
39
15
        }
40
15
    }
41

            
42
    /// Returns an empty statement for [`ResourceName::any()`].
43
4
    pub fn for_any() -> Self {
44
4
        Self {
45
4
            resources: vec![ResourceName::any()],
46
4
            actions: None,
47
4
            configuration: None,
48
4
        }
49
4
    }
50

            
51
    /// Returns an empty statement for a resources named `names`.
52
    pub fn for_resources<II: IntoIterator<Item = ResourceName<'static>>>(names: II) -> Self {
53
        Self {
54
            resources: names.into_iter().collect(),
55
            actions: None,
56
            configuration: None,
57
        }
58
    }
59

            
60
    /// Allows `action` to be performed.
61
    pub fn allow<A: Action>(&mut self, action: &A) {
62
1
        match &mut self.actions {
63
            Some(ActionNameList::All) => {}
64
1
            Some(ActionNameList::List(names)) => {
65
1
                names.push(action.name());
66
1
            }
67
7
            None => {
68
7
                self.actions = Some(ActionNameList::List(vec![action.name()]));
69
7
            }
70
        }
71
8
    }
72

            
73
    /// Allows `action` and returns self.
74
8
    pub fn allowing<A: Action>(mut self, action: &A) -> Self {
75
8
        self.allow(action);
76
8
        self
77
8
    }
78

            
79
    /// Allows [`ActionNameList::All`].
80
6
    pub fn allow_all(&mut self) {
81
6
        self.actions = Some(ActionNameList::All);
82
6
    }
83

            
84
    /// Allows [`ActionNameList::All`] and returns self.
85
6
    pub fn allowing_all(mut self) -> Self {
86
6
        self.allow_all();
87
6
        self
88
6
    }
89

            
90
    /// Sets `configuration` for `key` for the resources in this statement.
91
6
    pub fn configure<S: Into<String>, C: Into<Configuration>>(&mut self, key: S, configuration: C) {
92
6
        let configurations = self.configuration.get_or_insert_with(HashMap::default);
93
6
        configurations.insert(key.into(), configuration.into());
94
6
    }
95

            
96
    /// Configures `configuration` for `key` and returns self.
97
6
    pub fn with<S: Into<String>, C: Into<Configuration>>(
98
6
        mut self,
99
6
        key: S,
100
6
        configuration: C,
101
6
    ) -> Self {
102
6
        self.configure(key, configuration);
103
6
        self
104
6
    }
105
}
106

            
107
/// A single element of a [`ResourceName`]
108
18
#[derive(Debug, Clone, Serialize, Deserialize)]
109
pub enum Identifier<'a> {
110
    /// When checking for allowed permissions, allow any match where this
111
    /// identifier is used.
112
    Any,
113
    /// An integer identifier.
114
    Integer(u64),
115
    /// A string identifier.
116
    String(Cow<'a, str>),
117
    /// A binary identifier.
118
    Bytes(Cow<'a, [u8]>),
119
}
120

            
121
impl<'a> Hash for Identifier<'a> {
122
122
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
123
122
        // To ensure matching the implementation of Eq, we need to hash
124
122
        // everything to bytes in the same way they would be compared.
125
122
        match self {
126
28
            Identifier::Any => {
127
28
                // We get to pick an arbitrary hash for this value. It only
128
28
                // needs to be self consistent. A null byte is likely to be
129
28
                // unique in terms of produced hash values.
130
28
                state.write_u8(0);
131
28
            }
132
14
            Identifier::Integer(int) => {
133
14
                state.write(&int.to_be_bytes());
134
14
            }
135
78
            Identifier::String(string) => {
136
78
                state.write(string.as_bytes());
137
78
            }
138
2
            Identifier::Bytes(bytes) => {
139
2
                state.write(bytes);
140
2
            }
141
        }
142
122
    }
143
}
144

            
145
1
#[test]
146
1
fn identifier_hash_tests() {
147
14
    fn hash(identifier: &Identifier<'_>) -> u64 {
148
14
        use std::hash::Hasher;
149
14
        let mut hasher = std::collections::hash_map::DefaultHasher::default();
150
14
        identifier.hash(&mut hasher);
151
14
        hasher.finish()
152
14
    }
153
1

            
154
1
    let integer_a = Identifier::from(u64::from_be_bytes(*b"helloooo"));
155
1
    let string_a = Identifier::from("helloooo");
156
1
    let bytes_a = Identifier::from(b"helloooo");
157
1
    let string_b = Identifier::from("woooorld");
158
1

            
159
1
    assert_eq!(hash(&Identifier::Any), hash(&Identifier::Any));
160
1
    assert_eq!(hash(&string_a), hash(&string_a));
161
1
    assert_eq!(hash(&integer_a), hash(&string_a));
162
1
    assert_eq!(hash(&bytes_a), hash(&string_a));
163
1
    assert_ne!(hash(&string_a), hash(&string_b));
164
1
    assert_ne!(hash(&integer_a), hash(&string_b));
165
1
    assert_ne!(hash(&bytes_a), hash(&string_b));
166
1
}
167

            
168
impl<'a> Eq for Identifier<'a> {}
169

            
170
impl<'a> PartialEq for Identifier<'a> {
171
78
    fn eq(&self, other: &Self) -> bool {
172
78
        match other {
173
14
            Self::Any => matches!(self, Self::Any),
174
12
            Self::Integer(int) => self.eq_int(*int),
175
45
            Self::String(string) => self.eq_str(string),
176
7
            Self::Bytes(bytes) => self.eq_bytes(bytes),
177
        }
178
78
    }
179
}
180

            
181
impl<'a> Identifier<'a> {
182
    /// Convert this identifier to an un-borrowed identifier.
183
    #[must_use]
184
3
    pub fn to_owned(&self) -> Identifier<'static> {
185
3
        match self {
186
            Self::Any => Identifier::Any,
187
3
            Self::Integer(value) => Identifier::Integer(*value),
188
            Self::String(value) => Identifier::String(Cow::Owned(value.to_string())),
189
            Self::Bytes(value) => Identifier::Bytes(Cow::Owned(value.to_vec())),
190
        }
191
3
    }
192

            
193
12
    fn eq_int(&self, other: u64) -> bool {
194
12
        match self {
195
1
            Identifier::Any => false,
196
7
            Identifier::Integer(int) => *int == other,
197
2
            Identifier::String(string) => {
198
2
                let other = other.to_be_bytes();
199
2
                string.as_bytes() == other
200
            }
201
2
            Identifier::Bytes(bytes) => {
202
2
                let other = other.to_be_bytes();
203
2
                **bytes == other
204
            }
205
        }
206
12
    }
207

            
208
45
    fn eq_str(&self, other: &str) -> bool {
209
45
        match self {
210
2
            Identifier::Any => false,
211
2
            Identifier::Integer(int) => {
212
2
                let int = int.to_be_bytes();
213
2
                int == other.as_bytes()
214
            }
215
39
            Identifier::String(string) => string == other,
216
2
            Identifier::Bytes(bytes) => &**bytes == other.as_bytes(),
217
        }
218
45
    }
219

            
220
7
    fn eq_bytes(&self, other: &[u8]) -> bool {
221
7
        match self {
222
1
            Identifier::Any => false,
223
2
            Identifier::Integer(int) => {
224
2
                let int = int.to_be_bytes();
225
2
                int == other
226
            }
227
2
            Identifier::String(string) => string.as_bytes() == other,
228
2
            Identifier::Bytes(bytes) => &**bytes == other,
229
        }
230
7
    }
231
}
232

            
233
1
#[test]
234
1
fn identifier_equality_tests() {
235
1
    let integer_a = Identifier::from(u64::from_be_bytes(*b"helloooo"));
236
1
    let integer_b = Identifier::from(u64::from_be_bytes(*b"woooorld"));
237
1
    let string_a = Identifier::from("helloooo");
238
1
    let string_b = Identifier::from("woooorld");
239
1
    let bytes_a = Identifier::from(b"helloooo");
240
1
    let bytes_b = Identifier::from(b"woooorld");
241
1

            
242
1
    // Integer on left
243
1
    assert_ne!(integer_a, Identifier::Any);
244
1
    assert_eq!(integer_a, integer_a);
245
1
    assert_eq!(integer_a, string_a);
246
1
    assert_eq!(integer_a, bytes_a);
247
1
    assert_ne!(integer_a, integer_b);
248
1
    assert_ne!(integer_a, string_b);
249
1
    assert_ne!(integer_a, bytes_b);
250

            
251
    // String on left
252
1
    assert_ne!(string_a, Identifier::Any);
253
1
    assert_eq!(string_a, integer_a);
254
1
    assert_eq!(string_a, string_a);
255
1
    assert_eq!(string_a, bytes_a);
256
1
    assert_ne!(string_a, integer_b);
257
1
    assert_ne!(string_a, string_b);
258
1
    assert_ne!(string_a, bytes_b);
259

            
260
    // Bytes on left
261
1
    assert_ne!(bytes_a, Identifier::Any);
262
1
    assert_eq!(bytes_a, integer_a);
263
1
    assert_eq!(bytes_a, string_a);
264
1
    assert_eq!(bytes_a, bytes_a);
265
1
    assert_ne!(bytes_a, integer_b);
266
1
    assert_ne!(bytes_a, string_b);
267
1
    assert_ne!(bytes_a, bytes_b);
268

            
269
    // Any on left
270
1
    assert_eq!(Identifier::Any, Identifier::Any);
271
1
    assert_ne!(Identifier::Any, integer_a);
272
1
    assert_ne!(Identifier::Any, string_a);
273
1
    assert_ne!(Identifier::Any, bytes_a);
274
1
}
275

            
276
impl<'a> Display for Identifier<'a> {
277
11
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278
11
        match self {
279
1
            Self::Any => f.write_char('*'),
280
2
            Self::Integer(integer) => integer.fmt(f),
281
4
            Self::String(string) => string.fmt(f),
282
4
            Self::Bytes(bytes) => {
283
4
                f.write_char('$')?;
284
20
                for byte in bytes.iter() {
285
20
                    write!(f, "{:02x}", byte)?;
286
                }
287
4
                Ok(())
288
            }
289
        }
290
11
    }
291
}
292

            
293
1
#[test]
294
1
fn identifier_display_tests() {
295
1
    assert_eq!(Identifier::Any.to_string(), "*");
296
1
    assert_eq!(Identifier::from(1).to_string(), "1");
297
1
    assert_eq!(Identifier::from("string").to_string(), "string");
298
1
    assert_eq!(Identifier::from(b"bytes").to_string(), "$6279746573");
299
1
}
300

            
301
impl<'a> From<u64> for Identifier<'a> {
302
17
    fn from(id: u64) -> Self {
303
17
        Self::Integer(id)
304
17
    }
305
}
306

            
307
impl<'a> From<&'a str> for Identifier<'a> {
308
61
    fn from(id: &'a str) -> Self {
309
61
        Self::String(Cow::Borrowed(id))
310
61
    }
311
}
312

            
313
impl<'a> From<&'a String> for Identifier<'a> {
314
1
    fn from(id: &'a String) -> Self {
315
1
        Self::from(id.as_str())
316
1
    }
317
}
318

            
319
impl<'a> From<String> for Identifier<'a> {
320
1
    fn from(id: String) -> Self {
321
1
        Self::String(Cow::Owned(id))
322
1
    }
323
}
324

            
325
impl<'a, const N: usize> From<&'a [u8; N]> for Identifier<'a> {
326
5
    fn from(id: &'a [u8; N]) -> Self {
327
5
        Self::from(&id[..])
328
5
    }
329
}
330

            
331
impl<'a, const N: usize> From<[u8; N]> for Identifier<'a> {
332
1
    fn from(id: [u8; N]) -> Self {
333
1
        Self::from(id.to_vec())
334
1
    }
335
}
336

            
337
impl<'a> From<&'a [u8]> for Identifier<'a> {
338
5
    fn from(id: &'a [u8]) -> Self {
339
5
        Self::Bytes(Cow::Borrowed(id))
340
5
    }
341
}
342

            
343
impl<'a> From<&'a Vec<u8>> for Identifier<'a> {
344
1
    fn from(id: &'a Vec<u8>) -> Self {
345
1
        Self::from(id.clone())
346
1
    }
347
}
348

            
349
impl<'a> From<Vec<u8>> for Identifier<'a> {
350
2
    fn from(id: Vec<u8>) -> Self {
351
2
        Self::Bytes(Cow::Owned(id))
352
2
    }
353
}
354

            
355
1
#[test]
356
1
fn identifier_from_tests() {
357
1
    assert_eq!(Identifier::from(1).to_string(), "1");
358
1
    assert_eq!(Identifier::from("string").to_string(), "string");
359
1
    assert_eq!(
360
1
        Identifier::from(&String::from("string")).to_string(),
361
1
        "string"
362
1
    );
363
1
    assert_eq!(
364
1
        Identifier::from(String::from("string")).to_string(),
365
1
        "string"
366
1
    );
367
    // This calls through to from(&[u8])
368
1
    assert_eq!(Identifier::from(b"bytes").to_string(), "$6279746573");
369
    // This calls through to from(Vec<u8>)
370
1
    assert_eq!(Identifier::from(*b"bytes").to_string(), "$6279746573");
371
1
    assert_eq!(
372
1
        Identifier::from(&b"bytes".to_vec()).to_string(),
373
1
        "$6279746573"
374
1
    );
375
1
}
376

            
377
/// A list of [`ActionName`]s.
378
#[derive(Clone, Debug, Serialize, Deserialize)]
379
pub enum ActionNameList {
380
    /// A specific list of names.
381
    List(Vec<ActionName>),
382
    /// All actions.
383
    All,
384
}
385

            
386
impl<T> From<T> for ActionNameList
387
where
388
    T: Action,
389
{
390
    fn from(action: T) -> Self {
391
        Self::List(vec![action.name()])
392
    }
393
}
394

            
395
impl<T> From<Vec<T>> for ActionNameList
396
where
397
    T: Action,
398
{
399
    fn from(actions: Vec<T>) -> Self {
400
        Self::List(actions.into_iter().map(|action| action.name()).collect())
401
    }
402
}
403

            
404
/// A configured value for a resource.
405
11
#[derive(Debug, Clone, Serialize, Deserialize)]
406
pub enum Configuration {
407
    /// An unsigned integer configuration value.
408
    Unsigned(u64),
409
    /// A signed integer configuration value.
410
    Signed(i64),
411
    /// A string configuration value.
412
    String(String),
413
}
414

            
415
impl Configuration {
416
    /// Evaluates the contents of this configuration as a signed integer.
417
    /// Returns None if unable to convert safely.
418
    #[must_use]
419
3
    pub fn to_signed(&self) -> Option<i64> {
420
3
        match self {
421
1
            Configuration::Unsigned(unsigned) => i64::try_from(*unsigned).ok(),
422
2
            Configuration::Signed(signed) => Some(*signed),
423
            Configuration::String(string) => string.parse().ok(),
424
        }
425
3
    }
426

            
427
    /// Evaluates the contents of this configuration as an unsigned integer.
428
    /// Returns None if unable to convert safely.
429
    #[must_use]
430
2
    pub fn to_unsigned(&self) -> Option<u64> {
431
2
        match self {
432
1
            Configuration::Unsigned(unsigned) => Some(*unsigned),
433
1
            Configuration::Signed(signed) => u64::try_from(*signed).ok(),
434
            Configuration::String(string) => string.parse().ok(),
435
        }
436
2
    }
437
}
438

            
439
impl Display for Configuration {
440
4
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
441
4
        match self {
442
1
            Configuration::Unsigned(unsigned) => unsigned.fmt(f),
443
            Configuration::Signed(signed) => signed.fmt(f),
444
3
            Configuration::String(string) => string.fmt(f),
445
        }
446
4
    }
447
}
448

            
449
impl From<u64> for Configuration {
450
1
    fn from(value: u64) -> Self {
451
1
        Self::Unsigned(value)
452
1
    }
453
}
454

            
455
impl From<i64> for Configuration {
456
2
    fn from(value: i64) -> Self {
457
2
        Self::Signed(value)
458
2
    }
459
}
460

            
461
impl From<String> for Configuration {
462
    fn from(value: String) -> Self {
463
        Self::String(value)
464
    }
465
}
466

            
467
impl<'a> From<&'a str> for Configuration {
468
3
    fn from(value: &'a str) -> Self {
469
3
        Self::String(value.to_string())
470
3
    }
471
}
472

            
473
/// A unique name/identifier of a resource.
474
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
475
pub struct ResourceName<'a>(Vec<Identifier<'a>>);
476

            
477
impl<'a> ResourceName<'a> {
478
    /// Convert a borrowed name to an un-borrwed name.
479
    #[must_use]
480
3
    pub fn to_owned(&self) -> ResourceName<'static> {
481
3
        ResourceName(self.0.iter().map(Identifier::to_owned).collect())
482
3
    }
483
}
484

            
485
impl<'a> Display for ResourceName<'a> {
486
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
487
        for (index, identifier) in self.0.iter().enumerate() {
488
            if index > 0 {
489
                f.write_char('.')?;
490
            }
491

            
492
            identifier.fmt(f)?;
493
        }
494

            
495
        Ok(())
496
    }
497
}
498

            
499
impl<'a> ResourceName<'a> {
500
    /// Creates a `ResourceName` that matches any identifier.
501
    #[must_use]
502
4
    pub fn any() -> Self {
503
4
        Self::named(Identifier::Any)
504
4
    }
505

            
506
    /// Creates a `ResourceName` with `name`.
507
    #[must_use]
508
54
    pub fn named<I: Into<Identifier<'a>>>(name: I) -> Self {
509
54
        Self(vec![name.into()])
510
54
    }
511

            
512
    /// Adds another name segment.
513
    #[must_use]
514
13
    pub fn and<I: Into<Identifier<'a>>>(mut self, name: I) -> Self {
515
13
        self.0.push(name.into());
516
13
        self
517
13
    }
518
}
519

            
520
impl<'a> AsRef<[Identifier<'a>]> for ResourceName<'a> {
521
41
    fn as_ref(&self) -> &[Identifier<'a>] {
522
41
        &self.0
523
41
    }
524
}
525

            
526
impl<'a> IntoIterator for ResourceName<'a> {
527
    type IntoIter = std::vec::IntoIter<Identifier<'a>>;
528
    type Item = Identifier<'a>;
529

            
530
19
    fn into_iter(self) -> Self::IntoIter {
531
19
        self.0.into_iter()
532
19
    }
533
}
534

            
535
impl<'b, 'a> From<&'b [Identifier<'a>]> for ResourceName<'a> {
536
3
    fn from(parts: &'b [Identifier<'a>]) -> Self {
537
3
        Self(parts.to_vec())
538
3
    }
539
}
540

            
541
impl<'a> From<&'a str> for ResourceName<'a> {
542
2
    fn from(name: &'a str) -> Self {
543
2
        Self(vec![Identifier::from(name)])
544
2
    }
545
}
546

            
547
impl<'a> From<u64> for ResourceName<'a> {
548
1
    fn from(name: u64) -> Self {
549
1
        Self(vec![Identifier::from(name)])
550
1
    }
551
}