1 //! Mockable users and groups. 2 //! 3 //! When you’re testing your code, you don’t want to actually rely on the 4 //! system actually having various users and groups present - it’s much better 5 //! to have a custom set of users that are *guaranteed* to be there, so you can 6 //! test against them. 7 //! 8 //! This module allows you to create these custom users and groups 9 //! definitions, then access them using the same `Users` trait as in the main 10 //! library, with few changes to your code. 11 //! 12 //! 13 //! ## Creating Mock Users 14 //! 15 //! The only thing a mock users table needs to know in advance is the UID of 16 //! the current user. Aside from that, you can add users and groups with 17 //! `add_user` and `add_group` to the table: 18 //! 19 //! ``` 20 //! use std::sync::Arc; 21 //! use users::mock::{MockUsers, User, Group}; 22 //! use users::os::unix::{UserExt, GroupExt}; 23 //! 24 //! let mut users = MockUsers::with_current_uid(1000); 25 //! let bobbins = User::new(1000, "Bobbins", 1000).with_home_dir("/home/bobbins"); 26 //! users.add_user(bobbins); 27 //! users.add_group(Group::new(100, "funkyppl")); 28 //! ``` 29 //! 30 //! The exports get re-exported into the mock module, for simpler `use` lines. 31 //! 32 //! 33 //! ## Using Mock Users 34 //! 35 //! To set your program up to use either type of `Users` table, make your 36 //! functions and structs accept a generic parameter that implements the `Users` 37 //! trait. Then, you can pass in a value of either Cache or Mock type. 38 //! 39 //! Here’s a complete example: 40 //! 41 //! ``` 42 //! use std::sync::Arc; 43 //! use users::{Users, UsersCache, User}; 44 //! use users::os::unix::UserExt; 45 //! use users::mock::MockUsers; 46 //! 47 //! fn print_current_username<U: Users>(users: &mut U) { 48 //! println!("Current user: {:?}", users.get_current_username()); 49 //! } 50 //! 51 //! let mut users = MockUsers::with_current_uid(1001); 52 //! users.add_user(User::new(1001, "fred", 101)); 53 //! print_current_username(&mut users); 54 //! 55 //! let mut actual_users = UsersCache::new(); 56 //! print_current_username(&mut actual_users); 57 //! ``` 58 59 use std::collections::HashMap; 60 use std::ffi::OsStr; 61 use std::sync::Arc; 62 63 pub use libc::{uid_t, gid_t}; 64 pub use base::{User, Group}; 65 pub use traits::{Users, Groups}; 66 67 68 /// A mocking users table that you can add your own users and groups to. 69 pub struct MockUsers { 70 users: HashMap<uid_t, Arc<User>>, 71 groups: HashMap<gid_t, Arc<Group>>, 72 uid: uid_t, 73 } 74 75 76 impl MockUsers { 77 78 /// Create a new, empty mock users table. with_current_uid(current_uid: uid_t) -> Self79 pub fn with_current_uid(current_uid: uid_t) -> Self { 80 Self { 81 users: HashMap::new(), 82 groups: HashMap::new(), 83 uid: current_uid, 84 } 85 } 86 87 /// Add a user to the users table. add_user(&mut self, user: User) -> Option<Arc<User>>88 pub fn add_user(&mut self, user: User) -> Option<Arc<User>> { 89 self.users.insert(user.uid(), Arc::new(user)) 90 } 91 92 /// Add a group to the groups table. add_group(&mut self, group: Group) -> Option<Arc<Group>>93 pub fn add_group(&mut self, group: Group) -> Option<Arc<Group>> { 94 self.groups.insert(group.gid(), Arc::new(group)) 95 } 96 } 97 98 99 impl Users for MockUsers { get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>>100 fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> { 101 self.users.get(&uid).cloned() 102 } 103 get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>>104 fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>> { 105 self.users.values().find(|u| u.name() == username.as_ref()).cloned() 106 } 107 get_current_uid(&self) -> uid_t108 fn get_current_uid(&self) -> uid_t { 109 self.uid 110 } 111 get_current_username(&self) -> Option<Arc<OsStr>>112 fn get_current_username(&self) -> Option<Arc<OsStr>> { 113 self.users.get(&self.uid).map(|u| Arc::clone(&u.name_arc)) 114 } 115 get_effective_uid(&self) -> uid_t116 fn get_effective_uid(&self) -> uid_t { 117 self.uid 118 } 119 get_effective_username(&self) -> Option<Arc<OsStr>>120 fn get_effective_username(&self) -> Option<Arc<OsStr>> { 121 self.users.get(&self.uid).map(|u| Arc::clone(&u.name_arc)) 122 } 123 } 124 125 126 impl Groups for MockUsers { get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>>127 fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> { 128 self.groups.get(&gid).cloned() 129 } 130 get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>>131 fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>> { 132 self.groups.values().find(|g| g.name() == group_name.as_ref()).cloned() 133 } 134 get_current_gid(&self) -> uid_t135 fn get_current_gid(&self) -> uid_t { 136 self.uid 137 } 138 get_current_groupname(&self) -> Option<Arc<OsStr>>139 fn get_current_groupname(&self) -> Option<Arc<OsStr>> { 140 self.groups.get(&self.uid).map(|u| Arc::clone(&u.name_arc)) 141 } 142 get_effective_gid(&self) -> uid_t143 fn get_effective_gid(&self) -> uid_t { 144 self.uid 145 } 146 get_effective_groupname(&self) -> Option<Arc<OsStr>>147 fn get_effective_groupname(&self) -> Option<Arc<OsStr>> { 148 self.groups.get(&self.uid).map(|u| Arc::clone(&u.name_arc)) 149 } 150 } 151 152 153 #[cfg(test)] 154 mod test { 155 use super::MockUsers; 156 use base::{User, Group}; 157 use traits::{Users, Groups}; 158 159 use std::ffi::OsStr; 160 use std::sync::Arc; 161 162 #[test] current_username()163 fn current_username() { 164 let mut users = MockUsers::with_current_uid(1337); 165 users.add_user(User::new(1337, "fred", 101)); 166 assert_eq!(Some(Arc::from(OsStr::new("fred"))), 167 users.get_current_username()) 168 } 169 170 #[test] no_current_username()171 fn no_current_username() { 172 let users = MockUsers::with_current_uid(1337); 173 assert_eq!(None, users.get_current_username()) 174 } 175 176 #[test] uid()177 fn uid() { 178 let mut users = MockUsers::with_current_uid(0); 179 users.add_user(User::new(1337, "fred", 101)); 180 assert_eq!(Some(Arc::from(OsStr::new("fred"))), 181 users.get_user_by_uid(1337).map(|u| Arc::clone(&u.name_arc))) 182 } 183 184 #[test] username()185 fn username() { 186 let mut users = MockUsers::with_current_uid(1337); 187 users.add_user(User::new(1440, "fred", 101)); 188 assert_eq!(Some(1440), 189 users.get_user_by_name("fred").map(|u| u.uid())) 190 } 191 192 #[test] no_username()193 fn no_username() { 194 let mut users = MockUsers::with_current_uid(1337); 195 users.add_user(User::new(1337, "fred", 101)); 196 assert_eq!(None, 197 users.get_user_by_name("criminy").map(|u| u.uid())) 198 } 199 200 #[test] no_uid()201 fn no_uid() { 202 let users = MockUsers::with_current_uid(0); 203 assert_eq!(None, 204 users.get_user_by_uid(1337).map(|u| Arc::clone(&u.name_arc))) 205 } 206 207 #[test] gid()208 fn gid() { 209 let mut users = MockUsers::with_current_uid(0); 210 users.add_group(Group::new(1337, "fred")); 211 assert_eq!(Some(Arc::from(OsStr::new("fred"))), 212 users.get_group_by_gid(1337).map(|g| Arc::clone(&g.name_arc))) 213 } 214 215 #[test] group_name()216 fn group_name() { 217 let mut users = MockUsers::with_current_uid(0); 218 users.add_group(Group::new(1337, "fred")); 219 assert_eq!(Some(1337), 220 users.get_group_by_name("fred").map(|g| g.gid())) 221 } 222 223 #[test] no_group_name()224 fn no_group_name() { 225 let mut users = MockUsers::with_current_uid(0); 226 users.add_group(Group::new(1337, "fred")); 227 assert_eq!(None, 228 users.get_group_by_name("santa").map(|g| g.gid())) 229 } 230 231 #[test] no_gid()232 fn no_gid() { 233 let users = MockUsers::with_current_uid(0); 234 assert_eq!(None, 235 users.get_group_by_gid(1337).map(|g| Arc::clone(&g.name_arc))) 236 } 237 } 238