From 23782b87439f55ed9f52387c4fc7b12415067223 Mon Sep 17 00:00:00 2001 From: Hannes de Jager Date: Wed, 13 Sep 2023 23:05:31 +0200 Subject: [PATCH] Move restricting VFS to own crate I've moved the delegating storage back-end in src/storage/restrict.rs to its own crate at https://crates.io/crates/unftp-sbe-restrict. --- Cargo.lock | 115 ++++++++++++++++++----- Cargo.toml | 2 +- docs/libunftp/README.md | 1 + src/auth.rs | 17 +--- src/main.rs | 37 ++++---- src/storage/mod.rs | 3 +- src/storage/restrict.rs | 198 ---------------------------------------- 7 files changed, 116 insertions(+), 257 deletions(-) delete mode 100644 src/storage/restrict.rs diff --git a/Cargo.lock b/Cargo.lock index b1c7002..0314ee9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -85,13 +94,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.33", ] [[package]] @@ -156,6 +165,21 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -754,7 +778,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.33", ] [[package]] @@ -808,6 +832,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "git2" version = "0.15.0" @@ -958,7 +988,7 @@ dependencies = [ "httpdate", "itoa 1.0.6", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1118,9 +1148,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libgit2-sys" @@ -1383,6 +1413,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.6.1" @@ -1502,9 +1541,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1562,9 +1601,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -1676,9 +1715,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1834,6 +1873,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" @@ -2038,7 +2083,7 @@ checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.33", ] [[package]] @@ -2212,6 +2257,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -2253,9 +2308,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" dependencies = [ "proc-macro2", "quote", @@ -2348,7 +2403,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.33", ] [[package]] @@ -2407,18 +2462,18 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.28.2" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -2442,7 +2497,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.33", ] [[package]] @@ -2571,7 +2626,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.33", ] [[package]] @@ -2623,7 +2678,6 @@ version = "0.14.3" dependencies = [ "async-trait", "base64 0.13.1", - "bitflags", "built", "clap", "console-subscriber", @@ -2650,6 +2704,7 @@ dependencies = [ "unftp-auth-rest", "unftp-sbe-fs", "unftp-sbe-gcs", + "unftp-sbe-restrict", "unftp-sbe-rooter", ] @@ -2750,6 +2805,18 @@ dependencies = [ "yup-oauth2", ] +[[package]] +name = "unftp-sbe-restrict" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0ac03730e7d52a136b0db5cf16dda2b8b8fbeddeb281766a44ec1716dd2925" +dependencies = [ + "async-trait", + "bitflags", + "libunftp", + "tokio", +] + [[package]] name = "unftp-sbe-rooter" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3d46606..4413d79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ version="0.1.2" [dependencies] async-trait = "0.1.68" base64 = "0.13.1" -bitflags = "1.3.2" clap = { version = "3.2.25", features = ["derive", "env"] } console-subscriber = { version = "0.1.9", optional = true } flate2 = "1.0.26" @@ -52,6 +51,7 @@ unftp-sbe-gcs = { version = "0.2.3", optional = true } unftp-auth-rest = { version = "0.2.2", optional = true } unftp-auth-jsonfile = { version = "0.3.1", optional = true } unftp-sbe-rooter = "0.1.0" +unftp-sbe-restrict = "0.1.0" # Purely to resolve conflicts tracing= "0.1.37" diff --git a/docs/libunftp/README.md b/docs/libunftp/README.md index d70df60..1700d6a 100644 --- a/docs/libunftp/README.md +++ b/docs/libunftp/README.md @@ -35,6 +35,7 @@ Known storage back-ends: * [unftp-sbe-fs](https://crates.io/crates/unftp-sbe-fs) - Stores files on the local filesystem * [unftp-sbe-gcs](https://crates.io/crates/unftp-sbe-gcs) - Stores files in Google Cloud Storage * [unftp-sbe-rooter](https://crates.io/crates/unftp-sbe-rooter) - Wraps another storage back-end in order to root a user to a specific home directory. +* [unftp-sbe-restrict](https://crates.io/crates/unftp-sbe-rooter) - Wraps another storage back-end in order to restrict the FTP operations a user can do i.e. provide authorization. Known authentication back-ends: diff --git a/src/auth.rs b/src/auth.rs index 79babe4..7ad6fa4 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,9 +1,9 @@ use async_trait::async_trait; -use bitflags::bitflags; use libunftp::auth::{AuthenticationError, Credentials, DefaultUser, UserDetail}; use serde::Deserialize; use std::fmt::Formatter; use std::path::PathBuf; +use unftp_sbe_restrict::{UserWithPermissions, VfsOperations}; /// The unFTP user details #[derive(Debug, PartialEq, Eq)] @@ -57,18 +57,9 @@ impl crate::storage::UserWithRoot for User { } } -bitflags! { - pub struct VfsOperations: u32 { - const MK_DIR = 0b00000001; - const RM_DIR = 0b00000010; - const GET = 0b00000100; - const PUT = 0b00001000; - const DEL = 0b00010000; - const RENAME = 0b00100000; - const MD5 = 0b01000000; - const LIST = 0b10000000; - - const WRITE_OPS = Self::MK_DIR.bits | Self::RM_DIR.bits | Self::PUT.bits | Self::DEL.bits | Self::RENAME.bits; +impl UserWithPermissions for User { + fn permissions(&self) -> VfsOperations { + self.vfs_permissions } } diff --git a/src/main.rs b/src/main.rs index 1906012..dd0a4e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -208,8 +208,11 @@ fn make_json_auth(m: &clap::ArgMatches) -> Result { } type VfsProducer = Box< - dyn (Fn() -> storage::RooterVfs) - + Send + dyn (Fn() -> storage::RooterVfs< + storage::RestrictingVfs, + auth::User, + storage::SbeMeta, + >) + Send + Sync, >; @@ -218,12 +221,10 @@ fn fs_storage_backend(log: &Logger, m: &clap::ArgMatches) -> VfsProducer { let p: PathBuf = m.value_of(args::ROOT_DIR).unwrap().into(); let sub_log = Arc::new(log.new(o!("module" => "storage"))); Box::new(move || { - storage::RooterVfs::new(storage::RestrictingVfs { - delegate: storage::ChoosingVfs { - inner: storage::InnerVfs::File(unftp_sbe_fs::Filesystem::new(p.clone())), - log: sub_log.clone(), - }, - }) + storage::RooterVfs::new(storage::RestrictingVfs::new(storage::ChoosingVfs { + inner: storage::InnerVfs::File(unftp_sbe_fs::Filesystem::new(p.clone())), + log: sub_log.clone(), + })) }) } @@ -285,17 +286,15 @@ fn gcs_storage_backend(log: &Logger, m: &clap::ArgMatches) -> Result "storage"))); Ok(Box::new(move || { - storage::RooterVfs::new(storage::RestrictingVfs { - delegate: storage::ChoosingVfs { - inner: storage::InnerVfs::Cloud(unftp_sbe_gcs::CloudStorage::with_api_base( - base_url.clone(), - bucket.clone(), - root_dir.clone(), - auth_method.clone(), - )), - log: sub_log.clone(), - }, - }) + storage::RooterVfs::new(storage::RestrictingVfs::new(storage::ChoosingVfs { + inner: storage::InnerVfs::Cloud(unftp_sbe_gcs::CloudStorage::with_api_base( + base_url.clone(), + bucket.clone(), + root_dir.clone(), + auth_method.clone(), + )), + log: sub_log.clone(), + })) })) } diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 726518c..0c99af8 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1,6 +1,5 @@ mod choose; -mod restrict; pub use choose::{ChoosingVfs, InnerVfs, SbeMeta}; -pub use restrict::RestrictingVfs; +pub use unftp_sbe_restrict::RestrictingVfs; pub use unftp_sbe_rooter::{RooterVfs, UserWithRoot}; diff --git a/src/storage/restrict.rs b/src/storage/restrict.rs deleted file mode 100644 index 90a1784..0000000 --- a/src/storage/restrict.rs +++ /dev/null @@ -1,198 +0,0 @@ -use std::fmt::Debug; -use std::io::{Cursor, Error, ErrorKind}; -use std::path::{Path, PathBuf}; - -use async_trait::async_trait; -use libunftp::storage; -use libunftp::storage::{Fileinfo, Metadata, StorageBackend}; -use tokio::io::AsyncRead; - -use crate::auth::{User, VfsOperations}; -use crate::storage::choose::{ChoosingVfs, SbeMeta}; - -/// A virtual filesystem that checks if the user has permissions to do its operations before it -/// delegates to another storage back-end. -#[derive(Debug)] -pub struct RestrictingVfs { - pub delegate: ChoosingVfs, -} - -#[async_trait] -impl StorageBackend for RestrictingVfs { - type Metadata = SbeMeta; - - fn name(&self) -> &str { - self.delegate.name() - } - - fn supported_features(&self) -> u32 { - self.delegate.supported_features() - } - - async fn metadata + Send + Debug>( - &self, - user: &User, - path: P, - ) -> storage::Result { - self.delegate.metadata(user, path).await - } - - async fn md5 + Send + Debug>( - &self, - user: &User, - path: P, - ) -> storage::Result - where - P: AsRef + Send + Debug, - { - if user.vfs_permissions.contains(VfsOperations::MD5) { - self.delegate.md5(user, path).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn list + Send + Debug>( - &self, - user: &User, - path: P, - ) -> storage::Result>> - where - >::Metadata: Metadata, - { - if user.vfs_permissions.contains(VfsOperations::LIST) { - self.delegate.list(user, path).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn list_fmt

