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