diff --git a/src/predefinitions.rs b/src/predefinitions.rs index 2baad422..73168808 100644 --- a/src/predefinitions.rs +++ b/src/predefinitions.rs @@ -979,6 +979,13 @@ pub async fn initialize_predefined_configurations(database_pool: &deadpool_postg default_text_value: Some("^[a-zA-Z0-9._-]+$".to_string()), ..Default::default() }, + InitialConfigurationProperties { + name: "fields.allowedDisplayNameRegex".to_string(), + description: Some("A regular expression that field display names must match in order to be allowed.".to_string()), + value_type: ConfigurationValueType::Text, + default_text_value: Some("^.+$".to_string()), + ..Default::default() + }, InitialConfigurationProperties { name: "fields.maximumNameLength".to_string(), description: Some("The maximum length of field names in characters. Slashstep Group recommends keeping this value at a reasonable length to maintain performance.".to_string()), @@ -986,6 +993,13 @@ pub async fn initialize_predefined_configurations(database_pool: &deadpool_postg default_number_value: Some(Decimal::from(256 as i64)), ..Default::default() }, + InitialConfigurationProperties { + name: "fields.maximumDescriptionLength".to_string(), + description: Some("The maximum length of field descriptions in characters. Slashstep Group recommends keeping this value at a reasonable length to maintain performance.".to_string()), + value_type: ConfigurationValueType::Number, + default_number_value: Some(Decimal::from(1024 as i64)), + ..Default::default() + }, InitialConfigurationProperties { name: "fields.maximumDisplayNameLength".to_string(), description: Some("The maximum length of field display names in characters. Slashstep Group recommends keeping this value at a reasonable length to maintain performance.".to_string()), diff --git a/src/routes/actions/{action_id}/mod.rs b/src/routes/actions/{action_id}/mod.rs index 841c348e..313b18a8 100644 --- a/src/routes/actions/{action_id}/mod.rs +++ b/src/routes/actions/{action_id}/mod.rs @@ -23,7 +23,7 @@ use crate::{ }, action_log_entry::{ActionLogEntry, ActionLogEntryActorType, ActionLogEntryTargetResourceType, InitialActionLogEntryProperties}, app::App, app_authorization::AppAuthorization, http_transaction::HTTPTransaction, server_log_entry::ServerLogEntry, user::User }, utilities::{reusable_route_handlers::delete_resource, route_handler_utilities::{ - AuthenticatedPrincipal, get_action_by_id, get_action_by_name, get_action_log_entry_expiration_timestamp, get_authenticated_principal, get_request_body_without_json_rejection, get_resource_hierarchy, get_uuid_from_string, validate_action_display_name, validate_action_name, validate_resource_display_name_length, validate_resource_name_length, verify_delegate_permissions, verify_principal_permissions + AuthenticatedPrincipal, get_action_by_id, get_action_by_name, get_action_log_entry_expiration_timestamp, get_authenticated_principal, get_request_body_without_json_rejection, get_resource_hierarchy, get_uuid_from_string, validate_field_length, validate_resource_display_name, validate_resource_name, verify_delegate_permissions, verify_principal_permissions }} }; @@ -85,10 +85,18 @@ async fn handle_patch_action_request( let http_transaction = http_transaction.clone(); let updated_action_properties = get_request_body_without_json_rejection(body, &http_transaction, &state.database_pool).await?; - if let Some(updated_action_name) = &updated_action_properties.name { validate_resource_name_length(updated_action_name, "actions.maximumNameLength", "Action", &http_transaction, &state.database_pool).await?; }; - if let Some(updated_action_display_name) = &updated_action_properties.display_name { validate_resource_display_name_length(updated_action_display_name, "actions.maximumDisplayNameLength", "Action", &http_transaction, &state.database_pool).await?; }; - if let Some(updated_action_name) = &updated_action_properties.name { validate_action_name(updated_action_name, &http_transaction, &state.database_pool).await?; }; - if let Some(updated_action_display_name) = &updated_action_properties.display_name { validate_action_display_name(updated_action_display_name, &http_transaction, &state.database_pool).await?; } + if let Some(updated_action_name) = &updated_action_properties.name { + + validate_field_length(updated_action_name, "actions.maximumNameLength", "name", &http_transaction, &state.database_pool).await?; + validate_resource_name(updated_action_name, "actions.allowedNameRegex", "Action", &http_transaction, &state.database_pool).await?; + + }; + if let Some(updated_action_display_name) = &updated_action_properties.display_name { + + validate_field_length(updated_action_display_name, "actions.maximumDisplayNameLength", "name", &http_transaction, &state.database_pool).await?; + validate_resource_display_name(updated_action_display_name, "actions.allowedDisplayNameRegex", "Action", &http_transaction, &state.database_pool).await?; + + }; let original_target_action = get_action_by_id(&action_id, &http_transaction, &state.database_pool).await?; let resource_hierarchy = get_resource_hierarchy(&original_target_action, &AccessPolicyResourceType::Action, &original_target_action.id, &http_transaction, &state.database_pool).await?; let update_access_policy_action = get_action_by_name("actions.update", &http_transaction, &state.database_pool).await?; diff --git a/src/routes/apps/mod.rs b/src/routes/apps/mod.rs index 0d8ff3f6..4be48eac 100644 --- a/src/routes/apps/mod.rs +++ b/src/routes/apps/mod.rs @@ -18,12 +18,11 @@ use std::sync::Arc; use argon2::{Argon2, PasswordHasher, password_hash::{SaltString, rand_core::{OsRng, le}}}; use axum::{Extension, Json, Router, extract::{Query, State, rejection::JsonRejection}}; use axum_extra::response::ErasedJson; -use rand::{Rng, RngExt, distr::Alphanumeric}; +use rand::{RngExt, distr::Alphanumeric}; use reqwest::StatusCode; -use rust_decimal::prelude::ToPrimitive; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::{AppState, HTTPError, middleware::{authentication_middleware, http_request_middleware}, resources::{ResourceError, access_policy::{AccessPolicyResourceType, ActionPermissionLevel, ResourceHierarchy}, action_log_entry::{ActionLogEntry, ActionLogEntryActorType, ActionLogEntryTargetResourceType, InitialActionLogEntryProperties}, app::{App, AppClientType, AppParentResourceType, DEFAULT_MAXIMUM_APP_LIST_LIMIT, InitialAppProperties}, app_authorization::AppAuthorization, configuration::Configuration, http_transaction::HTTPTransaction, server_log_entry::ServerLogEntry, user::User}, utilities::{reusable_route_handlers::{ResourceListQueryParameters, list_resources}, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_id, get_action_by_name, get_action_log_entry_expiration_timestamp, get_authenticated_principal, get_configuration_by_name, get_request_body_without_json_rejection, validate_resource_display_name_length, validate_resource_name_length, verify_delegate_permissions, verify_principal_permissions}}}; +use crate::{AppState, HTTPError, middleware::{authentication_middleware, http_request_middleware}, resources::{access_policy::{AccessPolicyResourceType, ActionPermissionLevel, ResourceHierarchy}, action_log_entry::{ActionLogEntry, ActionLogEntryActorType, ActionLogEntryTargetResourceType, InitialActionLogEntryProperties}, app::{App, AppClientType, AppParentResourceType, DEFAULT_MAXIMUM_APP_LIST_LIMIT, InitialAppProperties}, app_authorization::AppAuthorization, http_transaction::HTTPTransaction, server_log_entry::ServerLogEntry, user::User}, utilities::{reusable_route_handlers::{ResourceListQueryParameters, list_resources}, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_name, get_action_log_entry_expiration_timestamp, get_authenticated_principal, get_configuration_by_name, get_request_body_without_json_rejection, validate_field_length, verify_delegate_permissions, verify_principal_permissions}}}; #[derive(Debug, Serialize, Deserialize, Default)] pub struct InitialAppPropertiesWithoutClientSecretHash { @@ -189,9 +188,9 @@ async fn handle_create_app_request( let http_transaction = http_transaction.clone(); let app_properties_json = get_request_body_without_json_rejection(body, &http_transaction, &state.database_pool).await?; validate_app_name(&app_properties_json.name, &http_transaction, &state.database_pool).await?; - validate_resource_name_length(&app_properties_json.name, "apps.maximumNameLength", "App", &http_transaction, &state.database_pool).await?; + validate_field_length(&app_properties_json.name, "apps.maximumNameLength", "name", &http_transaction, &state.database_pool).await?; validate_app_display_name(&app_properties_json.display_name, &http_transaction, &state.database_pool).await?; - validate_resource_display_name_length(&app_properties_json.display_name, "apps.maximumDisplayNameLength", "App", &http_transaction, &state.database_pool).await?; + validate_field_length(&app_properties_json.display_name, "apps.maximumDisplayNameLength", "display_name", &http_transaction, &state.database_pool).await?; // Make sure the authenticated_user can create apps for the target action log entry. let resource_hierarchy: ResourceHierarchy = vec![(AccessPolicyResourceType::Server, None)]; diff --git a/src/routes/apps/{app_id}/mod.rs b/src/routes/apps/{app_id}/mod.rs index 1e154e22..75e7b7fb 100644 --- a/src/routes/apps/{app_id}/mod.rs +++ b/src/routes/apps/{app_id}/mod.rs @@ -19,7 +19,7 @@ use crate::{ resources::{ access_policy::{AccessPolicyResourceType, ActionPermissionLevel}, action_log_entry::{ActionLogEntry, ActionLogEntryActorType, ActionLogEntryTargetResourceType, InitialActionLogEntryProperties}, app::{App, EditableAppProperties}, app_authorization::AppAuthorization, http_transaction::HTTPTransaction, server_log_entry::ServerLogEntry, user::User }, - utilities::{reusable_route_handlers::delete_resource, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_name, get_action_log_entry_expiration_timestamp, get_app_by_id, get_authenticated_principal, get_request_body_without_json_rejection, get_resource_hierarchy, get_uuid_from_string, validate_resource_display_name_length, validate_resource_name_length, verify_delegate_permissions, verify_principal_permissions}} + utilities::{reusable_route_handlers::delete_resource, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_name, get_action_log_entry_expiration_timestamp, get_app_by_id, get_authenticated_principal, get_request_body_without_json_rejection, get_resource_hierarchy, get_uuid_from_string, validate_field_length, verify_delegate_permissions, verify_principal_permissions}} }; #[path = "./access-policies/mod.rs"] @@ -119,12 +119,12 @@ async fn handle_patch_app_request( let updated_app_properties = get_request_body_without_json_rejection(body, &http_transaction, &state.database_pool).await?; if let Some(updated_app_name) = &updated_app_properties.name { - validate_resource_name_length(updated_app_name, "apps.maximumNameLength", "App", &http_transaction, &state.database_pool).await?; + validate_field_length(updated_app_name, "apps.maximumNameLength", "name", &http_transaction, &state.database_pool).await?; }; if let Some(updated_app_display_name) = &updated_app_properties.display_name { - validate_resource_display_name_length(updated_app_display_name, "apps.maximumDisplayNameLength", "App", &http_transaction, &state.database_pool).await?; + validate_field_length(updated_app_display_name, "apps.maximumDisplayNameLength", "display_name", &http_transaction, &state.database_pool).await?; }; @@ -136,9 +136,9 @@ async fn handle_patch_app_request( verify_principal_permissions(&authenticated_principal, &update_access_policy_action, &resource_hierarchy, &http_transaction, &ActionPermissionLevel::User, &state.database_pool).await?; ServerLogEntry::trace(&format!("Updating authenticated_app {}...", original_target_app.id), Some(&http_transaction.id), &state.database_pool).await.ok(); - let updated_target_action = match original_target_app.update(&updated_app_properties, &state.database_pool).await { + let updated_target_app = match original_target_app.update(&updated_app_properties, &state.database_pool).await { - Ok(updated_target_action) => updated_target_action, + Ok(updated_target_app) => updated_target_app, Err(error) => { @@ -158,22 +158,22 @@ async fn handle_patch_app_request( actor_type: if let AuthenticatedPrincipal::User(_) = &authenticated_principal { ActionLogEntryActorType::User } else { ActionLogEntryActorType::App }, actor_user_id: if let AuthenticatedPrincipal::User(authenticated_user) = &authenticated_principal { Some(authenticated_user.id.clone()) } else { None }, actor_app_id: if let AuthenticatedPrincipal::App(authenticated_app) = &authenticated_principal { Some(authenticated_app.id.clone()) } else { None }, - target_resource_type: ActionLogEntryTargetResourceType::Action, - target_action_id: Some(updated_target_action.id), + target_resource_type: ActionLogEntryTargetResourceType::App, + target_app_id: Some(updated_target_app.id), ..Default::default() }, &state.database_pool).await.ok(); - ServerLogEntry::success(&format!("Successfully updated action {}.", updated_target_action.id), Some(&http_transaction.id), &state.database_pool).await.ok(); + ServerLogEntry::success(&format!("Successfully updated app {}.", updated_target_app.id), Some(&http_transaction.id), &state.database_pool).await.ok(); - return Ok(Json(updated_target_action)); + return Ok(Json(updated_target_app)); } pub fn get_router(state: AppState) -> Router { let router = Router::::new() - .route("/apps/{action_id}", axum::routing::get(handle_get_app_request)) - .route("/apps/{action_id}", axum::routing::delete(handle_delete_app_request)) - .route("/apps/{action_id}", axum::routing::patch(handle_patch_app_request)) + .route("/apps/{app_id}", axum::routing::get(handle_get_app_request)) + .route("/apps/{app_id}", axum::routing::delete(handle_delete_app_request)) + .route("/apps/{app_id}", axum::routing::patch(handle_patch_app_request)) .layer(axum::middleware::from_fn_with_state(state.clone(), authentication_middleware::authenticate_user)) .layer(axum::middleware::from_fn_with_state(state.clone(), authentication_middleware::authenticate_app)) .layer(axum::middleware::from_fn_with_state(state.clone(), http_request_middleware::create_http_request)) diff --git a/src/routes/fields/{field_id}/mod.rs b/src/routes/fields/{field_id}/mod.rs index a385f7cf..158661b7 100644 --- a/src/routes/fields/{field_id}/mod.rs +++ b/src/routes/fields/{field_id}/mod.rs @@ -26,7 +26,7 @@ use crate::{ resources::{ access_policy::{AccessPolicyResourceType, ActionPermissionLevel}, action_log_entry::{ActionLogEntry, ActionLogEntryActorType, ActionLogEntryTargetResourceType, InitialActionLogEntryProperties}, app::App, app_authorization::AppAuthorization, field::{EditableFieldProperties, Field}, http_transaction::HTTPTransaction, server_log_entry::ServerLogEntry, user::User }, - utilities::{reusable_route_handlers::delete_resource, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_name, get_action_log_entry_expiration_timestamp, get_authenticated_principal, get_field_by_id, get_request_body_without_json_rejection, get_resource_hierarchy, get_uuid_from_string, validate_field_name, validate_resource_display_name_length, validate_resource_name_length, verify_delegate_permissions, verify_principal_permissions}} + utilities::{reusable_route_handlers::delete_resource, route_handler_utilities::{AuthenticatedPrincipal, get_action_by_name, get_action_log_entry_expiration_timestamp, get_authenticated_principal, get_field_by_id, get_request_body_without_json_rejection, get_resource_hierarchy, get_uuid_from_string, validate_field_length, validate_resource_display_name, validate_resource_name, verify_delegate_permissions, verify_principal_permissions}} }; /// GET /fields/{field_id} @@ -119,15 +119,17 @@ async fn handle_patch_field_request( let updated_field_properties = get_request_body_without_json_rejection(body, &http_transaction, &state.database_pool).await?; if let Some(field_name) = &updated_field_properties.name { - validate_field_name(field_name, &http_transaction, &state.database_pool).await?; - validate_resource_name_length(field_name, "fields.maximumNameLength", "Field", &http_transaction, &state.database_pool).await?; + validate_field_length(field_name, "fields.maximumNameLength", "name", &http_transaction, &state.database_pool).await?; + validate_resource_name(field_name, "fields.allowedNameRegex", "Field", &http_transaction, &state.database_pool).await?; }; if let Some(field_display_name) = &updated_field_properties.display_name { - validate_resource_display_name_length(field_display_name, "fields.maximumDisplayNameLength", "Field", &http_transaction, &state.database_pool).await?; + validate_field_length(field_display_name, "fields.maximumDisplayNameLength", "display_name", &http_transaction, &state.database_pool).await?; + validate_resource_display_name(field_display_name, "fields.allowedDisplayNameRegex", "Field", &http_transaction, &state.database_pool).await?; }; + if let Some(field_description) = &updated_field_properties.description { validate_field_length(field_description, "fields.maximumDescriptionLength", "description", &http_transaction, &state.database_pool).await?; }; let field_id = get_uuid_from_string(&field_id, "field", &http_transaction, &state.database_pool).await?; let original_target_field = get_field_by_id(&field_id, &http_transaction, &state.database_pool).await?; let resource_hierarchy = get_resource_hierarchy(&original_target_field, &AccessPolicyResourceType::Field, &original_target_field.id, &http_transaction, &state.database_pool).await?; diff --git a/src/routes/fields/{field_id}/tests.rs b/src/routes/fields/{field_id}/tests.rs index 7491c3b4..a2e9a2ae 100644 --- a/src/routes/fields/{field_id}/tests.rs +++ b/src/routes/fields/{field_id}/tests.rs @@ -1,6 +1,6 @@ /** * - * Any test cases for /fields/{app_id} should be handled here. + * Any test cases for /fields/{field_id} should be handled here. * * Programmers: * - Christian Toney (https://christiantoney.com) @@ -23,7 +23,7 @@ use crate::{ }, resources::{ ResourceError, access_policy::{ AccessPolicy, AccessPolicyPrincipalType, AccessPolicyResourceType, ActionPermissionLevel, InitialAccessPolicyProperties - }, app::App, configuration::{Configuration, EditableConfigurationProperties}, field::{EditableFieldProperties, Field, InitialFieldProperties} + }, app::App, configuration::{Configuration, EditableConfigurationProperties}, field::{EditableFieldProperties, Field} }, tests::{TestEnvironment, TestSlashstepServerError} }; diff --git a/src/utilities/route_handler_utilities.rs b/src/utilities/route_handler_utilities.rs index 98878acc..4a872e47 100644 --- a/src/utilities/route_handler_utilities.rs +++ b/src/utilities/route_handler_utilities.rs @@ -877,7 +877,7 @@ pub async fn get_configuration_by_name(configuration_name: &str, http_transactio } -pub async fn validate_resource_name_length(name: &str, configuration_name: &str, resource_type_name_singular: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { +pub async fn validate_field_length(name: &str, configuration_name: &str, field_name: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { let maximum_name_length_configuration = get_configuration_by_name(configuration_name, http_transaction, database_pool).await?; let maximum_name_length = match maximum_name_length_configuration.number_value.or(maximum_name_length_configuration.default_number_value) { @@ -905,10 +905,10 @@ pub async fn validate_resource_name_length(name: &str, configuration_name: &str, }; - ServerLogEntry::trace(&format!("Validating {} name length...", resource_type_name_singular.to_lowercase()), Some(&http_transaction.id), database_pool).await.ok(); + ServerLogEntry::trace(&format!("Validating \"{}\" field length...", field_name), Some(&http_transaction.id), database_pool).await.ok(); if name.len() > maximum_name_length { - let http_error = HTTPError::UnprocessableEntity(Some(format!("{} names must be at most {} characters long.", resource_type_name_singular, maximum_name_length))); + let http_error = HTTPError::UnprocessableEntity(Some(format!("The \"{}\" field must be at most {} characters long.", field_name, maximum_name_length))); ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); return Err(http_error); @@ -919,72 +919,30 @@ pub async fn validate_resource_name_length(name: &str, configuration_name: &str, } -pub async fn validate_resource_display_name_length(name: &str, configuration_name: &str, resource_type_name_singular: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { +pub async fn validate_resource_name(name: &str, configuration_name: &str, resource_type_name_singular: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { - let maximum_display_name_length_configuration = get_configuration_by_name(configuration_name, http_transaction, database_pool).await?; - let maximum_name_length = match maximum_display_name_length_configuration.number_value.or(maximum_display_name_length_configuration.default_number_value) { - - Some(maximum_name_length) => match maximum_name_length.to_usize() { - - Some(maximum_name_length) => maximum_name_length, - - None => { - - let http_error = HTTPError::InternalServerError(Some(format!("Invalid number value for configuration {}. The value must be a positive integer that can be represented as a usize.", configuration_name))); - ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); - return Err(http_error); - - } - - }, - - None => { - - ServerLogEntry::warning(&format!("Missing value and default value for configuration {}. This is a security risk. Consider setting a restrictive maximum display name length in the configuration.", configuration_name), Some(&http_transaction.id), database_pool).await.ok(); - return Ok(()); - - } - - }; - - ServerLogEntry::trace(&format!("Validating {} display name length...", resource_type_name_singular.to_lowercase()), Some(&http_transaction.id), database_pool).await.ok(); - if name.len() > maximum_name_length { - - let http_error = HTTPError::UnprocessableEntity(Some(format!("{} display names must be at most {} characters long.", resource_type_name_singular, maximum_name_length))); - ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); - return Err(http_error); - - } - - - Ok(()) - -} - -pub async fn validate_action_name(name: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { - - let allowed_name_regex_configuration = get_configuration_by_name("actions.allowedNameRegex", http_transaction, database_pool).await?; + let allowed_name_regex_configuration = get_configuration_by_name(configuration_name, http_transaction, database_pool).await?; let allowed_name_regex_string = match allowed_name_regex_configuration.text_value.or(allowed_name_regex_configuration.default_text_value) { Some(allowed_name_regex_string) => allowed_name_regex_string, None => { - ServerLogEntry::warning("Missing value and default value for configuration actions.allowedNameRegex. Consider setting a regex pattern in the configuration for better security.", Some(&http_transaction.id), database_pool).await.ok(); + ServerLogEntry::warning(&format!("Missing value and default value for configuration {}. Consider setting a regex pattern in the configuration for better security.", configuration_name), Some(&http_transaction.id), database_pool).await.ok(); return Ok(()); } }; - ServerLogEntry::trace("Creating regex for validating action names...", Some(&http_transaction.id), database_pool).await.ok(); + ServerLogEntry::trace(&format!("Creating regex for validating {} names...", resource_type_name_singular.to_lowercase()), Some(&http_transaction.id), database_pool).await.ok(); let regex = match regex::Regex::new(&allowed_name_regex_string) { Ok(regex) => regex, Err(error) => { - let http_error = HTTPError::InternalServerError(Some(format!("Failed to create regex for validating action names: {:?}", error))); + let http_error = HTTPError::InternalServerError(Some(format!("Failed to create regex for validating {} names: {:?}", resource_type_name_singular.to_lowercase(), error))); ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); return Err(http_error) @@ -992,10 +950,10 @@ pub async fn validate_action_name(name: &str, http_transaction: &HTTPTransaction }; - ServerLogEntry::trace("Validating action name against regex...", Some(&http_transaction.id), database_pool).await.ok(); + ServerLogEntry::trace(&format!("Validating {} name against regex...", resource_type_name_singular.to_lowercase()), Some(&http_transaction.id), database_pool).await.ok(); if !regex.is_match(name) { - let http_error = HTTPError::UnprocessableEntity(Some(format!("Action names must match the allowed pattern: {}", allowed_name_regex_string))); + let http_error = HTTPError::UnprocessableEntity(Some(format!("{} names must match the allowed pattern: {}", resource_type_name_singular, allowed_name_regex_string))); ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); return Err(http_error); @@ -1005,30 +963,30 @@ pub async fn validate_action_name(name: &str, http_transaction: &HTTPTransaction } -pub async fn validate_action_display_name(name: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { +pub async fn validate_resource_display_name(name: &str, configuration_name: &str, resource_type_name_singular: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { - let allowed_display_name_regex_configuration = get_configuration_by_name("actions.allowedDisplayNameRegex", http_transaction, database_pool).await?; + let allowed_display_name_regex_configuration = get_configuration_by_name(configuration_name, http_transaction, database_pool).await?; let allowed_display_name_regex_string = match allowed_display_name_regex_configuration.text_value.or(allowed_display_name_regex_configuration.default_text_value) { Some(allowed_display_name_regex_string) => allowed_display_name_regex_string, None => { - ServerLogEntry::warning("Missing value and default value for configuration actions.allowedDisplayNameRegex. Consider setting a regex pattern in the configuration for better security.", Some(&http_transaction.id), database_pool).await.ok(); + ServerLogEntry::warning(&format!("Missing value and default value for configuration {}. Consider setting a regex pattern in the configuration for better security.", configuration_name), Some(&http_transaction.id), database_pool).await.ok(); return Ok(()); } }; - ServerLogEntry::trace("Creating regex for validating action display names...", Some(&http_transaction.id), database_pool).await.ok(); + ServerLogEntry::trace(&format!("Creating regex for validating {} display names...", resource_type_name_singular.to_lowercase()), Some(&http_transaction.id), database_pool).await.ok(); let regex = match regex::Regex::new(&allowed_display_name_regex_string) { Ok(regex) => regex, Err(error) => { - let http_error = HTTPError::InternalServerError(Some(format!("Failed to create regex for validating action display names: {:?}", error))); + let http_error = HTTPError::InternalServerError(Some(format!("Failed to create regex for validating {} display names: {:?}", resource_type_name_singular.to_lowercase(), error))); ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); return Err(http_error) @@ -1036,10 +994,10 @@ pub async fn validate_action_display_name(name: &str, http_transaction: &HTTPTra }; - ServerLogEntry::trace("Validating action display name against regex...", Some(&http_transaction.id), database_pool).await.ok(); + ServerLogEntry::trace(&format!("Validating {} display name against regex...", resource_type_name_singular.to_lowercase()), Some(&http_transaction.id), database_pool).await.ok(); if !regex.is_match(name) { - let http_error = HTTPError::UnprocessableEntity(Some(format!("Action display names must match the allowed pattern: {}", allowed_display_name_regex_string))); + let http_error = HTTPError::UnprocessableEntity(Some(format!("{} display names must match the allowed pattern: {}", resource_type_name_singular, allowed_display_name_regex_string))); ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); return Err(http_error); @@ -1048,47 +1006,3 @@ pub async fn validate_action_display_name(name: &str, http_transaction: &HTTPTra Ok(()) } - -pub async fn validate_field_name(name: &str, http_transaction: &HTTPTransaction, database_pool: &deadpool_postgres::Pool) -> Result<(), HTTPError> { - - let allowed_field_name_regex_configuration = get_configuration_by_name("fields.allowedNameRegex", http_transaction, database_pool).await?; - let allowed_field_name_regex_string = match allowed_field_name_regex_configuration.text_value.or(allowed_field_name_regex_configuration.default_text_value) { - - Some(allowed_field_name_regex_string) => allowed_field_name_regex_string, - - None => { - - ServerLogEntry::warning("Missing value and default value for configuration fields.allowedNameRegex. Consider setting a regex pattern in the configuration for better security.", Some(&http_transaction.id), database_pool).await.ok(); - return Ok(()); - - } - - }; - - ServerLogEntry::trace("Creating regex for validating field names...", Some(&http_transaction.id), database_pool).await.ok(); - let regex = match regex::Regex::new(&allowed_field_name_regex_string) { - - Ok(regex) => regex, - - Err(error) => { - - let http_error = HTTPError::InternalServerError(Some(format!("Failed to create regex for validating field names: {:?}", error))); - ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); - return Err(http_error) - - } - - }; - - ServerLogEntry::trace("Validating field name against regex...", Some(&http_transaction.id), database_pool).await.ok(); - if !regex.is_match(name) { - - let http_error = HTTPError::UnprocessableEntity(Some(format!("Field names must match the allowed pattern: {}", allowed_field_name_regex_string))); - ServerLogEntry::from_http_error(&http_error, Some(&http_transaction.id), database_pool).await.ok(); - return Err(http_error); - - } - - Ok(()) - -} \ No newline at end of file