-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauthorization.rs
More file actions
152 lines (128 loc) · 5.16 KB
/
authorization.rs
File metadata and controls
152 lines (128 loc) · 5.16 KB
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
use crate::{Persisted, RuntimeResult};
use holochain::prelude::InstalledAppId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
const PERSISTED_FILE_NAME: &str = "authorized_app_clients.json";
/// A unique identifier representing the client instance
/// For example if the client is an android app, the client id would be the package name
/// i.e. "org.holochain.androidserviceclient.app"
#[derive(Eq, Hash, PartialEq, Clone, Serialize, Deserialize, Debug)]
pub struct ClientId(pub String);
/// A map of ClientIds authorized to make app api requests to the specified InstalledAppIds
#[derive(Clone, Serialize, Deserialize, Debug, Default)]
struct AuthorizedAppClients(HashMap<ClientId, Vec<InstalledAppId>>);
pub struct AuthorizedAppClientsManager {
authorized_app_clients: Arc<RwLock<AuthorizedAppClients>>,
persisted_path: PathBuf,
}
impl AuthorizedAppClientsManager {
pub fn new(data_root_path: PathBuf) -> RuntimeResult<Self> {
let persisted_path = data_root_path.join(PERSISTED_FILE_NAME);
let data = Self::read_from_file(persisted_path.clone())?.unwrap_or_default();
Ok(Self {
authorized_app_clients: Arc::new(RwLock::new(data)),
persisted_path,
})
}
pub fn authorize(
&self,
client_uid: ClientId,
installed_app_id: InstalledAppId,
) -> RuntimeResult<()> {
// Add the authorization to the in-memory data
{
let mut app_clients = self.authorized_app_clients.write().unwrap();
let mut app_ids = match app_clients.0.clone().get(&client_uid) {
Some(a) => a.clone(),
None => vec![],
};
app_ids.push(installed_app_id);
app_clients.0.insert(client_uid, app_ids.clone());
}
// Save all authorizations to the filesystem
self.save_to_persisted()?;
Ok(())
}
pub fn is_authorized(
&self,
client_uid: ClientId,
installed_app_id: InstalledAppId,
) -> RuntimeResult<bool> {
// Check the authorization from the in-memory data
let app_clients = self.authorized_app_clients.read().unwrap();
Ok(app_clients
.0
.get(&client_uid)
.map(|app_ids| app_ids.contains(&installed_app_id))
.unwrap_or_else(|| false))
}
}
impl Persisted<AuthorizedAppClients> for AuthorizedAppClientsManager {
fn get_file_path(&self) -> PathBuf {
self.persisted_path.clone()
}
fn get_data_lock(&self) -> Arc<RwLock<AuthorizedAppClients>> {
self.authorized_app_clients.clone()
}
}
#[cfg(test)]
mod test {
use super::{AuthorizedAppClients, AuthorizedAppClientsManager, ClientId, PERSISTED_FILE_NAME};
use std::collections::HashMap;
use std::fs::{exists, File};
use std::io::{Read, Write};
use tempfile::tempdir;
#[test]
fn authorize_saves_to_file() {
// Create tempfile path
let tmp_dir = tempdir().unwrap();
let path = tmp_dir.path().join(PERSISTED_FILE_NAME);
// Create new authorization manager and authorize an app pair
let manager = AuthorizedAppClientsManager::new(tmp_dir.path().to_path_buf()).unwrap();
manager
.authorize(crate::ClientId("client-1".to_string()), "app-1".to_string())
.unwrap();
// Assert persisted file exists
assert!(exists(manager.persisted_path.clone()).unwrap());
// Read persisted file
let mut f = File::open(path.clone()).unwrap();
let mut encoded = vec![];
f.read_to_end(&mut encoded).unwrap();
let decoded: AuthorizedAppClients = serde_json::from_slice(encoded.as_slice()).unwrap();
// Assert persisted file contains authorized app pair
assert_eq!(
decoded.0.get(&ClientId("client-1".to_string())),
Some(&vec!["app-1".to_string()])
);
}
#[test]
fn is_authorized_loads_from_file() {
// Create tempfile path
let tmp_dir = tempdir().unwrap();
let path = tmp_dir.path().join(PERSISTED_FILE_NAME);
// Create new authorization manager using that file
let manager = AuthorizedAppClientsManager::new(tmp_dir.path().to_path_buf()).unwrap();
// Assert app pair is not authorized
assert!(!manager
.is_authorized(ClientId("client-1".to_string()), "app-1".to_string())
.unwrap());
// Write app pair to file
let authorized_data = AuthorizedAppClients(HashMap::from([(
ClientId("client-1".to_string()),
vec!["app-1".to_string()],
)]));
let encoded = serde_json::to_vec(&authorized_data).unwrap();
{
let mut file = File::create(path.clone()).unwrap();
file.write_all(encoded.as_slice()).unwrap();
}
// Create new authorization manager using that file
let manager2 = AuthorizedAppClientsManager::new(tmp_dir.path().to_path_buf()).unwrap();
// Assert app pair is authorized
assert!(manager2
.is_authorized(ClientId("client-1".to_string()), "app-1".to_string())
.unwrap())
}
}