cesium_oauth/apps/
file_cached.rs1use std::collections::HashMap;
2use std::fs;
3use std::path::Path;
4
5use async_trait::async_trait;
6use log::{debug, warn};
7use serde::*;
8
9use crate::apps;
10use crate::apps::{AppInfo, AppProvider, RegisteredApp};
11use crate::error::Error;
12
13#[derive(Serialize, Deserialize, Default)]
14struct AppsFile {
15 apps: HashMap<String, RegisteredApp>,
16}
17
18#[derive(Serialize, Debug)]
19struct AppCreateRequest {
20 client_name: String,
21 redirect_uris: String,
22 scopes: String,
23}
24
25pub struct FileCachedAppProvider {
28 file_path: String,
29 cache: AppsFile,
30 app: AppInfo,
31}
32
33impl FileCachedAppProvider {
34 pub fn new(filename: &str, app: AppInfo) -> Result<Self, Error> {
36 let cache = Self::read_apps_file(filename)?;
37 debug!("Cache contains {} app(s)", cache.apps.len());
38 Ok(Self {
39 file_path: filename.to_owned(),
40 cache,
41 app,
42 })
43 }
44
45 fn read_apps_file(file_path: &str) -> Result<AppsFile, Error> {
46 debug!("Loading apps file `{file_path}`");
47 if Path::new(file_path).exists() {
48 fs::read_to_string(file_path)
49 .map_err(|error| Error::AppCacheIoError(error))
50 .and_then(|contents| {
51 toml::from_str(&contents).map_err(|error| Error::AppCacheInvalid(error))
52 })
53 } else {
54 debug!("Apps file not found, using blank apps cache");
55 Ok(AppsFile::default())
56 }
57 }
58
59 fn save_apps_file(&self) -> Result<(), Error> {
60 debug!("Saving apps cache to `{}`", self.file_path);
61 let contents = toml::to_string(&self.cache).expect("Unable to serialize app cache");
62 fs::write(&self.file_path, &contents).map_err(|error| Error::AppCacheIoError(error))
63 }
64}
65
66#[async_trait]
67impl AppProvider for FileCachedAppProvider {
68 async fn get_app_for(&mut self, instance_domain: &str) -> Result<RegisteredApp, Error> {
69 debug!("Get registered app for `{instance_domain}`");
70 if let Some(registered_app) = self.cache.apps.get(instance_domain) {
71 debug!(
72 "App found in cache (client_id={})",
73 registered_app.client_id
74 );
75 Ok(registered_app.clone())
76 } else if let Ok(registered_app) = apps::register_new_app(instance_domain, &self.app).await
77 {
78 debug!(
79 "New app registered (client_id={}), adding to cache",
80 registered_app.client_id
81 );
82 self.cache
83 .apps
84 .insert(instance_domain.to_owned(), registered_app.clone());
85 self.save_apps_file()?;
86 Ok(registered_app.clone())
87 } else {
88 warn!("Unable to register new app to `{instance_domain}`");
89 Err(Error::AppRegistrationError("Unable to register app"))
90 }
91 }
92}