diff --git a/.sqlx/query-e707c443a1176f9309aa284a5d5f754cf30abe8c690ded2356b2c2eed7c5c21d.json b/.sqlx/query-0798f7c6357c0017eda60fc08cf778914258ea9837bd59df7a1f8384fa436307.json similarity index 88% rename from .sqlx/query-e707c443a1176f9309aa284a5d5f754cf30abe8c690ded2356b2c2eed7c5c21d.json rename to .sqlx/query-0798f7c6357c0017eda60fc08cf778914258ea9837bd59df7a1f8384fa436307.json index ae9f96e05c..8f92fb8a8e 100644 --- a/.sqlx/query-e707c443a1176f9309aa284a5d5f754cf30abe8c690ded2356b2c2eed7c5c21d.json +++ b/.sqlx/query-0798f7c6357c0017eda60fc08cf778914258ea9837bd59df7a1f8384fa436307.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured\n FROM device d JOIN \"user\" u ON d.user_id = u.id JOIN group_user gu ON u.id = gu.user_id JOIN \"group\" g ON gu.group_id = g.id WHERE g.\"name\" IN (SELECT * FROM UNNEST($1::text[])) AND u.is_active = true AND d.device_type = 'user'::device_type ORDER BY d.id ASC", + "query": "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured\n FROM device d JOIN \"user\" u ON d.user_id = u.id JOIN group_user gu ON u.id = gu.user_id JOIN \"group\" g ON gu.group_id = g.id WHERE g.\"name\" IN (SELECT * FROM UNNEST($1::text[])) AND u.is_active AND d.device_type = 'user'::device_type ORDER BY d.id ASC", "describe": { "columns": [ { @@ -70,5 +70,5 @@ false ] }, - "hash": "e707c443a1176f9309aa284a5d5f754cf30abe8c690ded2356b2c2eed7c5c21d" + "hash": "0798f7c6357c0017eda60fc08cf778914258ea9837bd59df7a1f8384fa436307" } diff --git a/.sqlx/query-f98428d4f6faeaf5475a9292045ce68fdd5e37d84269497c82184b1bb1db710e.json b/.sqlx/query-1151dfec098a686f3d15154de87b701cc13c06dacb8981cba46c9d5a1fcc69e7.json similarity index 96% rename from .sqlx/query-f98428d4f6faeaf5475a9292045ce68fdd5e37d84269497c82184b1bb1db710e.json rename to .sqlx/query-1151dfec098a686f3d15154de87b701cc13c06dacb8981cba46c9d5a1fcc69e7.json index 18ccc08762..b439c1b326 100644 --- a/.sqlx/query-f98428d4f6faeaf5475a9292045ce68fdd5e37d84269497c82184b1bb1db710e.json +++ b/.sqlx/query-1151dfec098a686f3d15154de87b701cc13c06dacb8981cba46c9d5a1fcc69e7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT u.id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM aclruleuser r JOIN \"user\" u ON u.id = r.user_id WHERE r.rule_id = $1 AND r.allow AND u.is_active = true", + "query": "SELECT u.id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM aclruleuser r JOIN \"user\" u ON u.id = r.user_id WHERE r.rule_id = $1 AND NOT r.allow AND u.is_active", "describe": { "columns": [ { @@ -150,5 +150,5 @@ false ] }, - "hash": "f98428d4f6faeaf5475a9292045ce68fdd5e37d84269497c82184b1bb1db710e" + "hash": "1151dfec098a686f3d15154de87b701cc13c06dacb8981cba46c9d5a1fcc69e7" } diff --git a/.sqlx/query-451af8e67a5df3d25ee59d8d6bf40d78fc40046e7a82f49d73ff6cd27cf64a96.json b/.sqlx/query-14f22dfa4eb01052c552ca6b05746c4b49b4766c0fd2d4144bcd5c252222d7e7.json similarity index 89% rename from .sqlx/query-451af8e67a5df3d25ee59d8d6bf40d78fc40046e7a82f49d73ff6cd27cf64a96.json rename to .sqlx/query-14f22dfa4eb01052c552ca6b05746c4b49b4766c0fd2d4144bcd5c252222d7e7.json index ddcaa39ab2..a6d54e0900 100644 --- a/.sqlx/query-451af8e67a5df3d25ee59d8d6bf40d78fc40046e7a82f49d73ff6cd27cf64a96.json +++ b/.sqlx/query-14f22dfa4eb01052c552ca6b05746c4b49b4766c0fd2d4144bcd5c252222d7e7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured FROM device d JOIN \"user\" u ON d.user_id = u.id WHERE u.is_active = true AND d.device_type = 'user'::device_type ORDER BY d.id ASC", + "query": "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured FROM device d JOIN \"user\" u ON d.user_id = u.id WHERE u.is_active AND d.device_type = 'user'::device_type ORDER BY d.id ASC", "describe": { "columns": [ { @@ -68,5 +68,5 @@ false ] }, - "hash": "451af8e67a5df3d25ee59d8d6bf40d78fc40046e7a82f49d73ff6cd27cf64a96" + "hash": "14f22dfa4eb01052c552ca6b05746c4b49b4766c0fd2d4144bcd5c252222d7e7" } diff --git a/.sqlx/query-4dcd43e0b78c8948ffe3e9cc13c094bd2da10e535164bece43fe1f52945c8910.json b/.sqlx/query-21a3c5d3ea54a1b78bb6012e59c49216864fb05871f92a87110e99a05ace14ee.json similarity index 91% rename from .sqlx/query-4dcd43e0b78c8948ffe3e9cc13c094bd2da10e535164bece43fe1f52945c8910.json rename to .sqlx/query-21a3c5d3ea54a1b78bb6012e59c49216864fb05871f92a87110e99a05ace14ee.json index 4f5acfa90f..3068a74aca 100644 --- a/.sqlx/query-4dcd43e0b78c8948ffe3e9cc13c094bd2da10e535164bece43fe1f52945c8910.json +++ b/.sqlx/query-21a3c5d3ea54a1b78bb6012e59c49216864fb05871f92a87110e99a05ace14ee.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT d.id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", configured FROM aclruledevice r JOIN device d ON d.id = r.device_id WHERE r.rule_id = $1 AND r.allow = true AND d.configured = true", + "query": "SELECT d.id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", configured FROM aclruledevice r JOIN device d ON d.id = r.device_id WHERE r.rule_id = $1 AND r.allow AND d.configured", "describe": { "columns": [ { @@ -70,5 +70,5 @@ false ] }, - "hash": "4dcd43e0b78c8948ffe3e9cc13c094bd2da10e535164bece43fe1f52945c8910" + "hash": "21a3c5d3ea54a1b78bb6012e59c49216864fb05871f92a87110e99a05ace14ee" } diff --git a/.sqlx/query-198f1ff51b4777d0727dae0613f642a4ef429df77c70bdf5e2ca50f30af3eda1.json b/.sqlx/query-26291ea69ff50ad4cc2a0ab014225da834839d7a4934331ea8d993e1043c359d.json similarity index 50% rename from .sqlx/query-198f1ff51b4777d0727dae0613f642a4ef429df77c70bdf5e2ca50f30af3eda1.json rename to .sqlx/query-26291ea69ff50ad4cc2a0ab014225da834839d7a4934331ea8d993e1043c359d.json index c90fbf57c7..38999f2582 100644 --- a/.sqlx/query-198f1ff51b4777d0727dae0613f642a4ef429df77c70bdf5e2ca50f30af3eda1.json +++ b/.sqlx/query-26291ea69ff50ad4cc2a0ab014225da834839d7a4934331ea8d993e1043c359d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT wn.name FROM wireguard_network wn JOIN wireguard_network_allowed_group wnag ON wn.id = wnag.network_id WHERE wnag.group_id = $1", + "query": "SELECT DISTINCT wn.name FROM wireguard_network wn LEFT JOIN wireguard_network_allowed_group wnag ON wn.id = wnag.network_id WHERE wn.allow_all_groups OR wnag.group_id = $1", "describe": { "columns": [ { @@ -18,5 +18,5 @@ false ] }, - "hash": "198f1ff51b4777d0727dae0613f642a4ef429df77c70bdf5e2ca50f30af3eda1" + "hash": "26291ea69ff50ad4cc2a0ab014225da834839d7a4934331ea8d993e1043c359d" } diff --git a/.sqlx/query-872e70c5006c45dc3502642444a4d00e5f8a5c23062392e1a60613936acfa07b.json b/.sqlx/query-40a3146c95620a9c4632b9529bc3eaf179e9b8ad4657a50cdbf19e3c599b5496.json similarity index 70% rename from .sqlx/query-872e70c5006c45dc3502642444a4d00e5f8a5c23062392e1a60613936acfa07b.json rename to .sqlx/query-40a3146c95620a9c4632b9529bc3eaf179e9b8ad4657a50cdbf19e3c599b5496.json index cf444f7a97..3ea92503c4 100644 --- a/.sqlx/query-872e70c5006c45dc3502642444a4d00e5f8a5c23062392e1a60613936acfa07b.json +++ b/.sqlx/query-40a3146c95620a9c4632b9529bc3eaf179e9b8ad4657a50cdbf19e3c599b5496.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM wireguard_network_allowed_group WHERE network_id=$1", + "query": "DELETE FROM wireguard_network_allowed_group WHERE network_id = $1", "describe": { "columns": [], "parameters": { @@ -10,5 +10,5 @@ }, "nullable": [] }, - "hash": "872e70c5006c45dc3502642444a4d00e5f8a5c23062392e1a60613936acfa07b" + "hash": "40a3146c95620a9c4632b9529bc3eaf179e9b8ad4657a50cdbf19e3c599b5496" } diff --git a/.sqlx/query-751bb201b4bbf3477c87f81a2fd77089d0a6773cebb138f77700cc052d1229aa.json b/.sqlx/query-54fada56be8b91633550c77f7259703bcc3163f4935898d0988a6045c29e7dd8.json similarity index 81% rename from .sqlx/query-751bb201b4bbf3477c87f81a2fd77089d0a6773cebb138f77700cc052d1229aa.json rename to .sqlx/query-54fada56be8b91633550c77f7259703bcc3163f4935898d0988a6045c29e7dd8.json index 71b6a42369..8ef5321bb0 100644 --- a/.sqlx/query-751bb201b4bbf3477c87f81a2fd77089d0a6773cebb138f77700cc052d1229aa.json +++ b/.sqlx/query-54fada56be8b91633550c77f7259703bcc3163f4935898d0988a6045c29e7dd8.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT d.wireguard_pubkey pubkey, preshared_key, -- TODO possible to not use ARRAY-unnest here?\n ARRAY(\n SELECT host(ip)\n FROM unnest(wnd.wireguard_ips) AS ip\n ) \"allowed_ips!: Vec\" FROM wireguard_network_device wnd JOIN device d ON wnd.device_id = d.id JOIN \"user\" u ON d.user_id = u.id WHERE wireguard_network_id = $1 AND (is_authorized = true OR NOT $2) AND d.configured = true AND u.is_active = true ORDER BY d.id ASC", + "query": "SELECT d.wireguard_pubkey pubkey, preshared_key, -- TODO possible to not use ARRAY-unnest here?\n ARRAY(\n SELECT host(ip)\n FROM unnest(wnd.wireguard_ips) AS ip\n ) \"allowed_ips!: Vec\" FROM wireguard_network_device wnd JOIN device d ON wnd.device_id = d.id JOIN \"user\" u ON d.user_id = u.id WHERE wireguard_network_id = $1 AND (is_authorized OR NOT $2) AND d.configured AND u.is_active ORDER BY d.id ASC", "describe": { "columns": [ { @@ -31,5 +31,5 @@ null ] }, - "hash": "751bb201b4bbf3477c87f81a2fd77089d0a6773cebb138f77700cc052d1229aa" + "hash": "54fada56be8b91633550c77f7259703bcc3163f4935898d0988a6045c29e7dd8" } diff --git a/.sqlx/query-785629b8de2268c1311fe27a5727e3e416d4e6d1a5f5097d92b1bd8ec446deb1.json b/.sqlx/query-60e52199fdb9ac82621147e70e463f9afa2f96d12730d0bff07945b8a67884e3.json similarity index 84% rename from .sqlx/query-785629b8de2268c1311fe27a5727e3e416d4e6d1a5f5097d92b1bd8ec446deb1.json rename to .sqlx/query-60e52199fdb9ac82621147e70e463f9afa2f96d12730d0bff07945b8a67884e3.json index 321e92ec75..cc3531c1ce 100644 --- a/.sqlx/query-785629b8de2268c1311fe27a5727e3e416d4e6d1a5f5097d92b1bd8ec446deb1.json +++ b/.sqlx/query-60e52199fdb9ac82621147e70e463f9afa2f96d12730d0bff07945b8a67884e3.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE name = $1", + "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, allow_all_groups, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE name = $1", "describe": { "columns": [ { @@ -60,31 +60,36 @@ }, { "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 12, + "ordinal": 13, "name": "keepalive_interval", "type_info": "Int4" }, { - "ordinal": 13, + "ordinal": 14, "name": "peer_disconnect_threshold", "type_info": "Int4" }, { - "ordinal": 14, + "ordinal": 15, "name": "acl_enabled", "type_info": "Bool" }, { - "ordinal": 15, + "ordinal": 16, "name": "acl_default_allow", "type_info": "Bool" }, { - "ordinal": 16, + "ordinal": 17, "name": "location_mfa_mode: LocationMfaMode", "type_info": { "Custom": { @@ -100,7 +105,7 @@ } }, { - "ordinal": 17, + "ordinal": 18, "name": "service_location_mode: ServiceLocationMode", "type_info": { "Custom": { @@ -133,6 +138,7 @@ false, false, false, + false, true, false, false, @@ -142,5 +148,5 @@ false ] }, - "hash": "785629b8de2268c1311fe27a5727e3e416d4e6d1a5f5097d92b1bd8ec446deb1" + "hash": "60e52199fdb9ac82621147e70e463f9afa2f96d12730d0bff07945b8a67884e3" } diff --git a/.sqlx/query-b6e20a2b66be63641872b921a963bf0e9a25cab905322cc1ada5e03f1b5076c7.json b/.sqlx/query-660bd0146db2168c27775429cd3557040f57dae496d43dea49289f9a5d907129.json similarity index 91% rename from .sqlx/query-b6e20a2b66be63641872b921a963bf0e9a25cab905322cc1ada5e03f1b5076c7.json rename to .sqlx/query-660bd0146db2168c27775429cd3557040f57dae496d43dea49289f9a5d907129.json index e5e735bf82..80471baa88 100644 --- a/.sqlx/query-b6e20a2b66be63641872b921a963bf0e9a25cab905322cc1ada5e03f1b5076c7.json +++ b/.sqlx/query-660bd0146db2168c27775429cd3557040f57dae496d43dea49289f9a5d907129.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT d.id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", configured FROM aclruledevice r JOIN device d ON d.id = r.device_id WHERE r.rule_id = $1 AND r.allow = false AND d.configured = true", + "query": "SELECT d.id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", configured FROM aclruledevice r JOIN device d ON d.id = r.device_id WHERE r.rule_id = $1 AND r.allow = false AND d.configured", "describe": { "columns": [ { @@ -70,5 +70,5 @@ false ] }, - "hash": "b6e20a2b66be63641872b921a963bf0e9a25cab905322cc1ada5e03f1b5076c7" + "hash": "660bd0146db2168c27775429cd3557040f57dae496d43dea49289f9a5d907129" } diff --git a/.sqlx/query-ddcf4b1bff7758d8ccd0ee7af140448b94154dabc60a5ea12dfd16e1920cf52b.json b/.sqlx/query-685781e7a9a4971124f3fda74cda168898beb739aa3ff7abf9e625918034c436.json similarity index 74% rename from .sqlx/query-ddcf4b1bff7758d8ccd0ee7af140448b94154dabc60a5ea12dfd16e1920cf52b.json rename to .sqlx/query-685781e7a9a4971124f3fda74cda168898beb739aa3ff7abf9e625918034c436.json index d93ff4d993..e96dbf50d6 100644 --- a/.sqlx/query-ddcf4b1bff7758d8ccd0ee7af140448b94154dabc60a5ea12dfd16e1920cf52b.json +++ b/.sqlx/query-685781e7a9a4971124f3fda74cda168898beb739aa3ff7abf9e625918034c436.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"wireguard_network\" (\"name\",\"address\",\"port\",\"pubkey\",\"prvkey\",\"endpoint\",\"dns\",\"mtu\",\"fwmark\",\"allowed_ips\",\"connected_at\",\"acl_enabled\",\"acl_default_allow\",\"keepalive_interval\",\"peer_disconnect_threshold\",\"location_mfa_mode\",\"service_location_mode\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17) RETURNING id", + "query": "INSERT INTO \"wireguard_network\" (\"name\",\"address\",\"port\",\"pubkey\",\"prvkey\",\"endpoint\",\"dns\",\"mtu\",\"fwmark\",\"allowed_ips\",\"allow_all_groups\",\"connected_at\",\"acl_enabled\",\"acl_default_allow\",\"keepalive_interval\",\"peer_disconnect_threshold\",\"location_mfa_mode\",\"service_location_mode\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18) RETURNING id", "describe": { "columns": [ { @@ -21,6 +21,7 @@ "Int4", "Int8", "InetArray", + "Bool", "Timestamp", "Bool", "Bool", @@ -56,5 +57,5 @@ false ] }, - "hash": "ddcf4b1bff7758d8ccd0ee7af140448b94154dabc60a5ea12dfd16e1920cf52b" + "hash": "685781e7a9a4971124f3fda74cda168898beb739aa3ff7abf9e625918034c436" } diff --git a/.sqlx/query-078285805ae4812c5c5bc57b4f748840cee8e29186fc476e2ef148688385c6a4.json b/.sqlx/query-68612652f823a456563f91e69f99a0cb01b856ebf973cca2dd6ab9ec46a98e33.json similarity index 76% rename from .sqlx/query-078285805ae4812c5c5bc57b4f748840cee8e29186fc476e2ef148688385c6a4.json rename to .sqlx/query-68612652f823a456563f91e69f99a0cb01b856ebf973cca2dd6ab9ec46a98e33.json index 03c79bcb7b..4a1d5f7ce9 100644 --- a/.sqlx/query-078285805ae4812c5c5bc57b4f748840cee8e29186fc476e2ef148688385c6a4.json +++ b/.sqlx/query-68612652f823a456563f91e69f99a0cb01b856ebf973cca2dd6ab9ec46a98e33.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"wireguard_network\" SET \"name\" = $2,\"address\" = $3,\"port\" = $4,\"pubkey\" = $5,\"prvkey\" = $6,\"endpoint\" = $7,\"dns\" = $8,\"mtu\" = $9,\"fwmark\" = $10,\"allowed_ips\" = $11,\"connected_at\" = $12,\"acl_enabled\" = $13,\"acl_default_allow\" = $14,\"keepalive_interval\" = $15,\"peer_disconnect_threshold\" = $16,\"location_mfa_mode\" = $17,\"service_location_mode\" = $18 WHERE id = $1", + "query": "UPDATE \"wireguard_network\" SET \"name\" = $2,\"address\" = $3,\"port\" = $4,\"pubkey\" = $5,\"prvkey\" = $6,\"endpoint\" = $7,\"dns\" = $8,\"mtu\" = $9,\"fwmark\" = $10,\"allowed_ips\" = $11,\"allow_all_groups\" = $12,\"connected_at\" = $13,\"acl_enabled\" = $14,\"acl_default_allow\" = $15,\"keepalive_interval\" = $16,\"peer_disconnect_threshold\" = $17,\"location_mfa_mode\" = $18,\"service_location_mode\" = $19 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -16,6 +16,7 @@ "Int4", "Int8", "InetArray", + "Bool", "Timestamp", "Bool", "Bool", @@ -49,5 +50,5 @@ }, "nullable": [] }, - "hash": "078285805ae4812c5c5bc57b4f748840cee8e29186fc476e2ef148688385c6a4" + "hash": "68612652f823a456563f91e69f99a0cb01b856ebf973cca2dd6ab9ec46a98e33" } diff --git a/.sqlx/query-68f067597d1577f4bc05dae41b78ae792fab1b5c4e9e9e7269449aee64c24bf8.json b/.sqlx/query-8c17a407e2ded35ca49180b1dc56e10ef743bc4ddbdd6bf7d801a782d2e03e1f.json similarity index 84% rename from .sqlx/query-68f067597d1577f4bc05dae41b78ae792fab1b5c4e9e9e7269449aee64c24bf8.json rename to .sqlx/query-8c17a407e2ded35ca49180b1dc56e10ef743bc4ddbdd6bf7d801a782d2e03e1f.json index ac27392654..e2b9337784 100644 --- a/.sqlx/query-68f067597d1577f4bc05dae41b78ae792fab1b5c4e9e9e7269449aee64c24bf8.json +++ b/.sqlx/query-8c17a407e2ded35ca49180b1dc56e10ef743bc4ddbdd6bf7d801a782d2e03e1f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"name\",\"address\" \"address: _\",\"port\",\"pubkey\",\"prvkey\",\"endpoint\",\"dns\",\"mtu\",\"fwmark\",\"allowed_ips\" \"allowed_ips: _\",\"connected_at\",\"acl_enabled\",\"acl_default_allow\",\"keepalive_interval\",\"peer_disconnect_threshold\",\"location_mfa_mode\" \"location_mfa_mode: _\",\"service_location_mode\" \"service_location_mode: _\" FROM \"wireguard_network\"", + "query": "SELECT id, \"name\",\"address\" \"address: _\",\"port\",\"pubkey\",\"prvkey\",\"endpoint\",\"dns\",\"mtu\",\"fwmark\",\"allowed_ips\" \"allowed_ips: _\",\"allow_all_groups\",\"connected_at\",\"acl_enabled\",\"acl_default_allow\",\"keepalive_interval\",\"peer_disconnect_threshold\",\"location_mfa_mode\" \"location_mfa_mode: _\",\"service_location_mode\" \"service_location_mode: _\" FROM \"wireguard_network\"", "describe": { "columns": [ { @@ -60,31 +60,36 @@ }, { "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 12, + "ordinal": 13, "name": "acl_enabled", "type_info": "Bool" }, { - "ordinal": 13, + "ordinal": 14, "name": "acl_default_allow", "type_info": "Bool" }, { - "ordinal": 14, + "ordinal": 15, "name": "keepalive_interval", "type_info": "Int4" }, { - "ordinal": 15, + "ordinal": 16, "name": "peer_disconnect_threshold", "type_info": "Int4" }, { - "ordinal": 16, + "ordinal": 17, "name": "location_mfa_mode: _", "type_info": { "Custom": { @@ -100,7 +105,7 @@ } }, { - "ordinal": 17, + "ordinal": 18, "name": "service_location_mode: _", "type_info": { "Custom": { @@ -131,6 +136,7 @@ false, false, false, + false, true, false, false, @@ -140,5 +146,5 @@ false ] }, - "hash": "68f067597d1577f4bc05dae41b78ae792fab1b5c4e9e9e7269449aee64c24bf8" + "hash": "8c17a407e2ded35ca49180b1dc56e10ef743bc4ddbdd6bf7d801a782d2e03e1f" } diff --git a/.sqlx/query-25b07b45cfd25643e750e2ec0fe51bd760fae6db5f5416d017a3f4a24c44c185.json b/.sqlx/query-97a72f3016ead166cb12b02a88df534fa5fb4148d65585ffa445e6b8faf5a30b.json similarity index 87% rename from .sqlx/query-25b07b45cfd25643e750e2ec0fe51bd760fae6db5f5416d017a3f4a24c44c185.json rename to .sqlx/query-97a72f3016ead166cb12b02a88df534fa5fb4148d65585ffa445e6b8faf5a30b.json index 5c8df41994..6beac724e4 100644 --- a/.sqlx/query-25b07b45cfd25643e750e2ec0fe51bd760fae6db5f5416d017a3f4a24c44c185.json +++ b/.sqlx/query-97a72f3016ead166cb12b02a88df534fa5fb4148d65585ffa445e6b8faf5a30b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT at.id, user_id, created_at, name, token_hash FROM api_token at JOIN \"user\" ON \"user\".id = user_id WHERE token_hash = $1 AND \"user\".is_active = true", + "query": "SELECT at.id, user_id, created_at, name, token_hash FROM api_token at JOIN \"user\" ON \"user\".id = user_id WHERE token_hash = $1 AND \"user\".is_active", "describe": { "columns": [ { @@ -42,5 +42,5 @@ false ] }, - "hash": "25b07b45cfd25643e750e2ec0fe51bd760fae6db5f5416d017a3f4a24c44c185" + "hash": "97a72f3016ead166cb12b02a88df534fa5fb4148d65585ffa445e6b8faf5a30b" } diff --git a/.sqlx/query-6e832a038605f8c13f0cf4f0cd215fa8db7959af23c59766e2a9e4af5233b726.json b/.sqlx/query-9b1906f583d613959baa6dbe348cc57742b3e19a2548d2631a72108d737c7c29.json similarity index 82% rename from .sqlx/query-6e832a038605f8c13f0cf4f0cd215fa8db7959af23c59766e2a9e4af5233b726.json rename to .sqlx/query-9b1906f583d613959baa6dbe348cc57742b3e19a2548d2631a72108d737c7c29.json index 58dff1139b..f814470dd0 100644 --- a/.sqlx/query-6e832a038605f8c13f0cf4f0cd215fa8db7959af23c59766e2a9e4af5233b726.json +++ b/.sqlx/query-9b1906f583d613959baa6dbe348cc57742b3e19a2548d2631a72108d737c7c29.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT n.id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM aclrulenetwork r JOIN wireguard_network n ON n.id = r.network_id WHERE r.rule_id = $1", + "query": "SELECT n.id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, allow_all_groups, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM aclrulenetwork r JOIN wireguard_network n ON n.id = r.network_id WHERE r.rule_id = $1", "describe": { "columns": [ { @@ -60,31 +60,36 @@ }, { "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 12, + "ordinal": 13, "name": "keepalive_interval", "type_info": "Int4" }, { - "ordinal": 13, + "ordinal": 14, "name": "peer_disconnect_threshold", "type_info": "Int4" }, { - "ordinal": 14, + "ordinal": 15, "name": "acl_enabled", "type_info": "Bool" }, { - "ordinal": 15, + "ordinal": 16, "name": "acl_default_allow", "type_info": "Bool" }, { - "ordinal": 16, + "ordinal": 17, "name": "location_mfa_mode: LocationMfaMode", "type_info": { "Custom": { @@ -100,7 +105,7 @@ } }, { - "ordinal": 17, + "ordinal": 18, "name": "service_location_mode: ServiceLocationMode", "type_info": { "Custom": { @@ -133,6 +138,7 @@ false, false, false, + false, true, false, false, @@ -142,5 +148,5 @@ false ] }, - "hash": "6e832a038605f8c13f0cf4f0cd215fa8db7959af23c59766e2a9e4af5233b726" + "hash": "9b1906f583d613959baa6dbe348cc57742b3e19a2548d2631a72108d737c7c29" } diff --git a/.sqlx/query-62d68324433b5bb49e3764ec4cfaeb531f58e2160dc579fbae7f99621ef7eb31.json b/.sqlx/query-9ef3bbc058409f578f68a6a186dc0d60a34a755bf3acbdbc5620ba88be00872d.json similarity index 66% rename from .sqlx/query-62d68324433b5bb49e3764ec4cfaeb531f58e2160dc579fbae7f99621ef7eb31.json rename to .sqlx/query-9ef3bbc058409f578f68a6a186dc0d60a34a755bf3acbdbc5620ba88be00872d.json index f0280ad6e3..8a50ebaf43 100644 --- a/.sqlx/query-62d68324433b5bb49e3764ec4cfaeb531f58e2160dc579fbae7f99621ef7eb31.json +++ b/.sqlx/query-9ef3bbc058409f578f68a6a186dc0d60a34a755bf3acbdbc5620ba88be00872d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT EXISTS (SELECT 1 FROM group_user gu LEFT JOIN \"group\" g ON gu.group_id = g.id WHERE is_admin = true AND user_id = $1) \"bool!\"", + "query": "SELECT EXISTS (SELECT 1 FROM group_user gu LEFT JOIN \"group\" g ON gu.group_id = g.id WHERE is_admin AND user_id = $1) \"bool!\"", "describe": { "columns": [ { @@ -18,5 +18,5 @@ null ] }, - "hash": "62d68324433b5bb49e3764ec4cfaeb531f58e2160dc579fbae7f99621ef7eb31" + "hash": "9ef3bbc058409f578f68a6a186dc0d60a34a755bf3acbdbc5620ba88be00872d" } diff --git a/.sqlx/query-11c5c9eaade29091b93d5d3e2fca6bea01b09ec20e7ff70e626488191509bcf4.json b/.sqlx/query-a7e7af2754523b08fae3a2b79557db912ffd435531786493413df45b255adae9.json similarity index 95% rename from .sqlx/query-11c5c9eaade29091b93d5d3e2fca6bea01b09ec20e7ff70e626488191509bcf4.json rename to .sqlx/query-a7e7af2754523b08fae3a2b79557db912ffd435531786493413df45b255adae9.json index c96dd9aff6..b1318d9070 100644 --- a/.sqlx/query-11c5c9eaade29091b93d5d3e2fca6bea01b09ec20e7ff70e626488191509bcf4.json +++ b/.sqlx/query-a7e7af2754523b08fae3a2b79557db912ffd435531786493413df45b255adae9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT u.id, u.username, u.password_hash, u.last_name, u.first_name, u.email, u.phone, u.mfa_enabled, u.totp_enabled, u.email_mfa_enabled, u.totp_secret, u.email_mfa_secret, u.mfa_method \"mfa_method: _\", u.recovery_codes, u.is_active, u.openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM \"user\" u WHERE EXISTS (SELECT 1 FROM group_user gu LEFT JOIN \"group\" g ON gu.group_id = g.id WHERE is_admin = true AND user_id = u.id) AND u.is_active = true", + "query": "\n SELECT u.id, u.username, u.password_hash, u.last_name, u.first_name, u.email, u.phone, u.mfa_enabled, u.totp_enabled, u.email_mfa_enabled, u.totp_secret, u.email_mfa_secret, u.mfa_method \"mfa_method: _\", u.recovery_codes, u.is_active, u.openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM \"user\" u WHERE EXISTS (SELECT 1 FROM group_user gu LEFT JOIN \"group\" g ON gu.group_id = g.id WHERE is_admin AND user_id = u.id) AND u.is_active", "describe": { "columns": [ { @@ -148,5 +148,5 @@ false ] }, - "hash": "11c5c9eaade29091b93d5d3e2fca6bea01b09ec20e7ff70e626488191509bcf4" + "hash": "a7e7af2754523b08fae3a2b79557db912ffd435531786493413df45b255adae9" } diff --git a/.sqlx/query-4d28fb2ad919eafeefd19e2796ce6db89b7e0afa4f7301eb6e2c9fefe604042d.json b/.sqlx/query-ab17ac33cd462975a6e8975d4a8cc6700f42030c507917ef9fb3acf3523a802b.json similarity index 96% rename from .sqlx/query-4d28fb2ad919eafeefd19e2796ce6db89b7e0afa4f7301eb6e2c9fefe604042d.json rename to .sqlx/query-ab17ac33cd462975a6e8975d4a8cc6700f42030c507917ef9fb3acf3523a802b.json index 790bbc027c..f5424ec86f 100644 --- a/.sqlx/query-4d28fb2ad919eafeefd19e2796ce6db89b7e0afa4f7301eb6e2c9fefe604042d.json +++ b/.sqlx/query-ab17ac33cd462975a6e8975d4a8cc6700f42030c507917ef9fb3acf3523a802b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM \"user\" WHERE is_active = true", + "query": "SELECT id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM \"user\" WHERE is_active", "describe": { "columns": [ { @@ -148,5 +148,5 @@ false ] }, - "hash": "4d28fb2ad919eafeefd19e2796ce6db89b7e0afa4f7301eb6e2c9fefe604042d" + "hash": "ab17ac33cd462975a6e8975d4a8cc6700f42030c507917ef9fb3acf3523a802b" } diff --git a/.sqlx/query-c5691cac4edea09b4cfabd1105be053bb4bb030489f8084707fa0225a2bddce6.json b/.sqlx/query-b51c57865a5585c28061462655fbcd5a6f22b46090aff2bfe2af5f04cee4fcb4.json similarity index 88% rename from .sqlx/query-c5691cac4edea09b4cfabd1105be053bb4bb030489f8084707fa0225a2bddce6.json rename to .sqlx/query-b51c57865a5585c28061462655fbcd5a6f22b46090aff2bfe2af5f04cee4fcb4.json index 920e9824ef..801c746662 100644 --- a/.sqlx/query-c5691cac4edea09b4cfabd1105be053bb4bb030489f8084707fa0225a2bddce6.json +++ b/.sqlx/query-b51c57865a5585c28061462655fbcd5a6f22b46090aff2bfe2af5f04cee4fcb4.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured FROM device d JOIN \"user\" u ON d.user_id = u.id WHERE u.is_active = true AND d.device_type = 'user'::device_type AND d.user_id = $1 ORDER BY d.id ASC", + "query": "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured FROM device d JOIN \"user\" u ON d.user_id = u.id WHERE u.is_active AND d.device_type = 'user'::device_type AND d.user_id = $1 ORDER BY d.id ASC", "describe": { "columns": [ { @@ -70,5 +70,5 @@ false ] }, - "hash": "c5691cac4edea09b4cfabd1105be053bb4bb030489f8084707fa0225a2bddce6" + "hash": "b51c57865a5585c28061462655fbcd5a6f22b46090aff2bfe2af5f04cee4fcb4" } diff --git a/.sqlx/query-e8d80473ffc42c6febc3e81ca9c7e9b1b7f5040b0dfb3d31f60d2bcbdc06dfae.json b/.sqlx/query-bd0af2042bc33fdfc894514961af7a2b5bc013082b961d1f667b5fe55a7f996a.json similarity index 83% rename from .sqlx/query-e8d80473ffc42c6febc3e81ca9c7e9b1b7f5040b0dfb3d31f60d2bcbdc06dfae.json rename to .sqlx/query-bd0af2042bc33fdfc894514961af7a2b5bc013082b961d1f667b5fe55a7f996a.json index c4ae7b9d3a..22f6cd3398 100644 --- a/.sqlx/query-e8d80473ffc42c6febc3e81ca9c7e9b1b7f5040b0dfb3d31f60d2bcbdc06dfae.json +++ b/.sqlx/query-bd0af2042bc33fdfc894514961af7a2b5bc013082b961d1f667b5fe55a7f996a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE location_mfa_mode = 'external'::location_mfa_mode", + "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, allow_all_groups, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE location_mfa_mode = 'external'::location_mfa_mode", "describe": { "columns": [ { @@ -60,31 +60,36 @@ }, { "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 12, + "ordinal": 13, "name": "keepalive_interval", "type_info": "Int4" }, { - "ordinal": 13, + "ordinal": 14, "name": "peer_disconnect_threshold", "type_info": "Int4" }, { - "ordinal": 14, + "ordinal": 15, "name": "acl_enabled", "type_info": "Bool" }, { - "ordinal": 15, + "ordinal": 16, "name": "acl_default_allow", "type_info": "Bool" }, { - "ordinal": 16, + "ordinal": 17, "name": "location_mfa_mode: LocationMfaMode", "type_info": { "Custom": { @@ -100,7 +105,7 @@ } }, { - "ordinal": 17, + "ordinal": 18, "name": "service_location_mode: ServiceLocationMode", "type_info": { "Custom": { @@ -131,6 +136,7 @@ false, false, false, + false, true, false, false, @@ -140,5 +146,5 @@ false ] }, - "hash": "e8d80473ffc42c6febc3e81ca9c7e9b1b7f5040b0dfb3d31f60d2bcbdc06dfae" + "hash": "bd0af2042bc33fdfc894514961af7a2b5bc013082b961d1f667b5fe55a7f996a" } diff --git a/.sqlx/query-28a9206e4aef6a3d61e8712164419429e4fc7c1fee0da935845977634d730bec.json b/.sqlx/query-c58c7b4dc7463a93895b17d591e3e4a83ac3623590674e47bb1a1dbf9c25d77f.json similarity index 84% rename from .sqlx/query-28a9206e4aef6a3d61e8712164419429e4fc7c1fee0da935845977634d730bec.json rename to .sqlx/query-c58c7b4dc7463a93895b17d591e3e4a83ac3623590674e47bb1a1dbf9c25d77f.json index b14175e6e9..a38c619ecf 100644 --- a/.sqlx/query-28a9206e4aef6a3d61e8712164419429e4fc7c1fee0da935845977634d730bec.json +++ b/.sqlx/query-c58c7b4dc7463a93895b17d591e3e4a83ac3623590674e47bb1a1dbf9c25d77f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE id = $1", + "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, allow_all_groups, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE id = $1", "describe": { "columns": [ { @@ -60,31 +60,36 @@ }, { "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 12, + "ordinal": 13, "name": "keepalive_interval", "type_info": "Int4" }, { - "ordinal": 13, + "ordinal": 14, "name": "peer_disconnect_threshold", "type_info": "Int4" }, { - "ordinal": 14, + "ordinal": 15, "name": "acl_enabled", "type_info": "Bool" }, { - "ordinal": 15, + "ordinal": 16, "name": "acl_default_allow", "type_info": "Bool" }, { - "ordinal": 16, + "ordinal": 17, "name": "location_mfa_mode: LocationMfaMode", "type_info": { "Custom": { @@ -100,7 +105,7 @@ } }, { - "ordinal": 17, + "ordinal": 18, "name": "service_location_mode: ServiceLocationMode", "type_info": { "Custom": { @@ -133,6 +138,7 @@ false, false, false, + false, true, false, false, @@ -142,5 +148,5 @@ false ] }, - "hash": "28a9206e4aef6a3d61e8712164419429e4fc7c1fee0da935845977634d730bec" + "hash": "c58c7b4dc7463a93895b17d591e3e4a83ac3623590674e47bb1a1dbf9c25d77f" } diff --git a/.sqlx/query-cd6a0b78b618518ac4c1b644c3d1bd3ffabd5ad1ed9a260ca3a821c9a187b7d3.json b/.sqlx/query-d0f1dff40c9ebff747315a9230ca76689c92a45a6ead1f7b821ca680f8ac7055.json similarity index 84% rename from .sqlx/query-cd6a0b78b618518ac4c1b644c3d1bd3ffabd5ad1ed9a260ca3a821c9a187b7d3.json rename to .sqlx/query-d0f1dff40c9ebff747315a9230ca76689c92a45a6ead1f7b821ca680f8ac7055.json index 42e1a761c5..ca93a5f9d9 100644 --- a/.sqlx/query-cd6a0b78b618518ac4c1b644c3d1bd3ffabd5ad1ed9a260ca3a821c9a187b7d3.json +++ b/.sqlx/query-d0f1dff40c9ebff747315a9230ca76689c92a45a6ead1f7b821ca680f8ac7055.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"name\",\"address\" \"address: _\",\"port\",\"pubkey\",\"prvkey\",\"endpoint\",\"dns\",\"mtu\",\"fwmark\",\"allowed_ips\" \"allowed_ips: _\",\"connected_at\",\"acl_enabled\",\"acl_default_allow\",\"keepalive_interval\",\"peer_disconnect_threshold\",\"location_mfa_mode\" \"location_mfa_mode: _\",\"service_location_mode\" \"service_location_mode: _\" FROM \"wireguard_network\" WHERE id = $1", + "query": "SELECT id, \"name\",\"address\" \"address: _\",\"port\",\"pubkey\",\"prvkey\",\"endpoint\",\"dns\",\"mtu\",\"fwmark\",\"allowed_ips\" \"allowed_ips: _\",\"allow_all_groups\",\"connected_at\",\"acl_enabled\",\"acl_default_allow\",\"keepalive_interval\",\"peer_disconnect_threshold\",\"location_mfa_mode\" \"location_mfa_mode: _\",\"service_location_mode\" \"service_location_mode: _\" FROM \"wireguard_network\" WHERE id = $1", "describe": { "columns": [ { @@ -60,31 +60,36 @@ }, { "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 12, + "ordinal": 13, "name": "acl_enabled", "type_info": "Bool" }, { - "ordinal": 13, + "ordinal": 14, "name": "acl_default_allow", "type_info": "Bool" }, { - "ordinal": 14, + "ordinal": 15, "name": "keepalive_interval", "type_info": "Int4" }, { - "ordinal": 15, + "ordinal": 16, "name": "peer_disconnect_threshold", "type_info": "Int4" }, { - "ordinal": 16, + "ordinal": 17, "name": "location_mfa_mode: _", "type_info": { "Custom": { @@ -100,7 +105,7 @@ } }, { - "ordinal": 17, + "ordinal": 18, "name": "service_location_mode: _", "type_info": { "Custom": { @@ -133,6 +138,7 @@ false, false, false, + false, true, false, false, @@ -142,5 +148,5 @@ false ] }, - "hash": "cd6a0b78b618518ac4c1b644c3d1bd3ffabd5ad1ed9a260ca3a821c9a187b7d3" + "hash": "d0f1dff40c9ebff747315a9230ca76689c92a45a6ead1f7b821ca680f8ac7055" } diff --git a/.sqlx/query-2e2c6207e4ab947bc299a3c468c90eb7f330027affb1ea08f999a766ad3279f3.json b/.sqlx/query-d77e4f1215408d6ac39fa8315dded8326e6d79958b6fdfe9252aefa00b81c375.json similarity index 66% rename from .sqlx/query-2e2c6207e4ab947bc299a3c468c90eb7f330027affb1ea08f999a766ad3279f3.json rename to .sqlx/query-d77e4f1215408d6ac39fa8315dded8326e6d79958b6fdfe9252aefa00b81c375.json index 1670ba21dc..b016166082 100644 --- a/.sqlx/query-2e2c6207e4ab947bc299a3c468c90eb7f330027affb1ea08f999a766ad3279f3.json +++ b/.sqlx/query-d77e4f1215408d6ac39fa8315dded8326e6d79958b6fdfe9252aefa00b81c375.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "Update wizard SET migration_wizard_state = NULL WHERE is_singleton = TRUE", + "query": "Update wizard SET migration_wizard_state = NULL WHERE is_singleton", "describe": { "columns": [], "parameters": { @@ -8,5 +8,5 @@ }, "nullable": [] }, - "hash": "2e2c6207e4ab947bc299a3c468c90eb7f330027affb1ea08f999a766ad3279f3" + "hash": "d77e4f1215408d6ac39fa8315dded8326e6d79958b6fdfe9252aefa00b81c375" } diff --git a/.sqlx/query-b5598af6a2b352c73c4bdf5b93f21042ab6b05212938918eebe43cd56795ac76.json b/.sqlx/query-eabe841f211c8fab042d3dbb0166610188ce73aa66e76726aeaedfbb4ecf3290.json similarity index 96% rename from .sqlx/query-b5598af6a2b352c73c4bdf5b93f21042ab6b05212938918eebe43cd56795ac76.json rename to .sqlx/query-eabe841f211c8fab042d3dbb0166610188ce73aa66e76726aeaedfbb4ecf3290.json index 88a86ecca5..18047abbe1 100644 --- a/.sqlx/query-b5598af6a2b352c73c4bdf5b93f21042ab6b05212938918eebe43cd56795ac76.json +++ b/.sqlx/query-eabe841f211c8fab042d3dbb0166610188ce73aa66e76726aeaedfbb4ecf3290.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT u.id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM aclruleuser r JOIN \"user\" u ON u.id = r.user_id WHERE r.rule_id = $1 AND NOT r.allow AND u.is_active = true", + "query": "SELECT u.id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, totp_enabled, totp_secret, email_mfa_enabled, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending FROM aclruleuser r JOIN \"user\" u ON u.id = r.user_id WHERE r.rule_id = $1 AND r.allow AND u.is_active", "describe": { "columns": [ { @@ -150,5 +150,5 @@ false ] }, - "hash": "b5598af6a2b352c73c4bdf5b93f21042ab6b05212938918eebe43cd56795ac76" + "hash": "eabe841f211c8fab042d3dbb0166610188ce73aa66e76726aeaedfbb4ecf3290" } diff --git a/.sqlx/query-90cf3926dd7449fd8cd164871c63919b74f05a8797db690ccb3b5a8534415cfb.json b/.sqlx/query-eedf2266dbae38e45b9bc4e9a41352324d9d3d567b4c35957997c0b52092cfe1.json similarity index 81% rename from .sqlx/query-90cf3926dd7449fd8cd164871c63919b74f05a8797db690ccb3b5a8534415cfb.json rename to .sqlx/query-eedf2266dbae38e45b9bc4e9a41352324d9d3d567b4c35957997c0b52092cfe1.json index 648db1840f..a79bfc3407 100644 --- a/.sqlx/query-90cf3926dd7449fd8cd164871c63919b74f05a8797db690ccb3b5a8534415cfb.json +++ b/.sqlx/query-eedf2266dbae38e45b9bc4e9a41352324d9d3d567b4c35957997c0b52092cfe1.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE id IN (SELECT wireguard_network_id FROM wireguard_network_device WHERE device_id = $1 ORDER BY id LIMIT 1)", + "query": "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, allowed_ips, allow_all_groups, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", service_location_mode \"service_location_mode: ServiceLocationMode\" FROM wireguard_network WHERE id IN (SELECT wireguard_network_id FROM wireguard_network_device WHERE device_id = $1 ORDER BY id LIMIT 1)", "describe": { "columns": [ { @@ -60,31 +60,36 @@ }, { "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 12, + "ordinal": 13, "name": "keepalive_interval", "type_info": "Int4" }, { - "ordinal": 13, + "ordinal": 14, "name": "peer_disconnect_threshold", "type_info": "Int4" }, { - "ordinal": 14, + "ordinal": 15, "name": "acl_enabled", "type_info": "Bool" }, { - "ordinal": 15, + "ordinal": 16, "name": "acl_default_allow", "type_info": "Bool" }, { - "ordinal": 16, + "ordinal": 17, "name": "location_mfa_mode: LocationMfaMode", "type_info": { "Custom": { @@ -100,7 +105,7 @@ } }, { - "ordinal": 17, + "ordinal": 18, "name": "service_location_mode: ServiceLocationMode", "type_info": { "Custom": { @@ -133,6 +138,7 @@ false, false, false, + false, true, false, false, @@ -142,5 +148,5 @@ false ] }, - "hash": "90cf3926dd7449fd8cd164871c63919b74f05a8797db690ccb3b5a8534415cfb" + "hash": "eedf2266dbae38e45b9bc4e9a41352324d9d3d567b4c35957997c0b52092cfe1" } diff --git a/.sqlx/query-a2321eb640aa9302dd1ca51498a5d9c6147ceca80eb638037e548fb821c12968.json b/.sqlx/query-f475c5922df8b773a594e80e252a456bd6e531036a0046ba87f5ea9834d88a8f.json similarity index 87% rename from .sqlx/query-a2321eb640aa9302dd1ca51498a5d9c6147ceca80eb638037e548fb821c12968.json rename to .sqlx/query-f475c5922df8b773a594e80e252a456bd6e531036a0046ba87f5ea9834d88a8f.json index aaedff761e..e20c99c946 100644 --- a/.sqlx/query-a2321eb640aa9302dd1ca51498a5d9c6147ceca80eb638037e548fb821c12968.json +++ b/.sqlx/query-f475c5922df8b773a594e80e252a456bd6e531036a0046ba87f5ea9834d88a8f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured\n FROM device d JOIN \"user\" u ON d.user_id = u.id JOIN group_user gu ON u.id = gu.user_id JOIN \"group\" g ON gu.group_id = g.id WHERE g.\"name\" IN (SELECT * FROM UNNEST($1::text[])) AND u.is_active = true AND d.device_type = 'user'::device_type AND d.user_id = $2 ORDER BY d.id ASC", + "query": "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, d.device_type \"device_type: DeviceType\", configured\n FROM device d JOIN \"user\" u ON d.user_id = u.id JOIN group_user gu ON u.id = gu.user_id JOIN \"group\" g ON gu.group_id = g.id WHERE g.\"name\" IN (SELECT * FROM UNNEST($1::text[])) AND u.is_active AND d.device_type = 'user'::device_type AND d.user_id = $2 ORDER BY d.id ASC", "describe": { "columns": [ { @@ -71,5 +71,5 @@ false ] }, - "hash": "a2321eb640aa9302dd1ca51498a5d9c6147ceca80eb638037e548fb821c12968" + "hash": "f475c5922df8b773a594e80e252a456bd6e531036a0046ba87f5ea9834d88a8f" } diff --git a/.sqlx/query-20cf5e3e2aa6859d46ad2c1f0d5ad6ce698febb100b7d31cabc81fd8cfa5461a.json b/.sqlx/query-f946b5af07deecd30171260b8b760c27581ac94753f81a529b7814c23748d3db.json similarity index 92% rename from .sqlx/query-20cf5e3e2aa6859d46ad2c1f0d5ad6ce698febb100b7d31cabc81fd8cfa5461a.json rename to .sqlx/query-f946b5af07deecd30171260b8b760c27581ac94753f81a529b7814c23748d3db.json index 438dc8eb92..8282e5f00f 100644 --- a/.sqlx/query-20cf5e3e2aa6859d46ad2c1f0d5ad6ce698febb100b7d31cabc81fd8cfa5461a.json +++ b/.sqlx/query-f946b5af07deecd30171260b8b760c27581ac94753f81a529b7814c23748d3db.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT d.id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", configured FROM device d JOIN wireguard_network_device wnd ON d.id = wnd.device_id WHERE device_type = 'network'::device_type AND configured = true AND wireguard_network_id = $1", + "query": "SELECT d.id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", configured FROM device d JOIN wireguard_network_device wnd ON d.id = wnd.device_id WHERE device_type = 'network'::device_type AND configured AND wireguard_network_id = $1", "describe": { "columns": [ { @@ -70,5 +70,5 @@ false ] }, - "hash": "20cf5e3e2aa6859d46ad2c1f0d5ad6ce698febb100b7d31cabc81fd8cfa5461a" + "hash": "f946b5af07deecd30171260b8b760c27581ac94753f81a529b7814c23748d3db" } diff --git a/Cargo.lock b/Cargo.lock index fc5217cc04..8492e1cf12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,9 +122,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -143,9 +143,9 @@ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -833,9 +833,9 @@ checksum = "bba18ee93d577a8428902687bcc2b6b45a56b1981a1f6d779731c86cc4c5db18" [[package]] name = "clap" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -843,9 +843,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -855,9 +855,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", @@ -867,9 +867,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmac" @@ -3986,9 +3986,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -4035,9 +4035,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" dependencies = [ "bitflags 2.11.0", "cfg-if", @@ -4067,9 +4067,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" dependencies = [ "cc", "libc", diff --git a/crates/defguard_common/src/db/models/device.rs b/crates/defguard_common/src/db/models/device.rs index 65087a3408..1d98805009 100644 --- a/crates/defguard_common/src/db/models/device.rs +++ b/crates/defguard_common/src/db/models/device.rs @@ -502,8 +502,9 @@ impl WireguardNetworkDevice { query_as!( WireguardNetwork, "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, \ - allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, \ - acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ + allowed_ips, allow_all_groups, connected_at, keepalive_interval, \ + peer_disconnect_threshold, acl_enabled, acl_default_allow, \ + location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ service_location_mode \"service_location_mode: ServiceLocationMode\" \ FROM wireguard_network WHERE id = $1", self.wireguard_network_id @@ -966,11 +967,13 @@ impl Device { query_as!( WireguardNetwork, "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, \ - allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, \ - acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ + allowed_ips, allow_all_groups, connected_at, keepalive_interval, \ + peer_disconnect_threshold, acl_enabled, acl_default_allow, \ + location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ service_location_mode \"service_location_mode: ServiceLocationMode\" \ FROM wireguard_network WHERE id IN \ - (SELECT wireguard_network_id FROM wireguard_network_device WHERE device_id = $1 ORDER BY id LIMIT 1)", + (SELECT wireguard_network_id FROM wireguard_network_device \ + WHERE device_id = $1 ORDER BY id LIMIT 1)", self.id ) .fetch_all(executor) @@ -1435,11 +1438,15 @@ mod test { .await .unwrap(); - let mut network = WireguardNetwork::default(); + let mut network = WireguardNetwork:: { + allow_all_groups: true, + ..Default::default() + }; network.try_set_address("10.1.1.1/24").unwrap(); let network = network.save(&pool).await.unwrap(); let mut network_2 = WireguardNetwork:: { name: "testnetwork2".into(), + allow_all_groups: true, ..Default::default() }; network_2.try_set_address("10.1.2.1/24").unwrap(); @@ -1542,6 +1549,7 @@ mod test { let network = WireguardNetwork:: { address: vec![IpNetwork::new(IpAddr::V4(Ipv4Addr::new(192, 168, 42, 4)), 29).unwrap()], + allow_all_groups: true, ..Default::default() } .save(&pool) diff --git a/crates/defguard_common/src/db/models/group.rs b/crates/defguard_common/src/db/models/group.rs index e6f65e19f3..e875f14c1f 100644 --- a/crates/defguard_common/src/db/models/group.rs +++ b/crates/defguard_common/src/db/models/group.rs @@ -7,7 +7,6 @@ use utoipa::ToSchema; use crate::db::{Id, NoId, models::user::User}; -#[derive(Debug)] pub enum Permission { IsAdmin, } @@ -94,16 +93,15 @@ impl Group { .await } - /// Fetches a list of VPN locations where a given group is explicitly allowed. - /// This does not include VPN locations where all groups are implicitly allowed (admin group), - /// because no access control in configured. + /// Fetches a list of VPN locations where a given group is allowed. pub async fn allowed_vpn_locations<'e, E>(&self, executor: E) -> Result, SqlxError> where E: PgExecutor<'e>, { query_scalar!( - "SELECT wn.name FROM wireguard_network wn JOIN wireguard_network_allowed_group wnag ON wn.id = wnag.network_id \ - WHERE wnag.group_id = $1", + "SELECT DISTINCT wn.name FROM wireguard_network wn \ + LEFT JOIN wireguard_network_allowed_group wnag ON wn.id = wnag.network_id \ + WHERE wn.allow_all_groups OR wnag.group_id = $1", self.id ) .fetch_all(executor) @@ -117,9 +115,8 @@ impl Group { where E: PgExecutor<'e>, { - let query = format!( - "SELECT id, name, is_admin FROM \"group\" WHERE {permission} = TRUE ORDER BY id" - ); + let query = + format!("SELECT id, name, is_admin FROM \"group\" WHERE {permission} ORDER BY id"); query_as(&query).fetch_all(executor).await } diff --git a/crates/defguard_common/src/db/models/initial_setup_wizard.rs b/crates/defguard_common/src/db/models/initial_setup_wizard.rs index eadd89046e..b4f9b8c410 100644 --- a/crates/defguard_common/src/db/models/initial_setup_wizard.rs +++ b/crates/defguard_common/src/db/models/initial_setup_wizard.rs @@ -46,7 +46,7 @@ impl InitialSetupState { query( "UPDATE wizard SET initial_setup_state = $1 - WHERE is_singleton = TRUE", + WHERE is_singleton", ) .bind(initial_setup_state) .execute(executor) @@ -62,7 +62,7 @@ impl InitialSetupState { let state: Option> = query_scalar( "SELECT initial_setup_state FROM wizard - WHERE is_singleton = TRUE + WHERE is_singleton LIMIT 1", ) .fetch_one(executor) @@ -78,7 +78,7 @@ impl InitialSetupState { query( "UPDATE wizard SET initial_setup_state = NULL - WHERE is_singleton = TRUE", + WHERE is_singleton", ) .execute(executor) .await?; diff --git a/crates/defguard_common/src/db/models/migration_wizard.rs b/crates/defguard_common/src/db/models/migration_wizard.rs index c05c5aed37..3415a2f58d 100644 --- a/crates/defguard_common/src/db/models/migration_wizard.rs +++ b/crates/defguard_common/src/db/models/migration_wizard.rs @@ -64,7 +64,7 @@ impl MigrationWizardState { sqlx::query( "UPDATE wizard SET migration_wizard_state = $1 - WHERE is_singleton = TRUE", + WHERE is_singleton", ) .bind(state) .execute(executor) @@ -80,7 +80,7 @@ impl MigrationWizardState { sqlx::query!( "Update wizard \ SET migration_wizard_state = NULL \ - WHERE is_singleton = TRUE" + WHERE is_singleton" ) .execute(executor) .await?; diff --git a/crates/defguard_common/src/db/models/setup_auto_adoption.rs b/crates/defguard_common/src/db/models/setup_auto_adoption.rs index 5580e2dd6f..12a815557e 100644 --- a/crates/defguard_common/src/db/models/setup_auto_adoption.rs +++ b/crates/defguard_common/src/db/models/setup_auto_adoption.rs @@ -49,7 +49,7 @@ impl AutoAdoptionWizardState { query( "UPDATE wizard SET auto_adoption_state = $1 - WHERE is_singleton = TRUE", + WHERE is_singleton", ) .bind(auto_adoption_state) .execute(executor) @@ -65,7 +65,7 @@ impl AutoAdoptionWizardState { let state: Option> = query_scalar( "SELECT auto_adoption_state FROM wizard - WHERE is_singleton = TRUE + WHERE is_singleton LIMIT 1", ) .fetch_one(executor) @@ -81,7 +81,7 @@ impl AutoAdoptionWizardState { query( "UPDATE wizard SET auto_adoption_state = NULL - WHERE is_singleton = TRUE", + WHERE is_singleton", ) .execute(executor) .await?; diff --git a/crates/defguard_common/src/db/models/user.rs b/crates/defguard_common/src/db/models/user.rs index 3d6f8d50e7..ca9683579c 100644 --- a/crates/defguard_common/src/db/models/user.rs +++ b/crates/defguard_common/src/db/models/user.rs @@ -630,7 +630,7 @@ impl User { mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, from_ldap, \ ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending \ FROM \"user\" \ - WHERE is_active = true" + WHERE is_active" ) .fetch_all(executor) .await @@ -1135,7 +1135,7 @@ impl User { E: PgExecutor<'e>, { query_scalar!("SELECT EXISTS (SELECT 1 FROM group_user gu LEFT JOIN \"group\" g ON gu.group_id = g.id \ - WHERE is_admin = true AND user_id = $1) \"bool!\"", self.id) + WHERE is_admin AND user_id = $1) \"bool!\"", self.id) .fetch_one(executor) .await } @@ -1154,7 +1154,7 @@ impl User { from_ldap, ldap_pass_randomized, ldap_rdn, ldap_user_path, enrollment_pending \ FROM \"user\" u \ WHERE EXISTS (SELECT 1 FROM group_user gu LEFT JOIN \"group\" g ON gu.group_id = g.id \ - WHERE is_admin = true AND user_id = u.id) AND u.is_active = true" + WHERE is_admin AND user_id = u.id) AND u.is_active" ) .fetch_all(executor) .await diff --git a/crates/defguard_common/src/db/models/wireguard.rs b/crates/defguard_common/src/db/models/wireguard.rs index 23c20d659c..bc893b0d5f 100644 --- a/crates/defguard_common/src/db/models/wireguard.rs +++ b/crates/defguard_common/src/db/models/wireguard.rs @@ -41,7 +41,7 @@ use crate::{ pub const DEFAULT_KEEPALIVE_INTERVAL: i32 = 25; pub const DEFAULT_DISCONNECT_THRESHOLD: i32 = 300; /// Default MTU for WireGuard interfaces. -pub const DEFAULT_WIREGUARD_MTU: i32 = 1420; // TODO: change to u32 once sqlx unsigned integers. +pub const DEFAULT_WIREGUARD_MTU: i32 = 1420; // TODO: use u32 once sqlx supports unsigned integers. // Used in process of importing network from WireGuard config. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -121,6 +121,7 @@ pub struct WireguardNetwork { #[model(ref)] #[schema(value_type = Vec)] pub allowed_ips: Vec, + pub allow_all_groups: bool, pub connected_at: Option, pub acl_enabled: bool, pub acl_default_allow: bool, @@ -161,6 +162,7 @@ impl fmt::Debug for WireguardNetwork { .field("endpoint", &self.endpoint) .field("dns", &self.dns) .field("allowed_ips", &self.allowed_ips) + .field("allow_all_groups", &self.allow_all_groups) .field("connected_at", &self.connected_at) .field("acl_enabled", &self.acl_enabled) .field("acl_default_allow", &self.acl_default_allow) @@ -222,6 +224,7 @@ impl WireguardNetwork { mtu: i32, fwmark: i64, allowed_ips: Vec, + allow_all_groups: bool, keepalive_interval: i32, peer_disconnect_threshold: i32, acl_enabled: bool, @@ -243,6 +246,7 @@ impl WireguardNetwork { mtu, fwmark, allowed_ips, + allow_all_groups, connected_at: None, keepalive_interval, peer_disconnect_threshold, @@ -273,7 +277,7 @@ impl WireguardNetwork { let networks = query_as!( WireguardNetwork, "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, \ - allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, \ + allowed_ips, allow_all_groups, connected_at, keepalive_interval, peer_disconnect_threshold, \ acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ service_location_mode \"service_location_mode: ServiceLocationMode\" \ FROM wireguard_network WHERE name = $1", @@ -331,42 +335,40 @@ impl WireguardNetwork { &self, transaction: &mut PgConnection, ) -> Result>, ModelError> { - debug!("Fetching all allowed devices for network {}", self); - let devices = - match self.get_allowed_groups(&mut *transaction).await? { - // devices need to be filtered by allowed group - Some(allowed_groups) => { - query_as!( + debug!("Fetching all allowed devices for network {self}"); + if self.allow_all_groups { + return query_as!( Device, - "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, \ + "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, \ + d.device_type \"device_type: DeviceType\", configured \ + FROM device d \ + JOIN \"user\" u ON d.user_id = u.id \ + WHERE u.is_active \ + AND d.device_type = 'user'::device_type \ + ORDER BY d.id ASC" + ) + .fetch_all(&mut *transaction) + .await + .map_err(Into::into); + } + + let allowed_groups = self.get_allowed_groups(&mut *transaction).await?; + let devices = query_as!( + Device, + "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, \ d.description, d.device_type \"device_type: DeviceType\", configured FROM device d \ JOIN \"user\" u ON d.user_id = u.id \ JOIN group_user gu ON u.id = gu.user_id \ JOIN \"group\" g ON gu.group_id = g.id \ WHERE g.\"name\" IN (SELECT * FROM UNNEST($1::text[])) \ - AND u.is_active = true \ + AND u.is_active \ AND d.device_type = 'user'::device_type \ ORDER BY d.id ASC", - &allowed_groups - ) - .fetch_all(&mut *transaction) - .await? - } - // all devices of enabled users are allowed - None => query_as!( - Device, - "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, \ - d.device_type \"device_type: DeviceType\", configured \ - FROM device d \ - JOIN \"user\" u ON d.user_id = u.id \ - WHERE u.is_active = true \ - AND d.device_type = 'user'::device_type \ - ORDER BY d.id ASC" - ) - .fetch_all(&mut *transaction) - .await?, - }; + &allowed_groups + ) + .fetch_all(&mut *transaction) + .await?; Ok(devices) } @@ -379,44 +381,43 @@ impl WireguardNetwork { user_id: Id, ) -> Result>, ModelError> { debug!("Fetching all allowed devices for network {self}, user ID {user_id}"); - let devices = - match self.get_allowed_groups(&mut *transaction).await? { - // devices need to be filtered by allowed group - Some(allowed_groups) => { - query_as!( + if self.allow_all_groups { + return query_as!( Device, - "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, \ + "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, \ + d.device_type \"device_type: DeviceType\", configured \ + FROM device d \ + JOIN \"user\" u ON d.user_id = u.id \ + WHERE u.is_active \ + AND d.device_type = 'user'::device_type \ + AND d.user_id = $1 \ + ORDER BY d.id ASC", + user_id + ) + .fetch_all(&mut *transaction) + .await + .map_err(Into::into); + } + + let allowed_groups = self.get_allowed_groups(&mut *transaction).await?; + let devices = query_as!( + Device, + "SELECT DISTINCT ON (d.id) d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, \ d.description, d.device_type \"device_type: DeviceType\", configured FROM device d \ JOIN \"user\" u ON d.user_id = u.id \ JOIN group_user gu ON u.id = gu.user_id \ JOIN \"group\" g ON gu.group_id = g.id \ WHERE g.\"name\" IN (SELECT * FROM UNNEST($1::text[])) \ - AND u.is_active = true \ + AND u.is_active \ AND d.device_type = 'user'::device_type \ AND d.user_id = $2 \ ORDER BY d.id ASC", - &allowed_groups, user_id - ) - .fetch_all(&mut *transaction) - .await? - } - // all devices of enabled users are allowed - None => query_as!( - Device, - "SELECT d.id, d.name, d.wireguard_pubkey, d.user_id, d.created, d.description, \ - d.device_type \"device_type: DeviceType\", configured \ - FROM device d \ - JOIN \"user\" u ON d.user_id = u.id \ - WHERE u.is_active = true \ - AND d.device_type = 'user'::device_type \ - AND d.user_id = $1 \ - ORDER BY d.id ASC", - user_id - ) - .fetch_all(&mut *transaction) - .await?, - }; + &allowed_groups, + user_id + ) + .fetch_all(&mut *transaction) + .await?; Ok(devices) } @@ -1161,7 +1162,7 @@ impl WireguardNetwork { } } - // fetch all locations using external MFA + /// Fetch all locations using external MFA. pub async fn all_using_external_mfa<'e, E>(executor: E) -> sqlx::Result> where E: PgExecutor<'e>, @@ -1169,7 +1170,7 @@ impl WireguardNetwork { let locations = query_as!( WireguardNetwork, "SELECT id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, \ - allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, \ + allowed_ips, allow_all_groups, connected_at, keepalive_interval, peer_disconnect_threshold, acl_enabled, \ acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ service_location_mode \"service_location_mode: ServiceLocationMode\" \ FROM wireguard_network WHERE location_mfa_mode = 'external'::location_mfa_mode", @@ -1199,39 +1200,32 @@ impl WireguardNetwork { /// Return a list of allowed groups for a given network. /// Admin group should always be included. - /// If no `allowed_groups` are specified for a network then all devices are allowed. - /// In this case `None` is returned to signify that there's no filtering. /// This helper method is meant for use in all business logic gating /// access to networks based on allowed groups. pub async fn get_allowed_groups( &self, conn: &mut PgConnection, - ) -> Result>, ModelError> { + ) -> Result, ModelError> { debug!("Returning a list of allowed groups for network {self}"); let admin_groups = Group::find_by_permission(&mut *conn, Permission::IsAdmin).await?; // get allowed groups from DB let mut groups = self.fetch_allowed_groups(&mut *conn).await?; - // if no allowed groups are set then all groups are allowed - if groups.is_empty() { - return Ok(None); - } - for group in admin_groups { if !groups.iter().any(|name| name == &group.name) { groups.push(group.name); } } - Ok(Some(groups)) + Ok(groups) } /// Set allowed groups, removing or adding groups as necessary. pub async fn set_allowed_groups( &self, transaction: &mut PgConnection, - allowed_groups: Vec, + allowed_groups: &[String], ) -> Result<(), ModelError> { info!("Setting allowed groups for network {self} to: {allowed_groups:?}"); if allowed_groups.is_empty() { @@ -1242,7 +1236,7 @@ impl WireguardNetwork { let mut current_groups = self.fetch_allowed_groups(&mut *transaction).await?; // add to group if not already a member - for group in &allowed_groups { + for group in allowed_groups { if !current_groups.contains(group) { self.add_to_group(transaction, group).await?; } @@ -1251,12 +1245,14 @@ impl WireguardNetwork { // remove groups which are no longer present current_groups.retain(|group| !allowed_groups.contains(group)); if !current_groups.is_empty() { - self.remove_from_groups(transaction, current_groups).await?; + self.remove_from_groups(transaction, ¤t_groups) + .await?; } Ok(()) } + /// Add given group to the network. pub async fn add_to_group( &self, transaction: &mut PgConnection, @@ -1274,10 +1270,11 @@ impl WireguardNetwork { Ok(()) } + /// Remove given groups from the network. pub async fn remove_from_groups( &self, transaction: &mut PgConnection, - groups: Vec, + groups: &[String], ) -> Result<(), ModelError> { info!("Removing allowed groups {groups:?} for network {self}"); let result = query!( @@ -1287,7 +1284,7 @@ impl WireguardNetwork { WHERE name IN (SELECT * FROM UNNEST($2::text[])) \ )", self.id, - &groups + groups ) .execute(transaction) .await?; @@ -1298,11 +1295,14 @@ impl WireguardNetwork { Ok(()) } - /// Remove all allowed groups for a given network - async fn clear_allowed_groups(&self, transaction: &mut PgConnection) -> Result<(), ModelError> { + /// Remove all allowed groups for a given network. + pub async fn clear_allowed_groups( + &self, + transaction: &mut PgConnection, + ) -> Result<(), ModelError> { info!("Removing all allowed groups for network {self}"); let result = query!( - "DELETE FROM wireguard_network_allowed_group WHERE network_id=$1", + "DELETE FROM wireguard_network_allowed_group WHERE network_id = $1", self.id ) .execute(transaction) @@ -1362,6 +1362,7 @@ impl Default for WireguardNetwork { mtu: DEFAULT_WIREGUARD_MTU, fwmark: 0, allowed_ips: Vec::default(), + allow_all_groups: false, connected_at: Option::default(), keepalive_interval: DEFAULT_KEEPALIVE_INTERVAL, peer_disconnect_threshold: DEFAULT_DISCONNECT_THRESHOLD, @@ -1680,7 +1681,10 @@ mod test { #[sqlx::test] async fn test_get_allowed_devices_for_user(_: PgPoolOptions, options: PgConnectOptions) { let pool = setup_pool(options).await; - let mut network = WireguardNetwork::default(); + let mut network = WireguardNetwork:: { + allow_all_groups: true, + ..Default::default() + }; network.try_set_address("10.1.1.1/29").unwrap(); let network = network.save(&pool).await.unwrap(); @@ -1833,7 +1837,7 @@ mod test { let mut transaction = pool.begin().await.unwrap(); network - .set_allowed_groups(&mut transaction, vec![group1.name]) + .set_allowed_groups(&mut transaction, &[group1.name]) .await .unwrap(); @@ -1864,6 +1868,7 @@ mod test { DEFAULT_WIREGUARD_MTU, 0, vec![IpNetwork::from_str("10.1.1.0/24").unwrap()], + false, 300, 300, false, @@ -1998,6 +2003,7 @@ mod test { DEFAULT_WIREGUARD_MTU, 0, vec![IpNetwork::from_str("10.1.1.0/24").unwrap()], + false, 300, 300, false, diff --git a/crates/defguard_common/src/db/models/wizard.rs b/crates/defguard_common/src/db/models/wizard.rs index 004be1866e..8c80bdc4fb 100644 --- a/crates/defguard_common/src/db/models/wizard.rs +++ b/crates/defguard_common/src/db/models/wizard.rs @@ -54,7 +54,7 @@ impl Wizard { { sqlx::query( "UPDATE wizard SET active_wizard = $1, completed = $2 \ - WHERE is_singleton = TRUE", + WHERE is_singleton", ) .bind(self.active_wizard) .bind(self.completed) @@ -71,7 +71,7 @@ impl Wizard { let row = sqlx::query_as::<_, WizardDbRow>( "SELECT active_wizard, completed \ FROM wizard \ - WHERE is_singleton = TRUE \ + WHERE is_singleton \ LIMIT 1", ) .fetch_one(executor) diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index 567b6b463b..f1268aa0f3 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -975,8 +975,9 @@ impl AclRule { query_as!( WireguardNetwork, "SELECT n.id, name, address, port, pubkey, prvkey, endpoint, dns, mtu, fwmark, \ - allowed_ips, connected_at, keepalive_interval, peer_disconnect_threshold, \ - acl_enabled, acl_default_allow, location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ + allowed_ips, allow_all_groups, connected_at, keepalive_interval, \ + peer_disconnect_threshold, acl_enabled, acl_default_allow, \ + location_mfa_mode \"location_mfa_mode: LocationMfaMode\", \ service_location_mode \"service_location_mode: ServiceLocationMode\" \ FROM aclrulenetwork r \ JOIN wireguard_network n \ @@ -1046,7 +1047,7 @@ impl AclRule { ON u.id = r.user_id \ WHERE r.rule_id = $1 \ AND r.allow \ - AND u.is_active = true", + AND u.is_active", self.id, ) .fetch_all(executor) @@ -1072,7 +1073,7 @@ impl AclRule { ON u.id = r.user_id \ WHERE r.rule_id = $1 \ AND NOT r.allow \ - AND u.is_active = true", + AND u.is_active", self.id, ) .fetch_all(executor) @@ -1130,7 +1131,7 @@ impl AclRule { device_type \"device_type: DeviceType\", configured \ FROM aclruledevice r \ JOIN device d ON d.id = r.device_id \ - WHERE r.rule_id = $1 AND r.allow = true AND d.configured = true", + WHERE r.rule_id = $1 AND r.allow AND d.configured", self.id, ) .fetch_all(executor) @@ -1150,7 +1151,7 @@ impl AclRule { device_type \"device_type: DeviceType\", configured \ FROM aclruledevice r \ JOIN device d ON d.id = r.device_id \ - WHERE r.rule_id = $1 AND r.allow = false AND d.configured = true", + WHERE r.rule_id = $1 AND r.allow = false AND d.configured", self.id, ) .fetch_all(executor) @@ -1364,7 +1365,7 @@ impl AclRuleInfo { FROM device d \ JOIN wireguard_network_device wnd \ ON d.id = wnd.device_id \ - WHERE device_type = 'network'::device_type AND configured = true AND \ + WHERE device_type = 'network'::device_type AND configured AND \ wireguard_network_id = $1", location_id ) @@ -1396,7 +1397,7 @@ impl AclRuleInfo { FROM device d \ JOIN wireguard_network_device wnd \ ON d.id = wnd.device_id \ - WHERE device_type = 'network'::device_type AND configured = true AND \ + WHERE device_type = 'network'::device_type AND configured AND \ wireguard_network_id = $1", location_id ) 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 abbc35e50b..68aaef108c 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl/tests.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl/tests.rs @@ -157,6 +157,7 @@ async fn test_rule_relations(_: PgPoolOptions, options: PgConnectOptions) { DEFAULT_WIREGUARD_MTU, 0, Vec::new(), + true, 100, 100, false, @@ -176,6 +177,7 @@ async fn test_rule_relations(_: PgPoolOptions, options: PgConnectOptions) { DEFAULT_WIREGUARD_MTU, 0, Vec::new(), + true, 200, 200, false, diff --git a/crates/defguard_core/src/enterprise/db/models/api_tokens.rs b/crates/defguard_core/src/enterprise/db/models/api_tokens.rs index 7b65a80e96..474282177a 100644 --- a/crates/defguard_core/src/enterprise/db/models/api_tokens.rs +++ b/crates/defguard_core/src/enterprise/db/models/api_tokens.rs @@ -59,7 +59,7 @@ impl ApiToken { Self, "SELECT at.id, user_id, created_at, name, token_hash \ FROM api_token at JOIN \"user\" ON \"user\".id = user_id \ - WHERE token_hash = $1 AND \"user\".is_active = true", + WHERE token_hash = $1 AND \"user\".is_active", token_hash ) .fetch_optional(executor) diff --git a/crates/defguard_core/src/enterprise/directory_sync/tests.rs b/crates/defguard_core/src/enterprise/directory_sync/tests.rs index 9482839209..6b33822550 100644 --- a/crates/defguard_core/src/enterprise/directory_sync/tests.rs +++ b/crates/defguard_core/src/enterprise/directory_sync/tests.rs @@ -61,6 +61,7 @@ mod test { 1420, 0, Vec::new(), + true, 32, 32, false, @@ -640,7 +641,7 @@ mod test { let mut transaction = pool.begin().await.unwrap(); let group = Group::new("group1").save(&mut *transaction).await.unwrap(); network - .set_allowed_groups(&mut transaction, vec![group.name]) + .set_allowed_groups(&mut transaction, &[group.name]) .await .unwrap(); transaction.commit().await.unwrap(); @@ -664,13 +665,11 @@ mod test { if let Ok(GatewayEvent::DeviceDeleted(dev)) = event { assert_eq!(dev.device.user_id, user2_pre_sync.id); } else { - panic!("Expected a DeviceDeleted event"); + panic!("Expected DeviceDeleted event"); } let event = wg_rx.try_recv(); if let Ok(GatewayEvent::DeviceCreated(dev)) = event { - assert_eq!(dev.device.user_id, user.id); - } else { - panic!("Expected a DeviceDeleted event"); + panic!("Unexpected DeviceCreated event: {dev:?}"); } } diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index 02884c03bc..c56e012ccf 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -988,7 +988,7 @@ pub(crate) async fn get_location_active_acl_rules( use_manual_destination_settings \ FROM aclrule a \ LEFT JOIN aclrulenetwork an ON a.id = an.rule_id \ - WHERE (an.network_id = $1 OR a.all_locations = true) AND enabled = true \ + WHERE (an.network_id = $1 OR a.all_locations) AND enabled \ AND state = 'applied'::aclrule_state \ AND (expires IS NULL OR expires > NOW())", ) diff --git a/crates/defguard_core/src/enterprise/firewall/tests/mod.rs b/crates/defguard_core/src/enterprise/firewall/tests/mod.rs index ff40f94236..c8117ac25a 100644 --- a/crates/defguard_core/src/enterprise/firewall/tests/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/tests/mod.rs @@ -768,8 +768,8 @@ async fn test_generate_firewall_rules_ipv6(_: PgPoolOptions, options: PgConnectO user_id: user_1.id, // Owned by user 1 device_type: DeviceType::Network, description: Some("Test network device 1".into()), - wireguard_pubkey: Default::default(), - created: Default::default(), + wireguard_pubkey: String::default(), + created: NaiveDateTime::default(), configured: true, }; let network_device_1 = network_device_1.save(&pool).await.unwrap(); @@ -780,8 +780,8 @@ async fn test_generate_firewall_rules_ipv6(_: PgPoolOptions, options: PgConnectO user_id: user_2.id, // Owned by user 2 device_type: DeviceType::Network, description: Some("Test network device 2".into()), - wireguard_pubkey: Default::default(), - created: Default::default(), + wireguard_pubkey: String::default(), + created: NaiveDateTime::default(), configured: true, }; let network_device_2 = network_device_2.save(&pool).await.unwrap(); @@ -792,14 +792,14 @@ async fn test_generate_firewall_rules_ipv6(_: PgPoolOptions, options: PgConnectO user_id: user_3.id, // Owned by user 3 device_type: DeviceType::Network, description: Some("Test network device 3".into()), - wireguard_pubkey: Default::default(), - created: Default::default(), + wireguard_pubkey: String::default(), + created: NaiveDateTime::default(), configured: true, }; let network_device_3 = network_device_3.save(&pool).await.unwrap(); // Add network devices to location's VPN network - let network_devices = vec![ + let network_devices = [ ( network_device_1.id, IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0x0100, 1)), @@ -1156,8 +1156,8 @@ async fn test_generate_firewall_rules_ipv4_and_ipv6(_: PgPoolOptions, options: P user_id: user.id, device_type: DeviceType::User, description: None, - wireguard_pubkey: Default::default(), - created: Default::default(), + wireguard_pubkey: String::default(), + created: NaiveDateTime::default(), configured: true, }; let device = device.save(&pool).await.unwrap(); diff --git a/crates/defguard_core/src/grpc/proxy/client_mfa.rs b/crates/defguard_core/src/grpc/proxy/client_mfa.rs index a2aab9a605..9f27f76af1 100644 --- a/crates/defguard_core/src/grpc/proxy/client_mfa.rs +++ b/crates/defguard_core/src/grpc/proxy/client_mfa.rs @@ -380,22 +380,22 @@ impl ClientMfaServer { error!("Failed to fetch allowed groups for location {location}: {err}"); Status::internal("unexpected error") })?; - // if no groups are specified all users are allowed - if let Some(groups) = allowed_groups { - // check if user belongs to one of allowed groups - if !groups + // If not all groups are allowed, check if user belongs to one of the allowed groups. + if !location.allow_all_groups + && !allowed_groups .iter() .any(|allowed_group| user_info.groups.contains(allowed_group)) - { - error!( - "User {} not allowed to connect to location {location} because he doesn't belong to any of the allowed groups. - User groups: {:?}, allowed groups: {:?}", - user_info.username, user_info.groups, groups - ); - return Err(Status::unauthenticated("unauthorized")); - } + { + error!( + "User {} is not allowed to connect to location {location} because he/she doesn't \ + belong to any of the allowed groups. User groups: {:?}, allowed groups: \ + {allowed_groups:?}", + user_info.username, user_info.groups + ); + Err(Status::unauthenticated("unauthorized")) + } else { + Ok(()) } - Ok(()) } #[instrument(skip_all)] diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index 784001f62d..8a1e48b9e9 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -63,6 +63,7 @@ pub struct WireguardNetworkData { pub dns: Option, pub mtu: i32, pub fwmark: i64, + pub allow_all_groups: bool, pub allowed_groups: Vec, pub keepalive_interval: i32, pub peer_disconnect_threshold: i32, @@ -144,6 +145,7 @@ pub(crate) struct ImportNetworkData { name: String, endpoint: String, config: String, + allow_all_groups: bool, allowed_groups: Vec, } @@ -226,6 +228,7 @@ pub(crate) async fn create_network( data.mtu, data.fwmark, allowed_ips, + data.allow_all_groups, data.keepalive_interval, data.peer_disconnect_threshold, data.acl_enabled, @@ -237,7 +240,7 @@ pub(crate) async fn create_network( let mut transaction = appstate.pool.begin().await?; let network = network.save(&mut *transaction).await?; network - .set_allowed_groups(&mut transaction, data.allowed_groups) + .set_allowed_groups(&mut transaction, &data.allowed_groups) .await?; // generate IP addresses for existing devices @@ -342,6 +345,7 @@ pub(crate) async fn modify_network( network.mtu = data.mtu; network.fwmark = data.fwmark; network.peer_disconnect_threshold = data.peer_disconnect_threshold; + network.allow_all_groups = data.allow_all_groups; network.acl_enabled = data.acl_enabled; network.acl_default_allow = data.acl_default_allow; network.service_location_mode = if data.location_mfa_mode == LocationMfaMode::Disabled { @@ -357,7 +361,7 @@ pub(crate) async fn modify_network( network.save(&mut *transaction).await?; network - .set_allowed_groups(&mut transaction, data.allowed_groups) + .set_allowed_groups(&mut transaction, &data.allowed_groups) .await?; let _events = sync_location_allowed_devices(&network, &mut transaction, None).await?; @@ -588,11 +592,12 @@ pub(crate) async fn import_network( })?; network.name = data.name; network.endpoint = data.endpoint; + network.allow_all_groups = data.allow_all_groups; let mut transaction = appstate.pool.begin().await?; let network = network.save(&mut *transaction).await?; network - .set_allowed_groups(&mut transaction, data.allowed_groups) + .set_allowed_groups(&mut transaction, &data.allowed_groups) .await?; info!("New network {network} created"); @@ -638,7 +643,7 @@ pub(crate) async fn add_user_devices( Path(network_id): Path, Json(request_data): Json, ) -> ApiResult { - let mapped_devices = request_data.devices.clone(); + let mapped_devices = request_data.devices; let user = session.user; let device_count = mapped_devices.len(); @@ -656,7 +661,7 @@ pub(crate) async fn add_user_devices( if let Some(network) = WireguardNetwork::find_by_id(&appstate.pool, network_id).await? { // wrap loop in transaction to abort if a device is invalid let mut transaction = appstate.pool.begin().await?; - let events = handle_mapped_devices(&network, &mut transaction, mapped_devices).await?; + let events = handle_mapped_devices(&network, &mut transaction, &mapped_devices).await?; appstate.send_multiple_wireguard_events(events); transaction.commit().await?; @@ -1274,10 +1279,11 @@ pub(crate) async fn list_user_devices( Ok(ApiResponse::json(devices, StatusCode::OK)) } +/// GET "/network/{network_id}/device/{device_id}/config" pub(crate) async fn download_config( session: SessionInfo, State(appstate): State, - Path((network_id, device_id)): Path<(i64, i64)>, + Path((network_id, device_id)): Path<(Id, Id)>, ) -> Result { debug!("Creating config for device {device_id} in network {network_id}"); diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index 4a4fc9ffb0..ba963d2067 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -768,6 +768,7 @@ pub async fn init_dev_env(config: &DefGuardConfig) { DEFAULT_WIREGUARD_MTU, 0, vec![IpNetwork::new(IpAddr::V4(Ipv4Addr::new(10, 1, 1, 0)), 24).unwrap()], + true, DEFAULT_KEEPALIVE_INTERVAL, DEFAULT_DISCONNECT_THRESHOLD, false, @@ -871,6 +872,7 @@ pub async fn init_vpn_location( args.mtu as i32, i64::from(args.fwmark), args.allowed_ips.clone(), + true, DEFAULT_KEEPALIVE_INTERVAL, DEFAULT_DISCONNECT_THRESHOLD, false, @@ -913,6 +915,7 @@ pub async fn init_vpn_location( args.mtu as i32, i64::from(args.fwmark), args.allowed_ips.clone(), + true, DEFAULT_KEEPALIVE_INTERVAL, DEFAULT_DISCONNECT_THRESHOLD, false, diff --git a/crates/defguard_core/src/location_management/allowed_peers.rs b/crates/defguard_core/src/location_management/allowed_peers.rs index 50da60195e..c5848d43b8 100644 --- a/crates/defguard_core/src/location_management/allowed_peers.rs +++ b/crates/defguard_core/src/location_management/allowed_peers.rs @@ -37,9 +37,8 @@ where FROM wireguard_network_device wnd \ JOIN device d ON wnd.device_id = d.id \ JOIN \"user\" u ON d.user_id = u.id \ - WHERE wireguard_network_id = $1 AND (is_authorized = true OR NOT $2) \ - AND d.configured = true \ - AND u.is_active = true \ + WHERE wireguard_network_id = $1 AND (is_authorized OR NOT $2) \ + AND d.configured AND u.is_active \ ORDER BY d.id ASC", location.id, location.mfa_enabled() diff --git a/crates/defguard_core/src/location_management/mod.rs b/crates/defguard_core/src/location_management/mod.rs index 8836164aca..18e5fc1d9b 100644 --- a/crates/defguard_core/src/location_management/mod.rs +++ b/crates/defguard_core/src/location_management/mod.rs @@ -312,17 +312,17 @@ pub(crate) async fn handle_imported_devices( /// Handle device -> user mapping in second step of network import wizard pub(crate) async fn handle_mapped_devices( location: &WireguardNetwork, - transaction: &mut PgConnection, - mapped_devices: Vec, + conn: &mut PgConnection, + mapped_devices: &[MappedDevice], ) -> Result, WireguardNetworkError> { info!("Mapping user devices for network {location}"); // get allowed groups for network - let allowed_groups = location.get_allowed_groups(&mut *transaction).await?; + let allowed_groups = location.get_allowed_groups(&mut *conn).await?; let mut events = Vec::new(); // use a helper hashmap to avoid repeated queries let mut user_groups = HashMap::new(); - for mapped_device in &mapped_devices { + for mapped_device in mapped_devices { debug!("Mapping device {}", mapped_device.name); // validate device pubkey Device::validate_pubkey(&mapped_device.wireguard_pubkey).map_err(|_| { @@ -337,7 +337,7 @@ pub(crate) async fn handle_mapped_devices( None, true, ) - .save(&mut *transaction) + .save(&mut *conn) .await?; debug!("Saved new device {device}"); @@ -346,9 +346,9 @@ pub(crate) async fn handle_mapped_devices( // user info has already been fetched before Some(groups) => groups, // fetch user info - None => match User::find_by_id(&mut *transaction, device.user_id).await? { + None => match User::find_by_id(&mut *conn, device.user_id).await? { Some(user) => { - let groups = user.member_of_names(&mut *transaction).await?; + let groups = user.member_of_names(&mut *conn).await?; user_groups.insert(device.user_id, groups); // FIXME: ugly workaround to get around `groups` being dropped user_groups.get(&device.user_id).unwrap() @@ -358,44 +358,23 @@ pub(crate) async fn handle_mapped_devices( }; let mut network_info = Vec::new(); - match &allowed_groups { - None => { - let wireguard_network_device = WireguardNetworkDevice::new( - location.id, - device.id, - mapped_device.wireguard_ips.clone(), - ); - wireguard_network_device.insert(&mut *transaction).await?; - network_info.push(DeviceNetworkInfo { - network_id: location.id, - device_wireguard_ips: wireguard_network_device.wireguard_ips, - preshared_key: wireguard_network_device.preshared_key, - is_authorized: wireguard_network_device.is_authorized, - }); - } - Some(allowed) => { - // check if user belongs to an allowed group - if allowed.iter().any(|group| groups.contains(group)) { - // assign specified IP in imported network - let wireguard_network_device = WireguardNetworkDevice::new( - location.id, - device.id, - mapped_device.wireguard_ips.clone(), - ); - wireguard_network_device.insert(&mut *transaction).await?; - network_info.push(DeviceNetworkInfo { - network_id: location.id, - device_wireguard_ips: wireguard_network_device.wireguard_ips, - preshared_key: wireguard_network_device.preshared_key, - is_authorized: wireguard_network_device.is_authorized, - }); - } - } + if location.allow_all_groups || allowed_groups.iter().any(|group| groups.contains(group)) { + let wireguard_network_device = WireguardNetworkDevice::new( + location.id, + device.id, + mapped_device.wireguard_ips.clone(), + ); + wireguard_network_device.insert(&mut *conn).await?; + network_info.push(DeviceNetworkInfo { + network_id: location.id, + device_wireguard_ips: wireguard_network_device.wireguard_ips, + preshared_key: wireguard_network_device.preshared_key, + is_authorized: wireguard_network_device.is_authorized, + }); } - // assign IPs in other networks - let (mut all_network_info, _configs) = - device.add_to_all_networks(&mut *transaction).await?; + // Assign IP addresses in other networks. + let (mut all_network_info, _configs) = device.add_to_all_networks(&mut *conn).await?; network_info.append(&mut all_network_info); @@ -485,7 +464,15 @@ mod test { .await .unwrap(); + let group = Group::new("group").save(&pool).await.unwrap(); + user1.add_to_group(&pool, &group).await.unwrap(); + user2.add_to_group(&pool, &group).await.unwrap(); + let mut transaction = pool.begin().await.unwrap(); + network + .set_allowed_groups(&mut transaction, std::slice::from_ref(&group.name)) + .await + .unwrap(); // user1 sync let events = sync_allowed_devices_for_user(&network, &mut transaction, &user1, None) @@ -614,7 +601,7 @@ mod test { network .set_allowed_groups( &mut transaction, - vec![group1.name.clone(), group2.name.clone()], + &[group1.name.clone(), group2.name.clone()], ) .await .unwrap(); diff --git a/crates/defguard_core/src/location_management/tests.rs b/crates/defguard_core/src/location_management/tests.rs index 663644164d..a61dc0660d 100644 --- a/crates/defguard_core/src/location_management/tests.rs +++ b/crates/defguard_core/src/location_management/tests.rs @@ -25,6 +25,7 @@ fn test_network_readdress(_: PgPoolOptions, options: PgConnectOptions) { // 192.168.42.47: broadcast let mut network = WireguardNetwork:: { address: vec![IpNetwork::new(IpAddr::V4(Ipv4Addr::new(192, 168, 42, 46)), 30).unwrap()], + allow_all_groups: true, ..Default::default() } .save(&pool) diff --git a/crates/defguard_core/src/wg_config.rs b/crates/defguard_core/src/wg_config.rs index 7f443c3db3..82b055d0bc 100644 --- a/crates/defguard_core/src/wg_config.rs +++ b/crates/defguard_core/src/wg_config.rs @@ -117,6 +117,7 @@ pub(crate) fn parse_wireguard_config( mtu, fwmark, allowed_ips, + true, DEFAULT_KEEPALIVE_INTERVAL, DEFAULT_DISCONNECT_THRESHOLD, false, diff --git a/crates/defguard_core/tests/integration/api/acl/rules.rs b/crates/defguard_core/tests/integration/api/acl/rules.rs index 2e181c932e..6baa6bbd77 100644 --- a/crates/defguard_core/tests/integration/api/acl/rules.rs +++ b/crates/defguard_core/tests/integration/api/acl/rules.rs @@ -344,6 +344,7 @@ async fn test_related_objects(_: PgPoolOptions, options: PgConnectOptions) { DEFAULT_WIREGUARD_MTU, 0, Vec::new(), + true, 100, 100, false, @@ -864,6 +865,7 @@ async fn test_rule_delete_state_applied(_: PgPoolOptions, options: PgConnectOpti DEFAULT_WIREGUARD_MTU, 0, Vec::new(), + true, 100, 100, false, diff --git a/crates/defguard_core/tests/integration/api/common/mod.rs b/crates/defguard_core/tests/integration/api/common/mod.rs index 894f04786b..a30aac97ab 100644 --- a/crates/defguard_core/tests/integration/api/common/mod.rs +++ b/crates/defguard_core/tests/integration/api/common/mod.rs @@ -193,7 +193,8 @@ pub(crate) async fn make_network(client: &TestClient, name: &str) -> TestRespons "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, - "allowed_groups": [], + "allowed_groups": ["admin"], + "allow_all_groups": false, "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, diff --git a/crates/defguard_core/tests/integration/api/enterprise_settings.rs b/crates/defguard_core/tests/integration/api/enterprise_settings.rs index 878526329f..a2c02f7188 100644 --- a/crates/defguard_core/tests/integration/api/enterprise_settings.rs +++ b/crates/defguard_core/tests/integration/api/enterprise_settings.rs @@ -9,7 +9,7 @@ use reqwest::StatusCode; use serde_json::json; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; -use super::common::{exceed_enterprise_limits, make_network, make_test_client, setup_pool}; +use super::common::{exceed_enterprise_limits, make_test_client, setup_pool}; #[sqlx::test] async fn test_only_enterprise_can_modify_enterpise_settings( @@ -70,8 +70,30 @@ async fn test_admin_devices_management_is_enforced(_: PgPoolOptions, options: Pg exceed_enterprise_limits(&client).await; - // create network - make_network(&client, "network").await; + // create network with access for all groups so the user device gets assigned config + let response = client + .post("/api/v1/network") + .json(&json!({ + "name": "network", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allow_all_groups": true, + "allowed_groups": [], + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::CREATED); // setup admin devices management let settings = EnterpriseSettings { @@ -161,8 +183,30 @@ async fn test_regular_user_device_management(_: PgPoolOptions, options: PgConnec exceed_enterprise_limits(&client).await; - // create network - make_network(&client, "network").await; + // create network with access for all groups so the user device gets assigned config + let response = client + .post("/api/v1/network") + .json(&json!({ + "name": "network", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allow_all_groups": true, + "allowed_groups": [], + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::CREATED); // setup admin devices management let settings = EnterpriseSettings { @@ -244,8 +288,30 @@ async fn dg25_12_test_enforce_client_activation_only(_: PgPoolOptions, options: exceed_enterprise_limits(&client).await; - // create network - make_network(&client, "network").await; + // create network with access for all groups so the user device gets assigned config + let response = client + .post("/api/v1/network") + .json(&json!({ + "name": "network", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allowed_groups": [], + "allow_all_groups": true, + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::CREATED); // disable manual device management let settings = EnterpriseSettings { @@ -320,8 +386,31 @@ async fn dg25_13_test_disable_device_config(_: PgPoolOptions, options: PgConnect exceed_enterprise_limits(&client).await; - // create network - make_network(&client, "network").await; + // Allow all groups for network 1. + // Payload based on make_network(). + let response = client + .put("/api/v1/network/1") + .json(&json!({ + "name": "network1", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allowed_groups": ["admin"], + "allow_all_groups": true, + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::OK); // disable manual device management let settings = EnterpriseSettings { diff --git a/crates/defguard_core/tests/integration/api/wireguard.rs b/crates/defguard_core/tests/integration/api/wireguard.rs index c293530192..a36ca92e8a 100644 --- a/crates/defguard_core/tests/integration/api/wireguard.rs +++ b/crates/defguard_core/tests/integration/api/wireguard.rs @@ -55,7 +55,7 @@ async fn test_network(_: PgPoolOptions, options: PgConnectOptions) { // check vpn locations for `admin` group let response = client.get("/api/v1/group/admin").send().await; let group_info: GroupInfo = response.json().await; - assert!(group_info.vpn_locations.is_empty()); + assert_eq!(group_info.vpn_locations, vec!["network"]); // modify network let network_data = WireguardNetworkData { @@ -67,6 +67,7 @@ async fn test_network(_: PgPoolOptions, options: PgConnectOptions) { dns: None, mtu: DEFAULT_WIREGUARD_MTU, fwmark: 0, + allow_all_groups: false, allowed_groups: vec!["admin".into()], keepalive_interval: DEFAULT_KEEPALIVE_INTERVAL, peer_disconnect_threshold: DEFAULT_DISCONNECT_THRESHOLD, @@ -149,6 +150,7 @@ async fn test_location_mfa_mode_validation_create(_: PgPoolOptions, options: PgC dns: None, mtu: DEFAULT_WIREGUARD_MTU, fwmark: 0, + allow_all_groups: false, allowed_groups: vec!["admin".into()], keepalive_interval: DEFAULT_KEEPALIVE_INTERVAL, peer_disconnect_threshold: DEFAULT_DISCONNECT_THRESHOLD, @@ -233,6 +235,7 @@ async fn test_location_mfa_mode_validation_modify(_: PgPoolOptions, options: PgC dns: None, mtu: DEFAULT_WIREGUARD_MTU, fwmark: 0, + allow_all_groups: false, allowed_groups: vec!["admin".into()], keepalive_interval: DEFAULT_KEEPALIVE_INTERVAL, peer_disconnect_threshold: DEFAULT_DISCONNECT_THRESHOLD, @@ -544,7 +547,8 @@ async fn test_network_address_reassignment(_: PgPoolOptions, options: PgConnectO "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, - "allowed_groups": [], + "allow_all_groups": false, + "allowed_groups": ["admin"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, @@ -871,7 +875,8 @@ async fn test_network_size_validation(_: PgPoolOptions, options: PgConnectOption "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, - "allowed_groups": [], + "allow_all_groups": false, + "allowed_groups": ["admin"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, @@ -897,7 +902,8 @@ async fn test_network_size_validation(_: PgPoolOptions, options: PgConnectOption "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, - "allowed_groups": [], + "allow_all_groups": false, + "allowed_groups": ["admin"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, diff --git a/crates/defguard_core/tests/integration/api/wireguard_network_allowed_groups.rs b/crates/defguard_core/tests/integration/api/wireguard_network_allowed_groups.rs index 7c1eba543b..a0064518fc 100644 --- a/crates/defguard_core/tests/integration/api/wireguard_network_allowed_groups.rs +++ b/crates/defguard_core/tests/integration/api/wireguard_network_allowed_groups.rs @@ -156,6 +156,7 @@ async fn test_create_new_network(_: PgPoolOptions, options: PgConnectOptions) { "mtu": 1420, "fwmark": 0, "allowed_groups": ["allowed group"], + "allow_all_groups": false, "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, @@ -181,6 +182,61 @@ async fn test_create_new_network(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(peers[1].pubkey, devices[1].wireguard_pubkey); } +#[sqlx::test] +async fn test_create_new_network_allow_all_groups(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + + let (client, client_state) = make_test_client(pool).await; + let (_users, devices) = setup_test_users(&client_state.pool).await; + + let mut wg_rx = client_state.wireguard_rx; + + let auth = Auth::new("admin", "pass123"); + let response = &client.post("/api/v1/auth").json(&auth).send().await; + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .post("/api/v1/network") + .json(&json!({ + "name": "network", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allowed_groups": ["allowed group"], + "allow_all_groups": true, + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::CREATED); + let network: WireguardNetwork = response.json().await; + assert!(network.allow_all_groups); + let allowed_groups = network + .fetch_allowed_groups(&client_state.pool) + .await + .unwrap(); + assert_eq!(allowed_groups, vec!["allowed group"]); + assert_matches!(wg_rx.try_recv().unwrap(), GatewayEvent::NetworkCreated(..)); + + let peers = get_location_allowed_peers(&network, &client_state.pool) + .await + .unwrap(); + assert_eq!(peers.len(), 4); + assert_eq!(peers[0].pubkey, devices[0].wireguard_pubkey); + assert_eq!(peers[1].pubkey, devices[1].wireguard_pubkey); + assert_eq!(peers[2].pubkey, devices[2].wireguard_pubkey); + assert_eq!(peers[3].pubkey, devices[3].wireguard_pubkey); +} + #[sqlx::test] async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { let pool = setup_pool(options).await; @@ -206,6 +262,7 @@ async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, + "allow_all_groups": false, "allowed_groups": [], "keepalive_interval": 25, "peer_disconnect_threshold": 300, @@ -222,15 +279,12 @@ async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { let event = wg_rx.try_recv().unwrap(); assert_matches!(event, GatewayEvent::NetworkCreated(..)); - // network configuration was created for all devices + // network configuration was created only for the admin device let peers = get_location_allowed_peers(&network, &client_state.pool) .await .unwrap(); - assert_eq!(peers.len(), 4); + assert_eq!(peers.len(), 1); assert_eq!(peers[0].pubkey, devices[0].wireguard_pubkey); - assert_eq!(peers[1].pubkey, devices[1].wireguard_pubkey); - assert_eq!(peers[2].pubkey, devices[2].wireguard_pubkey); - assert_eq!(peers[3].pubkey, devices[3].wireguard_pubkey); // add an allowed group let response = client @@ -244,6 +298,7 @@ async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, + "allow_all_groups": false, "allowed_groups": ["allowed group"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, @@ -276,6 +331,7 @@ async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, + "allow_all_groups": false, "allowed_groups": ["allowed group", "not allowed group"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, @@ -309,6 +365,7 @@ async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, + "allow_all_groups": false, "allowed_groups": ["not allowed group"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, @@ -341,6 +398,7 @@ async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, + "allow_all_groups": false, "allowed_groups": [], "keepalive_interval": 25, "peer_disconnect_threshold": 300, @@ -357,15 +415,98 @@ async fn test_modify_network(_: PgPoolOptions, options: PgConnectOptions) { let new_peers = get_location_allowed_peers(&network, &client_state.pool) .await .unwrap(); - assert_eq!(new_peers.len(), 4); + assert_eq!(new_peers.len(), 1); assert_eq!(new_peers[0].pubkey, devices[0].wireguard_pubkey); - assert_eq!(new_peers[1].pubkey, devices[1].wireguard_pubkey); - assert_eq!(new_peers[2].pubkey, devices[2].wireguard_pubkey); - assert_eq!(new_peers[3].pubkey, devices[3].wireguard_pubkey); assert_err!(wg_rx.try_recv()); } +#[sqlx::test] +async fn test_modify_network_enable_allow_all_groups(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + + let (client, client_state) = make_test_client(pool).await; + let (_users, devices) = setup_test_users(&client_state.pool).await; + + let mut wg_rx = client_state.wireguard_rx; + + let auth = Auth::new("admin", "pass123"); + let response = &client.post("/api/v1/auth").json(&auth).send().await; + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .post("/api/v1/network") + .json(&json!({ + "name": "network", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allow_all_groups": false, + "allowed_groups": ["allowed group"], + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::CREATED); + let network: WireguardNetwork = response.json().await; + assert_matches!(wg_rx.try_recv().unwrap(), GatewayEvent::NetworkCreated(..)); + + let peers = get_location_allowed_peers(&network, &client_state.pool) + .await + .unwrap(); + assert_eq!(peers.len(), 2); + + let response = client + .put(format!("/api/v1/network/{}", network.id)) + .json(&json!({ + "name": "network", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allow_all_groups": true, + "allowed_groups": ["allowed group"], + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::OK); + let network: WireguardNetwork = response.json().await; + assert!(network.allow_all_groups); + let allowed_groups = network + .fetch_allowed_groups(&client_state.pool) + .await + .unwrap(); + assert_eq!(allowed_groups, vec!["allowed group"]); + assert_matches!(wg_rx.try_recv().unwrap(), GatewayEvent::NetworkModified(..)); + + let peers = get_location_allowed_peers(&network, &client_state.pool) + .await + .unwrap(); + assert_eq!(peers.len(), 4); + assert_eq!(peers[0].pubkey, devices[0].wireguard_pubkey); + assert_eq!(peers[1].pubkey, devices[1].wireguard_pubkey); + assert_eq!(peers[2].pubkey, devices[2].wireguard_pubkey); + assert_eq!(peers[3].pubkey, devices[3].wireguard_pubkey); +} + /// Test that devices that already exist are handled correctly during config import #[sqlx::test] async fn test_import_network_existing_devices(_: PgPoolOptions, options: PgConnectOptions) { @@ -410,7 +551,7 @@ async fn test_import_network_existing_devices(_: PgPoolOptions, options: PgConne // import network let response = client .post("/api/v1/network/import") - .json(&json!({"name": "network", "endpoint": "192.168.1.1", "config": wg_config, "allowed_groups": ["allowed group"]})) + .json(&json!({"name": "network", "endpoint": "192.168.1.1", "config": wg_config, "allow_all_groups": false, "allowed_groups": ["allowed group"]})) .send() .await; assert_eq!(response.status(), StatusCode::CREATED); @@ -507,7 +648,7 @@ PersistentKeepalive = 300 // import network let response = client .post("/api/v1/network/import") - .json(&json!({"name": "network", "endpoint": "192.168.1.1", "config": wg_config, "allowed_groups": ["allowed group"]})) + .json(&json!({"name": "network", "endpoint": "192.168.1.1", "config": wg_config, "allow_all_groups": false, "allowed_groups": ["allowed group"]})) .send() .await; assert_eq!(response.status(), StatusCode::CREATED); @@ -599,6 +740,7 @@ async fn test_modify_user(_: PgPoolOptions, options: PgConnectOptions) { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, + "allow_all_groups": false, "allowed_groups": ["allowed group"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, @@ -684,6 +826,119 @@ async fn test_modify_user(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(peers[1].pubkey, devices[3].wireguard_pubkey); } +#[sqlx::test] +async fn test_modify_user_no_effect_when_allow_all_groups( + _: PgPoolOptions, + options: PgConnectOptions, +) { + let pool = setup_pool(options).await; + + let (client, client_state) = make_test_client(pool).await; + let (_users, devices) = setup_test_users(&client_state.pool).await; + + let mut wg_rx = client_state.wireguard_rx; + + let auth = Auth::new("admin", "pass123"); + let response = &client.post("/api/v1/auth").json(&auth).send().await; + assert_eq!(response.status(), StatusCode::OK); + + let response = client + .post("/api/v1/network") + .json(&json!({ + "name": "network", + "address": "10.1.1.1/24", + "port": 55555, + "endpoint": "192.168.4.14", + "allowed_ips": "10.1.1.0/24", + "dns": "1.1.1.1", + "mtu": 1420, + "fwmark": 0, + "allow_all_groups": true, + "allowed_groups": [], + "keepalive_interval": 25, + "peer_disconnect_threshold": 300, + "acl_enabled": false, + "acl_default_allow": false, + "location_mfa_mode": "disabled", + "service_location_mode": "disabled" + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::CREATED); + let network: WireguardNetwork = response.json().await; + assert_matches!(wg_rx.try_recv().unwrap(), GatewayEvent::NetworkCreated(..)); + assert_err!(wg_rx.try_recv()); + + let peers = get_location_allowed_peers(&network, &client_state.pool) + .await + .unwrap(); + assert_eq!(peers.len(), 4); + + let mut user_details = fetch_user_details(&client, "ssnape").await; + user_details.user.groups = Vec::new(); + let response = client + .put("/api/v1/user/ssnape") + .json(&user_details.user) + .send() + .await; + assert_eq!(response.status(), StatusCode::OK); + + assert_err!(wg_rx.try_recv()); + + let peers = get_location_allowed_peers(&network, &client_state.pool) + .await + .unwrap(); + assert_eq!(peers.len(), 4); + assert_eq!(peers[0].pubkey, devices[0].wireguard_pubkey); + assert_eq!(peers[1].pubkey, devices[1].wireguard_pubkey); + assert_eq!(peers[2].pubkey, devices[2].wireguard_pubkey); + assert_eq!(peers[3].pubkey, devices[3].wireguard_pubkey); +} + +#[sqlx::test] +async fn test_import_network_allow_all_groups_preserves_allowed_groups( + _: PgPoolOptions, + options: PgConnectOptions, +) { + let pool = setup_pool(options).await; + + let (client, client_state) = make_test_client(pool).await; + let (_users, _devices) = setup_test_users(&client_state.pool).await; + + let auth = Auth::new("admin", "pass123"); + let response = &client.post("/api/v1/auth").json(&auth).send().await; + assert_eq!(response.status(), StatusCode::OK); + + let wg_config = " +[Interface] +PrivateKey = GAA2X3DW0WakGVx+DsGjhDpTgg50s1MlmrLf24Psrlg= +Address = 10.0.0.1/24 +ListenPort = 55055 +DNS = 10.0.0.2 +"; + + let response = client + .post("/api/v1/network/import") + .json(&json!({ + "name": "network", + "endpoint": "192.168.1.1", + "config": wg_config, + "allow_all_groups": true, + "allowed_groups": ["allowed group"] + })) + .send() + .await; + assert_eq!(response.status(), StatusCode::CREATED); + let response: ImportedNetworkData = response.json().await; + + let allowed_groups = response + .network + .fetch_allowed_groups(&client_state.pool) + .await + .unwrap(); + assert_eq!(allowed_groups, vec!["allowed group"]); +} + #[sqlx::test] async fn test_delete_only_allowed_group(_: PgPoolOptions, options: PgConnectOptions) { let pool = setup_pool(options).await; @@ -709,6 +964,7 @@ async fn test_delete_only_allowed_group(_: PgPoolOptions, options: PgConnectOpti "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, + "allow_all_groups": false, "allowed_groups": ["allowed group"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, @@ -736,13 +992,10 @@ async fn test_delete_only_allowed_group(_: PgPoolOptions, options: PgConnectOpti let response = client.delete("/api/v1/group/allowed%20group").send().await; assert_eq!(response.status(), StatusCode::OK); - // network configuration was created for all devices + // network configuration was created only for the admin device let peers = get_location_allowed_peers(&network, &client_state.pool) .await .unwrap(); - assert_eq!(peers.len(), 4); + assert_eq!(peers.len(), 1); assert_eq!(peers[0].pubkey, devices[0].wireguard_pubkey); - assert_eq!(peers[1].pubkey, devices[1].wireguard_pubkey); - assert_eq!(peers[2].pubkey, devices[2].wireguard_pubkey); - assert_eq!(peers[3].pubkey, devices[3].wireguard_pubkey); } diff --git a/crates/defguard_core/tests/integration/api/wireguard_network_devices.rs b/crates/defguard_core/tests/integration/api/wireguard_network_devices.rs index d91ae93c88..d0cc837ff7 100644 --- a/crates/defguard_core/tests/integration/api/wireguard_network_devices.rs +++ b/crates/defguard_core/tests/integration/api/wireguard_network_devices.rs @@ -32,7 +32,8 @@ async fn make_first_network(client: &TestClient) -> TestResponse { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, - "allowed_groups": [], + "allow_all_groups": false, + "allowed_groups": ["admin"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, @@ -58,7 +59,8 @@ async fn make_second_network(client: &TestClient) -> TestResponse { "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, - "allowed_groups": [], + "allow_all_groups": false, + "allowed_groups": ["admin"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, @@ -314,7 +316,8 @@ async fn test_device_ip_validation(_: PgPoolOptions, options: PgConnectOptions) "dns": "1.1.1.1", "mtu": 1420, "fwmark": 0, - "allowed_groups": [], + "allow_all_groups": false, + "allowed_groups": ["admin"], "keepalive_interval": 25, "peer_disconnect_threshold": 300, "acl_enabled": false, diff --git a/crates/defguard_core/tests/integration/api/wireguard_network_import.rs b/crates/defguard_core/tests/integration/api/wireguard_network_import.rs index b609239668..904747b174 100644 --- a/crates/defguard_core/tests/integration/api/wireguard_network_import.rs +++ b/crates/defguard_core/tests/integration/api/wireguard_network_import.rs @@ -59,6 +59,7 @@ async fn test_config_import(_: PgPoolOptions, options: PgConnectOptions) { DEFAULT_WIREGUARD_MTU, 0, Vec::new(), + false, DEFAULT_KEEPALIVE_INTERVAL, DEFAULT_DISCONNECT_THRESHOLD, false, @@ -114,7 +115,12 @@ async fn test_config_import(_: PgPoolOptions, options: PgConnectOptions) { // import network let response = client .post("/api/v1/network/import") - .json(&json!({"name": "network", "endpoint": "192.168.1.1", "config": wg_config, "allowed_groups": []})) + .json(&json!({ + "name": "network", + "endpoint": "192.168.1.1", + "config": wg_config, + "allow_all_groups": false, + "allowed_groups": ["admin"]})) .send() .await; assert_eq!(response.status(), StatusCode::CREATED); diff --git a/crates/defguard_core/tests/integration/grpc/gateway.rs b/crates/defguard_core/tests/integration/grpc/gateway.rs index 5564f02c43..e5de5b8268 100644 --- a/crates/defguard_core/tests/integration/grpc/gateway.rs +++ b/crates/defguard_core/tests/integration/grpc/gateway.rs @@ -55,7 +55,10 @@ async fn setup_test_server( 1000, "endpoint1".to_string(), None, + 1420, + 0, Vec::new(), + false, 100, 100, false, @@ -403,7 +406,10 @@ async fn test_gateway_update_routing(_: PgPoolOptions, options: PgConnectOptions 1000, "endpoint2".to_string(), None, + 1420, + 0, Vec::new(), + false, 100, 100, false, @@ -520,7 +526,10 @@ async fn test_gateway_config(_: PgPoolOptions, options: PgConnectOptions) { 1000, "endpoint2".to_string(), None, + 1420, + 0, Vec::new(), + false, 100, 100, false, diff --git a/crates/defguard_gateway_manager/src/tests.rs b/crates/defguard_gateway_manager/src/tests.rs index d91558685e..2257afcba0 100644 --- a/crates/defguard_gateway_manager/src/tests.rs +++ b/crates/defguard_gateway_manager/src/tests.rs @@ -77,9 +77,12 @@ async fn test_gateway(_: PgPoolOptions, options: PgConnectOptions) { 50051, "0.0.0.0".to_string(), None, - vec![IpNetwork::new(IpAddr::V4(Ipv4Addr::new(10, 1, 1, 0)), 24).unwrap()], - 0, + 1420, 0, + vec![IpNetwork::new(IpAddr::V4(Ipv4Addr::new(10, 1, 1, 0)), 24).unwrap()], + false, + 25, + 300, false, false, LocationMfaMode::default(), diff --git a/crates/defguard_session_manager/tests/common/mod.rs b/crates/defguard_session_manager/tests/common/mod.rs index f967e088cb..ddd8181288 100644 --- a/crates/defguard_session_manager/tests/common/mod.rs +++ b/crates/defguard_session_manager/tests/common/mod.rs @@ -125,6 +125,7 @@ pub(crate) async fn create_location_with_mfa_mode( 1420, 0, vec![IpNetwork::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0).unwrap()], + true, 25, 300, false, diff --git a/crates/defguard_setup/src/auto_adoption.rs b/crates/defguard_setup/src/auto_adoption.rs index cdd73df194..bdb19cfa47 100644 --- a/crates/defguard_setup/src/auto_adoption.rs +++ b/crates/defguard_setup/src/auto_adoption.rs @@ -744,6 +744,7 @@ id={} for new gateway", DEFAULT_WIREGUARD_MTU, 0, Vec::new(), + true, DEFAULT_KEEPALIVE_INTERVAL, DEFAULT_DISCONNECT_THRESHOLD, false, diff --git a/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql b/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql new file mode 100644 index 0000000000..3dadba730c --- /dev/null +++ b/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE wireguard_network +DROP COLUMN allow_all_groups; diff --git a/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql b/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql new file mode 100644 index 0000000000..3be846103c --- /dev/null +++ b/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE wireguard_network +ADD COLUMN allow_all_groups boolean NOT NULL DEFAULT false; diff --git a/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx b/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx index 6707b5290a..0259a39905 100644 --- a/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx +++ b/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx @@ -7,10 +7,16 @@ import { SelectionSection } from '../../../shared/components/SelectionSection/Se import type { SelectionOption } from '../../../shared/components/SelectionSection/type'; import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; import { Button } from '../../../shared/defguard-ui/components/Button/Button'; +import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { Toggle } from '../../../shared/defguard-ui/components/Toggle/Toggle'; +import { ThemeSpacing } from '../../../shared/defguard-ui/types'; import { AddLocationPageStep } from '../types'; import { useAddLocationStore } from '../useAddLocationStore'; export const AddLocationAccessStep = () => { + const [allowAllGroups, setAllowAllGroups] = useState( + useAddLocationStore.getState().allow_all_groups, + ); const [selected, setSelected] = useState>( new Set(useAddLocationStore.getState().allowed_groups), ); @@ -31,25 +37,39 @@ export const AddLocationAccessStep = () => { ); }, [groups]); - const saveChanges = useCallback((values: Set) => { + const saveChanges = useCallback((values: Set, allowAll: boolean) => { useAddLocationStore.setState({ + allow_all_groups: allowAll, allowed_groups: Array.from(values), }); }, []); return ( - { + const value = !allowAllGroups; + setAllowAllGroups(value); + }} /> + {!allowAllGroups && ( + <> + + + + )}