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 |