mirror of
https://github.com/kanidm/kanidm.git
synced 2025-04-29 21:55:05 +02:00
329 lines
9.9 KiB
Rust
329 lines
9.9 KiB
Rust
use kanidm_unix_common::client_sync::DaemonClientBlocking;
|
|
use kanidm_unix_common::unix_config::PamNssConfig;
|
|
use kanidm_unix_common::unix_passwd::{
|
|
read_etc_group_file, read_etc_passwd_file, EtcGroup, EtcUser,
|
|
};
|
|
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, NssGroup, NssUser};
|
|
|
|
use libnss::group::Group;
|
|
use libnss::interop::Response;
|
|
use libnss::passwd::Passwd;
|
|
|
|
#[cfg(test)]
|
|
use kanidm_unix_common::client_sync::UnixStream;
|
|
|
|
pub enum RequestOptions {
|
|
Main {
|
|
config_path: &'static str,
|
|
},
|
|
#[cfg(test)]
|
|
Test {
|
|
socket: Option<UnixStream>,
|
|
users: Vec<EtcUser>,
|
|
groups: Vec<EtcGroup>,
|
|
},
|
|
}
|
|
|
|
enum Source {
|
|
Daemon(DaemonClientBlocking),
|
|
Fallback {
|
|
users: Vec<EtcUser>,
|
|
groups: Vec<EtcGroup>,
|
|
},
|
|
}
|
|
|
|
impl RequestOptions {
|
|
fn connect_to_daemon(self) -> Source {
|
|
match self {
|
|
RequestOptions::Main { config_path } => {
|
|
let maybe_client = PamNssConfig::new()
|
|
.read_options_from_optional_config(config_path)
|
|
.ok()
|
|
.and_then(|cfg| {
|
|
DaemonClientBlocking::new(cfg.sock_path.as_str(), cfg.unix_sock_timeout)
|
|
.ok()
|
|
});
|
|
|
|
if let Some(client) = maybe_client {
|
|
Source::Daemon(client)
|
|
} else {
|
|
let users = read_etc_passwd_file("/etc/passwd").unwrap_or_default();
|
|
|
|
let groups = read_etc_group_file("/etc/group").unwrap_or_default();
|
|
|
|
Source::Fallback { users, groups }
|
|
}
|
|
}
|
|
#[cfg(test)]
|
|
RequestOptions::Test {
|
|
socket,
|
|
users,
|
|
groups,
|
|
} => {
|
|
if let Some(socket) = socket {
|
|
Source::Daemon(DaemonClientBlocking::from(socket))
|
|
} else {
|
|
Source::Fallback { users, groups }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_all_user_entries(req_options: RequestOptions) -> Response<Vec<Passwd>> {
|
|
match req_options.connect_to_daemon() {
|
|
Source::Daemon(mut daemon_client) => {
|
|
let req = ClientRequest::NssAccounts;
|
|
|
|
daemon_client
|
|
.call_and_wait(&req, None)
|
|
.map(|r| match r {
|
|
ClientResponse::NssAccounts(l) => {
|
|
l.into_iter().map(passwd_from_nssuser).collect()
|
|
}
|
|
_ => Vec::new(),
|
|
})
|
|
.map(Response::Success)
|
|
.unwrap_or_else(|_| Response::Success(vec![]))
|
|
}
|
|
Source::Fallback { users, groups: _ } => {
|
|
if users.is_empty() {
|
|
return Response::Unavail;
|
|
}
|
|
|
|
let users = users.into_iter().map(passwd_from_etcuser).collect();
|
|
|
|
Response::Success(users)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_user_entry_by_uid(uid: libc::uid_t, req_options: RequestOptions) -> Response<Passwd> {
|
|
match req_options.connect_to_daemon() {
|
|
Source::Daemon(mut daemon_client) => {
|
|
let req = ClientRequest::NssAccountByUid(uid);
|
|
daemon_client
|
|
.call_and_wait(&req, None)
|
|
.map(|r| match r {
|
|
ClientResponse::NssAccount(opt) => opt
|
|
.map(passwd_from_nssuser)
|
|
.map(Response::Success)
|
|
.unwrap_or_else(|| Response::NotFound),
|
|
_ => Response::NotFound,
|
|
})
|
|
.unwrap_or_else(|_| Response::NotFound)
|
|
}
|
|
Source::Fallback { users, groups: _ } => {
|
|
if users.is_empty() {
|
|
return Response::Unavail;
|
|
}
|
|
|
|
let user = users
|
|
.into_iter()
|
|
.filter_map(|etcuser| {
|
|
if etcuser.uid == uid {
|
|
Some(passwd_from_etcuser(etcuser))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.next();
|
|
|
|
if let Some(user) = user {
|
|
Response::Success(user)
|
|
} else {
|
|
Response::NotFound
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_user_entry_by_name(name: String, req_options: RequestOptions) -> Response<Passwd> {
|
|
match req_options.connect_to_daemon() {
|
|
Source::Daemon(mut daemon_client) => {
|
|
let req = ClientRequest::NssAccountByName(name);
|
|
daemon_client
|
|
.call_and_wait(&req, None)
|
|
.map(|r| match r {
|
|
ClientResponse::NssAccount(opt) => opt
|
|
.map(passwd_from_nssuser)
|
|
.map(Response::Success)
|
|
.unwrap_or_else(|| Response::NotFound),
|
|
_ => Response::NotFound,
|
|
})
|
|
.unwrap_or_else(|_| Response::NotFound)
|
|
}
|
|
Source::Fallback { users, groups: _ } => {
|
|
if users.is_empty() {
|
|
return Response::Unavail;
|
|
}
|
|
|
|
let user = users
|
|
.into_iter()
|
|
.filter_map(|etcuser| {
|
|
if etcuser.name == name {
|
|
Some(passwd_from_etcuser(etcuser))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.next();
|
|
|
|
if let Some(user) = user {
|
|
Response::Success(user)
|
|
} else {
|
|
Response::NotFound
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_all_group_entries(req_options: RequestOptions) -> Response<Vec<Group>> {
|
|
match req_options.connect_to_daemon() {
|
|
Source::Daemon(mut daemon_client) => {
|
|
let req = ClientRequest::NssGroups;
|
|
daemon_client
|
|
.call_and_wait(&req, None)
|
|
.map(|r| match r {
|
|
ClientResponse::NssGroups(l) => {
|
|
l.into_iter().map(group_from_nssgroup).collect()
|
|
}
|
|
_ => Vec::new(),
|
|
})
|
|
.map(Response::Success)
|
|
.unwrap_or_else(|_| Response::Success(vec![]))
|
|
}
|
|
Source::Fallback { users: _, groups } => {
|
|
if groups.is_empty() {
|
|
return Response::Unavail;
|
|
}
|
|
|
|
let groups = groups.into_iter().map(group_from_etcgroup).collect();
|
|
|
|
Response::Success(groups)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_group_entry_by_gid(gid: libc::gid_t, req_options: RequestOptions) -> Response<Group> {
|
|
match req_options.connect_to_daemon() {
|
|
Source::Daemon(mut daemon_client) => {
|
|
let req = ClientRequest::NssGroupByGid(gid);
|
|
daemon_client
|
|
.call_and_wait(&req, None)
|
|
.map(|r| match r {
|
|
ClientResponse::NssGroup(opt) => opt
|
|
.map(group_from_nssgroup)
|
|
.map(Response::Success)
|
|
.unwrap_or_else(|| Response::NotFound),
|
|
_ => Response::NotFound,
|
|
})
|
|
.unwrap_or_else(|_| Response::NotFound)
|
|
}
|
|
Source::Fallback { users: _, groups } => {
|
|
if groups.is_empty() {
|
|
return Response::Unavail;
|
|
}
|
|
|
|
let group = groups
|
|
.into_iter()
|
|
.filter_map(|etcgroup| {
|
|
if etcgroup.gid == gid {
|
|
Some(group_from_etcgroup(etcgroup))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.next();
|
|
|
|
if let Some(group) = group {
|
|
Response::Success(group)
|
|
} else {
|
|
Response::NotFound
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_group_entry_by_name(name: String, req_options: RequestOptions) -> Response<Group> {
|
|
match req_options.connect_to_daemon() {
|
|
Source::Daemon(mut daemon_client) => {
|
|
let req = ClientRequest::NssGroupByName(name);
|
|
daemon_client
|
|
.call_and_wait(&req, None)
|
|
.map(|r| match r {
|
|
ClientResponse::NssGroup(opt) => opt
|
|
.map(group_from_nssgroup)
|
|
.map(Response::Success)
|
|
.unwrap_or_else(|| Response::NotFound),
|
|
_ => Response::NotFound,
|
|
})
|
|
.unwrap_or_else(|_| Response::NotFound)
|
|
}
|
|
Source::Fallback { users: _, groups } => {
|
|
if groups.is_empty() {
|
|
return Response::Unavail;
|
|
}
|
|
|
|
let group = groups
|
|
.into_iter()
|
|
.filter_map(|etcgroup| {
|
|
if etcgroup.name == name {
|
|
Some(group_from_etcgroup(etcgroup))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.next();
|
|
|
|
if let Some(group) = group {
|
|
Response::Success(group)
|
|
} else {
|
|
Response::NotFound
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn passwd_from_etcuser(etc: EtcUser) -> Passwd {
|
|
Passwd {
|
|
name: etc.name,
|
|
gecos: etc.gecos,
|
|
passwd: "x".to_string(),
|
|
uid: etc.uid,
|
|
gid: etc.gid,
|
|
dir: etc.homedir,
|
|
shell: etc.shell,
|
|
}
|
|
}
|
|
|
|
fn passwd_from_nssuser(nu: NssUser) -> Passwd {
|
|
Passwd {
|
|
name: nu.name,
|
|
gecos: nu.gecos,
|
|
passwd: "x".to_string(),
|
|
uid: nu.uid,
|
|
gid: nu.gid,
|
|
dir: nu.homedir,
|
|
shell: nu.shell,
|
|
}
|
|
}
|
|
|
|
fn group_from_etcgroup(etc: EtcGroup) -> Group {
|
|
Group {
|
|
name: etc.name,
|
|
passwd: "x".to_string(),
|
|
gid: etc.gid,
|
|
members: etc.members,
|
|
}
|
|
}
|
|
|
|
fn group_from_nssgroup(ng: NssGroup) -> Group {
|
|
Group {
|
|
name: ng.name,
|
|
passwd: "x".to_string(),
|
|
gid: ng.gid,
|
|
members: ng.members,
|
|
}
|
|
}
|