1# Copyright 2019 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import pickle
6import re
7
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.cros.enterprise import enterprise_policy_base
10from autotest_lib.client.cros.enterprise import enterprise_network_api
11
12
13class policy_GlobalNetworkSettings(
14        enterprise_policy_base.EnterprisePolicyTest):
15    version = 1
16
17
18    def cleanup(self):
19        """Re-enable ethernet after the test is completed."""
20        if hasattr(self, 'net_api'):
21            self.net_api.chrome_net_context.enable_network_device('Ethernet')
22        super(policy_GlobalNetworkSettings, self).cleanup()
23
24
25    def _test_only_connect_if_available(self, policy_error, user_error):
26        """
27        Verify the AllowOnlyPolicyNetworksToConnectIfAvailable policy.
28
29        If both networks are available, only the policy network should
30        connect. If the policy network is unavailable, then the user network
31        may connect. Ensure the errors are caused by the policy blocking or
32        allowing the correct connections.
33
34        @param policy_error: Error (if any) raised from connecting to policy
35                network.
36        @param user_error: Error (if any) raised from connecting to user
37                network.
38
39        @raises error.TestFail: If errors do not match policy behavior.
40
41        """
42        OUT_OF_RANGE_ERROR = 'The SSID: .* is not in WiFi range of the DUT'
43        BLOCKED_ERROR = ('Could not connect to .* network. '
44                         'Error returned by chrome.networkingPrivate.'
45                         'startConnect API: blocked-by-policy')
46
47        if policy_error and user_error:
48            raise error.TestFail(
49                    'Unable to connect to user or policy network: %s'
50                    % policy_error)
51        elif not policy_error and user_error:
52            if not re.match(BLOCKED_ERROR, str(user_error)):
53                raise error.TestFail('Network API received unrecognized '
54                                     'error connecting to the user '
55                                     'network: %s' % user_error)
56        elif policy_error and not user_error:
57            if not re.match(OUT_OF_RANGE_ERROR, str(policy_error)):
58                raise error.TestFail('Network API received unrecognized '
59                                     'error connecting to the policy '
60                                     'network: %s' % policy_error)
61        elif not (policy_error or user_error):
62            raise error.TestFail('User network connected despite policy '
63                                 'network being available')
64
65
66    def _test_wifi_disabled_policy(self, policy_error, user_error):
67        """
68        Verify the DisableNetworkTypes policy on WiFi.
69
70        Both networks should show the 'No wifi networks found' error.
71
72        @param policy_error: Error (if any) raised from connecting to policy
73                network.
74        @param user_error: Error (if any) raised from connecting to user
75                network.
76
77        @raises error.TestFail: If errors do not match policy behavior.
78
79        """
80        for err in [policy_error, user_error]:
81            if err is None:
82                raise error.TestFail('DUT was able to connect to WiFi, but '
83                                     'WiFi should be blocked.')
84            elif str(err) != 'No wifi networks found.':
85                raise error.TestFail('Network API received unrecognized '
86                                     'error connecting to network: %s'
87                                     % err)
88
89
90    def _test_autoconnect_policy(self, ssid, policy_network, user_network):
91        """
92        Verify the AllowOnlyPolicyNetworksToAutoconnect policy.
93
94        Disconnect from the network. The policy network should reconnect,
95        and the user network should not.
96
97        @param ssid: SSID of connected network.
98        @param policy_network: Network policy defined network.
99        @param user_network: User defined network.
100
101        @raises error.TestFail: If errors do not match policy behavior.
102
103        """
104        self.net_api.disconnect_from_network(ssid)
105
106        if self.net_api.is_network_connected(ssid):
107            if ssid == user_network.ssid:
108                raise error.TestFail(
109                        'User network autoconnected despite '
110                        'AllowOnlyPolicyNetworksToAutoconnect=True')
111        elif ssid == policy_network.ssid:
112            raise error.TestFail(
113                    'Policy network did not autoconnect, despite '
114                    'AllowOnlyPolicyNetworksToAutoconnect=True')
115
116
117    def _test_allow_only_policy(self, policy_error, user_error):
118        """
119        Verify the AllowOnlyPolicyNetworksToConnect policy.
120
121        policy_error should be None, user_error should not.
122
123        @param policy_error: Error (if any) raised from connecting to policy
124                network.
125        @param user_error: Error (if any) raised from connecting to user
126                network.
127
128        @raises error.TestFail: If errors do not match policy behavior.
129
130        """
131        if policy_error:
132            raise error.TestFail('DUT should have connected to policy '
133                                 'network, but did not: %s' % policy_error)
134        if not user_error:
135            raise error.TestFail('DUT was able to connect to user '
136                                 'network, but should have been blocked.')
137
138
139    def test_global_settings(self, gnc_settings, policy_network, user_network):
140        """
141        Attempt to connect to the policy network, then the user_network.
142
143        Ensure connection behavior matches GlobalNetworkConfiguration.
144
145        @param gnc_settings: GlobalNetworkConfiguration dictionary value.
146        @param policy_network_pickle: NetworkConfig object representing
147                the policy network configuration.
148        @param user_network_pickle: NetworkConfig object representing
149                user network configuration.
150
151        @raise error.TestFail: DUT behavior does not match policy settings.
152
153        """
154        # Store connection errors to check later.
155        network_errors = {}
156        for ssid in [policy_network.ssid, user_network.ssid]:
157            network_errors[ssid] = None
158            try:
159                self.net_api.connect_to_network(ssid)
160            except error.TestFail as e:
161                network_errors[ssid] = e
162                continue
163
164            if gnc_settings.get('AllowOnlyPolicyNetworksToAutoconnect'):
165                self._test_autoconnect_policy(ssid,
166                                              policy_network,
167                                              user_network)
168                continue
169
170            if not self.net_api.is_network_connected(ssid):
171                raise error.TestFail(
172                        'Did not connect to network (%s)' % ssid)
173
174        policy_error = network_errors[policy_network.ssid]
175        user_error = network_errors[user_network.ssid]
176
177        if gnc_settings.get('AllowOnlyPolicyNetworksToConnectIfAvailable'):
178            self._test_only_connect_if_available(policy_error, user_error)
179
180        elif 'WiFi' in gnc_settings.get('DisableNetworkTypes', {}):
181            self._test_wifi_disabled_policy(policy_error, user_error)
182
183        elif gnc_settings.get('AllowOnlyPolicyNetworksToConnect'):
184            self._test_allow_only_policy(policy_error, user_error)
185
186
187    def run_once(self, gnc_settings=None, policy_network_pickle=None,
188                 user_network_pickle=None):
189        """
190        Setup and run the test configured for the specified test case.
191
192        policy_network is in the network policy, and user_network is not.
193        The GlobalNetworkConfiguration settings modify how the DUT is able to
194        connect to both of these networks.
195
196        @param gnc_settings: GlobalNetworkConfiguration dictionary value.
197        @param policy_network_pickle: Pickled NetworkConfig object to set as
198                the policy network configuration.
199        @param user_network_pickle: Pickled NetworkConfig object to set as the
200                user network configuration.
201
202        @raises error.TestFail: If DUT's actions do not match policy settings.
203
204        """
205        if policy_network_pickle is None or user_network_pickle is None:
206            raise error.TestError('Networks cannot be None')
207
208        policy_network = pickle.loads(policy_network_pickle)
209        user_network = pickle.loads(user_network_pickle)
210
211        self.setup_case(
212            user_policies={'OpenNetworkConfiguration': policy_network.policy()},
213            device_policies={'DeviceOpenNetworkConfiguration': {
214                             'GlobalNetworkConfiguration': gnc_settings}},
215            extension_paths=[
216                enterprise_network_api.NETWORK_TEST_EXTENSION_PATH
217            ],
218            enroll=True
219        )
220
221        self.net_api = enterprise_network_api.\
222                ChromeEnterpriseNetworkContext(self.cr)
223        # Disable ethernet so device will default to WiFi
224        self.net_api.disable_network_device('Ethernet')
225
226        self.test_global_settings(gnc_settings, policy_network, user_network)
227