Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python2.5 | 1 #!/usr/bin/python2.5 |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """A bare-bones test server for testing cloud policy support. | 6 """A bare-bones test server for testing cloud policy support. |
| 7 | 7 |
| 8 This implements a simple cloud policy test server that can be used to test | 8 This implements a simple cloud policy test server that can be used to test |
| 9 chrome's device management service client. The policy information is read from | 9 chrome's device management service client. The policy information is read from |
| 10 the file named device_management in the server's data directory. It contains | 10 the file named device_management in the server's data directory. It contains |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 54 import simplejson as json | 54 import simplejson as json |
| 55 except ImportError: | 55 except ImportError: |
| 56 try: | 56 try: |
| 57 import json | 57 import json |
| 58 except ImportError: | 58 except ImportError: |
| 59 json = None | 59 json = None |
| 60 | 60 |
| 61 import device_management_backend_pb2 as dm | 61 import device_management_backend_pb2 as dm |
| 62 import cloud_policy_pb2 as cp | 62 import cloud_policy_pb2 as cp |
| 63 | 63 |
| 64 # TODO(gfeher): Remove before commit. | |
| 65 LOG_FILENAME = '/dev/stdout' | |
| 66 logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) | |
|
gfeher
2011/02/25 20:36:35
I didn't forget this!
| |
| 64 | 67 |
| 65 class RequestHandler(object): | 68 class RequestHandler(object): |
| 66 """Decodes and handles device management requests from clients. | 69 """Decodes and handles device management requests from clients. |
| 67 | 70 |
| 68 The handler implements all the request parsing and protobuf message decoding | 71 The handler implements all the request parsing and protobuf message decoding |
| 69 and encoding. It calls back into the server to lookup, register, and | 72 and encoding. It calls back into the server to lookup, register, and |
| 70 unregister clients. | 73 unregister clients. |
| 71 """ | 74 """ |
| 72 | 75 |
| 73 def __init__(self, server, path, headers, request): | 76 def __init__(self, server, path, headers, request): |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 107 | 110 |
| 108 Parses the data supplied at construction time and returns a pair indicating | 111 Parses the data supplied at construction time and returns a pair indicating |
| 109 http status code and response data to be sent back to the client. | 112 http status code and response data to be sent back to the client. |
| 110 | 113 |
| 111 Returns: | 114 Returns: |
| 112 A tuple of HTTP status code and response data to send to the client. | 115 A tuple of HTTP status code and response data to send to the client. |
| 113 """ | 116 """ |
| 114 rmsg = dm.DeviceManagementRequest() | 117 rmsg = dm.DeviceManagementRequest() |
| 115 rmsg.ParseFromString(self._request) | 118 rmsg.ParseFromString(self._request) |
| 116 | 119 |
| 120 logging.debug('auth -> ' + self._headers.getheader('Authorization', '')) | |
| 121 logging.debug('deviceid -> ' + self.GetUniqueParam('deviceid')) | |
| 117 self.DumpMessage('Request', rmsg) | 122 self.DumpMessage('Request', rmsg) |
| 118 | 123 |
| 119 request_type = self.GetUniqueParam('request') | 124 request_type = self.GetUniqueParam('request') |
| 125 # Check server side requirements, as defined in | |
| 126 # device_management_backend.proto. | |
| 127 if (self.GetUniqueParam('devicetype') != '2' or | |
| 128 self.GetUniqueParam('apptype') != 'Chrome' or | |
| 129 (request_type != 'ping' and | |
| 130 len(self.GetUniqueParam('deviceid')) >= 64) or | |
| 131 len(self.GetUniqueParam('agent')) >= 64): | |
| 132 return (400, 'Invalid request parameter') | |
| 120 if request_type == 'register': | 133 if request_type == 'register': |
| 121 return self.ProcessRegister(rmsg.register_request) | 134 return self.ProcessRegister(rmsg.register_request) |
| 122 elif request_type == 'unregister': | 135 elif request_type == 'unregister': |
| 123 return self.ProcessUnregister(rmsg.unregister_request) | 136 return self.ProcessUnregister(rmsg.unregister_request) |
| 124 elif request_type == 'policy': | 137 elif request_type == 'policy' or request_type == 'ping': |
| 125 return self.ProcessPolicy(rmsg.policy_request) | 138 return self.ProcessPolicy(rmsg.policy_request, request_type) |
| 126 elif request_type == 'cloud_policy': | |
| 127 return self.ProcessCloudPolicyRequest(rmsg.cloud_policy_request) | |
| 128 elif request_type == 'managed_check': | |
| 129 return self.ProcessManagedCheck(rmsg.managed_check_request) | |
| 130 else: | 139 else: |
| 131 return (400, 'Invalid request parameter') | 140 return (400, 'Invalid request parameter') |
| 132 | 141 |
| 133 def CheckGoogleLogin(self): | 142 def CheckGoogleLogin(self): |
| 134 """Extracts the GoogleLogin auth token from the HTTP request, and | 143 """Extracts the GoogleLogin auth token from the HTTP request, and |
| 135 returns it. Returns None if the token is not present. | 144 returns it. Returns None if the token is not present. |
| 136 """ | 145 """ |
| 137 match = re.match('GoogleLogin auth=(\\w+)', | 146 match = re.match('GoogleLogin auth=(\\w+)', |
| 138 self._headers.getheader('Authorization', '')) | 147 self._headers.getheader('Authorization', '')) |
| 139 if not match: | 148 if not match: |
| 140 return None | 149 return None |
| 141 return match.group(1) | 150 return match.group(1) |
| 142 | 151 |
| 143 def GetDeviceName(self): | |
| 144 """Returns the name for the currently authenticated device based on its | |
| 145 device id. | |
| 146 """ | |
| 147 return 'chromeos-' + self.GetUniqueParam('deviceid') | |
| 148 | |
| 149 def ProcessRegister(self, msg): | 152 def ProcessRegister(self, msg): |
| 150 """Handles a register request. | 153 """Handles a register request. |
| 151 | 154 |
| 152 Checks the query for authorization and device identifier, registers the | 155 Checks the query for authorization and device identifier, registers the |
| 153 device with the server and constructs a response. | 156 device with the server and constructs a response. |
| 154 | 157 |
| 155 Args: | 158 Args: |
| 156 msg: The DeviceRegisterRequest message received from the client. | 159 msg: The DeviceRegisterRequest message received from the client. |
| 157 | 160 |
| 158 Returns: | 161 Returns: |
| 159 A tuple of HTTP status code and response data to send to the client. | 162 A tuple of HTTP status code and response data to send to the client. |
| 160 """ | 163 """ |
| 161 # Check the auth token and device ID. | 164 # Check the auth token and device ID. |
| 162 if not self.CheckGoogleLogin(): | 165 if not self.CheckGoogleLogin(): |
| 163 return (403, 'No authorization') | 166 return (403, 'No authorization') |
| 164 | 167 |
| 165 device_id = self.GetUniqueParam('deviceid') | 168 device_id = self.GetUniqueParam('deviceid') |
| 166 if not device_id: | 169 if not device_id: |
| 167 return (400, 'Missing device identifier') | 170 return (400, 'Missing device identifier') |
| 168 | 171 |
| 169 # Register the device and create a token. | 172 token_info = self._server.RegisterDevice(device_id, |
| 170 dmtoken = self._server.RegisterDevice(device_id) | 173 msg.machine_id, |
| 174 msg.type) | |
| 171 | 175 |
| 172 # Send back the reply. | 176 # Send back the reply. |
| 173 response = dm.DeviceManagementResponse() | 177 response = dm.DeviceManagementResponse() |
| 174 response.error = dm.DeviceManagementResponse.SUCCESS | 178 response.error = dm.DeviceManagementResponse.SUCCESS |
| 175 response.register_response.device_management_token = dmtoken | 179 response.register_response.device_management_token = ( |
| 180 token_info['device_token']) | |
| 181 response.register_response.machine_name = token_info['machine_name'] | |
| 176 | 182 |
| 177 self.DumpMessage('Response', response) | 183 self.DumpMessage('Response', response) |
| 178 | 184 |
| 179 return (200, response.SerializeToString()) | 185 return (200, response.SerializeToString()) |
| 180 | 186 |
| 181 def ProcessUnregister(self, msg): | 187 def ProcessUnregister(self, msg): |
| 182 """Handles a register request. | 188 """Handles a register request. |
| 183 | 189 |
| 184 Checks for authorization, unregisters the device and constructs the | 190 Checks for authorization, unregisters the device and constructs the |
| 185 response. | 191 response. |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 200 | 206 |
| 201 # Prepare and send the response. | 207 # Prepare and send the response. |
| 202 response = dm.DeviceManagementResponse() | 208 response = dm.DeviceManagementResponse() |
| 203 response.error = dm.DeviceManagementResponse.SUCCESS | 209 response.error = dm.DeviceManagementResponse.SUCCESS |
| 204 response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse()) | 210 response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse()) |
| 205 | 211 |
| 206 self.DumpMessage('Response', response) | 212 self.DumpMessage('Response', response) |
| 207 | 213 |
| 208 return (200, response.SerializeToString()) | 214 return (200, response.SerializeToString()) |
| 209 | 215 |
| 210 def ProcessManagedCheck(self, msg): | 216 def ProcessInitialPolicy(self, msg): |
| 211 """Handles a 'managed check' request. | 217 """Handles a 'preregister policy' request. |
| 212 | 218 |
| 213 Queries the list of managed users and responds the client if their user | 219 Queries the list of managed users and responds the client if their user |
| 214 is managed or not. | 220 is managed or not. |
| 215 | 221 |
| 216 Args: | 222 Args: |
| 217 msg: The ManagedCheckRequest message received from the client. | 223 msg: The PolicyFetchRequest message received from the client. |
| 218 | 224 |
| 219 Returns: | 225 Returns: |
| 220 A tuple of HTTP status code and response data to send to the client. | 226 A tuple of HTTP status code and response data to send to the client. |
| 221 """ | 227 """ |
| 222 # Check the management token. | 228 # Check the GAIA token. |
| 223 auth = self.CheckGoogleLogin() | 229 auth = self.CheckGoogleLogin() |
| 224 if not auth: | 230 if not auth: |
| 225 return (403, 'No authorization') | 231 return (403, 'No authorization') |
| 226 | 232 |
| 227 managed_check_response = dm.ManagedCheckResponse() | 233 chrome_initial_settings = dm.ChromeInitialSettingsProto() |
| 228 if ('*' in self._server.policy['managed_users'] or | 234 if ('*' in self._server.policy['managed_users'] or |
| 229 auth in self._server.policy['managed_users']): | 235 auth in self._server.policy['managed_users']): |
| 230 managed_check_response.mode = dm.ManagedCheckResponse.MANAGED; | 236 chrome_initial_settings.enrollment_provision = ( |
| 237 dm.ChromeInitialSettingsProto.MANAGED); | |
| 231 else: | 238 else: |
| 232 managed_check_response.mode = dm.ManagedCheckResponse.UNMANAGED; | 239 chrome_initial_settings.enrollment_provision = ( |
| 240 dm.ChromeInitialSettingsProto.UNMANAGED); | |
| 241 | |
| 242 policy_data = dm.PolicyData() | |
| 243 policy_data.policy_type = msg.policy_type | |
| 244 policy_data.policy_value = chrome_initial_settings.SerializeToString() | |
| 233 | 245 |
| 234 # Prepare and send the response. | 246 # Prepare and send the response. |
| 235 response = dm.DeviceManagementResponse() | 247 response = dm.DeviceManagementResponse() |
| 236 response.error = dm.DeviceManagementResponse.SUCCESS | 248 response.error = dm.DeviceManagementResponse.SUCCESS |
| 237 response.managed_check_response.CopyFrom(managed_check_response) | 249 fetch_response = response.policy_response.response.add() |
| 250 fetch_response.policy_data = ( | |
| 251 policy_data.SerializeToString()) | |
| 238 | 252 |
| 239 self.DumpMessage('Response', response) | 253 self.DumpMessage('Response', response) |
| 240 | 254 |
| 241 return (200, response.SerializeToString()) | 255 return (200, response.SerializeToString()) |
| 242 | 256 |
| 243 def ProcessPolicy(self, msg): | 257 def ProcessDevicePolicy(self, msg): |
| 244 """Handles a policy request. | |
| 245 | |
| 246 Checks for authorization, encodes the policy into protobuf representation | |
| 247 and constructs the response. | |
| 248 | |
| 249 Args: | |
| 250 msg: The DevicePolicyRequest message received from the client. | |
| 251 | |
| 252 Returns: | |
| 253 A tuple of HTTP status code and response data to send to the client. | |
| 254 """ | |
| 255 # Check the management token. | 258 # Check the management token. |
| 256 token, response = self.CheckToken() | 259 token, response = self.CheckToken() |
| 257 if not token: | 260 if not token: |
| 258 return response | 261 return response |
| 259 | 262 |
| 260 # Stuff the policy dictionary into a response message and send it back. | 263 # Stuff the policy dictionary into a response message and send it back. |
| 261 response = dm.DeviceManagementResponse() | 264 response = dm.DeviceManagementResponse() |
| 262 response.error = dm.DeviceManagementResponse.SUCCESS | 265 response.error = dm.DeviceManagementResponse.SUCCESS |
| 263 response.policy_response.CopyFrom(dm.DevicePolicyResponse()) | 266 response.policy_response.CopyFrom(dm.DevicePolicyResponse()) |
| 264 | 267 |
| 265 # Respond only if the client requested policy for the cros/device scope, | 268 # Respond only if the client requested policy for the cros/device scope, |
| 266 # since that's where chrome policy is supposed to live in. | 269 # since that's where chrome policy is supposed to live in. |
| 267 if msg.policy_scope in self._server.policy: | 270 if msg.policy_scope == 'chromeos/device': |
| 268 policy = self._server.policy[msg.policy_scope]['mandatory'] | 271 policy = self._server.policy['google/chromeos/user']['mandatory'] |
| 269 setting = response.policy_response.setting.add() | 272 setting = response.policy_response.setting.add() |
| 270 setting.policy_key = 'chrome-policy' | 273 setting.policy_key = 'chrome-policy' |
| 271 policy_value = dm.GenericSetting() | 274 policy_value = dm.GenericSetting() |
| 272 for (key, value) in policy.iteritems(): | 275 for (key, value) in policy.iteritems(): |
| 273 entry = policy_value.named_value.add() | 276 entry = policy_value.named_value.add() |
| 274 entry.name = key | 277 entry.name = key |
| 275 entry_value = dm.GenericValue() | 278 entry_value = dm.GenericValue() |
| 276 if isinstance(value, bool): | 279 if isinstance(value, bool): |
| 277 entry_value.value_type = dm.GenericValue.VALUE_TYPE_BOOL | 280 entry_value.value_type = dm.GenericValue.VALUE_TYPE_BOOL |
| 278 entry_value.bool_value = value | 281 entry_value.bool_value = value |
| 279 elif isinstance(value, int): | 282 elif isinstance(value, int): |
| 280 entry_value.value_type = dm.GenericValue.VALUE_TYPE_INT64 | 283 entry_value.value_type = dm.GenericValue.VALUE_TYPE_INT64 |
| 281 entry_value.int64_value = value | 284 entry_value.int64_value = value |
| 282 elif isinstance(value, str) or isinstance(value, unicode): | 285 elif isinstance(value, str) or isinstance(value, unicode): |
| 283 entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING | 286 entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING |
| 284 entry_value.string_value = value | 287 entry_value.string_value = value |
| 285 elif isinstance(value, list): | 288 elif isinstance(value, list): |
| 286 entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING_ARRAY | 289 entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING_ARRAY |
| 287 for list_entry in value: | 290 for list_entry in value: |
| 288 entry_value.string_array.append(str(list_entry)) | 291 entry_value.string_array.append(str(list_entry)) |
| 289 entry.value.CopyFrom(entry_value) | 292 entry.value.CopyFrom(entry_value) |
| 290 setting.policy_value.CopyFrom(policy_value) | 293 setting.policy_value.CopyFrom(policy_value) |
| 291 | 294 |
| 292 self.DumpMessage('Response', response) | 295 self.DumpMessage('Response', response) |
| 293 | 296 |
| 294 return (200, response.SerializeToString()) | 297 return (200, response.SerializeToString()) |
| 295 | 298 |
| 299 def ProcessPolicy(self, msg, request_type): | |
| 300 """Handles a policy request. | |
| 301 | |
| 302 Checks for authorization, encodes the policy into protobuf representation | |
| 303 and constructs the response. | |
| 304 | |
| 305 Args: | |
| 306 msg: The DevicePolicyRequest message received from the client. | |
| 307 | |
| 308 Returns: | |
| 309 A tuple of HTTP status code and response data to send to the client. | |
| 310 """ | |
| 311 | |
| 312 if msg.request: | |
| 313 for request in msg.request: | |
| 314 if request.policy_type == 'google/chromeos/unregistered_user': | |
| 315 if request_type != 'ping': | |
| 316 return (400, 'Invalid request type') | |
| 317 return self.ProcessInitialPolicy(request) | |
| 318 elif (request.policy_type in | |
| 319 ('google/chromeos/user', 'google/chromeos/device')): | |
| 320 if request_type != 'policy': | |
| 321 return (400, 'Invalid request type') | |
| 322 return self.ProcessCloudPolicy(request) | |
| 323 else: | |
| 324 return (400, 'Invalid policy_type') | |
| 325 else: | |
| 326 return self.ProcessDevicePolicy(msg) | |
| 327 | |
| 296 def SetProtobufMessageField(self, group_message, field, field_value): | 328 def SetProtobufMessageField(self, group_message, field, field_value): |
| 297 '''Sets a field in a protobuf message. | 329 '''Sets a field in a protobuf message. |
| 298 | 330 |
| 299 Args: | 331 Args: |
| 300 group_message: The protobuf message. | 332 group_message: The protobuf message. |
| 301 field: The field of the message to set, it shuold be a member of | 333 field: The field of the message to set, it shuold be a member of |
| 302 group_message.DESCRIPTOR.fields. | 334 group_message.DESCRIPTOR.fields. |
| 303 field_value: The value to set. | 335 field_value: The value to set. |
| 304 ''' | 336 ''' |
| 305 if field.label == field.LABEL_REPEATED: | 337 if field.type == field.TYPE_BOOL: |
| 338 assert type(field_value) == bool | |
| 339 elif field.type == field.TYPE_STRING: | |
| 340 assert type(field_value) == str | |
| 341 elif field.type == field.TYPE_INT64: | |
| 342 assert type(field_value) == int | |
| 343 elif (field.type == field.TYPE_MESSAGE and | |
| 344 field.message_type.name == 'StringList'): | |
| 306 assert type(field_value) == list | 345 assert type(field_value) == list |
| 307 assert field.type == field.TYPE_STRING | 346 entries = group_message.__getattribute__(field.name).entries |
| 308 list_field = group_message.__getattribute__(field.name) | |
| 309 for list_item in field_value: | 347 for list_item in field_value: |
| 310 list_field.append(list_item) | 348 entries.append(list_item) |
| 349 return | |
| 311 else: | 350 else: |
| 312 # Simple cases: | 351 raise Exception('Unknown field type %s' % field.type) |
| 313 if field.type == field.TYPE_BOOL: | 352 group_message.__setattr__(field.name, field_value) |
| 314 assert type(field_value) == bool | |
| 315 elif field.type == field.TYPE_STRING: | |
| 316 assert type(field_value) == str | |
| 317 elif field.type == field.TYPE_INT64: | |
| 318 assert type(field_value) == int | |
| 319 else: | |
| 320 raise Exception('Unknown field type %s' % field.type_name) | |
| 321 group_message.__setattr__(field.name, field_value) | |
| 322 | 353 |
| 323 def GatherPolicySettings(self, settings, policies): | 354 def GatherPolicySettings(self, settings, policies): |
| 324 '''Copies all the policies from a dictionary into a protobuf of type | 355 '''Copies all the policies from a dictionary into a protobuf of type |
| 325 CloudPolicySettings. | 356 CloudPolicySettings. |
| 326 | 357 |
| 327 Args: | 358 Args: |
| 328 settings: The destination: a CloudPolicySettings protobuf. | 359 settings: The destination: a CloudPolicySettings protobuf. |
| 329 policies: The source: a dictionary containing policies under keys | 360 policies: The source: a dictionary containing policies under keys |
| 330 'recommended' and 'mandatory'. | 361 'recommended' and 'mandatory'. |
| 331 ''' | 362 ''' |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 345 group_message.policy_options.mode = cp.PolicyOptions.MANDATORY | 376 group_message.policy_options.mode = cp.PolicyOptions.MANDATORY |
| 346 field_value = policies['mandatory'][field.name] | 377 field_value = policies['mandatory'][field.name] |
| 347 elif field.name in policies['recommended']: | 378 elif field.name in policies['recommended']: |
| 348 field_value = policies['recommended'][field.name] | 379 field_value = policies['recommended'][field.name] |
| 349 if field_value != None: | 380 if field_value != None: |
| 350 got_fields = True | 381 got_fields = True |
| 351 self.SetProtobufMessageField(group_message, field, field_value) | 382 self.SetProtobufMessageField(group_message, field, field_value) |
| 352 if got_fields: | 383 if got_fields: |
| 353 settings.__getattribute__(group.name).CopyFrom(group_message) | 384 settings.__getattribute__(group.name).CopyFrom(group_message) |
| 354 | 385 |
| 355 def ProcessCloudPolicyRequest(self, msg): | 386 def ProcessCloudPolicy(self, msg): |
| 356 """Handles a cloud policy request. (New protocol for policy requests.) | 387 """Handles a cloud policy request. (New protocol for policy requests.) |
| 357 | 388 |
| 358 Checks for authorization, encodes the policy into protobuf representation, | 389 Checks for authorization, encodes the policy into protobuf representation, |
| 359 signs it and constructs the repsonse. | 390 signs it and constructs the repsonse. |
| 360 | 391 |
| 361 Args: | 392 Args: |
| 362 msg: The CloudPolicyRequest message received from the client. | 393 msg: The CloudPolicyRequest message received from the client. |
| 363 | 394 |
| 364 Returns: | 395 Returns: |
| 365 A tuple of HTTP status code and response data to send to the client. | 396 A tuple of HTTP status code and response data to send to the client. |
| 366 """ | 397 """ |
| 367 token, response = self.CheckToken() | 398 |
| 368 if not token: | 399 token_info, error = self.CheckToken() |
| 369 return response | 400 if not token_info: |
| 401 return error | |
| 370 | 402 |
| 371 settings = cp.CloudPolicySettings() | 403 settings = cp.CloudPolicySettings() |
| 372 | 404 |
| 373 if msg.policy_scope in self._server.policy: | 405 if (msg.policy_type in token_info['allowed_policy_types'] and |
| 374 # Respond is only given if the scope is specified in the config file. | 406 msg.policy_type in self._server.policy): |
| 407 # Response is only given if the scope is specified in the config file. | |
| 375 # Normally 'chromeos/device' and 'chromeos/user' should be accepted. | 408 # Normally 'chromeos/device' and 'chromeos/user' should be accepted. |
| 376 self.GatherPolicySettings(settings, | 409 self.GatherPolicySettings(settings, |
| 377 self._server.policy[msg.policy_scope]) | 410 self._server.policy[msg.policy_type]) |
| 378 | 411 |
| 379 # Construct response | 412 policy_data = dm.PolicyData() |
| 380 signed_response = dm.SignedCloudPolicyResponse() | 413 policy_data.policy_value = settings.SerializeToString() |
| 381 signed_response.settings.CopyFrom(settings) | 414 policy_data.policy_type = msg.policy_type |
| 382 signed_response.timestamp = int(time.time()) | 415 policy_data.timestamp = int(time.time() * 1000) |
| 383 signed_response.request_token = token; | 416 policy_data.request_token = token_info['device_token']; |
| 384 signed_response.device_name = self.GetDeviceName() | 417 policy_data.machine_name = token_info['machine_name'] |
| 385 | 418 signed_data = policy_data.SerializeToString() |
| 386 cloud_response = dm.CloudPolicyResponse() | |
| 387 cloud_response.signed_response = signed_response.SerializeToString() | |
| 388 signed_data = cloud_response.signed_response | |
| 389 cloud_response.signature = ( | |
| 390 self._server.private_key.hashAndSign(signed_data).tostring()) | |
| 391 for certificate in self._server.cert_chain: | |
| 392 cloud_response.certificate_chain.append( | |
| 393 certificate.writeBytes().tostring()) | |
| 394 | 419 |
| 395 response = dm.DeviceManagementResponse() | 420 response = dm.DeviceManagementResponse() |
| 396 response.error = dm.DeviceManagementResponse.SUCCESS | 421 response.error = dm.DeviceManagementResponse.SUCCESS |
| 397 response.cloud_policy_response.CopyFrom(cloud_response) | 422 fetch_response = response.policy_response.response.add() |
| 423 fetch_response.policy_data = signed_data | |
| 424 fetch_response.policy_data_signature = ( | |
| 425 self._server.private_key.hashAndSign(signed_data).tostring()) | |
| 426 for certificate in self._server.cert_chain: | |
| 427 fetch_response.certificate_chain.append( | |
| 428 certificate.writeBytes().tostring()) | |
| 398 | 429 |
| 399 self.DumpMessage('Response', response) | 430 self.DumpMessage('Response', response) |
| 400 | 431 |
| 401 return (200, response.SerializeToString()) | 432 return (200, response.SerializeToString()) |
| 402 | 433 |
| 403 def CheckToken(self): | 434 def CheckToken(self): |
| 404 """Helper for checking whether the client supplied a valid DM token. | 435 """Helper for checking whether the client supplied a valid DM token. |
| 405 | 436 |
| 406 Extracts the token from the request and passed to the server in order to | 437 Extracts the token from the request and passed to the server in order to |
| 407 look up the client. Returns a pair of token and error response. If the token | 438 look up the client. |
| 408 is None, the error response is a pair of status code and error message. | |
| 409 | 439 |
| 410 Returns: | 440 Returns: |
| 411 A pair of DM token and error response. If the token is None, the message | 441 A pair of token information record and error response. If the first |
| 412 will contain the error response to send back. | 442 element is None, then the second contains an error code to send back to |
| 443 the client. Otherwise the first element is the same structure that is | |
| 444 returned by LookupToken(). | |
| 413 """ | 445 """ |
| 414 error = None | 446 error = None |
| 415 dmtoken = None | 447 dmtoken = None |
| 416 request_device_id = self.GetUniqueParam('deviceid') | 448 request_device_id = self.GetUniqueParam('deviceid') |
| 417 match = re.match('GoogleDMToken token=(\\w+)', | 449 match = re.match('GoogleDMToken token=(\\w+)', |
| 418 self._headers.getheader('Authorization', '')) | 450 self._headers.getheader('Authorization', '')) |
| 419 if match: | 451 if match: |
| 420 dmtoken = match.group(1) | 452 dmtoken = match.group(1) |
| 421 if not dmtoken: | 453 if not dmtoken: |
| 422 error = dm.DeviceManagementResponse.DEVICE_MANAGEMENT_TOKEN_INVALID | 454 error = dm.DeviceManagementResponse.DEVICE_MANAGEMENT_TOKEN_INVALID |
| 423 elif (not request_device_id or | |
| 424 not self._server.LookupDevice(dmtoken) == request_device_id): | |
| 425 error = dm.DeviceManagementResponse.DEVICE_NOT_FOUND | |
| 426 else: | 455 else: |
| 427 return (dmtoken, None) | 456 token_info = self._server.LookupToken(dmtoken) |
| 457 if (not token_info or | |
| 458 not request_device_id or | |
| 459 token_info['device_id'] != request_device_id): | |
| 460 error = dm.DeviceManagementResponse.DEVICE_NOT_FOUND | |
| 461 else: | |
| 462 return (token_info, None) | |
| 428 | 463 |
| 429 response = dm.DeviceManagementResponse() | 464 response = dm.DeviceManagementResponse() |
| 430 response.error = error | 465 response.error = error |
| 431 | 466 |
| 432 self.DumpMessage('Response', response) | 467 self.DumpMessage('Response', response) |
| 433 | 468 |
| 434 return (None, (200, response.SerializeToString())) | 469 return (None, (200, response.SerializeToString())) |
| 435 | 470 |
| 436 def DumpMessage(self, label, msg): | 471 def DumpMessage(self, label, msg): |
| 437 """Helper for logging an ASCII dump of a protobuf message.""" | 472 """Helper for logging an ASCII dump of a protobuf message.""" |
| 438 logging.debug('%s\n%s' % (label, str(msg))) | 473 logging.debug('%s\n%s' % (label, str(msg))) |
| 439 | 474 |
| 440 class TestServer(object): | 475 class TestServer(object): |
| 441 """Handles requests and keeps global service state.""" | 476 """Handles requests and keeps global service state.""" |
| 442 | 477 |
| 443 def __init__(self, policy_path, policy_cert_chain): | 478 def __init__(self, policy_path, policy_cert_chain): |
| 444 """Initializes the server. | 479 """Initializes the server. |
| 445 | 480 |
| 446 Args: | 481 Args: |
| 447 policy_path: Names the file to read JSON-formatted policy from. | 482 policy_path: Names the file to read JSON-formatted policy from. |
| 448 policy_cert_chain: List of paths to X.509 certificate files of the | 483 policy_cert_chain: List of paths to X.509 certificate files of the |
| 449 certificate chain used for signing responses. | 484 certificate chain used for signing responses. |
| 450 """ | 485 """ |
| 451 self._registered_devices = {} | 486 self._registered_tokens = {} |
| 452 self.policy = {} | 487 self.policy = {} |
| 453 if json is None: | 488 if json is None: |
| 454 print 'No JSON module, cannot parse policy information' | 489 print 'No JSON module, cannot parse policy information' |
| 455 else : | 490 else : |
| 456 try: | 491 try: |
| 457 self.policy = json.loads(open(policy_path).read()) | 492 self.policy = json.loads(open(policy_path).read()) |
| 458 except IOError: | 493 except IOError: |
| 459 print 'Failed to load policy from %s' % policy_path | 494 print 'Failed to load policy from %s' % policy_path |
| 460 | 495 |
| 461 self.private_key = None | 496 self.private_key = None |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 478 Args: | 513 Args: |
| 479 path: The request path and query parameters received from the client. | 514 path: The request path and query parameters received from the client. |
| 480 headers: A rfc822.Message-like object containing HTTP headers. | 515 headers: A rfc822.Message-like object containing HTTP headers. |
| 481 request: The request data received from the client as a string. | 516 request: The request data received from the client as a string. |
| 482 Returns: | 517 Returns: |
| 483 A pair of HTTP status code and response data to send to the client. | 518 A pair of HTTP status code and response data to send to the client. |
| 484 """ | 519 """ |
| 485 handler = RequestHandler(self, path, headers, request) | 520 handler = RequestHandler(self, path, headers, request) |
| 486 return handler.HandleRequest() | 521 return handler.HandleRequest() |
| 487 | 522 |
| 488 def RegisterDevice(self, device_id): | 523 def RegisterDevice(self, device_id, machine_id, type=None): |
| 489 """Registers a device and generate a DM token for it. | 524 """Registers a device or user and generates a DM token for it. |
| 490 | 525 |
| 491 Args: | 526 Args: |
| 492 device_id: The device identifier provided by the client. | 527 device_id: The device identifier provided by the client. |
| 493 | 528 |
| 494 Returns: | 529 Returns: |
| 495 The newly generated device token for the device. | 530 The newly generated device token for the device. |
| 496 """ | 531 """ |
| 497 dmtoken_chars = [] | 532 dmtoken_chars = [] |
| 498 while len(dmtoken_chars) < 32: | 533 while len(dmtoken_chars) < 32: |
| 499 dmtoken_chars.append(random.choice('0123456789abcdef')) | 534 dmtoken_chars.append(random.choice('0123456789abcdef')) |
| 500 dmtoken = ''.join(dmtoken_chars) | 535 dmtoken = ''.join(dmtoken_chars) |
| 501 self._registered_devices[dmtoken] = device_id | |
| 502 return dmtoken | |
| 503 | 536 |
| 504 def LookupDevice(self, dmtoken): | 537 self._registered_tokens[dmtoken] = { |
| 505 """Looks up a device by DMToken. | 538 'device_id': device_id, |
| 539 'device_token': dmtoken, | |
| 540 'allowed_policy_types': { | |
| 541 dm.DeviceRegisterRequest.USER: ['google/chromeos/user'], | |
| 542 dm.DeviceRegisterRequest.DEVICE: ['google/chromeos/device'], | |
| 543 dm.DeviceRegisterRequest.TT: ['google/chromeos/user'], | |
| 544 }[type], | |
|
Jakob Kummerow
2011/02/28 11:06:31
Not sure if I like this expression. It's concise,
gfeher
2011/02/28 12:21:32
How about it now?
| |
| 545 'machine_name': 'chromeos-' + machine_id, | |
| 546 } | |
| 547 return self._registered_tokens[dmtoken] | |
| 548 | |
| 549 def LookupToken(self, dmtoken): | |
| 550 """Looks up a device or a user by DM token. | |
| 506 | 551 |
| 507 Args: | 552 Args: |
| 508 dmtoken: The device management token provided by the client. | 553 dmtoken: The device management token provided by the client. |
| 509 | 554 |
| 510 Returns: | 555 Returns: |
| 511 The corresponding device identifier or None if not found. | 556 A dictionary with information about a device or user that is registered by |
| 557 dmtoken, or None if the token is not found. | |
| 512 """ | 558 """ |
| 513 return self._registered_devices.get(dmtoken, None) | 559 return self._registered_tokens.get(dmtoken, None) |
| 514 | 560 |
| 515 def UnregisterDevice(self, dmtoken): | 561 def UnregisterDevice(self, dmtoken): |
| 516 """Unregisters a device identified by the given DM token. | 562 """Unregisters a device identified by the given DM token. |
| 517 | 563 |
| 518 Args: | 564 Args: |
| 519 dmtoken: The device management token provided by the client. | 565 dmtoken: The device management token provided by the client. |
| 520 """ | 566 """ |
| 521 if dmtoken in self._registered_devices: | 567 if dmtoken in self._registered_tokens.keys(): |
| 522 del self._registered_devices[dmtoken] | 568 del self._registered_tokens[dmtoken] |
| OLD | NEW |