Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 58 additions & 50 deletions crates/defguard_common/src/db/models/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ pub struct Settings {
pub enrollment_welcome_email_subject: Option<String>,
pub enrollment_use_welcome_message_as_email: bool,
pub enrollment_send_welcome_email: bool,
pub enrollment_display_welcome_message: bool,
// Instance UUID needed for desktop client
#[serde(skip)]
pub uuid: Uuid,
Expand Down Expand Up @@ -280,6 +281,10 @@ impl fmt::Debug for Settings {
"enrollment_send_welcome_email",
&self.enrollment_send_welcome_email,
)
.field(
"enrollment_display_welcome_message",
&self.enrollment_display_welcome_message,
)
.field("uuid", &self.uuid)
.field("ldap_url", &self.ldap_url)
.field("ldap_bind_username", &self.ldap_bind_username)
Expand Down Expand Up @@ -487,6 +492,7 @@ impl Settings {
enrollment_vpn_step_optional, enrollment_welcome_message, \
enrollment_welcome_email, enrollment_welcome_email_subject, \
enrollment_use_welcome_message_as_email, enrollment_send_welcome_email, \
enrollment_display_welcome_message, \
uuid, ldap_url, ldap_bind_username, ldap_bind_password, \
ldap_group_search_base, ldap_user_search_base, ldap_user_obj_class, \
ldap_group_obj_class, ldap_username_attr, ldap_groupname_attr, \
Expand Down Expand Up @@ -577,56 +583,57 @@ impl Settings {
enrollment_welcome_email_subject = $23, \
enrollment_use_welcome_message_as_email = $24, \
enrollment_send_welcome_email = $25, \
uuid = $26, \
ldap_url = $27, \
ldap_bind_username = $28, \
ldap_bind_password = $29, \
ldap_group_search_base = $30, \
ldap_user_search_base = $31, \
ldap_user_obj_class = $32, \
ldap_group_obj_class = $33, \
ldap_username_attr = $34, \
ldap_groupname_attr = $35, \
ldap_group_member_attr = $36, \
ldap_member_attr = $37, \
ldap_use_starttls = $38, \
ldap_tls_verify_cert = $39, \
openid_create_account = $40, \
license = $41, \
gateway_disconnect_notifications_enabled = $42, \
gateway_disconnect_notifications_inactivity_threshold = $43, \
gateway_disconnect_notifications_reconnect_notification_enabled = $44, \
ldap_sync_status = $45, \
ldap_enabled = $46, \
ldap_sync_enabled = $47, \
ldap_is_authoritative = $48, \
ldap_sync_interval = $49, \
ldap_user_auxiliary_obj_classes = $50, \
ldap_uses_ad = $51, \
ldap_user_rdn_attr = $52, \
ldap_sync_groups = $53, \
ldap_remote_enrollment_enabled = $54, \
ldap_remote_enrollment_send_invite = $55, \
openid_username_handling = $56, \
defguard_url = $57, \
default_admin_group_name = $58, \
authentication_period_days = $59, \
mfa_code_timeout_seconds = $60, \
public_proxy_url = $61, \
default_admin_id = $62, \
secret_key = $63, \
openid_signing_key_der = $64, \
enable_stats_purge = $65, \
stats_purge_frequency_hours = $66, \
stats_purge_threshold_days = $67, \
enrollment_token_timeout_hours = $68, \
password_reset_token_timeout_hours = $69, \
enrollment_session_timeout_minutes = $70, \
password_reset_session_timeout_minutes = $71, \
ldap_sync_account_status = $72, \
ldap_disable_password_management = $73, \
smtp_oauth_tenant_id = $74, \
smtp_tls_verify_cert = $75 \
enrollment_display_welcome_message = $26, \
uuid = $27, \
ldap_url = $28, \
ldap_bind_username = $29, \
ldap_bind_password = $30, \
ldap_group_search_base = $31, \
ldap_user_search_base = $32, \
ldap_user_obj_class = $33, \
ldap_group_obj_class = $34, \
ldap_username_attr = $35, \
ldap_groupname_attr = $36, \
ldap_group_member_attr = $37, \
ldap_member_attr = $38, \
ldap_use_starttls = $39, \
ldap_tls_verify_cert = $40, \
openid_create_account = $41, \
license = $42, \
gateway_disconnect_notifications_enabled = $43, \
gateway_disconnect_notifications_inactivity_threshold = $44, \
gateway_disconnect_notifications_reconnect_notification_enabled = $45, \
ldap_sync_status = $46, \
ldap_enabled = $47, \
ldap_sync_enabled = $48, \
ldap_is_authoritative = $49, \
ldap_sync_interval = $50, \
ldap_user_auxiliary_obj_classes = $51, \
ldap_uses_ad = $52, \
ldap_user_rdn_attr = $53, \
ldap_sync_groups = $54, \
ldap_remote_enrollment_enabled = $55, \
ldap_remote_enrollment_send_invite = $56, \
openid_username_handling = $57, \
defguard_url = $58, \
default_admin_group_name = $59, \
authentication_period_days = $60, \
mfa_code_timeout_seconds = $61, \
public_proxy_url = $62, \
default_admin_id = $63, \
secret_key = $64, \
openid_signing_key_der = $65, \
enable_stats_purge = $66, \
stats_purge_frequency_hours = $67, \
stats_purge_threshold_days = $68, \
enrollment_token_timeout_hours = $69, \
password_reset_token_timeout_hours = $70, \
enrollment_session_timeout_minutes = $71, \
password_reset_session_timeout_minutes = $72, \
ldap_sync_account_status = $73, \
ldap_disable_password_management = $74, \
smtp_oauth_tenant_id = $75, \
smtp_tls_verify_cert = $76 \
WHERE id = 1",
self.openid_enabled,
self.wireguard_enabled,
Expand All @@ -653,6 +660,7 @@ impl Settings {
self.enrollment_welcome_email_subject,
self.enrollment_use_welcome_message_as_email,
self.enrollment_send_welcome_email,
self.enrollment_display_welcome_message,
self.uuid,
self.ldap_url,
self.ldap_bind_username,
Expand Down
84 changes: 81 additions & 3 deletions crates/defguard_proxy_manager/src/servers/enrollment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,22 @@ impl EnrollmentServer {
admin_device_management: enterprise_settings.admin_device_management,
mfa_required: instance_has_internal_mfa,
};
let settings = Settings::get_current_settings();
let final_page_content = if settings.enrollment_display_welcome_message {
enrollment
.get_welcome_page_content(&mut transaction)
.await?
} else {
debug!(
"Skipping enrollment welcome page content because it is disabled in settings"
);
String::new()
};
let response = defguard_proto::client_types::EnrollmentStartResponse {
admin: admin_info,
user: Some(user_info),
deadline_timestamp: session_deadline.and_utc().timestamp(),
final_page_content: enrollment
.get_welcome_page_content(&mut transaction)
.await?,
final_page_content,
instance: Some(instance_info.into()),
settings: Some(enrollment_settings),
};
Expand Down Expand Up @@ -1232,6 +1241,7 @@ mod test {
setup_pool,
};
use defguard_core::db::models::enrollment::{ENROLLMENT_TOKEN_TYPE, Token};
use defguard_proto::{client_types::EnrollmentStartRequest, proxy::DeviceInfo};
use sqlx::postgres::{PgConnectOptions, PgPoolOptions};
use tokio::sync::{broadcast, mpsc::unbounded_channel};

Expand Down Expand Up @@ -1283,4 +1293,72 @@ mod test {

assert!(result.is_ok());
}

#[sqlx::test]
async fn test_display_welcome_message_if_disabled_returns_empty(
_: PgPoolOptions,
options: PgConnectOptions,
) {
let pool = setup_pool(options).await;

let user = User::new(
"test_user_disabled_display",
None,
"Test",
"User",
"user-disabled-display@test.com",
None,
)
.save(&pool)
.await
.unwrap();

let token = Token::new(
user.id,
None,
Some(user.email.clone()),
3600,
Some(ENROLLMENT_TOKEN_TYPE.to_owned()),
);
token.save(&pool).await.unwrap();

Settings::initialize_runtime_defaults(&pool).await.unwrap();
initialize_current_settings(&pool).await.unwrap();

let mut settings = Settings::get_current_settings();
assert!(
settings
.enrollment_welcome_message
.as_deref()
.is_some_and(|msg| !msg.is_empty()),
"welcome message template must be non-empty for this test to be meaningful"
);
settings.enrollment_display_welcome_message = false;
update_current_settings(&pool, settings).await.unwrap();

let (gateway_tx, _gateway_rx) = broadcast::channel(1);
let (bidi_event_tx, _bidi_events_rx) = unbounded_channel();
let (ldap_tx, _ldap_rx) = unbounded_channel();
let server = EnrollmentServer::new(pool.clone(), gateway_tx, bidi_event_tx, ldap_tx);

let request = EnrollmentStartRequest {
token: token.id.clone(),
};
let device_info = DeviceInfo {
ip_address: "127.0.0.1".to_owned(),
user_agent: None,
version: None,
platform: None,
};
let response = server
.start_enrollment(request, Some(device_info))
.await
.expect("start_enrollment should succeed");

assert!(
response.final_page_content.is_empty(),
"final_page_content should be empty when display is disabled, got: {}",
response.final_page_content
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE settings DROP COLUMN enrollment_display_welcome_message;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE settings ADD COLUMN enrollment_display_welcome_message BOOLEAN NOT NULL DEFAULT true;
50 changes: 32 additions & 18 deletions web/src/pages/EnrollmentPage/tabs/MessageTemplatesTab.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useStore } from '@tanstack/react-form';
import { useMutation, useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import z from 'zod';
import { m } from '../../../paraglide/messages';
import api from '../../../shared/api/api';
Expand Down Expand Up @@ -77,7 +78,8 @@ const MessageTemplatesTabContent = ({ settings }: { settings: Settings }) => {

const defaultValues = useMemo(
(): MessageTemplatesFormFields => ({
enrollment_display_welcome_message: true,
enrollment_display_welcome_message:
settings.enrollment_display_welcome_message ?? true,
enrollment_welcome_message: settings.enrollment_welcome_message ?? '',
enrollment_send_welcome_email: settings.enrollment_send_welcome_email ?? true,
enrollment_welcome_email_subject: settings.enrollment_welcome_email_subject ?? '',
Expand All @@ -96,13 +98,22 @@ const MessageTemplatesTabContent = ({ settings }: { settings: Settings }) => {
onChange: messageTemplatesFormSchema,
},
onSubmit: async ({ value }) => {
const { enrollment_display_welcome_message: _displayWelcomeMessage, ...payload } =
value;
await mutateAsync(payload);
await mutateAsync(value);
form.reset(value);
},
});

const displayWelcomeMessage = useStore(
form.store,
(s) => s.values.enrollment_display_welcome_message,
);

useEffect(() => {
if (!displayWelcomeMessage) {
form.setFieldValue('enrollment_use_welcome_message_as_email', false);
}
}, [displayWelcomeMessage, form]);

return (
<SettingsLayout suggestion={<MessageTemplatesSuggestion />}>
<div data-testid="enrollment-tab-message-templates">
Expand All @@ -125,22 +136,24 @@ const MessageTemplatesTabContent = ({ settings }: { settings: Settings }) => {
<form.AppField name="enrollment_display_welcome_message">
{(field) => (
<field.FormInteractiveBlock
variant="empty"
variant="toggle"
title={m.settings_enrollment_template_display_message_title()}
content={m.settings_enrollment_template_display_message_description()}
>
<SizedBox height={ThemeSpacing.Xl2} />
<form.AppField name="enrollment_welcome_message">
{(field) => (
<field.FormTextarea
required
label={m.settings_enrollment_template_message_label()}
minHeight={383}
maxHeight={383}
helper={m.settings_enrollment_template_helper_welcome_message()}
/>
)}
</form.AppField>
<Fold open={displayWelcomeMessage}>
<SizedBox height={ThemeSpacing.Xl2} />
<form.AppField name="enrollment_welcome_message">
{(field) => (
<field.FormTextarea
required
label={m.settings_enrollment_template_message_label()}
minHeight={383}
maxHeight={383}
helper={m.settings_enrollment_template_helper_welcome_message()}
/>
)}
</form.AppField>
</Fold>
<Divider spacing={ThemeSpacing.Xl2} />
</field.FormInteractiveBlock>
)}
Expand Down Expand Up @@ -178,6 +191,7 @@ const MessageTemplatesTabContent = ({ settings }: { settings: Settings }) => {
<>
<field.FormCheckbox
text={m.settings_enrollment_template_same_as_message()}
disabled={!displayWelcomeMessage}
/>
<SizedBox height={ThemeSpacing.Xl} />
<Fold
Expand Down
1 change: 1 addition & 0 deletions web/src/shared/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,7 @@ export interface SettingsEnrollment {
enrollment_welcome_email_subject: string;
enrollment_use_welcome_message_as_email: boolean;
enrollment_send_welcome_email: boolean;
enrollment_display_welcome_message: boolean;
}

export interface SettingsModules {
Expand Down
Loading