Skip to content

Commit 42e750c

Browse files
committed
Merge branch 'dev' into video_tutorials_menu
2 parents d1c4c93 + 447b50f commit 42e750c

56 files changed

Lines changed: 620 additions & 191 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 22 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/defguard_core/src/enterprise/db/models/acl.rs

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -316,24 +316,19 @@ impl<I> AclRule<I> {
316316
impl AclRule {
317317
/// Creates new [`AclRule`] with all related objects based on [`ApiAclRule`]
318318
pub(crate) async fn create_from_api(
319-
pool: &PgPool,
319+
conn: &mut PgConnection,
320320
api_rule: &EditAclRule,
321321
actor: &str,
322322
) -> Result<ApiAclRule, AclError> {
323-
let mut transaction = pool.begin().await?;
324-
325323
// save the rule
326324
let mut rule: AclRule = api_rule.clone().try_into()?;
327325
rule.stamp_modified(actor);
328-
let rule = rule.save(&mut *transaction).await?;
326+
let rule = rule.save(&mut *conn).await?;
329327

330328
// create related objects
331-
rule.create_related_objects(&mut transaction, api_rule)
332-
.await?;
329+
rule.create_related_objects(conn, api_rule).await?;
333330

334-
let result = ApiAclRule::from(rule.to_info(&mut transaction).await?);
335-
336-
transaction.commit().await?;
331+
let result = ApiAclRule::from(rule.to_info(&mut *conn).await?);
337332

338333
Ok(result)
339334
}
@@ -355,21 +350,18 @@ impl AclRule {
355350
/// and performed appropriate operations, only that the next time configuration
356351
/// is being sent it will include this rule.
357352
pub(crate) async fn update_from_api(
358-
pool: &PgPool,
353+
conn: &mut PgConnection,
359354
id: Id,
360355
api_rule: &EditAclRule,
361356
actor: &str,
362357
) -> Result<ApiAclRule, AclError> {
363358
debug!("Updating rule ID {id} with {api_rule:?}");
364-
let mut transaction = pool.begin().await?;
365359

366360
// find the existing rule
367-
let existing_rule = AclRule::find_by_id(&mut *transaction, id)
368-
.await?
369-
.ok_or_else(|| {
370-
warn!("Update of nonexistent rule ({id}) failed");
371-
AclError::RuleNotFoundError(id)
372-
})?;
361+
let existing_rule = AclRule::find_by_id(&mut *conn, id).await?.ok_or_else(|| {
362+
warn!("Update of nonexistent rule ({id}) failed");
363+
AclError::RuleNotFoundError(id)
364+
})?;
373365

374366
// convert API rule to model
375367
let mut rule: AclRule<NoId> = api_rule.clone().try_into()?;
@@ -385,7 +377,7 @@ impl AclRule {
385377
);
386378
// remove old modifications of this rule
387379
let result = query!("DELETE FROM aclrule WHERE parent_id = $1", id)
388-
.execute(&mut *transaction)
380+
.execute(&mut *conn)
389381
.await?;
390382
debug!(
391383
"Removed {} old modifications of rule {id}",
@@ -395,11 +387,10 @@ impl AclRule {
395387
// save as a new rule with appropriate parent_id and state
396388
rule.state = RuleState::Modified;
397389
rule.parent_id = Some(id);
398-
let rule = rule.save(&mut *transaction).await?;
390+
let rule = rule.save(&mut *conn).await?;
399391

400392
// create related objects
401-
rule.create_related_objects(&mut transaction, api_rule)
402-
.await?;
393+
rule.create_related_objects(conn, api_rule).await?;
403394

404395
rule
405396
}
@@ -416,20 +407,17 @@ impl AclRule {
416407
let mut rule = rule.with_id(id);
417408
rule.parent_id = existing_rule.parent_id;
418409
rule.state = existing_rule.state;
419-
rule.save(&mut *transaction).await?;
410+
rule.save(&mut *conn).await?;
420411

421412
// recreate related objects
422-
rule.delete_related_objects(&mut transaction).await?;
423-
rule.create_related_objects(&mut transaction, api_rule)
424-
.await?;
413+
rule.delete_related_objects(conn).await?;
414+
rule.create_related_objects(conn, api_rule).await?;
425415

426416
rule
427417
}
428418
};
429419

430-
let rule_details = rule.to_info(&mut transaction).await?.into();
431-
432-
transaction.commit().await?;
420+
let rule_details = rule.to_info(&mut *conn).await?.into();
433421

434422
info!("Successfully updated rule {rule_details:?}");
435423
Ok(rule_details)

crates/defguard_core/src/enterprise/handlers/acl.rs

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use axum::{
99
use chrono::NaiveDateTime;
1010
use defguard_common::db::Id;
1111
use serde_json::{Value, json};
12-
use sqlx::query_as;
12+
use sqlx::{PgConnection, query_as};
1313
use utoipa::ToSchema;
1414

1515
use super::LicenseInfo;
@@ -137,17 +137,52 @@ pub struct EditAclRule {
137137
}
138138

139139
impl EditAclRule {
140-
pub fn validate(&self) -> Result<(), WebError> {
141-
let manual_configured = self.any_address
142-
|| self.any_port
143-
|| self.any_protocol
144-
|| !self.addresses.trim().is_empty()
145-
|| !self.ports.trim().is_empty()
146-
|| !self.protocols.is_empty();
140+
pub async fn validate(&self, conn: &mut PgConnection) -> Result<(), WebError> {
147141
if self.use_manual_destination_settings {
148-
if !manual_configured {
142+
// Determine what the selected component aliases collectively contribute.
143+
// Note: Component-kind aliases always have any_address/any_port/any_protocol = false,
144+
// so we only check whether they have non-empty arrays.
145+
let (alias_has_address, alias_has_port, alias_has_protocol) = if self.aliases.is_empty()
146+
{
147+
(false, false, false)
148+
} else {
149+
let row = query_as::<_, (Option<bool>, Option<bool>, Option<bool>)>(
150+
"SELECT \
151+
bool_or(array_length(addresses, 1) > 0) \
152+
OR EXISTS ( \
153+
SELECT 1 FROM aclaliasdestinationrange dr \
154+
JOIN aclalias a ON a.id = dr.alias_id \
155+
WHERE a.id = ANY($1) \
156+
AND a.kind = 'component'::aclalias_kind \
157+
), \
158+
bool_or(array_length(ports, 1) > 0), \
159+
bool_or(array_length(protocols, 1) > 0) \
160+
FROM aclalias WHERE id = ANY($1) AND kind = 'component'::aclalias_kind",
161+
)
162+
.bind(&self.aliases)
163+
.fetch_one(&mut *conn)
164+
.await
165+
.map_err(WebError::from)?;
166+
(
167+
row.0.unwrap_or(false),
168+
row.1.unwrap_or(false),
169+
row.2.unwrap_or(false),
170+
)
171+
};
172+
173+
if !self.any_address && self.addresses.trim().is_empty() && !alias_has_address {
149174
return Err(WebError::BadRequest(
150-
"Must provide manual destination settings".to_string(),
175+
"Must provide destination address, or enable any address".to_string(),
176+
));
177+
}
178+
if !self.any_port && self.ports.trim().is_empty() && !alias_has_port {
179+
return Err(WebError::BadRequest(
180+
"Must provide destination port, or enable any port".to_string(),
181+
));
182+
}
183+
if !self.any_protocol && self.protocols.is_empty() && !alias_has_protocol {
184+
return Err(WebError::BadRequest(
185+
"Must provide destination protocol, or enable any protocol".to_string(),
151186
));
152187
}
153188
} else if self.destinations.is_empty() {
@@ -347,15 +382,15 @@ pub(crate) async fn create_acl_rule(
347382
) -> ApiResult {
348383
debug!("User {} creating ACL rule {data:?}", session.user.username);
349384

350-
// validate submitted ACL rule
351-
data.validate()?;
352-
353-
let rule = AclRule::create_from_api(&appstate.pool, &data, &session.user.username)
385+
let mut tx = appstate.pool.begin().await?;
386+
data.validate(&mut tx).await?;
387+
let rule = AclRule::create_from_api(&mut tx, &data, &session.user.username)
354388
.await
355389
.map_err(|err| {
356390
error!("Error creating ACL rule {data:?}: {err}");
357391
err
358392
})?;
393+
tx.commit().await?;
359394
info!(
360395
"User {} created ACL rule {}",
361396
session.user.username, rule.id
@@ -386,15 +421,15 @@ pub(crate) async fn update_acl_rule(
386421
) -> ApiResult {
387422
debug!("User {} updating ACL rule {data:?}", session.user.username);
388423

389-
// validate submitted ACL rule
390-
data.validate()?;
391-
392-
let rule = AclRule::update_from_api(&appstate.pool, id, &data, &session.user.username)
424+
let mut tx = appstate.pool.begin().await?;
425+
data.validate(&mut tx).await?;
426+
let rule = AclRule::update_from_api(&mut tx, id, &data, &session.user.username)
393427
.await
394428
.map_err(|err| {
395429
error!("Error updating ACL rule {data:?}: {err}");
396430
err
397431
})?;
432+
tx.commit().await?;
398433
info!("User {} updated ACL rule", session.user.username);
399434
Ok(ApiResponse::json(rule, StatusCode::OK))
400435
}

crates/defguard_core/src/handlers/proxy.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -324,15 +324,14 @@ pub(crate) async fn proxy_cert_self_signed(
324324
WebError::Http(StatusCode::INTERNAL_SERVER_ERROR)
325325
})?;
326326

327-
let (ca_cert_der, ca_key_der) = match (certs.ca_cert_der.clone(), certs.ca_key_der.clone()) {
328-
(Some(c), Some(k)) => (c, k),
329-
_ => {
330-
warn!("CA not configured; cannot issue self-signed proxy cert");
331-
return Ok(ApiResponse::json(
332-
serde_json::json!({"msg": "Core CA is not configured"}),
333-
StatusCode::BAD_REQUEST,
334-
));
335-
}
327+
let (Some(ca_cert_der), Some(ca_key_der)) =
328+
(certs.ca_cert_der.clone(), certs.ca_key_der.clone())
329+
else {
330+
warn!("CA not configured; cannot issue self-signed proxy cert");
331+
return Ok(ApiResponse::json(
332+
serde_json::json!({"msg": "Core CA is not configured"}),
333+
StatusCode::BAD_REQUEST,
334+
));
336335
};
337336

338337
// Build CA from stored DER blobs.

0 commit comments

Comments
 (0)