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