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..efd6694487ec58e5fe4057f04c9dcceca0e84be4 100644 |
--- a/net/tools/testserver/device_management.py |
+++ b/net/tools/testserver/device_management.py |
@@ -114,19 +114,25 @@ class RequestHandler(object): |
rmsg = dm.DeviceManagementRequest() |
rmsg.ParseFromString(self._request) |
+ logging.debug('auth -> ' + self._headers.getheader('Authorization', '')) |
+ logging.debug('deviceid -> ' + self.GetUniqueParam('deviceid')) |
self.DumpMessage('Request', rmsg) |
request_type = self.GetUniqueParam('request') |
+ # Check server side requirements, as defined in |
+ # device_management_backend.proto. |
+ if (self.GetUniqueParam('devicetype') != '2' or |
+ 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 +146,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 +166,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,41 +210,50 @@ 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. |
+ def ProcessDevicePolicy(self, msg): |
+ """Handles a policy request that uses the deprecated protcol. |
+ TODO(gfeher): Remove this when we certainly don't need it. |
Checks for authorization, encodes the policy into protobuf representation |
and constructs the response. |
@@ -252,6 +264,7 @@ class RequestHandler(object): |
Returns: |
A tuple of HTTP status code and response data to send to the client. |
""" |
+ |
# Check the management token. |
token, response = self.CheckToken() |
if not token: |
@@ -264,8 +277,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 +306,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') |
+ else: |
+ return self.ProcessDevicePolicy(msg) |
+ |
def SetProtobufMessageField(self, group_message, field, field_value): |
'''Sets a field in a protobuf message. |
@@ -302,23 +344,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 +393,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 +405,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 +445,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 +462,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 +493,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 +530,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): |
+ """Registers a device or user and generates a DM token for it. |
Args: |
device_id: The device identifier provided by the client. |
@@ -498,19 +543,30 @@ 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. |
+ allowed_policy_types = { |
+ dm.DeviceRegisterRequest.USER: ['google/chromeos/user'], |
+ dm.DeviceRegisterRequest.DEVICE: ['google/chromeos/device'], |
+ dm.DeviceRegisterRequest.TT: ['google/chromeos/user'], |
+ } |
+ self._registered_tokens[dmtoken] = { |
+ 'device_id': device_id, |
+ 'device_token': dmtoken, |
+ 'allowed_policy_types': allowed_policy_types[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. |
Returns: |
- The corresponding device identifier or None if not found. |
+ A dictionary with information about a device or user that is registered by |
+ dmtoken, or None if the token is not found. |
""" |
- 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 +574,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] |