oxide_auth/endpoint/
query.rs

1use std::borrow::{Borrow, Cow};
2use std::collections::HashMap;
3use std::fmt;
4use std::iter::FromIterator;
5use std::hash::{BuildHasher, Hash};
6use std::rc::Rc;
7use std::sync::Arc;
8
9use serde::de;
10use serde::Deserializer;
11
12/// Allows access to the query parameters in an url or a body.
13///
14/// Use one of the listed implementations below. Since those may be a bit confusing due to their
15/// abundant use of generics, basically use any type of `HashMap` that maps 'str-likes' to a
16/// collection of other 'str-likes'. Popular instances may be:
17/// * `HashMap<String, String>`
18/// * `HashMap<String, Vec<String>>`
19/// * `HashMap<Cow<'static, str>, Cow<'static, str>>`
20///
21/// You should generally not have to implement this trait yourself, and if you do there are
22/// additional requirements on your implementation to guarantee standard conformance. Therefore the
23/// trait is marked as `unsafe`.
24pub unsafe trait QueryParameter {
25    /// Get the **unique** value associated with a key.
26    ///
27    /// If there are multiple values, return `None`. This is very important to guarantee
28    /// conformance to the RFC. Afaik it prevents potentially subverting validation middleware,
29    /// order dependent processing, or simple confusion between different components who parse the
30    /// query string from different ends.
31    fn unique_value(&self, key: &str) -> Option<Cow<str>>;
32
33    /// Guarantees that one can grab an owned copy.
34    fn normalize(&self) -> NormalizedParameter;
35}
36
37/// The query parameter normal form.
38///
39/// When a request wants to give access to its query or body parameters by reference, it can do so
40/// by a reference of the particular trait. But when the representation of the query is not stored
41/// in the memory associated with the request, it needs to be allocated to outlive the borrow on
42/// the request.  This allocation may as well perform the minimization/normalization into a
43/// representation actually consumed by the backend. This normal form thus encapsulates the
44/// associated `clone-into-normal form` by various possible constructors from references [WIP].
45///
46/// This gives rise to a custom `Cow<QueryParameter>` instance by requiring that normalization into
47/// memory with unrelated lifetime is always possible.
48///
49/// Internally a hashmap but this may change due to optimizations.
50#[derive(Clone, Debug, Default)]
51pub struct NormalizedParameter {
52    /// The value is `None` if the key appeared at least twice.
53    inner: HashMap<Cow<'static, str>, Option<Cow<'static, str>>>,
54}
55
56unsafe impl QueryParameter for NormalizedParameter {
57    fn unique_value(&self, key: &str) -> Option<Cow<str>> {
58        self.inner
59            .get(key)
60            .and_then(|val| val.as_ref().map(Cow::as_ref).map(Cow::Borrowed))
61    }
62
63    fn normalize(&self) -> NormalizedParameter {
64        self.clone()
65    }
66}
67
68impl NormalizedParameter {
69    /// Create an empty map.
70    pub fn new() -> Self {
71        NormalizedParameter::default()
72    }
73
74    /// Insert a key-value-pair or mark key as dead if already present.
75    ///
76    /// Since each key must appear at most once, we do not remove it from the map but instead mark
77    /// the key as having a duplicate entry.
78    pub fn insert_or_poison(&mut self, key: Cow<'static, str>, val: Cow<'static, str>) {
79        let unique_val = Some(val);
80        self.inner
81            .entry(key)
82            .and_modify(|val| *val = None)
83            .or_insert(unique_val);
84    }
85}
86
87impl Borrow<dyn QueryParameter> for NormalizedParameter {
88    fn borrow(&self) -> &(dyn QueryParameter + 'static) {
89        self
90    }
91}
92
93impl Borrow<dyn QueryParameter + Send> for NormalizedParameter {
94    fn borrow(&self) -> &(dyn QueryParameter + Send + 'static) {
95        self
96    }
97}
98
99impl<'de> de::Deserialize<'de> for NormalizedParameter {
100    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101    where
102        D: Deserializer<'de>,
103    {
104        struct Visitor(NormalizedParameter);
105
106        impl<'a> de::Visitor<'a> for Visitor {
107            type Value = NormalizedParameter;
108
109            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
110                write!(f, "a sequence of key-value-pairs")
111            }
112
113            fn visit_seq<A>(mut self, mut access: A) -> Result<Self::Value, A::Error>
114            where
115                A: de::SeqAccess<'a>,
116            {
117                while let Some((key, value)) = access.next_element::<(String, String)>()? {
118                    self.0.insert_or_poison(key.into(), value.into())
119                }
120
121                Ok(self.0)
122            }
123        }
124
125        let visitor = Visitor(NormalizedParameter::default());
126        deserializer.deserialize_seq(visitor)
127    }
128}
129
130impl<K, V> FromIterator<(K, V)> for NormalizedParameter
131where
132    K: Into<Cow<'static, str>>,
133    V: Into<Cow<'static, str>>,
134{
135    fn from_iter<T>(iter: T) -> Self
136    where
137        T: IntoIterator<Item = (K, V)>,
138    {
139        let mut target = NormalizedParameter::default();
140        iter.into_iter()
141            .for_each(|(k, v)| target.insert_or_poison(k.into(), v.into()));
142        target
143    }
144}
145
146impl ToOwned for dyn QueryParameter {
147    type Owned = NormalizedParameter;
148
149    fn to_owned(&self) -> Self::Owned {
150        self.normalize()
151    }
152}
153
154impl ToOwned for dyn QueryParameter + Send {
155    type Owned = NormalizedParameter;
156
157    fn to_owned(&self) -> Self::Owned {
158        self.normalize()
159    }
160}
161
162/// Return a reference to value in a collection if it is the only one.
163///
164/// For example, a vector of string like types returns a reference to its first
165/// element if there are no other, else it returns `None`.
166///
167/// If this were done with slices, that would require choosing a particular
168/// value type of the underlying slice e.g. `[String]`.
169pub unsafe trait UniqueValue {
170    /// Borrow the unique value reference.
171    fn get_unique(&self) -> Option<&str>;
172}
173
174unsafe impl<K, V, S: BuildHasher> QueryParameter for HashMap<K, V, S>
175where
176    K: Borrow<str> + Eq + Hash,
177    V: UniqueValue + Eq + Hash,
178{
179    fn unique_value(&self, key: &str) -> Option<Cow<str>> {
180        self.get(key).and_then(V::get_unique).map(Cow::Borrowed)
181    }
182
183    fn normalize(&self) -> NormalizedParameter {
184        let inner = self
185            .iter()
186            .filter_map(|(key, val)| {
187                val.get_unique().map(|value| {
188                    (
189                        Cow::Owned(key.borrow().to_string()),
190                        Some(Cow::Owned(value.to_string())),
191                    )
192                })
193            })
194            .collect();
195
196        NormalizedParameter { inner }
197    }
198}
199
200unsafe impl<K, V> QueryParameter for Vec<(K, V)>
201where
202    K: Borrow<str> + Eq + Hash,
203    V: Borrow<str> + Eq + Hash,
204{
205    fn unique_value(&self, key: &str) -> Option<Cow<str>> {
206        let mut value = None;
207
208        for entry in self.iter() {
209            if entry.0.borrow() == key {
210                if value.is_some() {
211                    return None;
212                }
213                value = Some(Cow::Borrowed(entry.1.borrow()));
214            }
215        }
216
217        value
218    }
219
220    fn normalize(&self) -> NormalizedParameter {
221        let mut params = NormalizedParameter::default();
222        self.iter()
223            .map(|&(ref key, ref val)| {
224                (
225                    Cow::Owned(key.borrow().to_string()),
226                    Cow::Owned(val.borrow().to_string()),
227                )
228            })
229            .for_each(|(key, val)| params.insert_or_poison(key, val));
230        params
231    }
232}
233
234unsafe impl<'a, Q: QueryParameter + 'a + ?Sized> QueryParameter for &'a Q {
235    fn unique_value(&self, key: &str) -> Option<Cow<str>> {
236        (**self).unique_value(key)
237    }
238
239    fn normalize(&self) -> NormalizedParameter {
240        (**self).normalize()
241    }
242}
243
244unsafe impl<'a, Q: QueryParameter + 'a + ?Sized> QueryParameter for &'a mut Q {
245    fn unique_value(&self, key: &str) -> Option<Cow<str>> {
246        (**self).unique_value(key)
247    }
248
249    fn normalize(&self) -> NormalizedParameter {
250        (**self).normalize()
251    }
252}
253
254unsafe impl UniqueValue for str {
255    fn get_unique(&self) -> Option<&str> {
256        Some(self)
257    }
258}
259
260unsafe impl UniqueValue for String {
261    fn get_unique(&self) -> Option<&str> {
262        Some(&self)
263    }
264}
265
266unsafe impl<'a, V> UniqueValue for &'a V
267where
268    V: AsRef<str> + ?Sized,
269{
270    fn get_unique(&self) -> Option<&str> {
271        Some(self.as_ref())
272    }
273}
274
275unsafe impl<'a> UniqueValue for Cow<'a, str> {
276    fn get_unique(&self) -> Option<&str> {
277        Some(self.as_ref())
278    }
279}
280
281unsafe impl<V: UniqueValue> UniqueValue for Option<V> {
282    fn get_unique(&self) -> Option<&str> {
283        self.as_ref().and_then(V::get_unique)
284    }
285}
286
287unsafe impl<V: UniqueValue> UniqueValue for [V] {
288    fn get_unique(&self) -> Option<&str> {
289        if self.len() > 1 {
290            None
291        } else {
292            self.get(0).and_then(V::get_unique)
293        }
294    }
295}
296
297unsafe impl<V: UniqueValue + ?Sized> UniqueValue for Box<V> {
298    fn get_unique(&self) -> Option<&str> {
299        (**self).get_unique()
300    }
301}
302
303unsafe impl<V: UniqueValue + ?Sized> UniqueValue for Rc<V> {
304    fn get_unique(&self) -> Option<&str> {
305        (**self).get_unique()
306    }
307}
308
309unsafe impl<V: UniqueValue + ?Sized> UniqueValue for Arc<V> {
310    fn get_unique(&self) -> Option<&str> {
311        (**self).get_unique()
312    }
313}
314
315unsafe impl<V: UniqueValue> UniqueValue for Vec<V> {
316    fn get_unique(&self) -> Option<&str> {
317        if self.len() > 1 {
318            None
319        } else {
320            self.get(0).and_then(V::get_unique)
321        }
322    }
323}
324
325mod test {
326    use super::*;
327
328    /// Compilation tests for various possible QueryParameter impls.
329    #[allow(unused)]
330    #[allow(dead_code)]
331    fn test_query_parameter_impls() {
332        let _ = (&HashMap::<String, String>::new()) as &dyn QueryParameter;
333        let _ = (&HashMap::<&'static str, &'static str>::new()) as &dyn QueryParameter;
334        let _ = (&HashMap::<Cow<'static, str>, Cow<'static, str>>::new()) as &dyn QueryParameter;
335
336        let _ = (&HashMap::<String, Vec<String>>::new()) as &dyn QueryParameter;
337        let _ = (&HashMap::<String, Box<String>>::new()) as &dyn QueryParameter;
338        let _ = (&HashMap::<String, Box<[Cow<'static, str>]>>::new()) as &dyn QueryParameter;
339    }
340}