oxide_auth/primitives/
grant.rs

1//! Encapsulates various shared mechanisms for handlings different grants.
2use super::{Url, Time};
3use super::scope::Scope;
4
5use std::borrow::{Cow, ToOwned};
6use std::collections::HashMap;
7use std::collections::hash_map::Iter;
8use std::rc::Rc;
9use std::sync::Arc;
10
11/// Provides a name registry for extensions.
12pub trait GrantExtension {
13    /// An unique identifier distinguishing this extension type for parsing and storing.
14    /// Obvious choices are the registered names as administered by IANA or private identifiers.
15    fn identifier(&self) -> &'static str;
16}
17
18/// Wraps the data for an extension as a string with access restrictions.
19///
20/// This is a generic way for extensions to store their data in a universal, encoded form. It is
21/// also able to indicate the intended readers for such an extension so that backends can ensure
22/// that private extension data is properly encrypted even when present in a self-encoded access
23/// token.
24///
25/// Some extensions have semantics where the presence alone is the stored data, so storing data
26/// is optional and storing no data is distinct from not attaching any extension instance at all.
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub enum Value {
29    /// An extension that the token owner is allowed to read and interpret.
30    Public(Option<String>),
31
32    /// Identifies an extenion whose content and/or existance MUST be kept secret.
33    Private(Option<String>),
34    // Content which is not saved on the server but initialized/interpreted from other sources.
35    // foreign_content: String,
36}
37
38/// Links one or several `GrantExtension` instances to their respective data.
39///
40/// This also serves as a clean interface for both frontend and backend to reliably and
41/// conveniently manipulate or query the stored data sets.
42#[derive(Clone, Default, Debug, PartialEq, Eq)]
43pub struct Extensions {
44    extensions: HashMap<String, Value>,
45}
46
47/// Owning copy of a grant.
48///
49/// This can be stored in a database without worrying about lifetimes or shared across thread
50/// boundaries. A reference to this can be converted to a purely referential `GrantRef`.
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct Grant {
53    /// Identifies the owner of the resource.
54    pub owner_id: String,
55
56    /// Identifies the client to which the grant was issued.
57    pub client_id: String,
58
59    /// The scope granted to the client.
60    pub scope: Scope,
61
62    /// The redirection uri under which the client resides. The url package does indeed seem to
63    /// parse valid URIs as well.
64    pub redirect_uri: Url,
65
66    /// Expiration date of the grant (Utc).
67    pub until: Time,
68
69    /// Encoded extensions existing on this Grant
70    pub extensions: Extensions,
71}
72
73impl Value {
74    /// Creates an extension whose presence and content can be unveiled by the token holder.
75    ///
76    /// Anyone in possession of the token corresponding to such a grant is potentially able to read
77    /// the content of a public extension.
78    pub fn public(content: Option<String>) -> Self {
79        Value::Public(content)
80    }
81
82    /// Creates an extension with secret content only visible for the server.
83    ///
84    /// Token issuers should take special care to protect the content and the identifier of such
85    /// an extension from being interpreted or correlated by the token holder.
86    pub fn private(content: Option<String>) -> Value {
87        Value::Private(content)
88    }
89
90    /// Inspect the public value.
91    ///
92    /// Returns an `Err` if this is not a public extension, `None` if the extension has no value
93    /// but consists only of the key, and `Some(_)` otherwise.
94    pub fn public_value(&self) -> Result<Option<&str>, ()> {
95        match self {
96            Value::Public(Some(content)) => Ok(Some(&content)),
97            Value::Public(None) => Ok(None),
98            _ => Err(()),
99        }
100    }
101
102    /// Convert into the public value.
103    ///
104    /// Returns an `Err` if this is not a public extension, `None` if the extension has no value
105    /// but consists only of the key, and `Some(_)` otherwise.
106    pub fn into_public_value(self) -> Result<Option<String>, ()> {
107        match self {
108            Value::Public(content) => Ok(content),
109            _ => Err(()),
110        }
111    }
112
113    /// Inspect the private value.
114    ///
115    /// Returns an `Err` if this is not a private extension, `None` if the extension has no value
116    /// but consists only of the key, and `Some(_)` otherwise.
117    pub fn private_value(&self) -> Result<Option<&str>, ()> {
118        match self {
119            Value::Private(Some(content)) => Ok(Some(&content)),
120            Value::Private(None) => Ok(None),
121            _ => Err(()),
122        }
123    }
124
125    /// Inspect the private value.
126    ///
127    /// Returns an `Err` if this is not a private extension, `None` if the extension has no value
128    /// but consists only of the key, and `Some(_)` otherwise.
129    pub fn into_private_value(self) -> Result<Option<String>, ()> {
130        match self {
131            Value::Private(content) => Ok(content),
132            _ => Err(()),
133        }
134    }
135}
136
137impl Extensions {
138    /// Create a new extension store.
139    pub fn new() -> Extensions {
140        Extensions::default()
141    }
142
143    /// Set the stored content for a `GrantExtension` instance.
144    pub fn set(&mut self, extension: &dyn GrantExtension, content: Value) {
145        self.extensions
146            .insert(extension.identifier().to_string(), content);
147    }
148
149    /// Set content for an extension without a corresponding instance.
150    pub fn set_raw(&mut self, identifier: String, content: Value) {
151        self.extensions.insert(identifier, content);
152    }
153
154    /// Retrieve the stored data of an instance.
155    ///
156    /// This removes the data from the store to avoid possible mixups and to allow a copyless
157    /// retrieval of bigger data strings.
158    pub fn remove(&mut self, extension: &dyn GrantExtension) -> Option<Value> {
159        self.extensions.remove(extension.identifier())
160    }
161
162    /// Iterate of the public extensions whose presence and content is not secret.
163    pub fn public(&self) -> PublicExtensions {
164        PublicExtensions {
165            iter: self.extensions.iter(),
166        }
167    }
168
169    /// Iterate of the private extensions whose presence and content must not be revealed.
170    pub fn private(&self) -> PrivateExtensions {
171        PrivateExtensions(self.extensions.iter())
172    }
173}
174
175/// An iterator over the public extensions of a grant.
176pub struct PublicExtensions<'a> {
177    iter: Iter<'a, String, Value>,
178}
179
180/// An iterator over the private extensions of a grant.
181///
182/// Implementations which acquire an instance should take special not to leak any secrets to
183/// clients and third parties.
184pub struct PrivateExtensions<'a>(Iter<'a, String, Value>);
185
186impl<'a> Iterator for PublicExtensions<'a> {
187    type Item = (&'a str, Option<&'a str>);
188
189    fn next(&mut self) -> Option<Self::Item> {
190        loop {
191            let (key, value) = self.iter.next()?;
192
193            if let Ok(value) = value.public_value() {
194                return Some((key, value));
195            }
196        }
197    }
198}
199
200impl<'a> Iterator for PrivateExtensions<'a> {
201    type Item = (&'a str, Option<&'a str>);
202
203    fn next(&mut self) -> Option<Self::Item> {
204        loop {
205            let (key, value) = self.0.next()?;
206
207            if let Ok(value) = value.private_value() {
208                return Some((key, value));
209            }
210        }
211    }
212}
213
214impl<'a, T: GrantExtension + ?Sized> GrantExtension for &'a T {
215    fn identifier(&self) -> &'static str {
216        (**self).identifier()
217    }
218}
219
220impl<'a, T: GrantExtension + ?Sized> GrantExtension for Cow<'a, T>
221where
222    T: Clone + ToOwned,
223{
224    fn identifier(&self) -> &'static str {
225        self.as_ref().identifier()
226    }
227}
228
229impl<T: GrantExtension + ?Sized> GrantExtension for Box<T> {
230    fn identifier(&self) -> &'static str {
231        (**self).identifier()
232    }
233}
234
235impl<T: GrantExtension + ?Sized> GrantExtension for Arc<T> {
236    fn identifier(&self) -> &'static str {
237        (**self).identifier()
238    }
239}
240
241impl<T: GrantExtension + ?Sized> GrantExtension for Rc<T> {
242    fn identifier(&self) -> &'static str {
243        (**self).identifier()
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::{Extensions, Value};
250
251    #[test]
252    fn iteration() {
253        let mut extensions = Extensions::new();
254        extensions.set_raw("pub".into(), Value::Public(Some("content".into())));
255        extensions.set_raw("pub_none".into(), Value::Public(None));
256        extensions.set_raw("priv".into(), Value::Private(Some("private".into())));
257        extensions.set_raw("priv_none".into(), Value::Private(None));
258
259        assert_eq!(
260            extensions
261                .public()
262                .filter(|&(name, value)| name == "pub" && value == Some("content"))
263                .count(),
264            1
265        );
266        assert_eq!(
267            extensions
268                .public()
269                .filter(|&(name, value)| name == "pub_none" && value == None)
270                .count(),
271            1
272        );
273        assert_eq!(extensions.public().count(), 2);
274
275        assert_eq!(
276            extensions
277                .private()
278                .filter(|&(name, value)| name == "priv" && value == Some("private"))
279                .count(),
280            1
281        );
282        assert_eq!(
283            extensions
284                .private()
285                .filter(|&(name, value)| name == "priv_none" && value == None)
286                .count(),
287            1
288        );
289        assert_eq!(extensions.private().count(), 2);
290    }
291}