Merge branch 'graft'

This commit is contained in:
Ben S
2016-01-28 16:07:01 +00:00
9 changed files with 619 additions and 195 deletions
+20 -35
View File
@@ -31,7 +31,7 @@ It does not (yet) offer *editing* functionality; the objects returned are read-o
The function `get_current_uid` returns a `uid_t` value representing the user currently running the program, and the `get_user_by_uid` function scans the users database and returns a User object with the users information.
This function returns `None` when there is no user for that ID.
A `User` object has the following public fields:
A `User` object has the following accessors:
- **uid:** The users ID
- **name:** The users name
@@ -42,7 +42,7 @@ Here is a complete example that prints out the current users name:
```rust
use users::{get_user_by_uid, get_current_uid};
let user = get_user_by_uid(get_current_uid()).unwrap();
println!("Hello, {}!", user.name);
println!("Hello, {}!", user.name());
```
This code assumes (with `unwrap()`) that the user hasnt been deleted after the program has started running.
@@ -63,11 +63,11 @@ To introduce a cache, create a new `OSUsers` object and call the same methods on
For example:
```rust
use users::{Users, OSUsers};
let mut cache = OSUsers::empty_cache();
use users::{Users, Groups, UsersCache};
let mut cache = UsersCache::new();
let uid = cache.get_current_uid();
let user = cache.get_user_by_uid(uid).unwrap();
println!("Hello again, {}!", user.name);
println!("Hello again, {}!", user.name());
```
This cache is **only additive**: its not possible to drop it, or erase selected entries, as when the database may have been modified, its best to start entirely afresh.
@@ -77,22 +77,18 @@ So to accomplish this, just start using a new `OSUsers` object.
## Groups
Finally, its possible to get groups in a similar manner.
A `Group` object has the following public fields:
A `Group` object has the following accessors:
- **gid:** The groups ID
- **name:** The groups name
- **members:** Vector of names of the users that belong to this group
And again, a complete example:
```rust
use users::{Users, OSUsers};
let mut cache = OSUsers::empty_cache();
use users::{Users, Groups, UsersCache};
let mut cache = UsersCache::new();
let group = cache.get_group_by_name("admin").expect("No such group 'admin'!");
println!("The '{}' group has the ID {}", group.name, group.gid);
for member in group.members.into_iter() {
println!("{} is a member of the group", member);
}
println!("The '{}' group has the ID {}", group.name(), group.gid());
```
@@ -117,19 +113,13 @@ Aside from that, you can add users and groups with `add_user` and `add_group` to
```rust
use users::mock::{MockUsers, User, Group};
use users::os::unix::{UserExt, GroupExt};
use std::sync::Arc;
let mut users = MockUsers::with_current_uid(1000);
users.add_user(User {
uid: 1000,
name: "Bobbins".to_string(),
primary_group: 100,
home_dir: "/home/bobbins".to_string(),
shell: "/bin/bash".to_string(),
});
users.add_group(Group {
gid: 100,
name: "funkyppl".to_string(),
members: vec![ "other_person".to_string() ]
});
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.
@@ -143,24 +133,19 @@ Then, you can pass in an object of either OS or Mock type.
Here's a complete example:
```rust
use users::{Users, OSUsers, User};
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 {
uid: 1001,
name: "fred".to_string(),
primary_group: 101,
home_dir: "/home/fred".to_string(),
shell: "/bin/bash".to_string(),
});
users.add_user(User::new(1001, "fred", 101));
print_current_username(&mut users);
let mut actual_users = OSUsers::empty_cache();
let mut actual_users = UsersCache::new();
print_current_username(&mut actual_users);
```
+3 -12
View File
@@ -8,17 +8,8 @@ fn main() {
println!("Your UID is {}", current_uid);
let you = cache.get_user_by_uid(current_uid).expect("No entry for current user!");
println!("Your username is {}", you.name);
println!("Your username is {}", you.name());
let primary_group = cache.get_group_by_gid(you.primary_group).expect("No entry for your primary group!");
println!("Your primary group has ID {} and name {}", primary_group.gid, primary_group.name);
if primary_group.members.is_empty() {
println!("There are no other members of that group.");
}
else {
for username in primary_group.members.iter() {
println!("User {} is also a member of that group.", username);
}
}
let primary_group = cache.get_group_by_gid(you.primary_group_id()).expect("No entry for your primary group!");
println!("Your primary group has ID {} and name {}", primary_group.gid(), primary_group.name());
}
+33
View File
@@ -0,0 +1,33 @@
extern crate users;
use users::{Users, Groups, UsersCache};
use users::os::unix::{UserExt, GroupExt};
//use users::os::bsd::UserExt as BSDUserExt;
fn main() {
let cache = UsersCache::new();
let current_uid = cache.get_current_uid();
println!("Your UID is {}", current_uid);
let you = cache.get_user_by_uid(current_uid).expect("No entry for current user!");
println!("Your username is {}", you.name());
println!("Your shell is {}", you.shell().display());
println!("Your home directory is {}", you.home_dir().display());
// The two fields below are only available on BSD systems.
// Linux systems dont have the fields in their `passwd` structs!
//println!("Your password change timestamp is {}", you.password_change_time());
//println!("Your password expiry timestamp is {}", you.password_expire_time());
let primary_group = cache.get_group_by_gid(you.primary_group_id()).expect("No entry for your primary group!");
println!("Your primary group has ID {} and name {}", primary_group.gid(), primary_group.name());
if primary_group.members().is_empty() {
println!("There are no other members of that group.");
}
else {
for username in primary_group.members() {
println!("User {} is also a member of that group.", username);
}
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ fn main() {
for uid in LO .. HI {
let cache = cache.lock().unwrap(); // Re-unlock the mutex
if let Some(u) = cache.get_user_by_uid(uid) { // Re-query our cache!
println!("User #{} is {}", u.uid, u.name)
println!("User #{} is {}", u.uid(), u.name())
}
else {
println!("User #{} does not exist", uid);
+444 -75
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -134,7 +134,7 @@ impl Users for UsersCache {
Vacant(entry) => {
match super::get_user_by_uid(uid) {
Some(user) => {
let newsername = user.name.clone();
let newsername = user.name_arc.clone();
let mut users_backward = self.users.backward.borrow_mut();
users_backward.insert(newsername, Some(uid));
@@ -161,7 +161,7 @@ impl Users for UsersCache {
Vacant(entry) => {
match super::get_user_by_name(username) {
Some(user) => {
let uid = user.uid;
let uid = user.uid();
let user_arc = Arc::new(user);
let mut users_forward = self.users.forward.borrow_mut();
@@ -199,7 +199,7 @@ impl Users for UsersCache {
fn get_current_username(&self) -> Option<Arc<String>> {
let uid = self.get_current_uid();
self.get_user_by_uid(uid).map(|u| u.name.clone())
self.get_user_by_uid(uid).map(|u| u.name_arc.clone())
}
fn get_effective_uid(&self) -> uid_t {
@@ -215,7 +215,7 @@ impl Users for UsersCache {
fn get_effective_username(&self) -> Option<Arc<String>> {
let uid = self.get_effective_uid();
self.get_user_by_uid(uid).map(|u| u.name.clone())
self.get_user_by_uid(uid).map(|u| u.name_arc.clone())
}
}
@@ -228,7 +228,7 @@ impl Groups for UsersCache {
let group = super::get_group_by_gid(gid);
match group {
Some(group) => {
let new_group_name = group.name.clone();
let new_group_name = group.name_arc.clone();
let mut groups_backward = self.groups.backward.borrow_mut();
groups_backward.insert(new_group_name, Some(gid));
@@ -257,7 +257,7 @@ impl Groups for UsersCache {
match user {
Some(group) => {
let group_arc = Arc::new(group.clone());
let gid = group.gid;
let gid = group.gid();
let mut groups_forward = self.groups.forward.borrow_mut();
groups_forward.insert(gid, Some(group_arc.clone()));
@@ -294,7 +294,7 @@ impl Groups for UsersCache {
fn get_current_groupname(&self) -> Option<Arc<String>> {
let gid = self.get_current_gid();
self.get_group_by_gid(gid).map(|g| g.name.clone())
self.get_group_by_gid(gid).map(|g| g.name_arc.clone())
}
fn get_effective_gid(&self) -> gid_t {
@@ -310,6 +310,6 @@ impl Groups for UsersCache {
fn get_effective_groupname(&self) -> Option<Arc<String>> {
let gid = self.get_effective_gid();
self.get_group_by_gid(gid).map(|g| g.name.clone())
self.get_group_by_gid(gid).map(|g| g.name_arc.clone())
}
}
+19 -11
View File
@@ -24,7 +24,7 @@
//! users database and returns a User object with the users information. This
//! function returns `None` when there is no user for that ID.
//!
//! A `User` object has the following public fields:
//! A `User` object has the following accessors:
//!
//! - **uid:** The users ID
//! - **name:** The users name
@@ -35,7 +35,7 @@
//! ```rust
//! use users::{get_user_by_uid, get_current_uid};
//! let user = get_user_by_uid(get_current_uid()).unwrap();
//! println!("Hello, {}!", user.name);
//! println!("Hello, {}!", user.name());
//! ```
//!
//! This code assumes (with `unwrap()`) that the user hasnt been deleted after
@@ -68,7 +68,7 @@
//! let mut cache = UsersCache::new();
//! let uid = cache.get_current_uid();
//! let user = cache.get_user_by_uid(uid).unwrap();
//! println!("Hello again, {}!", user.name);
//! println!("Hello again, {}!", user.name());
//! ```
//!
//! This cache is **only additive**: its not possible to drop it, or erase
@@ -80,22 +80,18 @@
//! ## Groups
//!
//! Finally, its possible to get groups in a similar manner.
//! A `Group` has the following public fields:
//! A `Group` has the following accessors:
//!
//! - **gid:** The groups ID
//! - **name:** The groups name
//! - **members:** Vector of names of the users that belong to this group
//!
//! And again, a complete example:
//!
//! ```rust
//! ```no_run
//! use users::{Users, Groups, UsersCache};
//! let mut cache = UsersCache::new();
//! let group = cache.get_group_by_name("admin").expect("No such group 'admin'!");
//! println!("The '{}' group has the ID {}", group.name, group.gid);
//! for member in &group.members {
//! println!("{} is a member of the group", member);
//! }
//! println!("The '{}' group has the ID {}", group.name(), group.gid());
//! ```
//!
//!
@@ -108,11 +104,23 @@
//! Use the mocking module to create custom tables to test your code for these
//! edge cases.
#![warn(missing_copy_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused_extern_crates, unused_qualifications)]
extern crate libc;
pub use libc::{uid_t, gid_t};
mod base;
pub use base::*;
pub use base::{User, Group, os};
pub use base::{get_user_by_uid, get_user_by_name};
pub use base::{get_group_by_gid, get_group_by_name};
pub use base::{get_current_uid, get_current_username};
pub use base::{get_effective_uid, get_effective_username};
pub use base::{get_current_gid, get_current_groupname};
pub use base::{get_effective_gid, get_effective_groupname};
pub mod cache;
pub use cache::UsersCache;
+30 -27
View File
@@ -18,11 +18,13 @@
//!
//! ```rust
//! use users::mock::{MockUsers, User, Group};
//! use users::os::unix::{UserExt, GroupExt};
//! use std::sync::Arc;
//!
//! let mut users = MockUsers::with_current_uid(1000);
//! users.add_user(User { uid: 1000, name: Arc::new("Bobbins".to_string()), primary_group: 100, home_dir: "/home/bobbins".to_string(), shell: "/bin/bash".to_string() });
//! users.add_group(Group { gid: 100, name: Arc::new("funkyppl".to_string()), members: vec![ "other_person".to_string() ] });
//! 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.
@@ -38,6 +40,7 @@
//!
//! ```rust
//! use users::{Users, UsersCache, User};
//! use users::os::unix::UserExt;
//! use users::mock::MockUsers;
//! use std::sync::Arc;
//!
@@ -46,7 +49,7 @@
//! }
//!
//! let mut users = MockUsers::with_current_uid(1001);
//! users.add_user(User { uid: 1001, name: Arc::new("fred".to_string()), primary_group: 101 , home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string()});
//! users.add_user(User::new(1001, "fred", 101));
//! print_current_username(&mut users);
//!
//! let mut actual_users = UsersCache::new();
@@ -81,12 +84,12 @@ impl MockUsers {
/// 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))
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))
self.groups.insert(group.gid(), Arc::new(group))
}
}
@@ -96,7 +99,7 @@ impl Users for MockUsers {
}
fn get_user_by_name(&self, username: &str) -> Option<Arc<User>> {
self.users.values().find(|u| &*u.name == username).cloned()
self.users.values().find(|u| u.name() == username).cloned()
}
fn get_current_uid(&self) -> uid_t {
@@ -104,7 +107,7 @@ impl Users for MockUsers {
}
fn get_current_username(&self) -> Option<Arc<String>> {
self.users.get(&self.uid).map(|u| u.name.clone())
self.users.get(&self.uid).map(|u| u.name_arc.clone())
}
fn get_effective_uid(&self) -> uid_t {
@@ -112,7 +115,7 @@ impl Users for MockUsers {
}
fn get_effective_username(&self) -> Option<Arc<String>> {
self.users.get(&self.uid).map(|u| u.name.clone())
self.users.get(&self.uid).map(|u| u.name_arc.clone())
}
}
@@ -122,7 +125,7 @@ impl Groups for MockUsers {
}
fn get_group_by_name(&self, group_name: &str) -> Option<Arc<Group>> {
self.groups.values().find(|g| &*g.name == group_name).cloned()
self.groups.values().find(|g| g.name() == group_name).cloned()
}
fn get_current_gid(&self) -> uid_t {
@@ -130,7 +133,7 @@ impl Groups for MockUsers {
}
fn get_current_groupname(&self) -> Option<Arc<String>> {
self.groups.get(&self.uid).map(|u| u.name.clone())
self.groups.get(&self.uid).map(|u| u.name_arc.clone())
}
fn get_effective_gid(&self) -> uid_t {
@@ -138,13 +141,13 @@ impl Groups for MockUsers {
}
fn get_effective_groupname(&self) -> Option<Arc<String>> {
self.groups.get(&self.uid).map(|u| u.name.clone())
self.groups.get(&self.uid).map(|u| u.name_arc.clone())
}
}
#[cfg(test)]
mod test {
use super::{MockUsers};
use super::MockUsers;
use base::{User, Group};
use traits::{Users, Groups};
use std::sync::Arc;
@@ -152,7 +155,7 @@ mod test {
#[test]
fn current_username() {
let mut users = MockUsers::with_current_uid(1337);
users.add_user(User { uid: 1337, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
users.add_user(User::new(1337, "fred", 101));
assert_eq!(Some(Arc::new("fred".into())), users.get_current_username())
}
@@ -165,54 +168,54 @@ mod test {
#[test]
fn uid() {
let mut users = MockUsers::with_current_uid(0);
users.add_user(User { uid: 1337, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(Some(Arc::new("fred".into())), users.get_user_by_uid(1337).map(|u| u.name.clone()))
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 { uid: 1440, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(Some(1440), users.get_user_by_name("fred").map(|u| u.uid))
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 { uid: 1440, name: Arc::new("fred".to_string()), primary_group: 101, home_dir: "/home/fred".to_string(), shell: "/bin/bash".to_string() });
assert_eq!(None, users.get_user_by_name("criminy").map(|u| u.uid))
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.clone()))
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 { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
assert_eq!(Some(Arc::new("fred".into())), users.get_group_by_gid(1337).map(|g| g.name.clone()))
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 { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
assert_eq!(Some(1337), users.get_group_by_name("fred").map(|g| g.gid))
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 { gid: 1337, name: Arc::new("fred".to_string()), members: vec![], });
assert_eq!(None, users.get_group_by_name("santa").map(|g| g.gid))
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.clone()))
assert_eq!(None, users.get_group_by_gid(1337).map(|g| g.name_arc.clone()))
}
}
+61 -26
View File
@@ -1,3 +1,5 @@
//! Functions for switching the running processs user or group.
use std::io::{Error as IOError, Result as IOResult};
use libc::{uid_t, gid_t, c_int};
@@ -16,60 +18,86 @@ extern {
}
/// Sets current user for the running process, requires root priviledges.
/// Sets the **current user** for the running process to the one with the
/// given user ID. Uses `setuid` internally.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
pub fn set_current_uid(uid: uid_t) -> IOResult<()> {
match unsafe { setuid(uid) } {
0 => Ok(()),
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
_ => unreachable!()
n => unreachable!("setuid returned {}", n)
}
}
/// Set current group for the running process, requires root priviledges.
/// Sets the **current group** for the running process to the one with the
/// given group ID. Uses `setgid` internally.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
pub fn set_current_gid(gid: gid_t) -> IOResult<()> {
match unsafe { setgid(gid) } {
0 => Ok(()),
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
_ => unreachable!()
n => unreachable!("setgid returned {}", n)
}
}
/// Set effective user for the running process, requires root priviledges.
/// Sets the **effective user** for the running process to the one with the
/// given user ID. Uses `seteuid` internally.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
pub fn set_effective_uid(uid: uid_t) -> IOResult<()> {
match unsafe { seteuid(uid) } {
0 => Ok(()),
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
_ => unreachable!()
n => unreachable!("seteuid returned {}", n)
}
}
/// Set effective user for the running process, requires root priviledges.
/// Sets the **effective group** for the running process to the one with the
/// given group ID. Uses `setegid` internally.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
pub fn set_effective_gid(gid: gid_t) -> IOResult<()> {
match unsafe { setegid(gid) } {
0 => Ok(()),
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
_ => unreachable!()
n => unreachable!("setegid returned {}", n)
}
}
/// Atomically set current and effective user for the running process, requires root priviledges.
/// Sets both the **current user** and the **effective user** for the running
/// process to the ones with the given user IDs. Uses `setreuid` internally.
///
/// Typically, trying to switch to anyone other than the user already running
/// the process requires root privileges.
pub fn set_both_uid(ruid: uid_t, euid: uid_t) -> IOResult<()> {
match unsafe { setreuid(ruid, euid) } {
0 => Ok(()),
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
_ => unreachable!()
n => unreachable!("setreuid returned {}", n)
}
}
/// Atomically set current and effective group for the running process, requires root priviledges.
/// Sets both the **current group** and the **effective group** for the
/// running process to the ones with the given group IDs. Uses `setregid`
/// internally.
///
/// Typically, trying to switch to any group other than the group already
/// running the process requires root privileges.
pub fn set_both_gid(rgid: gid_t, egid: gid_t) -> IOResult<()> {
match unsafe { setregid(rgid, egid) } {
0 => Ok(()),
0 => Ok(()),
-1 => Err(IOError::last_os_error()),
_ => unreachable!()
n => unreachable!("setregid returned {}", n)
}
}
/// Guard returned from a `switch_user_group` call.
pub struct SwitchUserGuard {
uid: uid_t,
gid: gid_t,
@@ -84,21 +112,28 @@ impl Drop for SwitchUserGuard {
}
}
/// Safely switch user and group for the current scope.
/// Requires root access.
/// Sets the **effective user** and the **effective group** for the current
/// scope.
///
/// Typically, trying to switch to any user or group other than the ones already
/// running the process requires root privileges.
///
/// **Use with care!** Possible security issues can happen, as Rust doesn't
/// guarantee running the destructor! If in doubt run `drop()` method on the
/// guard value manually!
///
/// ### Examples
///
/// ```no_run
/// use users::switch::switch_user_group;
///
/// ```ignore
/// {
/// let _guard = switch_user_group(1001, 1001);
/// // current and effective user and group ids are 1001
/// }
/// // back to the old values
/// ```
///
/// Use with care! Possible security issues can happen, as Rust doesn't
/// guarantee running the destructor! If in doubt run `drop()` method
/// on the guard value manually!
pub fn switch_user_group(uid: uid_t, gid: gid_t) -> Result<SwitchUserGuard, IOError> {
pub fn switch_user_group(uid: uid_t, gid: gid_t) -> IOResult<SwitchUserGuard> {
let current_state = SwitchUserGuard {
uid: get_effective_uid(),
gid: get_effective_gid(),