1
use actionable::Permissions;
2
use serde::{Deserialize, Serialize};
3

            
4
use crate::admin::group;
5
use crate::connection::{AsyncStorageConnection, Connection, IdentityReference, StorageConnection};
6
use crate::define_basic_unique_mapped_view;
7
use crate::document::{CollectionDocument, Emit};
8
use crate::schema::{Collection, Nameable, NamedCollection, SerializedCollection};
9

            
10
/// An assignable role, which grants permissions based on the associated [`PermissionGroup`](crate::admin::PermissionGroup)s.
11
115720
#[derive(Clone, Debug, Serialize, Deserialize, Collection)]
12
#[collection(name = "role", authority="khonsulabs", views = [ByName], core = crate)]
13
#[must_use]
14
pub struct Role {
15
    /// The name of the role. Must be unique.
16
    pub name: String,
17
    /// The IDs of the permission groups this role belongs to.
18
    pub groups: Vec<u64>,
19
}
20

            
21
impl Role {
22
    /// Returns a new role with no groups and the name provided.
23
16
    pub fn named<S: Into<String>>(name: S) -> Self {
24
16
        Self {
25
16
            name: name.into(),
26
16
            groups: Vec::new(),
27
16
        }
28
16
    }
29

            
30
    /// Builder-style method. Returns self after replacing the current groups with `ids`.
31
    pub fn with_group_ids<I: IntoIterator<Item = u64>>(mut self, ids: I) -> Self {
32
        self.groups = ids.into_iter().collect();
33
        self
34
    }
35

            
36
    pub fn assume_identity<'name, Storage: StorageConnection>(
37
        name_or_id: impl Nameable<'name, u64>,
38
        storage: &Storage,
39
    ) -> Result<Storage::Authenticated, crate::Error> {
40
        storage.assume_identity(IdentityReference::Role(name_or_id.name()?))
41
    }
42

            
43
2
    pub async fn assume_identity_async<'name, Storage: AsyncStorageConnection>(
44
2
        name_or_id: impl Nameable<'name, u64> + Send,
45
2
        storage: &Storage,
46
2
    ) -> Result<Storage::Authenticated, crate::Error> {
47
2
        storage
48
2
            .assume_identity(IdentityReference::Role(name_or_id.name()?))
49
2
            .await
50
2
    }
51

            
52
    /// Calculates the effective permissions based on the groups this role is assigned.
53
226
    pub fn effective_permissions<C: Connection>(
54
226
        &self,
55
226
        admin: &C,
56
226
        inherit_permissions: &Permissions,
57
226
    ) -> Result<Permissions, crate::Error> {
58
226
        let groups = group::PermissionGroup::get_multiple(&self.groups, admin)?;
59

            
60
        // Combine the permissions from all the groups into one.
61
226
        let merged_permissions = Permissions::merged(
62
226
            groups
63
226
                .into_iter()
64
226
                .map(|group| Permissions::from(group.contents.statements))
65
226
                .collect::<Vec<_>>()
66
226
                .iter()
67
226
                .chain(std::iter::once(inherit_permissions)),
68
226
        );
69
226

            
70
226
        Ok(merged_permissions)
71
226
    }
72
}
73

            
74
impl NamedCollection for Role {
75
    type ByNameView = ByName;
76
}
77

            
78
define_basic_unique_mapped_view!(
79
    ByName,
80
    Role,
81
    1,
82
    "by-name",
83
    String,
84
720
    |document: CollectionDocument<Role>| { document.header.emit_key(document.contents.name) }
85
);