Skip to content

Commit 3538c0a

Browse files
committed
Move more things to domain and infra packages
This is in preparation for adding a REST or HTTP user detail provider. I'm moving code into the `domain` and `infra` modules. The `domain` module contains the traits to be implemented in the infra packages.
1 parent a79f96c commit 3538c0a

File tree

12 files changed

+176
-160
lines changed

12 files changed

+176
-160
lines changed

Diff for: src/app.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(dead_code)]
2+
13
/// The application name
24
pub const NAME: &str = "unFTP";
35

Diff for: src/auth.rs

+2-133
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,13 @@
1+
use crate::domain::user::{User, UserDetailProvider};
12
use async_trait::async_trait;
2-
use libunftp::auth::{AuthenticationError, Credentials, DefaultUser, UserDetail};
3-
use serde::Deserialize;
4-
use std::fmt::Formatter;
5-
use std::path::PathBuf;
6-
use unftp_sbe_restrict::{UserWithPermissions, VfsOperations};
7-
8-
/// The unFTP user details
9-
#[derive(Debug, PartialEq, Eq)]
10-
pub struct User {
11-
pub username: String,
12-
pub name: Option<String>,
13-
pub surname: Option<String>,
14-
/// Tells whether this user can log in or not.
15-
pub account_enabled: bool,
16-
/// What FTP commands can the user perform
17-
pub vfs_permissions: VfsOperations,
18-
/// For some users we know they will only upload a certain type of file
19-
pub allowed_mime_types: Option<Vec<String>>, // TODO: Look at https://crates.io/crates/infer to do this
20-
/// The user's home directory relative to the storage back-end root
21-
pub root: Option<PathBuf>,
22-
}
23-
24-
impl User {
25-
fn with_defaults(username: impl Into<String>) -> Self {
26-
User {
27-
username: username.into(),
28-
name: None,
29-
surname: None,
30-
account_enabled: true,
31-
vfs_permissions: VfsOperations::all(),
32-
allowed_mime_types: None,
33-
root: None,
34-
}
35-
}
36-
}
37-
38-
impl UserDetail for User {
39-
fn account_enabled(&self) -> bool {
40-
self.account_enabled
41-
}
42-
}
43-
44-
impl std::fmt::Display for User {
45-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46-
write!(
47-
f,
48-
"User(username: {:?}, name: {:?}, surname: {:?})",
49-
self.username, self.name, self.surname
50-
)
51-
}
52-
}
53-
54-
impl crate::storage::UserWithRoot for User {
55-
fn user_root(&self) -> Option<PathBuf> {
56-
self.root.clone()
57-
}
58-
}
59-
60-
impl UserWithPermissions for User {
61-
fn permissions(&self) -> VfsOperations {
62-
self.vfs_permissions
63-
}
64-
}
3+
use libunftp::auth::{AuthenticationError, Credentials, DefaultUser};
654

665
#[derive(Debug)]
676
pub struct LookupAuthenticator {
687
inner: Box<dyn libunftp::auth::Authenticator<DefaultUser>>,
698
usr_detail: Option<Box<dyn UserDetailProvider + Send + Sync>>,
709
}
7110

