diff --git a/.sqlx/query-051c9b90e58622eb0bc07e185ce2e93fee1abd247d1198aaaf286ed4afe54088.json b/.sqlx/query-051c9b90e58622eb0bc07e185ce2e93fee1abd247d1198aaaf286ed4afe54088.json new file mode 100644 index 0000000000..4fb24ec2ac --- /dev/null +++ b/.sqlx/query-051c9b90e58622eb0bc07e185ce2e93fee1abd247d1198aaaf286ed4afe54088.json @@ -0,0 +1,75 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"name\",\"wireguard_pubkey\",\"user_id\",\"created\",\"device_type\" \"device_type: _\",\"description\",\"configured\" FROM \"device\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "wireguard_pubkey", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "created", + "type_info": "Timestamp" + }, + { + "ordinal": 5, + "name": "device_type: _", + "type_info": { + "Custom": { + "name": "device_type", + "kind": { + "Enum": [ + "user", + "network" + ] + } + } + } + }, + { + "ordinal": 6, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "configured", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + false + ] + }, + "hash": "051c9b90e58622eb0bc07e185ce2e93fee1abd247d1198aaaf286ed4afe54088" +} diff --git a/.sqlx/query-60a87e9d4f92298692c0a978f2077f7967909c3b13677177a5a0c87b6d07c0aa.json b/.sqlx/query-0f0c3641e4c2d677f55b0b1878db9c0b9f25ead9ffcf5e1f534a65ec3c419f81.json similarity index 65% rename from .sqlx/query-60a87e9d4f92298692c0a978f2077f7967909c3b13677177a5a0c87b6d07c0aa.json rename to .sqlx/query-0f0c3641e4c2d677f55b0b1878db9c0b9f25ead9ffcf5e1f534a65ec3c419f81.json index c2410566cd..8987903bda 100644 --- a/.sqlx/query-60a87e9d4f92298692c0a978f2077f7967909c3b13677177a5a0c87b6d07c0aa.json +++ b/.sqlx/query-0f0c3641e4c2d677f55b0b1878db9c0b9f25ead9ffcf5e1f534a65ec3c419f81.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT COUNT(*) FROM wireguard_network", + "query": "SELECT count(*) FROM \"aclrule\"", "describe": { "columns": [ { @@ -16,5 +16,5 @@ null ] }, - "hash": "60a87e9d4f92298692c0a978f2077f7967909c3b13677177a5a0c87b6d07c0aa" + "hash": "0f0c3641e4c2d677f55b0b1878db9c0b9f25ead9ffcf5e1f534a65ec3c419f81" } diff --git a/.sqlx/query-12457bde75bdc75b7de71fc8af5350395b14466c1d2ba1500ca3662ebec06fce.json b/.sqlx/query-12457bde75bdc75b7de71fc8af5350395b14466c1d2ba1500ca3662ebec06fce.json new file mode 100644 index 0000000000..80d6465b7a --- /dev/null +++ b/.sqlx/query-12457bde75bdc75b7de71fc8af5350395b14466c1d2ba1500ca3662ebec06fce.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"rule_id\",\"device_id\",\"allow\" FROM \"aclruledevice\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "rule_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "device_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "allow", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "12457bde75bdc75b7de71fc8af5350395b14466c1d2ba1500ca3662ebec06fce" +} diff --git a/.sqlx/query-165afcf1829ca9b94e9f1299c388e10afae39018c4c1035b779585bf591698a4.json b/.sqlx/query-165afcf1829ca9b94e9f1299c388e10afae39018c4c1035b779585bf591698a4.json new file mode 100644 index 0000000000..728580479c --- /dev/null +++ b/.sqlx/query-165afcf1829ca9b94e9f1299c388e10afae39018c4c1035b779585bf591698a4.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"user_snat_binding\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "165afcf1829ca9b94e9f1299c388e10afae39018c4c1035b779585bf591698a4" +} diff --git a/.sqlx/query-19ad6391ddfb43ab2f1100aefab048e985a9c6ba7c02475756c3e40e9a66b450.json b/.sqlx/query-19ad6391ddfb43ab2f1100aefab048e985a9c6ba7c02475756c3e40e9a66b450.json new file mode 100644 index 0000000000..febbd693ef --- /dev/null +++ b/.sqlx/query-19ad6391ddfb43ab2f1100aefab048e985a9c6ba7c02475756c3e40e9a66b450.json @@ -0,0 +1,95 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"location_id\",\"name\",\"address\",\"port\",\"connected_at\",\"disconnected_at\",\"certificate\",\"certificate_expiry\",\"version\",\"enabled\",\"modified_at\",\"modified_by\" FROM \"gateway\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "location_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "address", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "port", + "type_info": "Int4" + }, + { + "ordinal": 5, + "name": "connected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 6, + "name": "disconnected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 7, + "name": "certificate", + "type_info": "Text" + }, + { + "ordinal": 8, + "name": "certificate_expiry", + "type_info": "Timestamp" + }, + { + "ordinal": 9, + "name": "version", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 11, + "name": "modified_at", + "type_info": "Timestamp" + }, + { + "ordinal": 12, + "name": "modified_by", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + true, + true, + true, + true, + false, + false, + false + ] + }, + "hash": "19ad6391ddfb43ab2f1100aefab048e985a9c6ba7c02475756c3e40e9a66b450" +} diff --git a/.sqlx/query-1ac151a3eda9dd4403b4291eaf9ce575db684cb2a32d6d8b987ddeb7125b7aba.json b/.sqlx/query-1ac151a3eda9dd4403b4291eaf9ce575db684cb2a32d6d8b987ddeb7125b7aba.json new file mode 100644 index 0000000000..8c21f8087e --- /dev/null +++ b/.sqlx/query-1ac151a3eda9dd4403b4291eaf9ce575db684cb2a32d6d8b987ddeb7125b7aba.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"authentication_key\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "1ac151a3eda9dd4403b4291eaf9ce575db684cb2a32d6d8b987ddeb7125b7aba" +} diff --git a/.sqlx/query-1d54c74b2023ac8cf2a6087ccf6518764abec197f1766049e1915c3c59be0411.json b/.sqlx/query-1d54c74b2023ac8cf2a6087ccf6518764abec197f1766049e1915c3c59be0411.json new file mode 100644 index 0000000000..7ac1a42a5c --- /dev/null +++ b/.sqlx/query-1d54c74b2023ac8cf2a6087ccf6518764abec197f1766049e1915c3c59be0411.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"location_id\",\"public_ip\" \"public_ip: IpAddr\" FROM \"user_snat_binding\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "location_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "public_ip: IpAddr", + "type_info": "Inet" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "1d54c74b2023ac8cf2a6087ccf6518764abec197f1766049e1915c3c59be0411" +} diff --git a/.sqlx/query-1defdac79fce737a99aede47230d952cd0ebfa02ace37f2010d44863c7217318.json b/.sqlx/query-1defdac79fce737a99aede47230d952cd0ebfa02ace37f2010d44863c7217318.json new file mode 100644 index 0000000000..253cc8db69 --- /dev/null +++ b/.sqlx/query-1defdac79fce737a99aede47230d952cd0ebfa02ace37f2010d44863c7217318.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"name\",\"passkey\" FROM \"webauthn\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "passkey", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "1defdac79fce737a99aede47230d952cd0ebfa02ace37f2010d44863c7217318" +} diff --git a/.sqlx/query-1ecd264d768ded66b7a5e84d4750b342c0e69e4ef1f836d824daa7d19d45cbe3.json b/.sqlx/query-1ecd264d768ded66b7a5e84d4750b342c0e69e4ef1f836d824daa7d19d45cbe3.json new file mode 100644 index 0000000000..26a9da4544 --- /dev/null +++ b/.sqlx/query-1ecd264d768ded66b7a5e84d4750b342c0e69e4ef1f836d824daa7d19d45cbe3.json @@ -0,0 +1,63 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"yubikey_id\",\"name\",\"user_id\",\"key\",\"key_type\" \"key_type: _\" FROM \"authentication_key\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "yubikey_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "key", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "key_type: _", + "type_info": { + "Custom": { + "name": "authentication_key_type", + "kind": { + "Enum": [ + "ssh", + "gpg" + ] + } + } + } + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + true, + true, + false, + false, + false + ] + }, + "hash": "1ecd264d768ded66b7a5e84d4750b342c0e69e4ef1f836d824daa7d19d45cbe3" +} diff --git a/.sqlx/query-200a90010f6790cd472074ee3e581516831adc8d7139c7085f2ad2bc380e1d2d.json b/.sqlx/query-200a90010f6790cd472074ee3e581516831adc8d7139c7085f2ad2bc380e1d2d.json new file mode 100644 index 0000000000..969e3a341d --- /dev/null +++ b/.sqlx/query-200a90010f6790cd472074ee3e581516831adc8d7139c7085f2ad2bc380e1d2d.json @@ -0,0 +1,150 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"parent_id\",\"state\" \"state: _\",\"name\",\"allow_all_users\",\"deny_all_users\",\"allow_all_groups\",\"deny_all_groups\",\"allow_all_network_devices\",\"deny_all_network_devices\",\"all_locations\",\"addresses\" \"addresses: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"enabled\",\"expires\",\"any_address\",\"any_port\",\"any_protocol\",\"use_manual_destination_settings\" FROM \"aclrule\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "parent_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "state: _", + "type_info": { + "Custom": { + "name": "aclrule_state", + "kind": { + "Enum": [ + "applied", + "new", + "modified", + "deleted", + "expired" + ] + } + } + } + }, + { + "ordinal": 3, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "allow_all_users", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "deny_all_users", + "type_info": "Bool" + }, + { + "ordinal": 6, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "deny_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "allow_all_network_devices", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "deny_all_network_devices", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "all_locations", + "type_info": "Bool" + }, + { + "ordinal": 11, + "name": "addresses: _", + "type_info": "InetArray" + }, + { + "ordinal": 12, + "name": "ports: _", + "type_info": "Int4RangeArray" + }, + { + "ordinal": 13, + "name": "protocols: _", + "type_info": "Int4Array" + }, + { + "ordinal": 14, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "expires", + "type_info": "Timestamp" + }, + { + "ordinal": 16, + "name": "any_address", + "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "any_protocol", + "type_info": "Bool" + }, + { + "ordinal": 19, + "name": "use_manual_destination_settings", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false + ] + }, + "hash": "200a90010f6790cd472074ee3e581516831adc8d7139c7085f2ad2bc380e1d2d" +} diff --git a/.sqlx/query-20adf2baa9956dd33289fde61cc2d48c573c0efbf73950059aedd67c5c56440d.json b/.sqlx/query-20adf2baa9956dd33289fde61cc2d48c573c0efbf73950059aedd67c5c56440d.json new file mode 100644 index 0000000000..8f01be2e68 --- /dev/null +++ b/.sqlx/query-20adf2baa9956dd33289fde61cc2d48c573c0efbf73950059aedd67c5c56440d.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"name\",\"serial\",\"user_id\" FROM \"yubikey\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "serial", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "user_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "20adf2baa9956dd33289fde61cc2d48c573c0efbf73950059aedd67c5c56440d" +} diff --git a/.sqlx/query-21c42638fa012bb17c4f41cf35a129325d271435cb1c7d97e9a58d064efcd78c.json b/.sqlx/query-21c42638fa012bb17c4f41cf35a129325d271435cb1c7d97e9a58d064efcd78c.json new file mode 100644 index 0000000000..070a681520 --- /dev/null +++ b/.sqlx/query-21c42638fa012bb17c4f41cf35a129325d271435cb1c7d97e9a58d064efcd78c.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"openidprovider\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "21c42638fa012bb17c4f41cf35a129325d271435cb1c7d97e9a58d064efcd78c" +} diff --git a/.sqlx/query-2343ba38a9b03cb5d630e0065624f7d3d26db7ed1252268326ec832957e0d973.json b/.sqlx/query-2343ba38a9b03cb5d630e0065624f7d3d26db7ed1252268326ec832957e0d973.json new file mode 100644 index 0000000000..ee00a9decf --- /dev/null +++ b/.sqlx/query-2343ba38a9b03cb5d630e0065624f7d3d26db7ed1252268326ec832957e0d973.json @@ -0,0 +1,32 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM device WHERE device_type = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + { + "Custom": { + "name": "device_type", + "kind": { + "Enum": [ + "user", + "network" + ] + } + } + } + ] + }, + "nullable": [ + null + ] + }, + "hash": "2343ba38a9b03cb5d630e0065624f7d3d26db7ed1252268326ec832957e0d973" +} diff --git a/.sqlx/query-266cbdce48f82432dfeafe895d8890d4591eac8851eb8001121badc2c9915e17.json b/.sqlx/query-266cbdce48f82432dfeafe895d8890d4591eac8851eb8001121badc2c9915e17.json new file mode 100644 index 0000000000..dd8d899a0c --- /dev/null +++ b/.sqlx/query-266cbdce48f82432dfeafe895d8890d4591eac8851eb8001121badc2c9915e17.json @@ -0,0 +1,86 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", configured FROM device WHERE device_type = $1 ORDER BY name LIMIT $2 OFFSET $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "wireguard_pubkey", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "created", + "type_info": "Timestamp" + }, + { + "ordinal": 5, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "device_type: DeviceType", + "type_info": { + "Custom": { + "name": "device_type", + "kind": { + "Enum": [ + "user", + "network" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "configured", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + { + "Custom": { + "name": "device_type", + "kind": { + "Enum": [ + "user", + "network" + ] + } + } + }, + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false + ] + }, + "hash": "266cbdce48f82432dfeafe895d8890d4591eac8851eb8001121badc2c9915e17" +} diff --git a/.sqlx/query-28df23ae807eec13b0271ceedaafb4e05135d602d2e411353085379b562811b3.json b/.sqlx/query-28df23ae807eec13b0271ceedaafb4e05135d602d2e411353085379b562811b3.json new file mode 100644 index 0000000000..e2b146c75c --- /dev/null +++ b/.sqlx/query-28df23ae807eec13b0271ceedaafb4e05135d602d2e411353085379b562811b3.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"gateway\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "28df23ae807eec13b0271ceedaafb4e05135d602d2e411353085379b562811b3" +} diff --git a/.sqlx/query-2d445dd29fbd4a993bdbe4f2a165f4729443afc99fb5a45c75a3b415eda9e877.json b/.sqlx/query-2d445dd29fbd4a993bdbe4f2a165f4729443afc99fb5a45c75a3b415eda9e877.json new file mode 100644 index 0000000000..4866a2a280 --- /dev/null +++ b/.sqlx/query-2d445dd29fbd4a993bdbe4f2a165f4729443afc99fb5a45c75a3b415eda9e877.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"aclrulealias\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "2d445dd29fbd4a993bdbe4f2a165f4729443afc99fb5a45c75a3b415eda9e877" +} diff --git a/.sqlx/query-2dfb78505182ad04c18910a74973c790ecf981fdd75a9c60944196d01747424d.json b/.sqlx/query-2dfb78505182ad04c18910a74973c790ecf981fdd75a9c60944196d01747424d.json new file mode 100644 index 0000000000..8e44ae455d --- /dev/null +++ b/.sqlx/query-2dfb78505182ad04c18910a74973c790ecf981fdd75a9c60944196d01747424d.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"wireguard_network\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "2dfb78505182ad04c18910a74973c790ecf981fdd75a9c60944196d01747424d" +} diff --git a/.sqlx/query-36489e53e85877cb7e58682bdf81b62c85229f8fca873c54f1b190b27cb37fda.json b/.sqlx/query-36489e53e85877cb7e58682bdf81b62c85229f8fca873c54f1b190b27cb37fda.json new file mode 100644 index 0000000000..1cccae62f4 --- /dev/null +++ b/.sqlx/query-36489e53e85877cb7e58682bdf81b62c85229f8fca873c54f1b190b27cb37fda.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"group\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "36489e53e85877cb7e58682bdf81b62c85229f8fca873c54f1b190b27cb37fda" +} diff --git a/.sqlx/query-376cfd25e7c3020ab7086de565fbb1feb7195d08aec4183a5bea8fd1565ece83.json b/.sqlx/query-376cfd25e7c3020ab7086de565fbb1feb7195d08aec4183a5bea8fd1565ece83.json new file mode 100644 index 0000000000..7615fbe277 --- /dev/null +++ b/.sqlx/query-376cfd25e7c3020ab7086de565fbb1feb7195d08aec4183a5bea8fd1565ece83.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"name\",\"stream_type\" \"stream_type: _\",\"config\" FROM \"activity_log_stream\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "stream_type: _", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "config", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "376cfd25e7c3020ab7086de565fbb1feb7195d08aec4183a5bea8fd1565ece83" +} diff --git a/.sqlx/query-3a53fb20d5f1aa1e2cfcb07e1e74a88a2c3cb9ec22a43f01a1bfa8a4307073da.json b/.sqlx/query-3a53fb20d5f1aa1e2cfcb07e1e74a88a2c3cb9ec22a43f01a1bfa8a4307073da.json new file mode 100644 index 0000000000..d78b6da74f --- /dev/null +++ b/.sqlx/query-3a53fb20d5f1aa1e2cfcb07e1e74a88a2c3cb9ec22a43f01a1bfa8a4307073da.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"aclruleuser\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "3a53fb20d5f1aa1e2cfcb07e1e74a88a2c3cb9ec22a43f01a1bfa8a4307073da" +} diff --git a/.sqlx/query-3c9fc8052c5c3f07f27843941dbbc23913f2f0efbc0dee72ae019798529fe2bb.json b/.sqlx/query-3c9fc8052c5c3f07f27843941dbbc23913f2f0efbc0dee72ae019798529fe2bb.json new file mode 100644 index 0000000000..e407850037 --- /dev/null +++ b/.sqlx/query-3c9fc8052c5c3f07f27843941dbbc23913f2f0efbc0dee72ae019798529fe2bb.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"rule_id\",\"network_id\" FROM \"aclrulenetwork\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "rule_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "network_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "3c9fc8052c5c3f07f27843941dbbc23913f2f0efbc0dee72ae019798529fe2bb" +} diff --git a/.sqlx/query-3f884d7e2275627cccb6edde7dab3fd8d751c34728fa5a1040a4f1c241050f9a.json b/.sqlx/query-3f884d7e2275627cccb6edde7dab3fd8d751c34728fa5a1040a4f1c241050f9a.json new file mode 100644 index 0000000000..6fbbecef24 --- /dev/null +++ b/.sqlx/query-3f884d7e2275627cccb6edde7dab3fd8d751c34728fa5a1040a4f1c241050f9a.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"yubikey\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "3f884d7e2275627cccb6edde7dab3fd8d751c34728fa5a1040a4f1c241050f9a" +} diff --git a/.sqlx/query-41ed73ad49a5004227995746daac0f8ccd22ce1744cd5173c9ae85080cb47031.json b/.sqlx/query-41ed73ad49a5004227995746daac0f8ccd22ce1744cd5173c9ae85080cb47031.json new file mode 100644 index 0000000000..36a48cb44f --- /dev/null +++ b/.sqlx/query-41ed73ad49a5004227995746daac0f8ccd22ce1744cd5173c9ae85080cb47031.json @@ -0,0 +1,59 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"client_id\",\"client_secret\",\"redirect_uri\" \"redirect_uri: _\",\"scope\" \"scope: _\",\"name\",\"enabled\" FROM \"oauth2client\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "client_id", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "client_secret", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "redirect_uri: _", + "type_info": "TextArray" + }, + { + "ordinal": 4, + "name": "scope: _", + "type_info": "TextArray" + }, + { + "ordinal": 5, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "enabled", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "41ed73ad49a5004227995746daac0f8ccd22ce1744cd5173c9ae85080cb47031" +} diff --git a/.sqlx/query-4ac58f741639f60ddd54e71be4186a16173d2a2a7d5643dbadc20495320df0b2.json b/.sqlx/query-4ac58f741639f60ddd54e71be4186a16173d2a2a7d5643dbadc20495320df0b2.json new file mode 100644 index 0000000000..d39c4afd4e --- /dev/null +++ b/.sqlx/query-4ac58f741639f60ddd54e71be4186a16173d2a2a7d5643dbadc20495320df0b2.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"webhook\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "4ac58f741639f60ddd54e71be4186a16173d2a2a7d5643dbadc20495320df0b2" +} diff --git a/.sqlx/query-4b5d65bf132381ced1529def2b457c33fb274ca003c5038f20cc41309fe4a887.json b/.sqlx/query-4b5d65bf132381ced1529def2b457c33fb274ca003c5038f20cc41309fe4a887.json new file mode 100644 index 0000000000..86c2b72a29 --- /dev/null +++ b/.sqlx/query-4b5d65bf132381ced1529def2b457c33fb274ca003c5038f20cc41309fe4a887.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"pub_key\",\"device_id\" FROM \"biometric_auth\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "pub_key", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "device_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "4b5d65bf132381ced1529def2b457c33fb274ca003c5038f20cc41309fe4a887" +} diff --git a/.sqlx/query-4ef0f7f062273c6be0de900c481871dc849a9bca1f2b9af9bccb1a660ac560a6.json b/.sqlx/query-4ef0f7f062273c6be0de900c481871dc849a9bca1f2b9af9bccb1a660ac560a6.json new file mode 100644 index 0000000000..64a746342a --- /dev/null +++ b/.sqlx/query-4ef0f7f062273c6be0de900c481871dc849a9bca1f2b9af9bccb1a660ac560a6.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"vpn_session_stats\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "4ef0f7f062273c6be0de900c481871dc849a9bca1f2b9af9bccb1a660ac560a6" +} diff --git a/.sqlx/query-50454bc68dc576e6851d69d2f6607ac85ff95cfdd8a2077f1cb3ecb35b50cfb0.json b/.sqlx/query-50454bc68dc576e6851d69d2f6607ac85ff95cfdd8a2077f1cb3ecb35b50cfb0.json new file mode 100644 index 0000000000..8272a7c0a9 --- /dev/null +++ b/.sqlx/query-50454bc68dc576e6851d69d2f6607ac85ff95cfdd8a2077f1cb3ecb35b50cfb0.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"rule_id\",\"group_id\",\"allow\" FROM \"aclrulegroup\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "rule_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "group_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "allow", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "50454bc68dc576e6851d69d2f6607ac85ff95cfdd8a2077f1cb3ecb35b50cfb0" +} diff --git a/.sqlx/query-51ee8e7b679a3016d766650b4660cb654e233b6482388e464b43cd12dce91a26.json b/.sqlx/query-51ee8e7b679a3016d766650b4660cb654e233b6482388e464b43cd12dce91a26.json new file mode 100644 index 0000000000..f40be7142a --- /dev/null +++ b/.sqlx/query-51ee8e7b679a3016d766650b4660cb654e233b6482388e464b43cd12dce91a26.json @@ -0,0 +1,184 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"name\",\"base_url\",\"kind\" \"kind: _\",\"client_id\",\"client_secret\",\"display_name\",\"google_service_account_key\",\"google_service_account_email\",\"admin_email\",\"directory_sync_enabled\",\"directory_sync_interval\",\"directory_sync_user_behavior\" \"directory_sync_user_behavior: _\",\"directory_sync_admin_behavior\" \"directory_sync_admin_behavior: _\",\"directory_sync_target\" \"directory_sync_target: _\",\"okta_private_jwk\",\"okta_dirsync_client_id\",\"directory_sync_group_match\" \"directory_sync_group_match: _\",\"jumpcloud_api_key\",\"prefetch_users\" FROM \"openidprovider\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "base_url", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "kind: _", + "type_info": { + "Custom": { + "name": "openid_provider_kind", + "kind": { + "Enum": [ + "Custom", + "Google", + "Microsoft", + "Okta", + "JumpCloud", + "Zitadel" + ] + } + } + } + }, + { + "ordinal": 4, + "name": "client_id", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "client_secret", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "display_name", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "google_service_account_key", + "type_info": "Text" + }, + { + "ordinal": 8, + "name": "google_service_account_email", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "admin_email", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "directory_sync_enabled", + "type_info": "Bool" + }, + { + "ordinal": 11, + "name": "directory_sync_interval", + "type_info": "Int4" + }, + { + "ordinal": 12, + "name": "directory_sync_user_behavior: _", + "type_info": { + "Custom": { + "name": "dirsync_user_behavior", + "kind": { + "Enum": [ + "keep", + "disable", + "delete" + ] + } + } + } + }, + { + "ordinal": 13, + "name": "directory_sync_admin_behavior: _", + "type_info": { + "Custom": { + "name": "dirsync_user_behavior", + "kind": { + "Enum": [ + "keep", + "disable", + "delete" + ] + } + } + } + }, + { + "ordinal": 14, + "name": "directory_sync_target: _", + "type_info": { + "Custom": { + "name": "dirsync_target", + "kind": { + "Enum": [ + "all", + "users", + "groups" + ] + } + } + } + }, + { + "ordinal": 15, + "name": "okta_private_jwk", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "okta_dirsync_client_id", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "directory_sync_group_match: _", + "type_info": "TextArray" + }, + { + "ordinal": 18, + "name": "jumpcloud_api_key", + "type_info": "Text" + }, + { + "ordinal": 19, + "name": "prefetch_users", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false + ] + }, + "hash": "51ee8e7b679a3016d766650b4660cb654e233b6482388e464b43cd12dce91a26" +} diff --git a/.sqlx/query-586825cc1bb32392602980b89a02c67f2d6e9bb7ff8a597dfb3227dccf70dd8f.json b/.sqlx/query-586825cc1bb32392602980b89a02c67f2d6e9bb7ff8a597dfb3227dccf70dd8f.json new file mode 100644 index 0000000000..5e450ca9eb --- /dev/null +++ b/.sqlx/query-586825cc1bb32392602980b89a02c67f2d6e9bb7ff8a597dfb3227dccf70dd8f.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"activity_log_stream\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "586825cc1bb32392602980b89a02c67f2d6e9bb7ff8a597dfb3227dccf70dd8f" +} diff --git a/.sqlx/query-618d9453983f09c9aad6ca6fb15d6558006c515883df414e31030031d8d3ab87.json b/.sqlx/query-618d9453983f09c9aad6ca6fb15d6558006c515883df414e31030031d8d3ab87.json new file mode 100644 index 0000000000..38f0ba0d75 --- /dev/null +++ b/.sqlx/query-618d9453983f09c9aad6ca6fb15d6558006c515883df414e31030031d8d3ab87.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"oauth2authorizedapp\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "618d9453983f09c9aad6ca6fb15d6558006c515883df414e31030031d8d3ab87" +} diff --git a/.sqlx/query-6501f1901bd1c0ea058f57f7d6d66d93f52bf976ca867a437323c9d608e1dca9.json b/.sqlx/query-6501f1901bd1c0ea058f57f7d6d66d93f52bf976ca867a437323c9d608e1dca9.json new file mode 100644 index 0000000000..cdb5a530fd --- /dev/null +++ b/.sqlx/query-6501f1901bd1c0ea058f57f7d6d66d93f52bf976ca867a437323c9d608e1dca9.json @@ -0,0 +1,77 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"ip_address\",\"model\",\"family\",\"brand\",\"os_family\",\"browser\",\"event_type\",\"created\" FROM \"device_login_event\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "ip_address", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "model", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "family", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "brand", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "os_family", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "browser", + "type_info": "Text" + }, + { + "ordinal": 8, + "name": "event_type", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "created", + "type_info": "Timestamp" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + true, + false, + true, + false, + false, + false, + false + ] + }, + "hash": "6501f1901bd1c0ea058f57f7d6d66d93f52bf976ca867a437323c9d608e1dca9" +} diff --git a/.sqlx/query-678a18e6f757c7bd3963363170c41fcdf9d49a068ba62c935661b52d447d1886.json b/.sqlx/query-678a18e6f757c7bd3963363170c41fcdf9d49a068ba62c935661b52d447d1886.json new file mode 100644 index 0000000000..b0474e1441 --- /dev/null +++ b/.sqlx/query-678a18e6f757c7bd3963363170c41fcdf9d49a068ba62c935661b52d447d1886.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"proxy\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "678a18e6f757c7bd3963363170c41fcdf9d49a068ba62c935661b52d447d1886" +} diff --git a/.sqlx/query-70c807776ae7493a9092a2c7e1ff91a2fe6af723ac47149bd48c500308d07c37.json b/.sqlx/query-70c807776ae7493a9092a2c7e1ff91a2fe6af723ac47149bd48c500308d07c37.json new file mode 100644 index 0000000000..a71d264b97 --- /dev/null +++ b/.sqlx/query-70c807776ae7493a9092a2c7e1ff91a2fe6af723ac47149bd48c500308d07c37.json @@ -0,0 +1,71 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"url\",\"description\",\"token\",\"enabled\",\"on_user_created\",\"on_user_deleted\",\"on_user_modified\",\"on_hwkey_provision\" FROM \"webhook\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "url", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "token", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "on_user_created", + "type_info": "Bool" + }, + { + "ordinal": 6, + "name": "on_user_deleted", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "on_user_modified", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "on_hwkey_provision", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "70c807776ae7493a9092a2c7e1ff91a2fe6af723ac47149bd48c500308d07c37" +} diff --git a/.sqlx/query-723c4cadc9212c4c64171642a6c16bb0e2a54479b73b3e543483407ca70be765.json b/.sqlx/query-723c4cadc9212c4c64171642a6c16bb0e2a54479b73b3e543483407ca70be765.json new file mode 100644 index 0000000000..ee3fa934e6 --- /dev/null +++ b/.sqlx/query-723c4cadc9212c4c64171642a6c16bb0e2a54479b73b3e543483407ca70be765.json @@ -0,0 +1,101 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"location_id\",\"user_id\",\"device_id\",\"created_at\",\"connected_at\",\"disconnected_at\",\"mfa_method\" \"mfa_method?: _\",\"state\" \"state: _\",\"preshared_key\" FROM \"vpn_client_session\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "location_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "device_id", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "created_at", + "type_info": "Timestamp" + }, + { + "ordinal": 5, + "name": "connected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 6, + "name": "disconnected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 7, + "name": "mfa_method?: _", + "type_info": { + "Custom": { + "name": "vpn_client_mfa_method", + "kind": { + "Enum": [ + "totp", + "email", + "oidc", + "biometric", + "mobileapprove" + ] + } + } + } + }, + { + "ordinal": 8, + "name": "state: _", + "type_info": { + "Custom": { + "name": "vpn_client_session_state", + "kind": { + "Enum": [ + "new", + "connected", + "disconnected" + ] + } + } + } + }, + { + "ordinal": 9, + "name": "preshared_key", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + true, + true, + false, + true + ] + }, + "hash": "723c4cadc9212c4c64171642a6c16bb0e2a54479b73b3e543483407ca70be765" +} diff --git a/.sqlx/query-779bab5c5182cedc94fde97d8de2cd52eaee9adf329bcb5b8750e667be0fb720.json b/.sqlx/query-779bab5c5182cedc94fde97d8de2cd52eaee9adf329bcb5b8750e667be0fb720.json new file mode 100644 index 0000000000..84a997de10 --- /dev/null +++ b/.sqlx/query-779bab5c5182cedc94fde97d8de2cd52eaee9adf329bcb5b8750e667be0fb720.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"rule_id\",\"alias_id\" FROM \"aclrulealias\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "rule_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "alias_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "779bab5c5182cedc94fde97d8de2cd52eaee9adf329bcb5b8750e667be0fb720" +} diff --git a/.sqlx/query-78aa7073f678fae880f556d20d55976fc8e6356897b9461ff93b5f7a7942852e.json b/.sqlx/query-78aa7073f678fae880f556d20d55976fc8e6356897b9461ff93b5f7a7942852e.json new file mode 100644 index 0000000000..af3d1df941 --- /dev/null +++ b/.sqlx/query-78aa7073f678fae880f556d20d55976fc8e6356897b9461ff93b5f7a7942852e.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"biometric_auth\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "78aa7073f678fae880f556d20d55976fc8e6356897b9461ff93b5f7a7942852e" +} diff --git a/.sqlx/query-7bced9356f619689d57d972bb722f36a5126005e2aa86a4d8a54111ae054b0c5.json b/.sqlx/query-7bced9356f619689d57d972bb722f36a5126005e2aa86a4d8a54111ae054b0c5.json new file mode 100644 index 0000000000..c599657181 --- /dev/null +++ b/.sqlx/query-7bced9356f619689d57d972bb722f36a5126005e2aa86a4d8a54111ae054b0c5.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"pollingtoken\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "7bced9356f619689d57d972bb722f36a5126005e2aa86a4d8a54111ae054b0c5" +} diff --git a/.sqlx/query-804b9751f53ad69e73ffa8adcb49e5aa3193569f98100b2b4021eadf284b14e3.json b/.sqlx/query-804b9751f53ad69e73ffa8adcb49e5aa3193569f98100b2b4021eadf284b14e3.json new file mode 100644 index 0000000000..eb52281fe2 --- /dev/null +++ b/.sqlx/query-804b9751f53ad69e73ffa8adcb49e5aa3193569f98100b2b4021eadf284b14e3.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"oauth2client_id\" FROM \"oauth2authorizedapp\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "oauth2client_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "804b9751f53ad69e73ffa8adcb49e5aa3193569f98100b2b4021eadf284b14e3" +} diff --git a/.sqlx/query-81bd7091de10030f2c9bce6251a26a33e595ba9adc491e2ac33e7bb1b37352e6.json b/.sqlx/query-81bd7091de10030f2c9bce6251a26a33e595ba9adc491e2ac33e7bb1b37352e6.json new file mode 100644 index 0000000000..cbc3002970 --- /dev/null +++ b/.sqlx/query-81bd7091de10030f2c9bce6251a26a33e595ba9adc491e2ac33e7bb1b37352e6.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"webauthn\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "81bd7091de10030f2c9bce6251a26a33e595ba9adc491e2ac33e7bb1b37352e6" +} diff --git a/.sqlx/query-862f736d8bf5176fbc1389273b76ba752e474117151853e7f8e31c8ab728c0e1.json b/.sqlx/query-862f736d8bf5176fbc1389273b76ba752e474117151853e7f8e31c8ab728c0e1.json new file mode 100644 index 0000000000..42df74937a --- /dev/null +++ b/.sqlx/query-862f736d8bf5176fbc1389273b76ba752e474117151853e7f8e31c8ab728c0e1.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"device_login_event\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "862f736d8bf5176fbc1389273b76ba752e474117151853e7f8e31c8ab728c0e1" +} diff --git a/.sqlx/query-99bb839e4c27e4cb3b27e48cc15860bb6653c49196296923733d946b865fe711.json b/.sqlx/query-99bb839e4c27e4cb3b27e48cc15860bb6653c49196296923733d946b865fe711.json new file mode 100644 index 0000000000..d00b836ca5 --- /dev/null +++ b/.sqlx/query-99bb839e4c27e4cb3b27e48cc15860bb6653c49196296923733d946b865fe711.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"aclruledevice\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "99bb839e4c27e4cb3b27e48cc15860bb6653c49196296923733d946b865fe711" +} diff --git a/.sqlx/query-9acb3537b3f24b8ff0984329139c438c6876553444e2e844e45feaaa7757414d.json b/.sqlx/query-9acb3537b3f24b8ff0984329139c438c6876553444e2e844e45feaaa7757414d.json new file mode 100644 index 0000000000..bb636935f8 --- /dev/null +++ b/.sqlx/query-9acb3537b3f24b8ff0984329139c438c6876553444e2e844e45feaaa7757414d.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"user\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "9acb3537b3f24b8ff0984329139c438c6876553444e2e844e45feaaa7757414d" +} diff --git a/.sqlx/query-9ee5fda54016698095d0d300ea795fd3df00c02dc57cd398c85c6da32bf2af8f.json b/.sqlx/query-9ee5fda54016698095d0d300ea795fd3df00c02dc57cd398c85c6da32bf2af8f.json new file mode 100644 index 0000000000..8edf4c27de --- /dev/null +++ b/.sqlx/query-9ee5fda54016698095d0d300ea795fd3df00c02dc57cd398c85c6da32bf2af8f.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"token\",\"device_id\",\"created_at\" FROM \"pollingtoken\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "token", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "device_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamp" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "9ee5fda54016698095d0d300ea795fd3df00c02dc57cd398c85c6da32bf2af8f" +} diff --git a/.sqlx/query-a7a7d43d4125a983301f3fb7f534c13cebfbadecd3e46b4772e66a8103d0e5be.json b/.sqlx/query-a7a7d43d4125a983301f3fb7f534c13cebfbadecd3e46b4772e66a8103d0e5be.json new file mode 100644 index 0000000000..2fa6db5877 --- /dev/null +++ b/.sqlx/query-a7a7d43d4125a983301f3fb7f534c13cebfbadecd3e46b4772e66a8103d0e5be.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"api_token\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "a7a7d43d4125a983301f3fb7f534c13cebfbadecd3e46b4772e66a8103d0e5be" +} diff --git a/.sqlx/query-ad0262ed3631b5bcdb6b872c6875efc89f1a0183676780aa4e3693c219daf516.json b/.sqlx/query-ad0262ed3631b5bcdb6b872c6875efc89f1a0183676780aa4e3693c219daf516.json new file mode 100644 index 0000000000..53d3b9146f --- /dev/null +++ b/.sqlx/query-ad0262ed3631b5bcdb6b872c6875efc89f1a0183676780aa4e3693c219daf516.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"aclrulegroup\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "ad0262ed3631b5bcdb6b872c6875efc89f1a0183676780aa4e3693c219daf516" +} diff --git a/.sqlx/query-adbb2df90cce8e2693f0339b03915b4f14320ee584bea25145b6e35558d41cc3.json b/.sqlx/query-adbb2df90cce8e2693f0339b03915b4f14320ee584bea25145b6e35558d41cc3.json new file mode 100644 index 0000000000..da4a9fe0cf --- /dev/null +++ b/.sqlx/query-adbb2df90cce8e2693f0339b03915b4f14320ee584bea25145b6e35558d41cc3.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"device\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "adbb2df90cce8e2693f0339b03915b4f14320ee584bea25145b6e35558d41cc3" +} diff --git a/.sqlx/query-bb1a2dd0364e14174e66faca34180d00030cc90495fa21c3745051c4b66017d5.json b/.sqlx/query-bb1a2dd0364e14174e66faca34180d00030cc90495fa21c3745051c4b66017d5.json new file mode 100644 index 0000000000..e639d52795 --- /dev/null +++ b/.sqlx/query-bb1a2dd0364e14174e66faca34180d00030cc90495fa21c3745051c4b66017d5.json @@ -0,0 +1,95 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"timestamp\",\"user_id\",\"username\",\"location\",\"ip\" \"ip?: _\",\"event\" \"event: _\",\"module\" \"module: _\",\"device\",\"description\",\"metadata\" FROM \"activity_log_event\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "timestamp", + "type_info": "Timestamp" + }, + { + "ordinal": 2, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "username", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "location", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "ip?: _", + "type_info": "Inet" + }, + { + "ordinal": 6, + "name": "event: _", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "module: _", + "type_info": { + "Custom": { + "name": "activity_log_module", + "kind": { + "Enum": [ + "defguard", + "client", + "vpn", + "enrollment" + ] + } + } + } + }, + { + "ordinal": 8, + "name": "device", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "metadata", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + false, + false, + true, + true + ] + }, + "hash": "bb1a2dd0364e14174e66faca34180d00030cc90495fa21c3745051c4b66017d5" +} diff --git a/.sqlx/query-bb9dc6f26b8eb0fd0217ef9753eadf9f61409818cc9ae3ca31e1d5fbd1236357.json b/.sqlx/query-bb9dc6f26b8eb0fd0217ef9753eadf9f61409818cc9ae3ca31e1d5fbd1236357.json new file mode 100644 index 0000000000..77775bac74 --- /dev/null +++ b/.sqlx/query-bb9dc6f26b8eb0fd0217ef9753eadf9f61409818cc9ae3ca31e1d5fbd1236357.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"authorization_code\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "bb9dc6f26b8eb0fd0217ef9753eadf9f61409818cc9ae3ca31e1d5fbd1236357" +} diff --git a/.sqlx/query-bdb1cfa836d5f288e5409bf95366b7ec6694c76a9b3a2563395aaf97856a4719.json b/.sqlx/query-bdb1cfa836d5f288e5409bf95366b7ec6694c76a9b3a2563395aaf97856a4719.json new file mode 100644 index 0000000000..7fe43f8d63 --- /dev/null +++ b/.sqlx/query-bdb1cfa836d5f288e5409bf95366b7ec6694c76a9b3a2563395aaf97856a4719.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"name\",\"is_admin\" FROM \"group\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "is_admin", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "bdb1cfa836d5f288e5409bf95366b7ec6694c76a9b3a2563395aaf97856a4719" +} diff --git a/.sqlx/query-c26d84875185d0edfd825fc0d88043e5032a3b5f0e93f4911ad282df0e4dc909.json b/.sqlx/query-c26d84875185d0edfd825fc0d88043e5032a3b5f0e93f4911ad282df0e4dc909.json new file mode 100644 index 0000000000..74f6e46383 --- /dev/null +++ b/.sqlx/query-c26d84875185d0edfd825fc0d88043e5032a3b5f0e93f4911ad282df0e4dc909.json @@ -0,0 +1,77 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"session_id\",\"gateway_id\",\"collected_at\",\"latest_handshake\",\"endpoint\",\"total_upload\",\"total_download\",\"upload_diff\",\"download_diff\" FROM \"vpn_session_stats\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "session_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "gateway_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "collected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 4, + "name": "latest_handshake", + "type_info": "Timestamp" + }, + { + "ordinal": 5, + "name": "endpoint", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "total_upload", + "type_info": "Int8" + }, + { + "ordinal": 7, + "name": "total_download", + "type_info": "Int8" + }, + { + "ordinal": 8, + "name": "upload_diff", + "type_info": "Int8" + }, + { + "ordinal": 9, + "name": "download_diff", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "c26d84875185d0edfd825fc0d88043e5032a3b5f0e93f4911ad282df0e4dc909" +} diff --git a/.sqlx/query-c2bf39775cc4142f18165b1e342731b0d6b7370543b5c5e8cabdfd47487a3056.json b/.sqlx/query-c2bf39775cc4142f18165b1e342731b0d6b7370543b5c5e8cabdfd47487a3056.json new file mode 100644 index 0000000000..a1db3a8fba --- /dev/null +++ b/.sqlx/query-c2bf39775cc4142f18165b1e342731b0d6b7370543b5c5e8cabdfd47487a3056.json @@ -0,0 +1,89 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"name\",\"address\",\"port\",\"connected_at\",\"disconnected_at\",\"version\",\"enabled\",\"certificate\",\"certificate_expiry\",\"modified_at\",\"modified_by\" FROM \"proxy\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "address", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "port", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "connected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 5, + "name": "disconnected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 6, + "name": "version", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "certificate", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "certificate_expiry", + "type_info": "Timestamp" + }, + { + "ordinal": 10, + "name": "modified_at", + "type_info": "Timestamp" + }, + { + "ordinal": 11, + "name": "modified_by", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + true, + false, + true, + true, + false, + false + ] + }, + "hash": "c2bf39775cc4142f18165b1e342731b0d6b7370543b5c5e8cabdfd47487a3056" +} diff --git a/.sqlx/query-c596fb41eda368b05afa2db5d960ed303def29a0ed50b6aac6113385e6bd0845.json b/.sqlx/query-c596fb41eda368b05afa2db5d960ed303def29a0ed50b6aac6113385e6bd0845.json new file mode 100644 index 0000000000..172268fe47 --- /dev/null +++ b/.sqlx/query-c596fb41eda368b05afa2db5d960ed303def29a0ed50b6aac6113385e6bd0845.json @@ -0,0 +1,153 @@ +{ + "db_name": "PostgreSQL", + "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\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "address: _", + "type_info": "InetArray" + }, + { + "ordinal": 3, + "name": "port", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "pubkey", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "prvkey", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "endpoint", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "dns", + "type_info": "Text" + }, + { + "ordinal": 8, + "name": "mtu", + "type_info": "Int4" + }, + { + "ordinal": 9, + "name": "fwmark", + "type_info": "Int8" + }, + { + "ordinal": 10, + "name": "allowed_ips: _", + "type_info": "InetArray" + }, + { + "ordinal": 11, + "name": "allow_all_groups", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "connected_at", + "type_info": "Timestamp" + }, + { + "ordinal": 13, + "name": "acl_enabled", + "type_info": "Bool" + }, + { + "ordinal": 14, + "name": "acl_default_allow", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "keepalive_interval", + "type_info": "Int4" + }, + { + "ordinal": 16, + "name": "peer_disconnect_threshold", + "type_info": "Int4" + }, + { + "ordinal": 17, + "name": "location_mfa_mode: _", + "type_info": { + "Custom": { + "name": "location_mfa_mode", + "kind": { + "Enum": [ + "disabled", + "internal", + "external" + ] + } + } + } + }, + { + "ordinal": 18, + "name": "service_location_mode: _", + "type_info": { + "Custom": { + "name": "service_location_mode", + "kind": { + "Enum": [ + "disabled", + "prelogon", + "alwayson" + ] + } + } + } + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "c596fb41eda368b05afa2db5d960ed303def29a0ed50b6aac6113385e6bd0845" +} diff --git a/.sqlx/query-d6f3acf04ede4e44799cc45a5518787ef696cf528fbd0d41bb4662cf3c28a555.json b/.sqlx/query-d6f3acf04ede4e44799cc45a5518787ef696cf528fbd0d41bb4662cf3c28a555.json new file mode 100644 index 0000000000..8c2c27f750 --- /dev/null +++ b/.sqlx/query-d6f3acf04ede4e44799cc45a5518787ef696cf528fbd0d41bb4662cf3c28a555.json @@ -0,0 +1,47 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"created_at\",\"name\",\"token_hash\" FROM \"api_token\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "created_at", + "type_info": "Timestamp" + }, + { + "ordinal": 3, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "token_hash", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "d6f3acf04ede4e44799cc45a5518787ef696cf528fbd0d41bb4662cf3c28a555" +} diff --git a/.sqlx/query-ea19fb19954fefe18c5e6b38e8a434abc8e833105cd4ccfbbe9ea2aa4cb5a78b.json b/.sqlx/query-ea19fb19954fefe18c5e6b38e8a434abc8e833105cd4ccfbbe9ea2aa4cb5a78b.json new file mode 100644 index 0000000000..a46308c760 --- /dev/null +++ b/.sqlx/query-ea19fb19954fefe18c5e6b38e8a434abc8e833105cd4ccfbbe9ea2aa4cb5a78b.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"vpn_client_session\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "ea19fb19954fefe18c5e6b38e8a434abc8e833105cd4ccfbbe9ea2aa4cb5a78b" +} diff --git a/.sqlx/query-eb84d653ce75cd9124e26eeb147a8f11aecbf5ef4296d00ba3f4e9b9c64165da.json b/.sqlx/query-eb84d653ce75cd9124e26eeb147a8f11aecbf5ef4296d00ba3f4e9b9c64165da.json new file mode 100644 index 0000000000..bfe4d536fe --- /dev/null +++ b/.sqlx/query-eb84d653ce75cd9124e26eeb147a8f11aecbf5ef4296d00ba3f4e9b9c64165da.json @@ -0,0 +1,155 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"username\",\"password_hash\",\"last_name\",\"first_name\",\"email\",\"phone\",\"mfa_enabled\",\"is_active\",\"from_ldap\",\"ldap_pass_randomized\",\"ldap_rdn\",\"ldap_user_path\",\"openid_sub\",\"totp_enabled\",\"email_mfa_enabled\",\"totp_secret\",\"email_mfa_secret\",\"mfa_method\" \"mfa_method: _\",\"recovery_codes\" \"recovery_codes: _\",\"enrollment_pending\" FROM \"user\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "password_hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "last_name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "first_name", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "email", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "phone", + "type_info": "Text" + }, + { + "ordinal": 7, + "name": "mfa_enabled", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "is_active", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "from_ldap", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "ldap_pass_randomized", + "type_info": "Bool" + }, + { + "ordinal": 11, + "name": "ldap_rdn", + "type_info": "Text" + }, + { + "ordinal": 12, + "name": "ldap_user_path", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "openid_sub", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "totp_enabled", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "email_mfa_enabled", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "totp_secret", + "type_info": "Bytea" + }, + { + "ordinal": 17, + "name": "email_mfa_secret", + "type_info": "Bytea" + }, + { + "ordinal": 18, + "name": "mfa_method: _", + "type_info": { + "Custom": { + "name": "mfa_method", + "kind": { + "Enum": [ + "none", + "one_time_password", + "webauthn", + "email" + ] + } + } + } + }, + { + "ordinal": 19, + "name": "recovery_codes: _", + "type_info": "TextArray" + }, + { + "ordinal": 20, + "name": "enrollment_pending", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + true, + false, + false, + false, + false, + true, + true, + true, + false, + false, + true, + true, + false, + false, + false + ] + }, + "hash": "eb84d653ce75cd9124e26eeb147a8f11aecbf5ef4296d00ba3f4e9b9c64165da" +} diff --git a/.sqlx/query-ee810e6295ffa65a10bf5d2692caa59f15827d148396dba50b117d62719d279c.json b/.sqlx/query-ee810e6295ffa65a10bf5d2692caa59f15827d148396dba50b117d62719d279c.json new file mode 100644 index 0000000000..cbb4cff31d --- /dev/null +++ b/.sqlx/query-ee810e6295ffa65a10bf5d2692caa59f15827d148396dba50b117d62719d279c.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"activity_log_event\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "ee810e6295ffa65a10bf5d2692caa59f15827d148396dba50b117d62719d279c" +} diff --git a/.sqlx/query-f4c443edb5bcba45fd17a2572c301dfbd5c5ae75e86a41693f84b7c7e610fe29.json b/.sqlx/query-f4c443edb5bcba45fd17a2572c301dfbd5c5ae75e86a41693f84b7c7e610fe29.json new file mode 100644 index 0000000000..b86c9c8834 --- /dev/null +++ b/.sqlx/query-f4c443edb5bcba45fd17a2572c301dfbd5c5ae75e86a41693f84b7c7e610fe29.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"aclalias\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "f4c443edb5bcba45fd17a2572c301dfbd5c5ae75e86a41693f84b7c7e610fe29" +} diff --git a/.sqlx/query-f73139bb69e31208db0598d50325f660b0cdafc00e7e729d8fc8357ddf5d9aa7.json b/.sqlx/query-f73139bb69e31208db0598d50325f660b0cdafc00e7e729d8fc8357ddf5d9aa7.json new file mode 100644 index 0000000000..8d8cf40c1c --- /dev/null +++ b/.sqlx/query-f73139bb69e31208db0598d50325f660b0cdafc00e7e729d8fc8357ddf5d9aa7.json @@ -0,0 +1,71 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"client_id\",\"code\",\"redirect_uri\",\"scope\",\"auth_time\",\"nonce\",\"code_challenge\" FROM \"authorization_code\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "client_id", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "code", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "redirect_uri", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "scope", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "auth_time", + "type_info": "Int8" + }, + { + "ordinal": 7, + "name": "nonce", + "type_info": "Text" + }, + { + "ordinal": 8, + "name": "code_challenge", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + true, + true + ] + }, + "hash": "f73139bb69e31208db0598d50325f660b0cdafc00e7e729d8fc8357ddf5d9aa7" +} diff --git a/.sqlx/query-fb76746f9e3830edad26fab14a9690c61b8b81d92175b34238e72ae8becc3c11.json b/.sqlx/query-fb76746f9e3830edad26fab14a9690c61b8b81d92175b34238e72ae8becc3c11.json new file mode 100644 index 0000000000..a6cda2db07 --- /dev/null +++ b/.sqlx/query-fb76746f9e3830edad26fab14a9690c61b8b81d92175b34238e72ae8becc3c11.json @@ -0,0 +1,103 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"parent_id\",\"name\",\"kind\" \"kind: _\",\"state\" \"state: _\",\"addresses\" \"addresses: _\",\"ports\" \"ports: _\",\"protocols\" \"protocols: _\",\"any_address\",\"any_port\",\"any_protocol\" FROM \"aclalias\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "parent_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "kind: _", + "type_info": { + "Custom": { + "name": "aclalias_kind", + "kind": { + "Enum": [ + "destination", + "component" + ] + } + } + } + }, + { + "ordinal": 4, + "name": "state: _", + "type_info": { + "Custom": { + "name": "aclalias_state", + "kind": { + "Enum": [ + "applied", + "modified" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "addresses: _", + "type_info": "InetArray" + }, + { + "ordinal": 6, + "name": "ports: _", + "type_info": "Int4RangeArray" + }, + { + "ordinal": 7, + "name": "protocols: _", + "type_info": "Int4Array" + }, + { + "ordinal": 8, + "name": "any_address", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "any_port", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "any_protocol", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "fb76746f9e3830edad26fab14a9690c61b8b81d92175b34238e72ae8becc3c11" +} diff --git a/.sqlx/query-fbacf1945597ce0155c838d144e15a3bd31e4a90c464ee5fc76e01096672ddb9.json b/.sqlx/query-fbacf1945597ce0155c838d144e15a3bd31e4a90c464ee5fc76e01096672ddb9.json new file mode 100644 index 0000000000..5f32362778 --- /dev/null +++ b/.sqlx/query-fbacf1945597ce0155c838d144e15a3bd31e4a90c464ee5fc76e01096672ddb9.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"rule_id\",\"user_id\",\"allow\" FROM \"aclruleuser\" LIMIT $1 OFFSET $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "rule_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "allow", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "fbacf1945597ce0155c838d144e15a3bd31e4a90c464ee5fc76e01096672ddb9" +} diff --git a/.sqlx/query-fbd58e67752002e146073b120ae0e0cfb6870a18b2b0ee2d7ab30b0145f736e3.json b/.sqlx/query-fbd58e67752002e146073b120ae0e0cfb6870a18b2b0ee2d7ab30b0145f736e3.json new file mode 100644 index 0000000000..effe9e0a26 --- /dev/null +++ b/.sqlx/query-fbd58e67752002e146073b120ae0e0cfb6870a18b2b0ee2d7ab30b0145f736e3.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"oauth2client\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "fbd58e67752002e146073b120ae0e0cfb6870a18b2b0ee2d7ab30b0145f736e3" +} diff --git a/.sqlx/query-fe19743ca54f23d8478c2b5c2e24a0b029fe678f42f85048d76a635eec86b4fd.json b/.sqlx/query-fe19743ca54f23d8478c2b5c2e24a0b029fe678f42f85048d76a635eec86b4fd.json new file mode 100644 index 0000000000..db0839eadb --- /dev/null +++ b/.sqlx/query-fe19743ca54f23d8478c2b5c2e24a0b029fe678f42f85048d76a635eec86b4fd.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT count(*) FROM \"aclrulenetwork\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "fe19743ca54f23d8478c2b5c2e24a0b029fe678f42f85048d76a635eec86b4fd" +} diff --git a/Cargo.lock b/Cargo.lock index 451884c793..fab47d9761 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,9 +399,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.16.1" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" dependencies = [ "aws-lc-sys", "zeroize", @@ -409,9 +409,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" dependencies = [ "cc", "cmake", @@ -3118,9 +3118,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" @@ -7802,18 +7802,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.42" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.42" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" dependencies = [ "proc-macro2", "quote", diff --git a/crates/defguard_common/src/db/models/authentication_key.rs b/crates/defguard_common/src/db/models/authentication_key.rs index 6def68ff93..0527ec821e 100644 --- a/crates/defguard_common/src/db/models/authentication_key.rs +++ b/crates/defguard_common/src/db/models/authentication_key.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::fmt; use model_derive::Model; use serde::{Deserialize, Serialize}; @@ -14,12 +14,12 @@ pub enum AuthenticationKeyType { Gpg, } -impl Display for AuthenticationKeyType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AuthenticationKeyType::Ssh => write!(f, "SSH"), - AuthenticationKeyType::Gpg => write!(f, "GPG"), - } +impl fmt::Display for AuthenticationKeyType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + AuthenticationKeyType::Ssh => "SSH", + AuthenticationKeyType::Gpg => "GPG", + }) } } diff --git a/crates/defguard_common/src/db/models/device.rs b/crates/defguard_common/src/db/models/device.rs index 9baf0727ac..001a9510d6 100644 --- a/crates/defguard_common/src/db/models/device.rs +++ b/crates/defguard_common/src/db/models/device.rs @@ -61,10 +61,10 @@ pub enum DeviceType { impl fmt::Display for DeviceType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::User => write!(f, "user"), - Self::Network => write!(f, "network"), - } + f.write_str(match self { + Self::User => "user", + Self::Network => "network", + }) } } @@ -94,7 +94,7 @@ pub struct Device { impl fmt::Display for Device { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name) + f.write_str(&self.name) } } @@ -348,12 +348,12 @@ impl WireguardNetworkDevice { network: &WireguardNetwork, active_session: Option<&VpnClientSession>, ) -> DeviceNetworkInfo { - let (preshared_key, is_authorized) = if !network.mfa_enabled() { - (None, true) - } else { + let (preshared_key, is_authorized) = if network.mfa_enabled() { let preshared_key = active_session.and_then(|session| session.preshared_key.clone()); let is_authorized = preshared_key.is_some(); (preshared_key, is_authorized) + } else { + (None, true) }; DeviceNetworkInfo { @@ -1036,12 +1036,53 @@ impl Device { where E: PgExecutor<'e>, { - query_as!(Self, - "SELECT id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", \ - configured \ + query_as!( + Self, + "SELECT id, name, wireguard_pubkey, user_id, created, description, \ + device_type \"device_type: DeviceType\", configured \ FROM device WHERE device_type = $1 ORDER BY name", device_type as DeviceType - ).fetch_all(executor).await + ) + .fetch_all(executor) + .await + } + + pub async fn find_by_type_paginated<'e, E>( + executor: E, + device_type: DeviceType, + limit: i64, + offset: i64, + ) -> sqlx::Result> + where + E: PgExecutor<'e>, + { + query_as!( + Self, + "SELECT id, name, wireguard_pubkey, user_id, created, description, \ + device_type \"device_type: DeviceType\", configured \ + FROM device WHERE device_type = $1 ORDER BY name \ + LIMIT $2 OFFSET $3", + device_type as DeviceType, + limit, + offset + ) + .fetch_all(executor) + .await + } + + pub async fn count_by_type<'e, E>(executor: E, device_type: DeviceType) -> sqlx::Result + where + E: PgExecutor<'e>, + { + let count = query_scalar!( + "SELECT count(*) FROM device WHERE device_type = $1", + device_type as DeviceType + ) + .fetch_one(executor) + .await? + .unwrap_or_default(); + + Ok(count) } pub async fn find_by_type_and_network<'e, E>( @@ -1052,15 +1093,19 @@ impl Device { where E: PgExecutor<'e>, { - query_as!(Self, - "SELECT id, name, wireguard_pubkey, user_id, created, description, device_type \"device_type: DeviceType\", \ - configured \ + query_as!( + Self, + "SELECT id, name, wireguard_pubkey, user_id, created, description, \ + device_type \"device_type: DeviceType\", configured \ FROM device WHERE device_type = $1 \ - AND id IN (SELECT device_id FROM wireguard_network_device WHERE wireguard_network_id = $2) \ + AND id IN \ + (SELECT device_id FROM wireguard_network_device WHERE wireguard_network_id = $2) \ ORDER BY name", device_type as DeviceType, network_id - ).fetch_all(executor).await + ) + .fetch_all(executor) + .await } pub async fn get_owner<'e, E>(&self, executor: E) -> sqlx::Result> @@ -1069,13 +1114,15 @@ impl Device { { query_as!( User, - "SELECT id, username, password_hash, last_name, first_name, email, \ - phone, mfa_enabled, totp_enabled, email_mfa_enabled, \ - totp_secret, email_mfa_secret, mfa_method \"mfa_method: _\", recovery_codes, is_active, openid_sub, \ + "SELECT id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, \ + totp_enabled, email_mfa_enabled, totp_secret, 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 id = $1", self.user_id - ).fetch_one(executor).await + ) + .fetch_one(executor) + .await } pub async fn last_connected_at<'e, E: PgExecutor<'e>>( diff --git a/crates/defguard_common/src/db/models/device_login.rs b/crates/defguard_common/src/db/models/device_login.rs index 073e083f3e..d339682853 100644 --- a/crates/defguard_common/src/db/models/device_login.rs +++ b/crates/defguard_common/src/db/models/device_login.rs @@ -24,7 +24,7 @@ pub struct DeviceLoginEvent { impl fmt::Display for DeviceLoginEvent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.family) + f.write_str(&self.family) } } diff --git a/crates/defguard_common/src/db/models/group.rs b/crates/defguard_common/src/db/models/group.rs index 762efbe70f..a341b40251 100644 --- a/crates/defguard_common/src/db/models/group.rs +++ b/crates/defguard_common/src/db/models/group.rs @@ -14,7 +14,7 @@ pub enum Permission { impl fmt::Display for Permission { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::IsAdmin => write!(f, "is_admin"), + Self::IsAdmin => f.write_str("is_admin"), } } } diff --git a/crates/defguard_common/src/db/models/proxy.rs b/crates/defguard_common/src/db/models/proxy.rs index 91efead60f..a1d3b3afcf 100644 --- a/crates/defguard_common/src/db/models/proxy.rs +++ b/crates/defguard_common/src/db/models/proxy.rs @@ -29,7 +29,7 @@ pub struct Proxy { impl fmt::Display for Proxy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name) + f.write_str(&self.name) } } diff --git a/crates/defguard_common/src/db/models/user.rs b/crates/defguard_common/src/db/models/user.rs index 1152450606..6509b9e5f7 100644 --- a/crates/defguard_common/src/db/models/user.rs +++ b/crates/defguard_common/src/db/models/user.rs @@ -63,16 +63,12 @@ pub enum MFAMethod { // Web MFA methods impl fmt::Display for MFAMethod { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - MFAMethod::None => "None", - MFAMethod::OneTimePassword => "TOTP", - MFAMethod::Webauthn => "WebAuthn", - MFAMethod::Email => "Email", - } - ) + f.write_str(match self { + MFAMethod::None => "None", + MFAMethod::OneTimePassword => "TOTP", + MFAMethod::Webauthn => "WebAuthn", + MFAMethod::Email => "Email", + }) } } @@ -244,7 +240,7 @@ impl User { impl fmt::Display for User { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.username) + f.write_str(&self.username) } } diff --git a/crates/defguard_common/src/db/models/wireguard.rs b/crates/defguard_common/src/db/models/wireguard.rs index fdcf0cabbd..e11efe252e 100644 --- a/crates/defguard_common/src/db/models/wireguard.rs +++ b/crates/defguard_common/src/db/models/wireguard.rs @@ -1,6 +1,6 @@ use std::{ collections::{HashMap, HashSet}, - fmt::{self, Display}, + fmt, iter::zip, net::{IpAddr, Ipv4Addr}, }; @@ -78,13 +78,13 @@ pub enum LocationMfaMode { External, } -impl Display for LocationMfaMode { +impl fmt::Display for LocationMfaMode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - LocationMfaMode::Disabled => write!(f, "MFA disabled"), - LocationMfaMode::Internal => write!(f, "Internal MFA"), - LocationMfaMode::External => write!(f, "External MFA"), - } + f.write_str(match self { + LocationMfaMode::Disabled => "MFA disabled", + LocationMfaMode::Internal => "Internal MFA", + LocationMfaMode::External => "External MFA", + }) } } @@ -137,7 +137,7 @@ pub struct WireguardKey { impl fmt::Display for WireguardNetwork { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name) + f.write_str(&self.name) } } @@ -210,18 +210,6 @@ pub enum NetworkAddressError { } impl WireguardNetwork { - pub async fn count<'e, E>(executor: E) -> sqlx::Result - where - E: PgExecutor<'e>, - { - let count = query_scalar!("SELECT COUNT(*) FROM wireguard_network") - .fetch_one(executor) - .await? - .unwrap_or_default(); - - Ok(count as usize) - } - #[allow(clippy::too_many_arguments)] #[must_use] pub fn new( @@ -722,8 +710,8 @@ impl WireguardNetwork { conn: &PgPool, from: &NaiveDateTime, aggregation: &DateTimeAggregation, - page: u32, - page_size: u32, + limit: u32, + offset: u32, ) -> sqlx::Result<(Vec, u32)> { // helper struct used to fetch connected users from the DB struct ConnectedUserRow { @@ -735,8 +723,6 @@ impl WireguardNetwork { wireguard_ips: Vec, endpoint: String, } - let limit = page_size; - let offset = (page - 1) * page_size; // fetch currently connected users let connected_users = query_as!( diff --git a/crates/defguard_common/src/db/models/wizard.rs b/crates/defguard_common/src/db/models/wizard.rs index 5b4c43df51..b8aa66153e 100644 --- a/crates/defguard_common/src/db/models/wizard.rs +++ b/crates/defguard_common/src/db/models/wizard.rs @@ -22,12 +22,12 @@ pub enum ActiveWizard { impl fmt::Display for ActiveWizard { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::None => write!(f, "none"), - Self::Initial => write!(f, "initial setup"), - Self::AutoAdoption => write!(f, "auto-adoption"), - Self::Migration => write!(f, "migration"), - } + f.write_str(match self { + Self::None => "none", + Self::Initial => "initial setup", + Self::AutoAdoption => "auto-adoption", + Self::Migration => "migration", + }) } } diff --git a/crates/defguard_common/src/hex.rs b/crates/defguard_common/src/hex.rs index a72bfaa416..167fb1374b 100644 --- a/crates/defguard_common/src/hex.rs +++ b/crates/defguard_common/src/hex.rs @@ -1,7 +1,4 @@ -use std::{ - error::Error, - fmt::{Display, Formatter, Result as FmtResult}, -}; +use std::{error::Error, fmt}; #[derive(Debug, PartialEq)] pub enum HexError { @@ -11,8 +8,8 @@ pub enum HexError { impl Error for HexError {} -impl Display for HexError { - fn fmt(&self, f: &mut Formatter) -> FmtResult { +impl fmt::Display for HexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::InvalidCharacter(char) => { write!(f, "Invalid character {char}") diff --git a/crates/defguard_core/src/enterprise/db/models/acl.rs b/crates/defguard_core/src/enterprise/db/models/acl.rs index 21279899d1..f7b6231230 100644 --- a/crates/defguard_core/src/enterprise/db/models/acl.rs +++ b/crates/defguard_core/src/enterprise/db/models/acl.rs @@ -85,7 +85,7 @@ impl fmt::Display for PortRange { (start, end) if end == start => start.to_string(), (start, end) => format!("{start}-{end}"), }; - write!(f, "{s}") + f.write_str(&s) } } diff --git a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs index e0687d1634..364d0acf71 100644 --- a/crates/defguard_core/src/enterprise/db/models/openid_provider.rs +++ b/crates/defguard_core/src/enterprise/db/models/openid_provider.rs @@ -20,15 +20,11 @@ pub enum DirectorySyncUserBehavior { impl fmt::Display for DirectorySyncUserBehavior { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - DirectorySyncUserBehavior::Keep => "keep", - DirectorySyncUserBehavior::Disable => "disable", - DirectorySyncUserBehavior::Delete => "delete", - } - ) + f.write_str(match self { + DirectorySyncUserBehavior::Keep => "keep", + DirectorySyncUserBehavior::Disable => "disable", + DirectorySyncUserBehavior::Delete => "delete", + }) } } @@ -61,15 +57,11 @@ pub enum DirectorySyncTarget { impl fmt::Display for DirectorySyncTarget { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - DirectorySyncTarget::All => "all", - DirectorySyncTarget::Users => "users", - DirectorySyncTarget::Groups => "groups", - } - ) + f.write_str(match self { + DirectorySyncTarget::All => "all", + DirectorySyncTarget::Users => "users", + DirectorySyncTarget::Groups => "groups", + }) } } diff --git a/crates/defguard_core/src/enterprise/handlers/acl.rs b/crates/defguard_core/src/enterprise/handlers/acl.rs index 3230a72b07..2ffd111025 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl.rs @@ -3,7 +3,7 @@ pub mod destination; use axum::{ Json, - extract::{Path, State}, + extract::{Path, Query, State}, http::StatusCode, }; use chrono::NaiveDateTime; @@ -18,7 +18,10 @@ use crate::{ auth::{AdminRole, SessionInfo}, enterprise::db::models::acl::{AclRule, AclRuleInfo, Protocol, RuleState}, error::WebError, - handlers::{ApiResponse, ApiResult}, + handlers::{ + ApiResponse, ApiResult, + pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationParams}, + }, }; /// API representation of [`AclRule`] used in API responses. @@ -223,11 +226,20 @@ pub(crate) async fn list_acl_rules( _admin: AdminRole, State(appstate): State, session: SessionInfo, -) -> ApiResult { + pagination: Query, +) -> PaginatedApiResult { + let pagination = pagination.0; + debug!("User {} listing ACL rules", session.user.username); + let mut conn = appstate.pool.acquire().await?; - let rules = AclRule::all(&mut *conn).await?; - let mut api_rules = Vec::::with_capacity(rules.len()); + let rules = AclRule::all_paginated( + &mut *conn, + i64::from(pagination.per_page()), + i64::from(pagination.offset()), + ) + .await?; + let mut api_rules = Vec::with_capacity(rules.len()); for rule in &rules { // TODO: may require optimisation wrt. sql queries let info = rule.to_info(&mut conn).await.map_err(|err| { @@ -236,8 +248,15 @@ pub(crate) async fn list_acl_rules( })?; api_rules.push(info.into()); } + info!("User {} listed ACL rules", session.user.username); - Ok(ApiResponse::json(api_rules, StatusCode::OK)) + + let count = AclRule::count(&mut *conn).await?; + Ok(PaginatedApiResponse::new( + api_rules, + pagination, + count as u32, + )) } /// Count ACL rules by state. diff --git a/crates/defguard_core/src/enterprise/handlers/acl/alias.rs b/crates/defguard_core/src/enterprise/handlers/acl/alias.rs index ea26f409f8..bf72e58130 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl/alias.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl/alias.rs @@ -202,7 +202,7 @@ impl From for ApiAclAlias { path = "/api/v1/acl/alias", tag = "ACL", responses( - (status = OK, description = "ACL alias", body = Vec), + (status = OK, description = "ACL alias", body = [ApiAclAlias]), ), )] pub(crate) async fn list_acl_aliases( diff --git a/crates/defguard_core/src/enterprise/handlers/acl/destination.rs b/crates/defguard_core/src/enterprise/handlers/acl/destination.rs index 5e97646940..36fd425ab3 100644 --- a/crates/defguard_core/src/enterprise/handlers/acl/destination.rs +++ b/crates/defguard_core/src/enterprise/handlers/acl/destination.rs @@ -219,7 +219,7 @@ impl From for ApiAclDestination { path = "/api/v1/acl/destination", tag = "ACL", responses( - (status = OK, description = "ACL destination", body = Vec), + (status = OK, description = "ACL destination", body = [ApiAclDestination]), ) )] pub(crate) async fn list_acl_destinations( diff --git a/crates/defguard_core/src/enterprise/snat/handlers.rs b/crates/defguard_core/src/enterprise/snat/handlers.rs index 9c510a9771..a591ac6410 100644 --- a/crates/defguard_core/src/enterprise/snat/handlers.rs +++ b/crates/defguard_core/src/enterprise/snat/handlers.rs @@ -39,7 +39,7 @@ use crate::{ ("location_id" = Id, Path, description = "WireGuard location ID") ), responses( - (status = 200, description = "List of SNAT bindings", body = Vec), + (status = 200, description = "List of SNAT bindings", body = [UserSnatBinding]), (status = 401, description = "Unauthorized"), (status = 403, description = "Forbidden - Admin role required"), (status = 404, description = "Not found - location does not exist"), diff --git a/crates/defguard_core/src/grpc/proxy/client_mfa.rs b/crates/defguard_core/src/grpc/proxy/client_mfa.rs index e379eed6c1..94c046f901 100644 --- a/crates/defguard_core/src/grpc/proxy/client_mfa.rs +++ b/crates/defguard_core/src/grpc/proxy/client_mfa.rs @@ -481,7 +481,7 @@ impl ClientMfaServer { // Prepare event context let (ip, _user_agent) = parse_client_ip_agent(&info).map_err(Status::internal)?; let context = - BidiRequestContext::new(user.id, user.username.clone(), ip, format!("{}", device)); + BidiRequestContext::new(user.id, user.username.clone(), ip, format!("{device}")); // validate code match method { @@ -838,7 +838,7 @@ impl ClientMfaServer { user_id: user.id, username: user.username.clone(), ip: None, - device_name: format!("{}", device), + device_name: format!("{device}"), }; self.emit_event(BidiStreamEvent { context, diff --git a/crates/defguard_core/src/handlers/activity_log.rs b/crates/defguard_core/src/handlers/activity_log.rs index 6ff1d04127..4814efe339 100644 --- a/crates/defguard_core/src/handlers/activity_log.rs +++ b/crates/defguard_core/src/handlers/activity_log.rs @@ -1,4 +1,4 @@ -use std::fmt::{self, Display, Formatter}; +use std::fmt; use axum::extract::State; use axum_extra::extract::Query; @@ -7,10 +7,7 @@ use defguard_common::db::Id; use ipnetwork::IpNetwork; use sqlx::{FromRow, Postgres, QueryBuilder, Type}; -use super::{ - DEFAULT_API_PAGE_SIZE, - pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationMeta, PaginationParams}, -}; +use super::pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationParams}; use crate::{appstate::AppState, auth::SessionInfo, db::models::activity_log::ActivityLogModule}; #[derive(Debug, Deserialize, Default)] @@ -66,17 +63,17 @@ pub enum SortKey { Device, } -impl Display for SortKey { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::Timestamp => write!(f, "timestamp"), - Self::Username => write!(f, "username"), - Self::Location => write!(f, "location"), - Self::Ip => write!(f, "ip"), - Self::Event => write!(f, "event"), - Self::Module => write!(f, "module"), - Self::Device => write!(f, "device"), - } +impl fmt::Display for SortKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::Timestamp => "timestamp", + Self::Username => "username", + Self::Location => "location", + Self::Ip => "ip", + Self::Event => "event", + Self::Module => "module", + Self::Device => "device", + }) } } @@ -88,12 +85,12 @@ pub enum SortOrder { Desc, } -impl Display for SortOrder { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::Asc => write!(f, "ASC"), - Self::Desc => write!(f, "DESC"), - } +impl fmt::Display for SortOrder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::Asc => "ASC", + Self::Desc => "DESC", + }) } } @@ -133,11 +130,13 @@ pub async fn get_activity_log_events( filters: Query, sorting: Query, ) -> PaginatedApiResult { - debug!("Fetching activity log with filters {filters:?} and pagination {pagination:?}"); + let pagination = pagination.0; + debug!("Fetching activity log with filters {filters:?} and pagination {pagination}"); // start with base SELECT query // dummy WHERE filter is use to enable composable filtering let mut query_builder: QueryBuilder = QueryBuilder::new( - "SELECT id, timestamp, user_id, username, location, ip, event, module, device, description FROM activity_log_event WHERE 1=1 ", + "SELECT id, timestamp, user_id, username, location, ip, event, module, device, description \ + FROM activity_log_event WHERE 1=1 ", ); // filter events for non-admin users to show only their own events @@ -155,10 +154,12 @@ pub async fn get_activity_log_events( apply_sorting(&mut query_builder, &sorting); // add limit and offset to fetch a specific page - let limit = DEFAULT_API_PAGE_SIZE; - query_builder.push(" LIMIT ").push_bind(i64::from(limit)); - let offset = (pagination.page - 1) * DEFAULT_API_PAGE_SIZE; - query_builder.push(" OFFSET ").push_bind(i64::from(offset)); + query_builder + .push(" LIMIT ") + .push_bind(i64::from(pagination.per_page())); + query_builder + .push(" OFFSET ") + .push_bind(i64::from(pagination.offset())); // fetch filtered events let events = query_builder @@ -176,13 +177,11 @@ pub async fn get_activity_log_events( .fetch_one(&appstate.pool) .await?; - let pagination = - PaginationMeta::new(pagination.page, total_items as u32, DEFAULT_API_PAGE_SIZE); - - Ok(PaginatedApiResponse { - data: events, + Ok(PaginatedApiResponse::new( + events, pagination, - }) + total_items as u32, + )) } /// Adds optional filtering statements to SQL query based on request query params diff --git a/crates/defguard_core/src/handlers/group.rs b/crates/defguard_core/src/handlers/group.rs index e0027a7ac1..8ee7d95b37 100644 --- a/crates/defguard_core/src/handlers/group.rs +++ b/crates/defguard_core/src/handlers/group.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; use axum::{ - extract::{Json, Path, State}, + extract::{Json, Path, Query, State}, http::StatusCode, }; use defguard_common::db::{ @@ -25,28 +25,17 @@ use crate::{ }, error::WebError, events::{ApiEvent, ApiEventType, ApiRequestContext}, + handlers::pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationParams}, hashset, location_management::sync_all_networks, }; -#[derive(Serialize, ToSchema)] -pub(crate) struct Groups { - groups: Vec, -} - -impl Groups { - #[must_use] - pub fn new(groups: Vec) -> Self { - Self { groups } - } -} - -#[derive(Deserialize, Debug, Clone, ToSchema)] +#[derive(Deserialize, ToSchema)] pub(crate) struct BulkAssignToGroupsRequest { // groups by name groups: Vec, // users by id - users: Vec, + users: Vec, } /// Bulk assign users to groups @@ -77,13 +66,13 @@ pub(crate) async fn bulk_assign_to_groups( Json(data): Json, ) -> Result { debug!("Assigning groups to users."); - let mut users: Vec> = query_as!( + let mut users = query_as!( User, - "SELECT id, username, password_hash, last_name, first_name, email, \ - phone, mfa_enabled, totp_enabled, email_mfa_enabled, \ - totp_secret, 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 id = ANY($1)", + "SELECT id, username, password_hash, last_name, first_name, email, phone, mfa_enabled, \ + totp_enabled, email_mfa_enabled, totp_secret, 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 id = ANY($1)", &data.users ) .fetch_all(&appstate.pool) @@ -197,9 +186,7 @@ pub(crate) async fn list_groups_info( Ok(ApiResponse::json(q_result, StatusCode::OK)) } -/// Retrieve all groups. -/// -/// Retrieves group details by `name`. +/// Retrieve all group names. /// /// # Returns /// - `Groups` object @@ -209,7 +196,7 @@ pub(crate) async fn list_groups_info( get, path = "/api/v1/group", responses( - (status = 200, description = "Retrieve all groups.", body = Groups, example = json!({"groups": ["admin"]})), + (status = 200, description = "Retrieve all groups.", body = [String], example = json!({"groups": ["admin"]})), (status = 401, description = "Unauthorized to retrieve all groups.", body = ApiResponse, example = json!({"msg": "Session is required"})), (status = 500, description = "Cannot retrieve all groups.", body = ApiResponse, example = json!({"msg": "Internal server error"})) ), @@ -222,15 +209,26 @@ pub(crate) async fn list_groups( _admin: AdminRole, session: SessionInfo, State(appstate): State, -) -> ApiResult { - debug!("User {} lists groups", &session.user.username); - let groups = Group::all(&appstate.pool) - .await? - .into_iter() - .map(|group| group.name) - .collect(); + pagination: Query, +) -> PaginatedApiResult { + let pagination = pagination.0; + + debug!("User {} is listing groups", &session.user.username); + + let groups = Group::all_paginated( + &appstate.pool, + i64::from(pagination.per_page()), + i64::from(pagination.offset()), + ) + .await? + .into_iter() + .map(|group| group.name) + .collect(); + info!("User {} listed groups", &session.user.username); - Ok(ApiResponse::json(Groups::new(groups), StatusCode::OK)) + + let count = Group::count(&appstate.pool).await?; + Ok(PaginatedApiResponse::new(groups, pagination, count as u32)) } /// Retrieve group with name diff --git a/crates/defguard_core/src/handlers/location_stats.rs b/crates/defguard_core/src/handlers/location_stats.rs index af0929ea97..0f5ee959ae 100644 --- a/crates/defguard_core/src/handlers/location_stats.rs +++ b/crates/defguard_core/src/handlers/location_stats.rs @@ -19,8 +19,8 @@ use crate::{ auth::AdminRole, error::WebError, handlers::{ - ApiResponse, ApiResult, DEFAULT_API_PAGE_SIZE, - pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationMeta, PaginationParams}, + ApiResponse, ApiResult, + pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationParams}, }, }; @@ -98,7 +98,8 @@ pub(crate) async fn location_stats( /// Returns paginated list of connected users for a given location /// /// # Returns -/// Returns a paginated list of `LocationConnectedUser` objects for requested location and time period +/// Returns a paginated list of `LocationConnectedUser` objects for requested location and time +/// period. pub(crate) async fn location_connected_users( _role: AdminRole, State(appstate): State, @@ -106,8 +107,10 @@ pub(crate) async fn location_connected_users( Query(query_from): Query, pagination: Query, ) -> PaginatedApiResult { + let pagination = pagination.0; debug!( - "Displaying connected users for location {location_id} with time window {query_from:?} and pagination {pagination:?}" + "Displaying connected users for location {location_id} with time window {query_from:?} and \ + pagination {pagination}" ); let Some(location) = WireguardNetwork::find_by_id(&appstate.pool, location_id).await? else { @@ -123,23 +126,23 @@ pub(crate) async fn location_connected_users( &appstate.pool, &from, &aggregation, - pagination.page, - DEFAULT_API_PAGE_SIZE, + pagination.per_page(), + pagination.offset(), ) .await?; - let pagination = PaginationMeta::new(pagination.page, total_items, DEFAULT_API_PAGE_SIZE); - - Ok(PaginatedApiResponse { - data: connected_users, + Ok(PaginatedApiResponse::new( + connected_users, pagination, - }) + total_items, + )) } /// Returns paginated list of connected network devices for a given location /// /// # Returns -/// Returns a paginated list of `LocationConnectedNetworkDevice` objects for requested location and time period +/// Returns a paginated list of `LocationConnectedNetworkDevice` objects for requested location and +/// time period. pub(crate) async fn location_connected_network_devices( _role: AdminRole, State(appstate): State, @@ -147,8 +150,10 @@ pub(crate) async fn location_connected_network_devices( Query(query_from): Query, pagination: Query, ) -> PaginatedApiResult { + let pagination = pagination.0; debug!( - "Displaying connected network devices for location {location_id} with time window {query_from:?} and pagination {pagination:?}" + "Displaying connected network devices for location {location_id} with time window \ + {query_from:?} and pagination {pagination}" ); let Some(location) = WireguardNetwork::find_by_id(&appstate.pool, location_id).await? else { @@ -164,29 +169,29 @@ pub(crate) async fn location_connected_network_devices( &appstate.pool, &from, &aggregation, - pagination.page, - DEFAULT_API_PAGE_SIZE, + pagination.page(), + pagination.per_page(), ) .await?; - let pagination = PaginationMeta::new(pagination.page, total_items, DEFAULT_API_PAGE_SIZE); - - Ok(PaginatedApiResponse { - data: connected_network_devices, + Ok(PaginatedApiResponse::new( + connected_network_devices, pagination, - }) + total_items, + )) } #[derive(Deserialize)] pub(crate) struct ConnectedUserDevicesPath { - location_id: i64, - user_id: i64, + location_id: Id, + user_id: Id, } /// Returns list of connected devices for a specific user at a given location /// /// # Returns -/// Returns a list of `LocationConnectedUserDevice` objects for requested user, location and time period +/// Returns a list of `LocationConnectedUserDevice` objects for requested user, location and time +/// period. pub(crate) async fn location_connected_user_devices( _role: AdminRole, State(appstate): State, diff --git a/crates/defguard_core/src/handlers/mod.rs b/crates/defguard_core/src/handlers/mod.rs index f8a109c2ad..b8ba555153 100644 --- a/crates/defguard_core/src/handlers/mod.rs +++ b/crates/defguard_core/src/handlers/mod.rs @@ -67,7 +67,6 @@ pub enum WebErrorCode { pub static SESSION_COOKIE_NAME: &str = "defguard_session"; pub(crate) static SIGN_IN_COOKIE_NAME: &str = "defguard_sign_in"; pub(crate) const SIGN_IN_COOKIE_MAX_AGE: time::Duration = time::Duration::minutes(10); -pub(crate) const DEFAULT_API_PAGE_SIZE: u32 = 50; pub(crate) fn cookie_domain() -> Option { server_config().cookie_domain.clone().or_else(|| { diff --git a/crates/defguard_core/src/handlers/network_devices.rs b/crates/defguard_core/src/handlers/network_devices.rs index 0e5fc23dca..a2ed457a1a 100644 --- a/crates/defguard_core/src/handlers/network_devices.rs +++ b/crates/defguard_core/src/handlers/network_devices.rs @@ -4,7 +4,7 @@ use std::{ }; use axum::{ - extract::{Json, Path, State}, + extract::{Json, Path, Query, State}, http::StatusCode, }; use chrono::NaiveDateTime; @@ -32,6 +32,7 @@ use crate::{ enterprise::{firewall::try_get_location_firewall_config, limits::update_counts}, events::{ApiEvent, ApiEventType, ApiRequestContext}, grpc::GatewayEvent, + handlers::pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationParams}, }; #[derive(Serialize)] @@ -41,7 +42,7 @@ struct NetworkDeviceLocation { } #[derive(Serialize)] -struct NetworkDeviceInfo { +pub(crate) struct NetworkDeviceInfo { id: Id, name: String, assigned_ips: Vec, @@ -74,7 +75,7 @@ impl NetworkDeviceInfo { device.name, network.name )))?; let added_by = device.get_owner(&mut *transaction).await?; - let split_ips: Vec = wireguard_device + let split_ips = wireguard_device .wireguard_ips .iter() .copied() @@ -108,7 +109,7 @@ impl NetworkDeviceInfo { } } -pub async fn download_network_device_config( +pub(crate) async fn download_network_device_config( _admin_role: AdminRole, State(appstate): State, Path(device_id): Path, @@ -140,7 +141,7 @@ pub async fn download_network_device_config( Ok(Device::create_config(&network, &network_device)) } -pub async fn get_network_device( +pub(crate) async fn get_network_device( _admin_role: AdminRole, session: SessionInfo, Path(device_id): Path, @@ -169,14 +170,25 @@ pub async fn get_network_device( ))) } +/// GET /api/v1/device/network pub(crate) async fn list_network_devices( _admin_role: AdminRole, State(appstate): State, -) -> ApiResult { - debug!("Listing all network devices"); - let mut devices_response: Vec = Vec::new(); + pagination: Query, +) -> PaginatedApiResult { + let pagination = pagination.0; + + debug!("Listing network devices"); + + let mut devices_response = Vec::new(); let mut transaction = appstate.pool.begin().await?; - let devices = Device::find_by_type(&mut *transaction, DeviceType::Network).await?; + let devices = Device::find_by_type_paginated( + &mut *transaction, + DeviceType::Network, + i64::from(pagination.per_page()), + i64::from(pagination.offset()), + ) + .await?; for device in devices { match NetworkDeviceInfo::from_device(device, &mut transaction).await { Ok(device_info) => { @@ -190,10 +202,16 @@ pub(crate) async fn list_network_devices( } } } + let count = Device::count_by_type(&mut *transaction, DeviceType::Network).await?; transaction.commit().await?; info!("Listed {} network devices", devices_response.len()); - Ok(ApiResponse::json(devices_response, StatusCode::OK)) + + Ok(PaginatedApiResponse::new( + devices_response, + pagination, + count as u32, + )) } #[derive(Serialize, Deserialize, Debug)] @@ -243,8 +261,8 @@ pub(crate) async fn check_ip_availability( .await? .ok_or_else(|| { error!( - "Failed to check IP availability for location with ID {network_id}, location not found", - + "Failed to check IP availability for location with ID {network_id}, location not \ + found", ); WebError::BadRequest("Failed to check IP availability, location not found".into()) })?; @@ -255,7 +273,7 @@ pub(crate) async fn check_ip_availability( match IpAddr::from_str(ip) { Ok(ip) => { debug!( - "Checking if IP address {ip} can be assigned to a device in location {location}", + "Checking if IP address {ip} can be assigned to a device in location {location}" ); let result = match location .can_assign_ips(&mut transaction, &[ip], check.device_id) @@ -264,13 +282,15 @@ pub(crate) async fn check_ip_availability( Ok(()) => IpAvailabilityCheckResult::new(true, true), Err(NetworkAddressError::NoContainingNetwork(name, ip, networks)) => { warn!( - "Provided device IP address {ip} is not in the network {name} range: {networks:?}" + "Provided device IP address {ip} is not in the network {name} range: \ + {networks:?}" ); IpAvailabilityCheckResult::new(false, false) } Err(NetworkAddressError::ReservedForGateway(name, ip)) => { warn!( - "Provided device IP address {ip} may overlap with the gateway's IP address on network {name}", + "Provided device IP address {ip} may overlap with the gateway's IP \ + address on network {name}", ); IpAvailabilityCheckResult::new(false, true) } @@ -296,7 +316,8 @@ pub(crate) async fn check_ip_availability( } Err(_err) => { warn!( - "Failed to check IP availability for location {location}, invalid IP address {ip}", + "Failed to check IP availability for location {location}, invalid IP address \ + {ip}", ); validation_results.push(IpAvailabilityCheckResult { available: false, @@ -446,7 +467,8 @@ pub(crate) async fn start_network_device_setup( .await?; info!( - "User {} added a new unconfigured network device {device_name} with IPs {ips:?} to network {}", + "User {} added a new unconfigured network device {device_name} with IPs {ips:?} to network \ + {}", user.username, network.name ); @@ -468,7 +490,8 @@ pub(crate) async fn start_network_device_setup( .await?; debug!( - "Generated a new device CLI configuration token for a network device {device_name} with ID {}: {configuration_token}", + "Generated a new device CLI configuration token for a network device {device_name} with ID \ + {}: {configuration_token}", result.device.id ); @@ -477,7 +500,10 @@ pub(crate) async fn start_network_device_setup( transaction.commit().await?; Ok(ApiResponse::new( - json!({"enrollment_token": configuration_token, "enrollment_url": settings.proxy_public_url()?.to_string()}), + json!({ + "enrollment_token": configuration_token, + "enrollment_url": settings.proxy_public_url()?.to_string() + }), StatusCode::CREATED, )) } diff --git a/crates/defguard_core/src/handlers/openid_clients.rs b/crates/defguard_core/src/handlers/openid_clients.rs index ed9a74194f..25553643ee 100644 --- a/crates/defguard_core/src/handlers/openid_clients.rs +++ b/crates/defguard_core/src/handlers/openid_clients.rs @@ -1,10 +1,10 @@ use axum::{ - extract::{Json, Path, State}, + extract::{Json, Path, Query, State}, http::StatusCode, }; use defguard_common::{ db::{ - NoId, + Id, NoId, models::oauth2client::{OAuth2Client, OAuth2ClientSafe}, }, random::gen_alphanumeric, @@ -16,6 +16,7 @@ use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, events::{ApiEvent, ApiEventType, ApiRequestContext}, + handlers::pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationParams}, }; #[derive(Deserialize, Serialize)] @@ -42,7 +43,7 @@ impl From for OAuth2Client { } } -pub async fn add_openid_client( +pub(crate) async fn add_openid_client( _admin: AdminRole, session: SessionInfo, context: ApiRequestContext, @@ -78,12 +79,30 @@ pub async fn add_openid_client( Ok(ApiResponse::json(client, StatusCode::CREATED)) } -pub async fn list_openid_clients(_admin: AdminRole, State(appstate): State) -> ApiResult { - let clients = OAuth2Client::all(&appstate.pool).await?; - Ok(ApiResponse::json(clients, StatusCode::OK)) +/// GET: /api/v1/oauth +pub(crate) async fn list_openid_clients( + _admin: AdminRole, + State(appstate): State, + pagination: Query, +) -> PaginatedApiResult> { + let pagination = pagination.0; + + debug!("Listing OAuth clients"); + + let clients = OAuth2Client::all_paginated( + &appstate.pool, + i64::from(pagination.per_page()), + i64::from(pagination.offset()), + ) + .await?; + + debug!("Listed OAuth clients"); + + let count = OAuth2Client::count(&appstate.pool).await?; + Ok(PaginatedApiResponse::new(clients, pagination, count as u32)) } -pub async fn get_openid_client( +pub(crate) async fn get_openid_client( State(appstate): State, Path(client_id): Path, session: SessionInfo, @@ -103,7 +122,7 @@ pub async fn get_openid_client( } } -pub async fn change_openid_client( +pub(crate) async fn change_openid_client( _admin: AdminRole, session: SessionInfo, context: ApiRequestContext, @@ -157,7 +176,7 @@ pub async fn change_openid_client( Ok(ApiResponse::with_status(status)) } -pub async fn change_openid_client_state( +pub(crate) async fn change_openid_client_state( _admin: AdminRole, session: SessionInfo, context: ApiRequestContext, @@ -191,7 +210,7 @@ pub async fn change_openid_client_state( Ok(ApiResponse::with_status(status)) } -pub async fn delete_openid_client( +pub(crate) async fn delete_openid_client( _admin: AdminRole, session: SessionInfo, context: ApiRequestContext, diff --git a/crates/defguard_core/src/handlers/pagination.rs b/crates/defguard_core/src/handlers/pagination.rs index d4d667bf2d..cb4428aece 100644 --- a/crates/defguard_core/src/handlers/pagination.rs +++ b/crates/defguard_core/src/handlers/pagination.rs @@ -1,46 +1,91 @@ +use std::fmt; + use axum::{ body::Body, response::{IntoResponse, Response}, }; use reqwest::StatusCode; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::error::WebError; /// Query params for paginated endpoints -#[derive(Debug, Deserialize, Default)] -pub struct PaginationParams { - #[serde(default = "default_page")] - pub page: u32, +#[derive(Deserialize)] +#[serde(default)] +pub(crate) struct PaginationParams { + page: u32, + per_page: u32, +} + +impl PaginationParams { + /// Page getter. + #[must_use] + pub fn page(&self) -> u32 { + self.page + } + + /// Page size getter. + #[must_use] + pub fn per_page(&self) -> u32 { + self.per_page + } + + /// Calculate offset. + #[must_use] + pub fn offset(&self) -> u32 { + if self.page == 0 { + self.per_page + } else { + (self.page - 1) * self.per_page + } + } +} + +impl Default for PaginationParams { + fn default() -> Self { + Self { + page: 1, + per_page: 50, + } + } } -fn default_page() -> u32 { - 1 +impl fmt::Display for PaginationParams { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "page {}", self.page) + } } /// Metadata about the pagination included in response -#[derive(Debug, Serialize)] -pub struct PaginationMeta { - pub current_page: u32, - pub page_size: u32, - pub total_items: u32, - pub total_pages: u32, - pub next_page: Option, +#[derive(Serialize)] +struct PaginationMeta { + current_page: u32, + page_size: u32, + total_items: u32, + total_pages: u32, + next_page: Option, } impl PaginationMeta { - /// Prepares pagination metadata that's part of the response - pub fn new(current_page: u32, total_items: u32, page_size: u32) -> Self { - let total_pages = (total_items).div_ceil(page_size); - let next_page = if current_page < total_pages { - Some(current_page + 1) + /// Prepares pagination metadata that's part of the response. + #[must_use] + fn from_pagination(pagination: PaginationParams, total_items: u32) -> Self { + let PaginationParams { page, per_page } = pagination; + let total_pages = if per_page <= 1 { + // For 0 and 1, assume per_page is 1. + total_items + } else { + total_items.div_ceil(per_page) + }; + let next_page = if page < total_pages { + Some(page + 1) } else { None }; Self { - current_page, - page_size, + current_page: page, + page_size: per_page, total_items, total_pages, next_page, @@ -48,12 +93,22 @@ impl PaginationMeta { } } -pub type PaginatedApiResult = Result, WebError>; +pub(crate) type PaginatedApiResult = Result, WebError>; -#[derive(Debug, Serialize)] -pub struct PaginatedApiResponse { - pub data: Vec, - pub pagination: PaginationMeta, +#[derive(Serialize)] +pub(crate) struct PaginatedApiResponse { + data: Vec, + pagination: PaginationMeta, +} + +impl PaginatedApiResponse { + #[must_use] + pub(crate) fn new(data: Vec, pagination: PaginationParams, total_items: u32) -> Self { + Self { + data, + pagination: PaginationMeta::from_pagination(pagination, total_items), + } + } } impl IntoResponse for PaginatedApiResponse @@ -62,14 +117,12 @@ where { fn into_response(self) -> Response { // Convert the data to JSON - let json = match serde_json::to_string(&self) { - Ok(json) => json, + match serde_json::to_string(&self) { + Ok(json) => Response::new(Body::from(json)), Err(err) => { error!("Failed to convert paginated response into JSON: {err}"); - return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + StatusCode::INTERNAL_SERVER_ERROR.into_response() } - }; - - Response::new(Body::from(json)) + } } } diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index d1216553b3..850b0db81c 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use axum::{ - extract::{Json, Path, State}, + extract::{Json, Path, Query, State}, http::StatusCode, }; use defguard_common::{ @@ -48,6 +48,7 @@ use crate::{ }, error::WebError, events::{ApiEvent, ApiEventType, ApiRequestContext}, + handlers::pagination::{PaginatedApiResponse, PaginatedApiResult, PaginationParams}, is_valid_phone_number, user_management::{delete_user_and_cleanup_devices, sync_allowed_user_devices}, }; @@ -134,7 +135,7 @@ pub struct UserDetails { } impl UserDetails { - pub async fn from_user(pool: &PgPool, user: &User) -> sqlx::Result { + pub(crate) async fn from_user(pool: &PgPool, user: &User) -> sqlx::Result { let devices = user.user_devices(pool).await?; let security_keys = user.security_keys(pool).await?; let biometric_enabled_devices = BiometricAuth::find_by_user_id(pool, user.id) @@ -195,13 +196,33 @@ impl UserDetails { ("api_token" = []) ) )] -pub async fn list_users(_role: AdminRole, State(appstate): State) -> ApiResult { - let all_users = User::all(&appstate.pool).await?; - let mut users: Vec = Vec::with_capacity(all_users.len()); +pub(crate) async fn list_users( + _role: AdminRole, + State(appstate): State, + pagination: Query, +) -> PaginatedApiResult { + let pagination = pagination.0; + + debug!("Listing users"); + + let all_users = User::all_paginated( + &appstate.pool, + i64::from(pagination.per_page()), + i64::from(pagination.offset()), + ) + .await?; + // Map [`User`] to [`UserInfo`]. + // TODO: too many queries – optimise. + let mut users = Vec::with_capacity(all_users.len()); for user in all_users { users.push(UserInfo::from_user(&appstate.pool, &user).await?); } - Ok(ApiResponse::json(users, StatusCode::OK)) + + let count = User::count(&appstate.pool).await?; + + info!("Listed users"); + + Ok(PaginatedApiResponse::new(users, pagination, count as u32)) } /// Get user @@ -210,7 +231,6 @@ pub async fn list_users(_role: AdminRole, State(appstate): State) -> A /// /// # Returns /// - `UserDetails` object -/// /// - `WebError` if error occurs #[utoipa::path( get, @@ -253,7 +273,7 @@ pub async fn list_users(_role: AdminRole, State(appstate): State) -> A ("api_token" = []) ) )] -pub async fn get_user( +pub(crate) async fn get_user( session: SessionInfo, State(appstate): State, Path(username): Path, @@ -269,7 +289,6 @@ pub async fn get_user( /// /// # Returns /// - `UserInfo` object -/// /// - `WebError` if error occurs #[utoipa::path( post, @@ -306,7 +325,7 @@ pub async fn get_user( ("api_token" = []) ) )] -pub async fn add_user( +pub(crate) async fn add_user( _role: AdminRole, session: SessionInfo, context: ApiRequestContext, @@ -426,7 +445,7 @@ pub async fn add_user( ("api_token" = []) ) )] -pub async fn start_enrollment( +pub(crate) async fn start_enrollment( _role: AdminRole, session: SessionInfo, context: ApiRequestContext, @@ -545,7 +564,7 @@ pub async fn start_enrollment( ("api_token" = []) ) )] -pub async fn start_remote_desktop_configuration( +pub(crate) async fn start_remote_desktop_configuration( _can_manage_devices: CanManageDevices, session: SessionInfo, context: ApiRequestContext, @@ -644,7 +663,7 @@ pub async fn start_remote_desktop_configuration( ("api_token" = []) ) )] -pub async fn username_available( +pub(crate) async fn username_available( _role: AdminRole, State(appstate): State, Json(data): Json, @@ -694,7 +713,7 @@ pub async fn username_available( ("api_token" = []) ) )] -pub async fn modify_user( +pub(crate) async fn modify_user( session: SessionInfo, context: ApiRequestContext, State(appstate): State, @@ -872,7 +891,7 @@ pub async fn modify_user( ("api_token" = []) ) )] -pub async fn delete_user( +pub(crate) async fn delete_user( _role: AdminRole, State(appstate): State, Path(username): Path, @@ -943,7 +962,7 @@ pub async fn delete_user( ("api_token" = []) ) )] -pub async fn change_self_password( +pub(crate) async fn change_self_password( session: SessionInfo, context: ApiRequestContext, State(appstate): State, @@ -1004,7 +1023,7 @@ pub async fn change_self_password( ("api_token" = []) ) )] -pub async fn change_password( +pub(crate) async fn change_password( _role: AdminRole, session: SessionInfo, context: ApiRequestContext, @@ -1079,7 +1098,7 @@ pub async fn change_password( ("api_token" = []) ) )] -pub async fn reset_password( +pub(crate) async fn reset_password( _role: AdminRole, session: SessionInfo, context: ApiRequestContext, @@ -1185,7 +1204,7 @@ pub async fn reset_password( ("api_token" = []) ) )] -pub async fn delete_security_key( +pub(crate) async fn delete_security_key( session: SessionInfo, context: ApiRequestContext, State(appstate): State, @@ -1267,7 +1286,7 @@ pub async fn delete_security_key( ("api_token" = []) ) )] -pub async fn me(session: SessionInfo, State(appstate): State) -> ApiResult { +pub(crate) async fn me(session: SessionInfo, State(appstate): State) -> ApiResult { let user_info = UserInfo::from_user(&appstate.pool, &session.user).await?; Ok(ApiResponse::json(user_info, StatusCode::OK)) } @@ -1297,7 +1316,7 @@ pub async fn me(session: SessionInfo, State(appstate): State) -> ApiRe ("api_token" = []) ) )] -pub async fn delete_authorized_app( +pub(crate) async fn delete_authorized_app( session: SessionInfo, State(appstate): State, Path((username, oauth2client_id)): Path<(String, i64)>, diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index ba3e39451d..119b127221 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -516,7 +516,12 @@ pub async fn list_networks(_role: AdminRole, State(appstate): State) - pub async fn count_networks(_role: AdminRole, State(appstate): State) -> ApiResult { debug!("Counting WireGuard networks"); let count = WireguardNetwork::count(&appstate.pool).await?; - Ok(ApiResponse::json(LocationsCount { count }, StatusCode::OK)) + Ok(ApiResponse::json( + LocationsCount { + count: count as usize, + }, + StatusCode::OK, + )) } /// Details of network diff --git a/crates/defguard_core/src/openapi.rs b/crates/defguard_core/src/openapi.rs index 4340b7caa9..1724406789 100644 --- a/crates/defguard_core/src/openapi.rs +++ b/crates/defguard_core/src/openapi.rs @@ -19,7 +19,7 @@ use super::{ handlers::{ ApiResponse, EditGroupInfo, GroupInfo, PasswordChange, PasswordChangeSelf, SESSION_COOKIE_NAME, StartEnrollmentRequest, Username, auth, - group::{self, BulkAssignToGroupsRequest, Groups}, + group::{self, BulkAssignToGroupsRequest}, license, user::{self, UserDetails}, wireguard as device, wireguard as network, @@ -113,7 +113,7 @@ use super::{ ), components( schemas( - ApiResponse, UserInfo, UserDetails, UserDevice, Groups, Username, + ApiResponse, UserInfo, UserDetails, UserDevice, Username, StartEnrollmentRequest, PasswordChangeSelf, PasswordChange, AddDevice, AddDeviceResult, Device, ModifyDevice, BulkAssignToGroupsRequest, GroupInfo, EditGroupInfo, WebError, license::CheckParams diff --git a/crates/defguard_core/tests/integration/api/acl/rules.rs b/crates/defguard_core/tests/integration/api/acl/rules.rs index 40e7764e33..b2da9407f8 100644 --- a/crates/defguard_core/tests/integration/api/acl/rules.rs +++ b/crates/defguard_core/tests/integration/api/acl/rules.rs @@ -1,4 +1,5 @@ use super::*; +use crate::api::PaginatedApiResponse; fn assert_related_object_rules_rewritten( related_object_name: &str, @@ -61,7 +62,10 @@ async fn test_rule_crud(_: PgPoolOptions, options: PgConnectOptions) { // list let response = client.get("/api/v1/acl/rule").send().await; assert_eq!(response.status(), StatusCode::OK); - let response_rules: Vec = response.json().await; + let response_rules = response + .json::>() + .await + .data; assert_eq!(response_rules.len(), 1); let response_rule = response_rules[0].clone(); assert_eq!(response_rule, expected_response); @@ -89,7 +93,10 @@ async fn test_rule_crud(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(response.status(), StatusCode::NOT_FOUND); let response = client.get("/api/v1/acl/rule").send().await; assert_eq!(response.status(), StatusCode::OK); - let response_rules: Vec = response.json().await; + let response_rules = response + .json::>() + .await + .data; assert_eq!(response_rules.len(), 0); } @@ -229,7 +236,10 @@ async fn test_rule_enterprise(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(response.status(), StatusCode::CREATED); let response = client.get("/api/v1/acl/rule").send().await; assert_eq!(response.status(), StatusCode::OK); - let response_rules: Vec = response.json().await; + let response_rules = response + .json::>() + .await + .data; assert_eq!(response_rules.len(), 1); let response = client.get("/api/v1/acl/rule").send().await; assert_eq!(response.status(), StatusCode::OK); diff --git a/crates/defguard_core/tests/integration/api/mod.rs b/crates/defguard_core/tests/integration/api/mod.rs index a212df4ae5..14e2526a6e 100644 --- a/crates/defguard_core/tests/integration/api/mod.rs +++ b/crates/defguard_core/tests/integration/api/mod.rs @@ -27,3 +27,9 @@ mod wireguard_network_import; mod worker; const TEST_SERVER_URL: &str = "http://localhost:3000/"; + +#[derive(serde::Deserialize)] +struct PaginatedApiResponse { + data: Vec, + // pagination: PaginationMeta, +} diff --git a/crates/defguard_core/tests/integration/api/oauth.rs b/crates/defguard_core/tests/integration/api/oauth.rs index 7705d96b26..e8d5e17382 100644 --- a/crates/defguard_core/tests/integration/api/oauth.rs +++ b/crates/defguard_core/tests/integration/api/oauth.rs @@ -12,6 +12,8 @@ use reqwest::{StatusCode, Url, header::CONTENT_TYPE}; use serde_json::json; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; +use crate::api::PaginatedApiResponse; + use super::common::{make_client_with_db, setup_pool}; #[sqlx::test] @@ -184,7 +186,10 @@ async fn test_openid_app_management_access(_: PgPoolOptions, options: PgConnectO // list apps let response = client.get("/api/v1/oauth").send().await; assert_eq!(response.status(), StatusCode::OK); - let apps: Vec> = response.json().await; + let apps = response + .json::>>() + .await + .data; assert_eq!(apps.len(), 1); let test_app = &apps[0]; assert_eq!(test_app.name, oauth2client.name); @@ -243,7 +248,10 @@ async fn test_openid_app_management_access(_: PgPoolOptions, options: PgConnectO // list apps let response = client.get("/api/v1/oauth").send().await; assert_eq!(response.status(), StatusCode::OK); - let apps: Vec> = response.json().await; + let apps = response + .json::>>() + .await + .data; assert_eq!(apps.len(), 0); // add another app for further testing @@ -261,10 +269,13 @@ async fn test_openid_app_management_access(_: PgPoolOptions, options: PgConnectO assert_eq!(response.status(), StatusCode::CREATED); let response = client.get("/api/v1/oauth").send().await; assert_eq!(response.status(), StatusCode::OK); - let apps: Vec> = response.json().await; + let apps = response + .json::>>() + .await + .data; let test_app = &apps[0]; - // // login as standard user + // login as standard user let auth = Auth::new("hpotter", "pass123"); let response = client.post("/api/v1/auth").json(&auth).send().await; assert_eq!(response.status(), StatusCode::OK); diff --git a/crates/defguard_core/tests/integration/api/openid.rs b/crates/defguard_core/tests/integration/api/openid.rs index 6a3c8f3c9a..0e34212ee8 100644 --- a/crates/defguard_core/tests/integration/api/openid.rs +++ b/crates/defguard_core/tests/integration/api/openid.rs @@ -23,6 +23,8 @@ use rsa::RsaPrivateKey; use serde::Deserialize; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; +use crate::api::PaginatedApiResponse; + use super::{ TEST_SERVER_URL, common::{ @@ -63,7 +65,10 @@ async fn test_openid_client(_: PgPoolOptions, options: PgConnectOptions) { let response = client.get("/api/v1/oauth").send().await; assert_eq!(response.status(), StatusCode::OK); - let openid_clients: Vec> = response.json().await; + let openid_clients = response + .json::>>() + .await + .data; assert_eq!(openid_clients.len(), 1); openid_client.name = "Test changed".into(); @@ -93,8 +98,10 @@ async fn test_openid_client(_: PgPoolOptions, options: PgConnectOptions) { let response = client.get("/api/v1/oauth").send().await; assert_eq!(response.status(), StatusCode::OK); - - let openid_clients: Vec> = response.json().await; + let openid_clients = response + .json::>>() + .await + .data; assert!(openid_clients.is_empty()); } diff --git a/crates/defguard_core/tests/integration/api/openid_login.rs b/crates/defguard_core/tests/integration/api/openid_login.rs index 69453455d9..434d265eed 100644 --- a/crates/defguard_core/tests/integration/api/openid_login.rs +++ b/crates/defguard_core/tests/integration/api/openid_login.rs @@ -17,6 +17,8 @@ use reqwest::{StatusCode, Url}; use serde::Deserialize; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; +use crate::api::PaginatedApiResponse; + use super::common::{exceed_enterprise_limits, make_client, setup_pool}; #[derive(Deserialize)] @@ -134,7 +136,10 @@ async fn test_openid_login(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(response.status(), StatusCode::CREATED); let response = client.get("/api/v1/oauth").send().await; assert_eq!(response.status(), StatusCode::OK); - let openid_clients = response.json::>>().await; + let openid_clients = response + .json::>>() + .await + .data; assert_eq!(openid_clients.len(), 1); let openid_client = openid_clients.first().unwrap(); assert_eq!(openid_client.name, "Defguard"); diff --git a/crates/defguard_gateway_manager/src/handler.rs b/crates/defguard_gateway_manager/src/handler.rs index 9a66343f69..45dd490ada 100644 --- a/crates/defguard_gateway_manager/src/handler.rs +++ b/crates/defguard_gateway_manager/src/handler.rs @@ -183,7 +183,7 @@ impl GatewayHandler { info!("{} disconnected; email notification not sent", self.gateway); return; } - }; + } debug!("Sending Gateway disconnect email notification"); let name = self.gateway.name.clone(); diff --git a/crates/defguard_proto/src/lib.rs b/crates/defguard_proto/src/lib.rs index 84e78505c1..7b4d24624d 100644 --- a/crates/defguard_proto/src/lib.rs +++ b/crates/defguard_proto/src/lib.rs @@ -36,17 +36,13 @@ use tonic::Status; // Client MFA methods impl fmt::Display for MfaMethod { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::Totp => "TOTP", - Self::Email => "Email", - Self::Oidc => "OIDC", - Self::Biometric => "Biometric", - Self::MobileApprove => "MobileApprove", - } - ) + f.write_str(match self { + Self::Totp => "TOTP", + Self::Email => "Email", + Self::Oidc => "OIDC", + Self::Biometric => "Biometric", + Self::MobileApprove => "MobileApprove", + }) } } diff --git a/crates/defguard_version/src/lib.rs b/crates/defguard_version/src/lib.rs index 8d6881440e..615f9e2995 100644 --- a/crates/defguard_version/src/lib.rs +++ b/crates/defguard_version/src/lib.rs @@ -113,11 +113,11 @@ impl FromStr for DefguardComponent { impl fmt::Display for DefguardComponent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Core => write!(f, "core"), - Self::Proxy => write!(f, "proxy"), - Self::Gateway => write!(f, "gateway"), - } + f.write_str(match self { + Self::Core => "core", + Self::Proxy => "proxy", + Self::Gateway => "gateway", + }) } } diff --git a/crates/model_derive/src/lib.rs b/crates/model_derive/src/lib.rs index a40f0fe861..5d1584110c 100644 --- a/crates/model_derive/src/lib.rs +++ b/crates/model_derive/src/lib.rs @@ -1,39 +1,73 @@ use proc_macro::TokenStream; use quote::quote; use syn::{ - Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, GenericArgument, Ident, Path, - PathArguments, Type, TypePath, meta::parser, parse::Parser, parse_macro_input, + Attribute, Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, GenericArgument, Ident, + Path, PathArguments, Type, TypePath, parse_macro_input, }; +#[derive(Clone, Copy, PartialEq, Eq)] +enum ModelType { + Enum, + Ip, + Option, + Ref, + Secret, +} + /// Try to find the value of `model` attribute, e.g. `#[model(model_type)]`. -fn model_attr(f: &Field) -> Option { - // default - let mut model_type = None; +fn model_attr(field: &Field) -> syn::Result> { + for attr in &field.attrs { + if attr.path().is_ident("model") { + let mut model_type = None; + + attr.parse_nested_meta(|meta| { + if model_type.is_some() { + return Err(meta.error("expected a single model property")); + } + + model_type = Some(if meta.path.is_ident("enum") { + ModelType::Enum + } else if meta.path.is_ident("ip") { + ModelType::Ip + } else if meta.path.is_ident("option") { + ModelType::Option + } else if meta.path.is_ident("ref") { + ModelType::Ref + } else if meta.path.is_ident("secret") { + ModelType::Secret + } else { + return Err(meta.error("unsupported model property")); + }); - // Closure here, because `model_parser` must be dropped before `model_type` is returned. - { - let model_parser = parser(|meta| { - if let Some(ident) = meta.path.get_ident() { - model_type = Some(ident.to_string()); Ok(()) - } else { - Err(meta.error("unsupported model property")) - } - }); - - for attr in &f.attrs { - if attr.path().is_ident("model") { - if let Ok(inner) = attr.meta.require_list() { - // `proc_macro2::TokenStream` to `proc_macro::TokenStream` - let tokens: TokenStream = inner.tokens.clone().into(); - Parser::parse(model_parser, tokens).unwrap(); - break; + })?; + + return Ok(model_type); + } + } + + Ok(None) +} + +fn table_attr(attrs: &[Attribute], default_name: &Ident) -> syn::Result { + let mut table_name = default_name.to_string().to_ascii_lowercase(); + + for attr in attrs { + if attr.path().is_ident("table") { + attr.parse_nested_meta(|meta| { + if let Some(ident) = meta.path.get_ident() { + table_name = ident.to_string(); + Ok(()) + } else { + Err(meta.error("unsupported table property")) } - } + })?; + + break; } } - model_type + Ok(table_name) } fn field_type(ty: &Type) -> Option<&Ident> { @@ -75,149 +109,128 @@ pub fn derive(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let name = &ast.ident; - // Find the "table" attribute, e.g. `#[table(mytable)]` - let attribute = ast.attrs.iter().find(|a| a.path().is_ident("table")); - - // Default table name - let mut table_name = name.to_string().to_ascii_lowercase(); - - // Parse table name if attribute is present - if let Some(attr) = attribute { - let table_name_parser = parser(|meta| { - if let Some(ident) = meta.path.get_ident() { - table_name = ident.to_string(); - Ok(()) - } else { - Err(meta.error("unsupported table property")) - } - }); - if let Ok(inner) = attr.meta.require_list() { - // `proc_macro2::TokenStream` to `proc_macro::TokenStream` - let tokens: TokenStream = inner.tokens.clone().into(); - parse_macro_input!(tokens with table_name_parser); - } - } + let table_name = match table_attr(&ast.attrs, name) { + Ok(table_name) => table_name, + Err(err) => return err.to_compile_error().into(), + }; let Data::Struct(DataStruct { fields: Fields::Named(FieldsNamed { named, .. }), .. - }) = ast.data + }) = &ast.data else { - // fail for other but `struct` - unimplemented!(); + return syn::Error::new_spanned( + &ast, + "Model can only be derived for structs with named fields", + ) + .to_compile_error() + .into(); }; + let non_id_fields = named + .iter() + .filter(|field| field.ident.as_ref().is_some_and(|ident| ident != "id")) + .collect::>(); + // comma-separated fields ("field1", "field2", ...) - let mut cs_fields = String::new(); + let mut cs_fields = String::with_capacity(non_id_fields.len() * 16); // comma-separated fields with quotes and aliases ("field1", "field2" "field2: _", ...) - let mut cs_aliased_fields = String::new(); + let mut cs_aliased_fields = String::with_capacity(non_id_fields.len() * 24); // comma-separated values ($1, $2, ...) - let mut cs_values = String::new(); + let mut cs_values = String::with_capacity(non_id_fields.len() * 4); // comma-separated setters ("field1" = $2, "field2" = $3, ...) - let mut cs_setters = String::new(); - - let mut add_comma = false; - let mut value_number = 1; - named.iter().for_each(|field| { - if let Some(name) = &field.ident { - if name != "id" { - if add_comma { - cs_fields.push(','); - cs_aliased_fields.push(','); - cs_values.push(','); - cs_setters.push(','); - } else { - add_comma = true; - } + let mut cs_setters = String::with_capacity(non_id_fields.len() * 20); - let name_string = name.to_string(); - - cs_fields.push('"'); - cs_fields.push_str(&name_string); - cs_fields.push('"'); - cs_aliased_fields.push('"'); - cs_aliased_fields.push_str(&name_string); - cs_aliased_fields.push('"'); - if let Some(field_type) = model_attr(field) { - cs_aliased_fields.push_str(" \""); - cs_aliased_fields.push_str(&name_string); - if field_type == "secret" { - // FIXME: don't hard-code struct name - cs_aliased_fields.push_str("?: SecretString\""); - } else if field_type == "ip" { - cs_aliased_fields.push_str(": IpAddr\""); - } else if field_type == "option" { - cs_aliased_fields.push_str("?: _\""); - } else { - cs_aliased_fields.push_str(": _\""); - } - } - cs_values.push('$'); - cs_values.push_str(&value_number.to_string()); + let mut insert_args = Vec::with_capacity(non_id_fields.len()); + let mut struct_fields = Vec::with_capacity(non_id_fields.len()); - value_number += 1; + for (index, field) in non_id_fields.iter().enumerate() { + let Some(name) = &field.ident else { + continue; + }; - cs_setters.push('"'); - cs_setters.push_str(&name.to_string()); - cs_setters.push_str("\" = $"); - cs_setters.push_str(&value_number.to_string()); + let model_type = match model_attr(field) { + Ok(model_type) => model_type, + Err(err) => return err.to_compile_error().into(), + }; + + if index > 0 { + cs_fields.push(','); + cs_aliased_fields.push(','); + cs_values.push(','); + cs_setters.push(','); + } + + let name_string = name.to_string(); + let insert_value_number = index + 1; + let update_value_number = index + 2; + + cs_fields.push('"'); + cs_fields.push_str(&name_string); + cs_fields.push('"'); + cs_aliased_fields.push('"'); + cs_aliased_fields.push_str(&name_string); + cs_aliased_fields.push('"'); + if let Some(model_type) = model_type { + cs_aliased_fields.push_str(" \""); + cs_aliased_fields.push_str(&name_string); + match model_type { + ModelType::Secret => cs_aliased_fields.push_str("?: SecretString\""), + ModelType::Ip => cs_aliased_fields.push_str(": IpAddr\""), + ModelType::Option => cs_aliased_fields.push_str("?: _\""), + ModelType::Enum | ModelType::Ref => cs_aliased_fields.push_str(": _\""), } } - }); - - // TODO: handle fields wrapped in Option - // field arguments for queries - let insert_args = named.iter().filter_map(|field| { - if let Some(name) = &field.ident { - if name != "id" { - if let Some(tokens) = model_attr(field) { - if tokens == "enum" { - if let Some(field_type) = field_type(&field.ty) { - return Some(quote! { &self.#name as &#field_type }); - } - } else if tokens == "option" { - if let Some(field_type) = option_field_type(&field.ty) { - return Some(quote! { &self.#name as &Option<#field_type> }); - } - } else if tokens == "secret" { - // FIXME: hard-coded struct name - return Some(quote! { &self.#name as &Option }); - } else if tokens == "ip" { - // FIXME: hard-coded struct name - return Some(quote! { &self.#name as &IpAddr }); - } else { - return Some(quote! { &self.#name }); - } + cs_values.push('$'); + cs_values.push_str(&insert_value_number.to_string()); + cs_setters.push('"'); + cs_setters.push_str(&name_string); + cs_setters.push_str("\" = $"); + cs_setters.push_str(&update_value_number.to_string()); + + let insert_arg = match model_type { + Some(ModelType::Enum) => { + if let Some(field_type) = field_type(&field.ty) { + quote! { &self.#name as &#field_type } + } else { + quote! { &self.#name } } - return Some(quote! { self.#name }); } - } - None - }); - let update_args = insert_args.clone(); - // Struct fields without `id`. It is not possible to use `..self`: mismatched types. - let struct_fields: Vec<_> = named - .iter() - .filter_map(|field| { - if let Some(name) = &field.ident { - if name != "id" { - return Some(quote! { #name: self.#name }); + Some(ModelType::Option) => { + if let Some(field_type) = option_field_type(&field.ty) { + quote! { &self.#name as &Option<#field_type> } + } else { + quote! { &self.#name } } } - None - }) - .collect(); + Some(ModelType::Secret) => { + // FIXME: hard-coded struct name + quote! { &self.#name as &Option } + } + Some(ModelType::Ip) => { + // FIXME: hard-coded struct name + quote! { &self.#name as &IpAddr } + } + Some(ModelType::Ref) => quote! { &self.#name }, + None => quote! { self.#name }, + }; + + insert_args.push(insert_arg); + struct_fields.push(quote! { #name: self.#name }); + } + + let update_args = insert_args.clone(); // queries let all_query = format!("SELECT id, {cs_aliased_fields} FROM \"{table_name}\""); - let find_by_id_query = - format!("SELECT id, {cs_aliased_fields} FROM \"{table_name}\" WHERE id = $1"); + let all_query_limited = all_query.clone() + " LIMIT $1 OFFSET $2"; + let find_by_id_query = all_query.clone() + " WHERE id = $1"; let delete_query = format!("DELETE FROM \"{table_name}\" WHERE id = $1"); let insert_query = format!("INSERT INTO \"{table_name}\" ({cs_fields}) VALUES ({cs_values}) RETURNING id"); let update_query = format!("UPDATE \"{table_name}\" SET {cs_setters} WHERE id = $1"); + let count_query = format!("SELECT count(*) FROM \"{table_name}\""); - // TODO: add limit and offset for all(). quote! { impl #name { pub async fn save<'e, E>(self, executor: E) -> sqlx::Result<#name> @@ -225,7 +238,6 @@ pub fn derive(input: TokenStream) -> TokenStream { E: sqlx::PgExecutor<'e> { let id = sqlx::query_scalar!(#insert_query, #(#insert_args,)*).fetch_one(executor).await?; - Ok(#name { id, #(#struct_fields,)* }) } @@ -249,12 +261,18 @@ pub fn derive(input: TokenStream) -> TokenStream { sqlx::query_as!(Self, #all_query).fetch_all(executor).await } + pub async fn all_paginated<'e, E>(executor: E, limit: i64, offset: i64) -> sqlx::Result> + where + E: sqlx::PgExecutor<'e> + { + sqlx::query_as!(Self, #all_query_limited, limit, offset).fetch_all(executor).await + } + pub async fn delete<'e, E>(self, executor: E) -> sqlx::Result<()> where E: sqlx::PgExecutor<'e> { sqlx::query!(#delete_query, self.id).execute(executor).await?; - Ok(()) } @@ -263,16 +281,22 @@ pub fn derive(input: TokenStream) -> TokenStream { E: sqlx::PgExecutor<'e> { sqlx::query!(#update_query, self.id, #(#update_args,)*).execute(executor).await?; - Ok(()) } + pub async fn count<'e, E>(executor: E) -> sqlx::Result + where + E: sqlx::PgExecutor<'e>, + { + let count = sqlx::query_scalar!(#count_query).fetch_one(executor).await? + .unwrap_or_default(); + Ok(count) + } + pub fn as_noid(self) -> #name { #name { id: NoId, #(#struct_fields,)* } } - } - } .into() } diff --git a/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx b/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx index 0259a39905..4d5f42f437 100644 --- a/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx +++ b/web/src/pages/AddLocationPage/steps/AddLocationAccessStep.tsx @@ -24,7 +24,6 @@ export const AddLocationAccessStep = () => { const { data: groups } = useQuery({ queryFn: api.group.getGroups, queryKey: ['group'], - select: (resp) => resp.data.groups, }); const selectionOptions = useMemo(() => { diff --git a/web/src/pages/EditLocationPage/EditLocationPage.tsx b/web/src/pages/EditLocationPage/EditLocationPage.tsx index 7ceee535e7..0a2add85fd 100644 --- a/web/src/pages/EditLocationPage/EditLocationPage.tsx +++ b/web/src/pages/EditLocationPage/EditLocationPage.tsx @@ -169,8 +169,8 @@ const EditLocationForm = ({ location }: { location: NetworkLocation }) => { const { data: groupsOptions } = useQuery({ queryFn: api.group.getGroups, queryKey: ['group'], - select: (resp) => - resp.data.groups.map( + select: (groups) => + groups.map( (group): SelectionOption => ({ id: group, label: group, diff --git a/web/src/pages/RulesPage/tabs/RulesDeployedTab.tsx b/web/src/pages/RulesPage/tabs/RulesDeployedTab.tsx index 8a4a0342cb..96305ca4c7 100644 --- a/web/src/pages/RulesPage/tabs/RulesDeployedTab.tsx +++ b/web/src/pages/RulesPage/tabs/RulesDeployedTab.tsx @@ -14,7 +14,7 @@ import { useRuleDeps } from '../useRuleDeps'; export const RulesDeployedTab = () => { const { data: rules } = useSuspenseQuery({ ...getRulesQueryOptions, - select: (resp) => resp.data.filter((rule) => rule.state === AclStatus.Applied), + select: (rules) => rules.filter((rule) => rule.state === AclStatus.Applied), }); const isEmpty = rules.length === 0; diff --git a/web/src/pages/RulesPage/tabs/RulesPendingTab.tsx b/web/src/pages/RulesPage/tabs/RulesPendingTab.tsx index 9f64aca01b..f0ff2ac3c6 100644 --- a/web/src/pages/RulesPage/tabs/RulesPendingTab.tsx +++ b/web/src/pages/RulesPage/tabs/RulesPendingTab.tsx @@ -14,7 +14,7 @@ import { useRuleDeps } from '../useRuleDeps'; export const RulesPendingTab = () => { const { data: rules } = useSuspenseQuery({ ...getRulesQueryOptions, - select: (resp) => resp.data.filter((rule) => rule.state !== AclStatus.Applied), + select: (rules) => rules.filter((rule) => rule.state !== AclStatus.Applied), }); const isEmpty = rules.length === 0; diff --git a/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx b/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx index 0f42bfabbd..8581a266c2 100644 --- a/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx +++ b/web/src/pages/UsersOverviewPage/modals/AddUserModal/AddUserModal.tsx @@ -354,9 +354,7 @@ const AddUserModalForm = () => { } const clean = removeEmptyStrings(value); const { data: created } = await addUserMutation(clean); - const { - data: { groups }, - } = await api.group.getGroups(); + const groups = await api.group.getGroups(); if (enrollmentEnabled) { try { const enrollmentResponse = ( diff --git a/web/src/shared/api/api.ts b/web/src/shared/api/api.ts index 238c7b11d1..439b964869 100644 --- a/web/src/shared/api/api.ts +++ b/web/src/shared/api/api.ts @@ -2,6 +2,7 @@ import dayjs from 'dayjs'; import { cloneDeep } from 'lodash-es'; import { removeEmptyStrings } from '../utils/removeEmptyStrings'; import { client } from './api-client'; +import { fetchAllPages, fetchPage } from './pagination'; import type { AclAlias, AclCount, @@ -58,7 +59,6 @@ import type { GatewayInfo, GetCAResponse, GroupInfo, - GroupsResponse, IpValidation, LicenseCheckResponse, LicenseInfoResponse, @@ -81,7 +81,6 @@ import type { OpenIdAuthInfo, OpenIdClient, OpenIdProvidersResponse, - PaginatedResponse, RenameApiTokenRequest, RenameAuthKeyRequest, ResourceDisplay, @@ -115,7 +114,7 @@ import type { const api = { getUsersOverview: async (): Promise => { - const { data: users } = await api.user.getUsers(); + const users = await api.user.getUsers(); const res: UsersListItem[] = []; for (const user of users) { const { data: profile } = await api.user.getUser(user.username); @@ -153,7 +152,7 @@ const api = { callback: (data: unknown) => client.post(`/openid/callback`, data), }, openIdClient: { - getOpenIdClients: () => client.get(`/oauth`), + getOpenIdClients: () => fetchAllPages(`/oauth`), getOpenIdClient: (clientId: string) => client.get(`/oauth/${clientId}`), addOpenIdClient: (data: AddOpenIdClient) => client.post(`/oauth`, data), editOpenIdClient: (data: OpenIdClient) => @@ -166,7 +165,7 @@ const api = { }, group: { addGroup: (data: CreateGroupRequest) => client.post('/group', data), - getGroups: () => client.get('/group'), + getGroups: () => fetchAllPages('/group'), getGroupsInfo: () => client.get('/group-info'), editGroup: ({ originalName, ...data }: EditGroupRequest) => client.put(`/group/${originalName ?? data.name}`, data), @@ -184,7 +183,7 @@ const api = { username, }), getMe: () => client.get('/me'), - getUsers: () => client.get('/user'), + getUsers: () => fetchAllPages('/user'), getUser: (username: string) => client.get(`/user/${username}`), editUser: (data: { username: string; body: User }) => client.put(`/user/${data.username}`, data.body), @@ -307,7 +306,7 @@ const api = { editDevice: ({ id, ...data }: EditNetworkDeviceRequest) => client.put(`/device/network/${id}`, data), getDevice: (id: number) => client.get(`/device/network/${id}`), - getDevices: () => client.get(`/device/network`), + getDevices: () => fetchAllPages('/device/network'), getDeviceConfig: (id: number) => client.get(`/device/network/${id}/config`), generateToken: (id: number) => client.post(`/device/network/start_cli/${id}`), @@ -348,36 +347,25 @@ const api = { getLocationGatewaysStatus: (id: number) => client.get(`/network/${id}/gateways`), getLocationConnectedUsers: ({ id, ...params }: LocationConnectedUsersRequest) => - client - .get>( - `/network/${id}/stats/connected_users`, - { - params: { - ...params, - from: params.from - ? dayjs.utc().subtract(params.from, 'hour').toISOString() - : undefined, - }, - }, - ) - .then((resp) => resp.data), + fetchPage(`/network/${id}/stats/connected_users`, { + ...params, + from: params.from + ? dayjs.utc().subtract(params.from, 'hour').toISOString() + : undefined, + }), getLocationConnectedNetworkDevices: ({ id, ...params }: LocationConnectedNetworkDevicesRequest) => - client - .get>( - `/network/${id}/stats/connected_network_devices`, - { - params: { - ...params, - from: params.from - ? dayjs.utc().subtract(params.from, 'hour').toISOString() - : undefined, - }, - }, - ) - .then((resp) => resp.data), + fetchPage( + `/network/${id}/stats/connected_network_devices`, + { + ...params, + from: params.from + ? dayjs.utc().subtract(params.from, 'hour').toISOString() + : undefined, + }, + ), getLocationConnectedUserDevices: ({ locationId, userId, @@ -503,7 +491,7 @@ const api = { }, rule: { getCount: () => client.get('acl/rule/count'), - getRules: () => client.get(`/acl/rule`), + getRules: () => fetchAllPages(`/acl/rule`), getRule: (ruleId: number | string) => client.get(`/acl/rule/${ruleId}`), addRule: (data: AddAclRuleRequest) => client.post(`/acl/rule`, data), editRule: (data: EditAclRuleRequest) => client.put(`/acl/rule/${data.id}`, data), @@ -539,11 +527,7 @@ const api = { client.post('/license/check', data), getSessionInfo: () => client.get(`/session-info`), getActivityLog: (data?: ActivityLogRequestParams) => - client - .get>(`/activity_log`, { - params: data, - }) - .then((resp) => resp.data), + fetchPage(`/activity_log`, data), info: () => client.get('/info'), getLicenseInfo: () => client.get(`/enterprise_info`), } as const; diff --git a/web/src/shared/api/pagination.ts b/web/src/shared/api/pagination.ts new file mode 100644 index 0000000000..faac61df00 --- /dev/null +++ b/web/src/shared/api/pagination.ts @@ -0,0 +1,26 @@ +import { client } from './api-client'; +import type { PaginatedResponse } from './types'; + +export const fetchPage = ( + path: string, + params?: object, +): Promise> => + client + .get>(path, { + params, + }) + .then((resp) => resp.data); + +export const fetchAllPages = async (path: string): Promise => { + const data: T[] = []; + let page: number | null = 1; + + while (page !== null) { + const response: PaginatedResponse = await fetchPage(path, { page }); + + data.push(...response.data); + page = response.pagination.next_page; + } + + return data; +}; diff --git a/web/src/shared/api/types.ts b/web/src/shared/api/types.ts index 61124dd54a..f2b1ec342d 100644 --- a/web/src/shared/api/types.ts +++ b/web/src/shared/api/types.ts @@ -166,9 +166,6 @@ export interface GroupInfo { is_admin: boolean; } -export interface GroupsResponse { - groups: string[]; -} export interface UsersListItem extends User { name: string; devices: Device[]; @@ -1276,7 +1273,7 @@ export interface PaginationMeta { current_page: number; page_size: number; total_items: number; - total_pagers: number; + total_pages: number; next_page: number | null; } diff --git a/web/src/shared/query.ts b/web/src/shared/query.ts index 4ede8e0dbd..4cc177a7da 100644 --- a/web/src/shared/query.ts +++ b/web/src/shared/query.ts @@ -75,7 +75,6 @@ export const getGatewayQueryOptions = (id: number) => export const getNetworkDevicesQueryOptions = queryOptions({ queryFn: api.network_device.getDevices, queryKey: ['device', 'network'], - select: (resp) => resp.data, }); export const getUserMeQueryOptions = queryOptions({ @@ -142,7 +141,6 @@ export const getUsersQueryOptions = queryOptions({ queryKey: ['user'], refetchOnMount: true, refetchOnReconnect: true, - select: (resp) => resp.data, }); export const getUsersOverviewQueryOptions = queryOptions({ @@ -163,7 +161,6 @@ export const getGroupsInfoQueryOptions = queryOptions({ export const getOpenIdClientQueryOptions = queryOptions({ queryFn: api.openIdClient.getOpenIdClients, queryKey: ['oauth'], - select: (resp) => resp.data, }); export const getWebhooksQueryOptions = queryOptions({ @@ -204,7 +201,6 @@ export const getRulesCountQueryOptions = queryOptions({ export const getRulesQueryOptions = queryOptions({ queryFn: api.acl.rule.getRules, queryKey: ['acl', 'rule'], - select: (resp) => resp.data, }); export const getAliasesQueryOptions = queryOptions({