device_management.py revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
14a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#!/usr/bin/python2.5 272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen# Copyright (c) 2011 The Chromium Authors. All rights reserved. 34a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch# Use of this source code is governed by a BSD-style license that can be 44a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch# found in the LICENSE file. 54a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 64a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch"""A bare-bones test server for testing cloud policy support. 74a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 84a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochThis implements a simple cloud policy test server that can be used to test 94a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochchrome's device management service client. The policy information is read from 1072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenthe file named device_management in the server's data directory. It contains 1172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenenforced and recommended policies for the device and user scope, and a list 1272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenof managed users. 1372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 1472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenThe format of the file is JSON. The root dictionary contains a list under the 1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenkey "managed_users". It contains auth tokens for which the server will claim 1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenthat the user is managed. The token string "*" indicates that all users are 1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenclaimed to be managed. Other keys in the root dictionary identify request 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenscopes. Each request scope is described by a dictionary that holds two 1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsensub-dictionaries: "mandatory" and "recommended". Both these hold the policy 2072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsendefinitions as key/value stores, their format is identical to what the Linux 2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenimplementation reads from /etc. 2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExample: 244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch{ 2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen "chromeos/device": { 2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen "mandatory": { 2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen "HomepageLocation" : "http://www.chromium.org" 2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen }, 3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen "recommended": { 3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen "JavascriptEnabled": false, 3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen }, 3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen }, 3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen "managed_users": [ 3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen "secret123456" 3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ] 374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch} 384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 3972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch""" 414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochimport cgi 434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochimport logging 4472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenimport os 454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochimport random 464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochimport re 474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochimport sys 4872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenimport time 4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenimport tlslite 5072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenimport tlslite.api 514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch# The name and availability of the json module varies in python versions. 534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochtry: 544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch import simplejson as json 554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochexcept ImportError: 564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch try: 574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch import json 584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch except ImportError: 594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch json = None 604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochimport device_management_backend_pb2 as dm 6272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenimport cloud_policy_pb2 as cp 6372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass RequestHandler(object): 664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Decodes and handles device management requests from clients. 674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch The handler implements all the request parsing and protobuf message decoding 694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch and encoding. It calls back into the server to lookup, register, and 704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch unregister clients. 714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def __init__(self, server, path, headers, request): 744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Initialize the handler. 754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch server: The TestServer object to use for (un)registering clients. 784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch path: A string containing the request path and query parameters. 794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch headers: A rfc822.Message-like object containing HTTP headers. 804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch request: The request data received from the client as a string. 814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._server = server 834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._path = path 844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._headers = headers 854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._request = request 864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._params = None 874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def GetUniqueParam(self, name): 894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Extracts a unique query parameter from the request. 904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch name: Names the parameter to fetch. 934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch The parameter value or None if the parameter doesn't exist or is not 954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch unique. 964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if not self._params: 984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._params = cgi.parse_qs(self._path[self._path.find('?')+1:]) 994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch param_list = self._params.get(name, []) 1014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if len(param_list) == 1: 1024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return param_list[0] 1034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return None; 1044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def HandleRequest(self): 1064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Handles a request. 1074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Parses the data supplied at construction time and returns a pair indicating 1094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch http status code and response data to be sent back to the client. 1104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 1124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch A tuple of HTTP status code and response data to send to the client. 1134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 1144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch rmsg = dm.DeviceManagementRequest() 1154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch rmsg.ParseFromString(self._request) 1164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self.DumpMessage('Request', rmsg) 1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch request_type = self.GetUniqueParam('request') 1204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if request_type == 'register': 1214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return self.ProcessRegister(rmsg.register_request) 1224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch elif request_type == 'unregister': 1234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return self.ProcessUnregister(rmsg.unregister_request) 1244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch elif request_type == 'policy': 1254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return self.ProcessPolicy(rmsg.policy_request) 12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elif request_type == 'cloud_policy': 12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return self.ProcessCloudPolicyRequest(rmsg.cloud_policy_request) 12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elif request_type == 'managed_check': 12972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return self.ProcessManagedCheck(rmsg.managed_check_request) 1304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch else: 1314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return (400, 'Invalid request parameter') 1324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen def CheckGoogleLogin(self): 13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """Extracts the GoogleLogin auth token from the HTTP request, and 13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen returns it. Returns None if the token is not present. 13672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """ 13772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen match = re.match('GoogleLogin auth=(\\w+)', 13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self._headers.getheader('Authorization', '')) 13972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if not match: 14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return None 14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return match.group(1) 14272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 14372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen def GetDeviceName(self): 14472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """Returns the name for the currently authenticated device based on its 14572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen device id. 14672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """ 14772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return 'chromeos-' + self.GetUniqueParam('deviceid') 14872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 1494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def ProcessRegister(self, msg): 1504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Handles a register request. 1514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Checks the query for authorization and device identifier, registers the 1534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch device with the server and constructs a response. 1544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 1564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch msg: The DeviceRegisterRequest message received from the client. 1574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 1594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch A tuple of HTTP status code and response data to send to the client. 1604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 1614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Check the auth token and device ID. 16272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if not self.CheckGoogleLogin(): 1634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return (403, 'No authorization') 1644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch device_id = self.GetUniqueParam('deviceid') 1664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if not device_id: 1674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return (400, 'Missing device identifier') 1684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Register the device and create a token. 1704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch dmtoken = self._server.RegisterDevice(device_id) 1714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Send back the reply. 1734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response = dm.DeviceManagementResponse() 1744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response.error = dm.DeviceManagementResponse.SUCCESS 1754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response.register_response.device_management_token = dmtoken 17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen response.register_response.device_name = self.GetDeviceName() 1774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self.DumpMessage('Response', response) 1794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return (200, response.SerializeToString()) 1814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def ProcessUnregister(self, msg): 1834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Handles a register request. 1844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Checks for authorization, unregisters the device and constructs the 1864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response. 1874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 1894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch msg: The DeviceUnregisterRequest message received from the client. 1904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 1924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch A tuple of HTTP status code and response data to send to the client. 1934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 1944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Check the management token. 1954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch token, response = self.CheckToken(); 1964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if not token: 1974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return response 1984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 1994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Unregister the device. 2004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._server.UnregisterDevice(token); 2014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Prepare and send the response. 2034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response = dm.DeviceManagementResponse() 2044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response.error = dm.DeviceManagementResponse.SUCCESS 2054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse()) 2064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self.DumpMessage('Response', response) 2084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return (200, response.SerializeToString()) 2104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 21172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen def ProcessManagedCheck(self, msg): 21272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """Handles a 'managed check' request. 21372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 21472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Queries the list of managed users and responds the client if their user 21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen is managed or not. 21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Args: 21872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen msg: The ManagedCheckRequest message received from the client. 21972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 22072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Returns: 22172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen A tuple of HTTP status code and response data to send to the client. 22272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """ 22372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Check the management token. 22472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen auth = self.CheckGoogleLogin() 22572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if not auth: 22672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return (403, 'No authorization') 22772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 22872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen managed_check_response = dm.ManagedCheckResponse() 22972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if ('*' in self._server.policy['managed_users'] or 23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen auth in self._server.policy['managed_users']): 23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen managed_check_response.mode = dm.ManagedCheckResponse.MANAGED; 23272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen else: 23372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen managed_check_response.mode = dm.ManagedCheckResponse.UNMANAGED; 23472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 23572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Prepare and send the response. 23672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen response = dm.DeviceManagementResponse() 23772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen response.error = dm.DeviceManagementResponse.SUCCESS 23872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen response.managed_check_response.CopyFrom(managed_check_response) 23972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 24072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.DumpMessage('Response', response) 24172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 24272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return (200, response.SerializeToString()) 24372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 2444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def ProcessPolicy(self, msg): 2454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Handles a policy request. 2464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Checks for authorization, encodes the policy into protobuf representation 24872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen and constructs the response. 2494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 2514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch msg: The DevicePolicyRequest message received from the client. 2524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 2544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch A tuple of HTTP status code and response data to send to the client. 2554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 2564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Check the management token. 2574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch token, response = self.CheckToken() 2584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if not token: 2594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return response 2604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Stuff the policy dictionary into a response message and send it back. 2624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response = dm.DeviceManagementResponse() 2634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response.error = dm.DeviceManagementResponse.SUCCESS 2644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response.policy_response.CopyFrom(dm.DevicePolicyResponse()) 2654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # Respond only if the client requested policy for the cros/device scope, 2674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch # since that's where chrome policy is supposed to live in. 26872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if msg.policy_scope in self._server.policy: 26972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen policy = self._server.policy[msg.policy_scope]['mandatory'] 2704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch setting = response.policy_response.setting.add() 2714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch setting.policy_key = 'chrome-policy' 2724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch policy_value = dm.GenericSetting() 27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for (key, value) in policy.iteritems(): 2744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry = policy_value.named_value.add() 2754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry.name = key 2764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value = dm.GenericValue() 2774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if isinstance(value, bool): 2784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.value_type = dm.GenericValue.VALUE_TYPE_BOOL 2794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.bool_value = value 2804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch elif isinstance(value, int): 2814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.value_type = dm.GenericValue.VALUE_TYPE_INT64 2824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.int64_value = value 2834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch elif isinstance(value, str) or isinstance(value, unicode): 2844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING 2854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.string_value = value 2864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch elif isinstance(value, list): 2874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.value_type = dm.GenericValue.VALUE_TYPE_STRING_ARRAY 2884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch for list_entry in value: 2894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry_value.string_array.append(str(list_entry)) 2904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch entry.value.CopyFrom(entry_value) 2914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch setting.policy_value.CopyFrom(policy_value) 2924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self.DumpMessage('Response', response) 2944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 2954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return (200, response.SerializeToString()) 2964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 29772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen def SetProtobufMessageField(self, group_message, field, field_value): 29872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen '''Sets a field in a protobuf message. 29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Args: 30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen group_message: The protobuf message. 30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen field: The field of the message to set, it shuold be a member of 30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen group_message.DESCRIPTOR.fields. 30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen field_value: The value to set. 30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ''' 30672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if field.label == field.LABEL_REPEATED: 30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen assert type(field_value) == list 30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen assert field.type == field.TYPE_STRING 30972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen list_field = group_message.__getattribute__(field.name) 31072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for list_item in field_value: 31172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen list_field.append(list_item) 31272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen else: 31372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Simple cases: 31472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if field.type == field.TYPE_BOOL: 31572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen assert type(field_value) == bool 31672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elif field.type == field.TYPE_STRING: 31772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen assert type(field_value) == str 31872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elif field.type == field.TYPE_INT64: 31972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen assert type(field_value) == int 32072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen else: 32172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen raise Exception('Unknown field type %s' % field.type_name) 32272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen group_message.__setattr__(field.name, field_value) 32372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 32472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen def GatherPolicySettings(self, settings, policies): 32572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen '''Copies all the policies from a dictionary into a protobuf of type 32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CloudPolicySettings. 32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Args: 32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen settings: The destination: a CloudPolicySettings protobuf. 33072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen policies: The source: a dictionary containing policies under keys 33172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 'recommended' and 'mandatory'. 33272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ''' 33372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for group in settings.DESCRIPTOR.fields: 33472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Create protobuf message for group. 33572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen group_message = eval('cp.' + group.message_type.name + '()') 33672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # We assume that this policy group will be recommended, and only switch 33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # it to mandatory if at least one of its members is mandatory. 33872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen group_message.policy_options.mode = cp.PolicyOptions.RECOMMENDED 33972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Indicates if at least one field was set in |group_message|. 34072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen got_fields = False 34172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Iterate over fields of the message and feed them from the 34272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # policy config file. 34372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for field in group_message.DESCRIPTOR.fields: 34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen field_value = None 34572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if field.name in policies['mandatory']: 34672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen group_message.policy_options.mode = cp.PolicyOptions.MANDATORY 34772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen field_value = policies['mandatory'][field.name] 34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elif field.name in policies['recommended']: 34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen field_value = policies['recommended'][field.name] 35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if field_value != None: 35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen got_fields = True 35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.SetProtobufMessageField(group_message, field, field_value) 35372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if got_fields: 35472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen settings.__getattribute__(group.name).CopyFrom(group_message) 35572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 35672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen def ProcessCloudPolicyRequest(self, msg): 35772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """Handles a cloud policy request. (New protocol for policy requests.) 35872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Checks for authorization, encodes the policy into protobuf representation, 36072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signs it and constructs the repsonse. 36172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Args: 36372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen msg: The CloudPolicyRequest message received from the client. 36472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 36572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Returns: 36672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen A tuple of HTTP status code and response data to send to the client. 36772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen """ 36872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen token, response = self.CheckToken() 36972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if not token: 37072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return response 37172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 37272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen settings = cp.CloudPolicySettings() 37372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 37472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if msg.policy_scope in self._server.policy: 37572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Respond is only given if the scope is specified in the config file. 37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Normally 'chromeos/device' and 'chromeos/user' should be accepted. 37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.GatherPolicySettings(settings, 37872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self._server.policy[msg.policy_scope]) 37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 38072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen # Construct response 38172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signed_response = dm.SignedCloudPolicyResponse() 38272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signed_response.settings.CopyFrom(settings) 38372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signed_response.timestamp = int(time.time()) 38472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signed_response.request_token = token; 38572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signed_response.device_name = self.GetDeviceName() 38672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 38772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen cloud_response = dm.CloudPolicyResponse() 38872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen cloud_response.signed_response = signed_response.SerializeToString() 38972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen signed_data = cloud_response.signed_response 39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen cloud_response.signature = ( 39172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self._server.private_key.hashAndSign(signed_data).tostring()) 39272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for certificate in self._server.cert_chain: 39372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen cloud_response.certificate_chain.append( 39472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen certificate.writeBytes().tostring()) 39572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 39672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen response = dm.DeviceManagementResponse() 39772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen response.error = dm.DeviceManagementResponse.SUCCESS 39872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen response.cloud_policy_response.CopyFrom(cloud_response) 39972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 40072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.DumpMessage('Response', response) 40172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 40272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return (200, response.SerializeToString()) 40372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 4044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def CheckToken(self): 4054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Helper for checking whether the client supplied a valid DM token. 4064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Extracts the token from the request and passed to the server in order to 4084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch look up the client. Returns a pair of token and error response. If the token 4094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch is None, the error response is a pair of status code and error message. 4104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 4124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch A pair of DM token and error response. If the token is None, the message 4134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch will contain the error response to send back. 4144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 4154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch error = None 416201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch dmtoken = None 417201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch request_device_id = self.GetUniqueParam('deviceid') 4184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch match = re.match('GoogleDMToken token=(\\w+)', 4194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._headers.getheader('Authorization', '')) 4204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if match: 4214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch dmtoken = match.group(1) 422201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if not dmtoken: 423201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch error = dm.DeviceManagementResponse.DEVICE_MANAGEMENT_TOKEN_INVALID 424201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch elif (not request_device_id or 425201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch not self._server.LookupDevice(dmtoken) == request_device_id): 426201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch error = dm.DeviceManagementResponse.DEVICE_NOT_FOUND 427201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch else: 428201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return (dmtoken, None) 4294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response = dm.DeviceManagementResponse() 4314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch response.error = error 4324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self.DumpMessage('Response', response) 4344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return (None, (200, response.SerializeToString())) 4364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def DumpMessage(self, label, msg): 4384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Helper for logging an ASCII dump of a protobuf message.""" 4394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch logging.debug('%s\n%s' % (label, str(msg))) 4404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass TestServer(object): 4424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Handles requests and keeps global service state.""" 4434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 44472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen def __init__(self, policy_path, policy_cert_chain): 4454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Initializes the server. 4464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 4484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch policy_path: Names the file to read JSON-formatted policy from. 44972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen policy_cert_chain: List of paths to X.509 certificate files of the 45072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen certificate chain used for signing responses. 4514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 4524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._registered_devices = {} 4534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self.policy = {} 4544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if json is None: 4554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch print 'No JSON module, cannot parse policy information' 4564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch else : 4574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch try: 4584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self.policy = json.loads(open(policy_path).read()) 4594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch except IOError: 4604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch print 'Failed to load policy from %s' % policy_path 4614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 46272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.private_key = None 46372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.cert_chain = [] 46472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen for cert_path in policy_cert_chain: 46572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen try: 46672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen cert_text = open(cert_path).read() 46772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen except IOError: 46872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen print 'Failed to load certificate from %s' % cert_path 46972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen certificate = tlslite.api.X509() 47072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen certificate.parse(cert_text) 47172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.cert_chain.append(certificate) 47272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if self.private_key is None: 47372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen self.private_key = tlslite.api.parsePEMKey(cert_text, private=True) 47472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen assert self.private_key != None 47572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 4764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def HandleRequest(self, path, headers, request): 4774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Handles a request. 4784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 4804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch path: The request path and query parameters received from the client. 4814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch headers: A rfc822.Message-like object containing HTTP headers. 4824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch request: The request data received from the client as a string. 4834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 4844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch A pair of HTTP status code and response data to send to the client. 4854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 4864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch handler = RequestHandler(self, path, headers, request) 4874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return handler.HandleRequest() 4884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def RegisterDevice(self, device_id): 4904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Registers a device and generate a DM token for it. 4914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 4934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch device_id: The device identifier provided by the client. 4944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 4954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 4964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch The newly generated device token for the device. 4974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 4984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch dmtoken_chars = [] 4994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch while len(dmtoken_chars) < 32: 5004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch dmtoken_chars.append(random.choice('0123456789abcdef')) 5014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch dmtoken= ''.join(dmtoken_chars) 5024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch self._registered_devices[dmtoken] = device_id 5034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return dmtoken 5044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 5054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def LookupDevice(self, dmtoken): 5064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Looks up a device by DMToken. 5074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 5084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 5094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch dmtoken: The device management token provided by the client. 5104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 5114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Returns: 5124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch The corresponding device identifier or None if not found. 5134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 5144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return self._registered_devices.get(dmtoken, None) 5154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 5164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch def UnregisterDevice(self, dmtoken): 5174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """Unregisters a device identified by the given DM token. 5184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch 5194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch Args: 5204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch dmtoken: The device management token provided by the client. 5214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch """ 5224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if dmtoken in self._registered_devices: 5234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch del self._registered_devices[dmtoken] 524