@@ -203,6 +203,56 @@ impl From<openid::Userinfo> for UserInfo {
203203 }
204204}
205205
206+ /// Represents a user in a UserGroup - simplified structure for both user types
207+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , serde:: Serialize , serde:: Deserialize ) ]
208+ pub struct GroupUser {
209+ pub userid : String ,
210+ pub username : String ,
211+ pub method : String ,
212+ }
213+
214+ impl GroupUser {
215+ pub fn username ( & self ) -> & str {
216+ & self . username
217+ }
218+
219+ pub fn userid ( & self ) -> & str {
220+ & self . userid
221+ }
222+
223+ pub fn is_oauth ( & self ) -> bool {
224+ self . method == "oauth"
225+ }
226+
227+ pub fn user_type ( & self ) -> & str {
228+ if self . is_oauth ( ) { "oauth" } else { "native" }
229+ }
230+
231+ pub fn from_user ( user : & User ) -> Self {
232+ match & user. ty {
233+ UserType :: Native ( Basic { username, .. } ) => GroupUser {
234+ userid : username. clone ( ) , // Same value for basic users
235+ username : username. clone ( ) ,
236+ method : "native" . to_string ( ) ,
237+ } ,
238+ UserType :: OAuth ( OAuth { userid, user_info } ) => {
239+ // For OAuth users, derive the display username from user_info
240+ let display_username = user_info
241+ . name
242+ . clone ( )
243+ . or_else ( || user_info. email . clone ( ) )
244+ . unwrap_or_else ( || userid. clone ( ) ) ; // fallback to userid if nothing else available
245+
246+ GroupUser {
247+ userid : userid. clone ( ) ,
248+ username : display_username,
249+ method : "oauth" . to_string ( ) ,
250+ }
251+ }
252+ }
253+ }
254+ }
255+
206256/// Logically speaking, UserGroup is a collection of roles and is applied to a collection of users.
207257///
208258/// The users present in a group inherit all the roles present in the group for as long as they are a part of the group.
@@ -212,7 +262,7 @@ pub struct UserGroup {
212262 // #[serde(default = "crate::utils::uid::gen")]
213263 // pub id: Ulid,
214264 pub roles : HashSet < String > ,
215- pub users : HashSet < String > ,
265+ pub users : HashSet < GroupUser > ,
216266}
217267
218268fn is_valid_group_name ( name : & str ) -> bool {
@@ -239,9 +289,9 @@ impl UserGroup {
239289 let mut non_existent_users = Vec :: new ( ) ;
240290 if !self . users . is_empty ( ) {
241291 // validate that the users exist
242- for user in & self . users {
243- if !users ( ) . contains_key ( user ) {
244- non_existent_users. push ( user . clone ( ) ) ;
292+ for group_user in & self . users {
293+ if !users ( ) . contains_key ( group_user . userid ( ) ) {
294+ non_existent_users. push ( group_user . username ( ) . to_string ( ) ) ;
245295 }
246296 }
247297 }
@@ -266,7 +316,7 @@ impl UserGroup {
266316 Ok ( ( ) )
267317 }
268318 }
269- pub fn new ( name : String , roles : HashSet < String > , users : HashSet < String > ) -> Self {
319+ pub fn new ( name : String , roles : HashSet < String > , users : HashSet < GroupUser > ) -> Self {
270320 UserGroup { name, roles, users }
271321 }
272322
@@ -276,20 +326,20 @@ impl UserGroup {
276326 }
277327 self . roles . extend ( roles) ;
278328 // also refresh all user sessions
279- for username in & self . users {
280- mut_sessions ( ) . remove_user ( username ) ;
329+ for group_user in & self . users {
330+ mut_sessions ( ) . remove_user ( group_user . userid ( ) ) ;
281331 }
282332 Ok ( ( ) )
283333 }
284334
285- pub fn add_users ( & mut self , users : HashSet < String > ) -> Result < ( ) , RBACError > {
335+ pub fn add_users ( & mut self , users : HashSet < GroupUser > ) -> Result < ( ) , RBACError > {
286336 if users. is_empty ( ) {
287337 return Ok ( ( ) ) ;
288338 }
289339 self . users . extend ( users. clone ( ) ) ;
290340 // also refresh all user sessions
291- for username in & users {
292- mut_sessions ( ) . remove_user ( username ) ;
341+ for group_user in & users {
342+ mut_sessions ( ) . remove_user ( group_user . userid ( ) ) ;
293343 }
294344 Ok ( ( ) )
295345 }
@@ -307,30 +357,61 @@ impl UserGroup {
307357 self . roles . clone_from ( & new_roles) ;
308358
309359 // also refresh all user sessions
310- for username in & self . users {
311- mut_sessions ( ) . remove_user ( username ) ;
360+ for group_user in & self . users {
361+ mut_sessions ( ) . remove_user ( group_user . userid ( ) ) ;
312362 }
313363 Ok ( ( ) )
314364 }
315365
316- pub fn remove_users ( & mut self , users : HashSet < String > ) -> Result < ( ) , RBACError > {
366+ pub fn remove_users ( & mut self , users : HashSet < GroupUser > ) -> Result < ( ) , RBACError > {
317367 if users. is_empty ( ) {
318368 return Ok ( ( ) ) ;
319369 }
320370 let new_users = HashSet :: from_iter ( self . users . difference ( & users) . cloned ( ) ) ;
321- let removed_users: HashSet < String > = self . users . intersection ( & users) . cloned ( ) . collect ( ) ;
371+ let removed_users: HashSet < GroupUser > = self . users . intersection ( & users) . cloned ( ) . collect ( ) ;
322372 if removed_users. is_empty ( ) {
323373 return Ok ( ( ) ) ;
324374 }
325375 // also refresh all user sessions
326- for username in & removed_users {
327- mut_sessions ( ) . remove_user ( username ) ;
376+ for group_user in & removed_users {
377+ mut_sessions ( ) . remove_user ( group_user . userid ( ) ) ;
328378 }
329379 self . users . clone_from ( & new_users) ;
330380
331381 Ok ( ( ) )
332382 }
333383
384+ /// Get all usernames in this group
385+ pub fn get_usernames ( & self ) -> Vec < String > {
386+ self . users
387+ . iter ( )
388+ . map ( |u| u. username ( ) . to_string ( ) )
389+ . collect ( )
390+ }
391+
392+ /// Add users by converting from User references
393+ pub fn add_users_from_user_refs ( & mut self , user_refs : & [ & User ] ) -> Result < ( ) , RBACError > {
394+ let group_users: HashSet < GroupUser > =
395+ user_refs. iter ( ) . map ( |u| GroupUser :: from_user ( u) ) . collect ( ) ;
396+ self . add_users ( group_users)
397+ }
398+
399+ /// Remove users by user ID
400+ pub fn remove_users_by_user_ids ( & mut self , user_ids : HashSet < String > ) -> Result < ( ) , RBACError > {
401+ if user_ids. is_empty ( ) {
402+ return Ok ( ( ) ) ;
403+ }
404+
405+ let users_to_remove: HashSet < GroupUser > = self
406+ . users
407+ . iter ( )
408+ . filter ( |u| user_ids. contains ( u. userid ( ) ) )
409+ . cloned ( )
410+ . collect ( ) ;
411+
412+ self . remove_users ( users_to_remove)
413+ }
414+
334415 pub async fn update_in_metadata ( & self ) -> Result < ( ) , RBACError > {
335416 let mut metadata = get_metadata ( ) . await ?;
336417 metadata. user_groups . retain ( |x| x. name != self . name ) ;
0 commit comments