| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """A bare-bones test server for testing cloud policy support. | 5 """A bare-bones test server for testing cloud policy support. |
| 6 | 6 |
| 7 This implements a simple cloud policy test server that can be used to test | 7 This implements a simple cloud policy test server that can be used to test |
| 8 chrome's device management service client. The policy information is read from | 8 chrome's device management service client. The policy information is read from |
| 9 the file named device_management in the server's data directory. It contains | 9 the file named device_management in the server's data directory. It contains |
| 10 enforced and recommended policies for the device and user scope, and a list | 10 enforced and recommended policies for the device and user scope, and a list |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 } | 44 } |
| 45 }, | 45 }, |
| 46 "managed_users" : [ | 46 "managed_users" : [ |
| 47 "secret123456" | 47 "secret123456" |
| 48 ], | 48 ], |
| 49 "current_key_index": 0 | 49 "current_key_index": 0 |
| 50 } | 50 } |
| 51 | 51 |
| 52 """ | 52 """ |
| 53 | 53 |
| 54 import BaseHTTPServer |
| 54 import cgi | 55 import cgi |
| 55 import hashlib | 56 import hashlib |
| 56 import logging | 57 import logging |
| 57 import os | 58 import os |
| 58 import random | 59 import random |
| 59 import re | 60 import re |
| 60 import sys | 61 import sys |
| 61 import time | 62 import time |
| 62 import tlslite | 63 import tlslite |
| 63 import tlslite.api | 64 import tlslite.api |
| 64 import tlslite.utils | 65 import tlslite.utils |
| 65 | 66 |
| 66 # The name and availability of the json module varies in python versions. | 67 # The name and availability of the json module varies in python versions. |
| 67 try: | 68 try: |
| 68 import simplejson as json | 69 import simplejson as json |
| 69 except ImportError: | 70 except ImportError: |
| 70 try: | 71 try: |
| 71 import json | 72 import json |
| 72 except ImportError: | 73 except ImportError: |
| 73 json = None | 74 json = None |
| 74 | 75 |
| 75 import asn1der | 76 import asn1der |
| 77 import testserver_base |
| 78 |
| 76 import device_management_backend_pb2 as dm | 79 import device_management_backend_pb2 as dm |
| 77 import cloud_policy_pb2 as cp | 80 import cloud_policy_pb2 as cp |
| 78 import chrome_device_policy_pb2 as dp | 81 import chrome_device_policy_pb2 as dp |
| 79 | 82 |
| 80 # ASN.1 object identifier for PKCS#1/RSA. | 83 # ASN.1 object identifier for PKCS#1/RSA. |
| 81 PKCS1_RSA_OID = '\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01' | 84 PKCS1_RSA_OID = '\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01' |
| 82 | 85 |
| 83 # SHA256 sum of "0". | 86 # SHA256 sum of "0". |
| 84 SHA256_0 = hashlib.sha256('0').digest() | 87 SHA256_0 = hashlib.sha256('0').digest() |
| 85 | 88 |
| 86 # List of bad machine identifiers that trigger the |valid_serial_number_missing| | 89 # List of bad machine identifiers that trigger the |valid_serial_number_missing| |
| 87 # flag to be set set in the policy fetch response. | 90 # flag to be set set in the policy fetch response. |
| 88 BAD_MACHINE_IDS = [ '123490EN400015' ]; | 91 BAD_MACHINE_IDS = [ '123490EN400015' ]; |
| 89 | 92 |
| 90 # List of machines that trigger the server to send kiosk enrollment response | 93 # List of machines that trigger the server to send kiosk enrollment response |
| 91 # for the register request. | 94 # for the register request. |
| 92 KIOSK_MACHINE_IDS = [ 'KIOSK' ]; | 95 KIOSK_MACHINE_IDS = [ 'KIOSK' ]; |
| 93 | 96 |
| 94 class RequestHandler(object): | 97 |
| 98 class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
| 95 """Decodes and handles device management requests from clients. | 99 """Decodes and handles device management requests from clients. |
| 96 | 100 |
| 97 The handler implements all the request parsing and protobuf message decoding | 101 The handler implements all the request parsing and protobuf message decoding |
| 98 and encoding. It calls back into the server to lookup, register, and | 102 and encoding. It calls back into the server to lookup, register, and |
| 99 unregister clients. | 103 unregister clients. |
| 100 """ | 104 """ |
| 101 | 105 |
| 102 def __init__(self, server, path, headers, request): | 106 def __init__(self, request, client_address, server): |
| 103 """Initialize the handler. | 107 """Initialize the handler. |
| 104 | 108 |
| 105 Args: | 109 Args: |
| 110 request: The request data received from the client as a string. |
| 111 client_address: The client address. |
| 106 server: The TestServer object to use for (un)registering clients. | 112 server: The TestServer object to use for (un)registering clients. |
| 107 path: A string containing the request path and query parameters. | |
| 108 headers: A rfc822.Message-like object containing HTTP headers. | |
| 109 request: The request data received from the client as a string. | |
| 110 """ | 113 """ |
| 111 self._server = server | 114 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, |
| 112 self._path = path | 115 client_address, server) |
| 113 self._headers = headers | |
| 114 self._request = request | |
| 115 self._params = None | |
| 116 | 116 |
| 117 def GetUniqueParam(self, name): | 117 def GetUniqueParam(self, name): |
| 118 """Extracts a unique query parameter from the request. | 118 """Extracts a unique query parameter from the request. |
| 119 | 119 |
| 120 Args: | 120 Args: |
| 121 name: Names the parameter to fetch. | 121 name: Names the parameter to fetch. |
| 122 Returns: | 122 Returns: |
| 123 The parameter value or None if the parameter doesn't exist or is not | 123 The parameter value or None if the parameter doesn't exist or is not |
| 124 unique. | 124 unique. |
| 125 """ | 125 """ |
| 126 if not self._params: | 126 if not hasattr(self, '_params'): |
| 127 self._params = cgi.parse_qs(self._path[self._path.find('?') + 1:]) | 127 self._params = cgi.parse_qs(self.path[self.path.find('?') + 1:]) |
| 128 | 128 |
| 129 param_list = self._params.get(name, []) | 129 param_list = self._params.get(name, []) |
| 130 if len(param_list) == 1: | 130 if len(param_list) == 1: |
| 131 return param_list[0] | 131 return param_list[0] |
| 132 return None; | 132 return None; |
| 133 | 133 |
| 134 def do_POST(self): |
| 135 http_response, raw_reply = self.HandleRequest(); |
| 136 self.send_response(http_response) |
| 137 if (http_response == 200): |
| 138 self.send_header('Content-Type', 'application/x-protobuffer') |
| 139 self.end_headers() |
| 140 self.wfile.write(raw_reply) |
| 141 |
| 134 def HandleRequest(self): | 142 def HandleRequest(self): |
| 135 """Handles a request. | 143 """Handles a request. |
| 136 | 144 |
| 137 Parses the data supplied at construction time and returns a pair indicating | 145 Parses the data supplied at construction time and returns a pair indicating |
| 138 http status code and response data to be sent back to the client. | 146 http status code and response data to be sent back to the client. |
| 139 | 147 |
| 140 Returns: | 148 Returns: |
| 141 A tuple of HTTP status code and response data to send to the client. | 149 A tuple of HTTP status code and response data to send to the client. |
| 142 """ | 150 """ |
| 143 rmsg = dm.DeviceManagementRequest() | 151 rmsg = dm.DeviceManagementRequest() |
| 144 rmsg.ParseFromString(self._request) | 152 length = int(self.headers.getheader('content-length')) |
| 153 rmsg.ParseFromString(self.rfile.read(length)) |
| 145 | 154 |
| 146 logging.debug('gaia auth token -> ' + | 155 logging.debug('gaia auth token -> ' + |
| 147 self._headers.getheader('Authorization', '')) | 156 self.headers.getheader('Authorization', '')) |
| 148 logging.debug('oauth token -> ' + str(self.GetUniqueParam('oauth_token'))) | 157 logging.debug('oauth token -> ' + str(self.GetUniqueParam('oauth_token'))) |
| 149 logging.debug('deviceid -> ' + str(self.GetUniqueParam('deviceid'))) | 158 logging.debug('deviceid -> ' + str(self.GetUniqueParam('deviceid'))) |
| 150 self.DumpMessage('Request', rmsg) | 159 self.DumpMessage('Request', rmsg) |
| 151 | 160 |
| 152 request_type = self.GetUniqueParam('request') | 161 request_type = self.GetUniqueParam('request') |
| 153 # Check server side requirements, as defined in | 162 # Check server side requirements, as defined in |
| 154 # device_management_backend.proto. | 163 # device_management_backend.proto. |
| 155 if (self.GetUniqueParam('devicetype') != '2' or | 164 if (self.GetUniqueParam('devicetype') != '2' or |
| 156 self.GetUniqueParam('apptype') != 'Chrome' or | 165 self.GetUniqueParam('apptype') != 'Chrome' or |
| 157 (request_type != 'ping' and | 166 (request_type != 'ping' and |
| (...skipping 15 matching lines...) Expand all Loading... |
| 173 """Extracts the auth token from the request and returns it. The token may | 182 """Extracts the auth token from the request and returns it. The token may |
| 174 either be a GoogleLogin token from an Authorization header, or an OAuth V2 | 183 either be a GoogleLogin token from an Authorization header, or an OAuth V2 |
| 175 token from the oauth_token query parameter. Returns None if no token is | 184 token from the oauth_token query parameter. Returns None if no token is |
| 176 present. | 185 present. |
| 177 """ | 186 """ |
| 178 oauth_token = self.GetUniqueParam('oauth_token') | 187 oauth_token = self.GetUniqueParam('oauth_token') |
| 179 if oauth_token: | 188 if oauth_token: |
| 180 return oauth_token | 189 return oauth_token |
| 181 | 190 |
| 182 match = re.match('GoogleLogin auth=(\\w+)', | 191 match = re.match('GoogleLogin auth=(\\w+)', |
| 183 self._headers.getheader('Authorization', '')) | 192 self.headers.getheader('Authorization', '')) |
| 184 if match: | 193 if match: |
| 185 return match.group(1) | 194 return match.group(1) |
| 186 | 195 |
| 187 return None | 196 return None |
| 188 | 197 |
| 189 def ProcessRegister(self, msg): | 198 def ProcessRegister(self, msg): |
| 190 """Handles a register request. | 199 """Handles a register request. |
| 191 | 200 |
| 192 Checks the query for authorization and device identifier, registers the | 201 Checks the query for authorization and device identifier, registers the |
| 193 device with the server and constructs a response. | 202 device with the server and constructs a response. |
| 194 | 203 |
| 195 Args: | 204 Args: |
| 196 msg: The DeviceRegisterRequest message received from the client. | 205 msg: The DeviceRegisterRequest message received from the client. |
| 197 | 206 |
| 198 Returns: | 207 Returns: |
| 199 A tuple of HTTP status code and response data to send to the client. | 208 A tuple of HTTP status code and response data to send to the client. |
| 200 """ | 209 """ |
| 201 # Check the auth token and device ID. | 210 # Check the auth token and device ID. |
| 202 auth = self.CheckGoogleLogin() | 211 auth = self.CheckGoogleLogin() |
| 203 if not auth: | 212 if not auth: |
| 204 return (403, 'No authorization') | 213 return (403, 'No authorization') |
| 205 | 214 |
| 206 policy = self._server.GetPolicies() | 215 policy = self.server.GetPolicies() |
| 207 if ('*' not in policy['managed_users'] and | 216 if ('*' not in policy['managed_users'] and |
| 208 auth not in policy['managed_users']): | 217 auth not in policy['managed_users']): |
| 209 return (403, 'Unmanaged') | 218 return (403, 'Unmanaged') |
| 210 | 219 |
| 211 device_id = self.GetUniqueParam('deviceid') | 220 device_id = self.GetUniqueParam('deviceid') |
| 212 if not device_id: | 221 if not device_id: |
| 213 return (400, 'Missing device identifier') | 222 return (400, 'Missing device identifier') |
| 214 | 223 |
| 215 token_info = self._server.RegisterDevice(device_id, | 224 token_info = self.server.RegisterDevice(device_id, |
| 216 msg.machine_id, | 225 msg.machine_id, |
| 217 msg.type) | 226 msg.type) |
| 218 | 227 |
| 219 # Send back the reply. | 228 # Send back the reply. |
| 220 response = dm.DeviceManagementResponse() | 229 response = dm.DeviceManagementResponse() |
| 221 response.register_response.device_management_token = ( | 230 response.register_response.device_management_token = ( |
| 222 token_info['device_token']) | 231 token_info['device_token']) |
| 223 response.register_response.machine_name = token_info['machine_name'] | 232 response.register_response.machine_name = token_info['machine_name'] |
| 224 response.register_response.enrollment_type = token_info['enrollment_mode'] | 233 response.register_response.enrollment_type = token_info['enrollment_mode'] |
| 225 | 234 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 238 | 247 |
| 239 Returns: | 248 Returns: |
| 240 A tuple of HTTP status code and response data to send to the client. | 249 A tuple of HTTP status code and response data to send to the client. |
| 241 """ | 250 """ |
| 242 # Check the management token. | 251 # Check the management token. |
| 243 token, response = self.CheckToken(); | 252 token, response = self.CheckToken(); |
| 244 if not token: | 253 if not token: |
| 245 return response | 254 return response |
| 246 | 255 |
| 247 # Unregister the device. | 256 # Unregister the device. |
| 248 self._server.UnregisterDevice(token['device_token']); | 257 self.server.UnregisterDevice(token['device_token']); |
| 249 | 258 |
| 250 # Prepare and send the response. | 259 # Prepare and send the response. |
| 251 response = dm.DeviceManagementResponse() | 260 response = dm.DeviceManagementResponse() |
| 252 response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse()) | 261 response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse()) |
| 253 | 262 |
| 254 self.DumpMessage('Response', response) | 263 self.DumpMessage('Response', response) |
| 255 | 264 |
| 256 return (200, response.SerializeToString()) | 265 return (200, response.SerializeToString()) |
| 257 | 266 |
| 258 def ProcessPolicy(self, msg, request_type): | 267 def ProcessPolicy(self, msg, request_type): |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 429 | 438 |
| 430 Returns: | 439 Returns: |
| 431 A tuple of HTTP status code and response data to send to the client. | 440 A tuple of HTTP status code and response data to send to the client. |
| 432 """ | 441 """ |
| 433 | 442 |
| 434 token_info, error = self.CheckToken() | 443 token_info, error = self.CheckToken() |
| 435 if not token_info: | 444 if not token_info: |
| 436 return error | 445 return error |
| 437 | 446 |
| 438 if msg.machine_id: | 447 if msg.machine_id: |
| 439 self._server.UpdateMachineId(token_info['device_token'], msg.machine_id) | 448 self.server.UpdateMachineId(token_info['device_token'], msg.machine_id) |
| 440 | 449 |
| 441 # Response is only given if the scope is specified in the config file. | 450 # Response is only given if the scope is specified in the config file. |
| 442 # Normally 'google/chromeos/device', 'google/chromeos/user' and | 451 # Normally 'google/chromeos/device', 'google/chromeos/user' and |
| 443 # 'google/chromeos/publicaccount' should be accepted. | 452 # 'google/chromeos/publicaccount' should be accepted. |
| 444 policy = self._server.GetPolicies() | 453 policy = self.server.GetPolicies() |
| 445 policy_value = '' | 454 policy_value = '' |
| 446 policy_key = msg.policy_type | 455 policy_key = msg.policy_type |
| 447 if msg.settings_entity_id: | 456 if msg.settings_entity_id: |
| 448 policy_key += '/' + msg.settings_entity_id | 457 policy_key += '/' + msg.settings_entity_id |
| 449 if msg.policy_type in token_info['allowed_policy_types']: | 458 if msg.policy_type in token_info['allowed_policy_types']: |
| 450 if (msg.policy_type == 'google/chromeos/user' or | 459 if (msg.policy_type == 'google/chromeos/user' or |
| 451 msg.policy_type == 'google/chrome/user' or | 460 msg.policy_type == 'google/chrome/user' or |
| 452 msg.policy_type == 'google/chromeos/publicaccount'): | 461 msg.policy_type == 'google/chromeos/publicaccount'): |
| 453 settings = cp.CloudPolicySettings() | 462 settings = cp.CloudPolicySettings() |
| 454 self.GatherUserPolicySettings(settings, policy.get(policy_key, {})) | 463 self.GatherUserPolicySettings(settings, policy.get(policy_key, {})) |
| 455 elif msg.policy_type == 'google/chromeos/device': | 464 elif msg.policy_type == 'google/chromeos/device': |
| 456 settings = dp.ChromeDeviceSettingsProto() | 465 settings = dp.ChromeDeviceSettingsProto() |
| 457 self.GatherDevicePolicySettings(settings, policy.get(policy_key, {})) | 466 self.GatherDevicePolicySettings(settings, policy.get(policy_key, {})) |
| 458 | 467 |
| 459 # Sign with 'current_key_index', defaulting to key 0. | 468 # Sign with 'current_key_index', defaulting to key 0. |
| 460 signing_key = None | 469 signing_key = None |
| 461 req_key = None | 470 req_key = None |
| 462 current_key_index = policy.get('current_key_index', 0) | 471 current_key_index = policy.get('current_key_index', 0) |
| 463 nkeys = len(self._server.keys) | 472 nkeys = len(self.server.keys) |
| 464 if (msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and | 473 if (msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and |
| 465 current_key_index in range(nkeys)): | 474 current_key_index in range(nkeys)): |
| 466 signing_key = self._server.keys[current_key_index] | 475 signing_key = self.server.keys[current_key_index] |
| 467 if msg.public_key_version in range(1, nkeys + 1): | 476 if msg.public_key_version in range(1, nkeys + 1): |
| 468 # requested key exists, use for signing and rotate. | 477 # requested key exists, use for signing and rotate. |
| 469 req_key = self._server.keys[msg.public_key_version - 1]['private_key'] | 478 req_key = self.server.keys[msg.public_key_version - 1]['private_key'] |
| 470 | 479 |
| 471 # Fill the policy data protobuf. | 480 # Fill the policy data protobuf. |
| 472 policy_data = dm.PolicyData() | 481 policy_data = dm.PolicyData() |
| 473 policy_data.policy_type = msg.policy_type | 482 policy_data.policy_type = msg.policy_type |
| 474 policy_data.timestamp = int(time.time() * 1000) | 483 policy_data.timestamp = int(time.time() * 1000) |
| 475 policy_data.request_token = token_info['device_token'] | 484 policy_data.request_token = token_info['device_token'] |
| 476 policy_data.policy_value = settings.SerializeToString() | 485 policy_data.policy_value = settings.SerializeToString() |
| 477 policy_data.machine_name = token_info['machine_name'] | 486 policy_data.machine_name = token_info['machine_name'] |
| 478 policy_data.valid_serial_number_missing = ( | 487 policy_data.valid_serial_number_missing = ( |
| 479 token_info['machine_id'] in BAD_MACHINE_IDS) | 488 token_info['machine_id'] in BAD_MACHINE_IDS) |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 Returns: | 526 Returns: |
| 518 A pair of token information record and error response. If the first | 527 A pair of token information record and error response. If the first |
| 519 element is None, then the second contains an error code to send back to | 528 element is None, then the second contains an error code to send back to |
| 520 the client. Otherwise the first element is the same structure that is | 529 the client. Otherwise the first element is the same structure that is |
| 521 returned by LookupToken(). | 530 returned by LookupToken(). |
| 522 """ | 531 """ |
| 523 error = 500 | 532 error = 500 |
| 524 dmtoken = None | 533 dmtoken = None |
| 525 request_device_id = self.GetUniqueParam('deviceid') | 534 request_device_id = self.GetUniqueParam('deviceid') |
| 526 match = re.match('GoogleDMToken token=(\\w+)', | 535 match = re.match('GoogleDMToken token=(\\w+)', |
| 527 self._headers.getheader('Authorization', '')) | 536 self.headers.getheader('Authorization', '')) |
| 528 if match: | 537 if match: |
| 529 dmtoken = match.group(1) | 538 dmtoken = match.group(1) |
| 530 if not dmtoken: | 539 if not dmtoken: |
| 531 error = 401 | 540 error = 401 |
| 532 else: | 541 else: |
| 533 token_info = self._server.LookupToken(dmtoken) | 542 token_info = self.server.LookupToken(dmtoken) |
| 534 if (not token_info or | 543 if (not token_info or |
| 535 not request_device_id or | 544 not request_device_id or |
| 536 token_info['device_id'] != request_device_id): | 545 token_info['device_id'] != request_device_id): |
| 537 error = 410 | 546 error = 410 |
| 538 else: | 547 else: |
| 539 return (token_info, None) | 548 return (token_info, None) |
| 540 | 549 |
| 541 logging.debug('Token check failed with error %d' % error) | 550 logging.debug('Token check failed with error %d' % error) |
| 542 | 551 |
| 543 return (None, (error, 'Server error %d' % error)) | 552 return (None, (error, 'Server error %d' % error)) |
| 544 | 553 |
| 545 def DumpMessage(self, label, msg): | 554 def DumpMessage(self, label, msg): |
| 546 """Helper for logging an ASCII dump of a protobuf message.""" | 555 """Helper for logging an ASCII dump of a protobuf message.""" |
| 547 logging.debug('%s\n%s' % (label, str(msg))) | 556 logging.debug('%s\n%s' % (label, str(msg))) |
| 548 | 557 |
| 549 class TestServer(object): | 558 |
| 559 class PolicyTestServer(testserver_base.ClientRestrictingServerMixIn, |
| 560 testserver_base.BrokenPipeHandlerMixIn, |
| 561 testserver_base.StoppableHTTPServer): |
| 550 """Handles requests and keeps global service state.""" | 562 """Handles requests and keeps global service state.""" |
| 551 | 563 |
| 552 def __init__(self, policy_path, private_key_paths): | 564 def __init__(self, server_address, policy_path, private_key_paths): |
| 553 """Initializes the server. | 565 """Initializes the server. |
| 554 | 566 |
| 555 Args: | 567 Args: |
| 568 server_address: Server host and port. |
| 556 policy_path: Names the file to read JSON-formatted policy from. | 569 policy_path: Names the file to read JSON-formatted policy from. |
| 557 private_key_paths: List of paths to read private keys from. | 570 private_key_paths: List of paths to read private keys from. |
| 558 """ | 571 """ |
| 572 testserver_base.StoppableHTTPServer.__init__(self, server_address, |
| 573 PolicyRequestHandler) |
| 559 self._registered_tokens = {} | 574 self._registered_tokens = {} |
| 560 self.policy_path = policy_path | 575 self.policy_path = policy_path |
| 561 | 576 |
| 562 self.keys = [] | 577 self.keys = [] |
| 563 if private_key_paths: | 578 if private_key_paths: |
| 564 # Load specified keys from the filesystem. | 579 # Load specified keys from the filesystem. |
| 565 for key_path in private_key_paths: | 580 for key_path in private_key_paths: |
| 566 try: | 581 try: |
| 567 key = tlslite.api.parsePEMKey(open(key_path).read(), private=True) | 582 key = tlslite.api.parsePEMKey(open(key_path).read(), private=True) |
| 568 except IOError: | 583 except IOError: |
| (...skipping 28 matching lines...) Expand all Loading... |
| 597 policy = {} | 612 policy = {} |
| 598 if json is None: | 613 if json is None: |
| 599 print 'No JSON module, cannot parse policy information' | 614 print 'No JSON module, cannot parse policy information' |
| 600 else : | 615 else : |
| 601 try: | 616 try: |
| 602 policy = json.loads(open(self.policy_path).read()) | 617 policy = json.loads(open(self.policy_path).read()) |
| 603 except IOError: | 618 except IOError: |
| 604 print 'Failed to load policy from %s' % self.policy_path | 619 print 'Failed to load policy from %s' % self.policy_path |
| 605 return policy | 620 return policy |
| 606 | 621 |
| 607 def HandleRequest(self, path, headers, request): | |
| 608 """Handles a request. | |
| 609 | |
| 610 Args: | |
| 611 path: The request path and query parameters received from the client. | |
| 612 headers: A rfc822.Message-like object containing HTTP headers. | |
| 613 request: The request data received from the client as a string. | |
| 614 Returns: | |
| 615 A pair of HTTP status code and response data to send to the client. | |
| 616 """ | |
| 617 handler = RequestHandler(self, path, headers, request) | |
| 618 return handler.HandleRequest() | |
| 619 | |
| 620 def RegisterDevice(self, device_id, machine_id, type): | 622 def RegisterDevice(self, device_id, machine_id, type): |
| 621 """Registers a device or user and generates a DM token for it. | 623 """Registers a device or user and generates a DM token for it. |
| 622 | 624 |
| 623 Args: | 625 Args: |
| 624 device_id: The device identifier provided by the client. | 626 device_id: The device identifier provided by the client. |
| 625 | 627 |
| 626 Returns: | 628 Returns: |
| 627 The newly generated device token for the device. | 629 The newly generated device token for the device. |
| 628 """ | 630 """ |
| 629 dmtoken_chars = [] | 631 dmtoken_chars = [] |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 677 return self._registered_tokens.get(dmtoken, None) | 679 return self._registered_tokens.get(dmtoken, None) |
| 678 | 680 |
| 679 def UnregisterDevice(self, dmtoken): | 681 def UnregisterDevice(self, dmtoken): |
| 680 """Unregisters a device identified by the given DM token. | 682 """Unregisters a device identified by the given DM token. |
| 681 | 683 |
| 682 Args: | 684 Args: |
| 683 dmtoken: The device management token provided by the client. | 685 dmtoken: The device management token provided by the client. |
| 684 """ | 686 """ |
| 685 if dmtoken in self._registered_tokens.keys(): | 687 if dmtoken in self._registered_tokens.keys(): |
| 686 del self._registered_tokens[dmtoken] | 688 del self._registered_tokens[dmtoken] |
| 689 |
| 690 |
| 691 class PolicyServerRunner(testserver_base.TestServerRunner): |
| 692 |
| 693 def __init__(self): |
| 694 super(PolicyServerRunner, self).__init__() |
| 695 |
| 696 def create_server(self, server_data): |
| 697 config_file = ( |
| 698 self.options.config_file or |
| 699 os.path.join(self.options.data_dir or '', 'device_management')) |
| 700 server = PolicyTestServer((self.options.host, self.options.port), |
| 701 config_file, self.options.policy_keys) |
| 702 server_data['port'] = server.server_port |
| 703 return server |
| 704 |
| 705 def add_options(self): |
| 706 testserver_base.TestServerRunner.add_options(self) |
| 707 self.option_parser.add_option('--policy-key', action='append', |
| 708 dest='policy_keys', |
| 709 help='Specify a path to a PEM-encoded ' |
| 710 'private key to use for policy signing. May ' |
| 711 'be specified multiple times in order to ' |
| 712 'load multipe keys into the server. If the ' |
| 713 'server has multiple keys, it will rotate ' |
| 714 'through them in at each request in a ' |
| 715 'round-robin fashion. The server will ' |
| 716 'generate a random key if none is specified ' |
| 717 'on the command line.') |
| 718 self.option_parser.add_option('--log-level', dest='log_level', |
| 719 default='WARN', |
| 720 help='Log level threshold to use.') |
| 721 self.option_parser.add_option('--config-file', dest='config_file', |
| 722 help='Specify a configuration file to use ' |
| 723 'instead of the default ' |
| 724 '<data_dir>/device_management') |
| 725 |
| 726 def run_server(self): |
| 727 logger = logging.getLogger() |
| 728 logger.setLevel(getattr(logging, str(self.options.log_level).upper())) |
| 729 if (self.options.log_to_console): |
| 730 logger.addHandler(logging.StreamHandler()) |
| 731 if (self.options.log_file): |
| 732 logger.addHandler(logging.FileHandler(self.options.log_file)) |
| 733 |
| 734 testserver_base.TestServerRunner.run_server(self); |
| 735 |
| 736 |
| 737 if __name__ == '__main__': |
| 738 sys.exit(PolicyServerRunner().main()) |
| OLD | NEW |