| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 The Chromium 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 | |
| 5 """Base class for tests that need to update the policies enforced by Chrome. | |
| 6 | |
| 7 Subclasses can call SetUserPolicy (ChromeOS, Linux, Windows) and | |
| 8 SetDevicePolicy (ChromeOS only) with a dictionary of the policies to install. | |
| 9 | |
| 10 The current implementation depends on the platform. The implementations might | |
| 11 change in the future, but tests relying on the above calls will keep working. | |
| 12 """ | |
| 13 | |
| 14 # On ChromeOS, a mock DMServer is started and enterprise enrollment faked | |
| 15 # against it. The mock DMServer then serves user and device policy to Chrome. | |
| 16 # | |
| 17 # For this setup to work, the DNS, GAIA and TPM (if present) are mocked as well: | |
| 18 # | |
| 19 # * The mock DNS resolves all addresses to 127.0.0.1. This allows the mock GAIA | |
| 20 # to handle all login attempts. It also eliminates the impact of flaky network | |
| 21 # connections on tests. Beware though that no cloud services can be accessed | |
| 22 # due to this DNS redirect. | |
| 23 # | |
| 24 # * The mock GAIA permits login with arbitrary credentials and accepts any OAuth | |
| 25 # tokens sent to it for verification as valid. | |
| 26 # | |
| 27 # * When running on a real device, its TPM is disabled. If the TPM were enabled, | |
| 28 # enrollment could not be undone without a reboot. Disabling the TPM makes | |
| 29 # cryptohomed behave as if no TPM was present, allowing enrollment to be | |
| 30 # undone by removing the install attributes. | |
| 31 # | |
| 32 # To disable the TPM, 0 must be written to /sys/class/misc/tpm0/device/enabled. | |
| 33 # Since this file is not writeable, a tpmfs is mounted that shadows the file | |
| 34 # with a writeable copy. | |
| 35 | |
| 36 import json | |
| 37 import logging | |
| 38 import os | |
| 39 import subprocess | |
| 40 | |
| 41 import pyauto | |
| 42 | |
| 43 if pyauto.PyUITest.IsChromeOS(): | |
| 44 import sys | |
| 45 import warnings | |
| 46 | |
| 47 import pyauto_paths | |
| 48 | |
| 49 # Ignore deprecation warnings, they make our output more cluttered. | |
| 50 warnings.filterwarnings('ignore', category=DeprecationWarning) | |
| 51 | |
| 52 # Find the path to the pyproto and add it to sys.path. | |
| 53 # Prepend it so that google.protobuf is loaded from here. | |
| 54 for path in pyauto_paths.GetBuildDirs(): | |
| 55 p = os.path.join(path, 'pyproto') | |
| 56 if os.path.isdir(p): | |
| 57 sys.path = [p, os.path.join(p, 'chrome', 'browser', 'policy', | |
| 58 'proto')] + sys.path | |
| 59 break | |
| 60 sys.path.append('/usr/local') # to import autotest libs. | |
| 61 | |
| 62 import dbus | |
| 63 import device_management_backend_pb2 as dm | |
| 64 import pyauto_utils | |
| 65 import string | |
| 66 import tempfile | |
| 67 import urllib | |
| 68 import urllib2 | |
| 69 import uuid | |
| 70 from autotest.cros import auth_server | |
| 71 from autotest.cros import constants | |
| 72 from autotest.cros import cros_ui | |
| 73 from autotest.cros import dns_server | |
| 74 elif pyauto.PyUITest.IsWin(): | |
| 75 import _winreg as winreg | |
| 76 elif pyauto.PyUITest.IsMac(): | |
| 77 import getpass | |
| 78 import plistlib | |
| 79 | |
| 80 # ASN.1 object identifier for PKCS#1/RSA. | |
| 81 PKCS1_RSA_OID = '\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01' | |
| 82 | |
| 83 TPM_SYSFS_PATH = '/sys/class/misc/tpm0' | |
| 84 TPM_SYSFS_ENABLED_FILE = os.path.join(TPM_SYSFS_PATH, 'device/enabled') | |
| 85 | |
| 86 | |
| 87 class PolicyTestBase(pyauto.PyUITest): | |
| 88 """A base class for tests that need to set up and modify policies. | |
| 89 | |
| 90 Subclasses can use the methods SetUserPolicy (ChromeOS, Linux, Windows) and | |
| 91 SetDevicePolicy (ChromeOS only) to set the policies seen by Chrome. | |
| 92 """ | |
| 93 | |
| 94 if pyauto.PyUITest.IsChromeOS(): | |
| 95 # TODO(bartfab): Extend the C++ wrapper that starts the mock DMServer so | |
| 96 # that an owner can be passed in. Without this, the server will assume that | |
| 97 # the owner is user@example.com and for consistency, so must we. | |
| 98 owner = 'user@example.com' | |
| 99 # Subclasses may override these credentials to fake enrollment into another | |
| 100 # mode or use different device and machine IDs. | |
| 101 mode = 'enterprise' | |
| 102 device_id = string.upper(str(uuid.uuid4())) | |
| 103 machine_id = 'CROSTEST' | |
| 104 | |
| 105 _auth_server = None | |
| 106 _dns_server = None | |
| 107 | |
| 108 def ShouldAutoLogin(self): | |
| 109 return False | |
| 110 | |
| 111 @staticmethod | |
| 112 def _Call(command, check=False): | |
| 113 """Invokes a subprocess and optionally asserts the return value is zero.""" | |
| 114 with open(os.devnull, 'w') as devnull: | |
| 115 if check: | |
| 116 return subprocess.check_call(command.split(' '), stdout=devnull) | |
| 117 else: | |
| 118 return subprocess.call(command.split(' '), stdout=devnull) | |
| 119 | |
| 120 def _WriteFile(self, path, content): | |
| 121 """Writes content to path, creating any intermediary directories.""" | |
| 122 if not os.path.exists(os.path.dirname(path)): | |
| 123 os.makedirs(os.path.dirname(path)) | |
| 124 f = open(path, 'w') | |
| 125 f.write(content) | |
| 126 f.close() | |
| 127 | |
| 128 def _GetTestServerPoliciesFilePath(self): | |
| 129 """Returns the path of the cloud policy configuration file.""" | |
| 130 assert self.IsChromeOS() | |
| 131 return os.path.join(self._temp_data_dir, 'device_management') | |
| 132 | |
| 133 def _GetHttpURLForDeviceManagement(self): | |
| 134 """Returns the URL at which the TestServer is serving user policy.""" | |
| 135 assert self.IsChromeOS() | |
| 136 return self._http_server.GetURL('device_management').spec() | |
| 137 | |
| 138 def _RemoveIfExists(self, filename): | |
| 139 """Removes a file if it exists.""" | |
| 140 if os.path.exists(filename): | |
| 141 os.remove(filename) | |
| 142 | |
| 143 def _StartSessionManagerAndChrome(self): | |
| 144 """Starts the session manager and Chrome. | |
| 145 | |
| 146 Requires that the session manager be stopped already. | |
| 147 """ | |
| 148 # Ugly hack: session manager will not spawn Chrome if this file exists. That | |
| 149 # is usually a good thing (to keep the automation channel open), but in this | |
| 150 # case we really want to restart chrome. PyUITest.setUp() will be called | |
| 151 # after session manager and chrome have restarted, and will setup the | |
| 152 # automation channel. | |
| 153 restore_magic_file = False | |
| 154 if os.path.exists(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE): | |
| 155 logging.debug('DISABLE_BROWSER_RESTART_MAGIC_FILE found. ' | |
| 156 'Removing temporarily for the next restart.') | |
| 157 restore_magic_file = True | |
| 158 os.remove(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE) | |
| 159 assert not os.path.exists(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE) | |
| 160 | |
| 161 logging.debug('Starting session manager again') | |
| 162 cros_ui.start() | |
| 163 | |
| 164 # cros_ui.start() waits for the login prompt to be visible, so Chrome has | |
| 165 # already started once it returns. | |
| 166 if restore_magic_file: | |
| 167 open(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE, 'w').close() | |
| 168 assert os.path.exists(constants.DISABLE_BROWSER_RESTART_MAGIC_FILE) | |
| 169 | |
| 170 def _WritePolicyOnChromeOS(self): | |
| 171 """Updates the mock DMServer's input file with current policy.""" | |
| 172 assert self.IsChromeOS() | |
| 173 policy_dict = { | |
| 174 'google/chromeos/device': self._device_policy, | |
| 175 'google/chromeos/user': { | |
| 176 'mandatory': self._user_policy, | |
| 177 'recommended': {}, | |
| 178 }, | |
| 179 'managed_users': ['*'], | |
| 180 } | |
| 181 self._WriteFile(self._GetTestServerPoliciesFilePath(), | |
| 182 json.dumps(policy_dict, sort_keys=True, indent=2) + '\n') | |
| 183 | |
| 184 @staticmethod | |
| 185 def _IsCryptohomedReadyOnChromeOS(): | |
| 186 """Checks whether cryptohomed is running and ready to accept DBus calls.""" | |
| 187 assert pyauto.PyUITest.IsChromeOS() | |
| 188 try: | |
| 189 bus = dbus.SystemBus() | |
| 190 proxy = bus.get_object('org.chromium.Cryptohome', | |
| 191 '/org/chromium/Cryptohome') | |
| 192 dbus.Interface(proxy, 'org.chromium.CryptohomeInterface') | |
| 193 except dbus.DBusException: | |
| 194 return False | |
| 195 return True | |
| 196 | |
| 197 def _ClearInstallAttributesOnChromeOS(self): | |
| 198 """Resets the install attributes.""" | |
| 199 assert self.IsChromeOS() | |
| 200 self._RemoveIfExists('/home/.shadow/install_attributes.pb') | |
| 201 self._Call('restart cryptohomed', check=True) | |
| 202 assert self.WaitUntil(self._IsCryptohomedReadyOnChromeOS) | |
| 203 | |
| 204 def _DMPostRequest(self, request_type, request, headers): | |
| 205 """Posts a request to the mock DMServer.""" | |
| 206 assert self.IsChromeOS() | |
| 207 url = self._GetHttpURLForDeviceManagement() | |
| 208 url += '?' + urllib.urlencode({ | |
| 209 'deviceid': self.device_id, | |
| 210 'oauth_token': 'dummy_oauth_token_that_is_not_checked_anyway', | |
| 211 'request': request_type, | |
| 212 'devicetype': 2, | |
| 213 'apptype': 'Chrome', | |
| 214 'agent': 'Chrome', | |
| 215 }) | |
| 216 response = dm.DeviceManagementResponse() | |
| 217 response.ParseFromString(urllib2.urlopen(urllib2.Request( | |
| 218 url, request.SerializeToString(), headers)).read()) | |
| 219 return response | |
| 220 | |
| 221 def _DMRegisterDevice(self): | |
| 222 """Registers with the mock DMServer and returns the DMToken.""" | |
| 223 assert self.IsChromeOS() | |
| 224 dm_request = dm.DeviceManagementRequest() | |
| 225 request = dm_request.register_request | |
| 226 request.type = dm.DeviceRegisterRequest.DEVICE | |
| 227 request.machine_id = self.machine_id | |
| 228 dm_response = self._DMPostRequest('register', dm_request, {}) | |
| 229 return dm_response.register_response.device_management_token | |
| 230 | |
| 231 def _DMFetchPolicy(self, dm_token): | |
| 232 """Fetches device policy from the mock DMServer.""" | |
| 233 assert self.IsChromeOS() | |
| 234 dm_request = dm.DeviceManagementRequest() | |
| 235 policy_request = dm_request.policy_request | |
| 236 request = policy_request.request.add() | |
| 237 request.policy_type = 'google/chromeos/device' | |
| 238 request.signature_type = dm.PolicyFetchRequest.SHA1_RSA | |
| 239 headers = {'Authorization': 'GoogleDMToken token=' + dm_token} | |
| 240 dm_response = self._DMPostRequest('policy', dm_request, headers) | |
| 241 response = dm_response.policy_response.response[0] | |
| 242 assert response.policy_data | |
| 243 assert response.policy_data_signature | |
| 244 assert response.new_public_key | |
| 245 return response | |
| 246 | |
| 247 def ExtraChromeFlags(self): | |
| 248 """Sets up Chrome to use cloud policies on ChromeOS.""" | |
| 249 flags = pyauto.PyUITest.ExtraChromeFlags(self) | |
| 250 if self.IsChromeOS(): | |
| 251 while '--skip-oauth-login' in flags: | |
| 252 flags.remove('--skip-oauth-login') | |
| 253 url = self._GetHttpURLForDeviceManagement() | |
| 254 flags.append('--device-management-url=' + url) | |
| 255 flags.append('--disable-sync') | |
| 256 return flags | |
| 257 | |
| 258 def _SetUpWithSessionManagerStopped(self): | |
| 259 """Sets up the test environment after stopping the session manager.""" | |
| 260 assert self.IsChromeOS() | |
| 261 logging.debug('Stopping session manager') | |
| 262 cros_ui.stop(allow_fail=True) | |
| 263 | |
| 264 # Start mock GAIA server. | |
| 265 self._auth_server = auth_server.GoogleAuthServer() | |
| 266 self._auth_server.run() | |
| 267 | |
| 268 # Disable TPM if present. | |
| 269 if os.path.exists(TPM_SYSFS_PATH): | |
| 270 self._Call('mount -t tmpfs -o size=1k tmpfs %s' | |
| 271 % os.path.realpath(TPM_SYSFS_PATH), check=True) | |
| 272 self._WriteFile(TPM_SYSFS_ENABLED_FILE, '0') | |
| 273 | |
| 274 # Clear install attributes and restart cryptohomed to pick up the change. | |
| 275 self._ClearInstallAttributesOnChromeOS() | |
| 276 | |
| 277 # Set install attributes to mock enterprise enrollment. | |
| 278 bus = dbus.SystemBus() | |
| 279 proxy = bus.get_object('org.chromium.Cryptohome', | |
| 280 '/org/chromium/Cryptohome') | |
| 281 install_attributes = { | |
| 282 'enterprise.device_id': self.device_id, | |
| 283 'enterprise.domain': string.split(self.owner, '@')[-1], | |
| 284 'enterprise.mode': self.mode, | |
| 285 'enterprise.owned': 'true', | |
| 286 'enterprise.user': self.owner | |
| 287 } | |
| 288 interface = dbus.Interface(proxy, 'org.chromium.CryptohomeInterface') | |
| 289 for name, value in install_attributes.iteritems(): | |
| 290 interface.InstallAttributesSet(name, '%s\0' % value) | |
| 291 interface.InstallAttributesFinalize() | |
| 292 | |
| 293 # Start mock DNS server that redirects all traffic to 127.0.0.1. | |
| 294 self._dns_server = dns_server.LocalDns() | |
| 295 self._dns_server.run() | |
| 296 | |
| 297 # Start mock DMServer. | |
| 298 source_dir = os.path.normpath(pyauto_paths.GetSourceDir()) | |
| 299 self._temp_data_dir = tempfile.mkdtemp(dir=source_dir) | |
| 300 logging.debug('TestServer input path: %s' % self._temp_data_dir) | |
| 301 relative_temp_data_dir = os.path.basename(self._temp_data_dir) | |
| 302 self._http_server = self.StartHTTPServer(relative_temp_data_dir) | |
| 303 | |
| 304 # Initialize the policy served. | |
| 305 self._device_policy = {} | |
| 306 self._user_policy = {} | |
| 307 self._WritePolicyOnChromeOS() | |
| 308 | |
| 309 # Register with mock DMServer and retrieve initial device policy blob. | |
| 310 dm_token = self._DMRegisterDevice() | |
| 311 policy = self._DMFetchPolicy(dm_token) | |
| 312 | |
| 313 # Write the initial device policy blob. | |
| 314 self._WriteFile(constants.OWNER_KEY_FILE, policy.new_public_key) | |
| 315 self._WriteFile(constants.SIGNED_POLICY_FILE, policy.SerializeToString()) | |
| 316 | |
| 317 # Remove any existing vaults. | |
| 318 self.RemoveAllCryptohomeVaultsOnChromeOS() | |
| 319 | |
| 320 # Restart session manager and Chrome. | |
| 321 self._StartSessionManagerAndChrome() | |
| 322 | |
| 323 def _tearDownWithSessionManagerStopped(self): | |
| 324 """Resets the test environment after stopping the session manager.""" | |
| 325 assert self.IsChromeOS() | |
| 326 logging.debug('Stopping session manager') | |
| 327 cros_ui.stop(allow_fail=True) | |
| 328 | |
| 329 # Stop mock GAIA server. | |
| 330 if self._auth_server: | |
| 331 self._auth_server.stop() | |
| 332 | |
| 333 # Reenable TPM if present. | |
| 334 if os.path.exists(TPM_SYSFS_PATH): | |
| 335 self._Call('umount %s' % os.path.realpath(TPM_SYSFS_PATH)) | |
| 336 | |
| 337 # Clear install attributes and restart cryptohomed to pick up the change. | |
| 338 self._ClearInstallAttributesOnChromeOS() | |
| 339 | |
| 340 # Stop mock DNS server. | |
| 341 if self._dns_server: | |
| 342 self._dns_server.stop() | |
| 343 | |
| 344 # Stop mock DMServer. | |
| 345 self.StopHTTPServer(self._http_server) | |
| 346 | |
| 347 # Clear the policy served. | |
| 348 pyauto_utils.RemovePath(self._temp_data_dir) | |
| 349 | |
| 350 # Remove the device policy blob. | |
| 351 self._RemoveIfExists(constants.OWNER_KEY_FILE) | |
| 352 self._RemoveIfExists(constants.SIGNED_POLICY_FILE) | |
| 353 | |
| 354 # Remove any existing vaults. | |
| 355 self.RemoveAllCryptohomeVaultsOnChromeOS() | |
| 356 | |
| 357 # Restart session manager and Chrome. | |
| 358 self._StartSessionManagerAndChrome() | |
| 359 | |
| 360 def setUp(self): | |
| 361 """Sets up the platform for policy testing. | |
| 362 | |
| 363 On ChromeOS, part of the setup involves restarting the session manager to | |
| 364 inject an initial device policy blob. | |
| 365 """ | |
| 366 if self.IsChromeOS(): | |
| 367 # Perform the remainder of the setup with the device manager stopped. | |
| 368 try: | |
| 369 self.WaitForSessionManagerRestart( | |
| 370 self._SetUpWithSessionManagerStopped) | |
| 371 except: | |
| 372 # Destroy the non re-entrant services. | |
| 373 if self._auth_server: | |
| 374 self._auth_server.stop() | |
| 375 if self._dns_server: | |
| 376 self._dns_server.stop() | |
| 377 raise | |
| 378 | |
| 379 pyauto.PyUITest.setUp(self) | |
| 380 self._branding = self.GetBrowserInfo()['properties']['branding'] | |
| 381 | |
| 382 def tearDown(self): | |
| 383 """Cleans up the policies and related files created in tests.""" | |
| 384 if self.IsChromeOS(): | |
| 385 # Perform the cleanup with the device manager stopped. | |
| 386 self.WaitForSessionManagerRestart(self._tearDownWithSessionManagerStopped) | |
| 387 else: | |
| 388 # On other platforms, there is only user policy to clear. | |
| 389 self.SetUserPolicy(refresh=False) | |
| 390 | |
| 391 pyauto.PyUITest.tearDown(self) | |
| 392 | |
| 393 def LoginWithTestAccount(self, account='prod_enterprise_test_user'): | |
| 394 """Convenience method for logging in with one of the test accounts.""" | |
| 395 assert self.IsChromeOS() | |
| 396 credentials = self.GetPrivateInfo()[account] | |
| 397 self.Login(credentials['username'], credentials['password']) | |
| 398 assert self.GetLoginInfo()['is_logged_in'] | |
| 399 | |
| 400 def _GetCurrentLoginScreenId(self): | |
| 401 return self.ExecuteJavascriptInOOBEWebUI( | |
| 402 """window.domAutomationController.send( | |
| 403 String(cr.ui.Oobe.getInstance().currentScreen.id)); | |
| 404 """) | |
| 405 | |
| 406 def _WaitForLoginScreenId(self, id): | |
| 407 self.assertTrue( | |
| 408 self.WaitUntil(function=self._GetCurrentLoginScreenId, | |
| 409 expect_retval=id), | |
| 410 msg='Expected login screen "%s" to be visible.' % id) | |
| 411 | |
| 412 def _CheckLoginFormLoading(self): | |
| 413 return self.ExecuteJavascriptInOOBEWebUI( | |
| 414 """window.domAutomationController.send( | |
| 415 cr.ui.Oobe.getInstance().currentScreen.loading); | |
| 416 """) | |
| 417 | |
| 418 def PrepareToWaitForLoginFormReload(self): | |
| 419 self.assertEqual('gaia-signin', | |
| 420 self._GetCurrentLoginScreenId(), | |
| 421 msg='Expected the login form to be visible.') | |
| 422 self.assertTrue( | |
| 423 self.WaitUntil(function=self._CheckLoginFormLoading, | |
| 424 expect_retval=False), | |
| 425 msg='Expected the login form to finish loading.') | |
| 426 # Set up a sentinel variable that is false now and will toggle to true when | |
| 427 # the login form starts reloading. | |
| 428 self.ExecuteJavascriptInOOBEWebUI( | |
| 429 """var screen = cr.ui.Oobe.getInstance().currentScreen; | |
| 430 if (!('reload_started' in screen)) { | |
| 431 screen.orig_loadAuthExtension_ = screen.loadAuthExtension_; | |
| 432 screen.loadAuthExtension_ = function(data) { | |
| 433 this.orig_loadAuthExtension_(data); | |
| 434 if (this.loading) | |
| 435 this.reload_started = true; | |
| 436 } | |
| 437 } | |
| 438 screen.reload_started = false; | |
| 439 window.domAutomationController.send(true);""") | |
| 440 | |
| 441 def _CheckLoginFormReloaded(self): | |
| 442 return self.ExecuteJavascriptInOOBEWebUI( | |
| 443 """window.domAutomationController.send( | |
| 444 cr.ui.Oobe.getInstance().currentScreen.reload_started && | |
| 445 !cr.ui.Oobe.getInstance().currentScreen.loading); | |
| 446 """) | |
| 447 | |
| 448 def WaitForLoginFormReload(self): | |
| 449 self.assertEqual('gaia-signin', | |
| 450 self._GetCurrentLoginScreenId(), | |
| 451 msg='Expected the login form to be visible.') | |
| 452 self.assertTrue( | |
| 453 self.WaitUntil(function=self._CheckLoginFormReloaded), | |
| 454 msg='Expected the login form to finish reloading.') | |
| 455 | |
| 456 def _SetUserPolicyChromeOS(self, user_policy=None): | |
| 457 """Writes the given user policy to the mock DMServer's input file.""" | |
| 458 self._user_policy = user_policy or {} | |
| 459 self._WritePolicyOnChromeOS() | |
| 460 | |
| 461 def _SetUserPolicyWin(self, user_policy=None): | |
| 462 """Writes the given user policy to the Windows registry.""" | |
| 463 def SetValueEx(key, sub_key, value): | |
| 464 if isinstance(value, int): | |
| 465 winreg.SetValueEx(key, sub_key, 0, winreg.REG_DWORD, int(value)) | |
| 466 elif isinstance(value, basestring): | |
| 467 winreg.SetValueEx(key, sub_key, 0, winreg.REG_SZ, value.encode('ascii')) | |
| 468 elif isinstance(value, list): | |
| 469 k = winreg.CreateKey(key, sub_key) | |
| 470 for index, v in list(enumerate(value)): | |
| 471 SetValueEx(k, str(index + 1), v) | |
| 472 winreg.CloseKey(k) | |
| 473 else: | |
| 474 raise TypeError('Unsupported data type: "%s"' % value) | |
| 475 | |
| 476 assert self.IsWin() | |
| 477 if self._branding == 'Google Chrome': | |
| 478 reg_base = r'SOFTWARE\Policies\Google\Chrome' | |
| 479 else: | |
| 480 reg_base = r'SOFTWARE\Policies\Chromium' | |
| 481 | |
| 482 if subprocess.call( | |
| 483 r'reg query HKEY_LOCAL_MACHINE\%s' % reg_base) == 0: | |
| 484 logging.debug(r'Removing %s' % reg_base) | |
| 485 subprocess.call(r'reg delete HKLM\%s /f' % reg_base) | |
| 486 | |
| 487 if user_policy is not None: | |
| 488 root_key = winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, reg_base) | |
| 489 for k, v in user_policy.iteritems(): | |
| 490 SetValueEx(root_key, k, v) | |
| 491 winreg.CloseKey(root_key) | |
| 492 | |
| 493 def _SetUserPolicyLinux(self, user_policy=None): | |
| 494 """Writes the given user policy to the JSON policy file read by Chrome.""" | |
| 495 assert self.IsLinux() | |
| 496 sudo_cmd_file = os.path.join(os.path.dirname(__file__), | |
| 497 'policy_posix_util.py') | |
| 498 | |
| 499 if self._branding == 'Google Chrome': | |
| 500 policies_location_base = '/etc/opt/chrome' | |
| 501 else: | |
| 502 policies_location_base = '/etc/chromium' | |
| 503 | |
| 504 if os.path.exists(policies_location_base): | |
| 505 logging.debug('Removing directory %s' % policies_location_base) | |
| 506 subprocess.call(['suid-python', sudo_cmd_file, | |
| 507 'remove_dir', policies_location_base]) | |
| 508 | |
| 509 if user_policy is not None: | |
| 510 self._WriteFile('/tmp/chrome.json', | |
| 511 json.dumps(user_policy, sort_keys=True, indent=2) + '\n') | |
| 512 | |
| 513 policies_location = '%s/policies/managed' % policies_location_base | |
| 514 subprocess.call(['suid-python', sudo_cmd_file, | |
| 515 'setup_dir', policies_location]) | |
| 516 subprocess.call(['suid-python', sudo_cmd_file, | |
| 517 'perm_dir', policies_location]) | |
| 518 # Copy chrome.json file to the managed directory | |
| 519 subprocess.call(['suid-python', sudo_cmd_file, | |
| 520 'copy', '/tmp/chrome.json', policies_location]) | |
| 521 os.remove('/tmp/chrome.json') | |
| 522 | |
| 523 def _SetUserPolicyMac(self, user_policy=None): | |
| 524 """Writes the given user policy to the plist policy file read by Chrome.""" | |
| 525 assert self.IsMac() | |
| 526 sudo_cmd_file = os.path.join(os.path.dirname(__file__), | |
| 527 'policy_posix_util.py') | |
| 528 | |
| 529 if self._branding == 'Google Chrome': | |
| 530 policies_file_base = 'com.google.Chrome.plist' | |
| 531 else: | |
| 532 policies_file_base = 'org.chromium.Chromium.plist' | |
| 533 | |
| 534 policies_location = os.path.join('/Library', 'Managed Preferences', | |
| 535 getpass.getuser()) | |
| 536 | |
| 537 if os.path.exists(policies_location): | |
| 538 logging.debug('Removing directory %s' % policies_location) | |
| 539 subprocess.call(['suid-python', sudo_cmd_file, | |
| 540 'remove_dir', policies_location]) | |
| 541 | |
| 542 if user_policy is not None: | |
| 543 policies_tmp_file = os.path.join('/tmp', policies_file_base) | |
| 544 plistlib.writePlist(user_policy, policies_tmp_file) | |
| 545 subprocess.call(['suid-python', sudo_cmd_file, | |
| 546 'setup_dir', policies_location]) | |
| 547 # Copy policy file to the managed directory | |
| 548 subprocess.call(['suid-python', sudo_cmd_file, | |
| 549 'copy', policies_tmp_file, policies_location]) | |
| 550 os.remove(policies_tmp_file) | |
| 551 | |
| 552 def SetUserPolicy(self, user_policy=None, refresh=True): | |
| 553 """Sets the user policy provided as a dict. | |
| 554 | |
| 555 Args: | |
| 556 user_policy: The user policy to set. None clears it. | |
| 557 refresh: If True, Chrome will refresh and apply the new policy. | |
| 558 Requires Chrome to be alive for it. | |
| 559 """ | |
| 560 if self.IsChromeOS(): | |
| 561 self._SetUserPolicyChromeOS(user_policy=user_policy) | |
| 562 elif self.IsWin(): | |
| 563 self._SetUserPolicyWin(user_policy=user_policy) | |
| 564 elif self.IsLinux(): | |
| 565 self._SetUserPolicyLinux(user_policy=user_policy) | |
| 566 elif self.IsMac(): | |
| 567 self._SetUserPolicyMac(user_policy=user_policy) | |
| 568 else: | |
| 569 raise NotImplementedError('Not available on this platform.') | |
| 570 | |
| 571 if refresh: | |
| 572 self.RefreshPolicies() | |
| 573 | |
| 574 def SetDevicePolicy(self, device_policy=None, refresh=True): | |
| 575 """Sets the device policy provided as a dict. | |
| 576 | |
| 577 Args: | |
| 578 device_policy: The device policy to set. None clears it. | |
| 579 refresh: If True, Chrome will refresh and apply the new policy. | |
| 580 Requires Chrome to be alive for it. | |
| 581 """ | |
| 582 assert self.IsChromeOS() | |
| 583 self._device_policy = device_policy or {} | |
| 584 self._WritePolicyOnChromeOS() | |
| 585 if refresh: | |
| 586 self.RefreshPolicies() | |
| OLD | NEW |