mirror of
https://github.com/OMGeeky/yup-oauth2.git
synced 2025-12-26 16:27:25 +01:00
Don't publish the ScopeSet struct
Just pass `&[&str]` into custom storage providers. The scopeset struct has a range of unnecessary internal features. It's now also part of the interface for custom storage providers that the given scopes will be both unique and sorted. The only slightly awkward thing is that there's no conventient way to expose a `scopes_covered_by` helper method (which almost all custom storage engines will need), but it's still included in the example code.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
use std::sync::RwLock;
|
||||
use yup_oauth2::storage::{ScopeSet, TokenInfo, TokenStorage};
|
||||
use yup_oauth2::storage::{TokenInfo, TokenStorage};
|
||||
|
||||
struct ExampleTokenStore {
|
||||
store: RwLock<Vec<StoredToken>>,
|
||||
@@ -13,11 +13,19 @@ struct StoredToken {
|
||||
serialized_token: String,
|
||||
}
|
||||
|
||||
/// Is this set of scopes covered by the other? Returns true if the other
|
||||
/// set is a superset of this one. Use this when implementing TokenStorage.get()
|
||||
fn scopes_covered_by(scopes: &[&str], possible_match_or_superset: &[&str]) -> bool {
|
||||
scopes
|
||||
.iter()
|
||||
.all(|s| possible_match_or_superset.iter().any(|t| t == s))
|
||||
}
|
||||
|
||||
/// Here we implement our own token storage. You could write the serialized token and scope data
|
||||
/// to disk, an OS keychain, a database or whatever suits your use-case
|
||||
#[async_trait]
|
||||
impl TokenStorage for ExampleTokenStore {
|
||||
async fn set(&self, scopes: ScopeSet<'_, &str>, token: TokenInfo) -> anyhow::Result<()> {
|
||||
async fn set(&self, scopes: &[&str], token: TokenInfo) -> anyhow::Result<()> {
|
||||
let data = serde_json::to_string(&token).unwrap();
|
||||
|
||||
println!("Storing token for scopes {:?}", scopes);
|
||||
@@ -28,18 +36,25 @@ impl TokenStorage for ExampleTokenStore {
|
||||
.map_err(|_| anyhow!("Unable to lock store for writing"))?;
|
||||
|
||||
store.push(StoredToken {
|
||||
scopes: scopes.scopes(),
|
||||
scopes: scopes.iter().map(|str| str.to_string()).collect(),
|
||||
serialized_token: data,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get(&self, target_scopes: ScopeSet<'_, &str>) -> Option<TokenInfo> {
|
||||
async fn get(&self, target_scopes: &[&str]) -> Option<TokenInfo> {
|
||||
// Retrieve the token data
|
||||
self.store.read().ok().and_then(|store| {
|
||||
for stored_token in store.iter() {
|
||||
if target_scopes.is_covered_by(&stored_token.scopes) {
|
||||
if scopes_covered_by(
|
||||
target_scopes,
|
||||
&stored_token
|
||||
.scopes
|
||||
.iter()
|
||||
.map(|s| &s[..])
|
||||
.collect::<Vec<_>>()[..],
|
||||
) {
|
||||
return serde_json::from_str(&stored_token.serialized_token).ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl ScopeFilter {
|
||||
|
||||
/// A set of scopes
|
||||
#[derive(Debug)]
|
||||
pub struct ScopeSet<'a, T> {
|
||||
pub(crate) struct ScopeSet<'a, T> {
|
||||
hash: ScopeHash,
|
||||
filter: ScopeFilter,
|
||||
scopes: &'a [T],
|
||||
@@ -109,25 +109,6 @@ where
|
||||
scopes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the scopes for storage when implementing TokenStorage.set().
|
||||
/// Returned scope strings are unique and sorted.
|
||||
pub fn scopes(&self) -> Vec<String> {
|
||||
self.scopes
|
||||
.iter()
|
||||
.map(|scope| scope.as_ref().to_string())
|
||||
.sorted()
|
||||
.unique()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Is this set of scopes covered by the other? Returns true if the other
|
||||
/// set is a superset of this one. Use this when implementing TokenStorage.get()
|
||||
pub fn is_covered_by(&self, other_scopes: &[String]) -> bool {
|
||||
self.scopes
|
||||
.iter()
|
||||
.all(|s| other_scopes.iter().any(|t| t.as_str() == s.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement your own token storage solution by implementing this trait. You need a way to
|
||||
@@ -137,10 +118,10 @@ pub trait TokenStorage: Send + Sync {
|
||||
/// Store a token for the given set of scopes so that it can be retrieved later by get()
|
||||
/// ScopeSet implements Hash so that you can easily serialize and store it.
|
||||
/// TokenInfo can be serialized with serde.
|
||||
async fn set(&self, scopes: ScopeSet<'_, &str>, token: TokenInfo) -> anyhow::Result<()>;
|
||||
async fn set(&self, scopes: &[&str], token: TokenInfo) -> anyhow::Result<()>;
|
||||
|
||||
/// Retrieve a token stored by set for the given set of scopes
|
||||
async fn get(&self, scopes: ScopeSet<'_, &str>) -> Option<TokenInfo>;
|
||||
async fn get(&self, scopes: &[&str]) -> Option<TokenInfo>;
|
||||
}
|
||||
|
||||
pub(crate) enum Storage {
|
||||
@@ -162,15 +143,17 @@ impl Storage {
|
||||
Storage::Memory { tokens } => Ok(tokens.lock().await.set(scopes, token)?),
|
||||
Storage::Disk(disk_storage) => Ok(disk_storage.set(scopes, token).await?),
|
||||
Storage::Custom(custom_storage) => {
|
||||
let str_scopes: Vec<_> = scopes.scopes.iter().map(|scope| scope.as_ref()).collect();
|
||||
let str_scopes: Vec<_> = scopes
|
||||
.scopes
|
||||
.iter()
|
||||
.map(|scope| scope.as_ref())
|
||||
.sorted()
|
||||
.unique()
|
||||
.collect();
|
||||
|
||||
(*custom_storage)
|
||||
.set(
|
||||
ScopeSet {
|
||||
hash: scopes.hash,
|
||||
filter: scopes.filter,
|
||||
scopes: &str_scopes[..],
|
||||
},
|
||||
&str_scopes[..], // TODO: sorted, unique
|
||||
token,
|
||||
)
|
||||
.await
|
||||
@@ -186,15 +169,15 @@ impl Storage {
|
||||
Storage::Memory { tokens } => tokens.lock().await.get(scopes),
|
||||
Storage::Disk(disk_storage) => disk_storage.get(scopes).await,
|
||||
Storage::Custom(custom_storage) => {
|
||||
let str_scopes: Vec<_> = scopes.scopes.iter().map(|scope| scope.as_ref()).collect();
|
||||
let str_scopes: Vec<_> = scopes
|
||||
.scopes
|
||||
.iter()
|
||||
.map(|scope| scope.as_ref())
|
||||
.sorted()
|
||||
.unique()
|
||||
.collect();
|
||||
|
||||
(*custom_storage)
|
||||
.get(ScopeSet {
|
||||
hash: scopes.hash,
|
||||
filter: scopes.filter,
|
||||
scopes: &str_scopes[..],
|
||||
})
|
||||
.await
|
||||
(*custom_storage).get(&str_scopes[..]).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user