72-
/// Implementation of UserDetailProvider can look up and provide FTP user account details from
73-
/// a source.
74-
pub trait UserDetailProvider: std::fmt::Debug {
75-
fn provide_user_detail(&self, username: &str) -> Option<User>;
76-
}
77-
7811
impl LookupAuthenticator {
7912
pub fn new<A: libunftp::auth::Authenticator<DefaultUser> + Send + Sync + 'static>(
8013
inner: A,
@@ -119,67 +52,3 @@ impl UserDetailProvider for DefaultUserProvider {
11952
Some(User::with_defaults(username))
12053
}
12154
}
122-
123-
#[derive(Debug, Deserialize)]
124-
pub struct JsonUserProvider {
125-
users: Vec<UserJsonObj>,
126-
}
127-
128-
#[derive(Deserialize, Clone, Debug)]
129-
struct UserJsonObj {
130-
username: String,
131-
name: Option<String>,
132-
surname: Option<String>,
133-
vfs_perms: Option<Vec<String>>,
134-
#[allow(dead_code)]
135-
allowed_mime_types: Option<Vec<String>>,
136-
root: Option<String>,
137-
account_enabled: Option<bool>,
138-
}
139-
140-
impl JsonUserProvider {
141-
pub fn from_json(json: &str) -> std::result::Result<JsonUserProvider, String> {
142-
let v: Vec<UserJsonObj> = serde_json::from_str(json).map_err(|e| format!("{:?}", e))?;
143-
Ok(JsonUserProvider { users: v })
144-
}
145-
}
146-
147-
impl UserDetailProvider for JsonUserProvider {
148-
fn provide_user_detail(&self, username: &str) -> Option<User> {
149-
self.users.iter().find(|u| u.username == username).map(|u| {
150-
let u = u.clone();
151-
User {
152-
username: u.username,
153-
name: u.name,
154-
surname: u.surname,
155-
account_enabled: u.account_enabled.unwrap_or(true),
156-
vfs_permissions: u.vfs_perms.map_or(VfsOperations::all(), |p| {
157-
p.iter()
158-
.fold(VfsOperations::all(), |ops, s| match s.as_str() {
159-
"none" => VfsOperations::empty(),
160-
"all" => VfsOperations::all(),
161-
"-mkdir" => ops - VfsOperations::MK_DIR,
162-
"-rmdir" => ops - VfsOperations::RM_DIR,
163-
"-del" => ops - VfsOperations::DEL,
164-
"-ren" => ops - VfsOperations::RENAME,
165-
"-md5" => ops - VfsOperations::MD5,
166-
"-get" => ops - VfsOperations::GET,
167-
"-put" => ops - VfsOperations::PUT,
168-
"-list" => ops - VfsOperations::LIST,
169-
"+mkdir" => ops | VfsOperations::MK_DIR,
170-
"+rmdir" => ops | VfsOperations::RM_DIR,
171-
"+del" => ops | VfsOperations::DEL,
172-
"+ren" => ops | VfsOperations::RENAME,
173-
"+md5" => ops | VfsOperations::MD5,
174-
"+get" => ops | VfsOperations::GET,
175-
"+put" => ops | VfsOperations::PUT,
176-
"+list" => ops | VfsOperations::LIST,
177-
_ => ops,
178-
})
179-
}),
180-
allowed_mime_types: None,
181-
root: u.root.map(PathBuf::from),
182-
}
183-
})
184-
}
185-
}

Diff for: src/domain.rs renamed to src/domain/events.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
1+
//! Event definitions
2+
//!
3+
//! Packages elsewhere e.g. in the [`infra`](crate::infra) module implements these traits defined here.
4+
15
use async_trait::async_trait;
2-
use serde::__private::fmt::Debug;
36
use serde::{Deserialize, Serialize};
7+
use std::fmt::Debug;
48

59
// EventDispatcher can send events to the outside world.
610
#[async_trait]
711
pub trait EventDispatcher<T>: Send + Sync + Debug {
812
async fn dispatch(&self, event: T);
913
}
1014

11-
// An EventDispatcher that dispatches to the void of nothingness.
12-
#[derive(Debug)]
13-
pub struct NullEventDispatcher {}
14-
1515
#[async_trait]
1616
impl EventDispatcher<FTPEvent> for NullEventDispatcher {
1717
async fn dispatch(&self, _event: FTPEvent) {
1818
// Do Nothing
1919
}
2020
}
2121

22+
// An EventDispatcher that dispatches to the void of nothingness.
23+
#[derive(Debug)]
24+
pub struct NullEventDispatcher {}
25+
2226
// The event that will be sent
2327
#[derive(Serialize, Deserialize, Debug)]
2428
pub struct FTPEvent {

Diff for: src/domain/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! The domain module contains code to be dependent upon elsewhere in this project. It
2+
//! should itself not depend on any modules in this project.
3+
pub mod events;
4+
pub mod user;

Diff for: src/domain/user.rs

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use libunftp::auth::UserDetail;
2+
use std::fmt::{Debug, Display, Formatter};
3+
use std::path::PathBuf;
4+
use unftp_sbe_restrict::{UserWithPermissions, VfsOperations};
5+
use unftp_sbe_rooter::UserWithRoot;
6+
7+
/// The unFTP user details
8+
#[derive(Debug, PartialEq, Eq)]
9+
pub struct User {
10+
pub username: String,
11+
pub name: Option<String>,
12+
pub surname: Option<String>,
13+
/// Tells whether this user can log in or not.
14+
pub account_enabled: bool,
15+
/// What FTP commands can the user perform
16+
pub vfs_permissions: VfsOperations,
17+
/// For some users we know they will only upload a certain type of file
18+
pub allowed_mime_types: Option<Vec<String>>, // TODO: Look at https://crates.io/crates/infer to do this
19+
/// The user's home directory relative to the storage back-end root
20+
pub root: Option<PathBuf>,
21+
}
22+
23+
impl User {
24+
pub fn with_defaults(username: impl Into<String>) -> Self {
25+
User {
26+
username: username.into(),
27+
name: None,
28+
surname: None,
29+
account_enabled: true,
30+
vfs_permissions: VfsOperations::all(),
31+
allowed_mime_types: None,
32+
root: None,
33+
}
34+
}
35+
}
36+
37+
impl UserDetail for User {
38+
fn account_enabled(&self) -> bool {
39+
self.account_enabled
40+
}
41+
}
42+
43+
impl Display for User {
44+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45+
write!(
46+
f,
47+
"User(username: {:?}, name: {:?}, surname: {:?})",
48+
self.username, self.name, self.surname
49+
)
50+
}
51+
}
52+
53+
impl UserWithRoot for User {
54+
fn user_root(&self) -> Option<PathBuf> {
55+
self.root.clone()
56+
}
57+
}
58+
59+
impl UserWithPermissions for User {
60+
fn permissions(&self) -> VfsOperations {
61+
self.vfs_permissions
62+
}
63+
}
64+
65+
/// Implementation of UserDetailProvider can look up and provide FTP user account details from
66+
/// a source.
67+
pub trait UserDetailProvider: Debug {
68+
fn provide_user_detail(&self, username: &str) -> Option<User>;
69+
}

