Created sharing create permissions

This commit is contained in:
dal 2025-03-19 09:35:56 -06:00
parent 2058b0551f
commit 7a63b97e35
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
6 changed files with 157 additions and 10 deletions

View File

@ -13,6 +13,7 @@ tracing = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
diesel = { workspace = true } diesel = { workspace = true }
diesel-async = { workspace = true } diesel-async = { workspace = true }
thiserror = { workspace = true }
database = { path = "../database" } database = { path = "../database" }

View File

@ -1,4 +1,4 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result, anyhow};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use database::{ use database::{
enums::{AssetPermissionRole, AssetType, IdentityType}, enums::{AssetPermissionRole, AssetType, IdentityType},
@ -10,6 +10,9 @@ use diesel::{prelude::*, upsert::excluded};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use uuid::Uuid; use uuid::Uuid;
use crate::errors::SharingError;
use crate::types::find_user_by_email;
#[derive(Debug)] #[derive(Debug)]
pub struct ShareCreationInput { pub struct ShareCreationInput {
pub asset_id: Uuid, pub asset_id: Uuid,
@ -70,6 +73,53 @@ pub async fn create_share(
.context("Failed to create/update asset permission") .context("Failed to create/update asset permission")
} }
/// Creates or updates an asset permission for a user identified by email
///
/// # Arguments
/// * `email` - The email address of the user to grant access to
/// * `asset_id` - The ID of the asset to share
/// * `asset_type` - The type of asset (must not be deprecated)
/// * `role` - The permission role to assign (must be Owner or FullAccess)
/// * `created_by` - The ID of the user creating the permission
///
/// # Returns
/// * `Result<AssetPermission>` - The created or updated permission record
pub async fn create_share_by_email(
email: &str,
asset_id: Uuid,
asset_type: AssetType,
role: AssetPermissionRole,
created_by: Uuid,
) -> Result<AssetPermission> {
// Validate asset type is not deprecated
if matches!(asset_type, AssetType::Dashboard | AssetType::Thread) {
return Err(anyhow!(SharingError::DeprecatedAssetType(format!("{:?}", asset_type))));
}
// Validate role is either Owner or FullAccess
if !matches!(role, AssetPermissionRole::Owner | AssetPermissionRole::FullAccess) {
return Err(anyhow!(SharingError::InvalidPermissionRole(format!(
"Role must be Owner or FullAccess, got {:?}",
role
))));
}
// Find the user by email
let user = find_user_by_email(email).await?
.ok_or_else(|| anyhow!(SharingError::UserNotFound(email.to_string())))?;
// Create or update the permission
create_share(
asset_id,
asset_type,
user.id,
IdentityType::User,
role,
created_by,
)
.await
}
/// Creates multiple sharing records in bulk /// Creates multiple sharing records in bulk
pub async fn create_shares_bulk( pub async fn create_shares_bulk(
shares: Vec<ShareCreationInput>, shares: Vec<ShareCreationInput>,
@ -122,3 +172,47 @@ pub async fn create_shares_bulk(
.await .await
.context("Failed to create/update asset permissions in bulk") .context("Failed to create/update asset permissions in bulk")
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::SharingError;
#[tokio::test]
async fn test_create_share_by_email_validates_role() {
// Test that only Owner and FullAccess roles are accepted
let result = create_share_by_email(
"test@example.com",
Uuid::new_v4(),
AssetType::Chat,
AssetPermissionRole::CanEdit,
Uuid::new_v4(),
)
.await;
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("Role must be Owner or FullAccess"));
}
#[tokio::test]
async fn test_create_share_by_email_validates_asset_type() {
// Test that deprecated asset types are rejected
let result = create_share_by_email(
"test@example.com",
Uuid::new_v4(),
AssetType::Dashboard, // Deprecated asset type
AssetPermissionRole::Owner,
Uuid::new_v4(),
)
.await;
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("Asset type"));
assert!(err.contains("is deprecated"));
}
// Note: Additional integration tests would be needed to test the database interactions
// These would require mocking the database or using a test database
}

View File

@ -0,0 +1,22 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum SharingError {
#[error("User not found: {0}")]
UserNotFound(String),
#[error("Invalid permission role: {0}")]
InvalidPermissionRole(String),
#[error("Asset type {0:?} is deprecated")]
DeprecatedAssetType(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("Database error: {0}")]
DatabaseError(#[from] diesel::result::Error),
#[error("Unknown error: {0}")]
Unknown(String),
}

View File

@ -1,9 +1,13 @@
// pub mod check_asset_permission; // pub mod check_asset_permission;
pub mod create_asset_permission; pub mod create_asset_permission;
pub mod errors;
pub mod list_asset_permissions; pub mod list_asset_permissions;
pub mod remove_asset_permissions; pub mod remove_asset_permissions;
pub mod types;
// pub use check_asset_permission::check_access; // pub use check_asset_permission::check_access;
pub use create_asset_permission::create_share; pub use create_asset_permission::create_share;
pub use create_asset_permission::create_share_by_email;
pub use list_asset_permissions::list_shares; pub use list_asset_permissions::list_shares;
pub use remove_asset_permissions::remove_share; pub use remove_asset_permissions::remove_share;
pub use types::find_user_by_email;

View File

@ -0,0 +1,26 @@
use anyhow::{Context, Result};
use database::{
models::User,
pool::get_pg_pool,
schema::users,
};
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
/// Finds a user by their email address
///
/// # Arguments
/// * `email` - The email address to search for
///
/// # Returns
/// * `Result<Option<User>>` - The user if found, None otherwise
pub async fn find_user_by_email(email: &str) -> Result<Option<User>> {
let mut conn = get_pg_pool().get().await?;
users::table
.filter(users::email.eq(email))
.first::<User>(&mut conn)
.await
.optional()
.context("Failed to look up user by email")
}

View File

@ -77,17 +77,17 @@ The function should handle the following error cases:
- Error handling utilities - Error handling utilities
## Implementation Plan ## Implementation Plan
1. Enhance the `create_asset_permission.rs` file 1. Enhance the `create_asset_permission.rs` file
2. Implement the `create_share_by_email` function 2. Implement the `create_share_by_email` function
3. Add validation and error handling 3. Add validation and error handling
4. Write tests 4. Write tests
5. Update the library exports in `lib.rs` 5. Update the library exports in `lib.rs`
## Success Criteria ## Success Criteria
- Function correctly creates or updates permissions using email addresses - Function correctly creates or updates permissions using email addresses
- Appropriate validation and error handling is implemented - Appropriate validation and error handling is implemented
- Tests pass successfully - Tests pass successfully
- Code is well-documented - Code is well-documented
## Permission Requirements ## Permission Requirements
- Requires Owner or FullAccess permission to execute - Requires Owner or FullAccess permission to execute