def ProcessRegister(self, msg): """Handles a register request. Checks the query for authorization and device identifier, registers the device with the server and constructs a response. Args: msg: The DeviceRegisterRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ # Check the auth token and device ID. match = re.match('GoogleLogin auth=(\\w+)', self._headers.getheader('Authorization', '')) if not match: return (403, 'No authorization') auth_token = match.group(1) device_id = self.GetUniqueParam('deviceid') if not device_id: return (400, 'Missing device identifier') # Register the device and create a token. dmtoken = self._server.RegisterDevice(device_id) # Send back the reply. response = dm.DeviceManagementResponse() response.error = dm.DeviceManagementResponse.SUCCESS response.register_response.device_management_token = dmtoken self.DumpMessage('Response', response) return (200, response.SerializeToString())
def ProcessRegister(self, msg): """Handles a register request. Checks the query for authorization and device identifier, registers the device with the server and constructs a response. Args: msg: The DeviceRegisterRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ # Check the auth token and device ID. if not self.CheckGoogleLogin(): return (403, 'No authorization') device_id = self.GetUniqueParam('deviceid') if not device_id: return (400, 'Missing device identifier') token_info = self._server.RegisterDevice(device_id, msg.machine_id, msg.type) # Send back the reply. response = dm.DeviceManagementResponse() response.register_response.device_management_token = ( token_info['device_token']) response.register_response.machine_name = token_info['machine_name'] self.DumpMessage('Response', response) return (200, response.SerializeToString())
def CheckToken(self): """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. Returns: A pair of DM token and error response. If the token is None, the message will contain the error response to send back. """ error = None dmtoken = None request_device_id = self.GetUniqueParam('deviceid') match = re.match('GoogleDMToken token=(\\w+)', self._headers.getheader('Authorization', '')) if match: 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) response = dm.DeviceManagementResponse() response.error = error self.DumpMessage('Response', response) return (None, (200, response.SerializeToString()))
def CheckToken(self): """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 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 request_device_id = self.GetUniqueParam('deviceid') match = re.match('GoogleDMToken token=(\\w+)', self._headers.getheader('Authorization', '')) if match: dmtoken = match.group(1) if not dmtoken: error = dm.DeviceManagementResponse.DEVICE_MANAGEMENT_TOKEN_INVALID else: 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 self.DumpMessage('Response', response) return (None, (200, response.SerializeToString()))
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. Args: msg: The DevicePolicyRequest message received from the client. 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: return response # Stuff the policy dictionary into a response message and send it back. response = dm.DeviceManagementResponse() response.policy_response.CopyFrom(dm.DevicePolicyResponse()) # 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 == '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() for (key, value) in policy.iteritems(): entry = policy_value.named_value.add() entry.name = key entry_value = dm.GenericValue() if isinstance(value, bool): entry_value.value_type = dm.GenericValue.VALUE_TYPE_BOOL entry_value.bool_value = value elif isinstance(value, int): entry_value.value_type = dm.GenericValue.VALUE_TYPE_INT64 entry_value.int64_value = value elif isinstance(value, str) or isinstance(value, unicode): entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING entry_value.string_value = value elif isinstance(value, list): entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING_ARRAY for list_entry in value: entry_value.string_array.append(str(list_entry)) entry.value.CopyFrom(entry_value) setting.policy_value.CopyFrom(policy_value) self.DumpMessage('Response', response) return (200, response.SerializeToString())
def _RegisterAndGetDMToken(self, device): """Registers with the TestServer and returns the DMToken fetched. Registers for device policy if device is True. Otherwise registers for user policy. """ assert self.IsChromeOS() type = device and dmb.DeviceRegisterRequest.DEVICE \ or dmb.DeviceRegisterRequest.USER rstring = self._PostRegisterRequest(type) response = dmb.DeviceManagementResponse() response.ParseFromString(rstring) return response.register_response.device_management_token
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 DeviceManagementRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ token_info, error = self.CheckToken() if not token_info: return error key_update_request = msg.device_state_key_update_request if len(key_update_request.server_backed_state_key) > 0: self.server.UpdateStateKeys(token_info['device_token'], key_update_request.server_backed_state_key) # If this is a |publicaccount| request, get the |username| now and use # it in every PolicyFetchResponse produced. This is required to validate # policy for extensions in device-local accounts. # Unfortunately, the |username| can't be obtained from |msg| because that # requires interacting with GAIA. username = None for request in msg.policy_request.request: if request.policy_type == 'google/chromeos/publicaccount': username = request.settings_entity_id response = dm.DeviceManagementResponse() for request in msg.policy_request.request: if (request.policy_type in ('google/android/user', 'google/chromeos/device', 'google/chromeos/publicaccount', 'google/chromeos/user', 'google/chrome/user', 'google/ios/user')): fetch_response = response.policy_response.response.add() self.ProcessCloudPolicy(request, token_info, fetch_response, username) elif request.policy_type == 'google/chrome/extension': self.ProcessCloudPolicyForExtensions( request, response.policy_response, token_info, username) else: fetch_response.error_code = 400 fetch_response.error_message = 'Invalid policy_type' return (200, response)
def _DMPostRequest(self, request_type, request, headers): """Posts a request to the mock DMServer.""" assert self.IsChromeOS() url = self._GetHttpURLForDeviceManagement() url += '?' + urllib.urlencode({ 'deviceid': self.device_id, 'oauth_token': 'dummy_oauth_token_that_is_not_checked_anyway', 'request': request_type, 'devicetype': 2, 'apptype': 'Chrome', 'agent': 'Chrome', }) response = dm.DeviceManagementResponse() response.ParseFromString(urllib2.urlopen(urllib2.Request( url, request.SerializeToString(), headers)).read()) return response
def ProcessApiAuthorization(self, msg): """Handles an API authorization request. Args: msg: The DeviceServiceApiAccessRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ policy = self.server.GetPolicies() # Return the auth code from the config file if it's defined, # else return a descriptive default value. response = dm.DeviceManagementResponse() response.service_api_access_response.auth_code = policy.get( 'robot_api_auth_code', 'policy_testserver.py-auth_code') return (200, response)
def _FetchPolicy(self, token, device): """Fetches policy from the TestServer, using the given token. Token must be a valid token retrieved with _RegisterAndGetDMToken. If device is True, fetches signed device policy. Otherwise fetches user policy. This method also verifies the response, and returns the first policy fetch response. """ assert self.IsChromeOS() type = device and 'google/chromeos/device' or 'google/chromeos/user' rstring = self._PostPolicyRequest(token=token, type=type, want_signature=device) response = dmb.DeviceManagementResponse() response.ParseFromString(rstring) fetch_response = response.policy_response.response[0] assert fetch_response.policy_data assert fetch_response.policy_data_signature assert fetch_response.new_public_key return fetch_response
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 DeviceManagementRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ token_info, error = self.CheckToken() if not token_info: return error key_update_request = msg.device_state_key_update_request if len(key_update_request.server_backed_state_key) > 0: self.server.UpdateStateKeys( token_info['device_token'], key_update_request.server_backed_state_key) response = dm.DeviceManagementResponse() for request in msg.policy_request.request: fetch_response = response.policy_response.response.add() if (request.policy_type in ('google/android/user', 'google/chrome/extension', 'google/chromeos/device', 'google/chromeos/publicaccount', 'google/chromeos/user', 'google/chrome/user', 'google/ios/user')): if request_type != 'policy': fetch_response.error_code = 400 fetch_response.error_message = 'Invalid request type' else: self.ProcessCloudPolicy(request, token_info, fetch_response) else: fetch_response.error_code = 400 fetch_response.error_message = 'Invalid policy_type' return (200, response)
def ProcessRegister(self, msg): """Handles a register request. Checks the query for authorization and device identifier, registers the device with the server and constructs a response. Args: msg: The DeviceRegisterRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ # Check the auth token and device ID. auth = self.CheckGoogleLogin() if not auth: return (403, 'No authorization') policy = self.server.GetPolicies() if ('*' not in policy['managed_users'] and auth not in policy['managed_users']): return (403, 'Unmanaged') device_id = self.GetUniqueParam('deviceid') if not device_id: return (400, 'Missing device identifier') token_info = self.server.RegisterDevice(device_id, msg.machine_id, msg.type) # Send back the reply. response = dm.DeviceManagementResponse() response.register_response.device_management_token = ( token_info['device_token']) response.register_response.machine_name = token_info['machine_name'] response.register_response.enrollment_type = token_info[ 'enrollment_mode'] return (200, response)
def _GenerateDevicePolicyBlob(self, device_policy=None, owner=None): """Generates a signed device policy blob.""" # Fill in the device settings protobuf. device_policy = device_policy or {} owner = owner or constants.CREDENTIALS['$mockowner'][0] settings = dp.ChromeDeviceSettingsProto() for group in settings.DESCRIPTOR.fields: # Create protobuf message for group. group_message = eval('dp.' + group.message_type.name + '()') # Indicates if at least one field was set in |group_message|. got_fields = False # Iterate over fields of the message and feed them from the policy dict. for field in group_message.DESCRIPTOR.fields: field_value = None if field.name in device_policy: got_fields = True field_value = device_policy[field.name] self._SetProtobufMessageField(group_message, field, field_value) if got_fields: settings.__getattribute__(group.name).CopyFrom(group_message) # Fill in the policy data protobuf. policy_data = dm.PolicyData() policy_data.policy_type = 'google/chromeos/device' policy_data.policy_value = settings.SerializeToString() policy_data.username = owner serialized_policy_data = policy_data.SerializeToString() # Fill in the device management response protobuf. response = dm.DeviceManagementResponse() fetch_response = response.policy_response.response.add() fetch_response.policy_data = serialized_policy_data fetch_response.policy_data_signature = ( self._private_key.hashAndSign(serialized_policy_data).tostring()) self._device_policy_blob = fetch_response.SerializeToString()
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 PolicyFetchRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ # Check the GAIA token. auth = self.CheckGoogleLogin() if not auth: return (403, 'No authorization') chrome_initial_settings = dm.ChromeInitialSettingsProto() if ('*' in self._server.policy['managed_users'] or auth in self._server.policy['managed_users']): chrome_initial_settings.enrollment_provision = ( dm.ChromeInitialSettingsProto.MANAGED) else: 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() fetch_response = response.policy_response.response.add() fetch_response.policy_data = (policy_data.SerializeToString()) self.DumpMessage('Response', response) return (200, response.SerializeToString())
def ProcessCloudPolicy(self, msg): """Handles a cloud policy request. (New protocol for policy requests.) Checks for authorization, encodes the policy into protobuf representation, signs it and constructs the repsonse. Args: msg: The CloudPolicyRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ token_info, error = self.CheckToken() if not token_info: return error if msg.machine_id: self.server.UpdateMachineId(token_info['device_token'], msg.machine_id) # Response is only given if the scope is specified in the config file. # Normally 'google/chromeos/device', 'google/chromeos/user' and # 'google/chromeos/publicaccount' should be accepted. policy = self.server.GetPolicies() policy_value = '' policy_key = msg.policy_type if msg.settings_entity_id: policy_key += '/' + msg.settings_entity_id if msg.policy_type in token_info['allowed_policy_types']: if (msg.policy_type == 'google/chromeos/user' or msg.policy_type == 'google/chrome/user' or msg.policy_type == 'google/chromeos/publicaccount'): settings = cp.CloudPolicySettings() payload = self.server.ReadPolicyFromDataDir(policy_key, settings) if payload is None: self.GatherUserPolicySettings(settings, policy.get(policy_key, {})) payload = settings.SerializeToString() elif msg.policy_type == 'google/chromeos/device': settings = dp.ChromeDeviceSettingsProto() payload = self.server.ReadPolicyFromDataDir(policy_key, settings) if payload is None: self.GatherDevicePolicySettings(settings, policy.get(policy_key, {})) payload = settings.SerializeToString() # Sign with 'current_key_index', defaulting to key 0. signing_key = None req_key = None current_key_index = policy.get('current_key_index', 0) nkeys = len(self.server.keys) if (msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and current_key_index in range(nkeys)): signing_key = self.server.keys[current_key_index] if msg.public_key_version in range(1, nkeys + 1): # requested key exists, use for signing and rotate. req_key = self.server.keys[msg.public_key_version - 1]['private_key'] # Fill the policy data protobuf. policy_data = dm.PolicyData() policy_data.policy_type = msg.policy_type policy_data.timestamp = int(time.time() * 1000) policy_data.request_token = token_info['device_token'] policy_data.policy_value = payload policy_data.machine_name = token_info['machine_name'] policy_data.valid_serial_number_missing = ( token_info['machine_id'] in BAD_MACHINE_IDS) policy_data.settings_entity_id = msg.settings_entity_id if signing_key: policy_data.public_key_version = current_key_index + 1 if msg.policy_type == 'google/chromeos/publicaccount': policy_data.username = msg.settings_entity_id else: # For regular user/device policy, there is no way for the testserver to # know the user name belonging to the GAIA auth token we received (short # of actually talking to GAIA). To address this, we read the username from # the policy configuration dictionary, or use a default. policy_data.username = policy.get('policy_user', '*****@*****.**') policy_data.device_id = token_info['device_id'] signed_data = policy_data.SerializeToString() response = dm.DeviceManagementResponse() fetch_response = response.policy_response.response.add() fetch_response.policy_data = signed_data if signing_key: fetch_response.policy_data_signature = ( signing_key['private_key'].hashAndSign(signed_data).tostring()) if msg.public_key_version != current_key_index + 1: fetch_response.new_public_key = signing_key['public_key'] if req_key: fetch_response.new_public_key_signature = ( req_key.hashAndSign(fetch_response.new_public_key).tostring()) self.DumpMessage('Response', response) return (200, response.SerializeToString())
def ProcessCloudPolicy(self, msg): """Handles a cloud policy request. (New protocol for policy requests.) Checks for authorization, encodes the policy into protobuf representation, signs it and constructs the repsonse. Args: msg: The CloudPolicyRequest message received from the client. Returns: A tuple of HTTP status code and response data to send to the client. """ token_info, error = self.CheckToken() if not token_info: return error if msg.machine_id: self._server.UpdateMachineId(token_info['device_token'], msg.machine_id) # Response is only given if the scope is specified in the config file. # Normally 'google/chromeos/device' and 'google/chromeos/user' should be # accepted. policy = self._server.GetPolicies() policy_value = '' if (msg.policy_type in token_info['allowed_policy_types'] and msg.policy_type in policy): if msg.policy_type == 'google/chromeos/user': settings = cp.CloudPolicySettings() self.GatherUserPolicySettings(settings, policy[msg.policy_type]) policy_value = settings.SerializeToString() elif msg.policy_type == 'google/chromeos/device': settings = dp.ChromeDeviceSettingsProto() self.GatherDevicePolicySettings(settings, policy[msg.policy_type]) policy_value = settings.SerializeToString() # Figure out the key we want to use. If multiple keys are configured, the # server will rotate through them in a round-robin fashion. signing_key = None req_key = None key_version = 1 nkeys = len(self._server.keys) if msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and nkeys > 0: if msg.public_key_version in range(1, nkeys + 1): # requested key exists, use for signing and rotate. req_key = self._server.keys[msg.public_key_version - 1]['private_key'] key_version = (msg.public_key_version % nkeys) + 1 signing_key = self._server.keys[key_version - 1] # Fill the policy data protobuf. policy_data = dm.PolicyData() policy_data.policy_type = msg.policy_type policy_data.timestamp = int(time.time() * 1000) policy_data.request_token = token_info['device_token'] policy_data.policy_value = policy_value policy_data.machine_name = token_info['machine_name'] policy_data.valid_serial_number_missing = ( token_info['machine_id'] in BAD_MACHINE_IDS) if signing_key: policy_data.public_key_version = key_version policy_data.username = self._server.username policy_data.device_id = token_info['device_id'] signed_data = policy_data.SerializeToString() response = dm.DeviceManagementResponse() fetch_response = response.policy_response.response.add() fetch_response.policy_data = signed_data if signing_key: fetch_response.policy_data_signature = ( signing_key['private_key'].hashAndSign(signed_data).tostring()) if msg.public_key_version != key_version: fetch_response.new_public_key = signing_key['public_key'] if req_key: fetch_response.new_public_key_signature = ( req_key.hashAndSign(fetch_response.new_public_key).tostring()) self.DumpMessage('Response', response) return (200, response.SerializeToString())