1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6fn default_enabled() -> bool {
7 true
8}
9
10fn default_purpose() -> String {
11 "site-vpn".into()
12}
13
14fn default_remote_access_purpose() -> String {
15 "remote-user-vpn".into()
16}
17
18fn default_vpn_client_purpose() -> String {
19 "vpn-client".into()
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct CreateSiteToSiteVpnRequest {
24 pub name: String,
25 pub vpn_type: String,
26 #[serde(default = "default_purpose")]
27 pub purpose: String,
28 #[serde(default = "default_enabled")]
29 pub enabled: bool,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub remote_site_id: Option<String>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub x_ipsec_pre_shared_key: Option<String>,
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub ipsec_peer_ip: Option<String>,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub ipsec_dynamic_routing: Option<bool>,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub ipsec_separate_ikev2_networks: Option<bool>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub ipsec_tunnel_ip_enabled: Option<bool>,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub ipsec_tunnel_ip: Option<String>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub ipsec_key_exchange: Option<String>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub ipsec_remote_identifier_enabled: Option<bool>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub ipsec_remote_identifier: Option<String>,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub ipsec_local_identifier_enabled: Option<bool>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub ipsec_local_identifier: Option<String>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub ipsec_pfs: Option<bool>,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub ipsec_ike_encryption: Option<String>,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub ipsec_ike_hash: Option<String>,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub ipsec_ike_dh_group: Option<u32>,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub ipsec_dh_group: Option<u32>,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub ipsec_ike_lifetime: Option<u32>,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub ipsec_esp_encryption: Option<String>,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub ipsec_esp_hash: Option<String>,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 pub ipsec_esp_dh_group: Option<u32>,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub ipsec_esp_lifetime: Option<u32>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub ipsec_interface: Option<String>,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub ipsec_local_ip: Option<String>,
78 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub remote_vpn_dynamic_subnets_enabled: Option<bool>,
80 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub x_openvpn_shared_secret_key: Option<String>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pub openvpn_local_address: Option<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub openvpn_local_port: Option<u16>,
86 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub openvpn_encryption_cipher: Option<String>,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub openvpn_remote_host: Option<String>,
90 #[serde(default, skip_serializing_if = "Option::is_none")]
91 pub openvpn_remote_address: Option<String>,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub openvpn_remote_port: Option<u16>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub openvpn_mode: Option<String>,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub interface_mtu_enabled: Option<bool>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub interface_mtu: Option<u16>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub mss_clamp: Option<String>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub mss_clamp_mss: Option<u16>,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub route_distance: Option<u32>,
106 #[serde(default, skip_serializing_if = "Vec::is_empty")]
107 pub remote_vpn_subnets: Vec<String>,
108 #[serde(default, flatten)]
109 pub extra: BTreeMap<String, Value>,
110}
111
112pub type UpdateSiteToSiteVpnRequest = CreateSiteToSiteVpnRequest;
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct CreateRemoteAccessVpnServerRequest {
116 pub name: String,
117 pub vpn_type: String,
118 #[serde(default = "default_remote_access_purpose")]
119 pub purpose: String,
120 #[serde(default = "default_enabled")]
121 pub enabled: bool,
122 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub setting_preference: Option<String>,
124 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub l2tp_allow_weak_ciphers: Option<bool>,
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub require_mschapv2: Option<bool>,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub exposed_to_site_vpn: Option<bool>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub dhcpd_wins_enabled: Option<bool>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub dhcpd_wins_1: Option<String>,
134 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub dhcpd_wins_2: Option<String>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub dhcpd_dns_enabled: Option<bool>,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub dhcpd_dns_1: Option<String>,
140 #[serde(default, skip_serializing_if = "Option::is_none")]
141 pub dhcpd_dns_2: Option<String>,
142 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub local_port: Option<u16>,
144 #[serde(default, skip_serializing_if = "Option::is_none")]
145 pub x_wireguard_private_key: Option<String>,
146 #[serde(default, skip_serializing_if = "Option::is_none")]
147 pub vpn_client_configuration_remote_ip_override_enabled: Option<bool>,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
149 pub vpn_client_configuration_remote_ip_override: Option<String>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub x_ipsec_pre_shared_key: Option<String>,
152 #[serde(default, skip_serializing_if = "Option::is_none")]
153 pub radiusprofile_id: Option<String>,
154 #[serde(default, skip_serializing_if = "Option::is_none")]
155 pub ip_subnet: Option<String>,
156 #[serde(default, skip_serializing_if = "Option::is_none")]
157 pub ipv6_subnet: Option<String>,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub dhcpd_start: Option<String>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
161 pub dhcpd_stop: Option<String>,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
163 pub wireguard_interface: Option<String>,
164 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub wireguard_interface_binding_mode_ip_version: Option<String>,
166 #[serde(default, skip_serializing_if = "Option::is_none")]
167 pub l2tp_interface: Option<String>,
168 #[serde(default, skip_serializing_if = "Option::is_none")]
169 pub openvpn_interface: Option<String>,
170 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub wireguard_local_wan_ip: Option<String>,
172 #[serde(default, skip_serializing_if = "Option::is_none")]
173 pub l2tp_local_wan_ip: Option<String>,
174 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub openvpn_local_wan_ip: Option<String>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
177 pub vpn_binding_mode: Option<String>,
178 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub interface_mtu_enabled: Option<bool>,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
181 pub interface_mtu: Option<u16>,
182 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub mss_clamp: Option<String>,
184 #[serde(default, skip_serializing_if = "Option::is_none")]
185 pub mss_clamp_mss: Option<u16>,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub mss_clamp_ipv6: Option<String>,
188 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub mss_clamp_mss_ipv6: Option<u16>,
190 #[serde(default, flatten)]
191 pub extra: BTreeMap<String, Value>,
192}
193
194pub type UpdateRemoteAccessVpnServerRequest = CreateRemoteAccessVpnServerRequest;
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct CreateVpnClientProfileRequest {
198 pub name: String,
199 pub vpn_type: String,
200 #[serde(default = "default_vpn_client_purpose")]
201 pub purpose: String,
202 #[serde(default = "default_enabled")]
203 pub enabled: bool,
204 #[serde(default, flatten)]
205 pub extra: BTreeMap<String, Value>,
206}
207
208pub type UpdateVpnClientProfileRequest = CreateVpnClientProfileRequest;
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct CreateWireGuardPeerRequest {
212 pub name: String,
213 pub interface_ip: String,
214 #[serde(default, skip_serializing_if = "Option::is_none")]
215 pub interface_ipv6: Option<String>,
216 pub public_key: String,
217 #[serde(default, skip_serializing_if = "Vec::is_empty")]
218 pub allowed_ips: Vec<String>,
219 #[serde(default)]
220 pub preshared_key: String,
221 #[serde(default, skip_serializing_if = "Option::is_none")]
222 pub private_key: Option<String>,
223 #[serde(default, flatten)]
224 pub extra: BTreeMap<String, Value>,
225}
226
227pub type UpdateWireGuardPeerRequest = CreateWireGuardPeerRequest;
228
229#[cfg(test)]
230mod tests {
231 use super::{
232 CreateRemoteAccessVpnServerRequest, CreateSiteToSiteVpnRequest,
233 CreateVpnClientProfileRequest, CreateWireGuardPeerRequest,
234 };
235
236 #[test]
237 fn site_to_site_request_defaults_purpose_and_enabled() {
238 let request: CreateSiteToSiteVpnRequest = serde_json::from_value(serde_json::json!({
239 "name": "Branch Tunnel",
240 "vpn_type": "ipsec-vpn"
241 }))
242 .expect("request should deserialize");
243
244 assert_eq!(request.purpose, "site-vpn");
245 assert!(request.enabled);
246 }
247
248 #[test]
249 fn remote_access_request_defaults_purpose_and_enabled() {
250 let request: CreateRemoteAccessVpnServerRequest =
251 serde_json::from_value(serde_json::json!({
252 "name": "WireGuard Remote Access",
253 "vpn_type": "wireguard"
254 }))
255 .expect("request should deserialize");
256
257 assert_eq!(request.purpose, "remote-user-vpn");
258 assert!(request.enabled);
259 }
260
261 #[test]
262 fn vpn_client_profile_request_defaults_purpose_and_enabled() {
263 let request: CreateVpnClientProfileRequest = serde_json::from_value(serde_json::json!({
264 "name": "Branch Client",
265 "vpn_type": "openvpn-client"
266 }))
267 .expect("request should deserialize");
268
269 assert_eq!(request.purpose, "vpn-client");
270 assert!(request.enabled);
271 }
272
273 #[test]
274 fn wireguard_peer_request_preserves_empty_preshared_key() {
275 let request: CreateWireGuardPeerRequest = serde_json::from_value(serde_json::json!({
276 "name": "Laptop",
277 "interface_ip": "192.168.42.2",
278 "public_key": "pubkey",
279 "allowed_ips": []
280 }))
281 .expect("request should deserialize");
282
283 assert_eq!(request.preshared_key, "");
284 assert!(request.allowed_ips.is_empty());
285 assert!(request.private_key.is_none());
286 }
287}