Browse Source

Sends: store auth_type

pull/7363/head
Timshel 5 days ago
parent
commit
d6e0a74f57
  1. 12
      Cargo.lock
  2. 1
      Cargo.toml
  3. 3
      migrations/mysql/2026-06-18-120000_add_sends_emails/up.sql
  4. 3
      migrations/postgresql/2026-06-18-120000_add_sends_emails/up.sql
  5. 6
      migrations/sqlite/2026-06-18-120000_add_sends_emails/up.sql
  6. 44
      src/api/core/sends.rs
  7. 2
      src/db/models/mod.rs
  8. 18
      src/db/models/send.rs
  9. 1
      src/db/schema.rs

12
Cargo.lock

@ -4961,6 +4961,17 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.6",
]
[[package]]
name = "serde_spanned"
version = "0.6.9"
@ -5991,6 +6002,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
"serde_repr",
"subtle",
"svg-hush",
"syslog",

1
Cargo.toml

@ -105,6 +105,7 @@ tokio-util = { version = "0.7.19", features = ["compat"] }
# A generic serialization/deserialization framework
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
serde_repr = "0.1.21"
# A safe, extensible ORM and Query builder
diesel = { version = "2.3.13", features = ["chrono", "r2d2", "numeric"] }

3
migrations/mysql/2026-06-18-120000_add_sends_emails/up.sql