(&self, user: &User, path: P) -> storage::Result>> - where - P: AsRef + Send + Debug, - Self::Metadata: Metadata + 'static, - { - if user.vfs_permissions.contains(VfsOperations::LIST) { - self.delegate.list_fmt(user, path).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn nlst

(&self, user: &User, path: P) -> std::result::Result>, Error> - where - P: AsRef + Send + Debug, - Self::Metadata: Metadata + 'static, - { - if user.vfs_permissions.contains(VfsOperations::LIST) { - self.delegate.nlst(user, path).await - } else { - Err(ErrorKind::PermissionDenied.into()) - } - } - - async fn get_into<'a, P, W: ?Sized>( - &self, - user: &User, - path: P, - start_pos: u64, - output: &'a mut W, - ) -> storage::Result - where - W: tokio::io::AsyncWrite + Unpin + Sync + Send, - P: AsRef + Send + Debug, - { - if user.vfs_permissions.contains(VfsOperations::GET) { - self.delegate.get_into(user, path, start_pos, output).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn get + Send + Debug>( - &self, - user: &User, - path: P, - start_pos: u64, - ) -> storage::Result> { - if user.vfs_permissions.contains(VfsOperations::GET) { - self.delegate.get(user, path, start_pos).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn put< - P: AsRef + Send + Debug, - R: tokio::io::AsyncRead + Send + Sync + Unpin + 'static, - >( - &self, - user: &User, - input: R, - path: P, - start_pos: u64, - ) -> storage::Result { - if user.vfs_permissions.contains(VfsOperations::PUT) { - self.delegate.put(user, input, path, start_pos).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn del + Send + Debug>( - &self, - user: &User, - path: P, - ) -> storage::Result<()> { - if user.vfs_permissions.contains(VfsOperations::DEL) { - self.delegate.del(user, path).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn mkd + Send + Debug>( - &self, - user: &User, - path: P, - ) -> storage::Result<()> { - if user.vfs_permissions.contains(VfsOperations::MK_DIR) { - self.delegate.mkd(user, path).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn rename + Send + Debug>( - &self, - user: &User, - from: P, - to: P, - ) -> storage::Result<()> { - if user.vfs_permissions.contains(VfsOperations::RENAME) { - self.delegate.rename(user, from, to).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn rmd + Send + Debug>( - &self, - user: &User, - path: P, - ) -> storage::Result<()> { - if user.vfs_permissions.contains(VfsOperations::RM_DIR) { - self.delegate.rmd(user, path).await - } else { - Err(libunftp::storage::ErrorKind::PermissionDenied.into()) - } - } - - async fn cwd + Send + Debug>( - &self, - user: &User, - path: P, - ) -> storage::Result<()> { - self.delegate.cwd(user, path).await - } -}