Repository: nushell/nushell
File: crates/nu-utils/src/filesystem.rs (Rust)

Code before:
    PermissionDenied(&'a str),
}

// TODO: Maybe we should use file_attributes() from https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
// More on that here: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
#[cfg(windows)]
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
    match dir.as_ref().read_dir() {
        Err(e) => {
            if matches!(e.kind(), std::io::ErrorKind::PermissionDenied) {
                PermissionResult::PermissionDenied("Folder is unable to be read")
            } else {
                PermissionResult::PermissionOk
            }
        }
        Ok(_) => PermissionResult::PermissionOk,
    }
}

#[cfg(unix)]
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
    match access(dir.as_ref(), AccessFlags::X_OK) {
        Ok(_) => PermissionResult::PermissionOk,
        Err(Errno::EPERM) => PermissionResult::PermissionDenied("Permission denied"),
        Err(Errno::ENOENT) => PermissionResult::PermissionDenied("Path not exists?"),
        Err(_) => PermissionResult::PermissionDenied("Unknown error"),
    }
}

#[cfg(unix)]
pub mod users {
    use nix::unistd::{Gid, Group, Uid, User};

    pub fn get_user_by_uid(uid: Uid) -> Option<User> {
        User::from_uid(uid).ok().flatten()
    }

    pub fn get_group_by_gid(gid: Gid) -> Option<Group> {
        Group::from_gid(gid).ok().flatten()
    }

    pub fn get_current_uid() -> Uid {
        Uid::current()
    }

    pub fn get_current_gid() -> Gid {
        Gid::current()
    }

    #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "android")))]
    pub fn get_current_username() -> Option<String> {

Review comment (security):
could you actually remove the `&str` member of `PermissionResult::PermissionDenied` while you're changing this? We never actually access it, there's no reason to even add an explanation here. after that, this can safely be reduced to 

```suggestion
    match access(dir.as_ref(), AccessFlags::X_OK) {
        Ok(_) => PermissionResult::PermissionOk,
        Err(_) => PermissionResult::PermissionDenied,
```

Code after:
// TODO: Maybe we should use file_attributes() from https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
// More on that here: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
#[cfg(windows)]
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
    match dir.as_ref().read_dir() {
        Err(e) => {
            if matches!(e.kind(), std::io::ErrorKind::PermissionDenied) {
                PermissionResult::PermissionDenied
            } else {
                PermissionResult::PermissionOk
            }
        }
        Ok(_) => PermissionResult::PermissionOk,
    }
}

#[cfg(unix)]
/// Check that the process' user id has permissions to execute or
/// in the case of a directory traverse the particular directory
pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
    // We check permissions for real user id, but that's fine, because in
    // proper installations of nushell, effective UID (EUID) rarely differs
    // from real UID (RUID). We strongly advise against setting the setuid bit
    // on the nushell executable or shebang scripts starts with `#!/usr/bin/env nu` e.g.
    // Most Unix systems ignore setuid on shebang by default anyway.
    access(dir.as_ref(), AccessFlags::X_OK).into()
}

#[cfg(unix)]
pub mod users {
    use nix::unistd::{Gid, Group, Uid, User};

    pub fn get_user_by_uid(uid: Uid) -> Option<User> {
        User::from_uid(uid).ok().flatten()
    }

    pub fn get_group_by_gid(gid: Gid) -> Option<Group> {
        Group::from_gid(gid).ok().flatten()
    }

    pub fn get_current_uid() -> Uid {
        Uid::current()
    }

    pub fn get_current_gid() -> Gid {
        Gid::current()
    }

    #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "android")))]
    pub fn get_current_username() -> Option<String> {
        get_user_by_uid(get_current_uid()).map(|user| user.name)