realm.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. use std::{
  2. collections::HashMap,
  3. sync::{Arc, RwLock},
  4. };
  5. use microrm::prelude::*;
  6. use crate::{
  7. config::Config,
  8. key::{HMacType, KeyType},
  9. schema, UIDCError,
  10. };
  11. #[derive(serde::Serialize, serde::Deserialize)]
  12. pub struct AccessTokenClaims<'l> {
  13. pub sub: &'l str,
  14. pub iss: &'l str,
  15. pub aud: &'l str,
  16. pub iat: u64,
  17. pub exp: u64,
  18. pub scopes: Vec<&'l str>,
  19. pub roles: Vec<String>,
  20. }
  21. #[derive(serde::Serialize, serde::Deserialize)]
  22. pub struct RefreshTokenClaims {
  23. pub sub: String,
  24. pub iss: String,
  25. pub aud: String,
  26. pub iat: u64,
  27. pub exp: u64,
  28. #[serde(rename = "use")]
  29. pub use_: String,
  30. pub scopes: Vec<String>,
  31. }
  32. pub struct RealmCache {
  33. config: Config,
  34. db: schema::UIDCDatabase,
  35. realms: RwLock<HashMap<schema::RealmID, Arc<RealmHelper>>>,
  36. }
  37. impl RealmCache {
  38. pub fn new(config: Config, db: schema::UIDCDatabase) -> Self {
  39. Self {
  40. config,
  41. db,
  42. realms: Default::default(),
  43. }
  44. }
  45. pub fn get_helper(&self, id: schema::RealmID) -> Option<Arc<RealmHelper>> {
  46. if let Some(rh) = self.realms.read().unwrap().get(&id) {
  47. return Some(rh.clone());
  48. }
  49. let realm = self.db.realms.by_id(id).ok().flatten()?;
  50. self.realms
  51. .write()
  52. .unwrap()
  53. .insert(id, RealmHelper::new(self.config.clone(), realm).into());
  54. self.get_helper(id)
  55. }
  56. }
  57. pub struct RealmHelper {
  58. config: Config,
  59. realm: microrm::Stored<schema::Realm>,
  60. // cached for later
  61. issuer: String,
  62. encoding_key_cache: RwLock<HashMap<schema::KeyID, jsonwebtoken::EncodingKey>>,
  63. decoding_key_cache: RwLock<HashMap<schema::KeyID, jsonwebtoken::DecodingKey>>,
  64. }
  65. impl RealmHelper {
  66. pub fn new(config: Config, realm: microrm::Stored<schema::Realm>) -> Self {
  67. Self {
  68. issuer: format!("{}/{}", config.base_url, realm.shortname),
  69. config,
  70. realm,
  71. encoding_key_cache: Default::default(),
  72. decoding_key_cache: Default::default(),
  73. }
  74. }
  75. fn determine_roles<'a>(
  76. &self,
  77. client: &microrm::Stored<schema::Client>,
  78. user: &microrm::Stored<schema::User>,
  79. scopes: impl Iterator<Item = &'a str>,
  80. ) -> Result<Vec<String>, UIDCError> {
  81. let mut requested_roles = vec![];
  82. for scope_name in scopes {
  83. if scope_name.is_empty() {
  84. continue;
  85. }
  86. let Some(scope) = client.scopes.keyed((self.realm.id(), scope_name)).get()? else {
  87. // requested nonexistent scope
  88. return Err(UIDCError::Abort("requested nonexistent scope!"));
  89. };
  90. requested_roles.extend(scope.roles.get_ids()?.into_iter());
  91. }
  92. let mut available_roles = vec![]; // HashSet::<schema::RoleID>::new();
  93. for group in user.groups.get()? {
  94. available_roles.extend(group.roles.get_ids()?.into_iter());
  95. }
  96. requested_roles.sort();
  97. requested_roles.dedup();
  98. available_roles.sort();
  99. available_roles.dedup();
  100. let roles = requested_roles
  101. .into_iter()
  102. .filter(|x| available_roles.contains(x))
  103. .flat_map(|v| self.realm.roles.with_id(v).first().get())
  104. .flatten()
  105. .map(|v| v.wrapped().shortname)
  106. .collect();
  107. Ok(roles)
  108. }
  109. fn with_encoding_key<R>(
  110. &self,
  111. key: &microrm::Stored<schema::Key>,
  112. f: impl FnOnce(&jsonwebtoken::EncodingKey) -> R,
  113. ) -> R {
  114. // check to see if the cache will work
  115. if let Some(v) = self.encoding_key_cache.read().unwrap().get(&key.id()) {
  116. return f(v);
  117. }
  118. // parse an EncodingKey out of the stored key
  119. let ekey = match key.key_type.as_ref() {
  120. KeyType::HMac(_) => jsonwebtoken::EncodingKey::from_secret(&key.secret_data),
  121. KeyType::RSA2048 | KeyType::RSA4096 => {
  122. jsonwebtoken::EncodingKey::from_rsa_der(&key.secret_data)
  123. }
  124. KeyType::Ed25519 => jsonwebtoken::EncodingKey::from_ed_der(&key.secret_data),
  125. };
  126. f(self
  127. .encoding_key_cache
  128. .write()
  129. .unwrap()
  130. .entry(key.id())
  131. .or_insert(ekey))
  132. }
  133. fn with_decoding_key<R>(
  134. &self,
  135. key: &microrm::Stored<schema::Key>,
  136. f: impl FnOnce(&jsonwebtoken::DecodingKey) -> R,
  137. ) -> R {
  138. // check to see if the cache will work
  139. if let Some(v) = self.decoding_key_cache.read().unwrap().get(&key.id()) {
  140. return f(v);
  141. }
  142. // parse an EncodingKey out of the stored key
  143. let ekey = match key.key_type.as_ref() {
  144. KeyType::HMac(_) => jsonwebtoken::DecodingKey::from_secret(&key.secret_data),
  145. KeyType::RSA2048 | KeyType::RSA4096 => {
  146. jsonwebtoken::DecodingKey::from_rsa_der(&key.secret_data)
  147. }
  148. KeyType::Ed25519 => jsonwebtoken::DecodingKey::from_ed_der(&key.secret_data),
  149. };
  150. f(self
  151. .decoding_key_cache
  152. .write()
  153. .unwrap()
  154. .entry(key.id())
  155. .or_insert(ekey))
  156. }
  157. pub fn generate_access_token<'a>(
  158. &self,
  159. client: &microrm::Stored<schema::Client>,
  160. user: &microrm::Stored<schema::User>,
  161. scopes: impl Iterator<Item = &'a str> + Clone,
  162. ) -> Result<String, UIDCError> {
  163. let iat = std::time::SystemTime::now();
  164. let exp = iat + std::time::Duration::from_secs(self.config.auth_token_expiry);
  165. let resulting_roles = self.determine_roles(client, user, scopes.clone())?;
  166. let atclaims = AccessTokenClaims {
  167. sub: user.username.as_str(),
  168. iss: self.issuer.as_str(),
  169. aud: client.shortname.as_str(),
  170. iat: iat.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
  171. exp: exp.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
  172. scopes: scopes.collect(),
  173. roles: resulting_roles,
  174. };
  175. // find an active key that matches the required key type
  176. let Some(ekey) = self
  177. .realm
  178. .keys
  179. .with(
  180. schema::Key::KeyState,
  181. schema::KeyState::Active.into_serialized(),
  182. )
  183. .with(schema::Key::KeyType, &client.access_key_type)
  184. .first()
  185. .get()?
  186. else {
  187. return Err(UIDCError::Abort("no matching signing key"));
  188. };
  189. let mut hdr = jsonwebtoken::Header::new(match ekey.key_type.as_ref() {
  190. KeyType::Ed25519 => jsonwebtoken::Algorithm::EdDSA,
  191. KeyType::RSA2048 | KeyType::RSA4096 => jsonwebtoken::Algorithm::RS256,
  192. KeyType::HMac(HMacType::Sha256) => {
  193. Err(UIDCError::Abort("cannot sign access token with HS256"))?
  194. }
  195. KeyType::HMac(HMacType::Sha512) => {
  196. Err(UIDCError::Abort("cannot sign access token with HS512"))?
  197. }
  198. });
  199. hdr.kid = Some(ekey.key_id.clone());
  200. self.with_encoding_key(&ekey, |ekey| {
  201. jsonwebtoken::encode(&hdr, &atclaims, ekey)
  202. .map_err(|e| UIDCError::AbortString(format!("failed to sign token: {e}")))
  203. })
  204. }
  205. pub fn generate_refresh_token<'a>(
  206. &self,
  207. client: &microrm::Stored<schema::Client>,
  208. user: &microrm::Stored<schema::User>,
  209. scopes: impl Iterator<Item = &'a str> + Clone,
  210. ) -> Result<String, UIDCError> {
  211. let iat = std::time::SystemTime::now();
  212. let exp = iat + std::time::Duration::from_secs(self.config.refresh_token_expiry);
  213. let atclaims = RefreshTokenClaims {
  214. sub: user.username.clone(),
  215. iss: self.issuer.clone(),
  216. aud: client.shortname.clone(),
  217. iat: iat.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
  218. exp: exp.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
  219. scopes: scopes.map(str::to_string).collect(),
  220. use_: "refresh".to_string(),
  221. };
  222. // find an active key that matches the required key type
  223. let Some(ekey) = self
  224. .realm
  225. .keys
  226. .with(
  227. schema::Key::KeyState,
  228. schema::KeyState::Active.into_serialized(),
  229. )
  230. .with(schema::Key::KeyType, &client.refresh_key_type)
  231. .first()
  232. .get()?
  233. else {
  234. return Err(UIDCError::Abort("no matching signing key"));
  235. };
  236. let mut hdr = jsonwebtoken::Header::new(match ekey.key_type.as_ref() {
  237. KeyType::Ed25519 => jsonwebtoken::Algorithm::EdDSA,
  238. KeyType::RSA2048 | KeyType::RSA4096 => jsonwebtoken::Algorithm::RS256,
  239. KeyType::HMac(HMacType::Sha256) => jsonwebtoken::Algorithm::HS256,
  240. KeyType::HMac(HMacType::Sha512) => jsonwebtoken::Algorithm::HS512,
  241. });
  242. hdr.kid = Some(ekey.key_id.clone());
  243. self.with_encoding_key(&ekey, |ekey| {
  244. jsonwebtoken::encode(&hdr, &atclaims, ekey)
  245. .map_err(|_| UIDCError::Abort("failed to sign token"))
  246. })
  247. }
  248. pub fn trade_refresh_token(
  249. &self,
  250. client: &microrm::Stored<schema::Client>,
  251. rtoken: &str,
  252. ) -> Result<(String, String), UIDCError> {
  253. let header = jsonwebtoken::decode_header(rtoken)
  254. .map_err(|e| UIDCError::AbortString(format!("invalid JWT header: {e}")))?;
  255. let Some(kid) = header.kid else {
  256. return Err(UIDCError::Abort("no kid in header"));
  257. };
  258. let Some(key) = self
  259. .realm
  260. .keys
  261. .with(schema::Key::KeyId, kid)
  262. .first()
  263. .get()?
  264. else {
  265. return Err(UIDCError::Abort("no matching key"));
  266. };
  267. if *key.key_state.as_ref() == schema::KeyState::Retired {
  268. return Err(UIDCError::Abort("signing key retired"));
  269. }
  270. let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256);
  271. validation.set_issuer(&[self.issuer.as_str()]);
  272. validation.set_audience(&[client.shortname.as_str()]);
  273. let Ok(rt) = self.with_decoding_key(&key, |dkey| {
  274. jsonwebtoken::decode::<RefreshTokenClaims>(rtoken, dkey, &validation)
  275. }) else {
  276. return Err(UIDCError::Abort("token validation failed"));
  277. };
  278. if rt.claims.use_ != "refresh" {
  279. return Err(UIDCError::Abort("mismatching token use"));
  280. }
  281. let Some(user) = self
  282. .realm
  283. .users
  284. .keyed((self.realm.id(), rt.claims.sub.as_str()))
  285. .get()?
  286. else {
  287. return Err(UIDCError::Abort("user no longer exists or was renamed"));
  288. };
  289. let scopes = rt.claims.scopes.iter().map(String::as_str);
  290. let access_token = self.generate_access_token(client, &user, scopes.clone())?;
  291. let refresh_token = self.generate_refresh_token(client, &user, scopes)?;
  292. Ok((access_token, refresh_token))
  293. }
  294. }