diff --git a/.env b/.env-template similarity index 100% rename from .env rename to .env-template diff --git a/.sqlx/query-58ec5c6949966199cd615f5aa4b18095ac216e3d78c7214b77fc86aaf188b1e9.json b/.sqlx/query-03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956.json similarity index 65% rename from .sqlx/query-58ec5c6949966199cd615f5aa4b18095ac216e3d78c7214b77fc86aaf188b1e9.json rename to .sqlx/query-03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956.json index e0965a4c3..bf5ab8879 100644 --- a/.sqlx/query-58ec5c6949966199cd615f5aa4b18095ac216e3d78c7214b77fc86aaf188b1e9.json +++ b/.sqlx/query-03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT a.id, parent_id, name, state AS \"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 FROM aclrulealias r JOIN aclalias a ON a.id = r.alias_id WHERE r.rule_id = $1", "describe": { "columns": [ { @@ -20,6 +20,21 @@ }, { "ordinal": 3, + "name": "kind: AliasKind", + "type_info": { + "Custom": { + "name": "aclalias_kind", + "kind": { + "Enum": [ + "destination", + "component" + ] + } + } + } + }, + { + "ordinal": 4, "name": "state: AliasState", "type_info": { "Custom": { @@ -34,17 +49,17 @@ } }, { - "ordinal": 4, + "ordinal": 5, "name": "destination", "type_info": "InetArray" }, { - "ordinal": 5, + "ordinal": 6, "name": "ports", "type_info": "Int4RangeArray" }, { - "ordinal": 6, + "ordinal": 7, "name": "protocols", "type_info": "Int4Array" } @@ -61,8 +76,9 @@ false, false, false, + false, false ] }, - "hash": "58ec5c6949966199cd615f5aa4b18095ac216e3d78c7214b77fc86aaf188b1e9" + "hash": "03188f9728ec2c493d7b7f0ef995f5824fc0384b319b048c93e661124cbbc956" } diff --git a/.sqlx/query-eed564684048e6014f7181c08d856ad36f7d8b175e0052c287d1007e77da0e47.json b/.sqlx/query-6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270.json similarity index 65% rename from .sqlx/query-eed564684048e6014f7181c08d856ad36f7d8b175e0052c287d1007e77da0e47.json rename to .sqlx/query-6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270.json index ea26c1434..d36c3852c 100644 --- a/.sqlx/query-eed564684048e6014f7181c08d856ad36f7d8b175e0052c287d1007e77da0e47.json +++ b/.sqlx/query-6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"name\",\"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: _\" FROM \"aclalias\" WHERE id = $1", "describe": { "columns": [ { @@ -20,6 +20,21 @@ }, { "ordinal": 3, + "name": "kind: _", + "type_info": { + "Custom": { + "name": "aclalias_kind", + "kind": { + "Enum": [ + "destination", + "component" + ] + } + } + } + }, + { + "ordinal": 4, "name": "state: _", "type_info": { "Custom": { @@ -34,17 +49,17 @@ } }, { - "ordinal": 4, + "ordinal": 5, "name": "destination: _", "type_info": "InetArray" }, { - "ordinal": 5, + "ordinal": 6, "name": "ports: _", "type_info": "Int4RangeArray" }, { - "ordinal": 6, + "ordinal": 7, "name": "protocols: _", "type_info": "Int4Array" } @@ -61,8 +76,9 @@ false, false, false, + false, false ] }, - "hash": "eed564684048e6014f7181c08d856ad36f7d8b175e0052c287d1007e77da0e47" + "hash": "6069bf872680a736abd19c4667e572cb0ca37e41049364c1d10ebc9116c46270" } diff --git a/.sqlx/query-9aca9e0b4ed7974ed7ba1c71e48096b50a64a7fde11f18a3e479c16296f32224.json b/.sqlx/query-8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d.json similarity index 65% rename from .sqlx/query-9aca9e0b4ed7974ed7ba1c71e48096b50a64a7fde11f18a3e479c16296f32224.json rename to .sqlx/query-8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d.json index 2fb687cb3..7fe770b1c 100644 --- a/.sqlx/query-9aca9e0b4ed7974ed7ba1c71e48096b50a64a7fde11f18a3e479c16296f32224.json +++ b/.sqlx/query-8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"parent_id\",\"name\",\"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: _\" FROM \"aclalias\"", "describe": { "columns": [ { @@ -20,6 +20,21 @@ }, { "ordinal": 3, + "name": "kind: _", + "type_info": { + "Custom": { + "name": "aclalias_kind", + "kind": { + "Enum": [ + "destination", + "component" + ] + } + } + } + }, + { + "ordinal": 4, "name": "state: _", "type_info": { "Custom": { @@ -34,17 +49,17 @@ } }, { - "ordinal": 4, + "ordinal": 5, "name": "destination: _", "type_info": "InetArray" }, { - "ordinal": 5, + "ordinal": 6, "name": "ports: _", "type_info": "Int4RangeArray" }, { - "ordinal": 6, + "ordinal": 7, "name": "protocols: _", "type_info": "Int4Array" } @@ -59,8 +74,9 @@ false, false, false, + false, false ] }, - "hash": "9aca9e0b4ed7974ed7ba1c71e48096b50a64a7fde11f18a3e479c16296f32224" + "hash": "8e08ccc9d9fea975abc183c3e6019b0618788c2e6f45de6068cc67d3a4db684d" } diff --git a/.sqlx/query-360ed3ff9ae976e47d7f747447591a290a7702b02aadbd506fec02372da0b711.json b/.sqlx/query-bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0.json similarity index 54% rename from .sqlx/query-360ed3ff9ae976e47d7f747447591a290a7702b02aadbd506fec02372da0b711.json rename to .sqlx/query-bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0.json index cb1e8fab8..1d73ab2ce 100644 --- a/.sqlx/query-360ed3ff9ae976e47d7f747447591a290a7702b02aadbd506fec02372da0b711.json +++ b/.sqlx/query-bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"aclalias\" (\"parent_id\",\"name\",\"state\",\"destination\",\"ports\",\"protocols\") VALUES ($1,$2,$3,$4,$5,$6) RETURNING id", + "query": "INSERT INTO \"aclalias\" (\"parent_id\",\"name\",\"kind\",\"state\",\"destination\",\"ports\",\"protocols\") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING id", "describe": { "columns": [ { @@ -13,6 +13,17 @@ "Left": [ "Int8", "Text", + { + "Custom": { + "name": "aclalias_kind", + "kind": { + "Enum": [ + "destination", + "component" + ] + } + } + }, { "Custom": { "name": "aclalias_state", @@ -33,5 +44,5 @@ false ] }, - "hash": "360ed3ff9ae976e47d7f747447591a290a7702b02aadbd506fec02372da0b711" + "hash": "bb990f96142f0eb1608dda8625de14cf4965b4bc16f094c18afc10dfd4690ea0" } diff --git a/.sqlx/query-ce36aaa28a6a86e0d08f710bd413af5e97a33c6da7e5577ed663200435376793.json b/.sqlx/query-d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233.json similarity index 57% rename from .sqlx/query-ce36aaa28a6a86e0d08f710bd413af5e97a33c6da7e5577ed663200435376793.json rename to .sqlx/query-d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233.json index 044fd3fa3..40b467fcd 100644 --- a/.sqlx/query-ce36aaa28a6a86e0d08f710bd413af5e97a33c6da7e5577ed663200435376793.json +++ b/.sqlx/query-d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"aclalias\" SET \"parent_id\" = $2,\"name\" = $3,\"state\" = $4,\"destination\" = $5,\"ports\" = $6,\"protocols\" = $7 WHERE id = $1", + "query": "UPDATE \"aclalias\" SET \"parent_id\" = $2,\"name\" = $3,\"kind\" = $4,\"state\" = $5,\"destination\" = $6,\"ports\" = $7,\"protocols\" = $8 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -8,6 +8,17 @@ "Int8", "Int8", "Text", + { + "Custom": { + "name": "aclalias_kind", + "kind": { + "Enum": [ + "destination", + "component" + ] + } + } + }, { "Custom": { "name": "aclalias_state", @@ -26,5 +37,5 @@ }, "nullable": [] }, - "hash": "ce36aaa28a6a86e0d08f710bd413af5e97a33c6da7e5577ed663200435376793" + "hash": "d1d66eb92a1db9e88b0090d02e4d7ad03502b6966d4c4edcb6e37518d4873233" } diff --git a/Cargo.lock b/Cargo.lock index cd3280b0c..ef9af4d6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -607,9 +607,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", @@ -702,9 +702,9 @@ checksum = "bba18ee93d577a8428902687bcc2b6b45a56b1981a1f6d779731c86cc4c5db18" [[package]] name = "clap" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -712,9 +712,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -804,7 +804,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -1154,9 +1154,9 @@ checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "pem-rfc7468", @@ -1231,9 +1231,9 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", @@ -1725,9 +1725,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -2528,9 +2528,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libgit2-sys" @@ -2546,9 +2546,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" [[package]] name = "libsqlite3-sys" @@ -2600,12 +2600,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - [[package]] name = "log" version = "0.4.27" @@ -2906,7 +2900,7 @@ checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ "base64 0.22.1", "chrono", - "getrandom 0.2.15", + "getrandom 0.2.16", "http", "rand 0.8.5", "reqwest", @@ -3475,9 +3469,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -3600,13 +3594,13 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" +checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" dependencies = [ "bytes", "getrandom 0.3.2", - "rand 0.9.0", + "rand 0.9.1", "ring", "rustc-hash", "rustls", @@ -3666,13 +3660,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.24", ] [[package]] @@ -3707,7 +3700,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -3844,7 +3837,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -4448,9 +4441,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e22987355fbf8cfb813a0cf8cd97b1b4ec834b94dbd759a9e8679d41fabe83" +checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e" dependencies = [ "sqlx-core", "sqlx-macros", @@ -4461,9 +4454,9 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c4720d7d4cd3d5b00f61d03751c685ad09c33ae8290c8a2c11335e0604300b" +checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3" dependencies = [ "base64 0.22.1", "bytes", @@ -4499,9 +4492,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175147fcb75f353ac7675509bc58abb2cb291caf0fd24a3623b8f7e3eb0a754b" +checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce" dependencies = [ "proc-macro2", "quote", @@ -4512,9 +4505,9 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cde983058e53bfa75998e1982086c5efe3c370f3250bf0357e344fa3352e32b" +checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7" dependencies = [ "dotenvy", "either", @@ -4538,9 +4531,9 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847d2e5393a4f39e47e4f36cab419709bc2b83cbe4223c60e86e1471655be333" +checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7" dependencies = [ "atoi", "base64 0.22.1", @@ -4582,9 +4575,9 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc35947a541b9e0a2e3d85da444f1c4137c13040267141b208395a0d0ca4659f" +checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6" dependencies = [ "atoi", "base64 0.22.1", @@ -4622,9 +4615,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c48291dac4e5ed32da0927a0b981788be65674aeb62666d19873ab4289febde" +checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc" dependencies = [ "atoi", "chrono", @@ -5026,9 +5019,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -5306,7 +5299,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c9e1c3f893758f154004195fc2d2c52fbda462df725220ceaef830ac29affa" dependencies = [ - "derive_more 0.99.19", + "derive_more 0.99.20", "lazy_static", "regex", "serde", @@ -6169,9 +6162,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" dependencies = [ "memchr", ] @@ -6414,15 +6407,13 @@ dependencies = [ [[package]] name = "zopfli" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ "bumpalo", "crc32fast", - "lockfree-object-pool", "log", - "once_cell", "simd-adler32", ] diff --git a/flake.lock b/flake.lock index 61fefe55f..82c8920de 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1744463964, - "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=", + "lastModified": 1745391562, + "narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650", + "rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1744599145, - "narHash": "sha256-yzaDPkJwZdUtRj/dzdOeB74yryWzpngYaD7BedqFKk8=", + "lastModified": 1745548521, + "narHash": "sha256-xyliq8oS5OnzXjHRGr92RtmrtYI/dflf2gSEo0wMFjc=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "fd6795d3d28f956de01a0458b6fa7baae5c793b4", + "rev": "eb0afb4ac0720d55c29e88eb29432103d73ae11d", "type": "github" }, "original": { diff --git a/migrations/20250425071954_add_acl_alias_kind.down.sql b/migrations/20250425071954_add_acl_alias_kind.down.sql new file mode 100644 index 000000000..ead7bad9d --- /dev/null +++ b/migrations/20250425071954_add_acl_alias_kind.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE aclalias DROP COLUMN kind; +DROP TYPE aclalias_kind; diff --git a/migrations/20250425071954_add_acl_alias_kind.up.sql b/migrations/20250425071954_add_acl_alias_kind.up.sql new file mode 100644 index 000000000..dd2b0678f --- /dev/null +++ b/migrations/20250425071954_add_acl_alias_kind.up.sql @@ -0,0 +1,7 @@ +CREATE TYPE aclalias_kind AS ENUM ( + 'destination', + 'component' +); +-- set kind for existing aliases and then remove the default +ALTER TABLE aclalias ADD COLUMN kind aclalias_kind NOT NULL DEFAULT 'destination'; +ALTER TABLE aclalias ALTER COLUMN kind DROP DEFAULT; diff --git a/src/enterprise/db/models/acl.rs b/src/enterprise/db/models/acl.rs index 42bce7827..152956861 100644 --- a/src/enterprise/db/models/acl.rs +++ b/src/enterprise/db/models/acl.rs @@ -926,7 +926,7 @@ impl AclRule { { query_as!( AclAlias, - "SELECT a.id, parent_id, name, state AS \"state: AliasState\", destination, ports, protocols \ + "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 \ @@ -1341,6 +1341,7 @@ pub struct AclAliasInfo { pub id: I, pub parent_id: Option, pub name: String, + pub kind: AliasKind, pub state: AliasState, pub destination: Vec, pub destination_ranges: Vec>, @@ -1404,6 +1405,7 @@ impl TryFrom for AclAlias { id: NoId, parent_id: None, name: alias.name, + kind: alias.kind, state: AliasState::Applied, protocols: alias.protocols, }) @@ -1426,18 +1428,31 @@ pub enum AliasState { Modified, } +/// ACL alias can be of one of the following types: +/// - Destination: the alias defines a complete destination that an ACL rule applies to +/// - Component: the alias defines parts of a destination and will be combined with other parts manually defined in an ACL rule +#[derive(Clone, Debug, Default, Deserialize, Serialize, Type, PartialEq, Eq)] +#[sqlx(type_name = "aclalias_kind", rename_all = "lowercase")] +pub enum AliasKind { + #[default] + Destination, + Component, +} + /// Database representation of an ACL alias. Aliases can be used to define /// the destination part of an ACL rule so that it's easier to create new /// rules with common restrictions. In addition to the [`AclAlias`] we provide /// [`AclAliasInfo`] and [`ApiAclAlias`] that combine all related objects for /// easier downstream processing. -#[derive(Clone, Debug, Model, PartialEq)] +#[derive(Clone, Debug, Default, Model, PartialEq)] pub struct AclAlias { pub id: I, // if present points to the original alias before modification pub parent_id: Option, pub name: String, #[model(enum)] + pub kind: AliasKind, + #[model(enum)] pub state: AliasState, #[model(ref)] pub destination: Vec, @@ -1452,6 +1467,7 @@ impl AclAlias { pub fn new>( name: S, state: AliasState, + kind: AliasKind, destination: Vec, ports: Vec>, protocols: Vec, @@ -1460,6 +1476,7 @@ impl AclAlias { id: NoId, parent_id: None, name: name.into(), + kind, state, destination, ports, @@ -1765,6 +1782,7 @@ impl AclAlias { id: self.id, parent_id: self.parent_id, name: self.name.clone(), + kind: self.kind.clone(), state: self.state.clone(), destination: self.destination.clone(), ports: self.ports.clone().into_iter().map(Into::into).collect(), @@ -1970,6 +1988,7 @@ mod test { let alias = AclAlias::new( "alias", AliasState::Applied, + AliasKind::Destination, destination.clone(), ports.clone(), vec![20, 30], @@ -2268,6 +2287,7 @@ mod test { let alias1 = AclAlias::new( "alias1", AliasState::Applied, + AliasKind::Destination, Vec::new(), Vec::new(), Vec::new(), @@ -2278,6 +2298,7 @@ mod test { let _alias2 = AclAlias::new( "alias2", AliasState::Applied, + AliasKind::Destination, Vec::new(), Vec::new(), Vec::new(), diff --git a/src/enterprise/firewall.rs b/src/enterprise/firewall.rs index dad76cff8..7ddc94cd5 100644 --- a/src/enterprise/firewall.rs +++ b/src/enterprise/firewall.rs @@ -11,7 +11,7 @@ use super::db::models::acl::{ }; use crate::{ db::{models::error::ModelError, Device, Id, User, WireguardNetwork}, - enterprise::is_enterprise_enabled, + enterprise::{db::models::acl::AliasKind, is_enterprise_enabled}, grpc::proto::enterprise::firewall::{ ip_address::Address, port::Port as PortInner, FirewallConfig, FirewallPolicy, FirewallRule, IpAddress, IpRange, IpVersion, Port, PortRange as PortRangeProto, @@ -78,14 +78,39 @@ pub async fn generate_firewall_rules_from_acls( // extract destination parameters from ACL rule let AclRuleInfo { id, - destination, + mut destination, destination_ranges, - ports, + mut ports, mut protocols, aliases, .. } = acl; + // split aliases into types + let (destination_aliases, component_aliases): (Vec<_>, Vec<_>) = aliases + .into_iter() + .partition(|alias| alias.kind == AliasKind::Destination); + + // store alias ranges separately since they use a different struct + let mut alias_destination_ranges = Vec::new(); + + // process component aliases by appending destination parameters from each of them to existing lists + for alias in component_aliases { + // fetch destination ranges for a given alias + alias_destination_ranges.extend(alias.get_destination_ranges(&mut *conn).await?); + + // extend existing parameter lists + destination.extend(alias.destination); + ports.extend( + alias + .ports + .into_iter() + .map(|port_range| port_range.into()) + .collect::>(), + ); + protocols.extend(alias.protocols); + } + // prepare destination addresses let destination_addrs = process_destination_addrs(destination, destination_ranges, ip_version); @@ -130,14 +155,14 @@ pub async fn generate_firewall_rules_from_acls( debug!("DENY rule generated from ACL: {deny_rule:?}"); deny_rules.push(deny_rule); - // process aliases by creating a dedicated set of rules for each alias - if !aliases.is_empty() { + // process destination aliases by creating a dedicated set of rules for each of them + if !destination_aliases.is_empty() { debug!( "Generating firewall rules for {} aliases used in ACL rule {id:?}", - aliases.len() + destination_aliases.len() ); } - for alias in aliases { + for alias in destination_aliases { debug!("Processing ACL alias: {alias:?}"); // fetch destination ranges for a given alias @@ -757,8 +782,9 @@ mod test { }, enterprise::{ db::models::acl::{ - AclRule, AclRuleAlias, AclRuleDestinationRange, AclRuleDevice, AclRuleGroup, - AclRuleInfo, AclRuleNetwork, AclRuleUser, PortRange, RuleState, + AclAlias, AclRule, AclRuleAlias, AclRuleDestinationRange, AclRuleDevice, + AclRuleGroup, AclRuleInfo, AclRuleNetwork, AclRuleUser, AliasKind, PortRange, + RuleState, }, firewall::{ get_source_addrs, get_source_network_devices, ip_to_range, next_ip, previous_ip, @@ -2287,4 +2313,195 @@ mod test { // rule with `all_networks` enabled was used for this location assert_eq!(generated_firewall_rules.len(), 3); } + + #[sqlx::test] + async fn test_alias_kinds(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + + let mut rng = thread_rng(); + + // Create test location + let location = WireguardNetwork { + id: NoId, + acl_enabled: true, + ..Default::default() + } + .save(&pool) + .await + .unwrap(); + + // Setup some test users and their devices + let user_1: User = rng.gen(); + let user_1 = user_1.save(&pool).await.unwrap(); + let user_2: User = rng.gen(); + let user_2 = user_2.save(&pool).await.unwrap(); + + for user in [&user_1, &user_2] { + // Create 2 devices per user + for device_num in 1..3 { + let device = Device { + id: NoId, + name: format!("device-{}-{}", user.id, device_num), + user_id: user.id, + device_type: DeviceType::User, + description: None, + wireguard_pubkey: Default::default(), + created: Default::default(), + configured: true, + }; + let device = device.save(&pool).await.unwrap(); + + // Add device to location's VPN network + let network_device = WireguardNetworkDevice { + device_id: device.id, + wireguard_network_id: location.id, + wireguard_ip: IpAddr::V4(Ipv4Addr::new(10, 0, user.id as u8, device_num as u8)), + preshared_key: None, + is_authorized: true, + authorized_at: None, + }; + network_device.insert(&pool).await.unwrap(); + } + } + + // create ACL rule + let acl_rule = AclRule { + id: NoId, + name: "test rule".to_string(), + expires: None, + enabled: true, + state: RuleState::Applied, + destination: vec!["192.168.1.0/24".parse().unwrap()], + allow_all_users: true, + ..Default::default() + } + .save(&pool) + .await + .unwrap(); + + // create different kinds of aliases and add them to the rule + let destination_alias = AclAlias { + id: NoId, + name: "destination alias".to_string(), + kind: AliasKind::Destination, + ports: vec![PortRange::new(100, 200).into()], + ..Default::default() + } + .save(&pool) + .await + .unwrap(); + let component_alias = AclAlias { + id: NoId, + kind: AliasKind::Component, + destination: vec!["10.0.2.3".parse().unwrap()], + ..Default::default() + } + .save(&pool) + .await + .unwrap(); + for alias in [&destination_alias, &component_alias] { + let obj = AclRuleAlias { + id: NoId, + rule_id: acl_rule.id, + alias_id: alias.id, + }; + obj.save(&pool).await.unwrap(); + } + + // assign rule to location + let obj = AclRuleNetwork { + id: NoId, + rule_id: acl_rule.id, + network_id: location.id, + }; + obj.save(&pool).await.unwrap(); + + let mut conn = pool.acquire().await.unwrap(); + let generated_firewall_rules = location + .try_get_firewall_config(&mut conn) + .await + .unwrap() + .unwrap() + .rules; + + // check generated rules + assert_eq!(generated_firewall_rules.len(), 4); + let expected_source_addrs = vec![ + IpAddress { + address: Some(Address::IpRange(IpRange { + start: "10.0.1.1".to_string(), + end: "10.0.1.2".to_string(), + })), + }, + IpAddress { + address: Some(Address::IpRange(IpRange { + start: "10.0.2.1".to_string(), + end: "10.0.2.2".to_string(), + })), + }, + ]; + let expected_destination_addrs = vec![ + IpAddress { + address: Some(Address::Ip("10.0.2.3".to_string())), + }, + IpAddress { + address: Some(Address::IpRange(IpRange { + start: "192.168.1.0".to_string(), + end: "192.168.1.255".to_string(), + })), + }, + ]; + + let allow_rule = &generated_firewall_rules[0]; + assert_eq!(allow_rule.verdict, i32::from(FirewallPolicy::Allow)); + assert_eq!(allow_rule.source_addrs, expected_source_addrs); + assert_eq!(allow_rule.destination_addrs, expected_destination_addrs); + assert!(allow_rule.destination_ports.is_empty()); + assert!(allow_rule.protocols.is_empty()); + assert_eq!( + allow_rule.comment, + Some("ACL 1 - test rule ALLOW".to_string()) + ); + + let alias_allow_rule = &generated_firewall_rules[1]; + assert_eq!(alias_allow_rule.verdict, i32::from(FirewallPolicy::Allow)); + assert_eq!(alias_allow_rule.source_addrs, expected_source_addrs); + assert!(alias_allow_rule.destination_addrs.is_empty()); + assert_eq!( + alias_allow_rule.destination_ports, + vec![Port { + port: Some(PortInner::PortRange(PortRangeProto { + start: 100, + end: 200, + })) + }] + ); + assert!(alias_allow_rule.protocols.is_empty()); + assert_eq!( + alias_allow_rule.comment, + Some("ACL 1 - test rule, ALIAS 1 - destination alias ALLOW".to_string()) + ); + + let deny_rule = &generated_firewall_rules[2]; + assert_eq!(deny_rule.verdict, i32::from(FirewallPolicy::Deny)); + assert!(deny_rule.source_addrs.is_empty()); + assert_eq!(deny_rule.destination_addrs, expected_destination_addrs); + assert!(deny_rule.destination_ports.is_empty()); + assert!(deny_rule.protocols.is_empty()); + assert_eq!( + deny_rule.comment, + Some("ACL 1 - test rule DENY".to_string()) + ); + + let alias_deny_rule = &generated_firewall_rules[3]; + assert_eq!(alias_deny_rule.verdict, i32::from(FirewallPolicy::Deny)); + assert!(alias_deny_rule.source_addrs.is_empty()); + assert!(alias_deny_rule.destination_addrs.is_empty()); + assert!(alias_deny_rule.destination_ports.is_empty()); + assert!(alias_deny_rule.protocols.is_empty()); + assert_eq!( + alias_deny_rule.comment, + Some("ACL 1 - test rule, ALIAS 1 - destination alias DENY".to_string()) + ); + } } diff --git a/src/enterprise/handlers/acl.rs b/src/enterprise/handlers/acl.rs index c411172da..906fe3b25 100644 --- a/src/enterprise/handlers/acl.rs +++ b/src/enterprise/handlers/acl.rs @@ -12,7 +12,7 @@ use crate::{ auth::{AdminRole, SessionInfo}, db::Id, enterprise::db::models::acl::{ - AclAlias, AclAliasInfo, AclRule, AclRuleInfo, AliasState, Protocol, RuleState, + AclAlias, AclAliasInfo, AclRule, AclRuleInfo, AliasKind, AliasState, Protocol, RuleState, }, error::WebError, handlers::{ApiResponse, ApiResult}, @@ -155,6 +155,7 @@ pub struct ApiAclAlias { pub id: Id, pub parent_id: Option, pub name: String, + pub kind: AliasKind, pub state: AliasState, pub destination: String, pub ports: String, @@ -170,6 +171,7 @@ impl From> for ApiAclAlias { id: info.id, parent_id: info.parent_id, name: info.name, + kind: info.kind, state: info.state, protocols: info.protocols, rules: info.rules.iter().map(|v| v.id).collect(), @@ -181,6 +183,7 @@ impl From> for ApiAclAlias { #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct EditAclAlias { pub name: String, + pub kind: AliasKind, pub destination: String, pub ports: String, pub protocols: Vec, diff --git a/tests/integration/acl.rs b/tests/integration/acl.rs index c61b33693..64a97bfdc 100644 --- a/tests/integration/acl.rs +++ b/tests/integration/acl.rs @@ -5,7 +5,7 @@ use defguard::{ Device, Group, Id, User, WireguardNetwork, }, enterprise::{ - db::models::acl::{AclAlias, AclRule, AliasState, RuleState}, + db::models::acl::{AclAlias, AclRule, AliasKind, AliasState, RuleState}, handlers::acl::{ApiAclAlias, ApiAclRule, EditAclAlias, EditAclRule}, license::{get_cached_license, set_cached_license}, }, @@ -76,6 +76,7 @@ async fn set_rule_state(pool: &PgPool, id: Id, state: RuleState, parent_id: Opti fn make_alias() -> EditAclAlias { EditAclAlias { name: "alias".to_string(), + kind: AliasKind::Destination, 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(), @@ -126,6 +127,7 @@ fn edit_alias_data_into_api_response( parent_id, state, name: data.name.clone(), + kind: data.kind.clone(), destination: data.destination.clone(), ports: data.ports.clone(), protocols: data.protocols.clone(), @@ -476,6 +478,7 @@ async fn test_related_objects(_: PgPoolOptions, options: PgConnectOptions) { AclAlias::new( "alias1", AliasState::Applied, + AliasKind::Destination, Vec::new(), Vec::new(), Vec::new(), @@ -486,6 +489,7 @@ async fn test_related_objects(_: PgPoolOptions, options: PgConnectOptions) { AclAlias::new( "alias2", AliasState::Applied, + AliasKind::Destination, Vec::new(), Vec::new(), Vec::new(), @@ -623,6 +627,7 @@ async fn test_invalid_related_objects(_: PgPoolOptions, options: PgConnectOption AclAlias::new( "alias1", AliasState::Modified, + AliasKind::Destination, Vec::new(), Vec::new(), Vec::new(), diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts index 96cb253a6..c6bdfa1d6 100644 --- a/web/src/i18n/en/index.ts +++ b/web/src/i18n/en/index.ts @@ -2212,6 +2212,32 @@ Any other requests you can reach us at: support@defguard.net }, }, acl: { + messageBoxes: { + aclAliasKind: { + component: { + name: 'Component', + description: 'combined with manually configured destination fields in ACL', + }, + destination: { + name: 'Destination', + description: 'translated into a separate set of firewall rules', + }, + }, + networkSelectionIndicatorsHelper: { + //md + denied: ` + Location access **denied** by default - must be explicitly allowed + `, + //md + allowed: ` + Location access **allowed** by default - can be explicitly denied + `, + //md + unmanaged: ` + Location access unmanaged (ACL disabled) + `, + }, + }, sharedTitle: 'Access Control List', fieldsSelectionLabels: { ports: 'All ports', @@ -2285,14 +2311,6 @@ Any other requests you can reach us at: support@defguard.net status: 'Status', edit: 'Edit', }, - status: { - new: 'New', - change: 'Pending Change', - delete: 'Pending Deletion', - enabled: 'Enabled', - disabled: 'Disabled', - deployed: 'Deployed', - }, tags: { all: 'All', allDenied: 'All denied', @@ -2331,6 +2349,33 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T status: 'Status', }, }, + create: { + labels: { + name: 'Alias name', + kind: 'Alias kind', + ip: 'IPv4/6 CIDR range address', + ports: 'Ports or Port Ranges', + protocols: 'Protocols', + }, + placeholders: { + protocols: 'All Protocols', + ports: 'All Ports', + ip: 'All IPs', + }, + kindOptions: { + destination: 'Destination', + component: 'Component', + }, + controls: { + cancel: 'Cancel', + edit: 'Edit Alias', + create: 'Create Alias', + }, + messages: { + modified: 'Alias modified', + created: 'Alias created', + }, + }, }, listControls: { searchPlaceholder: 'Find name', @@ -2359,6 +2404,7 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T headers: { id: 'ID', name: 'Alias name', + kind: 'Alias kind', ip: 'Ipv4/6 CIDR range address', ports: 'Ports', protocols: 'Protocols', @@ -2393,20 +2439,6 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T // md destinationInstructions: ` Specify one or more fields (IPs or Ports) to define this rule. The rule will consider all inputs provided for matching conditions. Leave any fields blank if not needed.`, - networkSelectionIndicatorsHelper: { - //md - denied: ` - Location access **denied** by default - must be explicitly allowed - `, - //md - allowed: ` - Location access **allowed** by default - can be explicitly denied - `, - //md - unmanaged: ` - Location access unmanaged (ACL disabled) - `, - }, }, message: { create: 'Rule created and added to pending changes', @@ -2435,9 +2467,12 @@ This alias is currently in use by the following rule(s) and cannot be deleted. T protocols: 'Protocols', manualIp: 'IPv4/6 CIDR range or address', ports: 'Ports', + aliases: 'Aliases', + expires: 'Expiration Date', }, placeholders: { allProtocols: 'All protocols', + allIps: 'All IPs', }, }, }, diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts index 888c790d5..c42108c62 100644 --- a/web/src/i18n/i18n-types.ts +++ b/web/src/i18n/i18n-types.ts @@ -5212,6 +5212,50 @@ type RootTranslation = { } } acl: { + messageBoxes: { + aclAliasKind: { + component: { + /** + * C​o​m​p​o​n​e​n​t + */ + name: string + /** + * c​o​m​b​i​n​e​d​ ​w​i​t​h​ ​m​a​n​u​a​l​l​y​ ​c​o​n​f​i​g​u​r​e​d​ ​d​e​s​t​i​n​a​t​i​o​n​ ​f​i​e​l​d​s​ ​i​n​ ​A​C​L + */ + description: string + } + destination: { + /** + * D​e​s​t​i​n​a​t​i​o​n + */ + name: string + /** + * t​r​a​n​s​l​a​t​e​d​ ​i​n​t​o​ ​a​ ​s​e​p​a​r​a​t​e​ ​s​e​t​ ​o​f​ ​f​i​r​e​w​a​l​l​ ​r​u​l​e​s + */ + description: string + } + } + networkSelectionIndicatorsHelper: { + /** + * + ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​*​*​d​e​n​i​e​d​*​*​ ​b​y​ ​d​e​f​a​u​l​t​ ​-​ ​m​u​s​t​ ​b​e​ ​e​x​p​l​i​c​i​t​l​y​ ​a​l​l​o​w​e​d​ + ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ + */ + denied: string + /** + * + ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​*​*​a​l​l​o​w​e​d​*​*​ ​b​y​ ​d​e​f​a​u​l​t​ ​-​ ​c​a​n​ ​b​e​ ​e​x​p​l​i​c​i​t​l​y​ ​d​e​n​i​e​d​ + ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ + */ + allowed: string + /** + * + ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​u​n​m​a​n​a​g​e​d​ ​(​A​C​L​ ​d​i​s​a​b​l​e​d​)​ + ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ + */ + unmanaged: string + } + } /** * A​c​c​e​s​s​ ​C​o​n​t​r​o​l​ ​L​i​s​t */ @@ -5421,32 +5465,6 @@ type RootTranslation = { */ edit: string } - status: { - /** - * N​e​w - */ - 'new': string - /** - * P​e​n​d​i​n​g​ ​C​h​a​n​g​e - */ - change: string - /** - * P​e​n​d​i​n​g​ ​D​e​l​e​t​i​o​n - */ - 'delete': string - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - /** - * D​e​p​l​o​y​e​d - */ - deployed: string - } tags: { /** * A​l​l @@ -5537,6 +5555,78 @@ type RootTranslation = { status: string } } + create: { + labels: { + /** + * A​l​i​a​s​ ​n​a​m​e + */ + name: string + /** + * A​l​i​a​s​ ​k​i​n​d + */ + kind: string + /** + * I​P​v​4​/​6​ ​C​I​D​R​ ​r​a​n​g​e​ ​a​d​d​r​e​s​s + */ + ip: string + /** + * P​o​r​t​s​ ​o​r​ ​P​o​r​t​ ​R​a​n​g​e​s + */ + ports: string + /** + * P​r​o​t​o​c​o​l​s + */ + protocols: string + } + placeholders: { + /** + * A​l​l​ ​P​r​o​t​o​c​o​l​s + */ + protocols: string + /** + * A​l​l​ ​P​o​r​t​s + */ + ports: string + /** + * A​l​l​ ​I​P​s + */ + ip: string + } + kindOptions: { + /** + * D​e​s​t​i​n​a​t​i​o​n + */ + destination: string + /** + * C​o​m​p​o​n​e​n​t + */ + component: string + } + controls: { + /** + * C​a​n​c​e​l + */ + cancel: string + /** + * E​d​i​t​ ​A​l​i​a​s + */ + edit: string + /** + * C​r​e​a​t​e​ ​A​l​i​a​s + */ + create: string + } + messages: { + /** + * A​l​i​a​s​ ​m​o​d​i​f​i​e​d + */ + modified: string + /** + * A​l​i​a​s​ ​c​r​e​a​t​e​d + */ + created: string + } + } } listControls: { /** @@ -5613,6 +5703,10 @@ type RootTranslation = { * A​l​i​a​s​ ​n​a​m​e */ name: string + /** + * A​l​i​a​s​ ​k​i​n​d + */ + kind: string /** * I​p​v​4​/​6​ ​C​I​D​R​ ​r​a​n​g​e​ ​a​d​d​r​e​s​s */ @@ -5693,26 +5787,6 @@ type RootTranslation = { ​ ​ ​ ​ ​ ​ ​ ​ ​S​p​e​c​i​f​y​ ​o​n​e​ ​o​r​ ​m​o​r​e​ ​f​i​e​l​d​s​ ​(​I​P​s​ ​o​r​ ​P​o​r​t​s​)​ ​t​o​ ​d​e​f​i​n​e​ ​t​h​i​s​ ​r​u​l​e​.​ ​T​h​e​ ​r​u​l​e​ ​w​i​l​l​ ​c​o​n​s​i​d​e​r​ ​a​l​l​ ​i​n​p​u​t​s​ ​p​r​o​v​i​d​e​d​ ​f​o​r​ ​m​a​t​c​h​i​n​g​ ​c​o​n​d​i​t​i​o​n​s​.​ ​L​e​a​v​e​ ​a​n​y​ ​f​i​e​l​d​s​ ​b​l​a​n​k​ ​i​f​ ​n​o​t​ ​n​e​e​d​e​d​. */ destinationInstructions: string - networkSelectionIndicatorsHelper: { - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​*​*​d​e​n​i​e​d​*​*​ ​b​y​ ​d​e​f​a​u​l​t​ ​-​ ​m​u​s​t​ ​b​e​ ​e​x​p​l​i​c​i​t​l​y​ ​a​l​l​o​w​e​d​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - denied: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​*​*​a​l​l​o​w​e​d​*​*​ ​b​y​ ​d​e​f​a​u​l​t​ ​-​ ​c​a​n​ ​b​e​ ​e​x​p​l​i​c​i​t​l​y​ ​d​e​n​i​e​d​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - allowed: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​u​n​m​a​n​a​g​e​d​ ​(​A​C​L​ ​d​i​s​a​b​l​e​d​)​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - unmanaged: string - } } message: { /** @@ -5807,12 +5881,24 @@ type RootTranslation = { * P​o​r​t​s */ ports: string + /** + * A​l​i​a​s​e​s + */ + aliases: string + /** + * E​x​p​i​r​a​t​i​o​n​ ​D​a​t​e + */ + expires: string } placeholders: { /** * A​l​l​ ​p​r​o​t​o​c​o​l​s */ allProtocols: string + /** + * A​l​l​ ​I​P​s + */ + allIps: string } } } @@ -10973,6 +11059,50 @@ export type TranslationFunctions = { } } acl: { + messageBoxes: { + aclAliasKind: { + component: { + /** + * Component + */ + name: () => LocalizedString + /** + * combined with manually configured destination fields in ACL + */ + description: () => LocalizedString + } + destination: { + /** + * Destination + */ + name: () => LocalizedString + /** + * translated into a separate set of firewall rules + */ + description: () => LocalizedString + } + } + networkSelectionIndicatorsHelper: { + /** + * + Location access **denied** by default - must be explicitly allowed + + */ + denied: () => LocalizedString + /** + * + Location access **allowed** by default - can be explicitly denied + + */ + allowed: () => LocalizedString + /** + * + Location access unmanaged (ACL disabled) + + */ + unmanaged: () => LocalizedString + } + } /** * Access Control List */ @@ -11178,32 +11308,6 @@ export type TranslationFunctions = { */ edit: () => LocalizedString } - status: { - /** - * New - */ - 'new': () => LocalizedString - /** - * Pending Change - */ - change: () => LocalizedString - /** - * Pending Deletion - */ - 'delete': () => LocalizedString - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - /** - * Deployed - */ - deployed: () => LocalizedString - } tags: { /** * All @@ -11293,6 +11397,78 @@ export type TranslationFunctions = { status: () => LocalizedString } } + create: { + labels: { + /** + * Alias name + */ + name: () => LocalizedString + /** + * Alias kind + */ + kind: () => LocalizedString + /** + * IPv4/6 CIDR range address + */ + ip: () => LocalizedString + /** + * Ports or Port Ranges + */ + ports: () => LocalizedString + /** + * Protocols + */ + protocols: () => LocalizedString + } + placeholders: { + /** + * All Protocols + */ + protocols: () => LocalizedString + /** + * All Ports + */ + ports: () => LocalizedString + /** + * All IPs + */ + ip: () => LocalizedString + } + kindOptions: { + /** + * Destination + */ + destination: () => LocalizedString + /** + * Component + */ + component: () => LocalizedString + } + controls: { + /** + * Cancel + */ + cancel: () => LocalizedString + /** + * Edit Alias + */ + edit: () => LocalizedString + /** + * Create Alias + */ + create: () => LocalizedString + } + messages: { + /** + * Alias modified + */ + modified: () => LocalizedString + /** + * Alias created + */ + created: () => LocalizedString + } + } } listControls: { /** @@ -11366,6 +11542,10 @@ export type TranslationFunctions = { * Alias name */ name: () => LocalizedString + /** + * Alias kind + */ + kind: () => LocalizedString /** * Ipv4/6 CIDR range address */ @@ -11446,26 +11626,6 @@ export type TranslationFunctions = { Specify one or more fields (IPs or Ports) to define this rule. The rule will consider all inputs provided for matching conditions. Leave any fields blank if not needed. */ destinationInstructions: () => LocalizedString - networkSelectionIndicatorsHelper: { - /** - * - Location access **denied** by default - must be explicitly allowed - - */ - denied: () => LocalizedString - /** - * - Location access **allowed** by default - can be explicitly denied - - */ - allowed: () => LocalizedString - /** - * - Location access unmanaged (ACL disabled) - - */ - unmanaged: () => LocalizedString - } } message: { /** @@ -11560,12 +11720,24 @@ export type TranslationFunctions = { * Ports */ ports: () => LocalizedString + /** + * Aliases + */ + aliases: () => LocalizedString + /** + * Expiration Date + */ + expires: () => LocalizedString } placeholders: { /** * All protocols */ allProtocols: () => LocalizedString + /** + * All IPs + */ + allIps: () => LocalizedString } } } diff --git a/web/src/pages/acl/AclCreatePage/AclCreatePage.tsx b/web/src/pages/acl/AclCreatePage/AclCreatePage.tsx index 976dab2de..c9ea1b96a 100644 --- a/web/src/pages/acl/AclCreatePage/AclCreatePage.tsx +++ b/web/src/pages/acl/AclCreatePage/AclCreatePage.tsx @@ -20,8 +20,6 @@ import { FormCheckBox } from '../../../shared/defguard-ui/components/Form/FormCh import { FormInput } from '../../../shared/defguard-ui/components/Form/FormInput/FormInput'; import { FormSelect } from '../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; import { FormTextarea } from '../../../shared/defguard-ui/components/Form/FormTextarea/FormTextarea'; -import { ActivityIcon } from '../../../shared/defguard-ui/components/icons/ActivityIcon/ActivityIcon'; -import { ActivityIconVariant } from '../../../shared/defguard-ui/components/icons/ActivityIcon/types'; import { Button } from '../../../shared/defguard-ui/components/Layout/Button/Button'; import { ButtonSize, @@ -48,17 +46,14 @@ import { } from '../../../shared/types'; import { trimObjectStrings } from '../../../shared/utils/trimObjectStrings'; import { useAclLoadedContext } from '../acl-context'; -import { AclAliasStatus } from '../types'; -import { protocolOptions, protocolToString } from '../utils'; +import { AclAliasKindIcon } from '../AclIndexPage/components/shared/AclAliasKindIcon'; +import { AclMessageBoxes } from '../AclIndexPage/components/shared/AclMessageBoxes/AclMessageBoxes'; +import { NetworkAccessTypeIcon } from '../AclIndexPage/components/shared/NetworkAccessTypeIcon'; +import { AclAlias, AclAliasStatus } from '../types'; +import { networkToNetworkAccessType, protocolOptions, protocolToString } from '../utils'; import { aclDestinationValidator, aclPortsValidator } from '../validators'; -import { AclCreateNetworkSelectMessage } from './components/DialogSelect/AclCreateNetwrokSelectMessage/AclCreateNetwrokSelectMessage'; import { FormDialogSelect } from './components/DialogSelect/FormDialogSelect'; -type Alias = { - id: number; - name: string; -}; - type AclForm = Omit; export const AlcCreatePage = () => { @@ -382,7 +377,7 @@ export const AlcCreatePage = () => { label={labelsLL.locations()} searchKeys={['name']} disabled={allowAllLocations} - modalExtrasTop={} + modalExtrasTop={} forceShowErrorMessage /> @@ -400,8 +395,8 @@ export const AlcCreatePage = () => { }} /> @@ -413,9 +408,10 @@ export const AlcCreatePage = () => { } controller={{ control, name: 'aliases' }} options={aliasesOptions} - label="Aliases" + label={localLL.labels.aliases()} identKey="id" renderTagContent={renderAlias} searchKeys={['name']} @@ -424,6 +420,7 @@ export const AlcCreatePage = () => { { const renderNetworkSelectTag = (network: Network) => ( <>

{network.name}

- + ); @@ -657,6 +646,11 @@ const renderUserListItem = (user: User) => ( const renderNetworkDevice = (device: StandaloneDevice) =>

{device.name}

; -const renderAlias = (alias: Alias) =>

{alias.name}

; +const renderAlias = (alias: AclAlias) => ( + <> +

{alias.name}

+ + +); const renderGroup = (group: GroupInfo) =>

{group.name}

; diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/AclCreateNetwrokSelectMessage/AclCreateNetwrokSelectMessage.tsx b/web/src/pages/acl/AclCreatePage/components/DialogSelect/AclCreateNetwrokSelectMessage/AclCreateNetwrokSelectMessage.tsx deleted file mode 100644 index bf55b1950..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/AclCreateNetwrokSelectMessage/AclCreateNetwrokSelectMessage.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import './style.scss'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { RenderMarkdown } from '../../../../../../shared/components/Layout/RenderMarkdown/RenderMarkdown'; -import { ActivityIcon } from '../../../../../../shared/defguard-ui/components/icons/ActivityIcon/ActivityIcon'; -import { ActivityIconVariant } from '../../../../../../shared/defguard-ui/components/icons/ActivityIcon/types'; -import { MessageBox } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/types'; - -export const AclCreateNetworkSelectMessage = () => { - const { LL } = useI18nContext(); - const localLL = LL.acl.createPage.infoBox.networkSelectionIndicatorsHelper; - - return ( - -
-
-
- -
-

{}

- -
-
-
- -
- -
-
-
- -
- -
-
-
- ); -}; diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/AclCreateNetwrokSelectMessage/style.scss b/web/src/pages/acl/AclCreatePage/components/DialogSelect/AclCreateNetwrokSelectMessage/style.scss deleted file mode 100644 index 73f69adee..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/AclCreateNetwrokSelectMessage/style.scss +++ /dev/null @@ -1,23 +0,0 @@ -.acl-network-selection-help { - .indicators-help { - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - - & > div { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: flex-start; - - .icon-wrapper { - display: inline-flex; - flex-flow: column; - align-items: center; - justify-content: center; - width: 16px; - height: 16px; - } - } - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/AclIndexAliases.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/AclIndexAliases.tsx index 87256f613..6a42bc694 100644 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/AclIndexAliases.tsx +++ b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/AclIndexAliases.tsx @@ -250,7 +250,7 @@ export const AclIndexAliases = () => { items: [ { label: localLL.list.status.changed(), - searchValues: [LL.acl.listPage.rules.list.status.change()], + searchValues: [LL.acl.ruleStatus.modified()], value: aclAliasStatusToInt(AclAliasStatus.MODIFIED), }, { @@ -264,7 +264,7 @@ export const AclIndexAliases = () => { }; return res; }, [ - LL.acl.listPage.rules.list.status, + LL.acl.ruleStatus, aclRules, localLL.list.status, localLL.modals.filterGroupsModal.groupLabels, diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasesList.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasesList.tsx index 32176be55..071f00ea1 100644 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasesList.tsx +++ b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasesList.tsx @@ -93,6 +93,11 @@ export const AliasesList = ({ key: 'status', enabled: false, }, + { + label: headersLL.kind(), + sortKey: 'kind', + enabled: true, + }, { label: headersLL.edit(), key: 'edit', @@ -176,6 +181,9 @@ export const AliasesList = ({
+
+ +
diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/AlcAliasCEModal.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/AlcAliasCEModal.tsx index 86bd74790..cec67fae2 100644 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/AlcAliasCEModal.tsx +++ b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/AlcAliasCEModal.tsx @@ -17,12 +17,15 @@ import { ButtonStyleVariant, } from '../../../../../../../shared/defguard-ui/components/Layout/Button/types'; import { ModalWithTitle } from '../../../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; +import { SelectOption } from '../../../../../../../shared/defguard-ui/components/Layout/Select/types'; import { isPresent } from '../../../../../../../shared/defguard-ui/utils/isPresent'; import useApi from '../../../../../../../shared/hooks/useApi'; import { useToaster } from '../../../../../../../shared/hooks/useToaster'; import { QueryKeys } from '../../../../../../../shared/queries'; +import { AclAliasKind } from '../../../../../types'; import { protocolOptions, protocolToString } from '../../../../../utils'; import { aclDestinationValidator, aclPortsValidator } from '../../../../../validators'; +import { AclMessageBoxes } from '../../../shared/AclMessageBoxes/AclMessageBoxes'; import { useAclAliasCEModal } from './store'; export const AlcAliasCEModal = () => { @@ -41,6 +44,7 @@ export const AlcAliasCEModal = () => { return ( { const toaster = useToaster(); const { LL } = useI18nContext(); + const localLL = LL.acl.listPage.aliases.modals.create; + const formErrors = LL.form.error; const { acl: { aliases: { createAlias, editAlias }, @@ -73,12 +79,17 @@ const ModalContent = () => { const schema = useMemo( () => z.object({ - name: z.string(), + name: z + .string({ + required_error: formErrors.required(), + }) + .min(1, formErrors.required()), + kind: z.nativeEnum(AclAliasKind), ports: aclPortsValidator(LL), destination: aclDestinationValidator(LL), protocols: z.number().array(), }), - [LL], + [LL, formErrors], ); type FormFields = z.infer; @@ -91,6 +102,7 @@ const ModalContent = () => { defaultValues = { destination: '', name: '', + kind: AclAliasKind.DESTINATION, ports: '', protocols: [], }; @@ -115,10 +127,10 @@ const ModalContent = () => { ...values, id: initialAlias.id, }); - toaster.success('Alias modified'); + toaster.success(localLL.messages.modified()); } else { await createAlias(values); - toaster.success('Alias created'); + toaster.success(localLL.messages.created()); } await queryClient.invalidateQueries({ predicate: (query) => query.queryKey.includes(QueryKeys.FETCH_ACL_ALIASES), @@ -128,24 +140,51 @@ const ModalContent = () => { toaster.error(LL.messages.error()); console.error(e); } - closeModal(); }; + const aliasKindOptions = useMemo( + (): SelectOption[] => [ + { + key: AclAliasKind.DESTINATION, + value: AclAliasKind.DESTINATION, + label: localLL.kindOptions.destination(), + }, + { + key: AclAliasKind.COMPONENT, + value: AclAliasKind.COMPONENT, + label: localLL.kindOptions.component(), + }, + ], + [localLL.kindOptions], + ); + return (
- + + +

Destination

+ - ({ displayValue: protocolToString(val), key: val })} @@ -154,7 +193,7 @@ const ModalContent = () => {