Chromium Code Reviews| Index: net/tools/testserver/device_management.py |
| diff --git a/net/tools/testserver/device_management.py b/net/tools/testserver/device_management.py |
| index ccf1ac8a51ff68e798657c56cc6909811c377874..bd23cc0b34412a4f6e1f923a5281f8e6851240f9 100644 |
| --- a/net/tools/testserver/device_management.py |
| +++ b/net/tools/testserver/device_management.py |
| @@ -61,6 +61,9 @@ except ImportError: |
| import device_management_backend_pb2 as dm |
| import cloud_policy_pb2 as cp |
| +# TODO(gfeher): Remove before commit. |
|
Mattias Nissler (ping if slow)
2011/02/21 14:55:27
good idea :)
|
| +LOG_FILENAME = '/dev/stdout' |
| +logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) |
| class RequestHandler(object): |
| """Decodes and handles device management requests from clients. |
| @@ -117,16 +120,18 @@ class RequestHandler(object): |
| self.DumpMessage('Request', rmsg) |
| request_type = self.GetUniqueParam('request') |
| + if (self.GetUniqueParam('devicetype') != '2' or |
|
Mattias Nissler (ping if slow)
2011/02/21 14:55:27
Is that devicetype parameter from a discussion wit
gfeher
2011/02/22 15:57:29
Maybe. Hong had added some server-side requirement
|
| + self.GetUniqueParam('apptype') != 'Chrome' or |
| + (request_type != 'ping' and |
| + len(self.GetUniqueParam('deviceid')) >= 64) or |
| + len(self.GetUniqueParam('agent')) >= 64): |
| + return (400, 'Invalid request parameter') |
| if request_type == 'register': |
| return self.ProcessRegister(rmsg.register_request) |
| elif request_type == 'unregister': |
| return self.ProcessUnregister(rmsg.unregister_request) |
| - elif request_type == 'policy': |
| - return self.ProcessPolicy(rmsg.policy_request) |
| - elif request_type == 'cloud_policy': |
| - return self.ProcessCloudPolicyRequest(rmsg.cloud_policy_request) |
| - elif request_type == 'managed_check': |
| - return self.ProcessManagedCheck(rmsg.managed_check_request) |
| + elif request_type == 'policy' or request_type == 'ping': |
| + return self.ProcessPolicy(rmsg.policy_request, request_type) |
| else: |
| return (400, 'Invalid request parameter') |
| @@ -140,12 +145,6 @@ class RequestHandler(object): |
| return None |
| return match.group(1) |
| - def GetDeviceName(self): |
| - """Returns the name for the currently authenticated device based on its |
| - device id. |
| - """ |
| - return 'chromeos-' + self.GetUniqueParam('deviceid') |
| - |
| def ProcessRegister(self, msg): |
| """Handles a register request. |
| @@ -166,13 +165,16 @@ class RequestHandler(object): |
| if not device_id: |
| return (400, 'Missing device identifier') |
| - # Register the device and create a token. |
| - dmtoken = self._server.RegisterDevice(device_id) |
| + token_info = self._server.RegisterDevice(device_id, |
| + msg.machine_id, |
| + msg.type) |
| # Send back the reply. |
| response = dm.DeviceManagementResponse() |
| response.error = dm.DeviceManagementResponse.SUCCESS |
| - response.register_response.device_management_token = dmtoken |
| + response.register_response.device_management_token = ( |
| + token_info['device_token']) |
| + response.register_response.machine_name = token_info['machine_name'] |
| self.DumpMessage('Response', response) |
| @@ -207,51 +209,48 @@ class RequestHandler(object): |
| return (200, response.SerializeToString()) |
| - def ProcessManagedCheck(self, msg): |
| - """Handles a 'managed check' request. |
| + def ProcessInitialPolicy(self, msg): |
| + """Handles a 'preregister policy' request. |
| Queries the list of managed users and responds the client if their user |
| is managed or not. |
| Args: |
| - msg: The ManagedCheckRequest message received from the client. |
| + msg: The PolicyFetchRequest message received from the client. |
| Returns: |
| A tuple of HTTP status code and response data to send to the client. |
| """ |
| - # Check the management token. |
| + # Check the GAIA token. |
| auth = self.CheckGoogleLogin() |
| if not auth: |
| return (403, 'No authorization') |
| - managed_check_response = dm.ManagedCheckResponse() |
| + chrome_initial_settings = dm.ChromeInitialSettingsProto() |
| if ('*' in self._server.policy['managed_users'] or |
| auth in self._server.policy['managed_users']): |
| - managed_check_response.mode = dm.ManagedCheckResponse.MANAGED; |
| + chrome_initial_settings.enrollment_provision = ( |
| + dm.ChromeInitialSettingsProto.MANAGED); |
| else: |
| - managed_check_response.mode = dm.ManagedCheckResponse.UNMANAGED; |
| + chrome_initial_settings.enrollment_provision = ( |
| + dm.ChromeInitialSettingsProto.UNMANAGED); |
| + |
| + policy_data = dm.PolicyData() |
| + policy_data.policy_type = msg.policy_type |
| + policy_data.policy_value = chrome_initial_settings.SerializeToString() |
| # Prepare and send the response. |
| response = dm.DeviceManagementResponse() |
| response.error = dm.DeviceManagementResponse.SUCCESS |
| - response.managed_check_response.CopyFrom(managed_check_response) |
| + fetch_response = response.policy_response.response.add() |
| + fetch_response.policy_data = ( |
| + policy_data.SerializeToString()) |
| self.DumpMessage('Response', response) |
| return (200, response.SerializeToString()) |
| - def ProcessPolicy(self, msg): |
| - """Handles a policy request. |
| - |
| - Checks for authorization, encodes the policy into protobuf representation |
| - and constructs the response. |
| - |
| - Args: |
| - msg: The DevicePolicyRequest message received from the client. |
| - |
| - Returns: |
| - A tuple of HTTP status code and response data to send to the client. |
| - """ |
| + def ProcessDevicePolicy(self, msg): |
| # Check the management token. |
| token, response = self.CheckToken() |
| if not token: |
| @@ -264,8 +263,8 @@ class RequestHandler(object): |
| # Respond only if the client requested policy for the cros/device scope, |
| # since that's where chrome policy is supposed to live in. |
| - if msg.policy_scope in self._server.policy: |
| - policy = self._server.policy[msg.policy_scope]['mandatory'] |
| + if msg.policy_scope == 'chromeos/device': |
| + policy = self._server.policy['google/chromeos/user']['mandatory'] |
| setting = response.policy_response.setting.add() |
| setting.policy_key = 'chrome-policy' |
| policy_value = dm.GenericSetting() |
| @@ -293,6 +292,35 @@ class RequestHandler(object): |
| return (200, response.SerializeToString()) |
| + def ProcessPolicy(self, msg, request_type): |
| + """Handles a policy request. |
| + |
| + Checks for authorization, encodes the policy into protobuf representation |
| + and constructs the response. |
| + |
| + Args: |
| + msg: The DevicePolicyRequest message received from the client. |
| + |
| + Returns: |
| + A tuple of HTTP status code and response data to send to the client. |
| + """ |
| + |
| + if msg.request: |
| + for request in msg.request: |
| + if request.policy_type == 'google/chromeos/unregistered_user': |
| + if request_type != 'ping': |
| + return (400, 'Invalid request type') |
| + return self.ProcessInitialPolicy(request) |
| + elif (request.policy_type in |
| + ('google/chromeos/user', 'google/chromeos/device')): |
| + if request_type != 'policy': |
| + return (400, 'Invalid request type') |
| + return self.ProcessCloudPolicy(request) |
| + else: |
| + return (400, 'Invalid policy_type') |
|
Mattias Nissler (ping if slow)
2011/02/21 14:55:27
What about user policy? Have I missed something?
gfeher
2011/02/22 15:57:29
It is handled in self.ProcessCloudPolicy().
|
| + else: |
| + return self.ProcessDevicePolicy(msg) |
| + |
| def SetProtobufMessageField(self, group_message, field, field_value): |
| '''Sets a field in a protobuf message. |
| @@ -302,23 +330,22 @@ class RequestHandler(object): |
| group_message.DESCRIPTOR.fields. |
| field_value: The value to set. |
| ''' |
| - if field.label == field.LABEL_REPEATED: |
| + if field.type == field.TYPE_BOOL: |
| + assert type(field_value) == bool |
| + elif field.type == field.TYPE_STRING: |
| + assert type(field_value) == str |
| + elif field.type == field.TYPE_INT64: |
| + assert type(field_value) == int |
| + elif (field.type == field.TYPE_MESSAGE and |
| + field.message_type.name == 'StringList'): |
| assert type(field_value) == list |
| - assert field.type == field.TYPE_STRING |
| - list_field = group_message.__getattribute__(field.name) |
| + entries = group_message.__getattribute__(field.name).entries |
| for list_item in field_value: |
| - list_field.append(list_item) |
| + entries.append(list_item) |
| + return |
| else: |
| - # Simple cases: |
| - if field.type == field.TYPE_BOOL: |
| - assert type(field_value) == bool |
| - elif field.type == field.TYPE_STRING: |
| - assert type(field_value) == str |
| - elif field.type == field.TYPE_INT64: |
| - assert type(field_value) == int |
| - else: |
| - raise Exception('Unknown field type %s' % field.type_name) |
| - group_message.__setattr__(field.name, field_value) |
| + raise Exception('Unknown field type %s' % field.type) |
| + group_message.__setattr__(field.name, field_value) |
| def GatherPolicySettings(self, settings, policies): |
| '''Copies all the policies from a dictionary into a protobuf of type |
| @@ -352,7 +379,7 @@ class RequestHandler(object): |
| if got_fields: |
| settings.__getattribute__(group.name).CopyFrom(group_message) |
| - def ProcessCloudPolicyRequest(self, msg): |
| + def ProcessCloudPolicy(self, msg): |
| """Handles a cloud policy request. (New protocol for policy requests.) |
| Checks for authorization, encodes the policy into protobuf representation, |
| @@ -364,37 +391,37 @@ class RequestHandler(object): |
| Returns: |
| A tuple of HTTP status code and response data to send to the client. |
| """ |
| - token, response = self.CheckToken() |
| - if not token: |
| - return response |
| + |
| + token_info, error = self.CheckToken() |
| + if not token_info: |
| + return error |
| settings = cp.CloudPolicySettings() |
| - if msg.policy_scope in self._server.policy: |
| - # Respond is only given if the scope is specified in the config file. |
| + if (msg.policy_type in token_info['allowed_policy_types'] and |
| + msg.policy_type in self._server.policy): |
| + # Response is only given if the scope is specified in the config file. |
| # Normally 'chromeos/device' and 'chromeos/user' should be accepted. |
| self.GatherPolicySettings(settings, |
| - self._server.policy[msg.policy_scope]) |
| - |
| - # Construct response |
| - signed_response = dm.SignedCloudPolicyResponse() |
| - signed_response.settings.CopyFrom(settings) |
| - signed_response.timestamp = int(time.time()) |
| - signed_response.request_token = token; |
| - signed_response.device_name = self.GetDeviceName() |
| - |
| - cloud_response = dm.CloudPolicyResponse() |
| - cloud_response.signed_response = signed_response.SerializeToString() |
| - signed_data = cloud_response.signed_response |
| - cloud_response.signature = ( |
| - self._server.private_key.hashAndSign(signed_data).tostring()) |
| - for certificate in self._server.cert_chain: |
| - cloud_response.certificate_chain.append( |
| - certificate.writeBytes().tostring()) |
| + self._server.policy[msg.policy_type]) |
| + |
| + policy_data = dm.PolicyData() |
| + policy_data.policy_value = settings.SerializeToString() |
| + policy_data.policy_type = msg.policy_type |
| + policy_data.timestamp = int(time.time() * 1000) |
| + policy_data.request_token = token_info['device_token']; |
| + policy_data.machine_name = token_info['machine_name'] |
| + signed_data = policy_data.SerializeToString() |
| response = dm.DeviceManagementResponse() |
| response.error = dm.DeviceManagementResponse.SUCCESS |
| - response.cloud_policy_response.CopyFrom(cloud_response) |
| + fetch_response = response.policy_response.response.add() |
| + fetch_response.policy_data = signed_data |
| + fetch_response.policy_data_signature = ( |
| + self._server.private_key.hashAndSign(signed_data).tostring()) |
| + for certificate in self._server.cert_chain: |
| + fetch_response.certificate_chain.append( |
| + certificate.writeBytes().tostring()) |
| self.DumpMessage('Response', response) |
| @@ -404,12 +431,13 @@ class RequestHandler(object): |
| """Helper for checking whether the client supplied a valid DM token. |
| Extracts the token from the request and passed to the server in order to |
| - look up the client. Returns a pair of token and error response. If the token |
| - is None, the error response is a pair of status code and error message. |
| + look up the client. |
| Returns: |
| - A pair of DM token and error response. If the token is None, the message |
| - will contain the error response to send back. |
| + A pair of token information record and error response. If the first |
| + element is None, then the second contains an error code to send back to |
| + the client. Otherwise the first element is the same structure that is |
| + returned by LookupToken(). |
| """ |
| error = None |
| dmtoken = None |
| @@ -420,11 +448,14 @@ class RequestHandler(object): |
| dmtoken = match.group(1) |
| if not dmtoken: |
| error = dm.DeviceManagementResponse.DEVICE_MANAGEMENT_TOKEN_INVALID |
| - elif (not request_device_id or |
| - not self._server.LookupDevice(dmtoken) == request_device_id): |
| - error = dm.DeviceManagementResponse.DEVICE_NOT_FOUND |
| else: |
| - return (dmtoken, None) |
| + token_info = self._server.LookupToken(dmtoken) |
| + if (not token_info or |
| + not request_device_id or |
| + token_info['device_id'] != request_device_id): |
| + error = dm.DeviceManagementResponse.DEVICE_NOT_FOUND |
| + else: |
| + return (token_info, None) |
| response = dm.DeviceManagementResponse() |
| response.error = error |
| @@ -448,7 +479,7 @@ class TestServer(object): |
| policy_cert_chain: List of paths to X.509 certificate files of the |
| certificate chain used for signing responses. |
| """ |
| - self._registered_devices = {} |
| + self._registered_tokens = {} |
| self.policy = {} |
| if json is None: |
| print 'No JSON module, cannot parse policy information' |
| @@ -485,8 +516,8 @@ class TestServer(object): |
| handler = RequestHandler(self, path, headers, request) |
| return handler.HandleRequest() |
| - def RegisterDevice(self, device_id): |
| - """Registers a device and generate a DM token for it. |
| + def RegisterDevice(self, device_id, machine_id, type=None): |
| + """Registers a device or user and generates a DM token for it. |
| Args: |
| device_id: The device identifier provided by the client. |
| @@ -498,11 +529,21 @@ class TestServer(object): |
| while len(dmtoken_chars) < 32: |
| dmtoken_chars.append(random.choice('0123456789abcdef')) |
| dmtoken = ''.join(dmtoken_chars) |
| - self._registered_devices[dmtoken] = device_id |
| - return dmtoken |
| - def LookupDevice(self, dmtoken): |
| - """Looks up a device by DMToken. |
| + self._registered_tokens[dmtoken] = { |
| + 'device_id': device_id, |
| + 'device_token': dmtoken, |
| + 'allowed_policy_types': { |
| + dm.DeviceRegisterRequest.USER: ['google/chromeos/user'], |
| + dm.DeviceRegisterRequest.DEVICE: ['google/chromeos/device'], |
| + dm.DeviceRegisterRequest.TT: ['google/chromeos/user'], |
| + }[type], |
| + 'machine_name': 'chromeos-' + machine_id, |
| + } |
| + return self._registered_tokens[dmtoken] |
| + |
| + def LookupToken(self, dmtoken): |
| + """Looks up a device or a user by DM token. |
| Args: |
| dmtoken: The device management token provided by the client. |
| @@ -510,7 +551,7 @@ class TestServer(object): |
| Returns: |
| The corresponding device identifier or None if not found. |
|
Mattias Nissler (ping if slow)
2011/02/21 14:55:27
You might want to update this comment.
gfeher
2011/02/22 15:57:29
Done.
|
| """ |
| - return self._registered_devices.get(dmtoken, None) |
| + return self._registered_tokens.get(dmtoken, None) |
| def UnregisterDevice(self, dmtoken): |
| """Unregisters a device identified by the given DM token. |
| @@ -518,5 +559,5 @@ class TestServer(object): |
| Args: |
| dmtoken: The device management token provided by the client. |
| """ |
| - if dmtoken in self._registered_devices: |
| - del self._registered_devices[dmtoken] |
| + if dmtoken in self._registered_tokens.keys(): |
| + del self._registered_tokens[dmtoken] |