Skip to content

Commit 3aefb9b

Browse files
authored
Keep openid user information in metadata (#559)
Add UserInfo struct in user config. This is updated upon user login if any changes to user profile is made. Fixes #540
1 parent ac98b5d commit 3aefb9b

File tree

4 files changed

+110
-54
lines changed

4 files changed

+110
-54
lines changed

server/src/handlers/http/oidc.rs

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use crate::{
3636
option::CONFIG,
3737
rbac::{
3838
map::{SessionKey, DEFAULT_ROLE},
39-
user::{User, UserType},
39+
user::{self, User, UserType},
4040
Users,
4141
},
4242
storage::{self, ObjectStorageError, StorageMetadata},
@@ -138,19 +138,31 @@ pub async fn reply_login(
138138
else {
139139
return Ok(HttpResponse::Unauthorized().finish());
140140
};
141-
let username = user_info.sub.unwrap();
142-
let group: Option<HashSet<String>> = claims
141+
let username = user_info
142+
.sub
143+
.clone()
144+
.expect("OIDC provider did not return a sub which is currently required.");
145+
let user_info: user::UserInfo = user_info.into();
146+
147+
let group: HashSet<String> = claims
143148
.other
144149
.remove("groups")
145150
.map(serde_json::from_value)
146-
.transpose()?;
151+
.transpose()?
152+
.unwrap_or_else(|| {
153+
DEFAULT_ROLE
154+
.lock()
155+
.unwrap()
156+
.clone()
157+
.map(|role| HashSet::from([role]))
158+
.unwrap_or_default()
159+
});
147160

148161
// User may not exist
149162
// create a new one depending on state of metadata
150163
let user = match (Users.get_user(&username), group) {
151-
(Some(user), Some(group)) => update_user_if_changed(user, group).await?,
152-
(Some(user), None) => user,
153-
(None, group) => put_user(&username, group).await?,
164+
(Some(user), group) => update_user_if_changed(user, group, user_info).await?,
165+
(None, group) => put_user(&username, group, user_info).await?,
154166
};
155167
let id = Ulid::new();
156168
Users.new_session(&user, SessionKey::SessionId(id));
@@ -257,25 +269,18 @@ async fn request_token(
257269
// update local cache
258270
async fn put_user(
259271
username: &str,
260-
group: Option<HashSet<String>>,
272+
group: HashSet<String>,
273+
user_info: user::UserInfo,
261274
) -> Result<User, ObjectStorageError> {
262275
let mut metadata = get_metadata().await?;
263-
let group = group.unwrap_or_else(|| {
264-
DEFAULT_ROLE
265-
.lock()
266-
.unwrap()
267-
.clone()
268-
.map(|role| HashSet::from([role]))
269-
.unwrap_or_default()
270-
});
271276

272277
let user = metadata
273278
.users
274279
.iter()
275280
.find(|user| user.username() == username)
276281
.cloned()
277282
.unwrap_or_else(|| {
278-
let user = User::new_oauth(username.to_owned(), group);
283+
let user = User::new_oauth(username.to_owned(), group, user_info);
279284
metadata.users.push(user.clone());
280285
user
281286
});
@@ -288,14 +293,32 @@ async fn put_user(
288293
async fn update_user_if_changed(
289294
mut user: User,
290295
group: HashSet<String>,
296+
user_info: user::UserInfo,
291297
) -> Result<User, ObjectStorageError> {
292-
// update user if roles have changed
293-
if user.roles == group {
298+
let User { ty, roles } = &mut user;
299+
let UserType::OAuth(oauth_user) = ty else {
300+
unreachable!()
301+
};
302+
303+
// update user only if roles or userinfo has changed
304+
if roles == &group && oauth_user.user_info == user_info {
294305
return Ok(user);
295306
}
296-
let metadata = get_metadata().await?;
297-
user.roles = group;
298-
put_metadata(&metadata).await?;
307+
308+
oauth_user.user_info = user_info;
309+
*roles = group;
310+
311+
let mut metadata = get_metadata().await?;
312+
313+
if let Some(entry) = metadata
314+
.users
315+
.iter_mut()
316+
.find(|x| x.username() == user.username())
317+
{
318+
entry.clone_from(&user);
319+
put_metadata(&metadata).await?;
320+
}
321+
299322
Users.put_user(user.clone());
300323
Ok(user)
301324
}

server/src/rbac/map.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,8 @@ impl Sessions {
152152
permissions: Vec<Permission>,
153153
) {
154154
self.remove_expired_session(&user);
155-
self.user_sessions
156-
.entry(user.clone())
157-
.and_modify(|sessions| sessions.push((key.clone(), expiry)))
158-
.or_default();
155+
let sessions = self.user_sessions.entry(user.clone()).or_default();
156+
sessions.push((key.clone(), expiry));
159157
self.active_sessions.insert(key, (user, permissions));
160158
}
161159

@@ -220,7 +218,7 @@ impl Sessions {
220218
};
221219
(action == required_action || action == Action::All) && ok_stream
222220
}
223-
Permission::SelfRole if required_action == Action::GetUserRoles => {
221+
Permission::SelfUser if required_action == Action::GetUserRoles => {
224222
context_user.map(|x| x == username).unwrap_or_default()
225223
}
226224
_ => false,

server/src/rbac/role.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub enum Permission {
5050
Unit(Action),
5151
Stream(Action, String),
5252
StreamWithTag(Action, String, Option<String>),
53-
SelfRole,
53+
SelfUser,
5454
}
5555

5656
// Currently Roles are tied to one stream
@@ -77,37 +77,37 @@ impl RoleBuilder {
7777
let mut perms = Vec::new();
7878
for action in self.actions {
7979
let perm = match action {
80-
Action::Ingest => Permission::Stream(action, self.stream.clone().unwrap()),
8180
Action::Query => Permission::StreamWithTag(
8281
action,
8382
self.stream.clone().unwrap(),
8483
self.tag.clone(),
8584
),
86-
Action::CreateStream => Permission::Unit(action),
87-
Action::DeleteStream => Permission::Unit(action),
88-
Action::ListStream => Permission::Unit(action),
89-
Action::GetSchema => Permission::Stream(action, self.stream.clone().unwrap()),
90-
Action::GetStats => Permission::Stream(action, self.stream.clone().unwrap()),
91-
Action::GetRetention => Permission::Stream(action, self.stream.clone().unwrap()),
92-
Action::PutRetention => Permission::Stream(action, self.stream.clone().unwrap()),
93-
Action::PutAlert => Permission::Stream(action, self.stream.clone().unwrap()),
94-
Action::GetAlert => Permission::Stream(action, self.stream.clone().unwrap()),
95-
Action::PutUser => Permission::Unit(action),
96-
Action::ListUser => Permission::Unit(action),
97-
Action::PutUserRoles => Permission::Unit(action),
98-
Action::GetUserRoles => Permission::Unit(action),
99-
Action::DeleteUser => Permission::Unit(action),
100-
Action::GetAbout => Permission::Unit(action),
101-
Action::QueryLLM => Permission::Unit(action),
102-
Action::PutRole => Permission::Unit(action),
103-
Action::GetRole => Permission::Unit(action),
104-
Action::DeleteRole => Permission::Unit(action),
105-
Action::ListRole => Permission::Unit(action),
106-
Action::All => Permission::Stream(action, self.stream.clone().unwrap()),
85+
Action::PutUser
86+
| Action::ListUser
87+
| Action::PutUserRoles
88+
| Action::GetUserRoles
89+
| Action::DeleteUser
90+
| Action::GetAbout
91+
| Action::QueryLLM
92+
| Action::PutRole
93+
| Action::GetRole
94+
| Action::DeleteRole
95+
| Action::ListRole
96+
| Action::CreateStream
97+
| Action::DeleteStream
98+
| Action::ListStream => Permission::Unit(action),
99+
Action::Ingest
100+
| Action::GetSchema
101+
| Action::GetStats
102+
| Action::GetRetention
103+
| Action::PutRetention
104+
| Action::PutAlert
105+
| Action::GetAlert
106+
| Action::All => Permission::Stream(action, self.stream.clone().unwrap()),
107107
};
108108
perms.push(perm);
109109
}
110-
perms.push(Permission::SelfRole);
110+
perms.push(Permission::SelfUser);
111111
perms
112112
}
113113
}

server/src/rbac/user.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,12 @@ impl User {
5757
)
5858
}
5959

60-
pub fn new_oauth(username: String, roles: HashSet<String>) -> Self {
60+
pub fn new_oauth(username: String, roles: HashSet<String>, user_info: UserInfo) -> Self {
6161
Self {
62-
ty: UserType::OAuth(OAuth { userid: username }),
62+
ty: UserType::OAuth(OAuth {
63+
userid: username,
64+
user_info,
65+
}),
6366
roles,
6467
}
6568
}
@@ -69,6 +72,7 @@ impl User {
6972
UserType::Native(Basic { ref username, .. }) => username,
7073
UserType::OAuth(OAuth {
7174
userid: ref username,
75+
..
7276
}) => username,
7377
}
7478
}
@@ -148,5 +152,36 @@ pub fn get_admin_user() -> User {
148152

149153
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
150154
pub struct OAuth {
151-
userid: String,
155+
pub userid: String,
156+
pub user_info: UserInfo,
157+
}
158+
159+
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
160+
pub struct UserInfo {
161+
#[serde(default)]
162+
/// User's full name for display purposes.
163+
pub name: Option<String>,
164+
#[serde(default)]
165+
pub preferred_username: Option<String>,
166+
#[serde(default)]
167+
pub picture: Option<url::Url>,
168+
#[serde(default)]
169+
pub email: Option<String>,
170+
#[serde(default)]
171+
pub gender: Option<String>,
172+
#[serde(default)]
173+
pub updated_at: Option<i64>,
174+
}
175+
176+
impl From<openid::Userinfo> for UserInfo {
177+
fn from(user: openid::Userinfo) -> Self {
178+
UserInfo {
179+
name: user.name,
180+
preferred_username: user.preferred_username,
181+
picture: user.picture,
182+
email: user.email,
183+
gender: user.gender,
184+
updated_at: user.updated_at,
185+
}
186+
}
152187
}

0 commit comments

Comments
 (0)