add access control indicators

fixes #347
fixes #515

Signed-off-by: Martin Matous <m@matous.dev>
This commit is contained in:
Martin Matous 2022-03-15 05:13:52 +01:00 committed by Abin Simon
parent f1254a6b0c
commit 3b6b343944
9 changed files with 143 additions and 5 deletions

View file

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add support for `--no-sort` `-U` from [MichaelAug](https://github.com/MichaelAug)
- Add `--group-directories-first` as an alias for `--group-dirs=first` to improve compatibility with `coreutils/ls`
- Add `--permission` flag to choose permission formatting (rwx, octal) from [meain](https://github.com/meain)
- Display MAC and ACL indicators
### Fixed
- Support non-bold bright colors [#248](https://github.com/Peltoche/lsd/issues/248) from [meain](https://github.com/meain)
- Don't automatically dereference symlinks in tree/recursive [#637](https://github.com/Peltoche/lsd/issues/637) from [meain](https://github.com/meain)

18
Cargo.lock generated
View file

@ -364,9 +364,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
version = "0.4.6"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [
"scopeguard",
]
@ -417,6 +417,7 @@ dependencies = [
"version_check",
"wild",
"winapi",
"xattr",
"xdg",
"yaml-rust",
]
@ -740,9 +741,9 @@ dependencies = [
[[package]]
name = "signal-hook"
version = "0.3.13"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39"
dependencies = [
"libc",
"signal-hook-registry",
@ -969,6 +970,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xattr"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
dependencies = [
"libc",
]
[[package]]
name = "xdg"
version = "2.1.0"

View file

@ -40,6 +40,7 @@ serde_yaml = "0.8"
[target.'cfg(unix)'.dependencies]
users = "0.11.*"
xattr = "0.2.*"
[target.'cfg(windows)'.dependencies]
winapi = {version = "0.3.*", features = ["aclapi", "accctrl", "winnt", "winerror", "securitybaseapi", "winbase"]}

View file

@ -252,6 +252,8 @@ permission:
exec-sticky: 5
no-access: 245
octal: 6
acl: dark_cyan
security-label: white
date:
hour-old: 40
day-old: 42

View file

@ -36,6 +36,8 @@ pub enum Elem {
ExecSticky,
NoAccess,
Octal,
Acl,
SecurityLabel,
/// Last Time Modified
DayOld,
@ -104,6 +106,8 @@ impl Elem {
Elem::ExecSticky => theme.permission.exec_sticky,
Elem::NoAccess => theme.permission.no_access,
Elem::Octal => theme.permission.octal,
Elem::Acl => theme.permission.acl,
Elem::SecurityLabel => theme.permission.security_label,
Elem::DayOld => theme.date.day_old,
Elem::HourOld => theme.date.hour_old,
@ -343,6 +347,8 @@ mod elem {
exec_sticky: Color::Magenta,
no_access: Color::AnsiValue(245), // Grey
octal: Color::AnsiValue(6),
acl: Color::DarkCyan,
security_label: Color::White,
},
file_type: theme::FileType {
file: theme::File {

View file

@ -39,6 +39,8 @@ pub struct Permission {
pub exec_sticky: Color,
pub no_access: Color,
pub octal: Color,
pub acl: Color,
pub security_label: Color,
}
#[derive(Debug, Deserialize, PartialEq)]
@ -134,6 +136,8 @@ impl Default for Permission {
exec_sticky: Color::AnsiValue(5),
no_access: Color::AnsiValue(245), // Grey
octal: Color::AnsiValue(6),
acl: Color::DarkCyan,
security_label: Color::White,
}
}
}

View file

@ -271,6 +271,7 @@ fn get_output<'a>(
block_vec.extend(vec![
meta.file_type.render(colors),
meta.permissions.render(colors, flags),
meta.access_control.render_method(colors),
]);
}
Block::User => block_vec.push(meta.owner.render_user(colors)),

108
src/meta/access_control.rs Normal file
View file

@ -0,0 +1,108 @@
use crate::color::{ColoredString, Colors, Elem};
use std::path::Path;
#[derive(Clone, Debug)]
pub struct AccessControl {
has_acl: bool,
selinux_label: String,
smack_label: String,
}
impl AccessControl {
#[cfg(not(unix))]
pub fn for_path(_: &Path) -> Self {
Self::from_data(false, &[], &[])
}
#[cfg(unix)]
pub fn for_path(path: &Path) -> Self {
let has_acl = !xattr::get(path, Method::Acl.name())
.unwrap_or_default()
.unwrap_or_default()
.is_empty();
let selinux_label = xattr::get(path, Method::Selinux.name())
.unwrap_or_default()
.unwrap_or_default();
let smack_label = xattr::get(path, Method::Smack.name())
.unwrap_or_default()
.unwrap_or_default();
Self::from_data(has_acl, &selinux_label, &smack_label)
}
fn from_data(has_acl: bool, selinux_label: &[u8], smack_label: &[u8]) -> Self {
let selinux_label = String::from_utf8_lossy(selinux_label).to_string();
let smack_label = String::from_utf8_lossy(smack_label).to_string();
Self {
has_acl,
selinux_label,
smack_label,
}
}
pub fn render_method(&self, colors: &Colors) -> ColoredString {
if self.has_acl {
colors.colorize(String::from("+"), &Elem::Acl)
} else if !self.selinux_label.is_empty() || !self.smack_label.is_empty() {
colors.colorize(String::from("."), &Elem::SecurityLabel)
} else {
colors.colorize(String::from(""), &Elem::Acl)
}
}
}
#[cfg(unix)]
enum Method {
Acl,
Selinux,
Smack,
}
#[cfg(unix)]
impl Method {
fn name(&self) -> &'static str {
match self {
Method::Acl => "system.posix_acl_access",
Method::Selinux => "security.selinux",
Method::Smack => "security.SMACK64",
}
}
}
#[cfg(test)]
mod test {
use super::AccessControl;
use crate::color::{Colors, ThemeOption};
use crossterm::style::{Color, Stylize};
#[test]
fn test_acl_only_indicator() {
// actual file would collide with proper AC data, no permission to scrub those
let access_control = AccessControl::from_data(true, &[], &[]);
assert_eq!(
String::from("+").with(Color::DarkCyan),
access_control.render_method(&Colors::new(ThemeOption::Default))
);
}
#[test]
fn test_smack_only_indicator() {
let access_control = AccessControl::from_data(false, &[], &[b'a']);
assert_eq!(
String::from(".").with(Color::White),
access_control.render_method(&Colors::new(ThemeOption::Default))
);
}
#[test]
fn test_acl_and_selinux_indicator() {
let access_control = AccessControl::from_data(true, &[b'a'], &[]);
assert_eq!(
String::from("+").with(Color::DarkCyan),
access_control.render_method(&Colors::new(ThemeOption::Default))
);
}
}

View file

@ -1,3 +1,4 @@
mod access_control;
mod date;
mod filetype;
mod indicator;
@ -12,6 +13,7 @@ mod symlink;
#[cfg(windows)]
mod windows_utils;
pub use self::access_control::AccessControl;
pub use self::date::Date;
pub use self::filetype::FileType;
pub use self::indicator::Indicator;
@ -44,6 +46,7 @@ pub struct Meta {
pub inode: INode,
pub links: Links,
pub content: Option<Vec<Meta>>,
pub access_control: AccessControl,
}
impl Meta {
@ -231,6 +234,7 @@ impl Meta {
#[cfg(windows)]
let (owner, permissions) = windows_utils::get_file_data(path)?;
let access_control = AccessControl::for_path(path);
let file_type = FileType::new(&metadata, symlink_meta.as_ref(), &permissions);
let name = Name::new(path, file_type);
let inode = INode::from(&metadata);
@ -249,6 +253,7 @@ impl Meta {
name,
file_type,
content: None,
access_control: access_control,
})
}
}