Skip to content

Commit a618a28

Browse files
authored
add impersonation to user, and metadata to user and org (#2)
The User now has an impersonator_user_id field. This is the UUID of the other user that is impersonating this user. It will be None if impersonation is not happening (which it normally isn't). Metadata is private, user-specific metadata which can be set via the regular update_metadata endpoint. The user will never see or use this metadata, it's just for the PropelAuth customer. The Org objects have org_metadata. Much like user metadata, this is a private field. The metadata objects are simple hashes with a string key and any JSON object for the value.
1 parent d06c042 commit a618a28

File tree

6 files changed

+65
-16
lines changed

6 files changed

+65
-16
lines changed

Cargo.toml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "propelauth"
3-
version = "0.1.1"
3+
version = "0.2.0"
44
authors = ["support@propelauth.com"]
55
description = "A Rust crate for managing authentication and authorization with support for multi-tenant / B2B products, powered by PropelAuth"
66
keywords = ["authentication", "auth", "authorization", "b2b", "tenant"]
@@ -10,18 +10,16 @@ license = "MIT"
1010
edition = "2018"
1111

1212
[dependencies]
13+
actix-web = { version = "4", optional = true }
14+
axum = { version = "^0.6", optional = true }
15+
jsonwebtoken = "8.1.1"
1316
serde = "^1.0"
1417
serde_derive = "^1.0"
1518
serde_json = "^1.0"
1619
thiserror = "^1.0"
20+
tower = { version = "^0.4", optional = true }
1721
url = "^2.2"
1822
uuid = { version = "^1.0", features = ["serde"] }
19-
jsonwebtoken = "8.1.1"
20-
21-
axum = { version = "^0.6", optional = true }
22-
tower = { version = "^0.4", optional = true }
23-
24-
actix-web = { version = "4", optional = true }
2523

2624
[dependencies.reqwest]
2725
version = "^0.11"

src/models/update_metadata_request.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212

1313

14+
use std::collections::HashMap;
15+
16+
use serde_json::Value;
17+
1418
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
1519
pub struct UpdateMetadataRequest {
1620
#[serde(rename = "username", skip_serializing_if = "Option::is_none")]
@@ -21,6 +25,8 @@ pub struct UpdateMetadataRequest {
2125
pub last_name: Option<String>,
2226
#[serde(rename = "picture_url", skip_serializing_if = "Option::is_none")]
2327
pub picture_url: Option<String>,
28+
#[serde(rename = "metadata", skip_serializing_if = "Option::is_none")]
29+
pub metadata: Option<HashMap<String, Value>>,
2430
}
2531

2632
impl UpdateMetadataRequest {
@@ -30,6 +36,7 @@ impl UpdateMetadataRequest {
3036
first_name: None,
3137
last_name: None,
3238
picture_url: None,
39+
metadata: None,
3340
}
3441
}
3542
}

src/models/update_org_request.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,26 @@
1111

1212

1313

14+
use std::collections::HashMap;
15+
16+
use serde_json::Value;
17+
1418
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
1519
pub struct UpdateOrgRequest {
1620
#[serde(rename = "name", skip_serializing_if = "Option::is_none")]
1721
pub name: Option<String>,
1822
#[serde(rename = "can_setup_saml", skip_serializing_if = "Option::is_none")]
1923
pub can_setup_saml: Option<bool>,
24+
#[serde(rename = "metadata", skip_serializing_if = "Option::is_none")]
25+
pub metadata: Option<HashMap<String, Value>>,
2026
}
2127

2228
impl UpdateOrgRequest {
2329
pub fn new() -> UpdateOrgRequest {
2430
UpdateOrgRequest {
2531
name: None,
2632
can_setup_saml: None,
33+
metadata: None,
2734
}
2835
}
2936
}

src/models/user_metadata.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212

1313

14+
use std::collections::HashMap;
15+
16+
use serde_json::Value;
17+
1418
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
1519
pub struct UserMetadata {
1620
#[serde(rename = "user_id")]
@@ -40,9 +44,13 @@ pub struct UserMetadata {
4044
#[serde(rename = "last_active_at")]
4145
pub last_active_at: i64,
4246
#[serde(rename = "org_id_to_org_info", skip_serializing_if = "Option::is_none")]
43-
pub org_id_to_org_info: Option<::std::collections::HashMap<String, crate::models::UserInOrg>>,
47+
pub org_id_to_org_info: Option<HashMap<String, crate::models::UserInOrg>>,
4448
#[serde(rename = "legacy_user_id", skip_serializing_if = "Option::is_none")]
4549
pub legacy_user_id: Option<String>,
50+
#[serde(rename = "impersonated_user_id", skip_serializing_if = "Option::is_none")]
51+
pub impersonated_user_id: Option<String>,
52+
#[serde(rename = "metadata", skip_serializing_if = "Option::is_none")]
53+
pub metadata: Option<HashMap<String, Value>>,
4654
}
4755

4856
impl UserMetadata {
@@ -63,6 +71,8 @@ impl UserMetadata {
6371
last_active_at,
6472
org_id_to_org_info: None,
6573
legacy_user_id: None,
74+
impersonated_user_id: None,
75+
metadata: None,
6676
}
6777
}
6878
}

src/propelauth/token.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
use jsonwebtoken::{Algorithm, decode, DecodingKey, Validation};
2+
13
use crate::models::AuthTokenVerificationMetadata;
24
use crate::propelauth::errors::{
35
DetailedAuthError, UnauthorizedError, UnauthorizedOrForbiddenError,
46
};
57
use crate::propelauth::options::{RequiredOrg, UserRequirementsInOrg};
68
use crate::propelauth::token_models::{User, UserAndOrgMemberInfo};
7-
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
89

910
pub struct TokenService<'a> {
1011
pub(crate) token_verification_metadata: &'a AuthTokenVerificationMetadata,
@@ -64,6 +65,12 @@ impl TokenService<'_> {
6465

6566
#[cfg(test)]
6667
mod tests {
68+
use std::collections::HashMap;
69+
use std::time::SystemTime;
70+
71+
use jsonwebtoken::{Algorithm, encode, EncodingKey, Header};
72+
use openssl::rsa::Rsa;
73+
6774
use crate::models::AuthTokenVerificationMetadata;
6875
use crate::propelauth::errors::{
6976
DetailedAuthError, DetailedForbiddenError, UnauthorizedError, UnauthorizedOrForbiddenError,
@@ -72,10 +79,6 @@ mod tests {
7279
use crate::propelauth::options::UserRequirementsInOrg;
7380
use crate::propelauth::token::TokenService;
7481
use crate::propelauth::token_models::{OrgMemberInfo, User, UserAndOrgMemberInfo};
75-
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
76-
use openssl::rsa::Rsa;
77-
use std::collections::HashMap;
78-
use std::time::SystemTime;
7982

8083
const ISSUER: &'static str = "https://testissuer.propelauthtest.com";
8184

@@ -108,6 +111,8 @@ mod tests {
108111
user_id: "bf7b3bc0-739d-45a2-ba60-60655249a5b0".to_string(),
109112
org_id_to_org_member_info: get_org_id_to_org_member_info(),
110113
legacy_user_id: Some("legacy_id".to_string()),
114+
impersonated_user_id: None,
115+
metadata: HashMap::new(),
111116
};
112117
let (jwt, token_verification_metadata) =
113118
get_jwt_and_token_verification_metadata(expected_user.clone(), 24);
@@ -209,6 +214,8 @@ mod tests {
209214
user_id: "bf7b3bc0-739d-45a2-ba60-60655249a5b0".to_string(),
210215
org_id_to_org_member_info: get_org_id_to_org_member_info(),
211216
legacy_user_id: Some("legacy_id".to_string()),
217+
impersonated_user_id: None,
218+
metadata: HashMap::new(),
212219
};
213220
let (jwt, token_verification_metadata) =
214221
get_jwt_and_token_verification_metadata(expected_user.clone(), 24);
@@ -364,6 +371,8 @@ mod tests {
364371
user_id: "bf7b3bc0-739d-45a2-ba60-60655249a5b0".to_string(),
365372
org_id_to_org_member_info: get_org_id_to_org_member_info(),
366373
legacy_user_id: Some("legacy_id".to_string()),
374+
impersonated_user_id: None,
375+
metadata: HashMap::new(),
367376
};
368377
let (jwt, token_verification_metadata) =
369378
get_jwt_and_token_verification_metadata(expected_user.clone(), 24);
@@ -443,6 +452,7 @@ mod tests {
443452
OrgMemberInfo {
444453
org_id: "org_id_1".to_string(),
445454
org_name: "org_name_1".to_string(),
455+
org_metadata: HashMap::new(),
446456
url_safe_org_name: "org_name_1".to_string(),
447457
user_role: "Owner".to_string(),
448458
inherited_user_roles_plus_current_role: vec![
@@ -458,6 +468,7 @@ mod tests {
458468
OrgMemberInfo {
459469
org_id: "org_id_2".to_string(),
460470
org_name: "org_name_2".to_string(),
471+
org_metadata: HashMap::new(),
461472
url_safe_org_name: "org_name_2".to_string(),
462473
user_role: "Admin".to_string(),
463474
inherited_user_roles_plus_current_role: vec![
@@ -472,6 +483,7 @@ mod tests {
472483
OrgMemberInfo {
473484
org_id: "org_id_3".to_string(),
474485
org_name: "org_name_3".to_string(),
486+
org_metadata: HashMap::new(),
475487
url_safe_org_name: "org_name_3".to_string(),
476488
user_role: "Member".to_string(),
477489
inherited_user_roles_plus_current_role: vec!["Member".to_string()],

src/propelauth/token_models.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use crate::propelauth::errors::DetailedForbiddenError;
2-
use crate::propelauth::options::{RequiredOrg, UserRequirementsInOrg};
3-
use serde::{Deserialize, Serialize};
41
use std::collections::hash_map::{Keys, Values};
52
use std::collections::HashMap;
63

4+
use serde::{Deserialize, Serialize};
5+
use serde_json::Value;
6+
7+
use crate::propelauth::errors::DetailedForbiddenError;
8+
use crate::propelauth::options::{RequiredOrg, UserRequirementsInOrg};
9+
710
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
811
pub struct User {
912
pub user_id: String,
@@ -15,6 +18,13 @@ pub struct User {
1518
* this is their original ID from that system. */
1619
#[serde(default)]
1720
pub legacy_user_id: Option<String>,
21+
22+
#[serde(default)]
23+
pub impersonated_user_id: Option<String>,
24+
25+
#[serde(default)]
26+
pub metadata: HashMap<String, String>,
27+
1828
}
1929

2030
impl User {
@@ -85,12 +95,17 @@ impl User {
8595
pub fn get_num_orgs(&self) -> usize {
8696
self.org_id_to_org_member_info.len()
8797
}
98+
99+
pub fn is_impersonated(&self) -> bool {
100+
self.impersonated_user_id.is_some()
101+
}
88102
}
89103

90104
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
91105
pub struct OrgMemberInfo {
92106
pub org_id: String,
93107
pub org_name: String,
108+
pub org_metadata: HashMap<String, Value>,
94109
pub url_safe_org_name: String,
95110
pub user_role: String,
96111
pub inherited_user_roles_plus_current_role: Vec<String>,

0 commit comments

Comments
 (0)