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 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
//! Encapsulates various shared mechanisms for handlings different grants.
use super::{Url, Time};
use super::scope::Scope;
use std::borrow::{Cow, ToOwned};
use std::collections::HashMap;
use std::collections::hash_map::Iter;
use std::rc::Rc;
use std::sync::Arc;
/// Provides a name registry for extensions.
pub trait GrantExtension {
/// An unique identifier distinguishing this extension type for parsing and storing.
/// Obvious choices are the registered names as administered by IANA or private identifiers.
fn identifier(&self) -> &'static str;
}
/// Wraps the data for an extension as a string with access restrictions.
///
/// This is a generic way for extensions to store their data in a universal, encoded form. It is
/// also able to indicate the intended readers for such an extension so that backends can ensure
/// that private extension data is properly encrypted even when present in a self-encoded access
/// token.
///
/// Some extensions have semantics where the presence alone is the stored data, so storing data
/// is optional and storing no data is distinct from not attaching any extension instance at all.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Value {
/// An extension that the token owner is allowed to read and interpret.
Public(Option<String>),
/// Identifies an extenion whose content and/or existance MUST be kept secret.
Private(Option<String>),
// Content which is not saved on the server but initialized/interpreted from other sources.
// foreign_content: String,
}
/// Links one or several `GrantExtension` instances to their respective data.
///
/// This also serves as a clean interface for both frontend and backend to reliably and
/// conveniently manipulate or query the stored data sets.
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct Extensions {
extensions: HashMap<String, Value>,
}
/// Owning copy of a grant.
///
/// This can be stored in a database without worrying about lifetimes or shared across thread
/// boundaries. A reference to this can be converted to a purely referential `GrantRef`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Grant {
/// Identifies the owner of the resource.
pub owner_id: String,
/// Identifies the client to which the grant was issued.
pub client_id: String,
/// The scope granted to the client.
pub scope: Scope,
/// The redirection uri under which the client resides. The url package does indeed seem to
/// parse valid URIs as well.
pub redirect_uri: Url,
/// Expiration date of the grant (Utc).
pub until: Time,
/// Encoded extensions existing on this Grant
pub extensions: Extensions,
}
impl Value {
/// Creates an extension whose presence and content can be unveiled by the token holder.
///
/// Anyone in possession of the token corresponding to such a grant is potentially able to read
/// the content of a public extension.
pub fn public(content: Option<String>) -> Self {
Value::Public(content)
}
/// Creates an extension with secret content only visible for the server.
///
/// Token issuers should take special care to protect the content and the identifier of such
/// an extension from being interpreted or correlated by the token holder.
pub fn private(content: Option<String>) -> Value {
Value::Private(content)
}
/// Inspect the public value.
///
/// Returns an `Err` if this is not a public extension, `None` if the extension has no value
/// but consists only of the key, and `Some(_)` otherwise.
pub fn public_value(&self) -> Result<Option<&str>, ()> {
match self {
Value::Public(Some(content)) => Ok(Some(&content)),
Value::Public(None) => Ok(None),
_ => Err(()),
}
}
/// Convert into the public value.
///
/// Returns an `Err` if this is not a public extension, `None` if the extension has no value
/// but consists only of the key, and `Some(_)` otherwise.
pub fn into_public_value(self) -> Result<Option<String>, ()> {
match self {
Value::Public(content) => Ok(content),
_ => Err(()),
}
}
/// Inspect the private value.
///
/// Returns an `Err` if this is not a private extension, `None` if the extension has no value
/// but consists only of the key, and `Some(_)` otherwise.
pub fn private_value(&self) -> Result<Option<&str>, ()> {
match self {
Value::Private(Some(content)) => Ok(Some(&content)),
Value::Private(None) => Ok(None),
_ => Err(()),
}
}
/// Inspect the private value.
///
/// Returns an `Err` if this is not a private extension, `None` if the extension has no value
/// but consists only of the key, and `Some(_)` otherwise.
pub fn into_private_value(self) -> Result<Option<String>, ()> {
match self {
Value::Private(content) => Ok(content),
_ => Err(()),
}
}
}
impl Extensions {
/// Create a new extension store.
pub fn new() -> Extensions {
Extensions::default()
}
/// Set the stored content for a `GrantExtension` instance.
pub fn set(&mut self, extension: &dyn GrantExtension, content: Value) {
self.extensions
.insert(extension.identifier().to_string(), content);
}
/// Set content for an extension without a corresponding instance.
pub fn set_raw(&mut self, identifier: String, content: Value) {
self.extensions.insert(identifier, content);
}
/// Retrieve the stored data of an instance.
///
/// This removes the data from the store to avoid possible mixups and to allow a copyless
/// retrieval of bigger data strings.
pub fn remove(&mut self, extension: &dyn GrantExtension) -> Option<Value> {
self.extensions.remove(extension.identifier())
}
/// Iterate of the public extensions whose presence and content is not secret.
pub fn public(&self) -> PublicExtensions {
PublicExtensions {
iter: self.extensions.iter(),
}
}
/// Iterate of the private extensions whose presence and content must not be revealed.
pub fn private(&self) -> PrivateExtensions {
PrivateExtensions(self.extensions.iter())
}
}
/// An iterator over the public extensions of a grant.
pub struct PublicExtensions<'a> {
iter: Iter<'a, String, Value>,
}
/// An iterator over the private extensions of a grant.
///
/// Implementations which acquire an instance should take special not to leak any secrets to
/// clients and third parties.
pub struct PrivateExtensions<'a>(Iter<'a, String, Value>);
impl<'a> Iterator for PublicExtensions<'a> {
type Item = (&'a str, Option<&'a str>);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (key, value) = self.iter.next()?;
if let Ok(value) = value.public_value() {
return Some((key, value));
}
}
}
}
impl<'a> Iterator for PrivateExtensions<'a> {
type Item = (&'a str, Option<&'a str>);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (key, value) = self.0.next()?;
if let Ok(value) = value.private_value() {
return Some((key, value));
}
}
}
}
impl<'a, T: GrantExtension + ?Sized> GrantExtension for &'a T {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
impl<'a, T: GrantExtension + ?Sized> GrantExtension for Cow<'a, T>
where
T: Clone + ToOwned,
{
fn identifier(&self) -> &'static str {
self.as_ref().identifier()
}
}
impl<T: GrantExtension + ?Sized> GrantExtension for Box<T> {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
impl<T: GrantExtension + ?Sized> GrantExtension for Arc<T> {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
impl<T: GrantExtension + ?Sized> GrantExtension for Rc<T> {
fn identifier(&self) -> &'static str {
(**self).identifier()
}
}
#[cfg(test)]
mod tests {
use super::{Extensions, Value};
#[test]
fn iteration() {
let mut extensions = Extensions::new();
extensions.set_raw("pub".into(), Value::Public(Some("content".into())));
extensions.set_raw("pub_none".into(), Value::Public(None));
extensions.set_raw("priv".into(), Value::Private(Some("private".into())));
extensions.set_raw("priv_none".into(), Value::Private(None));
assert_eq!(
extensions
.public()
.filter(|&(name, value)| name == "pub" && value == Some("content"))
.count(),
1
);
assert_eq!(
extensions
.public()
.filter(|&(name, value)| name == "pub_none" && value == None)
.count(),
1
);
assert_eq!(extensions.public().count(), 2);
assert_eq!(
extensions
.private()
.filter(|&(name, value)| name == "priv" && value == Some("private"))
.count(),
1
);
assert_eq!(
extensions
.private()
.filter(|&(name, value)| name == "priv_none" && value == None)
.count(),
1
);
assert_eq!(extensions.private().count(), 2);
}
}