Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """A bare-bones test server for testing cloud policy support. | 5 """A bare-bones test server for testing cloud policy support. |
| 6 | 6 |
| 7 This implements a simple cloud policy test server that can be used to test | 7 This implements a simple cloud policy test server that can be used to test |
| 8 chrome's device management service client. The policy information is read from | 8 chrome's device management service client. The policy information is read from |
| 9 the file named device_management in the server's data directory. It contains | 9 the file named device_management in the server's data directory. It contains |
| 10 enforced and recommended policies for the device and user scope, and a list | 10 enforced and recommended policies for the device and user scope, and a list |
| 11 of managed users. | 11 of managed users. |
| 12 | 12 |
| 13 The format of the file is JSON. The root dictionary contains a list under the | 13 The format of the file is JSON. The root dictionary contains a list under the |
| 14 key "managed_users". It contains auth tokens for which the server will claim | 14 key "managed_users". It contains auth tokens for which the server will claim |
| 15 that the user is managed. The token string "*" indicates that all users are | 15 that the user is managed. The token string "*" indicates that all users are |
| 16 claimed to be managed. Other keys in the root dictionary identify request | 16 claimed to be managed. Other keys in the root dictionary identify request |
| 17 scopes. The user-request scope is described by a dictionary that holds two | 17 scopes. The user-request scope is described by a dictionary that holds two |
| 18 sub-dictionaries: "mandatory" and "recommended". Both these hold the policy | 18 sub-dictionaries: "mandatory" and "recommended". Both these hold the policy |
| 19 definitions as key/value stores, their format is identical to what the Linux | 19 definitions as key/value stores, their format is identical to what the Linux |
| 20 implementation reads from /etc. | 20 implementation reads from /etc. |
| 21 The device-scope holds the policy-definition directly as key/value stores in the | 21 The device-scope holds the policy-definition directly as key/value stores in the |
| 22 protobuf-format. | 22 protobuf-format. |
| 23 | 23 |
| 24 Example: | 24 Example: |
| 25 | 25 |
| 26 { | 26 { |
| 27 "google/chromeos/device" : { | 27 "google/chromeos/device" : { |
| 28 "guest_mode_enabled" : false | 28 "guest_mode_enabled" : false |
| 29 }, | 29 }, |
| 30 "google/chromeos/user" : { | 30 "google/chromeos/user" : { |
| 31 "mandatory" : { | 31 "mandatory" : { |
| 32 "HomepageLocation" : "http://www.chromium.org", | 32 "HomepageLocation" : "http://www.chromium.org", |
| 33 "IncognitoEnabled" : false | 33 "IncognitoEnabled" : false |
| 34 }, | 34 }, |
| 35 "recommended" : { | 35 "recommended" : { |
| 36 "JavascriptEnabled": false | 36 "JavascriptEnabled": false |
| 37 } | 37 } |
| 38 }, | 38 }, |
| 39 "google/chromeos/publicaccount/user@example.com" : { | |
| 40 "mandatory" : { | |
| 41 "HomepageLocation" : "http://www.chromium.org" | |
| 42 }, | |
| 43 "recommended" : { | |
| 44 } | |
| 45 } | |
|
Joao da Silva
2012/12/04 16:33:31
nitty nit: comma
Mattias Nissler (ping if slow)
2012/12/04 16:58:36
Done.
| |
| 39 "managed_users" : [ | 46 "managed_users" : [ |
| 40 "secret123456" | 47 "secret123456" |
| 41 ] | 48 ] |
| 42 } | 49 } |
| 43 | 50 |
| 44 """ | 51 """ |
| 45 | 52 |
| 46 import cgi | 53 import cgi |
| 47 import hashlib | 54 import hashlib |
| 48 import logging | 55 import logging |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 254 and constructs the response. | 261 and constructs the response. |
| 255 | 262 |
| 256 Args: | 263 Args: |
| 257 msg: The DevicePolicyRequest message received from the client. | 264 msg: The DevicePolicyRequest message received from the client. |
| 258 | 265 |
| 259 Returns: | 266 Returns: |
| 260 A tuple of HTTP status code and response data to send to the client. | 267 A tuple of HTTP status code and response data to send to the client. |
| 261 """ | 268 """ |
| 262 for request in msg.request: | 269 for request in msg.request: |
| 263 if (request.policy_type in | 270 if (request.policy_type in |
| 264 ('google/chromeos/user', 'google/chromeos/device')): | 271 ('google/chromeos/user', |
| 272 'google/chromeos/device', | |
| 273 'google/chromeos/publicaccount')): | |
| 265 if request_type != 'policy': | 274 if request_type != 'policy': |
| 266 return (400, 'Invalid request type') | 275 return (400, 'Invalid request type') |
| 267 else: | 276 else: |
| 268 return self.ProcessCloudPolicy(request) | 277 return self.ProcessCloudPolicy(request) |
| 269 else: | 278 else: |
| 270 return (400, 'Invalid policy_type') | 279 return (400, 'Invalid policy_type') |
| 271 | 280 |
| 272 def ProcessAutoEnrollment(self, msg): | 281 def ProcessAutoEnrollment(self, msg): |
| 273 """Handles an auto-enrollment check request. | 282 """Handles an auto-enrollment check request. |
| 274 | 283 |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 Args: | 393 Args: |
| 385 settings: The destination: a CloudPolicySettings protobuf. | 394 settings: The destination: a CloudPolicySettings protobuf. |
| 386 policies: The source: a dictionary containing policies under keys | 395 policies: The source: a dictionary containing policies under keys |
| 387 'recommended' and 'mandatory'. | 396 'recommended' and 'mandatory'. |
| 388 ''' | 397 ''' |
| 389 for field in settings.DESCRIPTOR.fields: | 398 for field in settings.DESCRIPTOR.fields: |
| 390 # |field| is the entry for a specific policy in the top-level | 399 # |field| is the entry for a specific policy in the top-level |
| 391 # CloudPolicySettings proto. | 400 # CloudPolicySettings proto. |
| 392 | 401 |
| 393 # Look for this policy's value in the mandatory or recommended dicts. | 402 # Look for this policy's value in the mandatory or recommended dicts. |
| 394 if field.name in policies['mandatory']: | 403 if field.name in policies.get('mandatory', {}): |
| 395 mode = cp.PolicyOptions.MANDATORY | 404 mode = cp.PolicyOptions.MANDATORY |
| 396 value = policies['mandatory'][field.name] | 405 value = policies['mandatory'][field.name] |
| 397 elif field.name in policies['recommended']: | 406 elif field.name in policies.get('recommended', {}): |
| 398 mode = cp.PolicyOptions.RECOMMENDED | 407 mode = cp.PolicyOptions.RECOMMENDED |
| 399 value = policies['recommended'][field.name] | 408 value = policies['recommended'][field.name] |
| 400 else: | 409 else: |
| 401 continue | 410 continue |
| 402 | 411 |
| 403 # Create protobuf message for this policy. | 412 # Create protobuf message for this policy. |
| 404 policy_message = eval('cp.' + field.message_type.name + '()') | 413 policy_message = eval('cp.' + field.message_type.name + '()') |
| 405 policy_message.policy_options.mode = mode | 414 policy_message.policy_options.mode = mode |
| 406 field_descriptor = policy_message.DESCRIPTOR.fields_by_name['value'] | 415 field_descriptor = policy_message.DESCRIPTOR.fields_by_name['value'] |
| 407 self.SetProtobufMessageField(policy_message, field_descriptor, value) | 416 self.SetProtobufMessageField(policy_message, field_descriptor, value) |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 421 """ | 430 """ |
| 422 | 431 |
| 423 token_info, error = self.CheckToken() | 432 token_info, error = self.CheckToken() |
| 424 if not token_info: | 433 if not token_info: |
| 425 return error | 434 return error |
| 426 | 435 |
| 427 if msg.machine_id: | 436 if msg.machine_id: |
| 428 self._server.UpdateMachineId(token_info['device_token'], msg.machine_id) | 437 self._server.UpdateMachineId(token_info['device_token'], msg.machine_id) |
| 429 | 438 |
| 430 # Response is only given if the scope is specified in the config file. | 439 # Response is only given if the scope is specified in the config file. |
| 431 # Normally 'google/chromeos/device' and 'google/chromeos/user' should be | 440 # Normally 'google/chromeos/device', 'google/chromeos/user' and |
| 432 # accepted. | 441 # 'google/chromeos/publicaccount' should be accepted. |
| 433 policy = self._server.GetPolicies() | 442 policy = self._server.GetPolicies() |
| 434 policy_value = '' | 443 policy_value = '' |
| 435 if (msg.policy_type in token_info['allowed_policy_types'] and | 444 policy_key = msg.policy_type |
| 436 msg.policy_type in policy): | 445 if msg.settings_entity_id: |
| 446 policy_key += '/' + msg.settings_entity_id | |
| 447 if msg.policy_type in token_info['allowed_policy_types']: | |
| 437 if msg.policy_type == 'google/chromeos/user': | 448 if msg.policy_type == 'google/chromeos/user': |
| 438 settings = cp.CloudPolicySettings() | 449 settings = cp.CloudPolicySettings() |
| 439 self.GatherUserPolicySettings(settings, | 450 self.GatherUserPolicySettings(settings, policy.get(policy_key, {})) |
| 440 policy[msg.policy_type]) | |
| 441 policy_value = settings.SerializeToString() | |
| 442 elif msg.policy_type == 'google/chromeos/device': | 451 elif msg.policy_type == 'google/chromeos/device': |
| 443 settings = dp.ChromeDeviceSettingsProto() | 452 settings = dp.ChromeDeviceSettingsProto() |
| 444 self.GatherDevicePolicySettings(settings, | 453 self.GatherDevicePolicySettings(settings, policy.get(policy_key, {})) |
| 445 policy[msg.policy_type]) | 454 elif msg.policy_type == 'google/chromeos/publicaccount': |
| 446 policy_value = settings.SerializeToString() | 455 settings = cp.CloudPolicySettings() |
| 456 self.GatherUserPolicySettings(settings, policy.get(policy_key, {})) | |
| 447 | 457 |
| 448 # Figure out the key we want to use. If multiple keys are configured, the | 458 # Figure out the key we want to use. If multiple keys are configured, the |
| 449 # server will rotate through them in a round-robin fashion. | 459 # server will rotate through them in a round-robin fashion. |
| 450 signing_key = None | 460 signing_key = None |
| 451 req_key = None | 461 req_key = None |
| 452 key_version = 1 | 462 key_version = 1 |
| 453 nkeys = len(self._server.keys) | 463 nkeys = len(self._server.keys) |
| 454 if msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and nkeys > 0: | 464 if msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and nkeys > 0: |
| 455 if msg.public_key_version in range(1, nkeys + 1): | 465 if msg.public_key_version in range(1, nkeys + 1): |
| 456 # requested key exists, use for signing and rotate. | 466 # requested key exists, use for signing and rotate. |
| 457 req_key = self._server.keys[msg.public_key_version - 1]['private_key'] | 467 req_key = self._server.keys[msg.public_key_version - 1]['private_key'] |
| 458 key_version = (msg.public_key_version % nkeys) + 1 | 468 key_version = (msg.public_key_version % nkeys) + 1 |
| 459 signing_key = self._server.keys[key_version - 1] | 469 signing_key = self._server.keys[key_version - 1] |
| 460 | 470 |
| 461 # Fill the policy data protobuf. | 471 # Fill the policy data protobuf. |
| 462 policy_data = dm.PolicyData() | 472 policy_data = dm.PolicyData() |
| 463 policy_data.policy_type = msg.policy_type | 473 policy_data.policy_type = msg.policy_type |
| 464 policy_data.timestamp = int(time.time() * 1000) | 474 policy_data.timestamp = int(time.time() * 1000) |
| 465 policy_data.request_token = token_info['device_token'] | 475 policy_data.request_token = token_info['device_token'] |
| 466 policy_data.policy_value = policy_value | 476 policy_data.policy_value = settings.SerializeToString() |
| 467 policy_data.machine_name = token_info['machine_name'] | 477 policy_data.machine_name = token_info['machine_name'] |
| 468 policy_data.valid_serial_number_missing = ( | 478 policy_data.valid_serial_number_missing = ( |
| 469 token_info['machine_id'] in BAD_MACHINE_IDS) | 479 token_info['machine_id'] in BAD_MACHINE_IDS) |
| 470 | 480 |
| 471 if signing_key: | 481 if signing_key: |
| 472 policy_data.public_key_version = key_version | 482 policy_data.public_key_version = key_version |
| 473 # There is no way for the testserver to know the user name belonging to | 483 if msg.policy_type == 'google/chromeos/publicaccount': |
| 474 # the GAIA auth token we received (short of actually talking to GAIA). To | 484 policy_data.username = msg.settings_entity_id |
| 475 # address this, we read the username from the policy configuration | 485 else: |
| 476 # dictionary, or use a default. | 486 # For regular user/device policy, there is no way for the testserver to |
| 477 policy_data.username = policy.get('policy_user', 'user@example.com') | 487 # know the user name belonging to the GAIA auth token we received (short |
| 488 # of actually talking to GAIA). To address this, we read the username from | |
| 489 # the policy configuration dictionary, or use a default. | |
| 490 policy_data.username = policy.get('policy_user', 'user@example.com') | |
| 478 policy_data.device_id = token_info['device_id'] | 491 policy_data.device_id = token_info['device_id'] |
| 479 signed_data = policy_data.SerializeToString() | 492 signed_data = policy_data.SerializeToString() |
| 480 | 493 |
| 481 response = dm.DeviceManagementResponse() | 494 response = dm.DeviceManagementResponse() |
| 482 fetch_response = response.policy_response.response.add() | 495 fetch_response = response.policy_response.response.add() |
| 483 fetch_response.policy_data = signed_data | 496 fetch_response.policy_data = signed_data |
| 484 if signing_key: | 497 if signing_key: |
| 485 fetch_response.policy_data_signature = ( | 498 fetch_response.policy_data_signature = ( |
| 486 signing_key['private_key'].hashAndSign(signed_data).tostring()) | 499 signing_key['private_key'].hashAndSign(signed_data).tostring()) |
| 487 if msg.public_key_version != key_version: | 500 if msg.public_key_version != key_version: |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 610 | 623 |
| 611 Returns: | 624 Returns: |
| 612 The newly generated device token for the device. | 625 The newly generated device token for the device. |
| 613 """ | 626 """ |
| 614 dmtoken_chars = [] | 627 dmtoken_chars = [] |
| 615 while len(dmtoken_chars) < 32: | 628 while len(dmtoken_chars) < 32: |
| 616 dmtoken_chars.append(random.choice('0123456789abcdef')) | 629 dmtoken_chars.append(random.choice('0123456789abcdef')) |
| 617 dmtoken = ''.join(dmtoken_chars) | 630 dmtoken = ''.join(dmtoken_chars) |
| 618 allowed_policy_types = { | 631 allowed_policy_types = { |
| 619 dm.DeviceRegisterRequest.USER: ['google/chromeos/user'], | 632 dm.DeviceRegisterRequest.USER: ['google/chromeos/user'], |
| 620 dm.DeviceRegisterRequest.DEVICE: ['google/chromeos/device'], | 633 dm.DeviceRegisterRequest.DEVICE: [ |
| 634 'google/chromeos/device', | |
| 635 'google/chromeos/publicaccount' | |
| 636 ], | |
| 621 dm.DeviceRegisterRequest.TT: ['google/chromeos/user'], | 637 dm.DeviceRegisterRequest.TT: ['google/chromeos/user'], |
| 622 } | 638 } |
| 623 if machine_id in KIOSK_MACHINE_IDS: | 639 if machine_id in KIOSK_MACHINE_IDS: |
| 624 enrollment_mode = dm.DeviceRegisterResponse.RETAIL | 640 enrollment_mode = dm.DeviceRegisterResponse.RETAIL |
| 625 else: | 641 else: |
| 626 enrollment_mode = dm.DeviceRegisterResponse.ENTERPRISE | 642 enrollment_mode = dm.DeviceRegisterResponse.ENTERPRISE |
| 627 self._registered_tokens[dmtoken] = { | 643 self._registered_tokens[dmtoken] = { |
| 628 'device_id': device_id, | 644 'device_id': device_id, |
| 629 'device_token': dmtoken, | 645 'device_token': dmtoken, |
| 630 'allowed_policy_types': allowed_policy_types[type], | 646 'allowed_policy_types': allowed_policy_types[type], |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 657 return self._registered_tokens.get(dmtoken, None) | 673 return self._registered_tokens.get(dmtoken, None) |
| 658 | 674 |
| 659 def UnregisterDevice(self, dmtoken): | 675 def UnregisterDevice(self, dmtoken): |
| 660 """Unregisters a device identified by the given DM token. | 676 """Unregisters a device identified by the given DM token. |
| 661 | 677 |
| 662 Args: | 678 Args: |
| 663 dmtoken: The device management token provided by the client. | 679 dmtoken: The device management token provided by the client. |
| 664 """ | 680 """ |
| 665 if dmtoken in self._registered_tokens.keys(): | 681 if dmtoken in self._registered_tokens.keys(): |
| 666 del self._registered_tokens[dmtoken] | 682 del self._registered_tokens[dmtoken] |
| OLD | NEW |