use newtype pattern for org_id

pull/5320/head
Stefan Melmuk 1 week ago
parent 0b9e3bafd3
commit 40a788084b
No known key found for this signature in database
GPG Key ID: 817020C608FE9C09

12
Cargo.lock generated

@ -778,6 +778,17 @@ dependencies = [
"url",
]
[[package]]
name = "diesel-derive-newtype"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5adf688c584fe33726ce0e2898f608a2a92578ac94a4a92fcecf73214fe0716"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "diesel_derives"
version = "2.2.3"
@ -3960,6 +3971,7 @@ dependencies = [
"data-encoding",
"data-url",
"diesel",
"diesel-derive-newtype",
"diesel_logger",
"diesel_migrations",
"dotenvy",

@ -77,6 +77,7 @@ serde_json = "1.0.133"
diesel = { version = "2.2.6", features = ["chrono", "r2d2", "numeric"] }
diesel_migrations = "2.2.0"
diesel_logger = { version = "0.4.0", optional = true }
diesel-derive-newtype = "2.1.2"
# Bundled/Static SQLite
libsqlite3-sys = { version = "0.30.1", features = ["bundled"], optional = true }

@ -488,7 +488,7 @@ async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) ->
struct MembershipTypeData {
user_type: NumberOrString,
user_uuid: String,
org_uuid: String,
org_uuid: OrganizationId,
}
#[post("/users/org_type", data = "<data>")]
@ -570,9 +570,9 @@ async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResu
Ok(Html(text))
}
#[post("/organizations/<uuid>/delete")]
async fn delete_organization(uuid: &str, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
let org = Organization::find_by_uuid(uuid, &mut conn).await.map_res("Organization doesn't exist")?;
#[post("/organizations/<org_uuid>/delete")]
async fn delete_organization(org_uuid: OrganizationId, _token: AdminToken, mut conn: DbConn) -> EmptyResult {
let org = Organization::find_by_uuid(&org_uuid, &mut conn).await.map_res("Organization doesn't exist")?;
org.delete(&mut conn).await
}

@ -459,7 +459,7 @@ struct UpdateEmergencyAccessData {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct UpdateResetPasswordData {
organization_id: String,
organization_id: OrganizationId,
reset_password_key: String,
}
@ -516,9 +516,10 @@ fn validate_keydata(
}
// Check that we're correctly rotating all the user's reset password keys
let existing_reset_password_ids = existing_memberships.iter().map(|m| m.org_uuid.as_str()).collect::<HashSet<_>>();
let existing_reset_password_ids =
existing_memberships.iter().map(|m| &m.org_uuid).collect::<HashSet<&OrganizationId>>();
let provided_reset_password_ids =
data.reset_password_keys.iter().map(|rp| rp.organization_id.as_str()).collect::<HashSet<_>>();
data.reset_password_keys.iter().map(|rp| &rp.organization_id).collect::<HashSet<&OrganizationId>>();
if !provided_reset_password_ids.is_superset(&existing_reset_password_ids) {
err!("All existing reset password keys must be included in the rotation")
}

@ -224,7 +224,7 @@ pub struct CipherData {
pub folder_id: Option<String>,
// TODO: Some of these might appear all the time, no need for Option
#[serde(alias = "organizationID")]
pub organization_id: Option<String>,
pub organization_id: Option<OrganizationId>,
key: Option<String>,
@ -1572,14 +1572,14 @@ async fn move_cipher_selected_put(
}
#[derive(FromForm)]
struct OrganizationId {
struct OrganizationIdData {
#[field(name = "organizationId")]
org_id: String,
org_id: OrganizationId,
}
#[post("/ciphers/purge?<organization..>", data = "<data>")]
async fn delete_all(
organization: Option<OrganizationId>,
organization: Option<OrganizationIdData>,
data: Json<PasswordOrOtpData>,
headers: Headers,
mut conn: DbConn,
@ -1835,10 +1835,10 @@ pub struct CipherSyncData {
pub cipher_folders: HashMap<String, String>,
pub cipher_favorites: HashSet<String>,
pub cipher_collections: HashMap<String, Vec<String>>,
pub members: HashMap<String, Membership>,
pub members: HashMap<OrganizationId, Membership>,
pub user_collections: HashMap<String, CollectionUser>,
pub user_collections_groups: HashMap<String, CollectionGroup>,
pub user_group_full_access_for_organizations: HashSet<String>,
pub user_group_full_access_for_organizations: HashSet<OrganizationId>,
}
#[derive(Eq, PartialEq)]
@ -1885,7 +1885,7 @@ impl CipherSyncData {
}
// Generate a HashMap with the Organization UUID as key and the Membership record
let members: HashMap<String, Membership> =
let members: HashMap<OrganizationId, Membership> =
Membership::find_by_user(user_uuid, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect();
// Generate a HashMap with the User_Collections UUID as key and the CollectionUser record
@ -1907,7 +1907,7 @@ impl CipherSyncData {
};
// Get all organizations that the given user has full access to via group assignment
let user_group_full_access_for_organizations: HashSet<String> = if CONFIG.org_groups_enabled() {
let user_group_full_access_for_organizations: HashSet<OrganizationId> = if CONFIG.org_groups_enabled() {
Group::get_orgs_by_user_with_full_access(user_uuid, conn).await.into_iter().collect()
} else {
HashSet::new()

@ -8,7 +8,7 @@ use crate::{
api::{EmptyResult, JsonResult},
auth::{AdminHeaders, Headers},
db::{
models::{Cipher, Event, Membership},
models::{Cipher, Event, Membership, OrganizationId},
DbConn, DbPool,
},
util::parse_date,
@ -153,7 +153,7 @@ struct EventCollection {
// Optional
cipher_id: Option<String>,
organization_id: Option<String>,
organization_id: Option<OrganizationId>,
}
// Upstream:
@ -261,7 +261,7 @@ async fn _log_user_event(
pub async fn log_event(
event_type: i32,
source_uuid: &str,
org_uuid: &str,
org_uuid: &OrganizationId,
act_user_uuid: &str,
device_type: i32,
ip: &IpAddr,
@ -277,7 +277,7 @@ pub async fn log_event(
async fn _log_event(
event_type: i32,
source_uuid: &str,
org_uuid: &str,
org_uuid: &OrganizationId,
act_user_uuid: &str,
device_type: i32,
event_date: Option<NaiveDateTime>,
@ -313,7 +313,7 @@ async fn _log_event(
_ => {}
}
event.org_uuid = Some(String::from(org_uuid));
event.org_uuid = Some(org_uuid.clone());
event.act_user_uuid = Some(String::from(act_user_uuid));
event.device_type = Some(device_type);
event.ip_address = Some(ip.to_string());

File diff suppressed because it is too large Load Diff

@ -179,7 +179,7 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
Ok(())
}
pub struct PublicToken(String);
pub struct PublicToken(OrganizationId);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for PublicToken {
@ -222,7 +222,8 @@ impl<'r> FromRequest<'r> for PublicToken {
let Some(org_uuid) = claims.client_id.strip_prefix("organization.") else {
err_handler!("Malformed client_id")
};
let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(org_uuid, &conn).await else {
let org_uuid: OrganizationId = org_uuid.to_string().into();
let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(&org_uuid, &conn).await else {
err_handler!("Invalid client_id")
};
if org_api_key.org_uuid != claims.client_sub {

@ -208,7 +208,7 @@ pub async fn enforce_2fa_policy(
}
pub async fn enforce_2fa_policy_for_org(
org_uuid: &str,
org_uuid: &OrganizationId,
act_uuid: &str,
device_type: i32,
ip: &std::net::IpAddr,

@ -472,7 +472,8 @@ async fn _organization_api_key_login(data: ConnectData, conn: &mut DbConn, ip: &
let Some(org_uuid) = client_id.strip_prefix("organization.") else {
err!("Malformed client_id", format!("IP: {}.", ip.ip))
};
let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(org_uuid, conn).await else {
let org_uuid: OrganizationId = org_uuid.to_string().into();
let Some(org_api_key) = OrganizationApiKey::find_by_org_uuid(&org_uuid, conn).await else {
err!("Invalid client_id", format!("IP: {}.", ip.ip))
};

@ -422,7 +422,7 @@ impl WebSocketUsers {
if *NOTIFICATIONS_DISABLED {
return;
}
let org_uuid = convert_option(cipher.organization_uuid.clone());
let org_uuid = convert_option(cipher.organization_uuid.as_deref());
// Depending if there are collections provided or not, we need to have different values for the following variables.
// The user_uuid should be `null`, and the revision date should be set to now, else the clients won't sync the collection change.
let (user_uuid, collection_uuids, revision_date) = if let Some(collection_uuids) = collection_uuids {

@ -14,6 +14,7 @@ use std::{
net::IpAddr,
};
use crate::db::models::OrganizationId;
use crate::{error::Error, CONFIG};
const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
@ -190,7 +191,7 @@ pub struct InviteJwtClaims {
pub sub: String,
pub email: String,
pub org_id: Option<String>,
pub org_id: Option<OrganizationId>,
pub member_id: Option<String>,
pub invited_by_email: Option<String>,
}
@ -198,7 +199,7 @@ pub struct InviteJwtClaims {
pub fn generate_invite_claims(
uuid: String,
email: String,
org_id: Option<String>,
org_id: Option<OrganizationId>,
member_id: Option<String>,
invited_by_email: Option<String>,
) -> InviteJwtClaims {
@ -266,18 +267,18 @@ pub struct OrgApiKeyLoginJwtClaims {
pub sub: String,
pub client_id: String,
pub client_sub: String,
pub client_sub: OrganizationId,
pub scope: Vec<String>,
}
pub fn generate_organization_api_key_login_claims(uuid: String, org_id: String) -> OrgApiKeyLoginJwtClaims {
pub fn generate_organization_api_key_login_claims(uuid: String, org_id: OrganizationId) -> OrgApiKeyLoginJwtClaims {
let time_now = Utc::now();
OrgApiKeyLoginJwtClaims {
nbf: time_now.timestamp(),
exp: (time_now + TimeDelta::try_hours(1).unwrap()).timestamp(),
iss: JWT_ORG_API_KEY_ISSUER.to_string(),
sub: uuid,
client_id: format!("organization.{org_id}"),
client_id: format!("organization.{}", org_id),
client_sub: org_id,
scope: vec!["api.organization".into()],
}
@ -549,17 +550,17 @@ impl<'r> FromRequest<'r> for OrgHeaders {
// org_id is usually the second path param ("/organizations/<org_id>"),
// but there are cases where it is a query value.
// First check the path, if this is not a valid uuid, try the query values.
let url_org_id: Option<&str> = {
let url_org_id: Option<OrganizationId> = {
let mut url_org_id = None;
if let Some(Ok(org_id)) = request.param::<&str>(1) {
if uuid::Uuid::parse_str(org_id).is_ok() {
url_org_id = Some(org_id);
url_org_id = Some(org_id.to_string().into());
}
}
if let Some(Ok(org_id)) = request.query_value::<&str>("organizationId") {
if uuid::Uuid::parse_str(org_id).is_ok() {
url_org_id = Some(org_id);
url_org_id = Some(org_id.to_string().into());
}
}
@ -574,7 +575,7 @@ impl<'r> FromRequest<'r> for OrgHeaders {
};
let user = headers.user;
let membership = match Membership::find_by_user_and_org(&user.uuid, org_id, &mut conn).await {
let membership = match Membership::find_by_user_and_org(&user.uuid, &org_id, &mut conn).await {
Some(member) => {
if member.status == MembershipStatus::Confirmed as i32 {
member

@ -3,6 +3,7 @@ use std::io::ErrorKind;
use bigdecimal::{BigDecimal, ToPrimitive};
use serde_json::Value;
use super::OrganizationId;
use crate::CONFIG;
db_object! {
@ -172,7 +173,7 @@ impl Attachment {
}}
}
pub async fn size_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn size_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
let result: Option<BigDecimal> = attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
@ -189,7 +190,7 @@ impl Attachment {
}}
}
pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))
@ -203,7 +204,11 @@ impl Attachment {
// This will return all attachments linked to the user or org
// There is no filtering done here if the user actually has access!
// It is used to speed up the sync process, and the matching is done in a different part.
pub async fn find_all_by_user_and_orgs(user_uuid: &str, org_uuids: &Vec<String>, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_all_by_user_and_orgs(
user_uuid: &str,
org_uuids: &Vec<OrganizationId>,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
attachments::table
.left_join(ciphers::table.on(ciphers::uuid.eq(attachments::cipher_uuid)))

@ -1,3 +1,4 @@
use super::OrganizationId;
use crate::crypto::ct_eq;
use chrono::{NaiveDateTime, Utc};
@ -9,7 +10,7 @@ db_object! {
pub struct AuthRequest {
pub uuid: String,
pub user_uuid: String,
pub organization_uuid: Option<String>,
pub organization_uuid: Option<OrganizationId>,
pub request_device_identifier: String,
pub device_type: i32, // https://github.com/bitwarden/server/blob/master/src/Core/Enums/DeviceType.cs

@ -4,7 +4,8 @@ use chrono::{NaiveDateTime, TimeDelta, Utc};
use serde_json::Value;
use super::{
Attachment, CollectionCipher, Favorite, FolderCipher, Group, Membership, MembershipStatus, MembershipType, User,
Attachment, CollectionCipher, Favorite, FolderCipher, Group, Membership, MembershipStatus, MembershipType,
OrganizationId, User,
};
use crate::api::core::{CipherData, CipherSyncData, CipherSyncType};
@ -22,7 +23,7 @@ db_object! {
pub updated_at: NaiveDateTime,
pub user_uuid: Option<String>,
pub organization_uuid: Option<String>,
pub organization_uuid: Option<OrganizationId>,
pub key: Option<String>,

@ -1,6 +1,6 @@
use serde_json::Value;
use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, User};
use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User};
use crate::CONFIG;
db_object! {
@ -9,7 +9,7 @@ db_object! {
#[diesel(primary_key(uuid))]
pub struct Collection {
pub uuid: String,
pub org_uuid: String,
pub org_uuid: OrganizationId,
pub name: String,
pub external_id: Option<String>,
}
@ -35,7 +35,7 @@ db_object! {
/// Local methods
impl Collection {
pub fn new(org_uuid: String, name: String, external_id: Option<String>) -> Self {
pub fn new(org_uuid: OrganizationId, name: String, external_id: Option<String>) -> Self {
let mut new_model = Self {
uuid: crate::util::get_uuid(),
org_uuid,
@ -185,7 +185,7 @@ impl Collection {
}}
}
pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> EmptyResult {
for collection in Self::find_by_organization(org_uuid, conn).await {
collection.delete(conn).await?;
}
@ -279,15 +279,19 @@ impl Collection {
}
}
pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_organization_and_user_uuid(
org_uuid: &OrganizationId,
user_uuid: &str,
conn: &mut DbConn,
) -> Vec<Self> {
Self::find_by_user_uuid(user_uuid.to_owned(), conn)
.await
.into_iter()
.filter(|c| c.org_uuid == org_uuid)
.filter(|c| &c.org_uuid == org_uuid)
.collect()
}
pub async fn find_by_organization(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
collections::table
.filter(collections::org_uuid.eq(org_uuid))
@ -297,7 +301,7 @@ impl Collection {
}}
}
pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
collections::table
.filter(collections::org_uuid.eq(org_uuid))
@ -308,7 +312,7 @@ impl Collection {
}}
}
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
collections::table
.filter(collections::uuid.eq(uuid))
@ -498,7 +502,11 @@ impl Collection {
/// Database methods
impl CollectionUser {
pub async fn find_by_organization_and_user_uuid(org_uuid: &str, user_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_organization_and_user_uuid(
org_uuid: &OrganizationId,
user_uuid: &str,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
users_collections::table
.filter(users_collections::user_uuid.eq(user_uuid))
@ -511,7 +519,7 @@ impl CollectionUser {
}}
}
pub async fn find_by_organization(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_collections::table
.inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid)))
@ -661,7 +669,11 @@ impl CollectionUser {
}}
}
pub async fn delete_all_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_user_and_org(
user_uuid: &str,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> EmptyResult {
let collectionusers = Self::find_by_organization_and_user_uuid(org_uuid, user_uuid, conn).await;
db_run! { conn: {

@ -1,6 +1,7 @@
use crate::db::DbConn;
use serde_json::Value;
use super::OrganizationId;
use crate::{api::EmptyResult, error::MapResult, CONFIG};
use chrono::{NaiveDateTime, TimeDelta, Utc};
@ -18,7 +19,7 @@ db_object! {
pub uuid: String,
pub event_type: i32, // EventType
pub user_uuid: Option<String>,
pub org_uuid: Option<String>,
pub org_uuid: Option<OrganizationId>,
pub cipher_uuid: Option<String>,
pub collection_uuid: Option<String>,
pub group_uuid: Option<String>,

@ -1,4 +1,4 @@
use super::{Membership, User};
use super::{Membership, OrganizationId, User};
use crate::api::EmptyResult;
use crate::db::DbConn;
use crate::error::MapResult;
@ -11,7 +11,7 @@ db_object! {
#[diesel(primary_key(uuid))]
pub struct Group {
pub uuid: String,
pub organizations_uuid: String,
pub organizations_uuid: OrganizationId,
pub name: String,
pub access_all: bool,
pub external_id: Option<String>,
@ -40,7 +40,12 @@ db_object! {
/// Local methods
impl Group {
pub fn new(organizations_uuid: String, name: String, access_all: bool, external_id: Option<String>) -> Self {
pub fn new(
organizations_uuid: OrganizationId,
name: String,
access_all: bool,
external_id: Option<String>,
) -> Self {
let now = Utc::now().naive_utc();
let mut new_model = Self {
@ -163,27 +168,27 @@ impl Group {
}
}
pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> EmptyResult {
for group in Self::find_by_organization(org_uuid, conn).await {
group.delete(conn).await?;
}
Ok(())
}
pub async fn find_by_organization(organizations_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
groups::table
.filter(groups::organizations_uuid.eq(organizations_uuid))
.filter(groups::organizations_uuid.eq(org_uuid))
.load::<GroupDb>(conn)
.expect("Error loading groups")
.from_db()
}}
}
pub async fn count_by_org(organizations_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
groups::table
.filter(groups::organizations_uuid.eq(organizations_uuid))
.filter(groups::organizations_uuid.eq(org_uuid))
.count()
.first::<i64>(conn)
.ok()
@ -191,7 +196,7 @@ impl Group {
}}
}
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
groups::table
.filter(groups::uuid.eq(uuid))
@ -202,7 +207,11 @@ impl Group {
}}
}
pub async fn find_by_external_id_and_org(external_id: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_external_id_and_org(
external_id: &str,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Self> {
db_run! { conn: {
groups::table
.filter(groups::external_id.eq(external_id))
@ -213,7 +222,7 @@ impl Group {
}}
}
//Returns all organizations the user has full access to
pub async fn get_orgs_by_user_with_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec<String> {
pub async fn get_orgs_by_user_with_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec<OrganizationId> {
db_run! { conn: {
groups_users::table
.inner_join(users_organizations::table.on(
@ -226,12 +235,12 @@ impl Group {
.filter(groups::access_all.eq(true))
.select(groups::organizations_uuid)
.distinct()
.load::<String>(conn)
.load::<OrganizationId>(conn)
.expect("Error loading organization group full access information for user")
}}
}
pub async fn is_in_full_access_group(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn is_in_full_access_group(user_uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> bool {
db_run! { conn: {
groups::table
.inner_join(groups_users::table.on(
@ -504,7 +513,7 @@ impl GroupUser {
}}
}
pub async fn has_full_access_by_member(org_uuid: &str, member_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn has_full_access_by_member(org_uuid: &OrganizationId, member_uuid: &str, conn: &mut DbConn) -> bool {
db_run! { conn: {
groups_users::table
.inner_join(groups::table.on(

@ -27,7 +27,9 @@ pub use self::favorite::Favorite;
pub use self::folder::{Folder, FolderCipher};
pub use self::group::{CollectionGroup, Group, GroupUser};
pub use self::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType};
pub use self::organization::{Membership, MembershipStatus, MembershipType, Organization, OrganizationApiKey};
pub use self::organization::{
Membership, MembershipStatus, MembershipType, Organization, OrganizationApiKey, OrganizationId,
};
pub use self::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::two_factor_duo_context::TwoFactorDuoContext;

@ -5,7 +5,7 @@ use crate::api::EmptyResult;
use crate::db::DbConn;
use crate::error::MapResult;
use super::{Membership, MembershipStatus, MembershipType, TwoFactor};
use super::{Membership, MembershipStatus, MembershipType, OrganizationId, TwoFactor};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -13,7 +13,7 @@ db_object! {
#[diesel(primary_key(uuid))]
pub struct OrgPolicy {
pub uuid: String,
pub org_uuid: String,
pub org_uuid: OrganizationId,
pub atype: i32,
pub enabled: bool,
pub data: String,
@ -62,7 +62,7 @@ pub enum OrgPolicyErr {
/// Local methods
impl OrgPolicy {
pub fn new(org_uuid: String, atype: OrgPolicyType, data: String) -> Self {
pub fn new(org_uuid: OrganizationId, atype: OrgPolicyType, data: String) -> Self {
Self {
uuid: crate::util::get_uuid(),
org_uuid,
@ -142,7 +142,7 @@ impl OrgPolicy {
}}
}
pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
org_policies::table
.filter(org_policies::org_uuid.eq(org_uuid))
@ -170,7 +170,11 @@ impl OrgPolicy {
}}
}
pub async fn find_by_org_and_type(org_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_org_and_type(
org_uuid: &OrganizationId,
policy_type: OrgPolicyType,
conn: &mut DbConn,
) -> Option<Self> {
db_run! { conn: {
org_policies::table
.filter(org_policies::org_uuid.eq(org_uuid))
@ -181,7 +185,7 @@ impl OrgPolicy {
}}
}
pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(org_policies::table.filter(org_policies::org_uuid.eq(org_uuid)))
.execute(conn)
@ -246,14 +250,14 @@ impl OrgPolicy {
pub async fn is_applicable_to_user(
user_uuid: &str,
policy_type: OrgPolicyType,
exclude_org_uuid: Option<&str>,
exclude_org_uuid: Option<&OrganizationId>,
conn: &mut DbConn,
) -> bool {
for policy in
OrgPolicy::find_accepted_and_confirmed_by_user_and_active_policy(user_uuid, policy_type, conn).await
{
// Check if we need to skip this organization.
if exclude_org_uuid.is_some() && exclude_org_uuid.unwrap() == policy.org_uuid {
if exclude_org_uuid.is_some() && *exclude_org_uuid.unwrap() == policy.org_uuid {
continue;
}
@ -268,7 +272,7 @@ impl OrgPolicy {
pub async fn is_user_allowed(
user_uuid: &str,
org_uuid: &str,
org_uuid: &OrganizationId,
exclude_current_org: bool,
conn: &mut DbConn,
) -> OrgPolicyResult {
@ -296,7 +300,7 @@ impl OrgPolicy {
Ok(())
}
pub async fn org_is_reset_password_auto_enroll(org_uuid: &str, conn: &mut DbConn) -> bool {
pub async fn org_is_reset_password_auto_enroll(org_uuid: &OrganizationId, conn: &mut DbConn) -> bool {
match OrgPolicy::find_by_org_and_type(org_uuid, OrgPolicyType::ResetPassword, conn).await {
Some(policy) => match serde_json::from_str::<ResetPasswordDataModel>(&policy.data) {
Ok(opts) => {

@ -1,9 +1,13 @@
use chrono::{NaiveDateTime, Utc};
use num_traits::FromPrimitive;
use rocket::request::FromParam;
use serde_json::Value;
use std::{
borrow::Borrow,
cmp::Ordering,
collections::{HashMap, HashSet},
fmt::{Display, Formatter},
ops::Deref,
};
use super::{CollectionUser, Group, GroupUser, OrgPolicy, OrgPolicyType, TwoFactor, User};
@ -15,7 +19,7 @@ db_object! {
#[diesel(table_name = organizations)]
#[diesel(primary_key(uuid))]
pub struct Organization {
pub uuid: String,
pub uuid: OrganizationId,
pub name: String,
pub billing_email: String,
pub private_key: Option<String>,
@ -28,7 +32,7 @@ db_object! {
pub struct Membership {
pub uuid: String,
pub user_uuid: String,
pub org_uuid: String,
pub org_uuid: OrganizationId,
pub access_all: bool,
pub akey: String,
@ -43,7 +47,7 @@ db_object! {
#[diesel(primary_key(uuid, org_uuid))]
pub struct OrganizationApiKey {
pub uuid: String,
pub org_uuid: String,
pub org_uuid: OrganizationId,
pub atype: i32,
pub api_key: String,
pub revision_date: NaiveDateTime,
@ -147,7 +151,7 @@ impl PartialOrd<MembershipType> for i32 {
impl Organization {
pub fn new(name: String, billing_email: String, private_key: Option<String>, public_key: Option<String>) -> Self {
Self {
uuid: crate::util::get_uuid(),
uuid: OrganizationId(crate::util::get_uuid()),
name,
billing_email,
private_key,
@ -200,7 +204,7 @@ impl Organization {
static ACTIVATE_REVOKE_DIFF: i32 = 128;
impl Membership {
pub fn new(user_uuid: String, org_uuid: String) -> Self {
pub fn new(user_uuid: String, org_uuid: OrganizationId) -> Self {
Self {
uuid: crate::util::get_uuid(),
@ -255,7 +259,7 @@ impl Membership {
}
impl OrganizationApiKey {
pub fn new(org_uuid: String, api_key: String) -> Self {
pub fn new(org_uuid: OrganizationId, api_key: String) -> Self {
Self {
uuid: crate::util::get_uuid(),
@ -336,7 +340,7 @@ impl Organization {
}}
}
pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid(uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
organizations::table
.filter(organizations::uuid.eq(uuid))
@ -655,7 +659,7 @@ impl Membership {
}}
}
pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> EmptyResult {
for member in Self::find_by_org(org_uuid, conn).await {
member.delete(conn).await?;
}
@ -669,9 +673,13 @@ impl Membership {
Ok(())
}
pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &mut DbConn) -> Option<Membership> {
pub async fn find_by_email_and_org(
email: &str,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Membership> {
if let Some(user) = User::find_by_mail(email, conn).await {
if let Some(member) = Membership::find_by_user_and_org(&user.uuid, org_id, conn).await {
if let Some(member) = Membership::find_by_user_and_org(&user.uuid, org_uuid, conn).await {
return Some(member);
}
}
@ -700,7 +708,7 @@ impl Membership {
}}
}
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::uuid.eq(uuid))
@ -750,7 +758,7 @@ impl Membership {
}}
}
pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -759,7 +767,7 @@ impl Membership {
}}
}
pub async fn find_confirmed_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_confirmed_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -769,7 +777,7 @@ impl Membership {
}}
}
pub async fn count_by_org(org_uuid: &str, conn: &mut DbConn) -> i64 {
pub async fn count_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> i64 {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -780,7 +788,11 @@ impl Membership {
}}
}
pub async fn find_by_org_and_type(org_uuid: &str, atype: MembershipType, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_org_and_type(
org_uuid: &OrganizationId,
atype: MembershipType,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -790,7 +802,11 @@ impl Membership {
}}
}
pub async fn count_confirmed_by_org_and_type(org_uuid: &str, atype: MembershipType, conn: &mut DbConn) -> i64 {
pub async fn count_confirmed_by_org_and_type(
org_uuid: &OrganizationId,
atype: MembershipType,
conn: &mut DbConn,
) -> i64 {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -802,7 +818,7 @@ impl Membership {
}}
}
pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_user_and_org(user_uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -812,7 +828,11 @@ impl Membership {
}}
}
pub async fn find_confirmed_by_user_and_org(user_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_confirmed_by_user_and_org(
user_uuid: &str,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
@ -834,12 +854,12 @@ impl Membership {
}}
}
pub async fn get_orgs_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<String> {
pub async fn get_orgs_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<OrganizationId> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid))
.select(users_organizations::org_uuid)
.load::<String>(conn)
.load::<OrganizationId>(conn)
.unwrap_or_default()
}}
}
@ -863,7 +883,7 @@ impl Membership {
}}
}
pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_cipher_and_org(cipher_uuid: &str, org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -886,7 +906,11 @@ impl Membership {
}}
}
pub async fn find_by_cipher_and_org_with_group(cipher_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_cipher_and_org_with_group(
cipher_uuid: &str,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -924,7 +948,11 @@ impl Membership {
}}
}
pub async fn find_by_collection_and_org(collection_uuid: &str, org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_collection_and_org(
collection_uuid: &str,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid))
@ -941,7 +969,11 @@ impl Membership {
}}
}
pub async fn find_by_external_id_and_org(ext_id: &str, org_uuid: &str, conn: &mut DbConn) -> Option<Self> {
pub async fn find_by_external_id_and_org(
ext_id: &str,
org_uuid: &OrganizationId,
conn: &mut DbConn,
) -> Option<Self> {
db_run! {conn: {
users_organizations::table
.filter(
@ -987,7 +1019,7 @@ impl OrganizationApiKey {
}
}
pub async fn find_by_org_uuid(org_uuid: &str, conn: &DbConn) -> Option<Self> {
pub async fn find_by_org_uuid(org_uuid: &OrganizationId, conn: &DbConn) -> Option<Self> {
db_run! { conn: {
organization_api_key::table
.filter(organization_api_key::org_uuid.eq(org_uuid))
@ -996,7 +1028,7 @@ impl OrganizationApiKey {
}}
}
pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult {
pub async fn delete_all_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> EmptyResult {
db_run! { conn: {
diesel::delete(organization_api_key::table.filter(organization_api_key::org_uuid.eq(org_uuid)))
.execute(conn)
@ -1005,6 +1037,57 @@ impl OrganizationApiKey {
}
}
#[derive(DieselNewType, FromForm, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct OrganizationId(String);
impl AsRef<str> for OrganizationId {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Deref for OrganizationId {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<str> for OrganizationId {
fn borrow(&self) -> &str {
&self.0
}
}
impl Display for OrganizationId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for OrganizationId {
fn from(raw: String) -> Self {
Self(raw)
}
}
impl<'r> FromParam<'r> for OrganizationId {
type Error = ();
#[inline(always)]
fn from_param(param: &'r str) -> Result<Self, Self::Error> {
if param.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' |'0'..='9' | '-')) {
Ok(OrganizationId(param.to_string()))
} else {
Err(())
}
}
}
#[derive(DieselNewType, Clone, Debug, Hash, PartialEq, Eq, Serialize)]
pub struct MembershipId(String);
#[cfg(test)]
mod tests {
use super::*;

@ -3,7 +3,7 @@ use serde_json::Value;
use crate::util::LowerCase;
use super::User;
use super::{OrganizationId, User};
db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -14,8 +14,7 @@ db_object! {
pub uuid: String,
pub user_uuid: Option<String>,
pub organization_uuid: Option<String>,
pub organization_uuid: Option<OrganizationId>,
pub name: String,
pub notes: Option<String>,
@ -332,7 +331,7 @@ impl Send {
Some(total)
}
pub async fn find_by_org(org_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec<Self> {
db_run! {conn: {
sends::table
.filter(sends::organization_uuid.eq(org_uuid))

@ -17,7 +17,7 @@ use crate::{
encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims,
generate_verify_email_claims,
},
db::models::{Device, DeviceType, User},
db::models::{Device, DeviceType, OrganizationId, User},
error::Error,
CONFIG,
};
@ -259,7 +259,7 @@ pub async fn send_single_org_removed_from_org(address: &str, org_name: &str) ->
pub async fn send_invite(
user: &User,
org_id: Option<String>,
org_id: Option<OrganizationId>,
member_id: Option<String>,
org_name: &str,
invited_by_email: Option<String>,

@ -24,6 +24,8 @@ extern crate log;
extern crate diesel;
#[macro_use]
extern crate diesel_migrations;
#[macro_use]
extern crate diesel_derive_newtype;
use std::{
collections::HashMap,

Loading…
Cancel
Save