@@ -2047,6 +2047,116 @@ def test_device_changelist_activate_deactivate_admin_action_security(
2047
2047
)
2048
2048
self .assertEqual (mocked_deactivate .call_count , 1 )
2049
2049
2050
+ def test_vpn_template_switch (self ):
2051
+ """
2052
+ Test switching between two VPN templates that use the same VPN server
2053
+ Verifies that:
2054
+ 1. Only one VpnClient exists at a time
2055
+ 2. VPN config variables are correctly resolved
2056
+ 3. Switching back and forth works properly
2057
+ """
2058
+ vpn = self ._create_vpn ()
2059
+ template1 = self ._create_template (
2060
+ name = 'vpn-test-1' ,
2061
+ type = 'vpn' ,
2062
+ vpn = vpn ,
2063
+ config = {
2064
+ 'openvpn' : [
2065
+ {
2066
+ 'cert' : '{{cert_path}}' ,
2067
+ 'key' : '{{key_path}}' ,
2068
+ 'dev' : 'tun0' ,
2069
+ 'client' : True ,
2070
+ }
2071
+ ]
2072
+ },
2073
+ auto_cert = True ,
2074
+ default = True , # This will auto-apply template1 to the device
2075
+ )
2076
+ template2 = self ._create_template (
2077
+ name = 'vpn-test-2' ,
2078
+ type = 'vpn' ,
2079
+ vpn = vpn ,
2080
+ config = {
2081
+ 'openvpn' : [
2082
+ {
2083
+ 'cert' : '{{cert_path}}' ,
2084
+ 'key' : '{{key_path}}' ,
2085
+ 'dev' : 'tun1' ,
2086
+ 'client' : True ,
2087
+ }
2088
+ ]
2089
+ },
2090
+ auto_cert = True ,
2091
+ )
2092
+
2093
+ # Add device with default template (template1)
2094
+ path = reverse (f'admin:{ self .app_label } _device_add' )
2095
+ params = self ._get_device_params (org = self ._get_org ())
2096
+ response = self .client .post (path , data = params , follow = True )
2097
+ self .assertEqual (response .status_code , 200 )
2098
+
2099
+ config = Device .objects .get (name = params ['name' ]).config
2100
+ self .assertEqual (config .templates .count (), 1 )
2101
+ self .assertEqual (config .vpnclient_set .count (), 1 )
2102
+
2103
+ # Verify initial VPN config is correct
2104
+ self .assertEqual (
2105
+ config .backend_instance .config ['openvpn' ][0 ]['cert' ],
2106
+ f'/etc/x509/client-{ vpn .pk .hex } .pem' ,
2107
+ )
2108
+
2109
+ # Switch to template2
2110
+ path = reverse (f'admin:{ self .app_label } _device_change' , args = [config .device_id ])
2111
+ params .update ({
2112
+ 'config-0-id' : str (config .pk ),
2113
+ 'config-0-device' : str (config .device_id ),
2114
+ 'config-0-templates' : str (template2 .pk ),
2115
+ 'config-INITIAL_FORMS' : 1 ,
2116
+ '_continue' : True ,
2117
+ })
2118
+ response = self .client .post (path , data = params , follow = True )
2119
+ self .assertEqual (response .status_code , 200 )
2120
+
2121
+ # Refresh config from DB
2122
+ config .refresh_from_db ()
2123
+
2124
+ # Verify only one VPN client exists
2125
+ self .assertEqual (config .vpnclient_set .count (), 1 )
2126
+
2127
+ # Verify VPN config variables are still resolved
2128
+ self .assertEqual (
2129
+ config .backend_instance .config ['openvpn' ][0 ]['cert' ],
2130
+ f'/etc/x509/client-{ vpn .pk .hex } .pem' ,
2131
+ )
2132
+ self .assertEqual (
2133
+ config .backend_instance .config ['openvpn' ][0 ]['dev' ],
2134
+ 'tun1' # Verify we're using template2's config
2135
+ )
2136
+
2137
+ # Switch back to template1
2138
+ params .update ({
2139
+ 'config-0-templates' : str (template1 .pk ),
2140
+ })
2141
+ response = self .client .post (path , data = params , follow = True )
2142
+ self .assertEqual (response .status_code , 200 )
2143
+
2144
+ # Refresh config from DB
2145
+ config .refresh_from_db ()
2146
+
2147
+ # Verify still only one VPN client exists
2148
+ self .assertEqual (config .vpnclient_set .count (), 1 )
2149
+
2150
+ # Verify VPN config variables are still resolved
2151
+ self .assertEqual (
2152
+ config .backend_instance .config ['openvpn' ][0 ]['cert' ],
2153
+ f'/etc/x509/client-{ vpn .pk .hex } .pem' ,
2154
+ )
2155
+ self .assertEqual (
2156
+ config .backend_instance .config ['openvpn' ][0 ]['dev' ],
2157
+ 'tun0' # Verify we're back to template1's config
2158
+ )
2159
+
2050
2160
2051
2161
class TestTransactionAdmin (
2052
2162
CreateConfigTemplateMixin ,
0 commit comments