1# Copyright 2015 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 logging
6
7from autotest_lib.client.cros import httpd
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.cros import enterprise_policy_base
10
11POLICY_NAME = 'URLBlacklist'
12URL_HOST = 'http://localhost'
13URL_PORT = 8080
14URL_BASE = '%s:%d/%s' % (URL_HOST, URL_PORT, 'test_data')
15ALL_URLS_LIST = [URL_BASE + website for website in
16                  ['/website1.html',
17                   '/website2.html',
18                   '/website3.html']]
19SINGLE_BLACKLISTED_FILE_DATA = ALL_URLS_LIST[:1]
20MULTIPLE_BLACKLISTED_FILES_DATA = ALL_URLS_LIST[:2]
21BLOCKED_USER_MESSAGE = 'Webpage Blocked'
22BLOCKED_ERROR_MESSAGE = 'ERR_BLOCKED_BY_ADMINISTRATOR'
23
24
25class policy_URLBlacklist(enterprise_policy_base.EnterprisePolicyTest):
26    """
27    Test effect of URLBlacklist policy on Chrome OS behavior.
28
29    Navigate to each the URLs in the ALL_URLS_LIST and verify that the URLs
30    specified by the URLBlackList policy are blocked.
31    Throw a warning if the user message on the blocked page is incorrect.
32
33    Two test cases (SingleBlacklistedFile, MultipleBlacklistedFiles) are
34    designed to verify that URLs specified in the URLBlacklist policy are
35    blocked.
36    The third test case(NotSet) is designed to verify that none of the URLs
37    are blocked since the URLBlacklist policy is set to None
38
39    The test case shall pass if the URLs that are part of the URLBlacklist
40    policy value are blocked.
41    The test case shall also pass if the URLs that are not part of the
42    URLBlacklist policy value are not blocked.
43    The test case shall fail if the above behavior is not enforced.
44
45    """
46    version = 1
47    TEST_CASES = {
48        'NotSet': '',
49        'SingleBlacklistedFile': SINGLE_BLACKLISTED_FILE_DATA,
50        'MultipleBlacklistedFiles': MULTIPLE_BLACKLISTED_FILES_DATA,
51    }
52
53    def initialize(self, args=()):
54        super(policy_URLBlacklist, self).initialize(args)
55        self.start_webserver(URL_PORT)
56
57    def navigate_to_website(self, url):
58        """
59        Open a new tab in the browser and navigate to the URL.
60
61        @param url: the URL that the browser is navigated to.
62        @returns: a chrome browser tab navigated to the URL.
63
64        """
65        tab = self.cr.browser.tabs.New()
66        logging.info('Navigating to URL:%s', url)
67        try:
68            tab.Navigate(url, timeout=10)
69        except Exception, err:
70            logging.error('Timeout Exception in navigating URL: %s\n %s',
71                    url, err)
72        tab.WaitForDocumentReadyStateToBeComplete()
73        return tab
74
75    def scrape_text_from_website(self, tab):
76        """
77        Returns a list of the text content matching the page_scrape_cmd filter.
78
79        @param tab: tab containing the website to be parsed.
80        @raises: TestFail if the expected text content was not found on the
81                 page.
82
83        """
84        parsed_message_string = ''
85        parsed_message_list = []
86        page_scrape_cmd = 'document.getElementById("main-message").innerText;'
87        try:
88            parsed_message_string = tab.EvaluateJavaScript(page_scrape_cmd)
89        except Exception as err:
90                raise error.TestFail('Unable to find the expected '
91                        'text content on the test page: %s\n %r'%(tab.url, err))
92        logging.info('Parsed message:%s', parsed_message_string)
93        parsed_message_list = [str(word) for word in
94                               parsed_message_string.split('\n') if word]
95        return parsed_message_list
96
97    def is_url_blocked(self, url):
98        """
99        Returns True if the URL is blocked else returns False.
100
101        @param url: The URL to be checked whether it is blocked.
102
103        """
104        parsed_message_list = []
105        tab = self.navigate_to_website(url)
106        parsed_message_list = self.scrape_text_from_website(tab)
107        if len(parsed_message_list) == 2 and \
108                parsed_message_list[0] == 'Website enabled' and \
109                parsed_message_list[1] == 'Website is enabled':
110            return False
111
112        #Check if the accurate user error message displayed on the error page.
113        if parsed_message_list[0] != BLOCKED_USER_MESSAGE or \
114                parsed_message_list[1] != BLOCKED_ERROR_MESSAGE:
115            logging.warning('The Blocked page user notification '
116                            'messages, %s and %s are not displayed on '
117                            'the blocked page. The messages may have '
118                            'been modified. Please check and update the '
119                            'messages in this file accordingly.',
120                            BLOCKED_USER_MESSAGE, BLOCKED_ERROR_MESSAGE)
121        return True
122
123    def _test_URLBlacklist(self, policy_value, policies_json):
124        """
125        Verify CrOS enforces URLBlacklist policy value.
126
127        When the URLBlacklist policy is set to one or more Domains,
128        check that navigation to URLs in the Blocked list are blocked.
129        When set to None, check that none of the websites are blocked.
130
131        @param policy_value: policy value expected on chrome://policy page.
132        @param policies_json: policy JSON data to send to the fake DM server.
133        @raises: TestFail if url is blocked/not blocked based on the
134                 corresponding policy values.
135
136        """
137        url_is_blocked = None
138        self.setup_case(POLICY_NAME, policy_value, policies_json)
139        logging.info('Running _test_URLBlacklist(%s, %s)',
140                     policy_value, policies_json)
141
142        for url in ALL_URLS_LIST:
143            url_is_blocked = self.is_url_blocked(url)
144            if policy_value:
145                if url in policy_value and not url_is_blocked:
146                    raise error.TestFail('The URL %s should have been blocked'
147                                         ' by policy, but it was allowed' % url)
148            elif url_is_blocked:
149                raise error.TestFail('The URL %s should have been allowed'
150                                      'by policy, but it was blocked' % url)
151
152    def _run_test_case(self, case):
153        """
154        Setup and run the test configured for the specified test case.
155
156        Set the expected |policy_value| and |policies_json| data based on the
157        test |case|. If the user specified an expected |value| in the command
158        line args, then use it to set the |policy_value| and blank out the
159        |policies_json|.
160
161        @param case: Name of the test case to run.
162
163        """
164        policy_value = None
165        policies_json = None
166
167        if case not in self.TEST_CASES:
168            raise error.TestError('Test case %s is not valid.' % case)
169        logging.info('Running test case: %s', case)
170
171        if self.is_value_given:
172            # If |value| was given in the command line args, then set expected
173            # |policy_value| to the given value, and |policies_json| to None.
174            policy_value = self.value
175            policies_json = None
176        else:
177            # Otherwise, set expected |policy_value| and setup |policies_json|
178            # data to the values required by the test |case|.
179            if case == 'NotSet':
180                policy_value = None
181                policies_json = {'URLBlacklist': None}
182            elif case == 'SingleBlacklistedFile':
183                policy_value = ','.join(SINGLE_BLACKLISTED_FILE_DATA)
184                policies_json = {'URLBlacklist': SINGLE_BLACKLISTED_FILE_DATA}
185
186            elif case == 'MultipleBlacklistedFiles':
187                policy_value = ','.join(MULTIPLE_BLACKLISTED_FILES_DATA)
188                policies_json = {
189                        'URLBlacklist': MULTIPLE_BLACKLISTED_FILES_DATA
190                        }
191
192        # Run test using the values configured for the test case.
193        self._test_URLBlacklist(policy_value, policies_json)
194
195    def run_once(self):
196            self.run_once_impl(self._run_test_case)
197