Diff for: src/infra/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
//! Infra contains infrastructure specific implementations of things in the [`domain`](crate::domain)
2+
//! module.
13
mod pubsub;
24
mod workload_identity;
35

6+
pub mod usrdetail_json;
7+
48
pub use pubsub::PubsubEventDispatcher;

Diff for: src/infra/pubsub.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::domain::{EventDispatcher, FTPEvent, FTPEventPayload};
1+
use crate::domain::events::{EventDispatcher, FTPEvent, FTPEventPayload};
22
use crate::infra::workload_identity;
33
use async_trait::async_trait;
44
use http::{header, Method, Request, StatusCode, Uri};

Diff for: src/infra/usrdetail_json.rs

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use crate::domain::user::{User, UserDetailProvider};
2+
use serde::Deserialize;
3+
use std::path::PathBuf;
4+
use unftp_sbe_restrict::VfsOperations;
5+
6+
/// A [`UserDetail`] provider that gets user details from a JSON file.
7+
#[derive(Debug, Deserialize)]
8+
pub struct JsonUserProvider {
9+
users: Vec<UserJsonObj>,
10+
}
11+
12+
#[derive(Deserialize, Clone, Debug)]
13+
struct UserJsonObj {
14+
username: String,
15+
name: Option<String>,
16+
surname: Option<String>,
17+
vfs_perms: Option<Vec<String>>,
18+
#[allow(dead_code)]
19+
allowed_mime_types: Option<Vec<String>>,
20+
root: Option<String>,
21+
account_enabled: Option<bool>,
22+
}
23+
24+
impl JsonUserProvider {
25+
pub fn from_json(json: &str) -> std::result::Result<JsonUserProvider, String> {
26+
let v: Vec<UserJsonObj> = serde_json::from_str(json).map_err(|e| format!("{:?}", e))?;
27+
Ok(JsonUserProvider { users: v })
28+
}
29+
}
30+
31+
impl UserDetailProvider for JsonUserProvider {
32+
fn provide_user_detail(&self, username: &str) -> Option<User> {
33+
self.users.iter().find(|u| u.username == username).map(|u| {
34+
let u = u.clone();
35+
User {
36+
username: u.username,
37+
name: u.name,
38+
surname: u.surname,
39+
account_enabled: u.account_enabled.unwrap_or(true),
40+
vfs_permissions: u.vfs_perms.map_or(VfsOperations::all(), |p| {
41+
p.iter()
42+
.fold(VfsOperations::all(), |ops, s| match s.as_str() {
43+
"none" => VfsOperations::empty(),
44+
"all" => VfsOperations::all(),
45+
"-mkdir" => ops - VfsOperations::MK_DIR,
46+
"-rmdir" => ops - VfsOperations::RM_DIR,
47+
"-del" => ops - VfsOperations::DEL,
48+
"-ren" => ops - VfsOperations::RENAME,
49+
"-md5" => ops - VfsOperations::MD5,
50+
"-get" => ops - VfsOperations::GET,
51+
"-put" => ops - VfsOperations::PUT,
52+
"-list" => ops - VfsOperations::LIST,
53+
"+mkdir" => ops | VfsOperations::MK_DIR,
54+
"+rmdir" => ops | VfsOperations::RM_DIR,
55+
"+del" => ops | VfsOperations::DEL,
56+
"+ren" => ops | VfsOperations::RENAME,
57+
"+md5" => ops | VfsOperations::MD5,
58+
"+get" => ops | VfsOperations::GET,
59+
"+put" => ops | VfsOperations::PUT,
60+
"+list" => ops | VfsOperations::LIST,
61+
_ => ops,
62+
})
63+
}),
64+
allowed_mime_types: None,
65+
root: u.root.map(PathBuf::from),
66+
}
67+
})
68+
}
69+
}

0 commit comments

Comments
 (0)