1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[cfg(feature = "diesel-support")]
mod diesel_support {
    pub use super::*;
    pub use diesel::{
        pg::Pg,
        serialize::Output,
        sql_types::Jsonb,
        types::{FromSql, ToSql},
    };
    pub use std::io::Write;

    // This allows you to use it in postgresql.
    impl FromSql<Jsonb, Pg> for Permissions {
        fn from_sql(bytes: Option<&[u8]>) -> diesel::deserialize::Result<Self> {
            let value =
                <serde_json::Value as FromSql<Jsonb, Pg>>::from_sql(bytes)?;
            Ok(serde_json::from_value(value)?)
        }
    }

    impl ToSql<Jsonb, Pg> for Permissions {
        fn to_sql<W: Write>(
            &self,
            out: &mut Output<W, Pg>,
        ) -> diesel::serialize::Result {
            let value = serde_json::to_value(self)?;
            <serde_json::Value as ToSql<Jsonb, Pg>>::to_sql(&value, out)
        }
    }
}
#[cfg(feature = "diesel-support")]
use diesel_support::*;

/// The permissions struct stores a vector of
/// `Permissions`
#[derive(Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd, Default)]
#[cfg_attr(
    feature = "diesel-support",
    derive(FromSqlRow, AsExpression),
    sql_type = "Jsonb"
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Permissions {
    #[cfg_attr(feature = "serde", serde(rename = "permissions"))]
    pub vec: Vec<Permission>,
}

impl Permissions {
    /// To create a new permissions you need to specify a vec to use.
    /// If you don't want this you can use the `default` function
    pub fn new(vec: Vec<Permission>) -> Self {
        Self { vec }
    }

    /// This function gets wether the permissions at
    /// `path` have the desired permissions.
    pub fn get_permission(&self, path: &str, r#mut: bool) -> bool {
        // Variable to store the closest path that we have
        // found.
        let mut best = None;
        // Variable to store how many path segments match
        let mut bestn = 0;
        // Splits the required path by path separators
        let path: Vec<&str> = path.split('/').collect();

        // Check the len isn't 0
        if path.is_empty() {
            return false;
        }

        // Begins a loop through the vec
        for i in &self.vec {
            // Gets the path, split by path separators
            let ipath: Vec<&str> = i.path.split('/').collect();
            let mut matches = 0;
            // Loops through the path separators.
            for path_segment in ipath {
                // Checks for a match
                if path_segment
                    == match path.get(matches) {
                        Some(m) => *m,
                        None => break,
                    }
                {
                    matches += 1;
                    // Checks if we have suppassed the required
                    // ammount of matches.
                    if bestn < matches {
                        // And if so makes this the new best.
                        bestn = matches;
                        best = Some(i);
                        // Checks if we already have a full
                        // match.
                        if matches == path.len() {
                            break;
                        }
                    }
                } else if matches == 0 {
                    break;
                }
            }
        }

        // Now check if there even was a match
        if let Some(best) = best {
            // And if so check if its permissions are valid
            return best.auth && (best.r#mut || !r#mut);
        }

        // Return false if in doubt.
        false
    }
}

// Its just easier to do it this way.
impl From<Vec<Permission>> for Permissions {
    fn from(vec: Vec<Permission>) -> Self {
        Self { vec }
    }
}

/// The permission struct stores an individual
/// permission.
/// This contains information about a user accessing
/// certain areas of a program.
///
/// It can be configured in toml like below:
/// First specify an individual element in a
/// `permissions` array.
/// In this you need to specify a path `path`,
/// and wether the user has permission `auth`.
/// Then you can specify mutability `mut`.
///
/// ```toml
/// # Create an array element
/// [[permissions]]
/// # Specify the path
/// path = "/contacts"
/// # Specify the authentication
/// auth = true
/// # Specify if they can mutate the contacts
/// mut = true
/// ```
///
/// This creates a permission in the `/contacts`
/// path, where the user can both read and mutate it.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Permission {
    pub path: String,
    pub auth: bool,
    #[cfg_attr(feature = "serde", serde(default))]
    pub r#mut: bool,
}

impl Permission {
    /// Creates a new permission.
    pub fn new(path: String, auth: bool, r#mut: bool) -> Self {
        Self { path, auth, r#mut }
    }
}