1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//! Mockable users and groups.
//!
//! When you’re testing your code, you don’t want to actually rely on the
//! system actually having various users and groups present - it’s much better
//! to have a custom set of users that are *guaranteed* to be there, so you can
//! test against them.
//!
//! This sub-library allows you to create these custom users and groups
//! definitions, then access them using the same `Users` trait as in the main
//! library, with few changes to your code.
//!
//!
//! ## Creating Mock Users
//!
//! The only thing a mock users table needs to know in advance is the UID of
//! the current user. Aside from that, you can add users and groups with
//! `add_user` and `add_group` to the table:
//!
//! ```
//! use users::mock::{MockUsers, User, Group};
//! use users::os::unix::{UserExt, GroupExt};
//! use std::sync::Arc;
//!
//! let mut users = MockUsers::with_current_uid(1000);
//! let bobbins = User::new(1000, "Bobbins", 1000).with_home_dir("/home/bobbins");
//! users.add_user(bobbins);
//! users.add_group(Group::new(100, "funkyppl"));
//! ```
//!
//! The exports get re-exported into the mock module, for simpler `use` lines.
//!
//!
//! ## Using Mock Users
//!
//! To set your program up to use either type of `Users` table, make your
//! functions and structs accept a generic parameter that implements the `Users`
//! trait. Then, you can pass in a value of either Cache or Mock type.
//!
//! Here’s a complete example:
//!
//! ```
//! use users::{Users, UsersCache, User};
//! use users::os::unix::UserExt;
//! use users::mock::MockUsers;
//! use std::sync::Arc;
//!
//! fn print_current_username<U: Users>(users: &mut U) {
//!     println!("Current user: {:?}", users.get_current_username());
//! }
//!
//! let mut users = MockUsers::with_current_uid(1001);
//! users.add_user(User::new(1001, "fred", 101));
//! print_current_username(&mut users);
//!
//! let mut actual_users = UsersCache::new();
//! print_current_username(&mut actual_users);
//! ```

use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::sync::Arc;

pub use libc::{uid_t, gid_t};
pub use base::{User, Group};
pub use traits::{Users, Groups};


/// A mocking users table that you can add your own users and groups to.
pub struct MockUsers {
    users: HashMap<uid_t, Arc<User>>,
    groups: HashMap<gid_t, Arc<Group>>,
    uid: uid_t,
}


impl MockUsers {

    /// Create a new, empty mock users table.
    pub fn with_current_uid(current_uid: uid_t) -> Self {
        MockUsers {
            users: HashMap::new(),
            groups: HashMap::new(),
            uid: current_uid,
        }
    }

    /// Add a user to the users table.
    pub fn add_user(&mut self, user: User) -> Option<Arc<User>> {
        self.users.insert(user.uid(), Arc::new(user))
    }

    /// Add a group to the groups table.
    pub fn add_group(&mut self, group: Group) -> Option<Arc<Group>> {
        self.groups.insert(group.gid(), Arc::new(group))
    }
}


impl Users for MockUsers {
    fn get_user_by_uid(&self, uid: uid_t) -> Option<Arc<User>> {
        self.users.get(&uid).cloned()
    }

    fn get_user_by_name<S: AsRef<OsStr> + ?Sized>(&self, username: &S) -> Option<Arc<User>> {
        self.users.values().find(|u| u.name() == username.as_ref()).cloned()
    }

    fn get_current_uid(&self) -> uid_t {
        self.uid
    }

    fn get_current_username(&self) -> Option<Arc<OsString>> {
        self.users.get(&self.uid).map(|u| u.name_arc.clone())
    }

    fn get_effective_uid(&self) -> uid_t {
        self.uid
    }

    fn get_effective_username(&self) -> Option<Arc<OsString>> {
        self.users.get(&self.uid).map(|u| u.name_arc.clone())
    }
}


impl Groups for MockUsers {
    fn get_group_by_gid(&self, gid: gid_t) -> Option<Arc<Group>> {
        self.groups.get(&gid).cloned()
    }

    fn get_group_by_name<S: AsRef<OsStr> + ?Sized>(&self, group_name: &S) -> Option<Arc<Group>> {
        self.groups.values().find(|g| g.name() == group_name.as_ref()).cloned()
    }

    fn get_current_gid(&self) -> uid_t {
        self.uid
    }

    fn get_current_groupname(&self) -> Option<Arc<OsString>> {
        self.groups.get(&self.uid).map(|u| u.name_arc.clone())
    }

    fn get_effective_gid(&self) -> uid_t {
        self.uid
    }

    fn get_effective_groupname(&self) -> Option<Arc<OsString>> {
        self.groups.get(&self.uid).map(|u| u.name_arc.clone())
    }
}


#[cfg(test)]
mod test {
    use super::MockUsers;
    use base::{User, Group};
    use traits::{Users, Groups};
    use std::sync::Arc;

    #[test]
    fn current_username() {
        let mut users = MockUsers::with_current_uid(1337);
        users.add_user(User::new(1337, "fred", 101));
        assert_eq!(Some(Arc::new("fred".into())), users.get_current_username())
    }

    #[test]
    fn no_current_username() {
        let users = MockUsers::with_current_uid(1337);
        assert_eq!(None, users.get_current_username())
    }

    #[test]
    fn uid() {
        let mut users = MockUsers::with_current_uid(0);
        users.add_user(User::new(1337, "fred", 101));
        assert_eq!(Some(Arc::new("fred".into())), users.get_user_by_uid(1337).map(|u| u.name_arc.clone()))
    }

    #[test]
    fn username() {
        let mut users = MockUsers::with_current_uid(1337);
        users.add_user(User::new(1440, "fred", 101));
        assert_eq!(Some(1440), users.get_user_by_name("fred").map(|u| u.uid()))
    }

    #[test]
    fn no_username() {
        let mut users = MockUsers::with_current_uid(1337);
        users.add_user(User::new(1337, "fred", 101));
        assert_eq!(None, users.get_user_by_name("criminy").map(|u| u.uid()))
    }

    #[test]
    fn no_uid() {
        let users = MockUsers::with_current_uid(0);
        assert_eq!(None, users.get_user_by_uid(1337).map(|u| u.name_arc.clone()))
    }

    #[test]
    fn gid() {
        let mut users = MockUsers::with_current_uid(0);
        users.add_group(Group::new(1337, "fred"));
        assert_eq!(Some(Arc::new("fred".into())), users.get_group_by_gid(1337).map(|g| g.name_arc.clone()))
    }

    #[test]
    fn group_name() {
        let mut users = MockUsers::with_current_uid(0);
        users.add_group(Group::new(1337, "fred"));
        assert_eq!(Some(1337), users.get_group_by_name("fred").map(|g| g.gid()))
    }

    #[test]
    fn no_group_name() {
        let mut users = MockUsers::with_current_uid(0);
        users.add_group(Group::new(1337, "fred"));
        assert_eq!(None, users.get_group_by_name("santa").map(|g| g.gid()))
    }

    #[test]
    fn no_gid() {
        let users = MockUsers::with_current_uid(0);
        assert_eq!(None, users.get_group_by_gid(1337).map(|g| g.name_arc.clone()))
    }
}