@ -1,3 +1,6 @@
ALTER TABLE sends ADD COLUMN auth_type INTEGER NOT NULL DEFAULT 2;
UPDATE sends SET auth_type = 1 WHERE password_hash is NOT NULL;
ALTER TABLE sends ADD COLUMN emails TEXT;
CREATE TABLE sends_otp (

3
migrations/postgresql/2026-06-18-120000_add_sends_emails/up.sql

@ -1,3 +1,6 @@
ALTER TABLE sends ADD COLUMN auth_type INTEGER NOT NULL DEFAULT 2;
UPDATE sends SET auth_type = 1 WHERE password_hash is NOT NULL;
ALTER TABLE sends ADD COLUMN emails TEXT;
CREATE TABLE sends_otp (

6
migrations/sqlite/2026-06-18-120000_add_sends_emails/up.sql

@ -8,6 +8,7 @@ CREATE TABLE sends (
notes TEXT,
atype INTEGER NOT NULL,
auth_type INTEGER NOT NULL,
data TEXT NOT NULL,
akey TEXT NOT NULL,
password_hash BLOB,
@ -30,11 +31,12 @@ CREATE TABLE sends (
INSERT INTO sends(
uuid, user_uuid, name, notes, atype, data, akey, password_hash, password_salt, password_iter,
max_access_count, access_count, creation_date, revision_date, expiration_date, deletion_date,
disabled, hide_email
disabled, hide_email, auth_type
) SELECT uuid, user_uuid, name, notes, atype, data, akey, password_hash, password_salt, password_iter,
max_access_count, access_count, creation_date, revision_date, expiration_date, deletion_date,
disabled,
CASE WHEN hide_email IS NOT NULL THEN hide_email ELSE false END
CASE WHEN hide_email IS NOT NULL THEN hide_email ELSE false END,
CASE WHEN password_hash IS NOT NULL THEN 1 ELSE 2 END
FROM sends_old WHERE user_uuid IS NOT NULL;
DROP TABLE sends_old;

44
src/api/core/sends.rs

@ -16,7 +16,7 @@ use crate::{
config::PathType,
db::{
DbConn, DbPool,
models::{Device, OrgPolicy, OrgPolicyType, Send, SendFileId, SendId, SendType, UserId},
models::{Device, OrgPolicy, OrgPolicyType, Send, SendAuthType, SendFileId, SendId, SendType, UserId},
},
util::{NumberOrString, save_temp_file},
};
@ -69,7 +69,8 @@ pub async fn purge_sends(pool: DbPool) {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SendData {
r#type: i32,
r#type: SendType,
auth_type: SendAuthType,
pub key: String,
password: Option<String>,
max_access_count: Option<NumberOrString>,
@ -127,12 +128,9 @@ async fn enforce_disable_hide_email_policy(data: &SendData, headers: &Headers, c
}
fn create_send(data: SendData, user_id: UserId) -> ApiResult<Send> {
let data_val = if data.r#type == SendType::Text as i32 {
data.text
} else if data.r#type == SendType::File as i32 {
data.file
} else {
err!("Invalid Send type")
let data_val = match data.r#type {
SendType::Text => data.text,
SendType::File => data.file,
};
let data_str = if let Some(mut d) = data_val {
@ -159,6 +157,7 @@ fn create_send(data: SendData, user_id: UserId) -> ApiResult<Send> {
let mut send = Send::new(
data.r#type,
data.auth_type,
user_id,
data.name,
data.notes,
@ -205,7 +204,7 @@ async fn post_send(data: Json<SendData>, headers: Headers, conn: DbConn, nt: Not
let data: SendData = data.into_inner();
enforce_disable_hide_email_policy(&data, &headers, &conn).await?;
if data.r#type == SendType::File as i32 {
if data.r#type == SendType::File {
err!("File sends should use /api/sends/file/v2")
}
@ -235,7 +234,7 @@ async fn post_send_file_v2(data: Json<SendData>, headers: Headers, conn: DbConn)
let data = data.into_inner();
if data.r#type != SendType::File as i32 {
if data.r#type != SendType::File {
err!("Send content is not a file");
}
@ -502,7 +501,7 @@ async fn update_send_from_data(
err!("Send is not owned by user")
}
if send.atype != data.r#type {
if send.atype != data.r#type as i32 {
err!("Sends can't change type")
}
@ -514,7 +513,7 @@ async fn update_send_from_data(
// When updating a file Send, we receive nulls in the File field, as it's immutable,
// so we only need to update the data field in the Text case
if data.r#type == SendType::Text as i32 {
if data.r#type == SendType::Text {
let data_str = if let Some(mut d) = data.text {
d.as_object_mut().and_then(|d| d.remove("response"));
serde_json::to_string(&d)?
@ -535,11 +534,24 @@ async fn update_send_from_data(
send.expiration_date = data.expiration_date.map(|d| d.naive_utc());
send.hide_email = data.hide_email.unwrap_or(false);
send.disabled = data.disabled;
send.emails = data.emails.map(|e| e.trim().to_lowercase()).filter(|e| !e.is_empty());
// Only change the value if it's present
if let Some(password) = data.password {
send.set_password(Some(&password));
send.auth_type = data.auth_type as i32;
match data.auth_type {
SendAuthType::Email => {
send.emails = data.emails.map(|e| e.trim().to_lowercase()).filter(|e| !e.is_empty());
send.set_password(None);
}
SendAuthType::Password => {
send.emails = None;
// Only change the value if it's present
if let Some(password) = data.password {
send.set_password(Some(&password));
}
}
SendAuthType::None => {
send.emails = None;
send.set_password(None);
}
}
send.save(conn).await?;

2
src/db/models/mod.rs

@ -34,7 +34,7 @@ pub use self::organization::{
Membership, MembershipId, MembershipStatus, MembershipType, OrgApiKeyId, Organization, OrganizationApiKey,
OrganizationId,
};
pub use self::send::{Send, SendFileId, SendId, SendOTP, SendType};
pub use self::send::{Send, SendAuthType, SendFileId, SendId, SendOTP, SendType};
pub use self::sso_auth::{OIDCAuthenticatedUser, OIDCCodeResponseError, SsoAuth};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::two_factor_duo_context::TwoFactorDuoContext;

18
src/db/models/send.rs

@ -6,6 +6,7 @@ use derive_more::{AsRef, Deref, Display, From};
use diesel::prelude::*;
use macros::{IdFromParam, UuidFromParam};
use serde_json::Value;
use serde_repr::Deserialize_repr;
use uuid::Uuid;
use crate::{
@ -35,6 +36,7 @@ pub struct Send {
pub notes: Option<String>,
pub atype: i32,
pub auth_type: i32,
pub data: String,
pub akey: String,
pub password_hash: Option<Vec<u8>>,
@ -54,14 +56,16 @@ pub struct Send {
pub hide_email: bool,
}
#[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
#[derive(Copy, Clone, Deserialize_repr, PartialEq, Eq, num_derive::FromPrimitive)]
#[repr(u8)]
pub enum SendType {
Text = 0,
File = 1,
}
enum SendAuthType {
#[allow(dead_code)]
#[derive(Copy, Clone, Deserialize_repr, Eq, PartialEq, num_derive::FromPrimitive)]
#[repr(u8)]
pub enum SendAuthType {
// Send requires email OTP verification
Email = 0,
// Send requires a password
@ -73,7 +77,8 @@ enum SendAuthType {
impl Send {
#[allow(clippy::too_many_arguments)]
pub fn new(
atype: i32,
atype: SendType,
auth_type: SendAuthType,
user_uuid: UserId,
name: String,
notes: Option<String>,
@ -93,7 +98,8 @@ impl Send {
user_uuid,
name,
notes,
atype,
atype: atype as i32,
auth_type: auth_type as i32,
data,
akey,
password_hash: None,
@ -175,7 +181,7 @@ impl Send {
"maxAccessCount": self.max_access_count,
"accessCount": self.access_count,
"password": self.password_hash.as_deref().map(|h| BASE64URL_NOPAD.encode(h)),
"authType": if self.password_hash.is_some() { SendAuthType::Password } else if self.emails.is_some() { SendAuthType::Email } else { SendAuthType::None } as i32,
"authType": self.auth_type,
"disabled": self.disabled,
"hideEmail": self.hide_email,
"emails": self.emails,

1
src/db/schema.rs

@ -137,6 +137,7 @@ table! {
name -> Text,
notes -> Nullable<Text>,
atype -> Integer,
auth_type -> Integer,
data -> Text,
akey -> Text,
password_hash -> Nullable<Binary>,

Loading…
Cancel
Save