From eac264578a9861d1c23ed5c5738c8ebde891705f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Tue, 27 Jan 2026 10:32:58 +0100 Subject: [PATCH 01/11] Destinations page --- Cargo.lock | 23 +++--------- crates/defguard_common/Cargo.toml | 2 +- .../DestinationsPage/DestinationsPage.tsx | 37 +++++++++++++++++++ web/src/routeTree.gen.ts | 22 +++++++++++ .../_authorized/_default/acl/destinations.tsx | 6 +++ .../components/Navigation/Navigation.tsx | 6 +++ web/src/shared/defguard-ui | 2 +- 7 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 web/src/pages/DestinationsPage/DestinationsPage.tsx create mode 100644 web/src/routes/_authorized/_default/acl/destinations.tsx diff --git a/Cargo.lock b/Cargo.lock index 82c1bfebcf..a8c9735c0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5029,9 +5029,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" @@ -6235,14 +6235,14 @@ dependencies = [ "derive_builder", "rustversion", "time", - "vergen-lib 9.1.0", + "vergen-lib", ] [[package]] name = "vergen-git2" -version = "1.0.7" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f6ee511ec45098eabade8a0750e76eec671e7fb2d9360c563911336bea9cac1" +checksum = "d51ab55ddf1188c8d679f349775362b0fa9e90bd7a4ac69838b2a087623f0d57" dependencies = [ "anyhow", "derive_builder", @@ -6250,18 +6250,7 @@ dependencies = [ "rustversion", "time", "vergen", - "vergen-lib 0.1.6", -] - -[[package]] -name = "vergen-lib" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", + "vergen-lib", ] [[package]] diff --git a/crates/defguard_common/Cargo.toml b/crates/defguard_common/Cargo.toml index a94108f5e8..2cbdec8f2a 100644 --- a/crates/defguard_common/Cargo.toml +++ b/crates/defguard_common/Cargo.toml @@ -43,4 +43,4 @@ x25519-dalek.workspace = true matches.workspace = true [build-dependencies] -vergen-git2 = { version = "1.0", features = ["build"] } +vergen-git2 = { version = "9.1", features = ["build"] } diff --git a/web/src/pages/DestinationsPage/DestinationsPage.tsx b/web/src/pages/DestinationsPage/DestinationsPage.tsx new file mode 100644 index 0000000000..19b0e8ff03 --- /dev/null +++ b/web/src/pages/DestinationsPage/DestinationsPage.tsx @@ -0,0 +1,37 @@ +import { useMemo, useState } from 'react'; +import { AclDeploymentState, type AclDeploymentStateValue } from '../../shared/api/types'; +import { Page } from '../../shared/components/Page/Page'; +import { Tabs } from '../../shared/defguard-ui/components/Tabs/Tabs'; +import type { TabsItem } from '../../shared/defguard-ui/components/Tabs/types'; + +export const DestinationsPage = () => { + const [activeTab, setActiveTab] = useState( + AclDeploymentState.Applied, + ); + + const tabs = useMemo( + (): TabsItem[] => [ + { + active: activeTab === AclDeploymentState.Applied, + onClick: () => { + setActiveTab(AclDeploymentState.Applied); + }, + title: 'Deployed', + }, + { + active: activeTab === AclDeploymentState.Modified, + onClick: () => { + setActiveTab(AclDeploymentState.Modified); + }, + title: 'Pending', + }, + ], + [activeTab], + ); + + return ( + + + + ); +}; diff --git a/web/src/routeTree.gen.ts b/web/src/routeTree.gen.ts index 18ed878a26..a43aef0a16 100644 --- a/web/src/routeTree.gen.ts +++ b/web/src/routeTree.gen.ts @@ -47,6 +47,7 @@ import { Route as AuthorizedDefaultSettingsEditOpenidRouteImport } from './route import { Route as AuthorizedDefaultSettingsClientRouteImport } from './routes/_authorized/_default/settings/client' import { Route as AuthorizedDefaultAclRulesRouteImport } from './routes/_authorized/_default/acl/rules' import { Route as AuthorizedDefaultAclEditAliasRouteImport } from './routes/_authorized/_default/acl/edit-alias' +import { Route as AuthorizedDefaultAclDestinationsRouteImport } from './routes/_authorized/_default/acl/destinations' import { Route as AuthorizedDefaultAclAliasesRouteImport } from './routes/_authorized/_default/acl/aliases' import { Route as AuthorizedDefaultAclAddRuleRouteImport } from './routes/_authorized/_default/acl/add-rule' import { Route as AuthorizedDefaultAclAddAliasRouteImport } from './routes/_authorized/_default/acl/add-alias' @@ -259,6 +260,12 @@ const AuthorizedDefaultAclEditAliasRoute = path: '/acl/edit-alias', getParentRoute: () => AuthorizedDefaultRoute, } as any) +const AuthorizedDefaultAclDestinationsRoute = + AuthorizedDefaultAclDestinationsRouteImport.update({ + id: '/acl/destinations', + path: '/acl/destinations', + getParentRoute: () => AuthorizedDefaultRoute, + } as any) const AuthorizedDefaultAclAliasesRoute = AuthorizedDefaultAclAliasesRouteImport.update({ id: '/acl/aliases', @@ -312,6 +319,7 @@ export interface FileRoutesByFullPath { '/acl/add-alias': typeof AuthorizedDefaultAclAddAliasRoute '/acl/add-rule': typeof AuthorizedDefaultAclAddRuleRoute '/acl/aliases': typeof AuthorizedDefaultAclAliasesRoute + '/acl/destinations': typeof AuthorizedDefaultAclDestinationsRoute '/acl/edit-alias': typeof AuthorizedDefaultAclEditAliasRoute '/acl/rules': typeof AuthorizedDefaultAclRulesRoute '/settings/client': typeof AuthorizedDefaultSettingsClientRoute @@ -353,6 +361,7 @@ export interface FileRoutesByTo { '/acl/add-alias': typeof AuthorizedDefaultAclAddAliasRoute '/acl/add-rule': typeof AuthorizedDefaultAclAddRuleRoute '/acl/aliases': typeof AuthorizedDefaultAclAliasesRoute + '/acl/destinations': typeof AuthorizedDefaultAclDestinationsRoute '/acl/edit-alias': typeof AuthorizedDefaultAclEditAliasRoute '/acl/rules': typeof AuthorizedDefaultAclRulesRoute '/settings/client': typeof AuthorizedDefaultSettingsClientRoute @@ -398,6 +407,7 @@ export interface FileRoutesById { '/_authorized/_default/acl/add-alias': typeof AuthorizedDefaultAclAddAliasRoute '/_authorized/_default/acl/add-rule': typeof AuthorizedDefaultAclAddRuleRoute '/_authorized/_default/acl/aliases': typeof AuthorizedDefaultAclAliasesRoute + '/_authorized/_default/acl/destinations': typeof AuthorizedDefaultAclDestinationsRoute '/_authorized/_default/acl/edit-alias': typeof AuthorizedDefaultAclEditAliasRoute '/_authorized/_default/acl/rules': typeof AuthorizedDefaultAclRulesRoute '/_authorized/_default/settings/client': typeof AuthorizedDefaultSettingsClientRoute @@ -442,6 +452,7 @@ export interface FileRouteTypes { | '/acl/add-alias' | '/acl/add-rule' | '/acl/aliases' + | '/acl/destinations' | '/acl/edit-alias' | '/acl/rules' | '/settings/client' @@ -483,6 +494,7 @@ export interface FileRouteTypes { | '/acl/add-alias' | '/acl/add-rule' | '/acl/aliases' + | '/acl/destinations' | '/acl/edit-alias' | '/acl/rules' | '/settings/client' @@ -527,6 +539,7 @@ export interface FileRouteTypes { | '/_authorized/_default/acl/add-alias' | '/_authorized/_default/acl/add-rule' | '/_authorized/_default/acl/aliases' + | '/_authorized/_default/acl/destinations' | '/_authorized/_default/acl/edit-alias' | '/_authorized/_default/acl/rules' | '/_authorized/_default/settings/client' @@ -819,6 +832,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthorizedDefaultAclEditAliasRouteImport parentRoute: typeof AuthorizedDefaultRoute } + '/_authorized/_default/acl/destinations': { + id: '/_authorized/_default/acl/destinations' + path: '/acl/destinations' + fullPath: '/acl/destinations' + preLoaderRoute: typeof AuthorizedDefaultAclDestinationsRouteImport + parentRoute: typeof AuthorizedDefaultRoute + } '/_authorized/_default/acl/aliases': { id: '/_authorized/_default/acl/aliases' path: '/acl/aliases' @@ -860,6 +880,7 @@ interface AuthorizedDefaultRouteChildren { AuthorizedDefaultAclAddAliasRoute: typeof AuthorizedDefaultAclAddAliasRoute AuthorizedDefaultAclAddRuleRoute: typeof AuthorizedDefaultAclAddRuleRoute AuthorizedDefaultAclAliasesRoute: typeof AuthorizedDefaultAclAliasesRoute + AuthorizedDefaultAclDestinationsRoute: typeof AuthorizedDefaultAclDestinationsRoute AuthorizedDefaultAclEditAliasRoute: typeof AuthorizedDefaultAclEditAliasRoute AuthorizedDefaultAclRulesRoute: typeof AuthorizedDefaultAclRulesRoute AuthorizedDefaultSettingsClientRoute: typeof AuthorizedDefaultSettingsClientRoute @@ -885,6 +906,7 @@ const AuthorizedDefaultRouteChildren: AuthorizedDefaultRouteChildren = { AuthorizedDefaultAclAddAliasRoute: AuthorizedDefaultAclAddAliasRoute, AuthorizedDefaultAclAddRuleRoute: AuthorizedDefaultAclAddRuleRoute, AuthorizedDefaultAclAliasesRoute: AuthorizedDefaultAclAliasesRoute, + AuthorizedDefaultAclDestinationsRoute: AuthorizedDefaultAclDestinationsRoute, AuthorizedDefaultAclEditAliasRoute: AuthorizedDefaultAclEditAliasRoute, AuthorizedDefaultAclRulesRoute: AuthorizedDefaultAclRulesRoute, AuthorizedDefaultSettingsClientRoute: AuthorizedDefaultSettingsClientRoute, diff --git a/web/src/routes/_authorized/_default/acl/destinations.tsx b/web/src/routes/_authorized/_default/acl/destinations.tsx new file mode 100644 index 0000000000..187bd6ee38 --- /dev/null +++ b/web/src/routes/_authorized/_default/acl/destinations.tsx @@ -0,0 +1,6 @@ +import { createFileRoute } from '@tanstack/react-router'; +import { DestinationsPage } from '../../../../pages/DestinationsPage/DestinationsPage'; + +export const Route = createFileRoute('/_authorized/_default/acl/destinations')({ + component: DestinationsPage, +}); diff --git a/web/src/shared/components/Navigation/Navigation.tsx b/web/src/shared/components/Navigation/Navigation.tsx index 2e38186df5..344c88c277 100644 --- a/web/src/shared/components/Navigation/Navigation.tsx +++ b/web/src/shared/components/Navigation/Navigation.tsx @@ -73,6 +73,12 @@ const navigationConfig: NavGroupProps[] = [ label: m.cmp_nav_item_rules(), link: '/acl/rules', }, + { + id: 'destinations', + icon: 'gateway', + label: m.cmp_nav_item_destinations(), + link: '/acl/destinations', + }, { id: 'aliases', icon: 'access-settings', diff --git a/web/src/shared/defguard-ui b/web/src/shared/defguard-ui index 8630c3cf23..dc3ada9c02 160000 --- a/web/src/shared/defguard-ui +++ b/web/src/shared/defguard-ui @@ -1 +1 @@ -Subproject commit 8630c3cf23a08d7e283a6c2e482e6a78cef7489e +Subproject commit dc3ada9c0254082fbcba925c8f7b11ff914b5865 From 68fb9b643b8964bbc4d849c7b338d6e592a2fecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Tue, 27 Jan 2026 11:29:39 +0100 Subject: [PATCH 02/11] Add any_ to aclalias --- .../src/enterprise/db/models/acl.rs | 14 +- .../src/enterprise/db/models/acl/tests.rs | 9 + .../src/enterprise/handlers/acl.rs | 172 +--------------- .../src/enterprise/handlers/acl/alias.rs | 186 ++++++++++++++++++ .../enterprise/handlers/acl/destination.rs | 9 +- crates/defguard_core/src/lib.rs | 10 +- crates/defguard_core/src/openapi.rs | 10 +- .../tests/integration/api/acl.rs | 9 + ...260127094513_[2.0.0]_aclalias_any.down.sql | 4 + ...20260127094513_[2.0.0]_aclalias_any.up.sql | 4 + 10 files changed, 239 insertions(+), 188 deletions(-) create mode 100644 crates/defguard_core/src/enterprise/handlers/acl/alias.rs create mode 100644 migrations/20260127094513_[2.0.0]_aclalias_any.down.sql create mode 100644 migrations/20260127094513_[2.0.0]_aclalias_any.up.sql diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index 39ba6c96ea..04d353113e 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -939,7 +939,7 @@ impl AclRule { query_as!( AclAlias, "SELECT a.id, parent_id, name, kind \"kind: AliasKind\",state \"state: AliasState\", \ - destination, ports, protocols \ + destination, ports, protocols, any_destination, any_port, any_protocol \ FROM aclrulealias r \ JOIN aclalias a \ ON a.id = r.alias_id \ @@ -1425,6 +1425,9 @@ pub struct AclAlias { pub ports: Vec>, #[model(ref)] pub protocols: Vec, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } impl AclAlias { @@ -1436,6 +1439,9 @@ impl AclAlias { destination: Vec, ports: Vec>, protocols: Vec, + any_destination: bool, + any_port: bool, + any_protocol: bool, ) -> Self { Self { id: NoId, @@ -1446,6 +1452,9 @@ impl AclAlias { destination, ports, protocols, + any_destination, + any_port, + any_protocol, } } @@ -1463,6 +1472,9 @@ impl AclAlias { kind, state: AliasState::Applied, protocols: alias.protocols.clone(), + any_destination: true, + any_port: true, + any_protocol: true, }) } diff --git a/crates/defguard_core/src/enterprise/db/models/acl/tests.rs b/crates/defguard_core/src/enterprise/db/models/acl/tests.rs index 9a0f5c96f4..08dbc295f6 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl/tests.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl/tests.rs @@ -31,6 +31,9 @@ async fn test_alias(_: PgPoolOptions, options: PgConnectOptions) { destination.clone(), ports.clone(), vec![20, 30], + true, + true, + true, ) .save(&pool) .await @@ -334,6 +337,9 @@ async fn test_rule_relations(_: PgPoolOptions, options: PgConnectOptions) { Vec::new(), Vec::new(), Vec::new(), + true, + true, + true, ) .save(&pool) .await @@ -345,6 +351,9 @@ async fn test_rule_relations(_: PgPoolOptions, options: PgConnectOptions) { Vec::new(), Vec::new(), Vec::new(), + true, + true, + true, ) .save(&pool) .await diff --git a/crates/defguard_core/src/enterprise/handlers/acl.rs b/crates/defguard_core/src/enterprise/handlers/acl.rs index b341029b16..9dcf3f2aa4 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl.rs @@ -1,3 +1,4 @@ +pub(crate) mod alias; pub(crate) mod destination; use axum::{ @@ -380,177 +381,6 @@ pub(crate) async fn delete_acl_rule( Ok(ApiResponse::default()) } -/// List all ACL aliases. -#[utoipa::path( - get, - path = "/api/v1/acl/alias", - tag = "ACL", - responses( - (status = OK, description = "ACL alias"), - ), -)] -pub(crate) async fn list_acl_aliases( - _license: LicenseInfo, - _admin: AdminRole, - State(appstate): State, - session: SessionInfo, -) -> ApiResult { - debug!("User {} listing ACL aliases", session.user.username); - let aliases = AclAlias::all(&appstate.pool).await?; - let mut api_aliases: Vec = Vec::with_capacity(aliases.len()); - for alias in &aliases { - // TODO: may require optimisation wrt. sql queries - let info = alias.to_info(&appstate.pool).await.map_err(|err| { - error!("Error retrieving ACL alias {alias:?}: {err}"); - err - })?; - api_aliases.push(info.into()); - } - info!("User {} listed ACL aliases", session.user.username); - Ok(ApiResponse { - json: json!(api_aliases), - status: StatusCode::OK, - }) -} - -/// Get ACL alias. -#[utoipa::path( - get, - path = "/api/v1/acl/alias/{id}", - tag = "ACL", - params( - ("id" = Id, Path, description = "ID of ACL alias",) - ), - responses( - (status = OK, description = "ACL alias"), - ) -)] -pub(crate) async fn get_acl_alias( - _license: LicenseInfo, - _admin: AdminRole, - State(appstate): State, - session: SessionInfo, - Path(id): Path, -) -> ApiResult { - debug!("User {} retrieving ACL alias {id}", session.user.username); - let (alias, status) = match AclAlias::find_by_id(&appstate.pool, id).await? { - Some(alias) => ( - json!(ApiAclAlias::from( - alias.to_info(&appstate.pool).await.map_err(|err| { - error!("Error retrieving ACL alias {alias:?}: {err}"); - err - })? - )), - StatusCode::OK, - ), - None => (Value::Null, StatusCode::NOT_FOUND), - }; - - info!("User {} retrieved ACL alias {id}", session.user.username); - Ok(ApiResponse { - json: alias, - status, - }) -} - -/// Create ACL alias. -#[utoipa::path( - post, - path = "/api/v1/acl/alias", - tag = "ACL", - request_body = EditAclAlias, - responses( - (status = CREATED, description = "ACL alias"), - ) -)] -pub(crate) async fn create_acl_alias( - _license: LicenseInfo, - _admin: AdminRole, - State(appstate): State, - session: SessionInfo, - Json(data): Json, -) -> ApiResult { - debug!("User {} creating ACL alias {data:?}", session.user.username); - let alias = AclAlias::create_from_api(&appstate.pool, &data, AliasKind::Component) - .await - .map_err(|err| { - error!("Error creating ACL alias {data:?}: {err}"); - err - })?; - info!( - "User {} created ACL alias {}", - session.user.username, alias.id - ); - Ok(ApiResponse { - json: json!(alias), - status: StatusCode::CREATED, - }) -} - -/// Update ACL alias. -#[utoipa::path( - put, - path = "/api/v1/acl/alias/{id}", - tag = "ACL", - params( - ("id" = Id, Path, description = "ID of ACL alias",) - ), - request_body = EditAclAlias, - responses( - (status = OK, description = "ACL alias"), - ) -)] -pub(crate) async fn update_acl_alias( - _license: LicenseInfo, - _admin: AdminRole, - State(appstate): State, - session: SessionInfo, - Path(id): Path, - Json(data): Json, -) -> ApiResult { - debug!("User {} updating ACL alias {data:?}", session.user.username); - let alias = AclAlias::update_from_api(&appstate.pool, id, &data, AliasKind::Component) - .await - .map_err(|err| { - error!("Error updating ACL alias {data:?}: {err}"); - err - })?; - info!("User {} updated ACL alias", session.user.username); - Ok(ApiResponse { - json: json!(alias), - status: StatusCode::OK, - }) -} - -/// Delete ACL alias. -#[utoipa::path( - delete, - path = "/api/v1/acl/alias/{id}", - params( - ("id" = Id, Path, description = "ID of ACL alias",) - ), - responses( - (status = OK, description = "ACL alias"), - ) -)] -pub(crate) async fn delete_acl_alias( - _license: LicenseInfo, - _admin: AdminRole, - State(appstate): State, - session: SessionInfo, - Path(id): Path, -) -> ApiResult { - debug!("User {} deleting ACL alias {id}", session.user.username); - AclAlias::delete_from_api(&appstate.pool, id) - .await - .map_err(|err| { - error!("Error deleting ACL alias {id}: {err}"); - err - })?; - info!("User {} deleted ACL alias {id}", session.user.username); - Ok(ApiResponse::default()) -} - /// Apply ACL alias. #[utoipa::path( put, diff --git a/crates/defguard_core/src/enterprise/handlers/acl/alias.rs b/crates/defguard_core/src/enterprise/handlers/acl/alias.rs new file mode 100644 index 0000000000..e6ba76e96c --- /dev/null +++ b/crates/defguard_core/src/enterprise/handlers/acl/alias.rs @@ -0,0 +1,186 @@ +use axum::{ + Json, + extract::{Path, State}, + http::StatusCode, +}; +use defguard_common::db::Id; +use serde_json::{Value, json}; + +use super::{ApiAclAlias, EditAclAlias, LicenseInfo}; +use crate::{ + appstate::AppState, + auth::{AdminRole, SessionInfo}, + enterprise::db::models::acl::{AclAlias, AliasKind}, + handlers::{ApiResponse, ApiResult}, +}; + +/// List all ACL aliases. +#[utoipa::path( + get, + path = "/api/v1/acl/alias", + tag = "ACL", + responses( + (status = OK, description = "ACL alias"), + ), +)] +pub(crate) async fn list_acl_aliases( + _license: LicenseInfo, + _admin: AdminRole, + State(appstate): State, + session: SessionInfo, +) -> ApiResult { + debug!("User {} listing ACL aliases", session.user.username); + let aliases = AclAlias::all(&appstate.pool).await?; + let mut api_aliases: Vec = Vec::with_capacity(aliases.len()); + for alias in &aliases { + // TODO: may require optimisation wrt. sql queries + let info = alias.to_info(&appstate.pool).await.map_err(|err| { + error!("Error retrieving ACL alias {alias:?}: {err}"); + err + })?; + api_aliases.push(info.into()); + } + info!("User {} listed ACL aliases", session.user.username); + Ok(ApiResponse { + json: json!(api_aliases), + status: StatusCode::OK, + }) +} + +/// Get ACL alias. +#[utoipa::path( + get, + path = "/api/v1/acl/alias/{id}", + tag = "ACL", + params( + ("id" = Id, Path, description = "ID of ACL alias",) + ), + responses( + (status = OK, description = "ACL alias"), + ) +)] +pub(crate) async fn get_acl_alias( + _license: LicenseInfo, + _admin: AdminRole, + State(appstate): State, + session: SessionInfo, + Path(id): Path, +) -> ApiResult { + debug!("User {} retrieving ACL alias {id}", session.user.username); + let (alias, status) = match AclAlias::find_by_id(&appstate.pool, id).await? { + Some(alias) => ( + json!(ApiAclAlias::from( + alias.to_info(&appstate.pool).await.map_err(|err| { + error!("Error retrieving ACL alias {alias:?}: {err}"); + err + })? + )), + StatusCode::OK, + ), + None => (Value::Null, StatusCode::NOT_FOUND), + }; + + info!("User {} retrieved ACL alias {id}", session.user.username); + Ok(ApiResponse { + json: alias, + status, + }) +} + +/// Create ACL alias. +#[utoipa::path( + post, + path = "/api/v1/acl/alias", + tag = "ACL", + request_body = EditAclAlias, + responses( + (status = CREATED, description = "ACL alias"), + ) +)] +pub(crate) async fn create_acl_alias( + _license: LicenseInfo, + _admin: AdminRole, + State(appstate): State, + session: SessionInfo, + Json(data): Json, +) -> ApiResult { + debug!("User {} creating ACL alias {data:?}", session.user.username); + let alias = AclAlias::create_from_api(&appstate.pool, &data, AliasKind::Component) + .await + .map_err(|err| { + error!("Error creating ACL alias {data:?}: {err}"); + err + })?; + info!( + "User {} created ACL alias {}", + session.user.username, alias.id + ); + Ok(ApiResponse { + json: json!(alias), + status: StatusCode::CREATED, + }) +} + +/// Update ACL alias. +#[utoipa::path( + put, + path = "/api/v1/acl/alias/{id}", + tag = "ACL", + params( + ("id" = Id, Path, description = "ID of ACL alias",) + ), + request_body = EditAclAlias, + responses( + (status = OK, description = "ACL alias"), + ) +)] +pub(crate) async fn update_acl_alias( + _license: LicenseInfo, + _admin: AdminRole, + State(appstate): State, + session: SessionInfo, + Path(id): Path, + Json(data): Json, +) -> ApiResult { + debug!("User {} updating ACL alias {data:?}", session.user.username); + let alias = AclAlias::update_from_api(&appstate.pool, id, &data, AliasKind::Component) + .await + .map_err(|err| { + error!("Error updating ACL alias {data:?}: {err}"); + err + })?; + info!("User {} updated ACL alias", session.user.username); + Ok(ApiResponse { + json: json!(alias), + status: StatusCode::OK, + }) +} + +/// Delete ACL alias. +#[utoipa::path( + delete, + path = "/api/v1/acl/alias/{id}", + params( + ("id" = Id, Path, description = "ID of ACL alias",) + ), + responses( + (status = OK, description = "ACL alias"), + ) +)] +pub(crate) async fn delete_acl_alias( + _license: LicenseInfo, + _admin: AdminRole, + State(appstate): State, + session: SessionInfo, + Path(id): Path, +) -> ApiResult { + debug!("User {} deleting ACL alias {id}", session.user.username); + AclAlias::delete_from_api(&appstate.pool, id) + .await + .map_err(|err| { + error!("Error deleting ACL alias {id}: {err}"); + err + })?; + info!("User {} deleted ACL alias {id}", session.user.username); + Ok(ApiResponse::default()) +} diff --git a/crates/defguard_core/src/enterprise/handlers/acl/destination.rs b/crates/defguard_core/src/enterprise/handlers/acl/destination.rs index 2df0c168f2..ecda97c003 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl/destination.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl/destination.rs @@ -6,16 +6,11 @@ use defguard_common::db::Id; use reqwest::StatusCode; use serde_json::{Value, json}; +use super::{ApiAclAlias, EditAclAlias, LicenseInfo}; use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, - enterprise::{ - db::models::acl::{AclAlias, AliasKind}, - handlers::{ - LicenseInfo, - acl::{ApiAclAlias, EditAclAlias}, - }, - }, + enterprise::db::models::acl::{AclAlias, AliasKind}, handlers::{ApiResponse, ApiResult}, }; diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index 7c7eff5661..4c91ef2e9d 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -79,14 +79,16 @@ use crate::{ enterprise::{ handlers::{ acl::{ - apply_acl_aliases, apply_acl_rules, create_acl_alias, create_acl_rule, - delete_acl_alias, delete_acl_rule, + alias::{ + create_acl_alias, delete_acl_alias, get_acl_alias, list_acl_aliases, + update_acl_alias, + }, + apply_acl_aliases, apply_acl_rules, create_acl_rule, delete_acl_rule, destination::{ create_acl_destination, delete_acl_destination, get_acl_destination, list_acl_destinations, update_acl_destination, }, - get_acl_alias, get_acl_rule, list_acl_aliases, list_acl_rules, update_acl_alias, - update_acl_rule, + get_acl_rule, list_acl_rules, update_acl_rule, }, activity_log_stream::{ create_activity_log_stream, delete_activity_log_stream, get_activity_log_stream, diff --git a/crates/defguard_core/src/openapi.rs b/crates/defguard_core/src/openapi.rs index 3d792b4449..2016b1f6a7 100644 --- a/crates/defguard_core/src/openapi.rs +++ b/crates/defguard_core/src/openapi.rs @@ -90,11 +90,11 @@ use super::{ acl::update_acl_rule, acl::delete_acl_rule, // /acl/alias - acl::list_acl_aliases, - acl::create_acl_alias, - acl::get_acl_alias, - acl::update_acl_alias, - acl::delete_acl_alias, + acl::alias::list_acl_aliases, + acl::alias::create_acl_alias, + acl::alias::get_acl_alias, + acl::alias::update_acl_alias, + acl::alias::delete_acl_alias, acl::apply_acl_aliases, // /acl/destination acl::destination::list_acl_destinations, diff --git a/crates/defguard_core/tests/integration/api/acl.rs b/crates/defguard_core/tests/integration/api/acl.rs index 419d4fddf0..1d4cc80beb 100644 --- a/crates/defguard_core/tests/integration/api/acl.rs +++ b/crates/defguard_core/tests/integration/api/acl.rs @@ -488,6 +488,9 @@ async fn test_related_objects(_: PgPoolOptions, options: PgConnectOptions) { Vec::new(), Vec::new(), Vec::new(), + true, + true, + true, ) .save(&pool) .await @@ -499,6 +502,9 @@ async fn test_related_objects(_: PgPoolOptions, options: PgConnectOptions) { Vec::new(), Vec::new(), Vec::new(), + true, + true, + true, ) .save(&pool) .await @@ -637,6 +643,9 @@ async fn test_invalid_related_objects(_: PgPoolOptions, options: PgConnectOption Vec::new(), Vec::new(), Vec::new(), + true, + true, + true, ) .save(&state.pool) .await diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql new file mode 100644 index 0000000000..7870670dbb --- /dev/null +++ b/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE aclalias + DROP COLUMN any_destination, + DROP COLUMN any_port, + DROP COLUMN any_protocol; diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql new file mode 100644 index 0000000000..ecad7d5280 --- /dev/null +++ b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE aclalias + ADD COLUMN any_destination boolean NOT NULL DEFAULT true, + ADD COLUMN any_port boolean NOT NULL DEFAULT true, + ADD COLUMN any_protocol boolean NOT NULL DEFAULT true; From ffe0ae380942b401c3b8949cf197e3aa6ac03a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Tue, 27 Jan 2026 12:01:28 +0100 Subject: [PATCH 03/11] Edit any_ in aliases --- .../src/enterprise/db/models/acl.rs | 12 ++++++-- .../src/enterprise/handlers/acl.rs | 9 ++++++ .../tests/integration/api/acl.rs | 6 ++++ web/src/pages/CEAliasPage/CEAliasPage.tsx | 29 ++++++++++--------- web/src/shared/api/types.ts | 3 ++ 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index 04d353113e..23bffa898b 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -1338,6 +1338,9 @@ pub(crate) struct AclAliasInfo { pub ports: Vec, pub protocols: Vec, pub rules: Vec>, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } impl AclAliasInfo { @@ -1472,9 +1475,9 @@ impl AclAlias { kind, state: AliasState::Applied, protocols: alias.protocols.clone(), - any_destination: true, - any_port: true, - any_protocol: true, + any_destination: alias.any_destination, + any_port: alias.any_port, + any_protocol: alias.any_protocol, }) } @@ -1793,6 +1796,9 @@ impl AclAlias { protocols: self.protocols.clone(), destination_ranges, rules, + any_destination: self.any_destination, + any_port: self.any_port, + any_protocol: self.any_protocol, }) } diff --git a/crates/defguard_core/src/enterprise/handlers/acl.rs b/crates/defguard_core/src/enterprise/handlers/acl.rs index 9dcf3f2aa4..6ee92f6e37 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl.rs @@ -165,6 +165,9 @@ pub struct ApiAclAlias { pub ports: String, pub protocols: Vec, pub rules: Vec, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } impl From> for ApiAclAlias { @@ -179,6 +182,9 @@ impl From> for ApiAclAlias { state: info.state, protocols: info.protocols, rules: info.rules.iter().map(|v| v.id).collect(), + any_destination: info.any_destination, + any_port: info.any_port, + any_protocol: info.any_protocol, } } } @@ -190,6 +196,9 @@ pub struct EditAclAlias { pub destination: String, pub ports: String, pub protocols: Vec, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } #[derive(Debug, Deserialize, ToSchema)] diff --git a/crates/defguard_core/tests/integration/api/acl.rs b/crates/defguard_core/tests/integration/api/acl.rs index 1d4cc80beb..6f6ef2d9ce 100644 --- a/crates/defguard_core/tests/integration/api/acl.rs +++ b/crates/defguard_core/tests/integration/api/acl.rs @@ -81,6 +81,9 @@ fn make_alias() -> EditAclAlias { destination: "10.2.2.2, 10.0.0.1/24, 10.0.10.1-10.0.20.1".to_string(), protocols: vec![6, 17], ports: "1, 2, 3, 10-20, 30-40".to_string(), + any_destination: true, + any_port: true, + any_protocol: true, } } @@ -134,6 +137,9 @@ fn edit_alias_data_into_api_response( ports: data.ports, protocols: data.protocols, rules, + any_destination: data.any_destination, + any_port: data.any_port, + any_protocol: data.any_protocol, } } diff --git a/web/src/pages/CEAliasPage/CEAliasPage.tsx b/web/src/pages/CEAliasPage/CEAliasPage.tsx index dd42307fa0..5a1077b634 100644 --- a/web/src/pages/CEAliasPage/CEAliasPage.tsx +++ b/web/src/pages/CEAliasPage/CEAliasPage.tsx @@ -74,19 +74,20 @@ const formSchema = z.object({ ports: aclPortsValidator, destination: aclDestinationValidator, protocols: z.set(z.enum(AclProtocol)), + any_destination: z.boolean(), + any_port: z.boolean(), + any_protocol: z.boolean(), }); type FormFields = z.infer; const FormContent = ({ alias }: { alias?: AclAlias }) => { const [allDestinations, setAllDestinations] = useState( - isPresent(alias) ? alias.destination.length === 0 : true, - ); - const [allPorts, setAllPorts] = useState( - isPresent(alias) ? alias.ports.length === 0 : true, + isPresent(alias) && alias.any_destination, ); + const [allPorts, setAllPorts] = useState(isPresent(alias) && alias.any_port); const [allProtocols, setAllProtocols] = useState( - isPresent(alias) ? alias.protocols.length === 0 : true, + isPresent(alias) && alias.any_protocol, ); const defaultValues = useMemo((): FormFields => { if (isPresent(alias)) { @@ -96,6 +97,9 @@ const FormContent = ({ alias }: { alias?: AclAlias }) => { kind: alias.kind, ports: alias.ports, protocols: new Set(alias.protocols), + any_destination: alias.any_destination, + any_port: alias.any_port, + any_protocol: alias.any_protocol, }; } return { @@ -104,6 +108,9 @@ const FormContent = ({ alias }: { alias?: AclAlias }) => { kind: AclAliasKind.Component, ports: '', protocols: new Set(), + any_destination: true, + any_port: true, + any_protocol: true, }; }, [alias]); @@ -134,16 +141,10 @@ const FormContent = ({ alias }: { alias?: AclAlias }) => { const toSend: AddAclAliasRequest = { ...cloneDeep(value), protocols: Array.from(value.protocols), + any_destination: allDestinations, + any_port: allPorts, + any_protocol: allProtocols, }; - if (allDestinations) { - toSend.destination = ''; - } - if (allProtocols) { - toSend.protocols = []; - } - if (allPorts) { - toSend.ports = ''; - } if (isPresent(alias)) { await editAlias({ ...toSend, id: alias.id }); } else { diff --git a/web/src/shared/api/types.ts b/web/src/shared/api/types.ts index 6e21e748db..942eaea980 100644 --- a/web/src/shared/api/types.ts +++ b/web/src/shared/api/types.ts @@ -816,6 +816,9 @@ export interface AclAlias { ports: string; protocols: AclProtocolValue[]; rules: number[]; + any_destination: boolean; + any_port: boolean; + any_protocol: boolean; } export type AddAclAliasRequest = Omit; From b4a97f6447902a4d2566bf5fe709505ce66e2e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Tue, 27 Jan 2026 12:08:27 +0100 Subject: [PATCH 04/11] .sqlx --- ...598bd90270df2e0b75d4b8a94da0cd426009.json} | 22 +++++++++++++++++-- ...e4c971a4c8c8da454627dc7f2542a9796c3a.json} | 22 +++++++++++++++++-- ...407a9ad71d5f76a7741882282fef0a2e928b.json} | 9 +++++--- ...ae1bb0ac7ebe995b9aac9e3528f3bb84c264.json} | 22 +++++++++++++++++-- ...c36d353c4d93e9afe577131c61a3550dc14c.json} | 9 +++++--- 5 files changed, 72 insertions(+), 12 deletions(-) rename .sqlx/{query-03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956.json => query-07f4a663712e84d62e115cfe2585598bd90270df2e0b75d4b8a94da0cd426009.json} (72%) rename .sqlx/{query-8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d.json => query-1faddb4c68980e6b922422c90b1ae4c971a4c8c8da454627dc7f2542a9796c3a.json} (74%) rename .sqlx/{query-d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233.json => query-2829f4f5fe69dcf6ea08a0346b91407a9ad71d5f76a7741882282fef0a2e928b.json} (76%) rename .sqlx/{query-6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270.json => query-2f971e30e3e976848107845d8d69ae1bb0ac7ebe995b9aac9e3528f3bb84c264.json} (74%) rename .sqlx/{query-bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0.json => query-4095f28a023f9fb2f96cd890c795c36d353c4d93e9afe577131c61a3550dc14c.json} (73%) diff --git a/.sqlx/query-03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956.json b/.sqlx/query-07f4a663712e84d62e115cfe2585598bd90270df2e0b75d4b8a94da0cd426009.json similarity index 72% rename from .sqlx/query-03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956.json rename to .sqlx/query-07f4a663712e84d62e115cfe2585598bd90270df2e0b75d4b8a94da0cd426009.json index bf5ab88792..f351aead3d 100644 --- a/.sqlx/query-03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956.json +++ b/.sqlx/query-07f4a663712e84d62e115cfe2585598bd90270df2e0b75d4b8a94da0cd426009.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT a.id, parent_id, name, kind \"kind: AliasKind\",state \"state: AliasState\", destination, ports, protocols FROM aclrulealias r JOIN aclalias a ON a.id = r.alias_id WHERE r.rule_id = $1", + "query": "SELECT a.id, parent_id, name, kind \"kind: AliasKind\",state \"state: AliasState\", destination, ports, protocols, any_destination, any_port, any_protocol FROM aclrulealias r JOIN aclalias a ON a.id = r.alias_id WHERE r.rule_id = $1", "describe": { "columns": [ { @@ -62,6 +62,21 @@ "ordinal": 7, "name": "protocols", "type_info": "Int4Array" + }, + { + "ordinal": 8, + "name": "any_destination", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "any_protocol", + "type_info": "Bool" } ], "parameters": { @@ -77,8 +92,11 @@ false, false, false, + false, + false, + false, false ] }, - "hash": "03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956" + "hash": "07f4a663712e84d62e115cfe2585598bd90270df2e0b75d4b8a94da0cd426009" } diff --git a/.sqlx/query-8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d.json b/.sqlx/query-1faddb4c68980e6b922422c90b1ae4c971a4c8c8da454627dc7f2542a9796c3a.json similarity index 74% rename from .sqlx/query-8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d.json rename to .sqlx/query-1faddb4c68980e6b922422c90b1ae4c971a4c8c8da454627dc7f2542a9796c3a.json index 7fe770b1c1..3c72f27599 100644 --- a/.sqlx/query-8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d.json +++ b/.sqlx/query-1faddb4c68980e6b922422c90b1ae4c971a4c8c8da454627dc7f2542a9796c3a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"name\",\"kind\" \"kind: _\",\"state\" \"state: _\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\" FROM \"aclalias\"", + "query": "SELECT id, \"parent_id\",\"name\",\"kind\" \"kind: _\",\"state\" \"state: _\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"any_destination\",\"any_port\",\"any_protocol\" FROM \"aclalias\"", "describe": { "columns": [ { @@ -62,6 +62,21 @@ "ordinal": 7, "name": "protocols: _", "type_info": "Int4Array" + }, + { + "ordinal": 8, + "name": "any_destination", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "any_protocol", + "type_info": "Bool" } ], "parameters": { @@ -75,8 +90,11 @@ false, false, false, + false, + false, + false, false ] }, - "hash": "8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d" + "hash": "1faddb4c68980e6b922422c90b1ae4c971a4c8c8da454627dc7f2542a9796c3a" } diff --git a/.sqlx/query-d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233.json b/.sqlx/query-2829f4f5fe69dcf6ea08a0346b91407a9ad71d5f76a7741882282fef0a2e928b.json similarity index 76% rename from .sqlx/query-d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233.json rename to .sqlx/query-2829f4f5fe69dcf6ea08a0346b91407a9ad71d5f76a7741882282fef0a2e928b.json index 40b467fcdd..49ac96f605 100644 --- a/.sqlx/query-d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233.json +++ b/.sqlx/query-2829f4f5fe69dcf6ea08a0346b91407a9ad71d5f76a7741882282fef0a2e928b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"aclalias\" SET \"parent_id\" = $2,\"name\" = $3,\"kind\" = $4,\"state\" = $5,\"destination\" = $6,\"ports\" = $7,\"protocols\" = $8 WHERE id = $1", + "query": "UPDATE \"aclalias\" SET \"parent_id\" = $2,\"name\" = $3,\"kind\" = $4,\"state\" = $5,\"destination\" = $6,\"ports\" = $7,\"protocols\" = $8,\"any_destination\" = $9,\"any_port\" = $10,\"any_protocol\" = $11 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -32,10 +32,13 @@ }, "InetArray", "Int4RangeArray", - "Int4Array" + "Int4Array", + "Bool", + "Bool", + "Bool" ] }, "nullable": [] }, - "hash": "d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233" + "hash": "2829f4f5fe69dcf6ea08a0346b91407a9ad71d5f76a7741882282fef0a2e928b" } diff --git a/.sqlx/query-6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270.json b/.sqlx/query-2f971e30e3e976848107845d8d69ae1bb0ac7ebe995b9aac9e3528f3bb84c264.json similarity index 74% rename from .sqlx/query-6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270.json rename to .sqlx/query-2f971e30e3e976848107845d8d69ae1bb0ac7ebe995b9aac9e3528f3bb84c264.json index d36c3852c3..18448beb88 100644 --- a/.sqlx/query-6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270.json +++ b/.sqlx/query-2f971e30e3e976848107845d8d69ae1bb0ac7ebe995b9aac9e3528f3bb84c264.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"name\",\"kind\" \"kind: _\",\"state\" \"state: _\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\" FROM \"aclalias\" WHERE id = $1", + "query": "SELECT id, \"parent_id\",\"name\",\"kind\" \"kind: _\",\"state\" \"state: _\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"any_destination\",\"any_port\",\"any_protocol\" FROM \"aclalias\" WHERE id = $1", "describe": { "columns": [ { @@ -62,6 +62,21 @@ "ordinal": 7, "name": "protocols: _", "type_info": "Int4Array" + }, + { + "ordinal": 8, + "name": "any_destination", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "any_protocol", + "type_info": "Bool" } ], "parameters": { @@ -77,8 +92,11 @@ false, false, false, + false, + false, + false, false ] }, - "hash": "6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270" + "hash": "2f971e30e3e976848107845d8d69ae1bb0ac7ebe995b9aac9e3528f3bb84c264" } diff --git a/.sqlx/query-bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0.json b/.sqlx/query-4095f28a023f9fb2f96cd890c795c36d353c4d93e9afe577131c61a3550dc14c.json similarity index 73% rename from .sqlx/query-bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0.json rename to .sqlx/query-4095f28a023f9fb2f96cd890c795c36d353c4d93e9afe577131c61a3550dc14c.json index 1d73ab2cec..2e8999a6a5 100644 --- a/.sqlx/query-bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0.json +++ b/.sqlx/query-4095f28a023f9fb2f96cd890c795c36d353c4d93e9afe577131c61a3550dc14c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"aclalias\" (\"parent_id\",\"name\",\"kind\",\"state\",\"destination\",\"ports\",\"protocols\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING id", + "query": "INSERT INTO \"aclalias\" (\"parent_id\",\"name\",\"kind\",\"state\",\"destination\",\"ports\",\"protocols\",\"any_destination\",\"any_port\",\"any_protocol\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING id", "describe": { "columns": [ { @@ -37,12 +37,15 @@ }, "InetArray", "Int4RangeArray", - "Int4Array" + "Int4Array", + "Bool", + "Bool", + "Bool" ] }, "nullable": [ false ] }, - "hash": "bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0" + "hash": "4095f28a023f9fb2f96cd890c795c36d353c4d93e9afe577131c61a3550dc14c" } From d5dc421c7a007ebd8384692aacd0402ebc10bbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Tue, 27 Jan 2026 12:31:37 +0100 Subject: [PATCH 05/11] Trim destinations --- .../DestinationsPage/DestinationsPage.tsx | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/web/src/pages/DestinationsPage/DestinationsPage.tsx b/web/src/pages/DestinationsPage/DestinationsPage.tsx index 19b0e8ff03..ec6fbdcc68 100644 --- a/web/src/pages/DestinationsPage/DestinationsPage.tsx +++ b/web/src/pages/DestinationsPage/DestinationsPage.tsx @@ -1,37 +1,9 @@ -import { useMemo, useState } from 'react'; -import { AclDeploymentState, type AclDeploymentStateValue } from '../../shared/api/types'; import { Page } from '../../shared/components/Page/Page'; -import { Tabs } from '../../shared/defguard-ui/components/Tabs/Tabs'; -import type { TabsItem } from '../../shared/defguard-ui/components/Tabs/types'; export const DestinationsPage = () => { - const [activeTab, setActiveTab] = useState( - AclDeploymentState.Applied, - ); - - const tabs = useMemo( - (): TabsItem[] => [ - { - active: activeTab === AclDeploymentState.Applied, - onClick: () => { - setActiveTab(AclDeploymentState.Applied); - }, - title: 'Deployed', - }, - { - active: activeTab === AclDeploymentState.Modified, - onClick: () => { - setActiveTab(AclDeploymentState.Modified); - }, - title: 'Pending', - }, - ], - [activeTab], - ); return ( - ); }; From ac809fd225fe44e1b012da70a010f56ae894b865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Tue, 27 Jan 2026 12:47:33 +0100 Subject: [PATCH 06/11] lint --- web/src/pages/DestinationsPage/DestinationsPage.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/web/src/pages/DestinationsPage/DestinationsPage.tsx b/web/src/pages/DestinationsPage/DestinationsPage.tsx index ec6fbdcc68..55b2e7ca79 100644 --- a/web/src/pages/DestinationsPage/DestinationsPage.tsx +++ b/web/src/pages/DestinationsPage/DestinationsPage.tsx @@ -1,9 +1,5 @@ import { Page } from '../../shared/components/Page/Page'; export const DestinationsPage = () => { - - return ( - - - ); + return ; }; From 73c786cbc0e3223ac55aefb941f8027890375991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Tue, 27 Jan 2026 13:52:18 +0100 Subject: [PATCH 07/11] Default is false now --- migrations/20260127094513_[2.0.0]_aclalias_any.up.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql index ecad7d5280..5233442ac7 100644 --- a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql +++ b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql @@ -1,4 +1,4 @@ ALTER TABLE aclalias - ADD COLUMN any_destination boolean NOT NULL DEFAULT true, - ADD COLUMN any_port boolean NOT NULL DEFAULT true, - ADD COLUMN any_protocol boolean NOT NULL DEFAULT true; + ADD COLUMN any_destination boolean NOT NULL DEFAULT false, + ADD COLUMN any_port boolean NOT NULL DEFAULT false, + ADD COLUMN any_protocol boolean NOT NULL DEFAULT false; From e8fe8f2115d94f7ded45074846cff1f5e34441f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 28 Jan 2026 10:11:00 +0100 Subject: [PATCH 08/11] Extend AclRule --- ...5f09734b2912b9a93821169c036baeab2c67.json} | 24 ++++++++++++++++--- ...1a8cc2642c32bf6ff816f0906cc1befbad11.json} | 24 ++++++++++++++++--- ...de9991bd88e02a62240a876067cd32267c60.json} | 9 ++++--- ...7d06366cf41d7c61403147666c01e38c8210.json} | 9 ++++--- ...2347293b5107a5a0f179aa07d97b65f2c128.json} | 24 ++++++++++++++++--- ...4e504dded78c5073c19b59afa4b0eb89849b.json} | 24 ++++++++++++++++--- Cargo.lock | 20 ++++++++-------- .../src/enterprise/db/models/acl.rs | 17 +++++++++++-- .../src/enterprise/db/models/acl/tests.rs | 12 ++++++++++ .../src/enterprise/firewall/mod.rs | 20 ++++++++-------- .../src/enterprise/firewall/tests.rs | 18 ++++++++++++++ .../src/enterprise/handlers/acl.rs | 6 +++++ crates/defguard_core/src/utility_thread.rs | 3 ++- .../tests/integration/api/acl.rs | 3 +++ ...260127094513_[2.0.0]_aclalias_any.down.sql | 4 ++++ ...20260127094513_[2.0.0]_aclalias_any.up.sql | 12 ++++++++++ 16 files changed, 188 insertions(+), 41 deletions(-) rename .sqlx/{query-b1780a123ae899aa62f0402157cc13cfd96a31e54fa4a65b4ab48072c3ce75ac.json => query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json} (82%) rename .sqlx/{query-5e765af5f354caaeb952fe3a79a81eb462ad45ebb09e70e4dbfe316ee338d89a.json => query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json} (79%) rename .sqlx/{query-53519f3e0d48fc874cea22517c0bd17d38d45c862924b8bf423edc7734c8cac1.json => query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json} (73%) rename .sqlx/{query-0739afc36fdd469cd2ac28a848eac3a057963dacddbc2d31874c75c554848b5e.json => query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json} (75%) rename .sqlx/{query-b08b17dddc565d9d0a035508315ff02ddb0960de123de36def1cb1e12de8ae98.json => query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json} (80%) rename .sqlx/{query-2517a3b667c7220342315c25baa3ed994e621a2c9b105705bfb3d3381c367218.json => query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json} (80%) diff --git a/.sqlx/query-b1780a123ae899aa62f0402157cc13cfd96a31e54fa4a65b4ab48072c3ce75ac.json b/.sqlx/query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json similarity index 82% rename from .sqlx/query-b1780a123ae899aa62f0402157cc13cfd96a31e54fa4a65b4ab48072c3ce75ac.json rename to .sqlx/query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json index d9267025d8..b09edd93b3 100644 --- a/.sqlx/query-b1780a123ae899aa62f0402157cc13cfd96a31e54fa4a65b4ab48072c3ce75ac.json +++ b/.sqlx/query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE aclrule SET state = 'expired'::aclrule_state WHERE state = 'applied'::aclrule_state AND expires < NOW() RETURNING id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires", + "query": "UPDATE aclrule SET state = 'expired'::aclrule_state WHERE state = 'applied'::aclrule_state AND expires < NOW() RETURNING id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires, any_destination, any_port, any_protocol", "describe": { "columns": [ { @@ -85,6 +85,21 @@ "ordinal": 13, "name": "expires", "type_info": "Timestamp" + }, + { + "ordinal": 14, + "name": "any_destination", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "any_protocol", + "type_info": "Bool" } ], "parameters": { @@ -104,8 +119,11 @@ false, false, false, - true + true, + false, + false, + false ] }, - "hash": "b1780a123ae899aa62f0402157cc13cfd96a31e54fa4a65b4ab48072c3ce75ac" + "hash": "143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67" } diff --git a/.sqlx/query-5e765af5f354caaeb952fe3a79a81eb462ad45ebb09e70e4dbfe316ee338d89a.json b/.sqlx/query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json similarity index 79% rename from .sqlx/query-5e765af5f354caaeb952fe3a79a81eb462ad45ebb09e70e4dbfe316ee338d89a.json rename to .sqlx/query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json index df797064fd..7467b82783 100644 --- a/.sqlx/query-5e765af5f354caaeb952fe3a79a81eb462ad45ebb09e70e4dbfe316ee338d89a.json +++ b/.sqlx/query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT ar.id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires FROM aclrulealias ara JOIN aclrule ar ON ar.id = ara.rule_id WHERE ara.alias_id = $1", + "query": "SELECT ar.id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires, any_destination, any_port, any_protocol FROM aclrulealias ara JOIN aclrule ar ON ar.id = ara.rule_id WHERE ara.alias_id = $1", "describe": { "columns": [ { @@ -85,6 +85,21 @@ "ordinal": 13, "name": "expires", "type_info": "Timestamp" + }, + { + "ordinal": 14, + "name": "any_destination", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "any_protocol", + "type_info": "Bool" } ], "parameters": { @@ -106,8 +121,11 @@ false, false, false, - true + true, + false, + false, + false ] }, - "hash": "5e765af5f354caaeb952fe3a79a81eb462ad45ebb09e70e4dbfe316ee338d89a" + "hash": "19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11" } diff --git a/.sqlx/query-53519f3e0d48fc874cea22517c0bd17d38d45c862924b8bf423edc7734c8cac1.json b/.sqlx/query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json similarity index 73% rename from .sqlx/query-53519f3e0d48fc874cea22517c0bd17d38d45c862924b8bf423edc7734c8cac1.json rename to .sqlx/query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json index f9c5a0897f..12def56afc 100644 --- a/.sqlx/query-53519f3e0d48fc874cea22517c0bd17d38d45c862924b8bf423edc7734c8cac1.json +++ b/.sqlx/query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"aclrule\" (\"parent_id\",\"state\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\",\"ports\",\"protocols\",\"enabled\",\"expires\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) RETURNING id", + "query": "INSERT INTO \"aclrule\" (\"parent_id\",\"state\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\",\"ports\",\"protocols\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16) RETURNING id", "describe": { "columns": [ { @@ -36,12 +36,15 @@ "Int4RangeArray", "Int4Array", "Bool", - "Timestamp" + "Timestamp", + "Bool", + "Bool", + "Bool" ] }, "nullable": [ false ] }, - "hash": "53519f3e0d48fc874cea22517c0bd17d38d45c862924b8bf423edc7734c8cac1" + "hash": "64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60" } diff --git a/.sqlx/query-0739afc36fdd469cd2ac28a848eac3a057963dacddbc2d31874c75c554848b5e.json b/.sqlx/query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json similarity index 75% rename from .sqlx/query-0739afc36fdd469cd2ac28a848eac3a057963dacddbc2d31874c75c554848b5e.json rename to .sqlx/query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json index 935c052eed..6a87281b45 100644 --- a/.sqlx/query-0739afc36fdd469cd2ac28a848eac3a057963dacddbc2d31874c75c554848b5e.json +++ b/.sqlx/query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"aclrule\" SET \"parent_id\" = $2,\"state\" = $3,\"name\" = $4,\"allow_all_users\" = $5,\"deny_all_users\" = $6,\"allow_all_network_devices\" = $7,\"deny_all_network_devices\" = $8,\"all_networks\" = $9,\"destination\" = $10,\"ports\" = $11,\"protocols\" = $12,\"enabled\" = $13,\"expires\" = $14 WHERE id = $1", + "query": "UPDATE \"aclrule\" SET \"parent_id\" = $2,\"state\" = $3,\"name\" = $4,\"allow_all_users\" = $5,\"deny_all_users\" = $6,\"allow_all_network_devices\" = $7,\"deny_all_network_devices\" = $8,\"all_networks\" = $9,\"destination\" = $10,\"ports\" = $11,\"protocols\" = $12,\"enabled\" = $13,\"expires\" = $14,\"any_destination\" = $15,\"any_port\" = $16,\"any_protocol\" = $17 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -31,10 +31,13 @@ "Int4RangeArray", "Int4Array", "Bool", - "Timestamp" + "Timestamp", + "Bool", + "Bool", + "Bool" ] }, "nullable": [] }, - "hash": "0739afc36fdd469cd2ac28a848eac3a057963dacddbc2d31874c75c554848b5e" + "hash": "c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210" } diff --git a/.sqlx/query-b08b17dddc565d9d0a035508315ff02ddb0960de123de36def1cb1e12de8ae98.json b/.sqlx/query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json similarity index 80% rename from .sqlx/query-b08b17dddc565d9d0a035508315ff02ddb0960de123de36def1cb1e12de8ae98.json rename to .sqlx/query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json index 6f27a2d469..fcd6e7d3f2 100644 --- a/.sqlx/query-b08b17dddc565d9d0a035508315ff02ddb0960de123de36def1cb1e12de8ae98.json +++ b/.sqlx/query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\" FROM \"aclrule\"", + "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\" FROM \"aclrule\"", "describe": { "columns": [ { @@ -85,6 +85,21 @@ "ordinal": 13, "name": "expires", "type_info": "Timestamp" + }, + { + "ordinal": 14, + "name": "any_destination", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "any_protocol", + "type_info": "Bool" } ], "parameters": { @@ -104,8 +119,11 @@ false, false, false, - true + true, + false, + false, + false ] }, - "hash": "b08b17dddc565d9d0a035508315ff02ddb0960de123de36def1cb1e12de8ae98" + "hash": "cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128" } diff --git a/.sqlx/query-2517a3b667c7220342315c25baa3ed994e621a2c9b105705bfb3d3381c367218.json b/.sqlx/query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json similarity index 80% rename from .sqlx/query-2517a3b667c7220342315c25baa3ed994e621a2c9b105705bfb3d3381c367218.json rename to .sqlx/query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json index 7de2f4df76..ca41a5c9d1 100644 --- a/.sqlx/query-2517a3b667c7220342315c25baa3ed994e621a2c9b105705bfb3d3381c367218.json +++ b/.sqlx/query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\" FROM \"aclrule\" WHERE id = $1", + "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\" FROM \"aclrule\" WHERE id = $1", "describe": { "columns": [ { @@ -85,6 +85,21 @@ "ordinal": 13, "name": "expires", "type_info": "Timestamp" + }, + { + "ordinal": 14, + "name": "any_destination", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "any_protocol", + "type_info": "Bool" } ], "parameters": { @@ -106,8 +121,11 @@ false, false, false, - true + true, + false, + false, + false ] }, - "hash": "2517a3b667c7220342315c25baa3ed994e621a2c9b105705bfb3d3381c367218" + "hash": "d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b" } diff --git a/Cargo.lock b/Cargo.lock index a8c9735c0c..db24cc7224 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -691,9 +691,9 @@ checksum = "bba18ee93d577a8428902687bcc2b6b45a56b1981a1f6d779731c86cc4c5db18" [[package]] name = "clap" -version = "4.5.54" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785" dependencies = [ "clap_builder", "clap_derive", @@ -701,9 +701,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61" dependencies = [ "anstream", "anstyle", @@ -713,9 +713,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", @@ -6956,18 +6956,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" dependencies = [ "proc-macro2", "quote", diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index 23bffa898b..2a12e32749 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -182,6 +182,9 @@ pub struct AclRuleInfo { pub aliases: Vec>, pub ports: Vec, pub protocols: Vec, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } impl AclRuleInfo { @@ -252,6 +255,9 @@ pub struct AclRule { pub protocols: Vec, pub enabled: bool, pub expires: Option, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } impl AclRule { @@ -837,6 +843,9 @@ impl TryFrom for AclRule { protocols: rule.protocols, enabled: rule.enabled, expires: rule.expires, + any_destination: rule.any_destination, + any_port: rule.any_port, + any_protocol: rule.any_protocol, }) } } @@ -1153,6 +1162,9 @@ impl AclRule { denied_groups, allowed_devices, denied_devices, + any_destination: self.any_destination, + any_port: self.any_port, + any_protocol: self.any_protocol, }) } } @@ -1768,8 +1780,9 @@ impl AclAlias { query_as!( AclRule, "SELECT ar.id, parent_id, state AS \"state: RuleState\", name, allow_all_users, \ - deny_all_users, allow_all_network_devices, deny_all_network_devices, \ - all_networks, destination, ports, protocols, enabled, expires \ + deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, \ + destination, ports, protocols, enabled, expires, any_destination, any_port, \ + any_protocol \ FROM aclrulealias ara \ JOIN aclrule ar ON ar.id = ara.rule_id \ WHERE ara.alias_id = $1", diff --git a/crates/defguard_core/src/enterprise/db/models/acl/tests.rs b/crates/defguard_core/src/enterprise/db/models/acl/tests.rs index 08dbc295f6..758d419f6e 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl/tests.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl/tests.rs @@ -68,6 +68,9 @@ async fn test_allow_conflicting_sources(_: PgPoolOptions, options: PgConnectOpti ports: Vec::new(), protocols: Vec::new(), expires: None, + any_destination: true, + any_port: true, + any_protocol: true, } .save(&pool) .await @@ -170,6 +173,9 @@ async fn test_rule_relations(_: PgPoolOptions, options: PgConnectOptions) { ports: Vec::new(), protocols: Vec::new(), expires: None, + any_destination: true, + any_port: true, + any_protocol: true, } .save(&pool) .await @@ -554,6 +560,9 @@ async fn test_all_allowed_users(_: PgPoolOptions, options: PgConnectOptions) { enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, } .save(&pool) .await @@ -669,6 +678,9 @@ async fn test_all_denied_users(_: PgPoolOptions, options: PgConnectOptions) { enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, } .save(&pool) .await diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index bff44267e1..6d24abef58 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -833,7 +833,8 @@ async fn generate_user_snat_bindings_for_location( if source_addrs.is_empty() { debug!( - "No compatible device IPs found for user {user_id} in location {location_id} with public IP {}, skipping SNAT binding", + "No compatible device IPs found for user {user_id} in location {location_id} with \ + public IP {}, skipping SNAT binding", user_binding.public_ip ); continue; @@ -872,14 +873,13 @@ pub(crate) async fn get_location_active_acl_rules( debug!("Fetching active ACL rules for location {location}"); let rules: Vec> = query_as( "SELECT DISTINCT ON (a.id) a.id, name, allow_all_users, deny_all_users, all_networks, \ - allow_all_network_devices, deny_all_network_devices, destination, ports, protocols, \ - expires, enabled, parent_id, state \ - FROM aclrule a \ - LEFT JOIN aclrulenetwork an \ - ON a.id = an.rule_id \ - WHERE (an.network_id = $1 OR a.all_networks = true) AND enabled = true \ - AND state = 'applied'::aclrule_state \ - AND (expires IS NULL OR expires > NOW())", + allow_all_network_devices, deny_all_network_devices, destination, ports, protocols, \ + expires, enabled, parent_id, state, any_destination, any_port, any_protocol \ + FROM aclrule a \ + LEFT JOIN aclrulenetwork an ON a.id = an.rule_id \ + WHERE (an.network_id = $1 OR a.all_networks = true) AND enabled = true \ + AND state = 'applied'::aclrule_state \ + AND (expires IS NULL OR expires > NOW())", ) .bind(location.id) .fetch_all(&mut *conn) @@ -908,7 +908,7 @@ pub async fn try_get_location_firewall_config( if !is_business_license_active() { debug!( "Enterprise features are disabled, skipping generating firewall config for \ - location {location}" + location {location}" ); return Ok(None); } diff --git a/crates/defguard_core/src/enterprise/firewall/tests.rs b/crates/defguard_core/src/enterprise/firewall/tests.rs index a39c26c4a4..f54c235c5c 100644 --- a/crates/defguard_core/src/enterprise/firewall/tests.rs +++ b/crates/defguard_core/src/enterprise/firewall/tests.rs @@ -1101,6 +1101,9 @@ async fn test_generate_firewall_rules_ipv4(_: PgPoolOptions, options: PgConnectO enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, }; let locations = vec![location.id]; let allowed_users = vec![user_1.id, user_2.id]; // First two users can access web @@ -1143,6 +1146,9 @@ async fn test_generate_firewall_rules_ipv4(_: PgPoolOptions, options: PgConnectO enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, }; let locations_2 = vec![location.id]; let allowed_users_2 = Vec::new(); @@ -1523,6 +1529,9 @@ async fn test_generate_firewall_rules_ipv6(_: PgPoolOptions, options: PgConnectO enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, }; let locations = vec![location.id]; let allowed_users = vec![user_1.id, user_2.id]; // First two users can access web @@ -1565,6 +1574,9 @@ async fn test_generate_firewall_rules_ipv6(_: PgPoolOptions, options: PgConnectO enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, }; let locations_2 = vec![location.id]; let allowed_users_2 = Vec::new(); @@ -1988,6 +2000,9 @@ async fn test_generate_firewall_rules_ipv4_and_ipv6(_: PgPoolOptions, options: P enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, }; let locations = vec![location.id]; let allowed_users = vec![user_1.id, user_2.id]; // First two users can access web @@ -2030,6 +2045,9 @@ async fn test_generate_firewall_rules_ipv4_and_ipv6(_: PgPoolOptions, options: P enabled: true, parent_id: None, state: RuleState::Applied, + any_destination: true, + any_port: true, + any_protocol: true, }; let locations_2 = vec![location.id]; let allowed_users_2 = Vec::new(); diff --git a/crates/defguard_core/src/enterprise/handlers/acl.rs b/crates/defguard_core/src/enterprise/handlers/acl.rs index 6ee92f6e37..ddd451cc52 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl.rs @@ -105,6 +105,9 @@ pub struct EditAclRule { pub aliases: Vec, pub ports: String, pub protocols: Vec, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } impl EditAclRule { @@ -147,6 +150,9 @@ impl From> for EditAclRule { aliases: info.aliases.iter().map(|v| v.id).collect(), protocols: info.protocols, enabled: info.enabled, + any_destination: info.any_destination, + any_port: info.any_port, + any_protocol: info.any_protocol, } } } diff --git a/crates/defguard_core/src/utility_thread.rs b/crates/defguard_core/src/utility_thread.rs index 5697c5cfb8..8d74796eb3 100644 --- a/crates/defguard_core/src/utility_thread.rs +++ b/crates/defguard_core/src/utility_thread.rs @@ -241,7 +241,8 @@ async fn expired_acl_rules_check( WHERE state = 'applied'::aclrule_state AND expires < NOW() \ RETURNING id, parent_id, state AS \"state: RuleState\", name, allow_all_users, \ deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, \ - destination, ports, protocols, enabled, expires" + destination, ports, protocols, enabled, expires, any_destination, any_port, \ + any_protocol" ) .fetch_all(pool) .await?; diff --git a/crates/defguard_core/tests/integration/api/acl.rs b/crates/defguard_core/tests/integration/api/acl.rs index 6f6ef2d9ce..12cddb389d 100644 --- a/crates/defguard_core/tests/integration/api/acl.rs +++ b/crates/defguard_core/tests/integration/api/acl.rs @@ -65,6 +65,9 @@ fn make_rule() -> EditAclRule { enabled: true, protocols: vec![6, 17], ports: "1, 2, 3, 10-20, 30-40".to_string(), + any_destination: true, + any_port: true, + any_protocol: true, } } diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql index 7870670dbb..75cd13fb9a 100644 --- a/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql +++ b/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql @@ -1,3 +1,7 @@ +ALTER TABLE aclrule + DROP COLUMN any_destination, + DROP COLUMN any_port, + DROP COLUMN any_protocol; ALTER TABLE aclalias DROP COLUMN any_destination, DROP COLUMN any_port, diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql index 5233442ac7..a45a8bb9e2 100644 --- a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql +++ b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql @@ -2,3 +2,15 @@ ALTER TABLE aclalias ADD COLUMN any_destination boolean NOT NULL DEFAULT false, ADD COLUMN any_port boolean NOT NULL DEFAULT false, ADD COLUMN any_protocol boolean NOT NULL DEFAULT false; +UPDATE aclalias SET + any_destination = array_length(destination, 1) IS NULL, + any_port = array_length(ports, 1) IS NULL, + any_protocol = array_length(protocols, 1) IS NULL; +ALTER TABLE aclrule + ADD COLUMN any_destination boolean NOT NULL DEFAULT false, + ADD COLUMN any_port boolean NOT NULL DEFAULT false, + ADD COLUMN any_protocol boolean NOT NULL DEFAULT false; +UPDATE aclrule SET + any_destination = array_length(destination, 1) IS NULL, + any_port = array_length(ports, 1) IS NULL, + any_protocol = array_length(protocols, 1) IS NULL; From 9167cc9a07e5927bded1f8311d28eaebccdff7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 28 Jan 2026 11:09:26 +0100 Subject: [PATCH 09/11] Add manual settings --- crates/defguard_core/src/enterprise/db/models/acl.rs | 2 ++ migrations/20260127094513_[2.0.0]_aclalias_any.down.sql | 3 ++- migrations/20260127094513_[2.0.0]_aclalias_any.up.sql | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index 2a12e32749..f9e252b3b4 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -185,6 +185,7 @@ pub struct AclRuleInfo { pub any_destination: bool, pub any_port: bool, pub any_protocol: bool, + pub manual_settings: bool, } impl AclRuleInfo { @@ -1165,6 +1166,7 @@ impl AclRule { any_destination: self.any_destination, any_port: self.any_port, any_protocol: self.any_protocol, + manual_settings: false, }) } } diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql index 75cd13fb9a..cbcfad0bf5 100644 --- a/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql +++ b/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql @@ -1,7 +1,8 @@ ALTER TABLE aclrule DROP COLUMN any_destination, DROP COLUMN any_port, - DROP COLUMN any_protocol; + DROP COLUMN any_protocol, + DROP COLUMN manual_settings; ALTER TABLE aclalias DROP COLUMN any_destination, DROP COLUMN any_port, diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql index a45a8bb9e2..52083d52ef 100644 --- a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql +++ b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql @@ -9,7 +9,8 @@ UPDATE aclalias SET ALTER TABLE aclrule ADD COLUMN any_destination boolean NOT NULL DEFAULT false, ADD COLUMN any_port boolean NOT NULL DEFAULT false, - ADD COLUMN any_protocol boolean NOT NULL DEFAULT false; + ADD COLUMN any_protocol boolean NOT NULL DEFAULT false, + ADD COLUMN manual_settings boolean NOT NULL DEFAULT false; UPDATE aclrule SET any_destination = array_length(destination, 1) IS NULL, any_port = array_length(ports, 1) IS NULL, From bf39858d19e77ef9466ebaa4b147948e28fae465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 28 Jan 2026 11:35:03 +0100 Subject: [PATCH 10/11] Add manual settings to AclRule --- ...44d67e34b7adc7ea6fc6cb3a04e5cd815c382671b3e32.json} | 10 ++++++++-- ...39b1932ab4fd9100d098098f181d8ebef6f77b8cd7097.json} | 5 +++-- ...96005d8c82eea1705b9849ec2afb19b998a91644bac25.json} | 10 ++++++++-- ...7b0eaf66a58a39e79ace0cc7798df471d26a786352b89.json} | 10 ++++++++-- ...1ddbde69612da6e2a687c5c5588e8ad4c1381b31946d8.json} | 10 ++++++++-- ...092d440175269185fe6b6d021bed8623fe25b891e01d5.json} | 5 +++-- crates/defguard_core/src/enterprise/db/models/acl.rs | 4 +++- .../src/enterprise/db/models/acl/tests.rs | 4 ++++ crates/defguard_core/src/enterprise/firewall/mod.rs | 3 ++- crates/defguard_core/src/enterprise/firewall/tests.rs | 6 ++++++ crates/defguard_core/src/utility_thread.rs | 6 +++--- 11 files changed, 56 insertions(+), 17 deletions(-) rename .sqlx/{query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json => query-617d61862f5fa1f3fde44d67e34b7adc7ea6fc6cb3a04e5cd815c382671b3e32.json} (90%) rename .sqlx/{query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json => query-6915bd0015a04c1caf439b1932ab4fd9100d098098f181d8ebef6f77b8cd7097.json} (89%) rename .sqlx/{query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json => query-e886ab4f4c0ac47d41e96005d8c82eea1705b9849ec2afb19b998a91644bac25.json} (91%) rename .sqlx/{query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json => query-e907d432de9692381c67b0eaf66a58a39e79ace0cc7798df471d26a786352b89.json} (89%) rename .sqlx/{query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json => query-e958dccd2e8b944bd051ddbde69612da6e2a687c5c5588e8ad4c1381b31946d8.json} (90%) rename .sqlx/{query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json => query-f7c6882463818d70c1f092d440175269185fe6b6d021bed8623fe25b891e01d5.json} (81%) diff --git a/.sqlx/query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json b/.sqlx/query-617d61862f5fa1f3fde44d67e34b7adc7ea6fc6cb3a04e5cd815c382671b3e32.json similarity index 90% rename from .sqlx/query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json rename to .sqlx/query-617d61862f5fa1f3fde44d67e34b7adc7ea6fc6cb3a04e5cd815c382671b3e32.json index b09edd93b3..b974c4dfe5 100644 --- a/.sqlx/query-143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67.json +++ b/.sqlx/query-617d61862f5fa1f3fde44d67e34b7adc7ea6fc6cb3a04e5cd815c382671b3e32.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE aclrule SET state = 'expired'::aclrule_state WHERE state = 'applied'::aclrule_state AND expires < NOW() RETURNING id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires, any_destination, any_port, any_protocol", + "query": "UPDATE aclrule SET state = 'expired'::aclrule_state WHERE state = 'applied'::aclrule_state AND expires < NOW() RETURNING id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires, any_destination, any_port, any_protocol, manual_settings", "describe": { "columns": [ { @@ -100,6 +100,11 @@ "ordinal": 16, "name": "any_protocol", "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "manual_settings", + "type_info": "Bool" } ], "parameters": { @@ -122,8 +127,9 @@ true, false, false, + false, false ] }, - "hash": "143237e0af5b5f5999e92e975c345f09734b2912b9a93821169c036baeab2c67" + "hash": "617d61862f5fa1f3fde44d67e34b7adc7ea6fc6cb3a04e5cd815c382671b3e32" } diff --git a/.sqlx/query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json b/.sqlx/query-6915bd0015a04c1caf439b1932ab4fd9100d098098f181d8ebef6f77b8cd7097.json similarity index 89% rename from .sqlx/query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json rename to .sqlx/query-6915bd0015a04c1caf439b1932ab4fd9100d098098f181d8ebef6f77b8cd7097.json index 6a87281b45..a4a593a9a9 100644 --- a/.sqlx/query-c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210.json +++ b/.sqlx/query-6915bd0015a04c1caf439b1932ab4fd9100d098098f181d8ebef6f77b8cd7097.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"aclrule\" SET \"parent_id\" = $2,\"state\" = $3,\"name\" = $4,\"allow_all_users\" = $5,\"deny_all_users\" = $6,\"allow_all_network_devices\" = $7,\"deny_all_network_devices\" = $8,\"all_networks\" = $9,\"destination\" = $10,\"ports\" = $11,\"protocols\" = $12,\"enabled\" = $13,\"expires\" = $14,\"any_destination\" = $15,\"any_port\" = $16,\"any_protocol\" = $17 WHERE id = $1", + "query": "UPDATE \"aclrule\" SET \"parent_id\" = $2,\"state\" = $3,\"name\" = $4,\"allow_all_users\" = $5,\"deny_all_users\" = $6,\"allow_all_network_devices\" = $7,\"deny_all_network_devices\" = $8,\"all_networks\" = $9,\"destination\" = $10,\"ports\" = $11,\"protocols\" = $12,\"enabled\" = $13,\"expires\" = $14,\"any_destination\" = $15,\"any_port\" = $16,\"any_protocol\" = $17,\"manual_settings\" = $18 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -34,10 +34,11 @@ "Timestamp", "Bool", "Bool", + "Bool", "Bool" ] }, "nullable": [] }, - "hash": "c91da2224327062d00f254c419de7d06366cf41d7c61403147666c01e38c8210" + "hash": "6915bd0015a04c1caf439b1932ab4fd9100d098098f181d8ebef6f77b8cd7097" } diff --git a/.sqlx/query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json b/.sqlx/query-e886ab4f4c0ac47d41e96005d8c82eea1705b9849ec2afb19b998a91644bac25.json similarity index 91% rename from .sqlx/query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json rename to .sqlx/query-e886ab4f4c0ac47d41e96005d8c82eea1705b9849ec2afb19b998a91644bac25.json index fcd6e7d3f2..133445ad9d 100644 --- a/.sqlx/query-cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128.json +++ b/.sqlx/query-e886ab4f4c0ac47d41e96005d8c82eea1705b9849ec2afb19b998a91644bac25.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\" FROM \"aclrule\"", + "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\",\"manual_settings\" FROM \"aclrule\"", "describe": { "columns": [ { @@ -100,6 +100,11 @@ "ordinal": 16, "name": "any_protocol", "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "manual_settings", + "type_info": "Bool" } ], "parameters": { @@ -122,8 +127,9 @@ true, false, false, + false, false ] }, - "hash": "cf36f4d85ed325020f21a07850b82347293b5107a5a0f179aa07d97b65f2c128" + "hash": "e886ab4f4c0ac47d41e96005d8c82eea1705b9849ec2afb19b998a91644bac25" } diff --git a/.sqlx/query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json b/.sqlx/query-e907d432de9692381c67b0eaf66a58a39e79ace0cc7798df471d26a786352b89.json similarity index 89% rename from .sqlx/query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json rename to .sqlx/query-e907d432de9692381c67b0eaf66a58a39e79ace0cc7798df471d26a786352b89.json index 7467b82783..21b384d146 100644 --- a/.sqlx/query-19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11.json +++ b/.sqlx/query-e907d432de9692381c67b0eaf66a58a39e79ace0cc7798df471d26a786352b89.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT ar.id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires, any_destination, any_port, any_protocol FROM aclrulealias ara JOIN aclrule ar ON ar.id = ara.rule_id WHERE ara.alias_id = $1", + "query": "SELECT ar.id, parent_id, state AS \"state: RuleState\", name, allow_all_users, deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, destination, ports, protocols, enabled, expires, any_destination, any_port, any_protocol, manual_settings FROM aclrulealias ara JOIN aclrule ar ON ar.id = ara.rule_id WHERE ara.alias_id = $1", "describe": { "columns": [ { @@ -100,6 +100,11 @@ "ordinal": 16, "name": "any_protocol", "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "manual_settings", + "type_info": "Bool" } ], "parameters": { @@ -124,8 +129,9 @@ true, false, false, + false, false ] }, - "hash": "19df2773b606e41acceb8d7b842c1a8cc2642c32bf6ff816f0906cc1befbad11" + "hash": "e907d432de9692381c67b0eaf66a58a39e79ace0cc7798df471d26a786352b89" } diff --git a/.sqlx/query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json b/.sqlx/query-e958dccd2e8b944bd051ddbde69612da6e2a687c5c5588e8ad4c1381b31946d8.json similarity index 90% rename from .sqlx/query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json rename to .sqlx/query-e958dccd2e8b944bd051ddbde69612da6e2a687c5c5588e8ad4c1381b31946d8.json index ca41a5c9d1..07966a453e 100644 --- a/.sqlx/query-d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b.json +++ b/.sqlx/query-e958dccd2e8b944bd051ddbde69612da6e2a687c5c5588e8ad4c1381b31946d8.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\" FROM \"aclrule\" WHERE id = $1", + "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\" \"destination: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\",\"manual_settings\" FROM \"aclrule\" WHERE id = $1", "describe": { "columns": [ { @@ -100,6 +100,11 @@ "ordinal": 16, "name": "any_protocol", "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "manual_settings", + "type_info": "Bool" } ], "parameters": { @@ -124,8 +129,9 @@ true, false, false, + false, false ] }, - "hash": "d142cbe1b1ecac9690aa044b24d24e504dded78c5073c19b59afa4b0eb89849b" + "hash": "e958dccd2e8b944bd051ddbde69612da6e2a687c5c5588e8ad4c1381b31946d8" } diff --git a/.sqlx/query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json b/.sqlx/query-f7c6882463818d70c1f092d440175269185fe6b6d021bed8623fe25b891e01d5.json similarity index 81% rename from .sqlx/query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json rename to .sqlx/query-f7c6882463818d70c1f092d440175269185fe6b6d021bed8623fe25b891e01d5.json index 12def56afc..73de745775 100644 --- a/.sqlx/query-64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60.json +++ b/.sqlx/query-f7c6882463818d70c1f092d440175269185fe6b6d021bed8623fe25b891e01d5.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"aclrule\" (\"parent_id\",\"state\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\",\"ports\",\"protocols\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16) RETURNING id", + "query": "INSERT INTO \"aclrule\" (\"parent_id\",\"state\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_networks\",\"destination\",\"ports\",\"protocols\",\"enabled\",\"expires\",\"any_destination\",\"any_port\",\"any_protocol\",\"manual_settings\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17) RETURNING id", "describe": { "columns": [ { @@ -39,6 +39,7 @@ "Timestamp", "Bool", "Bool", + "Bool", "Bool" ] }, @@ -46,5 +47,5 @@ false ] }, - "hash": "64afd98642be4c6b91aba8f9a1d7de9991bd88e02a62240a876067cd32267c60" + "hash": "f7c6882463818d70c1f092d440175269185fe6b6d021bed8623fe25b891e01d5" } diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index f9e252b3b4..efb15a9187 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -259,6 +259,7 @@ pub struct AclRule { pub any_destination: bool, pub any_port: bool, pub any_protocol: bool, + pub manual_settings: bool, } impl AclRule { @@ -847,6 +848,7 @@ impl TryFrom for AclRule { any_destination: rule.any_destination, any_port: rule.any_port, any_protocol: rule.any_protocol, + manual_settings: true, }) } } @@ -1784,7 +1786,7 @@ impl AclAlias { "SELECT ar.id, parent_id, state AS \"state: RuleState\", name, allow_all_users, \ deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, \ destination, ports, protocols, enabled, expires, any_destination, any_port, \ - any_protocol \ + any_protocol, manual_settings \ FROM aclrulealias ara \ JOIN aclrule ar ON ar.id = ara.rule_id \ WHERE ara.alias_id = $1", diff --git a/crates/defguard_core/src/enterprise/db/models/acl/tests.rs b/crates/defguard_core/src/enterprise/db/models/acl/tests.rs index 758d419f6e..29bd3691b3 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl/tests.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl/tests.rs @@ -71,6 +71,7 @@ async fn test_allow_conflicting_sources(_: PgPoolOptions, options: PgConnectOpti any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, } .save(&pool) .await @@ -176,6 +177,7 @@ async fn test_rule_relations(_: PgPoolOptions, options: PgConnectOptions) { any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, } .save(&pool) .await @@ -563,6 +565,7 @@ async fn test_all_allowed_users(_: PgPoolOptions, options: PgConnectOptions) { any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, } .save(&pool) .await @@ -681,6 +684,7 @@ async fn test_all_denied_users(_: PgPoolOptions, options: PgConnectOptions) { any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, } .save(&pool) .await diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index 6d24abef58..f298d957dd 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -874,7 +874,8 @@ pub(crate) async fn get_location_active_acl_rules( let rules: Vec> = query_as( "SELECT DISTINCT ON (a.id) a.id, name, allow_all_users, deny_all_users, all_networks, \ allow_all_network_devices, deny_all_network_devices, destination, ports, protocols, \ - expires, enabled, parent_id, state, any_destination, any_port, any_protocol \ + expires, enabled, parent_id, state, any_destination, any_port, any_protocol, + manual_settings \ FROM aclrule a \ LEFT JOIN aclrulenetwork an ON a.id = an.rule_id \ WHERE (an.network_id = $1 OR a.all_networks = true) AND enabled = true \ diff --git a/crates/defguard_core/src/enterprise/firewall/tests.rs b/crates/defguard_core/src/enterprise/firewall/tests.rs index f54c235c5c..2934ef4921 100644 --- a/crates/defguard_core/src/enterprise/firewall/tests.rs +++ b/crates/defguard_core/src/enterprise/firewall/tests.rs @@ -1104,6 +1104,7 @@ async fn test_generate_firewall_rules_ipv4(_: PgPoolOptions, options: PgConnectO any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, }; let locations = vec![location.id]; let allowed_users = vec![user_1.id, user_2.id]; // First two users can access web @@ -1149,6 +1150,7 @@ async fn test_generate_firewall_rules_ipv4(_: PgPoolOptions, options: PgConnectO any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, }; let locations_2 = vec![location.id]; let allowed_users_2 = Vec::new(); @@ -1532,6 +1534,7 @@ async fn test_generate_firewall_rules_ipv6(_: PgPoolOptions, options: PgConnectO any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, }; let locations = vec![location.id]; let allowed_users = vec![user_1.id, user_2.id]; // First two users can access web @@ -1577,6 +1580,7 @@ async fn test_generate_firewall_rules_ipv6(_: PgPoolOptions, options: PgConnectO any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, }; let locations_2 = vec![location.id]; let allowed_users_2 = Vec::new(); @@ -2003,6 +2007,7 @@ async fn test_generate_firewall_rules_ipv4_and_ipv6(_: PgPoolOptions, options: P any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, }; let locations = vec![location.id]; let allowed_users = vec![user_1.id, user_2.id]; // First two users can access web @@ -2048,6 +2053,7 @@ async fn test_generate_firewall_rules_ipv4_and_ipv6(_: PgPoolOptions, options: P any_destination: true, any_port: true, any_protocol: true, + manual_settings: true, }; let locations_2 = vec![location.id]; let allowed_users_2 = Vec::new(); diff --git a/crates/defguard_core/src/utility_thread.rs b/crates/defguard_core/src/utility_thread.rs index 8d74796eb3..123cb619b6 100644 --- a/crates/defguard_core/src/utility_thread.rs +++ b/crates/defguard_core/src/utility_thread.rs @@ -240,9 +240,9 @@ async fn expired_acl_rules_check( "UPDATE aclrule SET state = 'expired'::aclrule_state \ WHERE state = 'applied'::aclrule_state AND expires < NOW() \ RETURNING id, parent_id, state AS \"state: RuleState\", name, allow_all_users, \ - deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, \ - destination, ports, protocols, enabled, expires, any_destination, any_port, \ - any_protocol" + deny_all_users, allow_all_network_devices, deny_all_network_devices, all_networks, \ + destination, ports, protocols, enabled, expires, any_destination, any_port, \ + any_protocol, manual_settings" ) .fetch_all(pool) .await?; From e1585723065cfca63a6e2448e3a4db1c16808a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 28 Jan 2026 12:05:39 +0100 Subject: [PATCH 11/11] Fix tests --- crates/defguard_core/src/enterprise/db/models/acl.rs | 2 +- crates/defguard_core/src/enterprise/handlers/acl.rs | 6 ++++++ crates/defguard_core/tests/integration/api/acl.rs | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index efb15a9187..8a5885e017 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -278,7 +278,7 @@ impl AclRule { rule.create_related_objects(&mut transaction, api_rule) .await?; - let result: ApiAclRule = rule.to_info(&mut transaction).await?.into(); + let result = ApiAclRule::from(rule.to_info(&mut transaction).await?); transaction.commit().await?; diff --git a/crates/defguard_core/src/enterprise/handlers/acl.rs b/crates/defguard_core/src/enterprise/handlers/acl.rs index ddd451cc52..4cecdca3e7 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl.rs @@ -50,6 +50,9 @@ pub struct ApiAclRule { pub aliases: Vec, pub ports: String, pub protocols: Vec, + pub any_destination: bool, + pub any_port: bool, + pub any_protocol: bool, } impl From> for ApiAclRule { @@ -77,6 +80,9 @@ impl From> for ApiAclRule { aliases: info.aliases.iter().map(|v| v.id).collect(), protocols: info.protocols, enabled: info.enabled, + any_destination: info.any_destination, + any_port: info.any_port, + any_protocol: info.any_protocol, } } } diff --git a/crates/defguard_core/tests/integration/api/acl.rs b/crates/defguard_core/tests/integration/api/acl.rs index 12cddb389d..22f8632631 100644 --- a/crates/defguard_core/tests/integration/api/acl.rs +++ b/crates/defguard_core/tests/integration/api/acl.rs @@ -119,6 +119,9 @@ fn edit_rule_data_into_api_response( aliases: data.aliases.clone(), ports: data.ports.clone(), protocols: data.protocols.clone(), + any_destination: data.any_destination, + any_port: data.any_port, + any_protocol: data.any_protocol, } }