1#![deny(clippy::all)]
2#![deny(missing_docs)]
3#![forbid(unsafe_code)]
4#![doc(html_root_url = "https://docs.rs/unftp-sbe-restrict/0.1.1")]
5
6use async_trait::async_trait;
59use bitflags::bitflags;
60use libunftp::{
61 auth::UserDetail,
62 storage::{self, Fileinfo, Metadata, StorageBackend},
63};
64use std::fmt::Debug;
65use std::io::{Cursor, Error, ErrorKind};
66use std::marker::PhantomData;
67use std::path::{Path, PathBuf};
68use tokio::io::AsyncRead;
69
70bitflags! {
71 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
73 pub struct VfsOperations: u32 {
74 const MK_DIR = 0b00000001;
76 const RM_DIR = 0b00000010;
78 const GET = 0b00000100;
80 const PUT = 0b00001000;
82 const DEL = 0b00010000;
84 const RENAME = 0b00100000;
86 const MD5 = 0b01000000;
88 const LIST = 0b10000000;
90
91 const WRITE_OPS = Self::MK_DIR.bits() | Self::RM_DIR.bits() | Self::PUT.bits() | Self::DEL.bits() | Self::RENAME.bits();
93 }
94}
95
96pub trait UserWithPermissions: UserDetail {
98 fn permissions(&self) -> VfsOperations;
100}
101
102#[derive(Debug)]
105pub struct RestrictingVfs<Delegate, User, Meta>
106where
107 Delegate: StorageBackend<User>,
108 User: UserWithPermissions,
109 Meta: Metadata + Debug + Sync + Send,
110{
111 delegate: Delegate,
112 x: PhantomData<Meta>,
113 y: PhantomData<User>,
114}
115
116impl<Delegate, User, Meta> RestrictingVfs<Delegate, User, Meta>
117where
118 Delegate: StorageBackend<User>,
119 User: UserWithPermissions,
120 Meta: Metadata + Debug + Sync + Send,
121{
122 pub fn new(delegate: Delegate) -> Self {
124 RestrictingVfs {
125 delegate,
126 x: PhantomData,
127 y: PhantomData,
128 }
129 }
130}
131
132#[async_trait]
133impl<Delegate, User, Meta> StorageBackend<User> for RestrictingVfs<Delegate, User, Meta>
134where
135 Delegate: StorageBackend<User>,
136 User: UserWithPermissions,
137 Meta: Metadata + Debug + Sync + Send,
138{
139 type Metadata = Delegate::Metadata;
140
141 fn name(&self) -> &str {
142 self.delegate.name()
143 }
144
145 fn supported_features(&self) -> u32 {
146 self.delegate.supported_features()
147 }
148
149 async fn metadata<P: AsRef<Path> + Send + Debug>(
150 &self,
151 user: &User,
152 path: P,
153 ) -> storage::Result<Self::Metadata> {
154 self.delegate.metadata(user, path).await
155 }
156
157 async fn md5<P: AsRef<Path> + Send + Debug>(
158 &self,
159 user: &User,
160 path: P,
161 ) -> storage::Result<String>
162 where
163 P: AsRef<Path> + Send + Debug,
164 {
165 if user.permissions().contains(VfsOperations::MD5) {
166 self.delegate.md5(user, path).await
167 } else {
168 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
169 }
170 }
171
172 async fn list<P: AsRef<Path> + Send + Debug>(
173 &self,
174 user: &User,
175 path: P,
176 ) -> storage::Result<Vec<Fileinfo<PathBuf, Self::Metadata>>>
177 where
178 <Self as StorageBackend<User>>::Metadata: Metadata,
179 {
180 if user.permissions().contains(VfsOperations::LIST) {
181 self.delegate.list(user, path).await
182 } else {
183 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
184 }
185 }
186
187 async fn list_fmt<P>(&self, user: &User, path: P) -> storage::Result<Cursor<Vec<u8>>>
188 where
189 P: AsRef<Path> + Send + Debug,
190 Self::Metadata: Metadata + 'static,
191 {
192 if user.permissions().contains(VfsOperations::LIST) {
193 self.delegate.list_fmt(user, path).await
194 } else {
195 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
196 }
197 }
198
199 async fn nlst<P>(&self, user: &User, path: P) -> std::result::Result<Cursor<Vec<u8>>, Error>
200 where
201 P: AsRef<Path> + Send + Debug,
202 Self::Metadata: Metadata + 'static,
203 {
204 if user.permissions().contains(VfsOperations::LIST) {
205 self.delegate.nlst(user, path).await
206 } else {
207 Err(ErrorKind::PermissionDenied.into())
208 }
209 }
210
211 async fn get_into<'a, P, W: ?Sized>(
212 &self,
213 user: &User,
214 path: P,
215 start_pos: u64,
216 output: &'a mut W,
217 ) -> storage::Result<u64>
218 where
219 W: tokio::io::AsyncWrite + Unpin + Sync + Send,
220 P: AsRef<Path> + Send + Debug,
221 {
222 if user.permissions().contains(VfsOperations::GET) {
223 self.delegate.get_into(user, path, start_pos, output).await
224 } else {
225 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
226 }
227 }
228
229 async fn get<P: AsRef<Path> + Send + Debug>(
230 &self,
231 user: &User,
232 path: P,
233 start_pos: u64,
234 ) -> storage::Result<Box<dyn AsyncRead + Send + Sync + Unpin>> {
235 if user.permissions().contains(VfsOperations::GET) {
236 self.delegate.get(user, path, start_pos).await
237 } else {
238 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
239 }
240 }
241
242 async fn put<
243 P: AsRef<Path> + Send + Debug,
244 R: tokio::io::AsyncRead + Send + Sync + Unpin + 'static,
245 >(
246 &self,
247 user: &User,
248 input: R,
249 path: P,
250 start_pos: u64,
251 ) -> storage::Result<u64> {
252 if user.permissions().contains(VfsOperations::PUT) {
253 self.delegate.put(user, input, path, start_pos).await
254 } else {
255 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
256 }
257 }
258
259 async fn del<P: AsRef<Path> + Send + Debug>(
260 &self,
261 user: &User,
262 path: P,
263 ) -> storage::Result<()> {
264 if user.permissions().contains(VfsOperations::DEL) {
265 self.delegate.del(user, path).await
266 } else {
267 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
268 }
269 }
270
271 async fn mkd<P: AsRef<Path> + Send + Debug>(
272 &self,
273 user: &User,
274 path: P,
275 ) -> storage::Result<()> {
276 if user.permissions().contains(VfsOperations::MK_DIR) {
277 self.delegate.mkd(user, path).await
278 } else {
279 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
280 }
281 }
282
283 async fn rename<P: AsRef<Path> + Send + Debug>(
284 &self,
285 user: &User,
286 from: P,
287 to: P,
288 ) -> storage::Result<()> {
289 if user.permissions().contains(VfsOperations::RENAME) {
290 self.delegate.rename(user, from, to).await
291 } else {
292 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
293 }
294 }
295
296 async fn rmd<P: AsRef<Path> + Send + Debug>(
297 &self,
298 user: &User,
299 path: P,
300 ) -> storage::Result<()> {
301 if user.permissions().contains(VfsOperations::RM_DIR) {
302 self.delegate.rmd(user, path).await
303 } else {
304 Err(libunftp::storage::ErrorKind::PermissionDenied.into())
305 }
306 }
307
308 async fn cwd<P: AsRef<Path> + Send + Debug>(
309 &self,
310 user: &User,
311 path: P,
312 ) -> storage::Result<()> {
313 self.delegate.cwd(user, path).await
314 }
315}